scummvm/engines/mohawk/myst_card.cpp
2020-03-28 07:38:39 +01:00

461 lines
13 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "mohawk/myst_card.h"
#include "mohawk/myst_areas.h"
#include "mohawk/myst_graphics.h"
#include "mohawk/resource.h"
namespace Mohawk {
MystCard::MystCard(MohawkEngine_Myst *vm, uint16 id) :
_vm(vm),
_id(id),
_hoverResource(nullptr),
_activeResource(nullptr),
_clickedResource(nullptr) {
loadView();
loadResources();
loadCursorHints();
}
void MystCard::enter() {
// Handle images
drawBackground();
// Handle sound
_vm->applySoundBlock(_soundBlock);
if (_flags & kMystZipDestination)
_vm->_gameState->addZipDest(_vm->_stack->getStackId(), _id);
// Run the entrance script (if present)
runInitScript();
// Update the images of each area too
drawResourceImages();
for (uint16 i = 0; i < _resources.size(); i++)
_resources[i]->handleCardChange();
}
void MystCard::leave() {
runExitScript();
}
MystCard::~MystCard() {
for (uint32 i = 0; i < _resources.size(); i++)
delete _resources[i];
}
uint16 MystCard::getId() const {
return _id;
}
void MystCard::loadView() {
debugC(kDebugView, "Loading Card View: %d", _id);
Common::SeekableReadStream *viewStream = _vm->getResource(ID_VIEW, _id);
// Card Flags
_flags = viewStream->readUint16LE();
debugC(kDebugView, "Flags: 0x%04X", _flags);
// The Image Block (Reminiscent of Riven PLST resources)
uint16 conditionalImageCount = viewStream->readUint16LE();
debugC(kDebugView, "Conditional Image Count: %d", conditionalImageCount);
if (conditionalImageCount != 0) {
for (uint16 i = 0; i < conditionalImageCount; i++) {
MystCondition conditionalImage;
debugC(kDebugView, "\tImage %d:", i);
conditionalImage.var = viewStream->readUint16LE();
debugC(kDebugView, "\t\tVar: %d", conditionalImage.var);
uint16 numStates = viewStream->readUint16LE();
debugC(kDebugView, "\t\tNumber of States: %d", numStates);
for (uint16 j = 0; j < numStates; j++) {
conditionalImage.values.push_back(viewStream->readUint16LE());
debugC(kDebugView, "\t\tState %d -> Value %d", j, conditionalImage.values[j]);
}
_conditionalImages.push_back(conditionalImage);
}
_mainImage = 0;
} else {
_mainImage = viewStream->readUint16LE();
debugC(kDebugView, "Main Image: %d", _mainImage);
}
// The Sound Block (Reminiscent of Riven SLST resources)
_soundBlock = _vm->readSoundBlock(viewStream);
// Resources that scripts can call upon
uint16 scriptResCount = viewStream->readUint16LE();
debugC(kDebugView, "Script Resource Count: %d", scriptResCount);
for (uint16 i = 0; i < scriptResCount; i++) {
ScriptResource scriptResource;
debugC(kDebugView, "\tResource %d:", i);
scriptResource.type = (ScriptResourceType) viewStream->readUint16LE();
debugC(kDebugView, "\t\t Type: %d", scriptResource.type);
switch (scriptResource.type) {
case kResourceImage:
debugC(kDebugView, "\t\t\t\t= Image");
break;
case kResourceSound:
debugC(kDebugView, "\t\t\t\t= Sound");
break;
case kResourceSwitch:
debugC(kDebugView, "\t\t\t\t= Resource Switch");
break;
case kResourceImageNoCache:
debugC(kDebugView, "\t\t\t\t= Image - Caching disabled");
break;
case kResourceSoundNoCache:
debugC(kDebugView, "\t\t\t\t= Sound - Caching disabled");
break;
default:
debugC(kDebugView, "\t\t\t\t= Unknown");
warning("Unknown script resource type '%d' in card '%d'", scriptResource.type, _id);
break;
}
if (scriptResource.type == kResourceSwitch) {
scriptResource.switchVar = viewStream->readUint16LE();
debugC(kDebugView, "\t\t Var: %d", scriptResource.switchVar);
uint16 count = viewStream->readUint16LE();
debugC(kDebugView, "\t\t Resource List Count: %d", count);
scriptResource.switchResourceType = (ScriptResourceType) viewStream->readUint16LE();
debugC(kDebugView, "\t\t u0: %d", scriptResource.switchResourceType);
for (uint16 j = 0; j < count; j++) {
scriptResource.switchResourceIds.push_back(viewStream->readSint16LE());
debugC(kDebugView, "\t\t Resource List %d: %d", j, scriptResource.switchResourceIds[j]);
}
} else {
scriptResource.id = viewStream->readUint16LE();
debugC(kDebugView, "\t\t Id: %d", scriptResource.id);
}
_scriptResources.push_back(scriptResource);
}
// Identifiers for other resources. 0 if non existent. There is always an RLST.
_resourceListId = viewStream->readUint16LE();
if (!_resourceListId)
error("RLST Index missing");
_hintResourceId = viewStream->readUint16LE();
_initScriptId = viewStream->readUint16LE();
_exitScriptId = viewStream->readUint16LE();
delete viewStream;
// Precache Card Resources
uint32 cacheImageType;
if (_vm->isGameVariant(GF_ME))
cacheImageType = ID_PICT;
else
cacheImageType = ID_WDIB;
// Precache Image Block data
if (!_conditionalImages.empty()) {
for (uint16 i = 0; i < _conditionalImages.size(); i++) {
uint16 value = _vm->_stack->getVar(_conditionalImages[i].var);
_vm->cachePreload(cacheImageType, _conditionalImages[i].values[value]);
}
} else {
_vm->cachePreload(cacheImageType, _mainImage);
}
// Precache Sound Block data
if (_soundBlock.sound > 0)
_vm->cachePreload(ID_MSND, _soundBlock.sound);
else if (_soundBlock.sound == kMystSoundActionConditional) {
uint16 value = _vm->_stack->getVar(_soundBlock.soundVar);
if (_soundBlock.soundList[value].action > 0) {
_vm->cachePreload(ID_MSND, _soundBlock.soundList[value].action);
}
}
// Precache Script Resources
for (uint16 i = 0; i < _scriptResources.size(); i++) {
ScriptResourceType type;
int16 id;
if (_scriptResources[i].type == kResourceSwitch) {
type = _scriptResources[i].switchResourceType;
uint16 value = _vm->_stack->getVar(_scriptResources[i].switchVar);
id = _scriptResources[i].switchResourceIds[value];
} else {
type = _scriptResources[i].type;
id = _scriptResources[i].id;
}
if (id < 0) continue;
switch (type) {
case kResourceImage:
_vm->cachePreload(cacheImageType, id);
break;
case kResourceSound:
_vm->cachePreload(ID_MSND, id);
break;
default:
// The other resource types should not be cached
break;
}
}
}
void MystCard::loadCursorHints() {
if (!_hintResourceId) {
debugC(kDebugHint, "No HINT Present");
return;
}
debugC(kDebugHint, "Loading Cursor Hints:");
Common::SeekableReadStream *hintStream = _vm->getResource(ID_HINT, _id);
uint16 cursorHintCount = hintStream->readUint16LE();
debugC(kDebugHint, "Cursor Hint Count: %d", cursorHintCount);
for (uint16 i = 0; i < cursorHintCount; i++) {
MystCursorHint hint;
debugC(kDebugHint, "Cursor Hint %d:", i);
hint.id = hintStream->readUint16LE();
debugC(kDebugHint, "\tId: %d", hint.id);
hint.cursor = hintStream->readSint16LE();
debugC(kDebugHint, "\tCursor: %d", hint.cursor);
if (hint.cursor == -1) {
debugC(kDebugHint, "\tConditional Cursor Hints:");
hint.variableHint.var = hintStream->readUint16LE();
debugC(kDebugHint, "\tVar: %d", hint.variableHint.var);
uint16 numStates = hintStream->readUint16LE();
debugC(kDebugHint, "\tNumber of States: %d", numStates);
for (uint16 j = 0; j < numStates; j++) {
hint.variableHint.values.push_back(hintStream->readUint16LE());
debugC(kDebugHint, "\t\t State %d: Cursor %d", j, hint.variableHint.values[j]);
}
} else {
hint.variableHint.var = 0;
}
_cursorHints.push_back(hint);
}
delete hintStream;
}
void MystCard::loadResources() {
if (!_resourceListId) {
debugC(kDebugResource, "No RLST present");
return;
}
Common::SeekableReadStream *rlstStream = _vm->getResource(ID_RLST, _resourceListId);
uint16 resourceCount = rlstStream->readUint16LE();
debugC(kDebugResource, "RLST Resource Count: %d", resourceCount);
for (uint16 i = 0; i < resourceCount; i++) {
debugC(kDebugResource, "Resource #%d:", i);
_resources.push_back(_vm->loadResource(rlstStream, nullptr));
}
delete rlstStream;
}
uint16 MystCard::getBackgroundImageId() {
uint16 imageToDraw = 0;
if (_conditionalImages.empty())
imageToDraw = _mainImage;
else {
for (uint16 i = 0; i < _conditionalImages.size(); i++) {
uint16 varValue = _vm->_stack->getVar(_conditionalImages[i].var);
if (varValue < _conditionalImages[i].values.size())
imageToDraw = _conditionalImages[i].values[varValue];
}
}
return imageToDraw;
}
void MystCard::drawBackground() {
_vm->_gfx->copyImageToBackBuffer(getBackgroundImageId(), Common::Rect(0, 0, 544, 332));
}
void MystCard::runInitScript() {
if (!_initScriptId) {
debugC(kDebugINIT, "No INIT Present");
return;
}
debugC(kDebugINIT, "Running INIT script");
Common::SeekableReadStream *initStream = _vm->getResource(ID_INIT, _initScriptId);
MystScript script = _vm->_stack->readScript(initStream, kMystScriptInit);
delete initStream;
_vm->_stack->runScript(script);
}
void MystCard::runExitScript() {
if (!_exitScriptId) {
debugC(kDebugEXIT, "No EXIT Present");
return;
}
debugC(kDebugEXIT, "Running EXIT script");
Common::SeekableReadStream *exitStream = _vm->getResource(ID_EXIT, _exitScriptId);
MystScript script = _vm->_stack->readScript(exitStream, kMystScriptExit);
delete exitStream;
_vm->_stack->runScript(script);
}
void MystCard::drawResourceRects() {
for (uint16 i = 0; i < _resources.size(); i++) {
_resources[i]->getRect().debugPrint(0);
_resources[i]->drawBoundingRect();
}
}
void MystCard::updateActiveResource(const Common::Point &mouse) {
_activeResource = nullptr;
for (uint16 i = 0; i < _resources.size(); i++) {
if (_resources[i]->contains(mouse) && _resources[i]->canBecomeActive()) {
_activeResource = _resources[i];
break;
}
}
}
MystArea *MystCard::forceUpdateClickedResource(const Common::Point &mouse) {
updateActiveResource(mouse);
_clickedResource = _activeResource;
return _clickedResource;
}
void MystCard::updateResourcesForInput(const Common::Point &mouse, bool mouseClicked, bool mouseMoved) {
// Tell previous resource the mouse is no longer hovering it
if (_hoverResource && !_hoverResource->contains(mouse)) {
_hoverResource->handleMouseLeave();
_hoverResource = nullptr;
}
for (uint16 i = 0; i < _resources.size(); i++) {
if (_resources[i]->contains(mouse) && _resources[i]->hasType(kMystAreaHover)
&& _hoverResource != _resources[i]) {
_hoverResource = static_cast<MystAreaHover *>(_resources[i]);
_hoverResource->handleMouseEnter();
}
}
if (!mouseClicked && _clickedResource) {
if (_clickedResource->isEnabled()) {
_clickedResource->handleMouseUp();
}
_clickedResource = nullptr;
} else if (mouseMoved && _clickedResource) {
if (_clickedResource->isEnabled()) {
_clickedResource->handleMouseDrag();
}
} else if (mouseClicked && !_clickedResource) {
if (_activeResource && _activeResource->isEnabled()) {
_clickedResource = _activeResource;
_clickedResource->handleMouseDown();
}
}
}
int16 MystCard::getActiveResourceCursor() {
if (!_hintResourceId) {
// Default to the main cursor when no hints are present
return -1;
}
// Check all the cursor hints to see if we're in a hotspot that contains a hint.
for (uint16 i = 0; i < _cursorHints.size(); i++) {
if (_activeResource && _resources[_cursorHints[i].id] == _activeResource && _activeResource->isEnabled()) {
if (_cursorHints[i].cursor == -1) {
uint16 var_value = _vm->_stack->getVar(_cursorHints[i].variableHint.var);
if (var_value >= _cursorHints[i].variableHint.values.size())
warning("Variable %d Out of Range in variable HINT Resource %d", _cursorHints[i].variableHint.var,
i);
else {
uint16 cursor = _cursorHints[i].variableHint.values[var_value];
if (cursor == 0)
return -1;
else
return cursor;
}
} else {
if (_cursorHints[i].cursor == 0)
return -1;
else
return _cursorHints[i].cursor;
}
}
}
return -1;
}
void MystCard::setResourceEnabled(uint16 resourceIndex, bool enable) {
if (resourceIndex < _resources.size()) {
_resources[resourceIndex]->setEnabled(enable);
} else
warning("Attempt to change unknown resource enable state");
}
void MystCard::drawResourceImages() {
for (uint16 i = 0; i < _resources.size(); i++)
if (_resources[i]->isDrawSubimages())
_resources[i]->drawDataToScreen();
}
void MystCard::redrawArea(uint16 var, bool updateScreen) {
for (uint16 i = 0; i < _resources.size(); i++)
if (_resources[i]->hasType(kMystAreaImageSwitch) && _resources[i]->getImageSwitchVar() == var)
_vm->redrawResource(static_cast<MystAreaImageSwitch *>(_resources[i]), updateScreen);
}
bool MystCard::isDraggingResource() const {
return _clickedResource != nullptr;
}
void MystCard::resetClickedResource() {
_clickedResource = nullptr;
}
} // End of namespace Mohawk