mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-14 05:30:53 +00:00
432fd522d2
This flag is removed for a few reasons: * Engines universally set this flag to true for widths > 320, which made it redundant everywhere; * This flag functioned primarily as a "force 1x scaler" flag, since its behaviour was almost completely undocumented and users would need to figure out that they'd need an explicit non-default scaler set to get a scaler to operate at widths > 320; * (Most importantly) engines should not be in the business of deciding how the backend may choose to render its virtual screen. The choice of rendering behaviour belongs to the user, and the backend, in that order. A nearby future commit restores the default1x scaler behaviour in the SDL backend code for the moment, but in the future it is my hope that there will be a better configuration UI to allow users to specify how they want scaling to work for high resolutions.
1194 lines
30 KiB
C++
1194 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Based on
|
|
* WebVenture (c) 2010, Sean Kasun
|
|
* https://github.com/mrkite/webventure, http://seancode.com/webventure/
|
|
*
|
|
* Used with explicit permission from the author
|
|
*/
|
|
|
|
#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(3, 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;
|
|
_resourceManager = NULL;
|
|
_globalSettings = NULL;
|
|
_gui = NULL;
|
|
_world = 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 (_resourceManager)
|
|
delete _resourceManager;
|
|
|
|
if (_globalSettings)
|
|
delete _globalSettings;
|
|
|
|
if (_gui)
|
|
delete _gui;
|
|
|
|
if (_world)
|
|
delete _world;
|
|
|
|
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);
|
|
|
|
_debugger = new Console(this);
|
|
|
|
// Additional setup.
|
|
debug("MacVentureEngine::init");
|
|
|
|
_resourceManager = new Common::MacResManager();
|
|
if (!_resourceManager->open(getGameFileName()))
|
|
error("ENGINE: Could not open %s as a resource fork", getGameFileName());
|
|
|
|
// Engine-wide loading
|
|
if (!loadGlobalSettings())
|
|
error("ENGINE: 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("ENGINE: Could not load game from slot '%d'", directSaveSlotLoading);
|
|
}
|
|
} else {
|
|
setNewGameState();
|
|
}
|
|
selectControl(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();
|
|
_objQueue.clear();
|
|
_textQueue.clear();
|
|
}
|
|
|
|
void MacVentureEngine::resetGui() {
|
|
_gui->reloadInternals();
|
|
_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);
|
|
if (id == kClickToContinue) {
|
|
_clickToContinue = false;
|
|
_paused = true;
|
|
return;
|
|
}
|
|
|
|
_selectedControl = id;
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
const WindowData &windata = _gui->getWindowData(win);
|
|
|
|
if (shiftPressed) {
|
|
// TODO: Implement shift functionality.
|
|
} else {
|
|
if (_selectedControl && _currentSelection.size() > 0 && getInvolvedObjects() > 1) {
|
|
if (objID == 0) {
|
|
selectPrimaryObject(windata.objRef);
|
|
} else {
|
|
selectPrimaryObject(objID);
|
|
}
|
|
preparedToRun();
|
|
} else {
|
|
if (objID == 0) {
|
|
unselectAll();
|
|
objID = win;
|
|
}
|
|
if (objID > 0) {
|
|
int currentObjectIndex = findObjectInArray(objID, _currentSelection);
|
|
|
|
if (currentObjectIndex >= 0)
|
|
unselectAll();
|
|
|
|
if (isDoubleClick) {
|
|
selectObject(objID);
|
|
_destObject = objID;
|
|
setDeltaPoint(Common::Point(0, 0));
|
|
if (!_cmdReady) {
|
|
selectControl(kActivateObject);
|
|
_cmdReady = true;
|
|
}
|
|
} else {
|
|
selectObject(objID);
|
|
if (getInvolvedObjects() == 1)
|
|
_cmdReady = true;
|
|
}
|
|
preparedToRun();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::handleObjectDrop(ObjID objID, Common::Point delta, ObjID newParent) {
|
|
_destObject = newParent;
|
|
setDeltaPoint(delta);
|
|
selectControl(kMoveObject);
|
|
refreshReady();
|
|
preparedToRun();
|
|
}
|
|
|
|
void MacVentureEngine::setDeltaPoint(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();
|
|
|
|
_prepared = false;
|
|
warning("Show text entry: not fully tested");
|
|
return true;
|
|
}
|
|
|
|
void MacVentureEngine::setTextInput(Common::String content) {
|
|
_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);
|
|
|
|
delete[] fileName;
|
|
delete res;
|
|
|
|
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(3, kMVDebugMain, "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 (isGameRunning() && _world->isObjActive(obj)) {
|
|
if (_scriptEngine->runControl(_selectedControl, obj, _destObject, _deltaPoint)) {
|
|
_haltedInSelection = true;
|
|
return true;
|
|
}
|
|
updateState(true);
|
|
}
|
|
}
|
|
if (_selectedControl == 1) {
|
|
_gameChanged = false;
|
|
} else if (isGameRunning()) {
|
|
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:
|
|
// Empty in the original.
|
|
break;
|
|
}
|
|
}
|
|
if (pause && delay > 0) {
|
|
warning("Sound pausing not yet tested. Pausing for %d", delay);
|
|
g_system->delayMillis(delay);
|
|
preparedToRun();
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::updateControls() {
|
|
selectControl(kNoCommand);
|
|
_gui->clearControls();
|
|
toggleExits();
|
|
resetVars();
|
|
}
|
|
|
|
void MacVentureEngine::resetVars() {
|
|
selectControl(kNoCommand);
|
|
_currentSelection.clear();
|
|
_destObject = 0;
|
|
setDeltaPoint(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])) {
|
|
// TODO: Needs further testing, but it doesn't seem necessary.
|
|
//unselectAll();
|
|
}
|
|
}
|
|
if (findObjectInArray(objID, _currentSelection) == -1) {
|
|
_currentSelection.push_back(objID);
|
|
highlightExit(objID);
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::unselectObject(ObjID objID) {
|
|
int idxCur = findObjectInArray(objID, _currentSelection);
|
|
if (idxCur != -1) {
|
|
_currentSelection.remove_at(idxCur);
|
|
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;
|
|
return _decodingNamingArticles->getString(ndx);
|
|
}
|
|
|
|
Common::String MacVentureEngine::getNoun(ObjID ndx) {
|
|
return _decodingIndirectArticles->getString(ndx);
|
|
}
|
|
|
|
void MacVentureEngine::highlightExit(ObjID objID) {
|
|
// TODO: It seems unnecessary since the GUI checks whether an object
|
|
// is selected, which includes exits.
|
|
warning("STUB: highlightExit");
|
|
}
|
|
|
|
void MacVentureEngine::selectPrimaryObject(ObjID objID) {
|
|
if (objID == _destObject) {
|
|
return;
|
|
}
|
|
int idx;
|
|
debugC(4, kMVDebugMain, "Select primary object (%d)", objID);
|
|
if (_destObject > 0 &&
|
|
(idx = findObjectInArray(_destObject, _currentSelection)) != -1) {
|
|
unselectAll();
|
|
}
|
|
_destObject = objID;
|
|
if (findObjectInArray(_destObject, _currentSelection) == -1) {
|
|
selectObject(_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(3, 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");
|
|
_gui->tryCloseWindow(getObjWindow(objID));
|
|
return;
|
|
}
|
|
|
|
void MacVentureEngine::checkObject(QueuedObject old) {
|
|
bool hasChanged = false;
|
|
debugC(3, 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) {
|
|
WindowReference from = getObjWindow(fromID);
|
|
WindowReference to = getObjWindow(toID);
|
|
WindowReference tmp = to;
|
|
debugC(3, 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() {
|
|
Common::Array<ObjID> exits = _currentSelection;
|
|
while (!exits.empty()) {
|
|
ObjID obj = exits.front();
|
|
exits.remove_at(0);
|
|
highlightExit(obj);
|
|
updateWindow(findParentWindow(obj));
|
|
}
|
|
}
|
|
|
|
void MacVentureEngine::zoomObject(ObjID objID) {
|
|
warning("zoomObject: unimplemented");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool MacVentureEngine::isGameRunning() {
|
|
return (_gameState == kGameStateInit || _gameState == kGameStatePlaying);
|
|
}
|
|
|
|
ControlAction MacVenture::MacVentureEngine::referenceToAction(ControlType id) {
|
|
switch (id) {
|
|
case MacVenture::kControlExitBox:
|
|
return kActivateObject;//?? Like this in the original
|
|
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 {
|
|
if (id <= 3) { // We don't want a file in the subdirectory
|
|
return _filenames->getString(id);
|
|
} else { // We want a game file
|
|
return _filenames->getString(3) + "/" + _filenames->getString(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() {
|
|
// If there is no valid control selected, we return a number too big
|
|
// to be useful. There is no control that uses that many objects.
|
|
return (_selectedControl ? getGlobalSettings()._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) {
|
|
// If it's not the same parent, there's 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 (areaOne != 0) ? (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;
|
|
|
|
if ((resArray = _resourceManager->getResIDArray(MKTAG('G', 'N', 'R', 'L'))).size() == 0)
|
|
return false;
|
|
|
|
Common::SeekableReadStream *res;
|
|
res = _resourceManager->getResource(MKTAG('G', 'N', 'R', 'L'), kGlobalSettingsID);
|
|
if (res) {
|
|
_globalSettings = new GlobalSettings();
|
|
_globalSettings->loadSettings(res);
|
|
delete res;
|
|
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");
|
|
|
|
delete res;
|
|
delete[] masks;
|
|
delete[] lengths;
|
|
delete[] values;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Global Settings
|
|
GlobalSettings::GlobalSettings() {
|
|
}
|
|
|
|
GlobalSettings::~GlobalSettings() {
|
|
|
|
}
|
|
|
|
void GlobalSettings::loadSettings(Common::SeekableReadStream *dataStream) {
|
|
_numObjects = dataStream->readUint16BE();
|
|
_numGlobals = dataStream->readUint16BE();
|
|
_numCommands = dataStream->readUint16BE();
|
|
_numAttributes = dataStream->readUint16BE();
|
|
_numGroups = dataStream->readUint16BE();
|
|
dataStream->readUint16BE(); // unknown
|
|
_invTop = dataStream->readUint16BE();
|
|
_invLeft = dataStream->readUint16BE();
|
|
_invWidth = dataStream->readUint16BE();
|
|
_invHeight = dataStream->readUint16BE();
|
|
_invOffsetY = dataStream->readUint16BE();
|
|
_invOffsetX = dataStream->readSint16BE();
|
|
_defaultFont = dataStream->readUint16BE();
|
|
_defaultSize = dataStream->readUint16BE();
|
|
|
|
uint8 *attrIndices = new uint8[_numAttributes];
|
|
dataStream->read(attrIndices, _numAttributes);
|
|
_attrIndices = Common::Array<uint8>(attrIndices, _numAttributes);
|
|
delete[] attrIndices;
|
|
|
|
for (int i = 0; i < _numAttributes; i++) {
|
|
_attrMasks.push_back(dataStream->readUint16BE());
|
|
}
|
|
|
|
uint8 *attrShifts = new uint8[_numAttributes];
|
|
dataStream->read(attrShifts, _numAttributes);
|
|
_attrShifts = Common::Array<uint8>(attrShifts, _numAttributes);
|
|
delete[] attrShifts;
|
|
|
|
uint8 *cmdArgCnts = new uint8[_numCommands];
|
|
dataStream->read(cmdArgCnts, _numCommands);
|
|
_cmdArgCnts = Common::Array<uint8>(cmdArgCnts, _numCommands);
|
|
delete[] cmdArgCnts;
|
|
|
|
uint8 *commands = new uint8[_numCommands];
|
|
dataStream->read(commands, _numCommands);
|
|
_commands = Common::Array<uint8>(commands, _numCommands);
|
|
delete[] commands;
|
|
}
|
|
|
|
} // End of namespace MacVenture
|