aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk/cstime_game.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mohawk/cstime_game.cpp')
-rw-r--r--engines/mohawk/cstime_game.cpp1236
1 files changed, 1236 insertions, 0 deletions
diff --git a/engines/mohawk/cstime_game.cpp b/engines/mohawk/cstime_game.cpp
new file mode 100644
index 0000000000..5b197599e8
--- /dev/null
+++ b/engines/mohawk/cstime_game.cpp
@@ -0,0 +1,1236 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "mohawk/cstime_game.h"
+#include "mohawk/cstime_ui.h"
+#include "mohawk/cstime_view.h"
+#include "mohawk/resource.h"
+#include "mohawk/sound.h"
+#include "common/events.h"
+
+namespace Mohawk {
+
+// read a null-terminated string from a stream
+static Common::String readString(Common::SeekableReadStream *stream) {
+ Common::String ret;
+ while (!stream->eos()) {
+ byte in = stream->readByte();
+ if (!in)
+ break;
+ ret += in;
+ }
+ return ret;
+}
+
+// read a rect from a stream
+Common::Rect readRect(Common::SeekableReadStream *stream) {
+ Common::Rect rect;
+
+ rect.left = stream->readSint16BE();
+ rect.top = stream->readSint16BE();
+ rect.right = stream->readSint16BE();
+ rect.bottom = stream->readSint16BE();
+
+ return rect;
+}
+
+void Region::loadFrom(Common::SeekableReadStream *stream) {
+ uint16 rectCount = stream->readUint16BE();
+ if (!rectCount) {
+ // TODO: why this?
+ stream->skip(2);
+ rectCount = stream->readUint16BE();
+ }
+ for (uint i = 0; i < rectCount; i++) {
+ Common::Rect rect = readRect(stream);
+ if (!rect.isValidRect())
+ continue;
+ _rects.push_back(rect);
+ }
+}
+
+bool Region::containsPoint(Common::Point &pos) const {
+ for (uint i = 0; i < _rects.size(); i++)
+ if (_rects[i].contains(pos))
+ return true;
+
+ return false;
+}
+
+CSTimeChar::CSTimeChar(MohawkEngine_CSTime *vm, CSTimeScene *scene, uint id) : _vm(vm), _scene(scene), _id(id) {
+ _resting = true;
+ _flappingState = 0xffff;
+ _surfingState = 0;
+
+ _NIS = NULL;
+ _restFeature = NULL;
+ _talkFeature = NULL;
+
+ _talkFeature1 = NULL;
+ _talkFeature2 = NULL;
+ _talkFeature3 = NULL;
+ _lastTime1 = 0;
+ _lastTime2 = 0;
+ _lastTime3 = 0;
+
+ _playingWaveId = 0;
+}
+
+CSTimeChar::~CSTimeChar() {
+}
+
+void CSTimeChar::idle() {
+ if (!_unknown2)
+ return;
+
+ if (_flappingState == 1) {
+ idleTalk();
+ return;
+ }
+
+ if (!_NIS)
+ idleAmbients();
+}
+
+void CSTimeChar::setupAmbientAnims(bool onetime) {
+ CSTimeConversation *conv = _vm->getCase()->getCurrConversation();
+ if (_unknown1 == 0xffff || !_unknown2 || !_ambients.size() || !_resting || !_enabled ||
+ (conv->getState() != (uint)~0 && conv->getSourceChar() == _id)) {
+ setupRestPos();
+ _resting = true;
+ return;
+ }
+
+ removeChr();
+ for (uint i = 0; i < _ambients.size(); i++) {
+ // FIXME: check ambient condition
+ uint32 flags = kFeatureSortStatic;
+ if (_ambients[i].delay != 0xffff) {
+ flags |= kFeatureNewNoLoop;
+ if (onetime)
+ flags |= kFeatureNewDisableOnReset;
+ }
+ installAmbientAnim(i, flags);
+ }
+}
+
+void CSTimeChar::idleAmbients() {
+ if (_flappingState != 0xffff)
+ return;
+
+ for (uint i = 0; i < _ambients.size(); i++) {
+ if (!_ambients[i].feature)
+ continue;
+ uint16 delay = _ambients[i].delay;
+ if (delay == 0xffff)
+ continue;
+ uint32 now = _vm->_system->getMillis();
+ if (now < _ambients[i].nextTime)
+ continue;
+ _ambients[i].feature->resetFeatureScript(1, 0);
+ _ambients[i].nextTime = now + delay;
+ }
+}
+
+void CSTimeChar::stopAmbients(bool restpos) {
+ for (uint i = 0; i < _ambients.size(); i++) {
+ if (!_ambients[i].feature)
+ continue;
+ _vm->getView()->removeFeature(_ambients[i].feature, true);
+ _ambients[i].feature = NULL;
+ }
+
+ if (restpos)
+ setupRestPos();
+}
+
+void CSTimeChar::setupRestPos() {
+ if (_unknown1 == 0xffff || !_unknown1 || !_unknown2)
+ return;
+
+ if (!_restFeature) {
+ uint id = 0; // FIXME
+ uint32 flags = kFeatureSortStatic | kFeatureNewNoLoop | kFeatureNewDisableOnReset;
+ Feature *feature = _vm->getView()->installViewFeature(getChrBaseId() + id, flags, NULL);
+ // FIXME: fix priorities
+ _restFeature = feature;
+ } else {
+ _restFeature->resetFeatureScript(1, 0);
+ }
+
+ // FIXME: fix more priorities
+}
+
+void CSTimeChar::removeChr() {
+ if (_unknown1 == 0xffff || !_unknown1)
+ return;
+
+ if (_talkFeature) {
+ _vm->getView()->removeFeature(_talkFeature, true);
+ _vm->getView()->removeFeature(_talkFeature3, true);
+ if (_talkFeature1)
+ _vm->getView()->removeFeature(_talkFeature1, true);
+ if (_unknown1 > 1)
+ _vm->getView()->removeFeature(_talkFeature2, true);
+ }
+
+ if (_restFeature)
+ _vm->getView()->removeFeature(_restFeature, true);
+
+ _talkFeature1 = NULL;
+ _talkFeature2 = NULL;
+ _talkFeature3 = NULL;
+
+ _talkFeature = NULL;
+ _restFeature = NULL;
+}
+
+uint16 CSTimeChar::getChrBaseId() {
+ return _scene->getSceneId() + (_id + 1) * 200;
+}
+
+uint CSTimeChar::getScriptCount() {
+ static uint bases[4] = { 0, 10, 13, 21 };
+ assert(_unknown1 < 4);
+ return bases[_unknown1] + _ambients.size() + _unknown3;
+}
+
+void CSTimeChar::playNIS(uint16 id) {
+ if (_NIS)
+ removeNIS();
+ stopAmbients(false);
+ removeChr();
+ uint32 flags = kFeatureSortStatic | kFeatureNewNoLoop;
+ _NIS = _vm->getView()->installViewFeature(getChrTypeScriptBase() + id + _ambients.size(), flags, NULL);
+ // FIXME: fix priorities
+}
+
+bool CSTimeChar::NISIsDone() {
+ return (_NIS->_data.paused || !_NIS->_data.enabled);
+}
+
+void CSTimeChar::removeNIS() {
+ if (!_NIS)
+ return;
+ _vm->getView()->removeFeature(_NIS, true);
+ _NIS = NULL;
+}
+
+void CSTimeChar::startFlapping(uint16 id) {
+ if (!_unknown2)
+ return;
+
+ _scene->_activeChar = this;
+ if (_restFeature) {
+ _vm->getView()->removeFeature(_restFeature, true);
+ _restFeature = NULL;
+ }
+ stopAmbients(false);
+ setupTalk();
+ _flappingState = 1;
+ playFlapWave(id);
+}
+
+void CSTimeChar::interruptFlapping() {
+ if (_playingWaveId)
+ _vm->_sound->stopSound(_playingWaveId);
+ // TODO: kill any other (preload) sound
+ _waveStatus = 'q';
+}
+
+void CSTimeChar::installAmbientAnim(uint id, uint32 flags) {
+ Feature *feature = _vm->getView()->installViewFeature(getChrTypeScriptBase() + id, flags, NULL);
+ // FIXME: fix priorities
+
+ _ambients[id].feature = feature;
+ _ambients[id].nextTime = _vm->_system->getMillis() + _ambients[id].delay;
+}
+
+uint16 CSTimeChar::getChrTypeScriptBase() {
+ static uint bases[4] = { 0, 10, 13, 21 };
+ assert(_unknown1 < 4);
+ return bases[_unknown1] + getChrBaseId();
+}
+
+void CSTimeChar::playFlapWave(uint16 id) {
+ _playingWaveId = id;
+ _vm->_sound->playSound(id, Audio::Mixer::kMaxChannelVolume, false, &_cueList);
+ _nextCue = 0;
+ _waveStatus = 'b';
+}
+
+void CSTimeChar::updateWaveStatus() {
+ // This is a callback in the original, handling audio events.
+ assert(_playingWaveId);
+
+ // FIXME: state 's' for .. something? stopped?
+ if (!_vm->_sound->isPlaying(_playingWaveId)) {
+ _waveStatus = 'q';
+ return;
+ }
+
+ uint samples = _vm->_sound->getNumSamplesPlayed(_playingWaveId);
+ for (uint i = _nextCue; i < _cueList.pointCount; i++) {
+ if (_cueList.points[i].sampleFrame > samples)
+ return;
+ if (_cueList.points[i].name.empty())
+ warning("cue %d reached but was empty", i);
+ else
+ _waveStatus = _cueList.points[i].name[0];
+ _nextCue = i + 1;
+ }
+}
+
+void CSTimeChar::setupTalk() {
+ if (_unknown1 == 0xffff || !_unknown1 || !_unknown2 || _talkFeature)
+ return;
+
+ uint32 flags = kFeatureSortStatic | kFeatureNewNoLoop | kFeatureNewDisableOnReset;
+ _talkFeature = _vm->getView()->installViewFeature(getChrBaseId() + (_enabled ? 1 : 14), flags, NULL);
+
+ _talkFeature3 = _vm->getView()->installViewFeature(getChrBaseId() + (_enabled ? 4 : 15), flags, NULL);
+ if (_enabled) {
+ _talkFeature1 = _vm->getView()->installViewFeature(getChrBaseId() + 2, flags, NULL);
+ if (_unknown1 > 1) {
+ _talkFeature2 = _vm->getView()->installViewFeature(getChrBaseId() + 10, flags, NULL);
+ }
+ }
+ // FIXME: fix priorities
+}
+
+void CSTimeChar::idleTalk() {
+ updateWaveStatus();
+
+ if (_waveStatus == 'q') {
+ if (_surfingState) {
+ // FIXME
+ _surfingState = 0;
+ } else {
+ // FIXME
+ _playingWaveId = 0;
+ }
+ stopFlapping();
+ return;
+ }
+
+ if (_waveStatus == 's' && _surfingState) {
+ // FIXME
+ _waveStatus = 'q';
+ return;
+ }
+
+ CSTimeView *view = _vm->getView();
+
+ if (_enabled && view->getTime() > _lastTime1) {
+ _lastTime1 = view->getTime() + 2000 + _vm->_rnd->getRandomNumberRng(0, 1000);
+ if (_talkFeature1)
+ _talkFeature1->resetFeatureScript(1, getChrBaseId() + 2 + _vm->_rnd->getRandomNumberRng(0, 1));
+ }
+
+ if (_enabled && view->getTime() > _lastTime2) {
+ _lastTime2 = view->getTime() + 3000 + _vm->_rnd->getRandomNumberRng(0, 1000);
+ if (_talkFeature2)
+ _talkFeature2->resetFeatureScript(1, getChrBaseId() + 10 + _vm->_rnd->getRandomNumberRng(0, 2));
+ }
+
+ if (_waveStatus == 'c') {
+ if (_talkFeature3)
+ _talkFeature3->resetFeatureScript(1, getChrBaseId() + (_enabled ? 4 : 15));
+ } else if (view->getTime() > _lastTime3) {
+ _lastTime3 = view->getTime() + 100;
+ if (_talkFeature3)
+ _talkFeature3->resetFeatureScript(1, getChrBaseId() + (_enabled ? 4 : 15) + _vm->_rnd->getRandomNumberRng(1, 5));
+ }
+
+ // FIXME: more animations
+}
+
+void CSTimeChar::stopFlapping() {
+ _flappingState = 0;
+ removeChr();
+ // FIXME: stupid hardcoded hack for case 5
+ setupAmbientAnims(true);
+}
+
+CSTimeConversation::CSTimeConversation(MohawkEngine_CSTime *vm, uint id) : _vm(vm), _id(id) {
+ clear();
+
+ Common::SeekableReadStream *convStream = _vm->getResource(ID_CONV, id * 10 + 500);
+
+ _nameId = convStream->readUint16BE();
+ _greeting = convStream->readUint16BE();
+ _greeting2 = convStream->readUint16BE();
+
+ uint16 qarIds[8];
+ for (uint i = 0; i < 8; i++)
+ qarIds[i] = convStream->readUint16BE();
+
+ delete convStream;
+
+ for (uint i = 0; i < 8; i++) {
+ // FIXME: are they always in order?
+ if (qarIds[i] == 0xffff)
+ continue;
+ _qars.push_back(CSTimeQaR());
+ CSTimeQaR &qar = _qars.back();
+ loadQaR(qar, qarIds[i]);
+ }
+}
+
+void CSTimeConversation::start() {
+ uint16 greeting = _greeting;
+
+ if (_talkCount > 1)
+ greeting = _greeting2;
+
+ _state = 2;
+
+ if (greeting == 0xffff) {
+ finishProcessingQaR();
+ return;
+ }
+
+ CSTimeEvent event;
+ event.type = kCSTimeEventCharStartFlapping;
+ event.param1 = _sourceChar;
+ event.param2 = greeting;
+ _vm->addEvent(event);
+}
+
+void CSTimeConversation::finishProcessingQaR() {
+ if (_state == 2) {
+ _vm->getInterface()->getInventoryDisplay()->hide();
+ _vm->getInterface()->clearTextLine();
+ selectItemsToDisplay();
+ display();
+ return;
+ }
+
+ if (_nextToProcess == 0xffff)
+ return;
+
+ uint qarIndex = _itemsToDisplay[_nextToProcess];
+ CSTimeQaR &qar = _qars[qarIndex];
+
+ if (!qar.nextQaRsId) {
+ end(true);
+ _nextToProcess = 0xffff;
+ return;
+ }
+
+ if (qar.responseStringId != 0xffff) {
+ _vm->addEventList(qar.events);
+ }
+
+ if (qar.nextQaRsId == 0xffff) {
+ _qars.remove_at(qarIndex);
+ _vm->getInterface()->clearDialogLine(_nextToProcess);
+ _nextToProcess = 0xffff;
+ return;
+ }
+
+ loadQaR(qar, qar.nextQaRsId);
+ if (qar.unknown2)
+ qar.finished = true;
+ _vm->getInterface()->displayDialogLine(qar.questionStringId, _nextToProcess, qar.finished ? 13 : 32);
+
+ _nextToProcess = 0xffff;
+}
+
+void CSTimeConversation::end(bool useLastClicked, bool runEvents) {
+ if (runEvents) {
+ uint entry = _currEntry;
+ if (!useLastClicked)
+ entry = _itemsToDisplay.size() - 1;
+ CSTimeQaR &qar = _qars[_itemsToDisplay[entry]];
+ _vm->addEventList(qar.events);
+ if (_sourceChar != 0xffff)
+ _vm->getCase()->getCurrScene()->getChar(_sourceChar)->setupAmbientAnims(true);
+ }
+
+ CSTimeInterface *interface = _vm->getInterface();
+ CSTimeInventoryDisplay *invDisplay = interface->getInventoryDisplay();
+ if (invDisplay->getState() == 4) {
+ invDisplay->hide();
+ invDisplay->setState(0);
+ }
+
+ setState(~0);
+ _currHover = ~0;
+
+ interface->clearTextLine();
+ interface->clearDialogArea();
+ invDisplay->show();
+
+ // TODO: stupid case 20 stuff
+}
+
+void CSTimeConversation::display() {
+ _vm->getInterface()->clearDialogArea();
+
+ for (uint i = 0; i < _itemsToDisplay.size(); i++) {
+ // FIXME: some rect stuff?
+
+ CSTimeQaR &qar = _qars[_itemsToDisplay[i]];
+ _vm->getInterface()->displayDialogLine(qar.questionStringId, i, qar.finished ? 13 : 32);
+ }
+
+ _state = 1;
+}
+
+void CSTimeConversation::selectItemsToDisplay() {
+ _itemsToDisplay.clear();
+
+ for (uint i = 0; i < _qars.size(); i++) {
+ if (_qars[i].unknown1 != 0xffff && !_vm->getCase()->checkConvCondition(_qars[i].unknown1))
+ continue;
+ if (_itemsToDisplay.size() == 5)
+ error("Too many conversation paths");
+ _itemsToDisplay.push_back(i);
+ }
+}
+
+void CSTimeConversation::mouseDown(Common::Point &pos) {
+ if (_vm->getInterface()->getInventoryDisplay()->getState() == 4)
+ return;
+
+ // TODO: case 20 rect check
+
+ for (uint i = 0; i < _itemsToDisplay.size(); i++) {
+ Common::Rect thisRect = _vm->getInterface()->_dialogTextRect;
+ thisRect.top += 1 + i*15;
+ thisRect.bottom = thisRect.top + 15;
+ if (!thisRect.contains(pos))
+ continue;
+
+ _currEntry = i;
+ highlightLine(i);
+ _vm->getInterface()->cursorSetShape(5, true);
+ break;
+ }
+}
+
+void CSTimeConversation::mouseMove(Common::Point &pos) {
+ // TODO: case 20 rect check
+
+ bool mouseIsDown = _vm->getEventManager()->getButtonState() & 1;
+
+ for (uint i = 0; i < _itemsToDisplay.size(); i++) {
+ Common::Rect thisRect = _vm->getInterface()->_dialogTextRect;
+ thisRect.top += 1 + i*15;
+ thisRect.bottom = thisRect.top + 15;
+ if (!thisRect.contains(pos))
+ continue;
+
+ if (mouseIsDown) {
+ if (i != _currEntry)
+ break;
+ highlightLine(i);
+ }
+
+ _vm->getInterface()->cursorOverHotspot();
+ _currHover = i;
+ return;
+ }
+
+ if (_currHover != (uint)~0) {
+ if (_vm->getInterface()->cursorGetState() != 3) {
+ _vm->getInterface()->cursorSetShape(1, true);
+ if (_vm->getInterface()->getInventoryDisplay()->getState() != 4)
+ unhighlightLine(_currHover);
+ }
+ _currHover = ~0;
+ }
+}
+
+void CSTimeConversation::mouseUp(Common::Point &pos) {
+ if (_vm->getInterface()->getInventoryDisplay()->getState() == 4)
+ return;
+
+ if (_currEntry == (uint)~0)
+ return;
+
+ // TODO: case 20 rect check
+
+ CSTimeQaR &qar = _qars[_itemsToDisplay[_currEntry]];
+ Common::Rect thisRect = _vm->getInterface()->_dialogTextRect;
+ thisRect.top += 1 + _currEntry*15;
+ thisRect.bottom = thisRect.top + 15;
+ if (!thisRect.contains(pos))
+ return;
+
+ if (qar.responseStringId != 0xffff) {
+ CSTimeEvent newEvent;
+ newEvent.type = kCSTimeEventCharStartFlapping;
+ newEvent.param1 = _sourceChar;
+ newEvent.param2 = qar.responseStringId;
+ _vm->addEvent(newEvent);
+ _nextToProcess = _currEntry;
+ return;
+ }
+
+ if (!qar.nextQaRsId) {
+ _vm->getInterface()->cursorChangeShape(1);
+ end(true);
+ return;
+ }
+
+ _vm->addEventList(qar.events);
+ _nextToProcess = _currEntry;
+}
+
+void CSTimeConversation::setAsked(uint qar, uint entry) {
+ assert(qar < 8 && entry < 5);
+ _asked[qar][entry] = true;
+}
+
+void CSTimeConversation::clear() {
+ _state = ~0;
+ _talkCount = 0;
+ _sourceChar = 0xffff;
+ _currHover = ~0;
+ _currEntry = ~0;
+ _nextToProcess = 0xffff;
+ for (uint i = 0; i < 8; i++)
+ for (uint j = 0; j < 5; j++)
+ _asked[i][j] = false;
+}
+
+void CSTimeConversation::loadQaR(CSTimeQaR &qar, uint16 id) {
+ Common::SeekableReadStream *qarsStream = _vm->getResource(ID_QARS, id);
+ qar.finished = false;
+ qar.unknown1 = qarsStream->readUint16BE();
+ qar.questionStringId = qarsStream->readUint16BE();
+ qar.responseStringId = qarsStream->readUint16BE();
+ qar.unknown2 = qarsStream->readUint16BE();
+ qar.nextQaRsId = qarsStream->readUint16BE();
+ uint16 numEvents = qarsStream->readUint16BE();
+ for (uint j = 0; j < numEvents; j++) {
+ CSTimeEvent event;
+ event.type = qarsStream->readUint16BE();
+ event.param1 = qarsStream->readUint16BE();
+ event.param2 = qarsStream->readUint16BE();
+ qar.events.push_back(event);
+ }
+}
+
+void CSTimeConversation::highlightLine(uint line) {
+ CSTimeQaR &qar = _qars[_itemsToDisplay[line]];
+ _vm->getInterface()->displayDialogLine(qar.questionStringId, line, 244);
+}
+
+void CSTimeConversation::unhighlightLine(uint line) {
+ CSTimeQaR &qar = _qars[_itemsToDisplay[line]];
+ _vm->getInterface()->displayDialogLine(qar.questionStringId, line, qar.finished ? 13 : 32);
+}
+
+CSTimeCase::CSTimeCase(MohawkEngine_CSTime *vm, uint id) : _vm(vm), _id(id) {
+ _vm->loadResourceFile(Common::String::format("Cases/C%dText", id));
+ // We load this early, so we can use the text for debugging.
+ loadRolloverText();
+
+ _vm->loadResourceFile(Common::String::format("Cases/C%dInfo", id));
+ Common::SeekableReadStream *caseInfoStream = _vm->getResource(ID_CINF, 1);
+ uint16 numScenes = caseInfoStream->readUint16BE();
+ uint16 numInvObjs = caseInfoStream->readUint16BE();
+ uint16 numConversations = caseInfoStream->readUint16BE();
+ for (uint i = 0; i < 3; i++)
+ _noteFeatureId[i] = caseInfoStream->readUint16BE();
+ delete caseInfoStream;
+
+ debug("Loading %d inventory objects...", numInvObjs);
+ for (uint i = 0; i < numInvObjs; i++)
+ _inventoryObjs.push_back(loadInventoryObject(i));
+
+ _vm->loadResourceFile(Common::String::format("Cases/C%dArt", id));
+ _vm->loadResourceFile(Common::String::format("Cases/C%dDlog", id));
+
+ debug("Loading %d scenes...", numScenes);
+ for (uint i = 0; i < numScenes; i++)
+ _scenes.push_back(new CSTimeScene(_vm, this, i + 1));
+
+ debug("Loading %d conversations...", numConversations);
+ for (uint i = 0; i < numConversations; i++)
+ _conversations.push_back(new CSTimeConversation(_vm, i));
+
+ assert(!_conversations.empty());
+ _currConv = _conversations[0];
+
+ _currScene = ~0;
+}
+
+CSTimeCase::~CSTimeCase() {
+}
+
+void CSTimeCase::loadRolloverText() {
+ Common::SeekableReadStream *stringStream = _vm->getResource(ID_STRL, 9100);
+ while (stringStream->pos() < stringStream->size())
+ _rolloverText.push_back(readString(stringStream));
+ for (uint i = 0; i < _rolloverText.size(); i++)
+ debug("string %d: '%s'", i, _rolloverText[i].c_str());
+ delete stringStream;
+}
+
+CSTimeInventoryObject *CSTimeCase::loadInventoryObject(uint id) {
+ CSTimeInventoryObject *invObj = new CSTimeInventoryObject;
+ invObj->feature = NULL;
+ invObj->id = id;
+
+ Common::SeekableReadStream *invObjStream = _vm->getResource(ID_INVO, id + 1);
+ uint16 numHotspots = invObjStream->readUint16BE();
+ invObj->stringId = invObjStream->readUint16BE();
+ invObj->hotspotId = invObjStream->readUint16BE();
+ invObj->featureId = invObjStream->readUint16BE();
+ invObj->canTake = invObjStream->readUint16BE();
+ debug(" invobj '%s', hotspot id %d, feature id %d, can take %d", _rolloverText[invObj->stringId].c_str(), invObj->hotspotId, invObj->featureId, invObj->canTake);
+ uint16 numConsumableLocations = invObjStream->readUint16BE();
+ debug(" Loading %d consumable locations...", numConsumableLocations);
+ for (uint i = 0; i < numConsumableLocations; i++) {
+ CSTimeLocation location;
+ location.sceneId = invObjStream->readUint16BE();
+ location.hotspotId = invObjStream->readUint16BE();
+ invObj->locations.push_back(location);
+ }
+ uint16 numEvents = invObjStream->readUint16BE();
+ debug(" Loading %d events...", numEvents);
+ for (uint i = 0; i < numEvents; i++) {
+ CSTimeEvent event;
+ event.type = invObjStream->readUint16BE();
+ event.param1 = invObjStream->readUint16BE();
+ event.param2 = invObjStream->readUint16BE();
+ invObj->events.push_back(event);
+ }
+ debug(" Loading %d hotspots...", numHotspots);
+ for (uint i = 0; i < numHotspots; i++) {
+ CSTimeInventoryHotspot hotspot;
+ hotspot.sceneId = invObjStream->readUint16BE();
+ hotspot.hotspotId = invObjStream->readUint16BE();
+ hotspot.stringId = invObjStream->readUint16BE();
+ uint16 numHotspotEvents = invObjStream->readUint16BE();
+ debug(" scene %d, hotspot %d, string id %d, with %d hotspot events", hotspot.sceneId, hotspot.hotspotId, hotspot.stringId, numHotspotEvents);
+ for (uint j = 0; j < numHotspotEvents; j++) {
+ CSTimeEvent event;
+ event.type = invObjStream->readUint16BE();
+ event.param1 = invObjStream->readUint16BE();
+ event.param2 = invObjStream->readUint16BE();
+ hotspot.events.push_back(event);
+ }
+ invObj->hotspots.push_back(hotspot);
+ }
+ delete invObjStream;
+
+ return invObj;
+}
+
+CSTimeScene *CSTimeCase::getCurrScene() {
+ return _scenes[_currScene - 1];
+}
+
+CSTimeScene::CSTimeScene(MohawkEngine_CSTime *vm, CSTimeCase *case_, uint id) : _vm(vm), _case(case_), _id(id) {
+ _activeChar = NULL;
+ _currHotspot = ~0;
+ _hoverHotspot = ~0;
+ load();
+}
+
+void CSTimeScene::load() {
+ Common::SeekableReadStream *sceneStream = _vm->getResource(ID_SCEN, _id + 1000);
+ _unknown1 = sceneStream->readUint16BE();
+ _unknown2 = sceneStream->readUint16BE();
+ _helperId = sceneStream->readUint16BE();
+ _bubbleType = sceneStream->readUint16BE();
+ uint16 numHotspots = sceneStream->readUint16BE();
+ _numObjects = sceneStream->readUint16BE();
+ debug("Scene: unknowns %d, %d, %d, bubble type %d, %d objects", _unknown1, _unknown2, _helperId, _bubbleType, _numObjects);
+
+ uint16 numChars = sceneStream->readUint16BE();
+ uint16 numEvents = sceneStream->readUint16BE();
+ debug(" Loading %d events...", numEvents);
+ for (uint i = 0; i < numEvents; i++) {
+ CSTimeEvent event;
+ event.type = sceneStream->readUint16BE();
+ event.param1 = sceneStream->readUint16BE();
+ event.param2 = sceneStream->readUint16BE();
+ _events.push_back(event);
+ }
+ uint16 numEvents2 = sceneStream->readUint16BE();
+ debug(" Loading %d events2...", numEvents2);
+ for (uint i = 0; i < numEvents2; i++) {
+ CSTimeEvent event;
+ event.type = sceneStream->readUint16BE();
+ event.param1 = sceneStream->readUint16BE();
+ event.param2 = sceneStream->readUint16BE();
+ _events2.push_back(event);
+ }
+ debug(" Loading %d chars...", numChars);
+ for (uint i = 0; i < numChars; i++) {
+ CSTimeChar *chr = new CSTimeChar(_vm, this, i);
+ if (!_activeChar)
+ _activeChar = chr;
+ chr->_enabled = true;
+ chr->_unknown1 = sceneStream->readUint16BE();
+ chr->_unknown2 = sceneStream->readUint16BE();
+ uint16 numAmbients = sceneStream->readUint16BE();
+ chr->_unknown3 = sceneStream->readUint16BE();
+ debug(" unknowns %d, %d, %d, with %d ambients", chr->_unknown1, chr->_unknown2, chr->_unknown3, numAmbients);
+ for (uint j = 0; j < numAmbients; j++) {
+ CSTimeAmbient ambient;
+ ambient.delay = sceneStream->readUint16BE();
+ ambient.feature = NULL;
+ chr->_ambients.push_back(ambient);
+ }
+ _chars.push_back(chr);
+ }
+ debug(" Loading %d hotspots...", numHotspots);
+ for (uint i = 0; i < numHotspots; i++) {
+ CSTimeHotspot hotspot;
+ hotspot.stringId = sceneStream->readUint16BE();
+ hotspot.invObjId = sceneStream->readUint16BE();
+ hotspot.cursor = sceneStream->readUint16BE();
+ hotspot.state = sceneStream->readUint16BE();
+ uint16 numHotspotEvents = sceneStream->readUint16BE();
+ debug(" hotspot '%s', inv obj %d, cursor %d, state %d, with %d hotspot events", _case->getRolloverText(hotspot.stringId).c_str(), hotspot.invObjId, hotspot.cursor, hotspot.state, numHotspotEvents);
+ for (uint j = 0; j < numHotspotEvents; j++) {
+ CSTimeEvent event;
+ event.type = sceneStream->readUint16BE();
+ event.param1 = sceneStream->readUint16BE();
+ event.param2 = sceneStream->readUint16BE();
+ hotspot.events.push_back(event);
+ }
+ _hotspots.push_back(hotspot);
+ }
+ delete sceneStream;
+
+ Common::SeekableReadStream *hotspotStream = _vm->getResource(ID_HOTS, _id + 1100);
+ for (uint i = 0; i < _hotspots.size(); i++) {
+ _hotspots[i].region.loadFrom(hotspotStream);
+ }
+ delete hotspotStream;
+}
+
+void CSTimeScene::installGroup() {
+ uint16 resourceId = getSceneId();
+ _vm->getView()->installGroup(resourceId, _numObjects, 0, true, resourceId);
+
+ for (uint i = 0; i < _chars.size(); i++) {
+ uint count = _chars[i]->getScriptCount();
+ if (!count)
+ continue;
+ _vm->getView()->installGroup(resourceId, count, 0, true, _chars[i]->getChrBaseId());
+ }
+}
+
+void CSTimeScene::buildScene() {
+ uint16 resourceId = getSceneId();
+
+ _vm->getView()->installBG(resourceId);
+
+ for (uint i = 0; i < _numObjects; i++) {
+ if (!_case->checkObjectCondition(i)) {
+ _objectFeatures.push_back(NULL);
+ continue;
+ }
+
+ // FIXME: reset object if it already exists
+ // FIXME: deal with NULL
+ uint32 flags = kFeatureSortStatic | kFeatureNewNoLoop | kFeatureNewDisableOnReset;
+ assert(flags == 0x4C00000);
+ Feature *feature = _vm->getView()->installViewFeature(resourceId + i, flags, NULL);
+ _objectFeatures.push_back(feature);
+ }
+}
+
+void CSTimeScene::leave() {
+ for (uint i = 0; i < _objectFeatures.size(); i++) {
+ if (_objectFeatures[i] == NULL)
+ continue;
+ _vm->getView()->removeFeature(_objectFeatures[i], true);
+ _objectFeatures[i] = NULL;
+ }
+
+ for (uint i = 0; i < _chars.size(); i++) {
+ _chars[i]->stopAmbients(false);
+ _chars[i]->removeChr();
+ _chars[i]->removeNIS();
+ }
+
+ _vm->getView()->removeGroup(getSceneId());
+}
+
+uint16 CSTimeScene::getSceneId() {
+ // FIXME: there are a lot of special cases
+ uint16 resourceId = 10000 + 2000 * (_id - 1);
+ return resourceId;
+}
+
+void CSTimeScene::mouseDown(Common::Point &pos) {
+ CSTimeConversation *conv = _vm->getCase()->getCurrConversation();
+ bool convActive = (conv->getState() != (uint)~0);
+ bool helpActive = (_vm->getInterface()->getHelp()->getState() != (uint)~0);
+ if (convActive || helpActive) {
+ bool foundPoint = false;
+ for (uint i = 0; i < _hotspots.size(); i++) {
+ CSTimeHotspot &hotspot = _hotspots[i];
+ if (!hotspot.region.containsPoint(pos))
+ continue;
+ foundPoint = true;
+
+ if (!convActive) {
+ // In help mode, we ignore clicks on any help hotspot.
+ if (!hotspotContainsEvent(i, kCSTimeEventStartHelp))
+ break;
+ _currHotspot = ~0;
+ return;
+ }
+
+ // In conversation mode, we ignore clicks which would restart the current conversation.
+ for (uint j = 0; j < hotspot.events.size(); j++) {
+ if (hotspot.events[j].type != kCSTimeEventStartConversation)
+ continue;
+ // FIXME: check that the conversation *is* the current one
+ _currHotspot = ~0;
+ return;
+ }
+
+ break;
+ }
+
+ if (foundPoint) {
+ // We found a valid hotspot and we didn't ignore it, stop the conversation/help.
+ if (convActive) {
+ conv->end(false);
+ } else {
+ _vm->getInterface()->getHelp()->end();
+ }
+ }
+ } else {
+ // FIXME: check symbols
+ }
+
+ // FIXME: return if sailing puzzle
+
+ _currHotspot = ~0;
+ for (uint i = 0; i < _hotspots.size(); i++) {
+ CSTimeHotspot &hotspot = _hotspots[i];
+ if (!hotspot.region.containsPoint(pos))
+ continue;
+ if (hotspot.state != 1)
+ continue;
+ mouseDownOnHotspot(i);
+ break;
+ }
+
+ if (_currHotspot == (uint)~0)
+ _vm->getInterface()->cursorSetShape(4, false);
+}
+
+void CSTimeScene::mouseMove(Common::Point &pos) {
+ // TODO: if we're in sailing puzzle, return
+
+ bool mouseIsDown = _vm->getEventManager()->getButtonState() & 1;
+
+ if (_vm->getInterface()->getState() == kCSTimeInterfaceStateDragStart) {
+ Common::Point grabPoint = _vm->getInterface()->getGrabPoint();
+ if (mouseIsDown && (abs(grabPoint.x - pos.x) > 2 || abs(grabPoint.y - pos.y) > 2)) {
+ if (_vm->getInterface()->grabbedFromInventory()) {
+ _vm->getInterface()->startDragging(_vm->getInterface()->getInventoryDisplay()->getLastDisplayedClicked());
+ } else {
+ CSTimeHotspot &hotspot = _hotspots[_currHotspot];
+ if (_vm->_haveInvItem[hotspot.invObjId]) {
+ _vm->getInterface()->setState(kCSTimeInterfaceStateNormal);
+ } else {
+ // FIXME: special-casing for cases 9, 13, 15, 18 19
+
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[hotspot.invObjId];
+ if (invObj->feature)
+ error("Inventory object already had feature when dragging from scene");
+ uint16 id = 9000 + (invObj->id - 1);
+ // FIXME: 0x2000 is set! help?
+ uint32 flags = kFeatureNewNoLoop | 0x2000;
+ invObj->feature = _vm->getView()->installViewFeature(id, flags, &grabPoint);
+ _vm->getInterface()->startDragging(hotspot.invObjId);
+ }
+ }
+ }
+ }
+
+ for (uint i = 0; i < _hotspots.size(); i++) {
+ CSTimeHotspot &hotspot = _hotspots[i];
+ if (hotspot.state != 1)
+ continue;
+ if (!hotspot.region.containsPoint(pos))
+ continue;
+
+ if (_vm->getInterface()->getState() == kCSTimeInterfaceStateDragging) {
+ // Only show a hotspot here if the dragged object can be dropped onto it.
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[_vm->getInterface()->getDraggedNum()];
+ bool showText = false;
+ for (uint j = 0; j < invObj->hotspots.size(); j++) {
+ if (invObj->hotspots[j].sceneId != _id)
+ continue;
+ if (invObj->hotspots[j].hotspotId != i)
+ continue;
+ showText = true;
+ }
+ if (!showText)
+ continue;
+ } else {
+ // If we're not dragging but the mouse is down, ignore all hotspots
+ // except the one which was clicked on.
+ if (mouseIsDown && (i != _currHotspot))
+ continue;
+ }
+
+ if (i != _hoverHotspot);
+ _vm->getInterface()->clearTextLine();
+ cursorOverHotspot(i);
+ _hoverHotspot = i;
+ return;
+ }
+
+ if (_vm->getInterface()->getState() != kCSTimeInterfaceStateDragging) {
+ switch (_vm->getInterface()->cursorGetShape()) {
+ case 2:
+ case 13:
+ _vm->getInterface()->cursorSetShape(1);
+ break;
+ case 5:
+ case 14:
+ _vm->getInterface()->cursorSetShape(4);
+ break;
+ case 11:
+ _vm->getInterface()->cursorSetShape(10);
+ break;
+ }
+ }
+
+ if (_hoverHotspot == (uint)~0)
+ return;
+
+ CSTimeConversation *conv = _case->getCurrConversation();
+ CSTimeHelp *help = _vm->getInterface()->getHelp();
+ if (conv->getState() != (uint)~0 && conv->getState() != 0) {
+ Common::String text = "Talking to " + _case->getRolloverText(conv->getNameId());
+ _vm->getInterface()->displayTextLine(text);
+ } else if (help->getState() != (uint)~0 && help->getState() != 0) {
+ Common::String text = "Talking to " + _case->getRolloverText(0);
+ _vm->getInterface()->displayTextLine(text);
+ } else if (_vm->getInterface()->getState() == kCSTimeInterfaceStateDragging) {
+ // TODO: check symbols before this
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[_vm->getInterface()->getDraggedNum()];
+ Common::String text = "Holding " + _case->getRolloverText(invObj->stringId);
+ _vm->getInterface()->displayTextLine(text);
+ } else {
+ _vm->getInterface()->clearTextLine();
+ }
+
+ _hoverHotspot = (uint)~0;
+}
+
+void CSTimeScene::mouseUp(Common::Point &pos) {
+ // TODO: if sailing puzzle is active, return
+
+ if (_currHotspot == (uint)~0) {
+ if (_vm->getInterface()->cursorGetShape() == 4)
+ _vm->getInterface()->cursorChangeShape(1);
+ return;
+ }
+
+ if (_vm->getInterface()->getState() == kCSTimeInterfaceStateDragStart)
+ _vm->getInterface()->setState(kCSTimeInterfaceStateNormal);
+
+ CSTimeHotspot &hotspot = _hotspots[_currHotspot];
+ if (hotspot.region.containsPoint(pos) && hotspot.state == 1) {
+ mouseUpOnHotspot(_currHotspot);
+ } else {
+ if (_vm->getInterface()->cursorGetShape() == 4 || _vm->getInterface()->cursorGetShape() == 14)
+ _vm->getInterface()->cursorChangeShape(1);
+ }
+}
+
+void CSTimeScene::idle() {
+ // TODO: if sailing puzzle is active, idle it instead
+
+ for (uint i = 0; i < _chars.size(); i++)
+ _chars[i]->idle();
+}
+
+void CSTimeScene::setupAmbientAnims() {
+ for (uint i = 0; i < _chars.size(); i++)
+ _chars[i]->setupAmbientAnims(false);
+}
+
+void CSTimeScene::idleAmbientAnims() {
+ if (_vm->NISIsRunning())
+ return;
+
+ for (uint i = 0; i < _chars.size(); i++)
+ _chars[i]->idleAmbients();
+}
+
+bool CSTimeScene::eventIsActive() {
+ return _vm->NISIsRunning() /* TODO || _vm->soundIsPlaying()*/ || _vm->getCurrentEventType() == kCSTimeEventWaitForClick
+ || _activeChar->_flappingState != 0xffff || _vm->getInterface()->getState() == 4;
+}
+
+void CSTimeScene::cursorOverHotspot(uint id) {
+ CSTimeHotspot &hotspot = _hotspots[id];
+
+ if (!_vm->getInterface()->cursorGetState())
+ return;
+
+ if (_vm->getInterface()->getState() == kCSTimeInterfaceStateDragging) {
+ uint16 stringId = 0xffff;
+
+ uint16 draggedId = _vm->getInterface()->getDraggedNum();
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[draggedId];
+ for (uint j = 0; j < invObj->hotspots.size(); j++) {
+ if (invObj->hotspots[j].sceneId != _id)
+ continue;
+ if (invObj->hotspots[j].hotspotId != id)
+ continue;
+ stringId = invObj->hotspots[j].stringId;
+ break;
+ }
+
+ if (hotspot.stringId != 0xFFFF) {
+ Common::String textLine;
+ if (false) {
+ // FIXME: special-case time cuffs
+ } else {
+ bool isChar = (hotspot.cursor == 1) && (draggedId != TIME_CUFFS_ID);
+ textLine = (isChar ? "Give " : "Use ");
+ textLine += _case->getRolloverText(invObj->stringId);
+ textLine += (isChar ? " to " : " on ");
+ textLine += _case->getRolloverText(stringId);
+ }
+ _vm->getInterface()->displayTextLine(textLine);
+ }
+ } else {
+ if (hotspot.stringId != 0xFFFF)
+ _vm->getInterface()->displayTextLine(_case->getRolloverText(hotspot.stringId));
+ }
+
+ if (_vm->getEventManager()->getButtonState() & 1) {
+ if (_vm->getInterface()->getState() != kCSTimeInterfaceStateDragStart && _vm->getInterface()->getState() != kCSTimeInterfaceStateDragging) {
+ CSTimeHotspot &currHotspot = _hotspots[_currHotspot];
+ if (currHotspot.cursor == 2)
+ _vm->getInterface()->cursorSetShape(14);
+ else
+ _vm->getInterface()->cursorSetShape(5);
+ }
+ } else {
+ if (hotspot.cursor == 2)
+ _vm->getInterface()->cursorSetShape(13);
+ else if (_vm->getInterface()->cursorGetShape() != 8 && _vm->getInterface()->cursorGetShape() != 11)
+ _vm->getInterface()->cursorSetShape(2);
+ }
+}
+
+void CSTimeScene::mouseDownOnHotspot(uint id) {
+ CSTimeHotspot &hotspot = _hotspots[id];
+
+ _currHotspot = id;
+
+ if (hotspot.invObjId == 0xffff || _vm->_haveInvItem[hotspot.invObjId]) {
+ if (hotspot.cursor == 2)
+ _vm->getInterface()->cursorChangeShape(14);
+ else
+ _vm->getInterface()->cursorChangeShape(5);
+ return;
+ }
+
+ _vm->getInterface()->cursorSetShape(8, true);
+ _vm->getInterface()->setGrabPoint();
+ _vm->getInterface()->setState(kCSTimeInterfaceStateDragStart);
+
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[hotspot.invObjId];
+ _vm->getInterface()->displayTextLine("Pick up " + _case->getRolloverText(invObj->stringId));
+}
+
+void CSTimeScene::mouseUpOnHotspot(uint id) {
+ CSTimeHotspot &hotspot = _hotspots[id];
+
+ _vm->addEventList(hotspot.events);
+ if (_vm->getInterface()->cursorGetShape() == 8 || (!hotspot.events.empty() && _vm->getInterface()->cursorGetShape() == 11))
+ return;
+ if (hotspot.cursor == 2)
+ _vm->getInterface()->cursorChangeShape(13);
+ else
+ _vm->getInterface()->cursorChangeShape(2);
+}
+
+bool CSTimeScene::hotspotContainsEvent(uint id, uint16 eventType) {
+ CSTimeHotspot &hotspot = _hotspots[id];
+
+ for (uint i = 0; i < hotspot.events.size(); i++)
+ if (hotspot.events[i].type == eventType)
+ return true;
+
+ return false;
+}
+
+void CSTimeScene::setCursorForCurrentPoint() {
+ Common::Point mousePos = _vm->getEventManager()->getMousePos();
+
+ for (uint i = 0; i < _hotspots.size(); i++) {
+ if (!_hotspots[i].region.containsPoint(mousePos))
+ continue;
+ if (_hotspots[i].state != 1)
+ continue;
+ if (_hotspots[i].cursor == 2) {
+ _vm->getInterface()->cursorSetShape(13);
+ } else switch (_vm->getInterface()->cursorGetShape()) {
+ case 8:
+ break;
+ case 12:
+ _vm->getInterface()->cursorSetShape(11);
+ break;
+ default:
+ _vm->getInterface()->cursorSetShape(2);
+ break;
+ }
+ return;
+ }
+
+ _vm->getInterface()->cursorSetShape(1);
+}
+
+void CSTimeScene::drawHotspots() {
+ for (uint i = 0; i < _hotspots.size(); i++) {
+ for (uint j = 0; j < _hotspots[i].region._rects.size(); j++) {
+ _vm->_gfx->drawRect(_hotspots[i].region._rects[j], 10 + 5*i);
+ }
+ }
+}
+
+const Common::Array<CSTimeEvent> &CSTimeScene::getEvents(bool second) {
+ if (second)
+ return _events2;
+ else
+ return _events;
+}
+
+} // End of namespace Mohawk