mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-05 09:10:29 +00:00
1159 lines
30 KiB
C++
1159 lines
30 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "common/system.h"
|
|
#include "common/debug-channels.h"
|
|
#include "common/debug.h"
|
|
#include "common/error.h"
|
|
#include "common/config-manager.h"
|
|
#include "engines/util.h"
|
|
|
|
#include "macventure/macventure.h"
|
|
|
|
// To move
|
|
#include "common/file.h"
|
|
|
|
namespace MacVenture {
|
|
|
|
// HACK, see below
|
|
void toASCII(Common::String &str) {
|
|
debugC(1, kMVDebugMain, "toASCII: %s", str.c_str());
|
|
Common::String::iterator it = str.begin();
|
|
for (; it != str.end(); it++) {
|
|
if (*it == '\216') { str.replace(it, it + 1, "e"); }
|
|
if (*it == '\210') { str.replace(it, it + 1, "a"); }
|
|
}
|
|
}
|
|
|
|
enum {
|
|
kMaxMenuTitleLength = 30
|
|
};
|
|
|
|
MacVentureEngine::MacVentureEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst) {
|
|
_gameDescription = gameDesc;
|
|
_rnd = new Common::RandomSource("macventure");
|
|
|
|
initDebugChannels();
|
|
|
|
_debugger = NULL;
|
|
_gui = NULL;
|
|
_scriptEngine = NULL;
|
|
_filenames = NULL;
|
|
|
|
_decodingDirectArticles = NULL;
|
|
_decodingNamingArticles = NULL;
|
|
_decodingIndirectArticles = NULL;
|
|
_textHuffman = NULL;
|
|
|
|
_soundManager = NULL;
|
|
|
|
_dataBundle = NULL;
|
|
|
|
debug("MacVenture::MacVentureEngine()");
|
|
}
|
|
|
|
MacVentureEngine::~MacVentureEngine() {
|
|
debug("MacVenture::~MacVentureEngine()");
|
|
|
|
DebugMan.clearAllDebugChannels();
|
|
|
|
if (_rnd)
|
|
delete _rnd;
|
|
|
|
if (_debugger)
|
|
delete _debugger;
|
|
|
|
if (_gui)
|
|
delete _gui;
|
|
|
|
if (_scriptEngine)
|
|
delete _scriptEngine;
|
|
|
|
if (_filenames)
|
|
delete _filenames;
|
|
|
|
if (_decodingDirectArticles)
|
|
delete _decodingDirectArticles;
|
|
|
|
if (_decodingNamingArticles)
|
|
delete _decodingNamingArticles;
|
|
|
|
if (_decodingIndirectArticles)
|
|
delete _decodingIndirectArticles;
|
|
|
|
if (_textHuffman)
|
|
delete _textHuffman;
|
|
|
|
if (_soundManager)
|
|
delete _soundManager;
|
|
|
|
if (_dataBundle)
|
|
delete _dataBundle;
|
|
}
|
|
|
|
void MacVentureEngine::initDebugChannels() {
|
|
DebugMan.addDebugChannel(kMVDebugMain, "main", "Engine state");
|
|
DebugMan.addDebugChannel(kMVDebugGUI, "gui", "Gui");
|
|
DebugMan.addDebugChannel(kMVDebugText, "text", "Text decoders and printers");
|
|
DebugMan.addDebugChannel(kMVDebugImage, "image", "Image decoders and renderers");
|
|
DebugMan.addDebugChannel(kMVDebugScript, "script", "Script engine");
|
|
DebugMan.addDebugChannel(kMVDebugSound, "sound", "Sound decoders");
|
|
DebugMan.addDebugChannel(kMVDebugContainer, "container", "Containers");
|
|
}
|
|
|
|
Common::Error MacVentureEngine::run() {
|
|
debug("MacVenture::MacVentureEngine::init()");
|
|
initGraphics(kScreenWidth, kScreenHeight, true);
|
|
|
|
_debugger = new Console(this);
|
|
|
|
// Additional setup.
|
|
debug("MacVentureEngine::init");
|
|
|
|
_resourceManager = new Common::MacResManager();
|
|
if (!_resourceManager->open(getGameFileName()))
|
|
error("Could not open %s as a resource fork", getGameFileName());
|
|
|
|
// Engine-wide loading
|
|
if (!loadGlobalSettings())
|
|
error("Could not load the engine settings");
|
|
|
|
_oldTextEncoding = !loadTextHuffman();
|
|
|
|
_filenames = new StringTable(this, _resourceManager, kFilenamesStringTableID);
|
|
_decodingDirectArticles = new StringTable(this, _resourceManager, kCommonArticlesStringTableID);
|
|
_decodingNamingArticles = new StringTable(this, _resourceManager, kNamingArticlesStringTableID);
|
|
_decodingIndirectArticles = new StringTable(this, _resourceManager, kIndirectArticlesStringTableID);
|
|
|
|
loadDataBundle();
|
|
|
|
// Big class instantiation
|
|
_gui = new Gui(this, _resourceManager);
|
|
_world = new World(this, _resourceManager);
|
|
_scriptEngine = new ScriptEngine(this, _world);
|
|
|
|
_soundManager = new SoundManager(this, _mixer);
|
|
|
|
setInitialFlags();
|
|
|
|
int directSaveSlotLoading = ConfMan.getInt("save_slot");
|
|
if (directSaveSlotLoading >= 0) {
|
|
if (loadGameState(directSaveSlotLoading).getCode() != Common::kNoError) {
|
|
error("Could not load game from slot '%d'", directSaveSlotLoading);
|
|
}
|
|
} else {
|
|
setNewGameState();
|
|
}
|
|
_selectedControl = kStartOrResume;
|
|
|
|
_gui->addChild(kSelfWindow, 1);
|
|
_gui->updateWindow(kSelfWindow, false);
|
|
|
|
while (_gameState != kGameStateQuitting) {
|
|
processEvents();
|
|
|
|
if (_gameState != kGameStateQuitting && !_gui->isDialogOpen()) {
|
|
|
|
if (_prepared) {
|
|
_prepared = false;
|
|
|
|
if (!_halted)
|
|
updateState(false);
|
|
|
|
if (_cmdReady || _halted) {
|
|
_halted = false;
|
|
if (runScriptEngine()) {
|
|
_halted = true;
|
|
_paused = true;
|
|
} else {
|
|
_paused = false;
|
|
updateState(true);
|
|
updateControls();
|
|
updateExits();
|
|
}
|
|
}
|
|
|
|
if (_gameState == kGameStateWinnig || _gameState == kGameStateLosing) {
|
|
endGame();
|
|
}
|
|
}
|
|
}
|
|
refreshScreen();
|
|
}
|
|
|
|
return Common::kNoError;
|
|
}
|
|
|
|
void MacVentureEngine::refreshScreen() {
|
|
_gui->draw();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(50);
|
|
}
|
|
|
|
void MacVentureEngine::newGame() {
|
|
_world->startNewGame();
|
|
reset();
|
|
setInitialFlags();
|
|
setNewGameState();
|
|
}
|
|
|
|
void MacVentureEngine::setInitialFlags() {
|
|
_paused = false;
|
|
_halted = false;
|
|
_cmdReady = false;
|
|
_haltedAtEnd = false;
|
|
_haltedInSelection = false;
|
|
_clickToContinue = true;
|
|
_gameState = kGameStateInit;
|
|
_destObject = 0;
|
|
_prepared = true;
|
|
}
|
|
|
|
void MacVentureEngine::setNewGameState() {
|
|
_cmdReady = true;
|
|
ObjID playerParent = _world->getObjAttr(1, kAttrParentObject);
|
|
_currentSelection.push_back(playerParent);// Push the parent of the player
|
|
_world->setObjAttr(playerParent, kAttrContainerOpen, 1);
|
|
}
|
|
|
|
void MacVentureEngine::reset() {
|
|
resetInternals();
|
|
resetGui();
|
|
}
|
|
|
|
void MacVentureEngine::resetInternals() {
|
|
_scriptEngine->reset();
|
|
_currentSelection.clear();
|
|
_selectedObjs.clear();
|
|
_objQueue.clear();
|
|
_textQueue.clear();
|
|
}
|
|
|
|
void MacVentureEngine::resetGui() {
|
|
_gui->updateWindowInfo(kMainGameWindow, getParent(1), _world->getChildren(getParent(1), true));
|
|
// HACK! should update all inventories
|
|
_gui->ensureInventoryOpen(kInventoryStart, 1);
|
|
_gui->updateWindowInfo(kInventoryStart, 1, _world->getChildren(1, true));
|
|
updateControls();
|
|
updateExits();
|
|
refreshScreen();
|
|
}
|
|
|
|
void MacVentureEngine::requestQuit() {
|
|
// TODO: Display save game dialog and such
|
|
_gameState = kGameStateQuitting;
|
|
}
|
|
|
|
void MacVentureEngine::requestUnpause() {
|
|
_paused = false;
|
|
_gameState = kGameStatePlaying;
|
|
}
|
|
|
|
void MacVentureEngine::selectControl(ControlAction id) {
|
|
debugC(2, kMVDebugMain, "Select control %x", id);
|
|
_selectedControl = id;
|
|
}
|
|
|
|
void MacVentureEngine::activateCommand(ControlAction id) {
|
|
if (id == kClickToContinue) {
|
|
_clickToContinue = false;
|
|
_paused = true;
|
|
return;
|
|
}
|
|
if (id != _activeControl) {
|
|
if (_activeControl)
|
|
_activeControl = kNoCommand;
|
|
_activeControl = id;
|
|
}
|
|
debugC(2, kMVDebugMain, "Activating Command %x... Command %x is active", id, _activeControl);
|
|
refreshReady();
|
|
}
|
|
|
|
void MacVentureEngine::refreshReady() {
|
|
switch (getInvolvedObjects()) {
|
|
case 0: // No selected object
|
|
_cmdReady = true;
|
|
break;
|
|
case 1: // We have some selected object
|
|
_cmdReady = _currentSelection.size() != 0;
|
|
break;
|
|
case 2:
|
|
if (_destObject > 0) // We have a destination seleted
|
|
_cmdReady = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::preparedToRun() {
|
|
_prepared = true;
|
|
}
|
|
|
|
void MacVentureEngine::gameChanged() {
|
|
_gameChanged = true;
|
|
}
|
|
|
|
void MacVentureEngine::winGame() {
|
|
_gui->showPrebuiltDialog(kWinGameDialog);
|
|
_gameState = kGameStateWinnig;
|
|
}
|
|
|
|
void MacVentureEngine::loseGame() {
|
|
_gui->showPrebuiltDialog(kLoseGameDialog);
|
|
_paused = true;
|
|
//_gameState = kGameStateLosing;
|
|
}
|
|
|
|
void MacVentureEngine::clickToContinue() {
|
|
_clickToContinue = true;
|
|
}
|
|
|
|
void MacVentureEngine::enqueueObject(ObjectQueueID type, ObjID objID, ObjID target) {
|
|
QueuedObject obj;
|
|
obj.id = type;
|
|
|
|
if (type == kUpdateObject && isObjEnqueued(objID)) {
|
|
return;
|
|
}
|
|
|
|
if (type == kUpdateWindow) { obj.target = target; }
|
|
|
|
if (type != kHightlightExits) {
|
|
obj.object = objID;
|
|
obj.parent = _world->getObjAttr(objID, kAttrParentObject);
|
|
obj.x = _world->getObjAttr(objID, kAttrPosX);
|
|
obj.y = _world->getObjAttr(objID, kAttrPosY);
|
|
obj.exitx = _world->getObjAttr(objID, kAttrExitX);
|
|
obj.exity = _world->getObjAttr(objID, kAttrExitY);
|
|
obj.hidden = _world->getObjAttr(objID, kAttrHiddenExit);
|
|
obj.offscreen = _world->getObjAttr(objID, kAttrInvisible);
|
|
obj.invisible = _world->getObjAttr(objID, kAttrUnclickable);
|
|
}
|
|
_objQueue.push_back(obj);
|
|
}
|
|
|
|
bool MacVentureEngine::isObjEnqueued(ObjID objID) {
|
|
Common::Array<QueuedObject>::const_iterator it;
|
|
for (it = _objQueue.begin(); it != _objQueue.end(); it++) {
|
|
if ((*it).object == objID) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MacVentureEngine::enqueueText(TextQueueID type, ObjID target, ObjID source, ObjID text) {
|
|
QueuedText newText;
|
|
newText.id = type;
|
|
newText.destination = target;
|
|
newText.source = source;
|
|
newText.asset = text;
|
|
_textQueue.push_back(newText);
|
|
}
|
|
|
|
void MacVentureEngine::enqueueSound(SoundQueueID type, ObjID target) {
|
|
QueuedSound newSound;
|
|
newSound.id = type;
|
|
newSound.reference = target;
|
|
_soundQueue.push_back(newSound);
|
|
}
|
|
|
|
void MacVentureEngine::handleObjectSelect(ObjID objID, WindowReference win, bool shiftPressed, bool isDoubleClick) {
|
|
if (win == kExitsWindow) {
|
|
win = kMainGameWindow;
|
|
}
|
|
|
|
bool canDrag = (objID && !_world->getObjAttr(objID, kAttrInvisible));
|
|
|
|
const WindowData &windata = _gui->getWindowData(win);
|
|
|
|
if (shiftPressed) {
|
|
// Do shift ;)
|
|
} else {
|
|
if (_selectedControl && _currentSelection.size() > 0 && getInvolvedObjects() > 1) {
|
|
if (objID == 0)
|
|
selectPrimaryObject(windata.objRef);
|
|
else
|
|
selectPrimaryObject(objID);
|
|
preparedToRun();
|
|
} else {
|
|
if (objID == 0) {
|
|
unselectAll();
|
|
//if (windata.type == kAnimateBack) {
|
|
//doLasso(win, event, canDrag);
|
|
//}
|
|
//else {
|
|
objID = win;
|
|
//}
|
|
}
|
|
if (objID > 0) {
|
|
int i = findObjectInArray(objID, _currentSelection);
|
|
|
|
if (isDoubleClick) {
|
|
if (i >= 0)
|
|
unselectAll();
|
|
selectObject(objID);
|
|
_destObject = objID;
|
|
_deltaPoint = Common::Point(0, 0);
|
|
if (!_cmdReady) {
|
|
selectControl(kActivateObject);
|
|
_activeControl = kActivateObject;
|
|
_cmdReady = true;
|
|
}
|
|
preparedToRun();
|
|
} else {
|
|
if (i >= 0)
|
|
unselectAll();
|
|
selectObject(objID);
|
|
if (getInvolvedObjects() == 1)
|
|
_cmdReady = true;
|
|
preparedToRun();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::handleObjectDrop(ObjID objID, Common::Point delta, ObjID newParent) {
|
|
_destObject = newParent;
|
|
updateDelta(delta);
|
|
selectControl(kMoveObject);
|
|
activateCommand(kMoveObject);
|
|
refreshReady();
|
|
preparedToRun();
|
|
}
|
|
|
|
void MacVentureEngine::updateDelta(Common::Point newPos) {
|
|
debugC(4, kMVDebugMain, "Update delta: Old(%d, %d), New(%d, %d)",
|
|
_deltaPoint.x, _deltaPoint.y,
|
|
newPos.x, newPos.y);
|
|
_deltaPoint = newPos;
|
|
}
|
|
|
|
void MacVentureEngine::focusObjWin(ObjID objID) {
|
|
_gui->bringToFront(getObjWindow(objID));
|
|
}
|
|
|
|
void MacVentureEngine::updateWindow(WindowReference winID) {
|
|
_gui->updateWindow(winID, true);
|
|
}
|
|
|
|
bool MacVentureEngine::showTextEntry(ObjID text, ObjID srcObj, ObjID destObj) {
|
|
debugC(3, kMVDebugMain, "Showing speech dialog, asset %d from %d to %d", text, srcObj, destObj);
|
|
_gui->getTextFromUser();
|
|
|
|
// HACK WITH FLAGS
|
|
_prepared = false;
|
|
warning("Show text entry: not fully tested");
|
|
return true;
|
|
}
|
|
|
|
void MacVentureEngine::setTextInput(Common::String content) {
|
|
// HACK WITH FLAGS
|
|
_prepared = true;
|
|
_userInput = content;
|
|
_clickToContinue = false;
|
|
}
|
|
|
|
Common::String MacVentureEngine::getUserInput() {
|
|
return _userInput;
|
|
}
|
|
|
|
|
|
Common::String MacVentureEngine::getStartGameFileName() {
|
|
Common::SeekableReadStream *res;
|
|
res = _resourceManager->getResource(MKTAG('S', 'T', 'R', ' '), kStartGameFilenameID);
|
|
if (!res)
|
|
return "";
|
|
|
|
byte length = res->readByte();
|
|
char *fileName = new char[length + 1];
|
|
res->read(fileName, length);
|
|
fileName[length] = '\0';
|
|
Common::String result = Common::String(fileName, length);
|
|
// HACK, see definition of toASCII
|
|
toASCII(result);
|
|
return result;
|
|
}
|
|
|
|
const GlobalSettings& MacVentureEngine::getGlobalSettings() const {
|
|
return _globalSettings;
|
|
}
|
|
|
|
// Private engine methods
|
|
void MacVentureEngine::processEvents() {
|
|
Common::Event event;
|
|
|
|
while (_eventMan->pollEvent(event)) {
|
|
if (_gui->processEvent(event))
|
|
continue;
|
|
|
|
switch (event.type) {
|
|
case Common::EVENT_QUIT:
|
|
_gameState = kGameStateQuitting;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MacVenture::MacVentureEngine::runScriptEngine() {
|
|
debugC(4, kMVDebugMain, "MAIN: Running script engine");
|
|
if (_haltedAtEnd) {
|
|
_haltedAtEnd = false;
|
|
if (_scriptEngine->resume(false)) {
|
|
_haltedAtEnd = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (_haltedInSelection) {
|
|
_haltedInSelection = false;
|
|
if (_scriptEngine->resume(false)) {
|
|
_haltedInSelection = true;
|
|
return true;
|
|
}
|
|
updateState(true);
|
|
}
|
|
|
|
while (!_currentSelection.empty()) {
|
|
ObjID obj = _currentSelection.front();
|
|
_currentSelection.remove_at(0);
|
|
if ((_gameState == kGameStateInit || _gameState == kGameStatePlaying) && _world->isObjActive(obj)) {
|
|
if (_scriptEngine->runControl(_selectedControl, obj, _destObject, _deltaPoint)) {
|
|
_haltedInSelection = true;
|
|
return true;
|
|
}
|
|
updateState(true);
|
|
}
|
|
}
|
|
if (_selectedControl == 1)
|
|
_gameChanged = false;
|
|
|
|
else if (_gameState == kGameStateInit || _gameState == kGameStatePlaying) {
|
|
if (_scriptEngine->runControl(kTick, _selectedControl, _destObject, _deltaPoint)) {
|
|
_haltedAtEnd = true;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MacVentureEngine::endGame() {
|
|
requestQuit();
|
|
}
|
|
|
|
void MacVentureEngine::updateState(bool pause) {
|
|
_prepared = false;
|
|
runObjQueue();
|
|
printTexts();
|
|
playSounds(pause);
|
|
}
|
|
|
|
void MacVentureEngine::revert() {
|
|
_gui->invertWindowColors(kMainGameWindow);
|
|
preparedToRun();
|
|
}
|
|
|
|
void MacVentureEngine::runObjQueue() {
|
|
while (!_objQueue.empty()) {
|
|
uint32 biggest = 0;
|
|
uint32 index = 0;
|
|
uint32 temp;
|
|
for (uint i = 0; i < _objQueue.size(); i++) {
|
|
temp = _objQueue[i].id;
|
|
if (temp > biggest) {
|
|
biggest = temp;
|
|
index = i;
|
|
}
|
|
}
|
|
QueuedObject obj = _objQueue[index];
|
|
_objQueue.remove_at(index);
|
|
switch (obj.id) {
|
|
case 0x2:
|
|
focusObjectWindow(obj.object);
|
|
break;
|
|
case 0x3:
|
|
openObject(obj.object);
|
|
break;
|
|
case 0x4:
|
|
closeObject(obj.object);
|
|
break;
|
|
case 0x7:
|
|
checkObject(obj);
|
|
break;
|
|
case 0x8:
|
|
reflectSwap(obj.object, obj.target);
|
|
break;
|
|
case 0xc:
|
|
_world->setObjAttr(_gui->getWindowData(kMainGameWindow).refcon, kAttrContainerOpen, 0);
|
|
_world->setObjAttr(_world->getObjAttr(1, kAttrParentObject), kAttrContainerOpen, 1);
|
|
break;
|
|
case 0xd:
|
|
toggleExits();
|
|
break;
|
|
case 0xe:
|
|
zoomObject(obj.object);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::printTexts() {
|
|
for (uint i = 0; i < _textQueue.size(); i++) {
|
|
QueuedText text = _textQueue.front();
|
|
_textQueue.remove_at(0);
|
|
switch (text.id) {
|
|
case kTextNumber:
|
|
_gui->printText(Common::String(text.asset));
|
|
gameChanged();
|
|
break;
|
|
case kTextNewLine:
|
|
_gui->printText(Common::String(""));
|
|
gameChanged();
|
|
break;
|
|
case kTextPlain:
|
|
_gui->printText(_world->getText(text.asset, text.source, text.destination));
|
|
gameChanged();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::playSounds(bool pause) {
|
|
int delay=0;
|
|
while (!_soundQueue.empty()) {
|
|
QueuedSound item = _soundQueue.front();
|
|
_soundQueue.remove_at(0);
|
|
switch (item.id) {
|
|
case kSoundPlay:
|
|
_soundManager->playSound(item.reference);
|
|
break;
|
|
case kSoundPlayAndWait:
|
|
delay = _soundManager->playSound(item.reference);
|
|
break;
|
|
case kSoundWait:
|
|
//wait for sound to finish?
|
|
break;
|
|
}
|
|
}
|
|
if (pause && delay > 0) {
|
|
warning("Sound pausing not yet tested. Pausing for %d", delay);
|
|
g_system->delayMillis(delay);
|
|
preparedToRun();
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::updateControls() {
|
|
if (_activeControl)
|
|
_activeControl = kNoCommand;
|
|
_gui->clearControls();
|
|
toggleExits();
|
|
resetVars();
|
|
}
|
|
|
|
void MacVentureEngine::resetVars() {
|
|
_selectedControl = kNoCommand;
|
|
_activeControl = kNoCommand;
|
|
_currentSelection.clear();
|
|
_destObject = 0;
|
|
_deltaPoint = Common::Point(0, 0);
|
|
_cmdReady = false;
|
|
}
|
|
|
|
void MacVentureEngine::unselectAll() {
|
|
while (!_currentSelection.empty()) {
|
|
unselectObject(_currentSelection.front());
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::selectObject(ObjID objID) {
|
|
if (!_currentSelection.empty()) {
|
|
if (findParentWindow(objID) != findParentWindow(_currentSelection[0]))
|
|
unselectAll();
|
|
}
|
|
if (findObjectInArray(objID, _currentSelection) == -1)
|
|
_currentSelection.push_back(objID);
|
|
if (findObjectInArray(objID, _selectedObjs) == -1) {
|
|
_selectedObjs.push_back(objID);
|
|
highlightExit(objID);
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::unselectObject(ObjID objID) {
|
|
int idxCur = findObjectInArray(objID, _currentSelection);
|
|
int idxSel = findObjectInArray(objID, _selectedObjs);
|
|
if (idxCur != -1) _currentSelection.remove_at(idxCur);
|
|
if (idxSel != -1) {
|
|
_selectedObjs.remove_at(idxSel);
|
|
highlightExit(objID);
|
|
}
|
|
}
|
|
|
|
|
|
void MacVentureEngine::updateExits() {
|
|
_gui->clearExits();
|
|
_gui->unselectExits();
|
|
|
|
Common::Array<ObjID> exits = _world->getChildren(_world->getObjAttr(1, kAttrParentObject), true);
|
|
for (uint i = 0; i < exits.size(); i++)
|
|
_gui->updateExit(exits[i]);
|
|
|
|
}
|
|
|
|
int MacVentureEngine::findObjectInArray(ObjID objID, const Common::Array<ObjID> &list) {
|
|
// Find the object in the current selection
|
|
bool found = false;
|
|
uint i = 0;
|
|
while (i < list.size() && !found) {
|
|
if (list[i] == objID) found = true;
|
|
else i++;
|
|
}
|
|
// HACK, should use iterator
|
|
return found ? i : -1;
|
|
}
|
|
|
|
uint MacVentureEngine::getPrefixNdx(ObjID obj) {
|
|
return _world->getObjAttr(obj, kAttrPrefixes);
|
|
}
|
|
|
|
Common::String MacVentureEngine::getPrefixString(uint flag, ObjID obj) {
|
|
uint ndx = getPrefixNdx(obj);
|
|
ndx = ((ndx) >> flag) & 3;
|
|
if (ndx) {
|
|
return _decodingNamingArticles->getString(ndx);
|
|
} else {
|
|
return Common::String("m1551gn0 ");
|
|
}
|
|
}
|
|
|
|
Common::String MacVentureEngine::getNoun(ObjID ndx) {
|
|
return _decodingIndirectArticles->getString(ndx);
|
|
}
|
|
|
|
void MacVentureEngine::highlightExit(ObjID objID) {
|
|
//ObjID ctl = _gui->getWinChild(obj);
|
|
/*if (ctl) {
|
|
if (findObjectInArray(obj, _selectedObjs) != -1)
|
|
_gui->selectExit(ctl);
|
|
else
|
|
_gui->unselectExit(ctl);
|
|
}
|
|
if (obj == _world->getObjAttr(1, kAttrParentObject)) {
|
|
if (findObjectInArray(obj, _selectedObjs) != -1)
|
|
_gui->selectExit(obj);
|
|
else
|
|
_gui->unselectExit(obj);
|
|
}*/
|
|
//updateWindow(findParentWindow(obj));
|
|
}
|
|
|
|
void MacVentureEngine::selectPrimaryObject(ObjID objID) {
|
|
if (objID == _destObject) return;
|
|
int idx;
|
|
if (_destObject > 0 &&
|
|
(idx = findObjectInArray(_destObject, _selectedObjs)) != -1 &&
|
|
findObjectInArray(_destObject, _currentSelection) == -1)
|
|
{
|
|
_selectedObjs.remove_at(idx);
|
|
highlightExit(_destObject);
|
|
}
|
|
_destObject = objID;
|
|
if (findObjectInArray(_destObject, _selectedObjs) == -1) {
|
|
_selectedObjs.push_back(_destObject);
|
|
highlightExit(_destObject);
|
|
}
|
|
_cmdReady = true;
|
|
}
|
|
|
|
void MacVentureEngine::focusObjectWindow(ObjID objID) {
|
|
if (objID) {
|
|
WindowReference win = getObjWindow(objID);
|
|
if (win)
|
|
_gui->bringToFront(win);
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::openObject(ObjID objID) {
|
|
|
|
debugC(1, kMVDebugMain, "Open Object[%d] parent[%d] x[%d] y[%d]",
|
|
objID,
|
|
_world->getObjAttr(objID, kAttrParentObject),
|
|
_world->getObjAttr(objID, kAttrPosX),
|
|
_world->getObjAttr(objID, kAttrPosY));
|
|
|
|
if (getObjWindow(objID)) return;
|
|
if (objID == _world->getObjAttr(1, kAttrParentObject)) {
|
|
_gui->updateWindowInfo(kMainGameWindow, objID, _world->getChildren(objID, true));
|
|
_gui->updateWindow(kMainGameWindow, _world->getObjAttr(objID, kAttrContainerOpen));
|
|
updateExits();
|
|
_gui->setWindowTitle(kMainGameWindow, _world->getText(objID, objID, objID)); // it ignores source and target in the original
|
|
} else { // Open inventory window
|
|
Common::Point p(_world->getObjAttr(objID, kAttrPosX), _world->getObjAttr(objID, kAttrPosY));
|
|
WindowReference invID = _gui->createInventoryWindow(objID);
|
|
_gui->setWindowTitle(invID, _world->getText(objID, objID, objID));
|
|
_gui->updateWindowInfo(invID, objID, _world->getChildren(objID, true));
|
|
_gui->updateWindow(invID, _world->getObjAttr(objID, kAttrContainerOpen));
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::closeObject(ObjID objID) {
|
|
warning("closeObject: not fully implemented");
|
|
bool success = _gui->tryCloseWindow(getObjWindow(objID));
|
|
return;
|
|
}
|
|
|
|
void MacVentureEngine::checkObject(QueuedObject old) {
|
|
//warning("checkObject: unimplemented");
|
|
bool hasChanged = false;
|
|
debugC(1, kMVDebugMain, "Check Object[%d] parent[%d] x[%d] y[%d]",
|
|
old.object,
|
|
old.parent,
|
|
old.x,
|
|
old.y);
|
|
ObjID id = old.object;
|
|
if (id == 1) {
|
|
if (old.parent != _world->getObjAttr(id, kAttrParentObject)) {
|
|
enqueueObject(kSetToPlayerParent, id);
|
|
}
|
|
if (old.offscreen != _world->getObjAttr(id, kAttrInvisible) ||
|
|
old.invisible != _world->getObjAttr(id, kAttrUnclickable)) {
|
|
updateWindow(findParentWindow(id));
|
|
}
|
|
} else if (old.parent != _world->getObjAttr(id, kAttrParentObject) ||
|
|
old.x != _world->getObjAttr(id, kAttrPosX) ||
|
|
old.y != _world->getObjAttr(id, kAttrPosY)) {
|
|
WindowReference oldWin = getObjWindow(old.parent);
|
|
if (oldWin) {
|
|
_gui->removeChild(oldWin, id);
|
|
hasChanged = true;
|
|
}
|
|
|
|
WindowReference newWin = findParentWindow(id);
|
|
if (newWin) {
|
|
_gui->addChild(newWin, id);
|
|
hasChanged = true;
|
|
}
|
|
} else if (old.offscreen != _world->getObjAttr(id, kAttrInvisible) ||
|
|
old.invisible != _world->getObjAttr(id, kAttrUnclickable)) {
|
|
updateWindow(findParentWindow(id));
|
|
}
|
|
|
|
if (_world->getObjAttr(id, kAttrIsExit)) {
|
|
if (hasChanged ||
|
|
old.hidden != _world->getObjAttr(id, kAttrHiddenExit) ||
|
|
old.exitx != _world->getObjAttr(id, kAttrExitX) ||
|
|
old.exity != _world->getObjAttr(id, kAttrExitY))
|
|
_gui->updateExit(id);
|
|
}
|
|
WindowReference win = getObjWindow(id);
|
|
ObjID cur = id;
|
|
ObjID root = _world->getObjAttr(1, kAttrParentObject);
|
|
while (cur != root) {
|
|
if (cur == 0 || !_world->getObjAttr(cur, kAttrContainerOpen)) break;
|
|
cur = _world->getObjAttr(cur, kAttrParentObject);
|
|
}
|
|
if (cur == root) {
|
|
if (win) return;
|
|
enqueueObject(kOpenWindow, id); //open
|
|
} else {
|
|
if (!win) return;
|
|
enqueueObject(kCloseWindow, id); //close
|
|
}
|
|
|
|
// Update children
|
|
Common::Array<ObjID> children = _world->getChildren(id, true);
|
|
for (uint i = 0; i < children.size(); i++) {
|
|
enqueueObject(kUpdateObject, children[i]);
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::reflectSwap(ObjID fromID, ObjID toID) {
|
|
//warning("reflectSwap: untested");
|
|
WindowReference from = getObjWindow(fromID);
|
|
WindowReference to = getObjWindow(toID);
|
|
WindowReference tmp = to;
|
|
debugC(1, kMVDebugMain, "Swap Object[%d] to Object[%d], from win[%d] to win[%d] ",
|
|
fromID, toID, from, to);
|
|
|
|
if (!to) {
|
|
tmp = from;
|
|
}
|
|
if (tmp) {
|
|
Common::String newTitle = _world->getText(toID, 0, 0); // Ignores src and targ in the original
|
|
_gui->setWindowTitle(tmp, newTitle);
|
|
_gui->updateWindowInfo(tmp, toID, _world->getChildren(toID, true));
|
|
updateWindow(tmp);
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::toggleExits() {
|
|
while (!_selectedObjs.empty()) {
|
|
ObjID obj = _selectedObjs.front();
|
|
_selectedObjs.remove_at(0);
|
|
highlightExit(obj);
|
|
updateWindow(findParentWindow(obj));
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::zoomObject(ObjID objID) {
|
|
warning("zoomObject: unimplemented");
|
|
}
|
|
|
|
ControlAction MacVenture::MacVentureEngine::referenceToAction(ControlType id) {
|
|
switch (id) {
|
|
case MacVenture::kControlExitBox:
|
|
return kActivateObject;//??
|
|
case MacVenture::kControlExamine:
|
|
return kExamine;
|
|
case MacVenture::kControlOpen:
|
|
return kOpen;
|
|
case MacVenture::kControlClose:
|
|
return kClose;
|
|
case MacVenture::kControlSpeak:
|
|
return kSpeak;
|
|
case MacVenture::kControlOperate:
|
|
return kOperate;
|
|
case MacVenture::kControlGo:
|
|
return kGo;
|
|
case MacVenture::kControlHit:
|
|
return kHit;
|
|
case MacVenture::kControlConsume:
|
|
return kConsume;
|
|
default:
|
|
return kNoCommand;
|
|
}
|
|
}
|
|
|
|
// Data retrieval
|
|
|
|
bool MacVentureEngine::isPaused() {
|
|
return _paused;
|
|
}
|
|
|
|
bool MacVentureEngine::needsClickToContinue() {
|
|
return _clickToContinue;
|
|
}
|
|
|
|
Common::String MacVentureEngine::getCommandsPausedString() const {
|
|
return Common::String("Click to continue");
|
|
}
|
|
|
|
Common::String MacVentureEngine::getFilePath(FilePathID id) const {
|
|
const Common::Array<Common::String> &names = _filenames->getStrings();
|
|
if (id <= 3) { // We don't want a file in the subdirectory
|
|
return Common::String(names[id]);
|
|
} else { // We want a game file
|
|
return Common::String(names[3] + "/" + names[id]);
|
|
}
|
|
}
|
|
|
|
bool MacVentureEngine::isOldText() const {
|
|
return _oldTextEncoding;
|
|
}
|
|
|
|
const HuffmanLists * MacVentureEngine::getDecodingHuffman() const {
|
|
return _textHuffman;
|
|
}
|
|
|
|
uint32 MacVentureEngine::randBetween(uint32 min, uint32 max) {
|
|
return _rnd->getRandomNumber(max - min) + min;
|
|
}
|
|
|
|
uint32 MacVentureEngine::getInvolvedObjects() {
|
|
return (_selectedControl ? _globalSettings.cmdArgCnts[_selectedControl - 1] : 3000);
|
|
}
|
|
|
|
Common::Point MacVentureEngine::getObjPosition(ObjID objID) {
|
|
return Common::Point(_world->getObjAttr(objID, kAttrPosX), _world->getObjAttr(objID, kAttrPosY));
|
|
}
|
|
|
|
bool MacVentureEngine::isObjVisible(ObjID objID) {
|
|
return _world->getObjAttr(objID, kAttrInvisible) == 0;
|
|
}
|
|
|
|
bool MacVentureEngine::isObjClickable(ObjID objID) {
|
|
return _world->getObjAttr(objID, kAttrUnclickable) == 0;
|
|
}
|
|
|
|
bool MacVentureEngine::isObjSelected(ObjID objID) {
|
|
int idx = findObjectInArray(objID, _currentSelection);
|
|
return idx != -1;
|
|
}
|
|
|
|
bool MacVentureEngine::isObjExit(ObjID objID) {
|
|
return _world->getObjAttr(objID, kAttrIsExit);
|
|
}
|
|
|
|
bool MacVentureEngine::isHiddenExit(ObjID objID) {
|
|
return _world->getObjAttr(objID, kAttrHiddenExit);
|
|
}
|
|
|
|
Common::Point MacVentureEngine::getObjExitPosition(ObjID objID) {
|
|
uint x = _world->getObjAttr(objID, kAttrExitX);
|
|
uint y = _world->getObjAttr(objID, kAttrExitY);
|
|
return Common::Point(x, y);
|
|
}
|
|
|
|
ObjID MacVentureEngine::getParent(ObjID objID) {
|
|
return _world->getObjAttr(objID, kAttrParentObject);
|
|
}
|
|
|
|
Common::Rect MacVentureEngine::getObjBounds(ObjID objID) {
|
|
Common::Point pos = getObjPosition(objID);
|
|
|
|
WindowReference win = findParentWindow(objID);
|
|
if (win != kNoWindow) { // If it's not in a window YET, we don't really care about the border
|
|
BorderBounds bounds = borderBounds(_gui->getWindowData(win).type); // HACK
|
|
pos.x += bounds.leftOffset;
|
|
pos.y += bounds.topOffset;
|
|
}
|
|
Common::Point measures = _gui->getObjMeasures(objID);
|
|
uint w = measures.x;
|
|
uint h = measures.y;
|
|
return Common::Rect(pos.x, pos.y, pos.x + w, pos.y + h);
|
|
}
|
|
|
|
uint MacVentureEngine::getOverlapPercent(ObjID one, ObjID other) {
|
|
//not the same parent? 0 overlap
|
|
if (_world->getObjAttr(one, kAttrParentObject) !=
|
|
_world->getObjAttr(other, kAttrParentObject))
|
|
return 0;
|
|
|
|
Common::Rect oneBounds = getObjBounds(one);
|
|
Common::Rect otherBounds = getObjBounds(other);
|
|
if (otherBounds.intersects(oneBounds) ||
|
|
oneBounds.intersects(otherBounds))
|
|
{
|
|
uint areaOne = oneBounds.width() * oneBounds.height();
|
|
uint areaOther = otherBounds.width() * otherBounds.height();
|
|
return (areaOther * 100 / areaOne) | 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
WindowReference MacVentureEngine::getObjWindow(ObjID objID) {
|
|
return _gui->getObjWindow(objID);
|
|
}
|
|
|
|
WindowReference MacVentureEngine::findParentWindow(ObjID objID) {
|
|
if (objID == 1) return kSelfWindow;
|
|
ObjID parent = _world->getObjAttr(objID, kAttrParentObject);
|
|
if (parent == 0) return kNoWindow;
|
|
return getObjWindow(parent);
|
|
}
|
|
|
|
Common::Point MacVentureEngine::getDeltaPoint() {
|
|
return _deltaPoint;
|
|
}
|
|
|
|
ObjID MacVentureEngine::getDestObject() {
|
|
return _destObject;
|
|
}
|
|
|
|
ControlAction MacVentureEngine::getSelectedControl() {
|
|
return _selectedControl;
|
|
}
|
|
|
|
// Data loading
|
|
|
|
bool MacVentureEngine::loadGlobalSettings() {
|
|
Common::MacResIDArray resArray;
|
|
Common::SeekableReadStream *res;
|
|
|
|
if ((resArray = _resourceManager->getResIDArray(MKTAG('G', 'N', 'R', 'L'))).size() == 0)
|
|
return false;
|
|
|
|
res = _resourceManager->getResource(MKTAG('G', 'N', 'R', 'L'), kGlobalSettingsID);
|
|
if (res) {
|
|
_globalSettings.numObjects = res->readUint16BE();
|
|
_globalSettings.numGlobals = res->readUint16BE();
|
|
_globalSettings.numCommands = res->readUint16BE();
|
|
_globalSettings.numAttributes = res->readUint16BE();
|
|
_globalSettings.numGroups = res->readUint16BE();
|
|
res->readUint16BE(); // unknown
|
|
_globalSettings.invTop = res->readUint16BE();
|
|
_globalSettings.invLeft = res->readUint16BE();
|
|
_globalSettings.invWidth = res->readUint16BE();
|
|
_globalSettings.invHeight = res->readUint16BE();
|
|
_globalSettings.invOffsetY = res->readUint16BE();
|
|
_globalSettings.invOffsetX = res->readSint16BE();
|
|
_globalSettings.defaultFont = res->readUint16BE();
|
|
_globalSettings.defaultSize = res->readUint16BE();
|
|
|
|
_globalSettings.attrIndices = new uint8[_globalSettings.numAttributes];
|
|
res->read(_globalSettings.attrIndices, _globalSettings.numAttributes);
|
|
|
|
_globalSettings.attrMasks = new uint16[_globalSettings.numAttributes];
|
|
for (int i = 0; i < _globalSettings.numAttributes; i++)
|
|
_globalSettings.attrMasks[i] = res->readUint16BE();
|
|
|
|
_globalSettings.attrShifts = new uint8[_globalSettings.numAttributes];
|
|
res->read(_globalSettings.attrShifts, _globalSettings.numAttributes);
|
|
|
|
_globalSettings.cmdArgCnts = new uint8[_globalSettings.numCommands];
|
|
res->read(_globalSettings.cmdArgCnts, _globalSettings.numCommands);
|
|
|
|
_globalSettings.commands = new uint8[_globalSettings.numCommands];
|
|
res->read(_globalSettings.commands, _globalSettings.numCommands);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MacVentureEngine::loadTextHuffman() {
|
|
Common::MacResIDArray resArray;
|
|
Common::SeekableReadStream *res;
|
|
|
|
if ((resArray = _resourceManager->getResIDArray(MKTAG('G', 'N', 'R', 'L'))).size() == 0)
|
|
return false;
|
|
|
|
res = _resourceManager->getResource(MKTAG('G', 'N', 'R', 'L'), kTextHuffmanTableID);
|
|
if (res) {
|
|
uint32 numEntries = res->readUint16BE();
|
|
res->readUint16BE(); // Skip
|
|
|
|
uint32 *masks = new uint32[numEntries];
|
|
for (uint i = 0; i < numEntries - 1; i++)
|
|
// For some reason there are one lass mask than entries
|
|
masks[i] = res->readUint16BE();
|
|
|
|
uint32 *lengths = new uint32[numEntries];
|
|
for (uint i = 0; i < numEntries; i++)
|
|
lengths[i] = res->readByte();
|
|
|
|
uint32 *values = new uint32[numEntries];
|
|
for (uint i = 0; i < numEntries; i++)
|
|
values[i] = res->readByte();
|
|
|
|
_textHuffman = new HuffmanLists(numEntries, lengths, masks, values);
|
|
debugC(4, kMVDebugMain, "Text is huffman-encoded");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace MacVenture
|