aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/mohawk/console.cpp51
-rw-r--r--engines/mohawk/console.h15
-rw-r--r--engines/mohawk/cstime.cpp529
-rw-r--r--engines/mohawk/cstime.h191
-rw-r--r--engines/mohawk/cstime_cases.cpp241
-rw-r--r--engines/mohawk/cstime_cases.h47
-rw-r--r--engines/mohawk/cstime_game.cpp1236
-rw-r--r--engines/mohawk/cstime_game.h287
-rw-r--r--engines/mohawk/cstime_ui.cpp1186
-rw-r--r--engines/mohawk/cstime_ui.h254
-rw-r--r--engines/mohawk/cstime_view.cpp537
-rw-r--r--engines/mohawk/cstime_view.h97
-rw-r--r--engines/mohawk/detection.cpp5
-rw-r--r--engines/mohawk/graphics.cpp33
-rw-r--r--engines/mohawk/graphics.h18
-rw-r--r--engines/mohawk/module.mk5
16 files changed, 4731 insertions, 1 deletions
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 67bc77fb9d..146fc5e77b 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -31,6 +31,7 @@
#include "mohawk/riven.h"
#include "mohawk/riven_external.h"
#include "mohawk/livingbooks.h"
+#include "mohawk/cstime.h"
#include "mohawk/sound.h"
#include "mohawk/video.h"
@@ -703,4 +704,54 @@ bool LivingBooksConsole::Cmd_ChangePage(int argc, const char **argv) {
return true;
}
+CSTimeConsole::CSTimeConsole(MohawkEngine_CSTime *vm) : GUI::Debugger(), _vm(vm) {
+ DCmd_Register("playSound", WRAP_METHOD(CSTimeConsole, Cmd_PlaySound));
+ DCmd_Register("stopSound", WRAP_METHOD(CSTimeConsole, Cmd_StopSound));
+ DCmd_Register("drawImage", WRAP_METHOD(CSTimeConsole, Cmd_DrawImage));
+ DCmd_Register("drawSubimage", WRAP_METHOD(CSTimeConsole, Cmd_DrawSubimage));
+}
+
+CSTimeConsole::~CSTimeConsole() {
+}
+
+bool CSTimeConsole::Cmd_PlaySound(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: playSound <value>\n");
+ return true;
+ }
+
+ _vm->_sound->stopSound();
+ _vm->_sound->playSound((uint16)atoi(argv[1]));
+ return false;
+}
+
+bool CSTimeConsole::Cmd_StopSound(int argc, const char **argv) {
+ DebugPrintf("Stopping Sound\n");
+
+ _vm->_sound->stopSound();
+ return true;
+}
+
+bool CSTimeConsole::Cmd_DrawImage(int argc, const char **argv) {
+ if (argc == 1) {
+ DebugPrintf("Usage: drawImage <value>\n");
+ return true;
+ }
+
+ _vm->_gfx->copyAnimImageToScreen((uint16)atoi(argv[1]));
+ _vm->_system->updateScreen();
+ return false;
+}
+
+bool CSTimeConsole::Cmd_DrawSubimage(int argc, const char **argv) {
+ if (argc < 3) {
+ DebugPrintf("Usage: drawSubimage <value> <subimage>\n");
+ return true;
+ }
+
+ _vm->_gfx->copyAnimSubImageToScreen((uint16)atoi(argv[1]), (uint16)atoi(argv[2]));
+ _vm->_system->updateScreen();
+ return false;
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/console.h b/engines/mohawk/console.h
index 41949c3d06..f199856cab 100644
--- a/engines/mohawk/console.h
+++ b/engines/mohawk/console.h
@@ -33,6 +33,7 @@ namespace Mohawk {
class MohawkEngine_Myst;
class MohawkEngine_Riven;
class MohawkEngine_LivingBooks;
+class MohawkEngine_CSTime;
class MystConsole : public GUI::Debugger {
public:
@@ -99,6 +100,20 @@ private:
bool Cmd_ChangePage(int argc, const char **argv);
};
+class CSTimeConsole : public GUI::Debugger {
+public:
+ CSTimeConsole(MohawkEngine_CSTime *vm);
+ virtual ~CSTimeConsole(void);
+
+private:
+ MohawkEngine_CSTime *_vm;
+
+ bool Cmd_PlaySound(int argc, const char **argv);
+ bool Cmd_StopSound(int argc, const char **argv);
+ bool Cmd_DrawImage(int argc, const char **argv);
+ bool Cmd_DrawSubimage(int argc, const char **argv);
+};
+
} // End of namespace Mohawk
#endif
diff --git a/engines/mohawk/cstime.cpp b/engines/mohawk/cstime.cpp
new file mode 100644
index 0000000000..36599c9d33
--- /dev/null
+++ b/engines/mohawk/cstime.cpp
@@ -0,0 +1,529 @@
+/* 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.h"
+#include "mohawk/cstime_cases.h"
+#include "mohawk/cstime_game.h"
+#include "mohawk/cstime_ui.h"
+#include "mohawk/cstime_view.h"
+#include "mohawk/resource.h"
+#include "mohawk/cursors.h"
+#include "mohawk/sound.h"
+#include "mohawk/video.h"
+
+#include "common/events.h"
+#include "common/EventRecorder.h"
+
+#include "engines/util.h"
+
+namespace Mohawk {
+
+MohawkEngine_CSTime::MohawkEngine_CSTime(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
+ _rnd = new Common::RandomSource();
+ g_eventRec.registerRandomSource(*_rnd, "cstime");
+
+ _state = kCSTStateStartup;
+
+ reset();
+}
+
+MohawkEngine_CSTime::~MohawkEngine_CSTime() {
+ delete _interface;
+ delete _view;
+ delete _console;
+ delete _gfx;
+ delete _rnd;
+}
+
+Common::Error MohawkEngine_CSTime::run() {
+ MohawkEngine::run();
+
+ _console = new CSTimeConsole(this);
+ _gfx = new CSTimeGraphics(this);
+ _cursor = new DefaultCursorManager(this, ID_CURS);
+
+ _interface = new CSTimeInterface(this);
+
+ _view = new CSTimeView(this);
+ _view->setupView();
+ _view->setModule(new CSTimeModule(this));
+
+ while (!shouldQuit()) {
+ switch (_state) {
+ case kCSTStateStartup:
+ // We just jump straight to the case for now.
+ _state = kCSTStateNewCase;
+ break;
+
+ case kCSTStateNewCase:
+ initCase();
+ _state = kCSTStateNewScene;
+ break;
+
+ case kCSTStateNewScene:
+ nextScene();
+ _state = kCSTStateNormal;
+ break;
+
+ case kCSTStateNormal:
+ update();
+ break;
+ }
+ }
+
+ return Common::kNoError;
+}
+
+void MohawkEngine_CSTime::update() {
+ Common::Event event;
+ while (_eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ _interface->mouseMove(event.mouse);
+ _needsUpdate = true;
+ break;
+
+ case Common::EVENT_LBUTTONUP:
+ _interface->mouseUp(event.mouse);
+ break;
+
+ case Common::EVENT_LBUTTONDOWN:
+ _interface->mouseDown(event.mouse);
+ break;
+
+ case Common::EVENT_RBUTTONDOWN:
+ // (All of this case is only run if the relevant option is enabled.)
+
+ // FIXME: Don't do these if the options are open.
+ if (_case->getCurrScene()->_activeChar->_flappingState != 0xffff)
+ _case->getCurrScene()->_activeChar->interruptFlapping();
+ if (getCurrentEventType() == kCSTimeEventWaitForClick)
+ mouseClicked();
+
+ // TODO: This is always run, even if not in-game.
+ //pauseCurrentNIS();
+ //stopSound();
+ // FIXME: There's some more stuff here.
+ break;
+
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_d:
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ _console->attach();
+ _console->onFrame();
+ }
+ break;
+
+ case Common::KEYCODE_SPACE:
+ pauseGame();
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ _needsUpdate = true;
+
+ if (_video->updateMovies())
+ _needsUpdate = true;
+
+ if (_needsUpdate) {
+ _view->_needsUpdate = true;
+ _needsUpdate = false;
+ }
+
+ eventIdle();
+ _interface->idle();
+
+ // Cut down on CPU usage
+ _system->delayMillis(10);
+}
+
+void MohawkEngine_CSTime::initCase() {
+ _interface->openResFile();
+ _interface->install();
+ _interface->cursorInstall();
+ // TODO: _interface->paletteOff(true);
+ _interface->cursorActivate(true);
+ _interface->cursorSetShape(1, false);
+ for (uint i = 0; i < 19; i++)
+ _haveInvItem[i] = 0;
+ _interface->getInventoryDisplay()->clearDisplay();
+ _interface->getCarmenNote()->clearPieces();
+ // TODO: fixup book rect for case 20
+ for (uint i = 0; i < 20; i++)
+ _caseVariable[i] = 0;
+ _case = new CSTimeCase1(this); // TODO
+ _interface->getInventoryDisplay()->install();
+ _nextSceneId = 1;
+}
+
+void MohawkEngine_CSTime::nextScene() {
+ _case->setCurrScene(_nextSceneId);
+ CSTimeScene *scene = _case->getCurrScene();
+ // TODO: scene->setState(1);
+ scene->installGroup();
+
+ _interface->draw();
+ scene->buildScene();
+ scene->setupAmbientAnims();
+ _interface->cursorSetShape(1, false);
+
+ addEvent(CSTimeEvent(kCSTimeEventWait, 0xffff, 500));
+ scene->idleAmbientAnims();
+ // TODO: startEnvironmentSound();
+ // TODO: queue one of the scene event queues, depending on whether a value is <= 1 or not
+ addEventList(scene->getEvents(false));
+ _view->idleView();
+ // TODO: maybe startMusic();
+}
+
+void MohawkEngine_CSTime::loadResourceFile(Common::String name) {
+ MohawkArchive *archive = new MohawkArchive();
+ if (!archive->open(name + ".mhk"))
+ error("failed to open %s.mhk", name.c_str());
+ _mhk.push_back(archive);
+}
+
+void MohawkEngine_CSTime::addEvent(const CSTimeEvent &event) {
+ _events.push_back(event);
+}
+
+void MohawkEngine_CSTime::addEventList(const Common::Array<CSTimeEvent> &list) {
+ for (uint i = 0; i < list.size(); i++)
+ addEvent(list[i]);
+}
+
+void MohawkEngine_CSTime::insertEventAtFront(const CSTimeEvent &event) {
+ if (_events.empty())
+ _events.push_front(event);
+ else
+ _events.insert(++_events.begin(), event);
+}
+
+uint16 MohawkEngine_CSTime::getCurrentEventType() {
+ if (_events.empty())
+ return 0xffff;
+ else
+ return _events.front().type;
+}
+
+void MohawkEngine_CSTime::eventIdle() {
+ bool done = false;
+ while (_events.size() && !done && true /* TODO: !_options->getState() */) {
+ _lastTimeout = ~0;
+
+ bool advanceQueue = true;
+ bool processEvent = true;
+ CSTimeEvent &event = _events.front();
+
+ switch (event.type) {
+ case kCSTimeEventCharPlayNIS:
+ if (_NISRunning) {
+ CSTimeChar *chr = _case->getCurrScene()->getChar(event.param1);
+ if (chr->NISIsDone()) {
+ chr->removeNIS();
+ _NISRunning = false;
+ chr->setupAmbientAnims(true);
+ _events.pop_front();
+ processEvent = false;
+ } else {
+ done = true;
+ }
+ } else {
+ advanceQueue = false;
+ }
+ break;
+
+ case kCSTimeEventNewScene:
+ if (_processingEvent) {
+ processEvent = false;
+ _events.pop_front();
+ _processingEvent = false;
+ // FIXME: check skip global, if set:
+ // stopMusic(), stopEnvironmentSound(), set scene to _nextSceneId,
+ // set scene state to 1, set state to idle loop
+ } else {
+ triggerEvent(event);
+ done = true;
+ _processingEvent = true;
+ }
+ break;
+
+ case kCSTimeEventCharStartFlapping:
+ assert(_case->getCurrScene()->_activeChar);
+ switch (_case->getCurrScene()->_activeChar->_flappingState) {
+ case 0xffff:
+ // FIXME: check skip global, if set, processEvent = false and pop event
+ advanceQueue = false;
+ break;
+ case 0:
+ _case->getCurrScene()->_activeChar->_flappingState = 0xffff;
+ _events.pop_front();
+ processEvent = false;
+ break;
+ default:
+ done = true;
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!done && processEvent) {
+ _interface->cursorSetWaitCursor();
+ triggerEvent(event);
+ if (advanceQueue)
+ _events.pop_front();
+ }
+
+ if (!_events.size()) {
+ Common::Point pos = _system->getEventManager()->getMousePos();
+ if (_interface->_sceneRect.contains(pos))
+ _case->getCurrScene()->setCursorForCurrentPoint();
+ else
+ _interface->setCursorForCurrentPoint();
+ _interface->mouseMove(pos);
+ resetTimeout();
+ }
+ }
+}
+
+void MohawkEngine_CSTime::resetTimeout() {
+ _lastTimeout = _system->getMillis();
+}
+
+void MohawkEngine_CSTime::mouseClicked() {
+ // TODO
+}
+
+bool MohawkEngine_CSTime::NISIsRunning() {
+ if (_NISRunning || (!_events.empty() && _events.front().type == kCSTimeEventUnused63))
+ return true;
+
+ return false;
+}
+
+void MohawkEngine_CSTime::reset() {
+ _NISRunning = false;
+ _lastTimeout = ~0;
+ _processingEvent = false;
+}
+
+void MohawkEngine_CSTime::triggerEvent(CSTimeEvent &event) {
+ debug("triggerEvent: type %d, param1 %d, param2 %d", event.type, event.param1, event.param2);
+
+ switch (event.type) {
+ case kCSTimeEventNothing:
+ case kCSTimeEventUnused8:
+ case kCSTimeEventUnused21:
+ case kCSTimeEventUnused63:
+ break;
+
+ case kCSTimeEventCondition:
+ _case->handleConditionalEvent(event);
+ break;
+
+ case kCSTimeEventCharPlayNIS:
+ _case->getCurrScene()->getChar(event.param1)->playNIS(event.param2);
+ _NISRunning = true;
+ break;
+
+ case kCSTimeEventStartConversation:
+ _case->setConversation(event.param2);
+ _case->getCurrConversation()->setSourceChar(event.param1);
+ _case->getCurrConversation()->incrementTalkCount();
+ _case->getCurrConversation()->start();
+ break;
+
+ case kCSTimeEventNewScene:
+ if (_case->getCurrConversation()->getState() != (uint)~0)
+ _case->getCurrConversation()->end(false);
+ _interface->clearTextLine();
+ // TODO: _interface->fadeDown();
+ _interface->cursorSetShape(1);
+ // TODO: swap cursor
+ // TODO: unloadPreloadedSounds?
+ if (_interface->getInventoryDisplay()->getState() == 4) {
+ _interface->getInventoryDisplay()->hide();
+ _interface->getInventoryDisplay()->setState(0);
+ }
+ // TODO: stupid case 20 handling
+ _case->getCurrScene()->leave();
+ // TODO: set dim palette(true)
+ _view->_needsUpdate = true;
+ _view->idleView();
+ // TODO: set dim palette(false)
+ _nextSceneId = event.param2;
+ _state = kCSTStateNewScene;
+ break;
+
+ case kCSTimeEventCharStartFlapping:
+ {
+ CSTimeChar *chr = _case->getCurrScene()->getChar(event.param1);
+ if (!chr->_enabled) {
+ // FIXME
+ warning("chr not enabled in kCSTimeEventCharStartFlapping");
+ break;
+ }
+
+ chr->startFlapping(event.param2);
+ if (event.param2)
+ _interface->drawTextIdToBubble(event.param2);
+
+ CSTimeEvent newEvent;
+ newEvent.param1 = 0xffff;
+ newEvent.param2 = 0xffff;
+
+ newEvent.type = kCSTimeEventUnknown70;
+ insertEventAtFront(newEvent);
+
+ newEvent.type = kCSTimeEventUpdateBubble;
+ insertEventAtFront(newEvent);
+
+ newEvent.type = kCSTimeEventUnknown69;
+ insertEventAtFront(newEvent);
+ }
+ break;
+
+ case kCSTimeEventDropItemInInventory:
+ _interface->dropItemInInventory(event.param2);
+ break;
+
+ case kCSTimeEventAddNotePiece:
+ _interface->clearTextLine();
+ _interface->getCarmenNote()->addPiece(event.param2, event.param1);
+ break;
+
+ case kCSTimeEventDisableHotspot:
+ _case->getCurrScene()->getHotspot(event.param2).state = 0;
+ break;
+
+ case kCSTimeEventDisableFeature:
+ if (!_case->getCurrScene()->_objectFeatures[event.param2])
+ break;
+ _view->removeFeature(_case->getCurrScene()->_objectFeatures[event.param2], true);
+ _case->getCurrScene()->_objectFeatures[event.param2] = NULL;
+ break;
+
+ case kCSTimeEventAddFeature:
+ // TODO: merge this with buildScene somehow?
+ if (_case->getCurrScene()->_objectFeatures[event.param2]) {
+ _case->getCurrScene()->_objectFeatures[event.param2]->resetFeatureScript(1, 0);
+ break;
+ }
+ {
+ uint32 flags = kFeatureSortStatic | kFeatureNewNoLoop | kFeatureNewDisableOnReset;
+ _case->getCurrScene()->_objectFeatures[event.param2] = _view->installViewFeature(_case->getCurrScene()->getSceneId() + event.param2, flags, NULL);
+ // FIXME: a mess of priority stuff
+ }
+ break;
+
+ case kCSTimeEventEnableHotspot:
+ _case->getCurrScene()->getHotspot(event.param2).state = 1;
+ break;
+
+ case kCSTimeEventSetAsked:
+ uint qar, entry;
+ qar = event.param2 / 5;
+ entry = event.param2 % 5;
+ if (qar > 7)
+ error("SetAsked event out of range");
+ _case->getCurrConversation()->setAsked(qar, entry);
+ break;
+
+ case kCSTimeEventShowBigNote:
+ _interface->getCarmenNote()->drawBigNote();
+ break;
+
+ case kCSTimeEventActivateCuffs:
+ _interface->getInventoryDisplay()->activateCuffs(true);
+ break;
+
+ case kCSTimeEventUnknown25:
+ _case->getCurrScene()->getChar(event.param1)->_unknown2 = 1;
+ break;
+
+ case kCSTimeEventUnknown26:
+ _case->getCurrScene()->getChar(event.param1)->_unknown2 = 0;
+ break;
+
+ case kCSTimeEventWait:
+ warning("ignoring wait");
+ // FIXME
+ break;
+
+ case kCSTimeEventUpdateBubble:
+ switch (event.param2) {
+ case 0:
+ // FIXME
+ warning("ignoring bubble update (queue events)");
+ break;
+ case 1:
+ // FIXME
+ warning("ignoring bubble update (install)");
+ break;
+ default:
+ _interface->closeBubble();
+ break;
+ }
+ break;
+
+ case kCSTimeEventInitScene:
+ _interface->displayTextLine("");
+
+ // FIXME: install palette
+
+ // FIXME: swapCursor(true)
+ break;
+
+ case kCSTimeEventUnknown69:
+ // TODO: if persistent text, insert a kCSTimeEventWaitForClick in front of the queue
+ break;
+
+ case kCSTimeEventUnknown70:
+ if (_case->getCurrConversation()->getState() != 0xffff && _case->getCurrConversation()->getState()) {
+ _case->getCurrConversation()->finishProcessingQaR();
+ } else {
+ // FIXME: handle help stuff
+ warning("ignoring unknown 70");
+ }
+ break;
+
+ default:
+ error("unknown scene event type %d", event.type);
+ }
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/cstime.h b/engines/mohawk/cstime.h
new file mode 100644
index 0000000000..13a8db863e
--- /dev/null
+++ b/engines/mohawk/cstime.h
@@ -0,0 +1,191 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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$
+ *
+ */
+
+#ifndef MOHAWK_CSTIME_H
+#define MOHAWK_CSTIME_H
+
+#include "mohawk/mohawk.h"
+#include "mohawk/console.h"
+#include "mohawk/graphics.h"
+
+#include "common/random.h"
+#include "common/list.h"
+
+#include "sound/mixer.h"
+
+namespace Mohawk {
+
+class CSTimeCase;
+class CSTimeInterface;
+class CSTimeView;
+
+enum {
+ kCSTimeEventNothing = 0xffff,
+ kCSTimeEventCondition = 1,
+ kCSTimeEventCharPlayNIS = 2,
+ kCSTimeEventStartConversation = 3,
+ kCSTimeEventNewScene = 4,
+ kCSTimeEventCharStartFlapping = 5,
+ kCSTimeEventSetCaseVariable = 6,
+ kCSTimeEventSetupAmbientAnims = 7,
+ kCSTimeEventUnused8 = 8,
+ kCSTimeEventDropItemInInventory = 9,
+ kCSTimeEventRemoveItemFromInventory = 10,
+ kCSTimeEventAddNotePiece = 11,
+ kCSTimeEventDisableHotspot = 12,
+ kCSTimeEventDisableFeature = 13,
+ kCSTimeEventAddFeature = 14,
+ kCSTimeEventStartMusic = 15,
+ kCSTimeEventStopMusic = 16,
+ kCSTimeEventEnableHotspot = 17,
+ kCSTimeEventSetAsked = 18,
+ kCSTimeEventStartHelp = 19,
+ kCSTimeEventPlaySound = 20,
+ kCSTimeEventUnused21 = 21,
+ kCSTimeEventShowBigNote = 22,
+ kCSTimeEventActivateCuffs = 23,
+ kCSTimeEventSetupRestPos = 24, // TODO
+ kCSTimeEventUnknown25 = 25,
+ kCSTimeEventUnknown26 = 26,
+ kCSTimeEventRemoveChar = 27,
+ kCSTimeEventUnknown28 = 28,
+ kCSTimeEventUnknown29 = 29,
+ kCSTimeEventUnknown30 = 30,
+ kCSTimeEventUnknown31 = 31,
+ kCSTimeEventCharSomeNIS32 = 32,
+ kCSTimeEventCharResetNIS = 33,
+ kCSTimeEventUnknown34 = 34,
+ kCSTimeEventCharPauseAmbients = 35,
+ kCSTimeEventCharUnauseAmbients = 36,
+ kCSTimeEventCharDisableAmbients = 37,
+ kCSTimeEventStopAmbientAnims = 38,
+ kCSTimeEventUnknown39 = 39,
+ kCSTimeEventWait = 40,
+ kCSTimeEventSpeech = 41,
+ kCSTimeEventUnknown42 = 42,
+ kCSTimeEventUnknown43 = 43,
+ kCSTimeEventCharSetupRestPos = 44, // TODO
+ kCSTimeEventCharStopAmbients = 45,
+ kCSTimeEventCharRestartAmbients = 46,
+ kCSTimeEventStopEnvironmentSound = 47,
+ kCSTimeEventWaitForClick = 48,
+ kCSTimeEventSetMusic = 49,
+ kCSTimeEventStartEnvironmentSound = 50,
+ kCSTimeEventPreloadSound = 51,
+ kCSTimeEventPlayPreloadedSound = 52,
+ kCSTimeEventUnknown53 = 53,
+ kCSTimeEventSetEnvironmentSound = 54,
+ kCSTimeEventCharSomeNIS55 = 55,
+ kCSTimeEventUnknown56 = 56,
+ kCSTimeEventUpdateBubble = 57,
+ kCSTimeEventCharSurfAndFlap = 58,
+ kCSTimeEventInitScene = 59,
+ kCSTimeEventFadeDown = 60,
+ kCSTimeEventCharSomeNIS61 = 61,
+ kCSTimeEventCharPlaySimultaneousAnim = 62,
+ kCSTimeEventUnused63 = 63,
+ kCSTimeEventUnknown64 = 64,
+ kCSTimeEventPrepareSave = 65,
+ kCSTimeEventSave = 66,
+ kCSTimeEventQuit = 67,
+ kCSTimeEventPlayMovie = 68,
+ kCSTimeEventUnknown69 = 69, // queues Unknown48
+ kCSTimeEventUnknown70 = 70 // conv/QaR cleanup
+};
+
+struct CSTimeEvent {
+ CSTimeEvent() { }
+ CSTimeEvent(uint16 t, uint16 p1, uint16 p2) : type(t), param1(p1), param2(p2) { }
+
+ uint16 type;
+ uint16 param1;
+ uint16 param2;
+};
+
+enum CSTimeState {
+ kCSTStateStartup,
+ kCSTStateNewCase,
+ kCSTStateNewScene,
+ kCSTStateNormal
+};
+
+class MohawkEngine_CSTime : public MohawkEngine {
+protected:
+ Common::Error run();
+
+public:
+ MohawkEngine_CSTime(OSystem *syst, const MohawkGameDescription *gamedesc);
+ virtual ~MohawkEngine_CSTime();
+
+ Common::RandomSource *_rnd;
+
+ CSTimeGraphics *_gfx;
+ bool _needsUpdate;
+
+ GUI::Debugger *getDebugger() { return _console; }
+ CSTimeView *getView() { return _view; }
+ CSTimeCase *getCase() { return _case; }
+ CSTimeInterface *getInterface() { return _interface; }
+
+ void loadResourceFile(Common::String name);
+
+ void addEvent(const CSTimeEvent &event);
+ void addEventList(const Common::Array<CSTimeEvent> &list);
+ void insertEventAtFront(const CSTimeEvent &event);
+ uint16 getCurrentEventType();
+ void eventIdle();
+ void resetTimeout();
+ void mouseClicked();
+ bool NISIsRunning();
+
+ uint16 _haveInvItem[19];
+ uint16 _caseVariable[20];
+
+private:
+ CSTimeCase *_case;
+ CSTimeConsole *_console;
+ CSTimeInterface *_interface;
+ CSTimeView *_view;
+
+ CSTimeState _state;
+
+ void initCase();
+ void nextScene();
+ void update();
+
+ uint16 _nextSceneId;
+
+ bool _processingEvent;
+ bool _NISRunning;
+ uint32 _lastTimeout;
+ void reset();
+
+ Common::List<CSTimeEvent> _events;
+ void triggerEvent(CSTimeEvent &event);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/cstime_cases.cpp b/engines/mohawk/cstime_cases.cpp
new file mode 100644
index 0000000000..746196e120
--- /dev/null
+++ b/engines/mohawk/cstime_cases.cpp
@@ -0,0 +1,241 @@
+/* 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_cases.h"
+#include "mohawk/cstime_ui.h"
+
+namespace Mohawk {
+
+CSTimeCase1::CSTimeCase1(MohawkEngine_CSTime *vm) : CSTimeCase(vm, 1) {
+}
+
+CSTimeCase1::~CSTimeCase1() {
+}
+
+bool CSTimeCase1::checkConvCondition(uint16 conditionId) {
+ const Common::Array<CSTimeHotspot> &hotspots = getCurrScene()->getHotspots();
+
+ bool gotTorch = _vm->_haveInvItem[1];
+
+ // These are all for conversations in the first scene (with the boatman).
+ switch (conditionId) {
+ case 0:
+ // Got the torch?
+ return (gotTorch);
+ case 1:
+ // Is the bag still on land?
+ return (hotspots[5].state == 1);
+ case 2:
+ // Is the bag on the boat, but player hasn't taken the torch?
+ return (hotspots[5].state != 1 && !gotTorch);
+ }
+
+ return false;
+}
+
+bool CSTimeCase1::checkAmbientCondition(uint16 charId, uint16 ambientId) {
+ return true;
+}
+
+bool CSTimeCase1::checkObjectCondition(uint16 objectId) {
+ const Common::Array<CSTimeHotspot> &hotspots = getCurrScene()->getHotspots();
+
+ switch (_currScene) {
+ case 1:
+ switch (objectId) {
+ case 1:
+ // Hide bag on boat if it's not there.
+ return (hotspots[5].state == 1);
+ case 2:
+ // Hide bag on land if it's not there.
+ return (hotspots[5].state != 1);
+ case 3:
+ // Hide torch if it's been picked up.
+ return (hotspots[4].state == 1);
+ }
+ break;
+
+ case 2:
+ // The first note piece.
+ return !_vm->getInterface()->getCarmenNote()->havePiece(0);
+
+ case 3:
+ // The features representing different stages in the body sequence.
+ if (objectId == 6 && _vm->_caseVariable[3] != 0)
+ return false;
+ else if (objectId == 7 && _vm->_caseVariable[3] != 1)
+ return false;
+ else if (objectId == 8 && _vm->_caseVariable[3] != 2)
+ return false;
+ break;
+
+ case 4:
+ // The second note piece?
+ if (objectId == 0)
+ return (hotspots[0].state > 0);
+ break;
+
+ case 5:
+ // The third note piece.
+ if (objectId == 1)
+ return !_vm->getInterface()->getCarmenNote()->havePiece(2);
+ }
+
+ return true;
+}
+
+void CSTimeCase1::selectHelpStrings() {
+ if (_currScene == 1) {
+ if (_vm->_haveInvItem[1]) {
+ // Got the torch, ready to leave.
+ // FIXME
+ } else {
+ // Still don't have the torch.
+ // FIXME
+ }
+ } else {
+ // FIXME
+ }
+
+ // FIXME
+}
+
+void CSTimeCase1::handleConditionalEvent(const CSTimeEvent &event) {
+ CSTimeEvent newEvent;
+
+ switch (event.param2) {
+ case 0:
+ // Trying to enter the first room of the tomb.
+ if (!_conversations[1]->getAsked(2, 0)) {
+ // We need a plan first.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, getCurrScene()->getHelperId(), 12352));
+ } else if (!_vm->getInterface()->getCarmenNote()->havePiece(0)) {
+ // Shouldn't we take a look at that note?
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, getCurrScene()->getHelperId(), 10355));
+ } else {
+ // Onward!
+ _vm->addEvent(CSTimeEvent(kCSTimeEventNewScene, event.param1, 3));
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, 1, 12551));
+ }
+ break;
+
+ case 1:
+ // Poking at the jars. The response depends on whether the hieroglyphs on the tomb wall
+ // have been seen yet or not.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, getCurrScene()->getHelperId(),
+ _vm->_caseVariable[2] ? 14304 : 14303));
+ break;
+
+ case 2:
+ // FIXME: Leaving the mummy-preparing room?
+ error("cond event 2");
+ break;
+
+ case 3:
+ // FIXME: Body sequence stuff.
+ error("cond event 3");
+ break;
+
+ case 4:
+ // Woven bag dragged.
+ if (_conversations[0]->getAsked(2, 1)) {
+ // We were asked to move it.
+ if (event.param1 == 5) {
+ // Yay, the player got it on the boat!
+ // Congratulate the player and enable the torch.
+ _vm->insertEventAtFront(CSTimeEvent(kCSTimeEventCharStartFlapping, 2, 10551));
+ getCurrScene()->getHotspot(4).invObjId = 1;
+ _vm->insertEventAtFront(CSTimeEvent(kCSTimeEventEnableHotspot, 2, 6));
+ _vm->insertEventAtFront(CSTimeEvent(kCSTimeEventAddFeature, 2, 2));
+ } else {
+ assert(event.param1 < 7);
+ // It didn't get dropped onto the boat, so we complain about it.
+ newEvent.type = kCSTimeEventCharStartFlapping;
+ // Does the Good Guide complain (if we gave it to her, or put it in the inventory)?
+ newEvent.param1 = (event.param1 == 1 || event.param1 == 6) ? getCurrScene()->getHelperId() : 2;
+ // Which string?
+ static const uint16 strings[7] = { 30201, 30103, 30202, 30203, 30203, 0, 10352};
+ newEvent.param2 = strings[event.param1];
+ _vm->insertEventAtFront(newEvent);
+ }
+ } else {
+ // We're just randomly moving the woven bag!
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, getCurrScene()->getHelperId(), 10351));
+
+ if (event.param1 == 5) {
+ // This went onto the boat hotspot, so the bag was removed; put it back.
+ _vm->insertEventAtFront(CSTimeEvent(kCSTimeEventEnableHotspot, 0xffff, 5));
+ _vm->insertEventAtFront(CSTimeEvent(kCSTimeEventAddFeature, 0xffff, 1));
+ }
+ }
+ break;
+
+ case 5:
+ // We're ready to shove off!
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, getCurrScene()->getHelperId(), 10356));
+ break;
+
+ case 6:
+ // Trying to leave the first scene by walking.
+ if (_vm->_haveInvItem[1]) {
+ // If you have the torch, the Good Guide prods you to use the boat.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, getCurrScene()->getHelperId(), 10305));
+ } else {
+ // Otherwise, the boatman tells you that you can't leave yet.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, 2, 10506));
+ }
+ break;
+
+ case 7:
+ // Clicking on the woven bag.
+ if (_conversations[0]->getAsked(2, 0)) {
+ // If we were asked to move it, the Good Guide prods us to try dragging.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, getCurrScene()->getHelperId(), 10306));
+ } else {
+ // Otherwise, the boatman tells us what it is.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, 2, 10502));
+ }
+ break;
+
+ case 8:
+ // One-time-only reminder that you can re-ask questions.
+ if (_vm->_caseVariable[7])
+ break;
+ _vm->_caseVariable[7] = 1;
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, 0, 12359));
+ break;
+
+ case 9:
+ // Trying to give the torch to the Good Guide; you get a different message
+ // depending on whether it's already in your inventory or not.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, 0, _vm->_haveInvItem[1] ? 9906 : 30119));
+ break;
+
+ default:
+ error("unknown Conditional Event type %d for case 1", event.param2);
+ }
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/cstime_cases.h b/engines/mohawk/cstime_cases.h
new file mode 100644
index 0000000000..5f6791fdc1
--- /dev/null
+++ b/engines/mohawk/cstime_cases.h
@@ -0,0 +1,47 @@
+/* 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$
+ *
+ */
+
+#ifndef MOHAWK_CSTIME_CASES_H
+#define MOHAWK_CSTIME_CASES_H
+
+#include "mohawk/cstime_game.h"
+
+namespace Mohawk {
+
+class CSTimeCase1 : public CSTimeCase {
+public:
+ CSTimeCase1(MohawkEngine_CSTime *vm);
+ ~CSTimeCase1();
+
+ bool checkConvCondition(uint16 conditionId);
+ bool checkAmbientCondition(uint16 charId, uint16 ambientId);
+ bool checkObjectCondition(uint16 objectId);
+ void selectHelpStrings();
+ void handleConditionalEvent(const CSTimeEvent &event);
+};
+
+} // End of namespace Mohawk
+
+#endif
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
diff --git a/engines/mohawk/cstime_game.h b/engines/mohawk/cstime_game.h
new file mode 100644
index 0000000000..ce955cc524
--- /dev/null
+++ b/engines/mohawk/cstime_game.h
@@ -0,0 +1,287 @@
+/* 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$
+ *
+ */
+
+#ifndef MOHAWK_CSTIME_GAME_H
+#define MOHAWK_CSTIME_GAME_H
+
+#include "mohawk/cstime.h"
+#include "mohawk/sound.h" // CueList
+
+namespace Mohawk {
+
+class Feature;
+class CSTimeCase;
+class CSTimeScene;
+
+class Region {
+public:
+ void loadFrom(Common::SeekableReadStream *stream);
+ bool containsPoint(Common::Point &pos) const;
+
+ Common::Array<Common::Rect> _rects;
+};
+
+struct CSTimeAmbient {
+ uint16 delay;
+ Feature *feature;
+ uint32 nextTime;
+};
+
+class CSTimeChar {
+public:
+ CSTimeChar(MohawkEngine_CSTime *vm, CSTimeScene *scene, uint id);
+ ~CSTimeChar();
+
+ void idle();
+ void setupAmbientAnims(bool onetime);
+ void idleAmbients();
+ void stopAmbients(bool restpos);
+ void setupRestPos();
+ void removeChr();
+ uint16 getChrBaseId();
+ uint getScriptCount();
+ void playNIS(uint16 id);
+ bool NISIsDone();
+ void removeNIS();
+ void startFlapping(uint16 id);
+ void interruptFlapping();
+
+ uint16 _unknown1, _unknown2, _unknown3;
+ Common::Array<CSTimeAmbient> _ambients;
+ bool _enabled;
+ uint16 _flappingState;
+
+protected:
+ MohawkEngine_CSTime *_vm;
+ CSTimeScene *_scene;
+ uint _id;
+
+ Feature *_NIS;
+ Feature *_restFeature;
+ Feature *_talkFeature;
+ Feature *_talkFeature1, *_talkFeature2, *_talkFeature3;
+
+ uint16 _playingWaveId;
+ CueList _cueList;
+ uint _nextCue;
+ uint32 _lastTime1, _lastTime2, _lastTime3;
+
+ bool _resting;
+ byte _waveStatus;
+ byte _surfingState;
+
+ void installAmbientAnim(uint id, uint32 flags);
+ uint16 getChrTypeScriptBase();
+ void playFlapWave(uint16 id);
+ void updateWaveStatus();
+ void setupTalk();
+ void idleTalk();
+ void stopFlapping();
+};
+
+struct CSTimeHotspot {
+ uint16 stringId;
+ uint16 state;
+ uint16 invObjId;
+ uint16 cursor;
+ Common::Array<CSTimeEvent> events;
+ Region region;
+};
+
+struct CSTimeLocation {
+ uint16 sceneId, hotspotId;
+};
+
+struct CSTimeInventoryHotspot {
+ uint16 sceneId, hotspotId, stringId;
+ Common::Array<CSTimeEvent> events;
+};
+
+#define TIME_CUFFS_ID 0
+struct CSTimeInventoryObject {
+ uint16 id, stringId, hotspotId, featureId, canTake;
+ Feature *feature;
+ Common::Array<CSTimeLocation> locations;
+ Common::Array<CSTimeInventoryHotspot> hotspots;
+ Common::Array<CSTimeEvent> events;
+};
+
+struct CSTimeQaR {
+ bool finished;
+ uint16 id;
+ uint16 unknown1;
+ uint16 questionStringId;
+ uint16 responseStringId;
+ uint16 unknown2;
+ uint16 nextQaRsId;
+ Common::Array<CSTimeEvent> events;
+};
+
+class CSTimeConversation {
+public:
+ CSTimeConversation(MohawkEngine_CSTime *vm, uint id);
+ ~CSTimeConversation();
+
+ void start();
+ void finishProcessingQaR();
+ void end(bool useLastClicked, bool runEvents = true);
+ void display();
+ void selectItemsToDisplay();
+
+ void mouseDown(Common::Point &pos);
+ void mouseMove(Common::Point &pos);
+ void mouseUp(Common::Point &pos);
+
+ void incrementTalkCount() { _talkCount++; }
+ uint16 getNameId() { return _nameId; }
+ uint16 getSourceChar() { return _sourceChar; }
+ void setSourceChar(uint16 source) { _sourceChar = source; }
+ void setAsked(uint qar, uint entry);
+ bool getAsked(uint qar, uint entry) { return _asked[qar][entry]; }
+
+ void setState(uint state) { _state = state; }
+ uint getState() { return _state; }
+
+protected:
+ MohawkEngine_CSTime *_vm;
+ uint _id;
+
+ uint _state;
+
+ uint16 _nameId;
+ uint16 _greeting, _greeting2;
+
+ uint _talkCount;
+ uint16 _sourceChar;
+
+ uint _currEntry, _currHover;
+ uint16 _nextToProcess;
+
+ bool _asked[8][5];
+ Common::Array<CSTimeQaR> _qars;
+ Common::Array<uint> _itemsToDisplay;
+
+ void clear();
+ void loadQaR(CSTimeQaR &qar, uint16 id);
+
+ void highlightLine(uint line);
+ void unhighlightLine(uint line);
+};
+
+class CSTimeScene {
+public:
+ CSTimeScene(MohawkEngine_CSTime *vm, CSTimeCase *case_, uint id);
+ ~CSTimeScene();
+
+ void installGroup();
+ void buildScene();
+ void leave();
+ uint16 getSceneId();
+ void mouseDown(Common::Point &pos);
+ void mouseMove(Common::Point &pos);
+ void mouseUp(Common::Point &pos);
+ void idle();
+ void setupAmbientAnims();
+ void idleAmbientAnims();
+ bool eventIsActive();
+
+ void setCursorForCurrentPoint();
+ void drawHotspots(); // debugging
+
+ uint16 getBubbleType() { return _bubbleType; }
+ const Common::Array<CSTimeEvent> &getEvents(bool second);
+ const Common::Array<CSTimeHotspot> &getHotspots() { return _hotspots; }
+ CSTimeHotspot &getHotspot(uint id) { return _hotspots[id]; }
+ uint16 getInvObjForCurrentHotspot() { return _hotspots[_currHotspot].invObjId; }
+ CSTimeChar *getChar(uint id) { return _chars[id]; }
+ uint16 getHelperId() { return _helperId; }
+ uint getId() { return _id; }
+
+ CSTimeChar *_activeChar;
+
+ Common::Array<Feature *> _objectFeatures;
+
+protected:
+ MohawkEngine_CSTime *_vm;
+ CSTimeCase *_case;
+ uint _id;
+
+ uint _currHotspot;
+ uint _hoverHotspot;
+
+ void load();
+ void cursorOverHotspot(uint id);
+ void mouseDownOnHotspot(uint id);
+ void mouseUpOnHotspot(uint id);
+ bool hotspotContainsEvent(uint id, uint16 eventType);
+
+ uint16 _unknown1, _unknown2, _helperId;
+ uint16 _bubbleType;
+ uint16 _numObjects;
+ Common::Array<CSTimeEvent> _events, _events2;
+ Common::Array<CSTimeChar *> _chars;
+ Common::Array<CSTimeHotspot> _hotspots;
+};
+
+class CSTimeCase {
+public:
+ CSTimeCase(MohawkEngine_CSTime *vm, uint id);
+ virtual ~CSTimeCase();
+
+ uint getId() { return _id; }
+ Common::String &getRolloverText(uint id) { return _rolloverText[id]; }
+ CSTimeScene *getCurrScene();
+ void setCurrScene(uint id) { _currScene = id; }
+ void setConversation(uint id) { _currConv = _conversations[id]; }
+ CSTimeConversation *getCurrConversation() { return _currConv; }
+ uint16 getNoteFeatureId(uint16 id) { return _noteFeatureId[id]; }
+
+ // Hard-coded per-case logic, implemented in subclasses.
+ virtual bool checkConvCondition(uint16 conditionId) = 0;
+ virtual bool checkAmbientCondition(uint16 charId, uint16 ambientId) = 0;
+ virtual bool checkObjectCondition(uint16 objectId) = 0;
+ virtual void selectHelpStrings() = 0;
+ virtual void handleConditionalEvent(const CSTimeEvent &event) = 0;
+
+ Common::Array<CSTimeInventoryObject *> _inventoryObjs;
+
+protected:
+ MohawkEngine_CSTime *_vm;
+ uint _id;
+
+ uint _currScene;
+ uint16 _noteFeatureId[3];
+ Common::Array<Common::String> _rolloverText;
+ Common::Array<CSTimeScene *> _scenes;
+ Common::Array<CSTimeConversation *> _conversations;
+ CSTimeConversation *_currConv;
+
+ void loadRolloverText();
+ CSTimeInventoryObject *loadInventoryObject(uint id);
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/cstime_ui.cpp b/engines/mohawk/cstime_ui.cpp
new file mode 100644
index 0000000000..08f70ea291
--- /dev/null
+++ b/engines/mohawk/cstime_ui.cpp
@@ -0,0 +1,1186 @@
+/* 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 "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;
+}
+
+CSTimeInterface::CSTimeInterface(MohawkEngine_CSTime *vm) : _vm(vm) {
+ _sceneRect = Common::Rect(0, 0, 640, 340);
+ _uiRect = Common::Rect(0, 340, 640, 480);
+
+ _bookRect = Common::Rect(_uiRect.right - 95, _uiRect.top + 32, _uiRect.right - 25, _uiRect.top + 122);
+ _noteRect = Common::Rect(_uiRect.left + 27, _uiRect.top + 31, _uiRect.left + 103, _uiRect.top + 131);
+ _dialogTextRect = Common::Rect(0 + 125, 340 + 40, 640 - 125, 480 - 20);
+
+ _cursorActive = false;
+ _cursorShapes[0] = 0xFFFF;
+ _cursorShapes[1] = 0xFFFF;
+ _cursorShapes[2] = 0xFFFF;
+ _cursorNextTime = 0;
+
+ _help = new CSTimeHelp(_vm);
+ _inventoryDisplay = new CSTimeInventoryDisplay(_vm, _dialogTextRect);
+ _book = new CSTimeBook(_vm);
+ _note = new CSTimeCarmenNote(_vm);
+ _options = new CSTimeOptions(_vm);
+
+ if (!_normalFont.loadFromFON("95instal/EvP14.fon"))
+ error("failed to load normal font");
+ if (!_dialogFont.loadFromFON("95instal/Int1212.fon"))
+ error("failed to load dialog font");
+ if (!_rolloverFont.loadFromFON("95instal/Int1818.fon"))
+ error("failed to load rollover font");
+
+ _uiFeature = NULL;
+ _dialogTextFeature = NULL;
+ _rolloverTextFeature = NULL;
+ _bubbleTextFeature = NULL;
+
+ _mouseWasInScene = false;
+ _state = kCSTimeInterfaceStateNormal;
+
+ _dialogLines.resize(5);
+ _dialogLineColors.resize(5);
+}
+
+CSTimeInterface::~CSTimeInterface() {
+ delete _help;
+ delete _inventoryDisplay;
+ delete _book;
+ delete _note;
+ delete _options;
+}
+
+void CSTimeInterface::cursorInstall() {
+ _vm->getView()->loadBitmapCursors(200);
+}
+
+void CSTimeInterface::cursorIdle() {
+ if (!_cursorActive || _cursorShapes[1] == 0xFFFF)
+ return;
+
+ if (_vm->_system->getMillis() <= _cursorNextTime + 250)
+ return;
+
+ cursorSetShape(_cursorShapes[1], false);
+ _cursorShapes[1] = _cursorShapes[2];
+ _cursorShapes[2] = 0xFFFF;
+}
+
+void CSTimeInterface::cursorActivate(bool state) {
+ _cursorActive = state;
+}
+
+void CSTimeInterface::cursorChangeShape(uint16 id) {
+ if (_cursorShapes[1] == 0xFFFF)
+ _cursorShapes[1] = id;
+ else
+ _cursorShapes[2] = id;
+}
+
+uint16 CSTimeInterface::cursorGetShape() {
+ if (_cursorShapes[2] != 0xFFFF)
+ return _cursorShapes[2];
+ else if (_cursorShapes[1] != 0xFFFF)
+ return _cursorShapes[1];
+ else
+ return _cursorShapes[0];
+}
+
+void CSTimeInterface::cursorSetShape(uint16 id, bool reset) {
+ if (_cursorShapes[0] != id) {
+ _cursorShapes[0] = id;
+ _vm->getView()->setBitmapCursor(id);
+ _cursorNextTime = _vm->_system->getMillis();
+ }
+}
+
+void CSTimeInterface::cursorSetWaitCursor() {
+ uint16 shape = cursorGetShape();
+ switch (shape) {
+ case 8:
+ cursorChangeShape(9);
+ break;
+ case 9:
+ break;
+ case 11:
+ cursorChangeShape(12);
+ break;
+ case 13:
+ cursorChangeShape(15);
+ break;
+ default:
+ cursorChangeShape(3);
+ break;
+ }
+}
+
+void CSTimeInterface::openResFile() {
+ _vm->loadResourceFile("data/iface");
+}
+
+void CSTimeInterface::install() {
+ uint16 resourceId = 100; // TODO
+ _vm->getView()->installGroup(resourceId, 16, 0, true, 100);
+
+ // TODO: store/reset these
+
+ _dialogTextFeature = _vm->getView()->installViewFeature(0, 0, NULL);
+ _dialogTextFeature->_data.bounds = _dialogTextRect;
+ _dialogTextFeature->_data.bitmapIds[0] = 0;
+ // We don't set a port.
+ _dialogTextFeature->_moveProc = (Module::FeatureProc)&CSTimeModule::dialogTextMoveProc;
+ _dialogTextFeature->_drawProc = (Module::FeatureProc)&CSTimeModule::dialogTextDrawProc;
+ _dialogTextFeature->_timeProc = NULL;
+ _dialogTextFeature->_flags = kFeatureOldSortForeground; // FIXME: not in original
+
+ _rolloverTextFeature = _vm->getView()->installViewFeature(0, 0, NULL);
+ _rolloverTextFeature->_data.bounds = Common::Rect(0 + 100, 340 + 5, 640 - 100, 480 - 25);
+ _rolloverTextFeature->_data.bitmapIds[0] = 0;
+ _rolloverTextFeature->_moveProc = (Module::FeatureProc)&CSTimeModule::rolloverTextMoveProc;
+ _rolloverTextFeature->_drawProc = (Module::FeatureProc)&CSTimeModule::rolloverTextDrawProc;
+ _rolloverTextFeature->_timeProc = NULL;
+ _rolloverTextFeature->_flags = kFeatureOldSortForeground; // FIXME: not in original
+}
+
+void CSTimeInterface::draw() {
+ // TODO
+ if (!_uiFeature) {
+ uint32 flags = kFeatureSortStatic | kFeatureNewNoLoop;
+ assert(flags == 0x4800000);
+ uint16 resourceId = 100; // TODO
+ _uiFeature = _vm->getView()->installViewFeature(resourceId, flags, NULL);
+ // TODO: special-case for case 20
+ } else {
+ _uiFeature->resetFeatureScript(1, 0);
+ // TODO: special-case for case 20
+ }
+
+ // TODO: special-case for cases 19 and 20
+
+ _note->drawSmallNote();
+ _book->drawSmallBook();
+ _inventoryDisplay->draw();
+}
+
+void CSTimeInterface::idle() {
+ // TODO: inv sound handling
+
+ _vm->getCase()->getCurrScene()->idle();
+ _inventoryDisplay->idle();
+ cursorIdle();
+
+ _vm->getView()->idleView();
+}
+
+void CSTimeInterface::mouseDown(Common::Point pos) {
+ _vm->resetTimeout();
+
+ if (_options->getState()) {
+ // TODO: _options->mouseDown(pos);
+ return;
+ }
+
+ if (!cursorGetState())
+ return;
+ if (_vm->getCase()->getCurrScene()->eventIsActive())
+ return;
+
+ switch (cursorGetShape()) {
+ case 1:
+ cursorChangeShape(4);
+ break;
+ case 2:
+ cursorChangeShape(5);
+ break;
+ case 13:
+ cursorChangeShape(14);
+ break;
+ }
+
+ if (_book->getState() == 2) {
+ // TODO: _book->mouseDown(pos);
+ return;
+ }
+
+ // TODO: if in sailing puzzle, sailing puzzle mouse down, return
+
+ if (_note->getState() > 0) {
+ return;
+ }
+
+ if (_sceneRect.contains(pos)) {
+ _vm->getCase()->getCurrScene()->mouseDown(pos);
+ return;
+ }
+
+ // TODO: case 20 ui craziness is handled seperately..
+
+ CSTimeConversation *conv = _vm->getCase()->getCurrConversation();
+ if (_bookRect.contains(pos) || (_noteRect.contains(pos) && _note->havePiece(0xffff))) {
+ if (conv->getState() != (uint)~0)
+ conv->end(false);
+ if (_help->getState() != (uint)~0)
+ _help->end();
+ return;
+ }
+
+ if (_help->getState() != (uint)~0) {
+ // FIXME: _help->mouseDown(pos);
+ return;
+ }
+
+ if (conv->getState() != (uint)~0) {
+ conv->mouseDown(pos);
+ return;
+ }
+
+ // TODO: handle symbols
+
+ if (_inventoryDisplay->_invRect.contains(pos)) {
+ // TODO: special handling for case 6
+
+ _inventoryDisplay->mouseDown(pos);
+ }
+}
+
+void CSTimeInterface::mouseMove(Common::Point pos) {
+ if (_options->getState()) {
+ // TODO: _options->mouseMove(pos);
+ return;
+ }
+
+ if (!cursorGetState())
+ return;
+
+ if (_mouseWasInScene && _uiRect.contains(pos)) {
+ clearTextLine();
+ _mouseWasInScene = false;
+ }
+
+ if (_book->getState() == 2) {
+ // TODO: _book->mouseMove(pos);
+ return;
+ }
+
+ if (_note->getState() == 2)
+ return;
+
+ // TODO: case 20 ui craziness is handled seperately..
+
+ if (_sceneRect.contains(pos) && !_vm->getCase()->getCurrScene()->eventIsActive()) {
+ _vm->getCase()->getCurrScene()->mouseMove(pos);
+ _mouseWasInScene = true;
+ return;
+ }
+
+ if (cursorGetShape() == 13) {
+ cursorSetShape(1);
+ return;
+ } else if (cursorGetShape() == 14) {
+ cursorSetShape(4);
+ return;
+ }
+
+ bool mouseIsDown = _vm->getEventManager()->getButtonState() & 1;
+
+ if (_book->getState() == 1 && !_bookRect.contains(pos)) {
+ if (_state != kCSTimeInterfaceStateDragging) {
+ clearTextLine();
+ cursorSetShape(mouseIsDown ? 4 : 1);
+ _book->setState(0);
+ }
+ return;
+ }
+
+ // TODO: case 20 ui craziness again
+
+ if (_note->getState() == 1 && !_noteRect.contains(pos)) {
+ if (_state != kCSTimeInterfaceStateDragging) {
+ clearTextLine();
+ cursorSetShape(mouseIsDown ? 4 : 1);
+ _note->setState(0);
+ }
+ return;
+ }
+
+ // TODO: handle symbols
+
+ if (_bookRect.contains(pos)) {
+ if (_state != kCSTimeInterfaceStateDragging) {
+ displayTextLine("Open Chronopedia");
+ cursorSetShape(mouseIsDown ? 5 : 2);
+ _book->setState(1);
+ }
+ return;
+ }
+
+ if (_noteRect.contains(pos)) {
+ if (_state != kCSTimeInterfaceStateDragging && _note->havePiece(0xffff) && !_note->getState()) {
+ displayTextLine("Look at Note");
+ cursorSetShape(mouseIsDown ? 5 : 2);
+ _note->setState(1);
+ }
+ return;
+ }
+
+ if (_vm->getCase()->getCurrConversation()->getState() != (uint)~0) {
+ _vm->getCase()->getCurrConversation()->mouseMove(pos);
+ return;
+ }
+
+ if (_help->getState() != (uint)~0) {
+ // FIXME: _help->mouseMove(pos);
+ return;
+ }
+
+ if (_state == kCSTimeInterfaceStateDragging) {
+ // FIXME: dragging
+ return;
+ }
+
+ // FIXME: if case is 20, we're done, return
+
+ if (_inventoryDisplay->_invRect.contains(pos)) {
+ _inventoryDisplay->mouseMove(pos);
+ }
+}
+
+void CSTimeInterface::mouseUp(Common::Point pos) {
+ if (_options->getState()) {
+ // TODO: options->mouseUp(pos);
+ return;
+ }
+
+ if (!cursorGetState())
+ return;
+
+ if (_state == kCSTimeInterfaceStateDragging) {
+ stopDragging();
+ return;
+ }
+
+ if (_state == kCSTimeInterfaceStateDragStart)
+ _state = kCSTimeInterfaceStateNormal;
+
+ switch (cursorGetShape()) {
+ case 4:
+ cursorChangeShape(1);
+ break;
+ case 5:
+ cursorChangeShape(2);
+ break;
+ case 14:
+ cursorChangeShape(13);
+ break;
+ }
+
+ if (_vm->getCase()->getCurrScene()->eventIsActive()) {
+ if (_vm->getCurrentEventType() == kCSTimeEventWaitForClick)
+ _vm->mouseClicked();
+ return;
+ }
+
+ if (_book->getState() == 2) {
+ // TODO: _book->mouseUp(pos);
+ return;
+ }
+
+ if (_note->getState() == 2) {
+ // TODO: _note->closeNote();
+ mouseMove(pos);
+ return;
+ }
+
+ // TODO: if in sailing puzzle, sailing puzzle mouse up, return
+
+ // TODO: case 20 ui craziness is handled seperately..
+
+ if (_sceneRect.contains(pos)) {
+ _vm->getCase()->getCurrScene()->mouseUp(pos);
+ return;
+ }
+
+ if (_vm->getCase()->getCurrConversation()->getState() != (uint)~0) {
+ _vm->getCase()->getCurrConversation()->mouseUp(pos);
+ return;
+ }
+
+ if (_help->getState() != (uint)~0) {
+ // FIXME: _help->mouseUp(pos);
+ return;
+ }
+
+ // TODO: handle symbols
+
+ if (_bookRect.contains(pos)) {
+ // TODO: handle flashing cuffs
+ // TODO: _book->open();
+ return;
+ }
+
+ if (_noteRect.contains(pos)) {
+ // TODO: special-casing for case 19
+ if (_note->havePiece(0xffff))
+ _note->drawBigNote();
+ }
+
+ if (_inventoryDisplay->_invRect.contains(pos)) {
+ // TODO: special-casing for case 6
+ _inventoryDisplay->mouseUp(pos);
+ }
+}
+
+void CSTimeInterface::cursorOverHotspot() {
+ if (cursorGetState() != 1)
+ return;
+ if (_state == kCSTimeInterfaceStateDragStart || _state == kCSTimeInterfaceStateDragging)
+ return;
+ if (cursorGetShape() == 3 || cursorGetShape() == 9)
+ return;
+
+ bool mouseIsDown = _vm->getEventManager()->getButtonState() & 1;
+ if (mouseIsDown)
+ cursorSetShape(5);
+ else if (cursorGetShape() == 1)
+ cursorChangeShape(2);
+}
+
+void CSTimeInterface::setCursorForCurrentPoint() {
+ uint16 shape = 1;
+
+ Common::Point mousePos = _vm->getEventManager()->getMousePos();
+ if (_bookRect.contains(mousePos)) {
+ shape = 2;
+ } else {
+ uint convState = _vm->getCase()->getCurrConversation()->getState();
+ uint helpState = _help->getState();
+ // TODO: symbols too
+ if (convState == (uint)~0 || convState == 0 || helpState == (uint)~0 || helpState == 0) {
+ // FIXME: set cursor to 2 if inventory display occupied
+ } else if (convState == 1 || helpState == 1) {
+ // FIXME: set cursor to 2 if over conversation rect
+ }
+ }
+
+ cursorSetShape(shape);
+}
+
+void CSTimeInterface::clearTextLine() {
+ _rolloverText.clear();
+}
+
+void CSTimeInterface::displayTextLine(Common::String text) {
+ _rolloverText = text;
+}
+
+void CSTimeInterface::clearDialogArea() {
+ _dialogLines.clear();
+ _dialogLines.resize(5);
+ // TODO: dirty feature
+}
+
+void CSTimeInterface::clearDialogLine(uint line) {
+ _dialogLines[line].clear();
+ // TODO: dirty feature
+}
+
+void CSTimeInterface::displayDialogLine(uint16 id, uint line, byte color) {
+ Common::SeekableReadStream *stream = _vm->getResource(ID_STRI, id);
+ Common::String text = readString(stream);
+ delete stream;
+
+ _dialogLines[line] = text;
+ _dialogLineColors[line] = color;
+ // TODO: dirty feature
+}
+
+void CSTimeInterface::drawTextIdToBubble(uint16 id) {
+ Common::SeekableReadStream *stream = _vm->getResource(ID_STRI, id);
+ Common::String text = readString(stream);
+ delete stream;
+
+ drawTextToBubble(&text);
+}
+
+void CSTimeInterface::drawTextToBubble(Common::String *text) {
+ if (_bubbleTextFeature)
+ error("Attempt to display two text objects");
+ if (!text)
+ text = &_bubbleText;
+ if (text->empty())
+ return;
+
+ _currentBubbleText = *text;
+
+ uint bubbleId = _vm->getCase()->getCurrScene()->getBubbleType();
+ Common::Rect bounds;
+ switch (bubbleId) {
+ case 0:
+ bounds = Common::Rect(15, 7, 625, 80);
+ break;
+ case 1:
+ bounds = Common::Rect(160, 260, 625, 333);
+ break;
+ case 2:
+ bounds = Common::Rect(356, 3, 639, 90);
+ break;
+ case 3:
+ bounds = Common::Rect(10, 7, 380, 80);
+ break;
+ case 4:
+ bounds = Common::Rect(15, 270, 625, 328);
+ break;
+ case 5:
+ bounds = Common::Rect(15, 7, 550, 70);
+ break;
+ case 6:
+ bounds = Common::Rect(0, 0, 313, 76);
+ break;
+ case 7:
+ bounds = Common::Rect(200, 25, 502, 470);
+ break;
+ default:
+ error("unknown bubble type %d in drawTextToBubble", bubbleId);
+ }
+
+ _bubbleTextFeature = _vm->getView()->installViewFeature(0, 0, NULL);
+ _bubbleTextFeature->_data.bounds = bounds;
+ _bubbleTextFeature->_data.bitmapIds[0] = 0;
+ _bubbleTextFeature->_moveProc = (Module::FeatureProc)&CSTimeModule::bubbleTextMoveProc;
+ _bubbleTextFeature->_drawProc = (Module::FeatureProc)&CSTimeModule::bubbleTextDrawProc;
+ _bubbleTextFeature->_timeProc = NULL;
+ _bubbleTextFeature->_flags = kFeatureOldSortForeground; // FIXME: not in original
+}
+
+void CSTimeInterface::closeBubble() {
+ if (_bubbleTextFeature)
+ _vm->getView()->removeFeature(_bubbleTextFeature, true);
+ _bubbleTextFeature = NULL;
+}
+
+void CSTimeInterface::startDragging(uint16 id) {
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[id];
+
+ cursorSetShape(11);
+ _draggedItem = id;
+
+ if (_draggedItem == TIME_CUFFS_ID) {
+ if (_inventoryDisplay->getCuffsShape() == 11) {
+ _inventoryDisplay->setCuffsFlashing();
+ _vm->getView()->idleView();
+ }
+ }
+
+ uint32 dragFlags = (grabbedFromInventory() ? 0x800 : 0x600);
+ _vm->getView()->dragFeature((NewFeature *)invObj->feature, _grabPoint, 4, dragFlags, NULL);
+
+ if (_vm->getCase()->getId() == 1 && id == 2) {
+ // Hardcoded behaviour for the torch in the first case.
+ if (_vm->getCase()->getCurrScene()->getId() == 4) {
+ // This is the dark tomb.
+ // FIXME: apply torch hack
+ _vm->_caseVariable[2]++;
+ } else {
+ // FIXME: unapply torch hack
+ }
+ }
+
+ _state = kCSTimeInterfaceStateDragging;
+
+ if (grabbedFromInventory())
+ return;
+
+ // Hide the associated scene feature, if there is one.
+ if (invObj->featureId != 0xffff) {
+ CSTimeEvent event;
+ event.type = kCSTimeEventDisableFeature;
+ event.param2 = invObj->featureId;
+ _vm->addEvent(event);
+ }
+
+ _vm->addEventList(invObj->events);
+}
+
+void CSTimeInterface::stopDragging() {
+ CSTimeScene *scene = _vm->getCase()->getCurrScene();
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[_draggedItem];
+
+ Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
+ _state = kCSTimeInterfaceStateNormal;
+
+ if (_sceneRect.contains(mousePos))
+ scene->setCursorForCurrentPoint();
+ else
+ setCursorForCurrentPoint();
+
+ // Find the inventory object hotspot which is topmost for this drop, if any.
+ uint foundInvObjHotspot = ~0;
+ const Common::Array<CSTimeHotspot> &hotspots = scene->getHotspots();
+ for (uint i = 0; i < hotspots.size(); i++) {
+ if (hotspots[i].state != 1)
+ continue;
+ if (!hotspots[i].region.containsPoint(mousePos))
+ continue;
+ for (uint j = 0; j < invObj->hotspots.size(); j++) {
+ if (invObj->hotspots[j].sceneId != scene->getId())
+ continue;
+ if (invObj->hotspots[j].hotspotId != i)
+ continue;
+ if (foundInvObjHotspot != (uint)~0 && invObj->hotspots[foundInvObjHotspot].hotspotId < invObj->hotspots[j].hotspotId)
+ continue;
+ foundInvObjHotspot = j;
+ }
+ }
+
+ // Work out if we're going to consume (nom-nom) the object after the drop.
+ bool consumeObj = false;
+ bool runConsumeEvents = false;
+ if (foundInvObjHotspot != (uint)~0) {
+ CSTimeInventoryHotspot &hotspot = invObj->hotspots[foundInvObjHotspot];
+
+ clearTextLine();
+ for (uint i = 0; i < invObj->locations.size(); i++) {
+ if (invObj->locations[i].sceneId != hotspot.sceneId)
+ continue;
+ if (invObj->locations[i].hotspotId != hotspot.hotspotId)
+ continue;
+ consumeObj = true;
+ break;
+ }
+
+ if (_draggedItem == TIME_CUFFS_ID && !_inventoryDisplay->getCuffsState()) {
+ consumeObj = false;
+ // Nuh-uh, they're not activated yet.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, _vm->getCase()->getCurrScene()->getHelperId(), 9902));
+ } else {
+ // FIXME: ding();
+ runConsumeEvents = true;
+ }
+ }
+
+ // Deal with the actual drop.
+ if (grabbedFromInventory()) {
+ if (!consumeObj) {
+ _vm->getView()->dragFeature((NewFeature *)invObj->feature, mousePos, 2, 0x800, NULL);
+ // TODO: playSound(151);
+ } else if (_draggedItem != TIME_CUFFS_ID) {
+ _vm->getView()->dragFeature((NewFeature *)invObj->feature, mousePos, 2, 0x800, NULL);
+ _vm->_haveInvItem[_draggedItem] = 0;
+ // FIXME: the original sets the feature to -2 here, see comment below
+ invObj->feature = NULL;
+ _inventoryDisplay->removeItem(_draggedItem);
+ } else if (!_inventoryDisplay->getCuffsState()) {
+ // Inactive cuffs.
+ _vm->getView()->dragFeature((NewFeature *)invObj->feature, mousePos, 2, 0x800, NULL);
+ invObj->feature = NULL;
+ } else {
+ // Active cuffs.
+ _vm->getView()->dragFeature((NewFeature *)invObj->feature, mousePos, 2, 0x600, NULL);
+ _vm->_haveInvItem[_draggedItem] = 0;
+ // FIXME: the original sets the feature to -2 here, see comment below
+ invObj->feature = NULL;
+ }
+
+ if (runConsumeEvents) {
+ _vm->addEventList(invObj->hotspots[foundInvObjHotspot].events);
+ }
+
+ _inventoryDisplay->draw();
+ } else {
+ uint32 dragFlags = 0x600;
+ _vm->getView()->dragFeature((NewFeature *)invObj->feature, mousePos, 2, dragFlags, NULL);
+
+ if (_inventoryDisplay->_invRect.contains(mousePos)) {
+ // Dropped into the inventory.
+ invObj->feature = NULL;
+ if (invObj->canTake) {
+ dropItemInInventory(_draggedItem);
+ if (invObj->hotspotId)
+ _vm->addEvent(CSTimeEvent(kCSTimeEventDisableHotspot, 0xffff, invObj->hotspotId));
+ } else {
+ if (invObj->featureId)
+ _vm->addEvent(CSTimeEvent(kCSTimeEventAddFeature, 0xffff, invObj->featureId));
+ }
+
+ for (uint i = 0; i < invObj->hotspots.size(); i++) {
+ if (invObj->hotspots[i].sceneId != scene->getId())
+ continue;
+ if (invObj->hotspots[i].hotspotId != 0xffff)
+ continue;
+ _vm->addEventList(invObj->hotspots[i].events);
+ }
+ } else {
+ // Dropped into the scene.
+
+ CSTimeEvent event;
+ event.param1 = 0xffff;
+ if (consumeObj) {
+ // FIXME: the original sets the feature to -2 here, which is used in the inventory display drawing
+ // so it knows not to draw the object. we should replace that with a flag.
+ invObj->feature = NULL;
+ event.type = kCSTimeEventDisableHotspot;
+ event.param2 = invObj->hotspotId;
+ } else {
+ invObj->feature = NULL;
+ event.type = kCSTimeEventAddFeature;
+ event.param2 = invObj->featureId;
+ }
+ _vm->addEvent(event);
+
+ if (runConsumeEvents) {
+ _vm->addEventList(invObj->hotspots[foundInvObjHotspot].events);
+ } else {
+ for (uint i = 0; i < invObj->hotspots.size(); i++) {
+ if (invObj->hotspots[i].sceneId != scene->getId())
+ continue;
+ if (invObj->hotspots[i].hotspotId != 0xfffe)
+ continue;
+ _vm->addEventList(invObj->hotspots[i].events);
+ }
+ }
+ }
+ }
+
+ if (_vm->getCase()->getId() == 1 && _vm->getCase()->getCurrScene()->getId() == 4) {
+ // Hardcoded behaviour for torches in the dark tomb, in the first case.
+ if (_draggedItem == 1 && foundInvObjHotspot == (uint)~0) {
+ // Trying to drag an unlit torch around?
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, 0, 16352));
+ } else if (_draggedItem == 2 && _vm->_caseVariable[2] == 1) {
+ // This the first time we tried dragging the lit torch around.
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, 0, 16354));
+ }
+ }
+
+ // TODO: Is this necessary?
+ _draggedItem = 0xffff;
+}
+
+void CSTimeInterface::setGrabPoint() {
+ _grabPoint = _vm->_system->getEventManager()->getMousePos();
+}
+
+bool CSTimeInterface::grabbedFromInventory() {
+ return (_inventoryDisplay->_invRect.contains(_grabPoint));
+}
+
+void CSTimeInterface::dropItemInInventory(uint16 id) {
+ if (_vm->_haveInvItem[id])
+ return;
+
+ _vm->_haveInvItem[id] = 1;
+ _vm->getCase()->_inventoryObjs[id]->feature = NULL;
+
+ _inventoryDisplay->insertItemInDisplay(id);
+
+ // TODO: deal with symbols
+
+ if (_vm->getCase()->getCurrConversation()->getState() == (uint)~0 || _vm->getCase()->getCurrConversation()->getState() == 0) {
+ // FIXME: additional check here
+ // FIXME: play sound 151?
+ _inventoryDisplay->draw();
+ return;
+ }
+
+ // FIXME: ding();
+ clearDialogArea();
+ _inventoryDisplay->show();
+ _inventoryDisplay->draw();
+ _inventoryDisplay->setState(4);
+}
+
+CSTimeHelp::CSTimeHelp(MohawkEngine_CSTime *vm) : _vm(vm) {
+ _state = ~0;
+}
+
+CSTimeHelp::~CSTimeHelp() {
+}
+
+void CSTimeHelp::end(bool runEvents) {
+ // FIXME
+}
+
+CSTimeInventoryDisplay::CSTimeInventoryDisplay(MohawkEngine_CSTime *vm, Common::Rect baseRect) : _vm(vm) {
+ _state = 0;
+ _cuffsState = false;
+ _cuffsShape = 10;
+
+ _invRect = baseRect;
+
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
+ _itemRect[i].left = baseRect.left + 15 + i * 92;
+ _itemRect[i].top = baseRect.top + 5;
+ _itemRect[i].right = _itemRect[i].left + 90;
+ _itemRect[i].bottom = _itemRect[i].top + 70;
+ }
+}
+
+CSTimeInventoryDisplay::~CSTimeInventoryDisplay() {
+}
+
+void CSTimeInventoryDisplay::install() {
+ uint count = _vm->getCase()->_inventoryObjs.size() - 1;
+
+ // FIXME: some cases have hard-coded counts
+
+ _vm->getView()->installGroup(9000, count, 0, true, 9000);
+}
+
+void CSTimeInventoryDisplay::draw() {
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
+ if (_displayedItems[i] == (uint)~0)
+ continue;
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[_displayedItems[i]];
+
+ // FIXME: ignore on -2 feature (see CSTimeInterface::stopDragging)
+
+ if (invObj->feature) {
+ invObj->feature->resetFeatureScript(1, 0);
+ continue;
+ }
+
+ // TODO: 0x2000 is set! help?
+ uint32 flags = kFeatureSortStatic | kFeatureNewNoLoop | 0x2000;
+ if (i == TIME_CUFFS_ID) {
+ // Time Cuffs are handled differently.
+ // TODO: Can we not use _cuffsShape here?
+ uint16 id = 100 + 10;
+ if (_cuffsState) {
+ id = 100 + 12;
+ flags &= ~kFeatureNewNoLoop;
+ }
+ invObj->feature = _vm->getView()->installViewFeature(id, flags, NULL);
+ } else {
+ Common::Point pos((_itemRect[i].left + _itemRect[i].right) / 2, (_itemRect[i].top + _itemRect[i].bottom) / 2);
+ uint16 id = 9000 + (invObj->id - 1);
+ invObj->feature = _vm->getView()->installViewFeature(id, flags, &pos);
+ }
+ }
+}
+
+void CSTimeInventoryDisplay::show() {
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
+ if (_displayedItems[i] == (uint)~0)
+ continue;
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[_displayedItems[i]];
+ if (!invObj->feature)
+ continue;
+ invObj->feature->show();
+ }
+}
+
+void CSTimeInventoryDisplay::hide() {
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
+ if (_displayedItems[i] == (uint)~0)
+ continue;
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[_displayedItems[i]];
+ if (!invObj->feature)
+ continue;
+ invObj->feature->hide(true);
+ }
+}
+
+void CSTimeInventoryDisplay::idle() {
+ if (_vm->getInterface()->getCarmenNote()->getState() ||
+ _vm->getCase()->getCurrConversation()->getState() != 0xffff ||
+ _vm->getInterface()->getHelp()->getState() != 0xffff) {
+ if (_state == 4) {
+ // FIXME: check timeout!
+ hide();
+ _vm->getCase()->getCurrConversation()->display();
+ _state = 0;
+ }
+ return;
+ }
+
+ if (!_state)
+ return;
+
+ // FIXME: deal with actual inventory stuff
+}
+
+void CSTimeInventoryDisplay::clearDisplay() {
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++)
+ _displayedItems[i] = ~0;
+
+ // We always start out with the Time Cuffs.
+ _vm->_haveInvItem[TIME_CUFFS_ID] = 1;
+ insertItemInDisplay(TIME_CUFFS_ID);
+
+ _cuffsState = false;
+}
+
+void CSTimeInventoryDisplay::insertItemInDisplay(uint id) {
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++)
+ if (_displayedItems[i] == id)
+ return;
+
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++)
+ if (_displayedItems[i] == (uint)~0) {
+ _displayedItems[i] = id;
+ return;
+ }
+
+ error("couldn't insert item into display");
+}
+
+void CSTimeInventoryDisplay::removeItem(uint id) {
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[id];
+ if (invObj->feature) {
+ _vm->getView()->removeFeature(invObj->feature, true);
+ invObj->feature = NULL;
+ }
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++)
+ if (_displayedItems[i] == id)
+ _displayedItems[i] = ~0;
+}
+
+void CSTimeInventoryDisplay::mouseDown(Common::Point &pos) {
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
+ if (_displayedItems[i] == (uint)~0)
+ continue;
+ if (!_itemRect[i].contains(pos))
+ continue;
+ _draggedItem = i;
+ _vm->getInterface()->cursorSetShape(8);
+ _vm->getInterface()->setGrabPoint();
+ _vm->getInterface()->setState(kCSTimeInterfaceStateDragStart);
+ }
+}
+
+void CSTimeInventoryDisplay::mouseMove(Common::Point &pos) {
+ bool mouseIsDown = _vm->getEventManager()->getButtonState() & 1;
+ if (mouseIsDown && _vm->getInterface()->cursorGetShape() == 8) {
+ 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(getLastDisplayedClicked());
+ } else {
+ // TODO: CSTimeScene::mouseMove does quite a lot more, why not here too?
+ _vm->getInterface()->startDragging(_vm->getCase()->getCurrScene()->getInvObjForCurrentHotspot());
+ }
+ }
+ }
+
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
+ if (_displayedItems[i] == (uint)~0)
+ continue;
+ if (!_itemRect[i].contains(pos))
+ continue;
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[_displayedItems[i]];
+ Common::String text = "Pick up ";
+ // TODO: special-case for case 11, scene 3, inv obj 1 (just use "Read " instead)
+ text += _vm->getCase()->getRolloverText(invObj->stringId);
+ _vm->getInterface()->displayTextLine(text);
+ _vm->getInterface()->cursorOverHotspot();
+ // FIXME: there's some trickery here to store the id for the below
+ return;
+ }
+
+ if (false /* FIXME: if we get here and the stored id mentioned above is set.. */) {
+ _vm->getInterface()->clearTextLine();
+ if (_vm->getInterface()->getState() != kCSTimeInterfaceStateDragging) {
+ if (_vm->getInterface()->cursorGetShape() != 3 && _vm->getInterface()->cursorGetShape() != 9)
+ _vm->getInterface()->cursorSetShape(1);
+ }
+ // FIXME: reset that stored id
+ }
+}
+
+void CSTimeInventoryDisplay::mouseUp(Common::Point &pos) {
+ for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
+ if (_displayedItems[i] == (uint)~0)
+ continue;
+ if (!_itemRect[i].contains(pos))
+ continue;
+ // TODO: instead, stupid hack for case 11, scene 3, inv obj 1 (kCSTimeEventUnknown39, 0xffff, 0xffff)
+ // TODO: instead, stupid hack for case 18, scene not 3, inv obj 4 (kCSTimeEventCondition, 1, 29)
+ CSTimeEvent event;
+ event.param1 = _vm->getCase()->getCurrScene()->getHelperId();
+ if (event.param1 == 0xffff)
+ event.type = kCSTimeEventSpeech;
+ else
+ event.type = kCSTimeEventCharStartFlapping;
+ if (i == TIME_CUFFS_ID) {
+ if (_cuffsState)
+ event.param2 = 9903;
+ else
+ event.param2 = 9902;
+ } else {
+ event.param2 = 9905 + _displayedItems[i];
+ }
+ _vm->addEvent(event);
+ }
+}
+
+void CSTimeInventoryDisplay::activateCuffs(bool active) {
+ _cuffsState = active;
+ if (!_cuffsState)
+ return;
+
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[TIME_CUFFS_ID];
+ _cuffsShape = 11;
+ if (invObj->feature)
+ _vm->getView()->removeFeature(invObj->feature, true);
+ uint32 flags = kFeatureSortStatic | kFeatureNewNoLoop;
+ invObj->feature = _vm->getView()->installViewFeature(100 + _cuffsShape, flags, NULL);
+}
+
+void CSTimeInventoryDisplay::setCuffsFlashing() {
+ CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[TIME_CUFFS_ID];
+ _cuffsShape = 12;
+ if (invObj->feature)
+ _vm->getView()->removeFeature(invObj->feature, true);
+ uint32 flags = kFeatureSortStatic | 0x2000;
+ invObj->feature = _vm->getView()->installViewFeature(100 + _cuffsShape, flags, NULL);
+}
+
+CSTimeBook::CSTimeBook(MohawkEngine_CSTime *vm) : _vm(vm) {
+ _state = 0;
+ _smallBookFeature = NULL;
+}
+
+CSTimeBook::~CSTimeBook() {
+}
+
+void CSTimeBook::drawSmallBook() {
+ if (!_smallBookFeature) {
+ _smallBookFeature = _vm->getView()->installViewFeature(101, kFeatureSortStatic | kFeatureNewNoLoop, NULL);;
+ } else {
+ _smallBookFeature->resetFeature(false, NULL, 0);
+ }
+}
+
+CSTimeCarmenNote::CSTimeCarmenNote(MohawkEngine_CSTime *vm) : _vm(vm) {
+ _state = 0;
+ _feature = NULL;
+ clearPieces();
+}
+
+CSTimeCarmenNote::~CSTimeCarmenNote() {
+}
+
+void CSTimeCarmenNote::clearPieces() {
+ for (uint i = 0; i < NUM_NOTE_PIECES; i++)
+ _pieces[i] = 0xffff;
+}
+
+bool CSTimeCarmenNote::havePiece(uint16 piece) {
+ for (uint i = 0; i < NUM_NOTE_PIECES; i++) {
+ if (piece == 0xffff) {
+ if (_pieces[i] != 0xffff)
+ return true;
+ } else if (_pieces[i] == piece)
+ return true;
+ }
+
+ return false;
+}
+
+void CSTimeCarmenNote::addPiece(uint16 piece, uint16 speech) {
+ uint i;
+ for (i = 0; i < NUM_NOTE_PIECES; i++) {
+ if (_pieces[i] == 0xffff) {
+ _pieces[i] = piece;
+ break;
+ }
+ }
+ if (i == NUM_NOTE_PIECES)
+ error("addPiece couldn't add piece to carmen note");
+
+ // Get the Good Guide to say something.
+ if (i == 2)
+ speech = 9900; // Found the third piece.
+ if (speech != 0xffff)
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, _vm->getCase()->getCurrScene()->getHelperId(), speech));
+
+ // Remove the note feature, if any.
+ uint16 noteFeatureId = _vm->getCase()->getNoteFeatureId(piece);
+ if (noteFeatureId != 0xffff)
+ _vm->addEvent(CSTimeEvent(kCSTimeEventDisableFeature, 0xffff, noteFeatureId));
+
+ _vm->addEvent(CSTimeEvent(kCSTimeEventShowBigNote, 0xffff, 0xffff));
+
+ if (i != 2)
+ return;
+
+ // TODO: special-casing for case 5
+
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharPlayNIS, _vm->getCase()->getCurrScene()->getHelperId(), 3));
+
+ // TODO: special-casing for case 5
+
+ _vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, _vm->getCase()->getCurrScene()->getHelperId(), 9901));
+ _vm->addEvent(CSTimeEvent(kCSTimeEventActivateCuffs, 0xffff, 0xffff));
+}
+
+void CSTimeCarmenNote::drawSmallNote() {
+ if (!havePiece(0xffff))
+ return;
+
+ if (_feature)
+ _vm->getView()->removeFeature(_feature, true);
+
+ uint16 id = 100;
+ if (_pieces[2] != 0xffff)
+ id += 5;
+ else if (_pieces[1] != 0xffff)
+ id += 4;
+ else
+ id += 2;
+
+ _feature = _vm->getView()->installViewFeature(id, kFeatureSortStatic | kFeatureNewNoLoop, NULL);
+}
+
+void CSTimeCarmenNote::drawBigNote() {
+ // FIXME
+}
+
+CSTimeOptions::CSTimeOptions(MohawkEngine_CSTime *vm) : _vm(vm) {
+ _state = 0;
+}
+
+CSTimeOptions::~CSTimeOptions() {
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/cstime_ui.h b/engines/mohawk/cstime_ui.h
new file mode 100644
index 0000000000..236f73ab22
--- /dev/null
+++ b/engines/mohawk/cstime_ui.h
@@ -0,0 +1,254 @@
+/* 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$
+ *
+ */
+
+#ifndef MOHAWK_CSTIME_UI_H
+#define MOHAWK_CSTIME_UI_H
+
+#include "mohawk/cstime.h"
+#include "graphics/fonts/winfont.h"
+
+namespace Mohawk {
+
+class CSTimeHelp {
+public:
+ CSTimeHelp(MohawkEngine_CSTime *vm);
+ ~CSTimeHelp();
+
+ void end(bool runEvents = true);
+
+ uint getState() { return _state; }
+
+protected:
+ MohawkEngine_CSTime *_vm;
+
+ uint _state;
+};
+
+class CSTimeOptions {
+public:
+ CSTimeOptions(MohawkEngine_CSTime *vm);
+ ~CSTimeOptions();
+
+ uint getState() { return _state; }
+
+protected:
+ MohawkEngine_CSTime *_vm;
+
+ uint _state;
+};
+
+#define MAX_DISPLAYED_ITEMS 4
+class CSTimeInventoryDisplay {
+public:
+ CSTimeInventoryDisplay(MohawkEngine_CSTime *vm, Common::Rect baseRect);
+ ~CSTimeInventoryDisplay();
+
+ void install();
+ void draw();
+ void show();
+ void hide();
+ void idle();
+ void clearDisplay();
+ void insertItemInDisplay(uint id);
+ void removeItem(uint id);
+
+ void mouseDown(Common::Point &pos);
+ void mouseMove(Common::Point &pos);
+ void mouseUp(Common::Point &pos);
+
+ void activateCuffs(bool active);
+ void setCuffsFlashing();
+ bool getCuffsState() { return _cuffsState; }
+ uint16 getCuffsShape() { return _cuffsShape; }
+
+ uint16 getDisplayedNum(uint id) { return _displayedItems[id]; }
+ uint16 getLastDisplayedClicked() { return getDisplayedNum(_draggedItem); }
+
+ void setState(uint state) { _state = state; }
+ uint getState() { return _state; }
+
+ Common::Rect _invRect;
+ Common::Rect _itemRect[MAX_DISPLAYED_ITEMS];
+
+protected:
+ MohawkEngine_CSTime *_vm;
+
+ uint _state;
+ bool _cuffsState;
+ uint16 _cuffsShape;
+ uint16 _draggedItem;
+ uint _displayedItems[MAX_DISPLAYED_ITEMS];
+};
+
+class CSTimeBook {
+public:
+ CSTimeBook(MohawkEngine_CSTime *vm);
+ ~CSTimeBook();
+
+ uint getState() { return _state; }
+ void setState(uint state) { _state = state; }
+
+ void drawSmallBook();
+
+protected:
+ MohawkEngine_CSTime *_vm;
+
+ uint _state;
+ Feature *_smallBookFeature;
+};
+
+#define NUM_NOTE_PIECES 3
+class CSTimeCarmenNote {
+public:
+ CSTimeCarmenNote(MohawkEngine_CSTime *vm);
+ ~CSTimeCarmenNote();
+
+ uint getState() { return _state; }
+ void setState(uint state) { _state = state; }
+
+ void clearPieces();
+ bool havePiece(uint16 piece);
+ void addPiece(uint16 piece, uint16 speech);
+ void drawSmallNote();
+ void drawBigNote();
+
+protected:
+ MohawkEngine_CSTime *_vm;
+
+ uint _state;
+ uint16 _pieces[NUM_NOTE_PIECES];
+ Feature *_feature;
+};
+
+enum CSTimeInterfaceState {
+ kCSTimeInterfaceStateNormal = 1,
+ kCSTimeInterfaceStateDragStart = 2,
+ kCSTimeInterfaceStateDragging = 3
+};
+
+class CSTimeInterface {
+public:
+ CSTimeInterface(MohawkEngine_CSTime *vm);
+ ~CSTimeInterface();
+
+ void cursorInstall();
+ void cursorActivate(bool state);
+ bool cursorGetState() { return _cursorActive; }
+ void cursorIdle();
+ void cursorChangeShape(uint16 id);
+ uint16 cursorGetShape();
+ void cursorSetShape(uint16 id, bool reset = true);
+ void cursorSetWaitCursor();
+
+ void openResFile();
+ void install();
+ void draw();
+ void idle();
+ void mouseDown(Common::Point pos);
+ void mouseMove(Common::Point pos);
+ void mouseUp(Common::Point pos);
+
+ void cursorOverHotspot();
+ void setCursorForCurrentPoint();
+
+ void clearTextLine();
+ void displayTextLine(Common::String text);
+
+ void clearDialogArea();
+ void clearDialogLine(uint line);
+ void displayDialogLine(uint16 id, uint line, byte color = 32);
+
+ void drawTextIdToBubble(uint16 id);
+ void drawTextToBubble(Common::String *text);
+ void closeBubble();
+
+ void startDragging(uint16 id);
+ void stopDragging();
+ void setGrabPoint();
+ uint16 getDraggedNum() { return _draggedItem; }
+ Common::Point getGrabPoint() { return _grabPoint; }
+ bool grabbedFromInventory();
+
+ void dropItemInInventory(uint16 id);
+
+ CSTimeInterfaceState getState() { return _state; }
+ void setState(CSTimeInterfaceState state) { _state = state; }
+
+ CSTimeHelp *getHelp() { return _help; }
+ CSTimeInventoryDisplay *getInventoryDisplay() { return _inventoryDisplay; }
+ CSTimeBook *getBook() { return _book; }
+ CSTimeCarmenNote *getCarmenNote() { return _note; }
+ CSTimeOptions *getOptions() { return _options; }
+
+ const Common::String &getRolloverText() { return _rolloverText; }
+ const Common::String &getDialogText() { return _dialogText; }
+ const Common::String &getCurrBubbleText() { return _currentBubbleText; }
+ const Common::Array<Common::String> &getDialogLines() { return _dialogLines; }
+ const Common::Array<byte> &getDialogLineColors() { return _dialogLineColors; }
+
+ const Graphics::WinFont &getNormalFont() { return _normalFont; }
+ const Graphics::WinFont &getDialogFont() { return _dialogFont; }
+ const Graphics::WinFont &getRolloverFont() { return _rolloverFont; }
+
+ Common::Rect _sceneRect, _uiRect;
+ Common::Rect _dialogTextRect, _bookRect, _noteRect;
+
+protected:
+ MohawkEngine_CSTime *_vm;
+
+ Common::String _bubbleText;
+ bool _mouseWasInScene;
+ CSTimeInterfaceState _state;
+
+ CSTimeHelp *_help;
+ CSTimeInventoryDisplay *_inventoryDisplay;
+ CSTimeBook *_book;
+ CSTimeCarmenNote *_note;
+ CSTimeOptions *_options;
+
+ Feature *_uiFeature;
+ Feature *_dialogTextFeature;
+ Feature *_rolloverTextFeature;
+ Feature *_bubbleTextFeature;
+
+ Common::String _rolloverText;
+ Common::String _dialogText;
+ Common::String _currentBubbleText;
+ Common::Array<Common::String> _dialogLines;
+ Common::Array<byte> _dialogLineColors;
+
+ uint16 _draggedItem;
+ Common::Point _grabPoint;
+
+ Graphics::WinFont _normalFont, _dialogFont, _rolloverFont;
+
+ bool _cursorActive;
+ uint16 _cursorShapes[3];
+ uint32 _cursorNextTime;
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/cstime_view.cpp b/engines/mohawk/cstime_view.cpp
new file mode 100644
index 0000000000..bf8305d3ee
--- /dev/null
+++ b/engines/mohawk/cstime_view.cpp
@@ -0,0 +1,537 @@
+/* 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" // debugging..
+#include "mohawk/cstime_ui.h"
+#include "mohawk/cstime_view.h"
+#include "mohawk/resource.h"
+#include "mohawk/cursors.h"
+
+#include "common/events.h"
+
+namespace Mohawk {
+
+CSTimeView::CSTimeView(MohawkEngine_CSTime *vm) : View(vm) {
+ _timeVm = vm;
+ _gfx = vm->_gfx;
+ _bitmapCursorId = 0;
+}
+
+uint32 CSTimeView::getTime() {
+ return _vm->_system->getMillis();
+}
+
+void CSTimeView::setupView() {
+ _rootNode = new NewFeature(this);
+ _cursorNode = new NewFeature(this);
+ _rootNode->setNodeDefaults(NULL, _cursorNode);
+ _rootNode->_id = 1; // TODO: 10 in new?
+ _rootNode->_data.enabled = 0;
+ _rootNode->_flags = kFeatureSortBackground;
+ _rootNode->_moveProc = NULL;
+ _rootNode->_drawProc = NULL;
+ _rootNode->_timeProc = NULL;
+ _cursorNode->setNodeDefaults(_rootNode, NULL);
+ _cursorNode->_id = 0xffff; // TODO: 1 in new?
+ _cursorNode->_data.enabled = 0;
+ _cursorNode->_flags = kFeatureOldSortForeground; // TODO: 0x4000 in new..
+ _cursorNode->_moveProc = (Module::FeatureProc)&CSTimeModule::cursorMoveProc;
+ _cursorNode->_drawProc = (Module::FeatureProc)&CSTimeModule::cursorDrawProc;
+ _cursorNode->_timeProc = NULL;
+}
+
+Feature *CSTimeView::installViewFeature(uint16 scrbId, uint32 flags, Common::Point *pos) {
+ Feature *node = _rootNode;
+ // FIXME: find the right node to insert under
+ while (node) {
+ if (node->_next && (node->_next->_id == 0xffff || ((flags & 0x8000) && !(node->_next->_flags & 0x8000))))
+ break;
+ node = node->_next;
+ }
+ if (!node)
+ error("failed to install view feature");
+
+ NewFeature *feature = new NewFeature(this);
+ feature->setNodeDefaults(node, node->_next);
+
+ feature->_moveProc = (Module::FeatureProc)&CSTimeModule::defaultMoveProc;
+ feature->_drawProc = (Module::FeatureProc)&CSTimeModule::defaultDrawProc;
+ feature->_timeProc = (Module::BooleanProc)&CSTimeModule::defaultTimeProc;
+ feature->_pickupProc = (Module::PickupProc)&CSTimeModule::defaultPickupProc;
+ feature->_dropProc = (Module::FeatureProc)&CSTimeModule::defaultDropProc;
+ feature->_dragMoveProc = (Module::FeatureProc)&CSTimeModule::defaultDragMoveProc;
+ feature->_oldMoveProc = NULL;
+ feature->_dragFlags = 0x8000;
+
+ feature->_id = getNewFeatureId();
+ node->_next = feature;
+ feature->_next->_prev = feature;
+ if (pos) {
+ feature->_data.currentPos = *pos;
+ feature->_unknown168 = 1;
+ } else {
+ feature->_data.currentPos = Common::Point();
+ feature->_unknown168 = 0x7FFFFFFF;
+ }
+ feature->_data.nextPos = Common::Point();
+ feature->_scrbId = scrbId;
+ feature->_flags = flags;
+ feature->_delayTime = 100;
+ return feature;
+}
+
+void CSTimeView::installGroup(uint16 resourceId, uint size, uint count, bool regs, uint16 baseId) {
+ // TODO: make sure this is in sync!
+ assert(_numSCRBGroups < 14);
+ installFeatureShapes(regs, _numSCRBGroups, resourceId);
+ if (baseId == 0xffff)
+ baseId = resourceId;
+ installGroupOfSCRBs(false, baseId, size, count);
+}
+
+void CSTimeView::removeGroup(uint16 resourceId) {
+ // FIXME: deal with zero resourceId
+ if (resourceId == 0)
+ error("removeGroup got zero resourceId");
+
+ uint16 groupId = getGroupFromBaseId(resourceId);
+ if (groupId == 0xffff)
+ return;
+
+ removeObjectsUsingBaseId(resourceId);
+ freeShapesUsingGroupId(groupId);
+ freeScriptsUsingGroupId(groupId);
+ adjustShapeGroups(groupId);
+}
+
+void CSTimeView::removeObjectsUsingBaseId(uint16 baseId) {
+ uint16 groupId = getGroupFromBaseId(baseId);
+
+ Feature *node = _rootNode->_next;
+ while (node->_next) {
+ Feature *curr = node;
+ node = node->_next;
+ if (curr->_data.compoundSHAPIndex == groupId) {
+ removeFeature(curr, true);
+ }
+ }
+}
+
+void CSTimeView::freeShapesUsingGroupId(uint16 groupId) {
+ _compoundSHAPGroups[groupId] = 0xffff; // FIXME
+}
+
+void CSTimeView::freeScriptsUsingGroupId(uint16 groupId) {
+ _SCRBGroupBases[groupId] = 0xffff; // FIXME
+}
+
+void CSTimeView::adjustShapeGroups(uint16 groupId) {
+ // FIXME
+}
+
+void CSTimeView::loadBitmapCursors(uint16 baseId) {
+ // TODO
+}
+
+void CSTimeView::setBitmapCursor(uint16 id) {
+ if (_bitmapCursorId == id)
+ return;
+
+ if (!id) {
+ _vm->_cursor->showCursor();
+ } else {
+ _vm->_cursor->hideCursor();
+ }
+
+ _bitmapCursorId = id;
+}
+
+void CSTimeView::dragFeature(NewFeature *feature, Common::Point pos, uint mode, uint32 flags, Common::Rect *rect) {
+ feature->_data.hidden = 0;
+
+ if (mode == 2) {
+ if (feature->_dragFlags & 0x800000) {
+ feature->_dragFlags |= 0x8000;
+ if (!(flags & 1))
+ (_currentModule->*(feature->_dropProc))(feature);
+ }
+ return;
+ }
+
+ if (feature->_dragFlags & 0x800000)
+ (_currentModule->*(feature->_dropProc))(feature);
+ else
+ (_currentModule->*(feature->_pickupProc))(feature, pos, flags, rect);
+}
+
+void CSTimeView::finishDraw() {
+ // TODO: This is a kinda stupid hack, here just for debugging.
+ ((MohawkEngine_CSTime *)_vm)->getCase()->getCurrScene()->drawHotspots();
+}
+
+CSTimeModule::CSTimeModule(MohawkEngine_CSTime *vm) : _vm(vm) {
+}
+
+void CSTimeModule::defaultMoveProc(Feature *feature) {
+ if (feature->_data.paused > 0)
+ return;
+
+ if (!feature->_data.enabled)
+ return;
+
+ if (feature->_timeProc && !(this->*(feature->_timeProc))(feature))
+ return;
+
+ if (feature->_needsReset) {
+ feature->resetFeatureScript(1, feature->_scrbId);
+ if ((feature->_flags & kFeatureNewDisable) || (feature->_flags & kFeatureNewDisableOnReset)) {
+ feature->_data.enabled = 0;
+ }
+ feature->_dirty = 1;
+ if (feature->_flags & kFeatureInternalRegion) {
+ // TODO: create region [+140] (if not already done)
+ }
+ } else {
+ if (!(feature->_flags & kFeatureNewClip)) {
+ if (feature->_data.useClipRect) {
+ // TODO: or clip with _unknown228
+ } else if (feature->_region) {
+ // TODO: or clip with region
+ } else {
+ // TODO: or clip with bounds
+ }
+ }
+ feature->_dirty = 1;
+ if (feature->_flags & kFeatureNewInternalTiming) {
+ feature->_nextTime += feature->_delayTime;
+ } else {
+ feature->_nextTime = _vm->getView()->_lastIdleTime + feature->_delayTime;
+ }
+ if (feature->_done) {
+ if (feature->_flags & kFeatureNewNoLoop) {
+ // FIXME: sync channel reset
+ uint16 unknown184 = 1, unknown186 = 1; // FIXME: XXX
+ if (feature->_flags & kFeatureDisableOnEnd || (unknown184 != 0 && unknown186 != 0)) { // FIXME: XXX
+ feature->_data.enabled = 0;
+ if (feature->_doneProc) {
+ (this->*(feature->_doneProc))(feature); // TODO: with -2
+ }
+ }
+ return;
+ }
+
+ feature->_data.currOffset = 26;
+ feature->_done = 0;
+ }
+ if (feature->_flags & kFeatureNewDisable)
+ feature->_data.enabled = 0;
+ }
+
+ int xOffset = feature->_data.currentPos.x + feature->_data.nextPos.x;
+ int yOffset = feature->_data.currentPos.y + feature->_data.nextPos.y;
+
+ Common::SeekableReadStream *ourSCRB = _vm->getView()->getSCRB(feature->_data.scrbIndex);
+ ourSCRB->seek(feature->_data.currOffset);
+
+ bool setBitmap = false;
+ uint bitmapId = 0;
+ bool done = false;
+ while (!done) {
+ byte opcode = ourSCRB->readByte();
+ byte size = ourSCRB->readByte();
+ switch (opcode) {
+ case 1:
+ ourSCRB->skip(size - 2);
+ opcode = ourSCRB->readByte();
+ size = ourSCRB->readByte();
+ if (opcode != 0) {
+ ourSCRB->seek(-2, SEEK_CUR);
+ done = true;
+ break;
+ }
+ case 0:
+ // TODO: set ptr +176 to 1
+ feature->_done = 1;
+ if (feature->_doneProc) {
+ (this->*(feature->_doneProc))(feature); // TODO: with -1
+ }
+ done = true;
+ break;
+
+ case 3:
+ {
+ int32 pos = ourSCRB->pos();
+ ourSCRB->seek(2);
+ uint16 base = ourSCRB->readUint16BE();
+ ourSCRB->seek(pos);
+ base += ourSCRB->readUint16BE();
+ if (base) {
+ // FIXME: sound?
+ }
+ ourSCRB->skip(size - 4);
+ }
+ warning("saw feature opcode 0x3 (size %d)", size);
+ break;
+
+ case 4:
+ // FIXME
+ if (false /* TODO: !+72 */) {
+ ourSCRB->skip(size - 2);
+ } else {
+ uint16 time = ourSCRB->readUint16BE();
+ // FIXME: not right
+ feature->_delayTime = time;
+ ourSCRB->skip(size - 4);
+ }
+ warning("saw feature opcode 0x4 (size %d)", size);
+ break;
+
+ case 9:
+ // FIXME
+ ourSCRB->skip(size - 2);
+ warning("ignoring feature opcode 0x9 (size %d)", size);
+ break;
+
+ case 0xf:
+ // FIXME
+ ourSCRB->skip(size - 2);
+ warning("ignoring feature opcode 0xf (size %d)", size);
+ break;
+
+ case 0x10:
+ while (bitmapId < 48) {
+ if (!size)
+ break;
+ size--;
+ feature->_data.bitmapIds[bitmapId] = ourSCRB->readUint16BE() & 0xFFF;
+ feature->_data.bitmapPos[bitmapId].x = ourSCRB->readUint16BE() + xOffset;
+ feature->_data.bitmapPos[bitmapId].y = ourSCRB->readUint16BE() + yOffset;
+ bitmapId++;
+ }
+ feature->_data.bitmapIds[bitmapId] = 0;
+ setBitmap = true;
+ break;
+
+ default:
+ warning("unknown new feature opcode %d", opcode);
+ ourSCRB->skip(size - 2);
+ break;
+ }
+ }
+
+ feature->_data.currOffset = ourSCRB->pos();
+ if (!setBitmap) {
+ // TODO: set fail flag
+ return;
+ }
+ if (feature->_frameProc) {
+ (this->*(feature->_frameProc))(feature);
+ }
+ // TODO: set palette if needed
+
+ // TODO: adjust for regs if needed
+ Common::Array<int16> regsX, regsY;
+ Common::SeekableReadStream *regsStream;
+ uint16 compoundSHAPIndex = _vm->getView()->getCompoundSHAPId(feature->_data.compoundSHAPIndex);
+ regsStream = _vm->getResource(ID_REGS, compoundSHAPIndex);
+ while (regsStream->pos() != regsStream->size())
+ regsX.push_back(regsStream->readSint16BE());
+ delete regsStream;
+ regsStream = _vm->getResource(ID_REGS, compoundSHAPIndex + 1);
+ while (regsStream->pos() != regsStream->size())
+ regsY.push_back(regsStream->readSint16BE());
+ delete regsStream;
+ for (uint i = 0; i < 48; i++) {
+ uint16 thisBitmapId = feature->_data.bitmapIds[i];
+ if (!thisBitmapId)
+ break;
+ feature->_data.bitmapPos[i].x -= regsX[thisBitmapId];
+ feature->_data.bitmapPos[i].y -= regsY[thisBitmapId];
+ }
+
+ // TODO: set bounds
+ // TODO: unset fail flag
+}
+
+void CSTimeModule::defaultDrawProc(Feature *feature) {
+ if (feature->_data.hidden > 0)
+ return;
+
+ feature->defaultDraw();
+}
+
+bool CSTimeModule::defaultTimeProc(Feature *feature) {
+ return (feature->_nextTime <= _vm->getView()->getTime());
+}
+
+void CSTimeModule::defaultPickupProc(NewFeature *feature, Common::Point pos, uint32 flags, Common::Rect *rect) {
+ _vm->getView()->removeFeature(feature, false);
+
+ feature->_dragFlags |= flags | 0x800000;
+ feature->_oldFlags = feature->_flags;
+ feature->_data.useClipRect = 0;
+ // TODO: these flags are weird/different
+ feature->_flags = (feature->_flags & ~kFeatureSortBackground) | kFeatureOldSortForeground | kFeatureSortStatic | 0x2000;
+ _vm->getView()->insertUnderCursor(feature);
+
+ feature->_nextTime = 0;
+ // FIXME: preserve old delayTime (see also script op 4)
+ feature->_delayTime = 50;
+
+ feature->_oldPos = feature->_data.currentPos;
+
+ feature->_posDiff.x = pos.x - feature->_data.currentPos.x;
+ feature->_posDiff.y = pos.y - feature->_data.currentPos.y;
+ debug("defaultPickupProc: diff is %d, %d", feature->_posDiff.x, feature->_posDiff.y);
+
+ feature->_oldMoveProc = feature->_moveProc;
+ feature->_moveProc = feature->_dragMoveProc;
+
+ // FIXME: deal with rect
+ if (rect)
+ error("defaultPickupProc doesn't handle rect yet");
+}
+
+void CSTimeModule::defaultDropProc(NewFeature *feature) {
+ // FIXME: invalidation
+
+ feature->_flags = feature->_oldFlags;
+ // FIXME: restore old delayTime
+ feature->_dragFlags &= ~0x800000;
+
+ if (feature->_dragFlags & 0x800)
+ feature->moveAndUpdate(feature->_oldPos);
+ if (feature->_dragFlags & 0x200)
+ feature->hide(true);
+ feature->_moveProc = feature->_oldMoveProc;
+}
+
+void CSTimeModule::defaultDragMoveProc(NewFeature *feature) {
+ // FIXME
+
+ if (feature->_dragFlags & 0x8000)
+ feature->_currDragPos = _vm->getEventManager()->getMousePos();
+
+ Common::Point pos = feature->_currDragPos;
+ pos.x -= feature->_posDiff.x;
+ pos.y -= feature->_posDiff.y;
+
+ if (feature->_dragFlags & 0x80) {
+ // FIXME: handle 0x80 case
+ error("encountered 0x80 case in defaultDragMoveProc");
+ }
+
+ feature->moveAndUpdate(pos);
+
+ (this->*(feature->_oldMoveProc))(feature);
+}
+
+void CSTimeModule::cursorMoveProc(Feature *feature) {
+ uint16 cursor = _vm->getView()->getBitmapCursor();
+ if (!cursor)
+ return;
+
+ Common::Point pos = _vm->getEventManager()->getMousePos();
+
+ // FIXME: shouldn't be hardcoded
+ uint16 compoundSHAPIndex = 200;
+ // FIXME: stupid REGS stuff..
+ Common::SeekableReadStream *regsStream = _vm->getResource(ID_REGS, compoundSHAPIndex);
+ regsStream->seek(cursor * 2);
+ feature->_data.bounds.left = pos.x - regsStream->readSint16BE();
+ delete regsStream;
+ regsStream = _vm->getResource(ID_REGS, compoundSHAPIndex + 1);
+ regsStream->seek(cursor * 2);
+ feature->_data.bounds.top = pos.y - regsStream->readSint16BE();
+ delete regsStream;
+}
+
+void CSTimeModule::cursorDrawProc(Feature *feature) {
+ uint16 cursor = _vm->getView()->getBitmapCursor();
+ if (!cursor)
+ return;
+ // FIXME: shouldn't be hardcoded
+ uint16 compoundSHAPIndex = 200;
+ _vm->getView()->getGfx()->copyAnimSubImageToScreen(compoundSHAPIndex, cursor - 1, feature->_data.bounds.left, feature->_data.bounds.top);
+}
+
+void CSTimeModule::rolloverTextMoveProc(Feature *feature) {
+ // Should OR the whole bounds into the dirty region, if the text changed.
+}
+
+void CSTimeModule::rolloverTextDrawProc(Feature *feature) {
+ // TODO: if timeBook->getState() is 2, return
+ const Common::String &text = _vm->getInterface()->getRolloverText();
+ if (!text.empty()) {
+ Common::Rect &rect = feature->_data.bounds;
+ Graphics::Surface *screen = g_system->lockScreen();
+ _vm->getInterface()->getRolloverFont().drawString(screen, text, rect.left, rect.top, rect.width(), 32, Graphics::kTextAlignCenter);
+ g_system->unlockScreen();
+ }
+ // TODO: some special case about dragging in case 1, scene 4 (torch?)
+ // TODO: unset text changed flag
+}
+
+void CSTimeModule::dialogTextMoveProc(Feature *feature) {
+ // FIXME
+}
+
+void CSTimeModule::dialogTextDrawProc(Feature *feature) {
+ const Common::Array<Common::String> &lines = _vm->getInterface()->getDialogLines();
+ const Common::Array<byte> &colors = _vm->getInterface()->getDialogLineColors();
+ const Common::Rect &bounds = feature->_data.bounds;
+ const Graphics::WinFont &font = _vm->getInterface()->getDialogFont();
+
+ Graphics::Surface *screen = _vm->_system->lockScreen();
+ for (uint i = 0; i < lines.size(); i++)
+ font.drawString(screen, lines[i], bounds.left, bounds.top + 1 + i*15, bounds.width(), colors[i], Graphics::kTextAlignCenter);
+ _vm->_system->unlockScreen();
+ // FIXME
+}
+
+void CSTimeModule::bubbleTextMoveProc(Feature *feature) {
+ // FIXME
+}
+
+void CSTimeModule::bubbleTextDrawProc(Feature *feature) {
+ Common::Rect bounds = feature->_data.bounds;
+ bounds.grow(-5);
+ const Graphics::WinFont &font = _vm->getInterface()->getDialogFont();
+ uint height = font.getFontHeight();
+
+ Common::Array<Common::String> lines;
+ font.wordWrapText(_vm->getInterface()->getCurrBubbleText(), bounds.width(), lines);
+
+ Graphics::Surface *screen = _vm->_system->lockScreen();
+ for (int x = -2; x < 2; x++)
+ for (int y = -1; y < 3; y++)
+ for (uint i = 0; i < lines.size(); i++)
+ font.drawString(screen, lines[i], bounds.left + x, bounds.top + y + i*height, bounds.width(), 241, Graphics::kTextAlignCenter);
+ for (uint i = 0; i < lines.size(); i++)
+ font.drawString(screen, lines[i], bounds.left, bounds.top + i*height, bounds.width(), 32, Graphics::kTextAlignCenter);
+ _vm->_system->unlockScreen();
+}
+
+} // End of namespace Mohawk
diff --git a/engines/mohawk/cstime_view.h b/engines/mohawk/cstime_view.h
new file mode 100644
index 0000000000..9cc3a14da4
--- /dev/null
+++ b/engines/mohawk/cstime_view.h
@@ -0,0 +1,97 @@
+/* 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$
+ *
+ */
+
+#ifndef MOHAWK_CSTIME_VIEW_H
+#define MOHAWK_CSTIME_VIEW_H
+
+#include "mohawk/cstime.h"
+#include "mohawk/view.h"
+
+namespace Mohawk {
+
+class CSTimeModule : public Module {
+public:
+ CSTimeModule(MohawkEngine_CSTime *vm);
+
+ // TODO: these don't really belong here
+ void init() { }
+ void shutdown() { }
+ void update() { }
+
+ void defaultMoveProc(Feature *feature);
+ void defaultDrawProc(Feature *feature);
+ bool defaultTimeProc(Feature *feature);
+
+ void defaultPickupProc(NewFeature *feature, Common::Point pos, uint32 flags, Common::Rect *rect);
+ void defaultDropProc(NewFeature *feature);
+ void defaultDragMoveProc(NewFeature *feature);
+
+ void cursorMoveProc(Feature *feature);
+ void cursorDrawProc(Feature *feature);
+
+ void dialogTextMoveProc(Feature *feature);
+ void dialogTextDrawProc(Feature *feature);
+ void rolloverTextMoveProc(Feature *feature);
+ void rolloverTextDrawProc(Feature *feature);
+ void bubbleTextMoveProc(Feature *feature);
+ void bubbleTextDrawProc(Feature *feature);
+
+protected:
+ MohawkEngine_CSTime *_vm;
+};
+
+class CSTimeView : public View {
+public:
+ CSTimeView(MohawkEngine_CSTime *vm);
+
+ uint32 getTime();
+ void setupView();
+ Feature *installViewFeature(uint16 scrbId, uint32 flags, Common::Point *pos);
+
+ void installGroup(uint16 resourceId, uint size, uint count, bool regs, uint16 baseId);
+ void removeGroup(uint16 resourceId);
+
+ void loadBitmapCursors(uint16 baseId);
+ void setBitmapCursor(uint16 id);
+ uint16 getBitmapCursor() { return _bitmapCursorId; }
+
+ void dragFeature(NewFeature *feature, Common::Point pos, uint mode, uint32 flags, Common::Rect *rect);
+
+protected:
+ MohawkEngine_CSTime *_timeVm;
+
+ uint16 _bitmapCursorId;
+
+ void removeObjectsUsingBaseId(uint16 baseId);
+ void freeShapesUsingGroupId(uint16 groupId);
+ void freeScriptsUsingGroupId(uint16 groupId);
+ void adjustShapeGroups(uint16 groupId);
+
+ void finishDraw();
+};
+
+} // End of namespace Mohawk
+
+#endif
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index 95ae18d273..f9a9a01c43 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -33,6 +33,7 @@
#include "mohawk/myst.h"
#include "mohawk/riven.h"
#include "mohawk/livingbooks.h"
+#include "mohawk/cstime.h"
namespace Mohawk {
@@ -231,8 +232,10 @@ bool MohawkMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGa
case Mohawk::GType_LIVINGBOOKSV3:
*engine = new Mohawk::MohawkEngine_LivingBooks(syst, gd);
break;
- case Mohawk::GType_ZOOMBINI:
case Mohawk::GType_CSTIME:
+ *engine = new Mohawk::MohawkEngine_CSTime(syst, gd);
+ break;
+ case Mohawk::GType_ZOOMBINI:
case Mohawk::GType_CSWORLD:
case Mohawk::GType_CSAMTRAK:
case Mohawk::GType_JAMESMATH:
diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp
index 84e9e57946..ece2a69864 100644
--- a/engines/mohawk/graphics.cpp
+++ b/engines/mohawk/graphics.cpp
@@ -28,6 +28,7 @@
#include "mohawk/myst.h"
#include "mohawk/riven.h"
#include "mohawk/livingbooks.h"
+#include "mohawk/cstime.h"
#include "common/substream.h"
#include "engines/util.h"
@@ -1022,4 +1023,36 @@ void LBGraphics::setPalette(uint16 id) {
}
}
+CSTimeGraphics::CSTimeGraphics(MohawkEngine_CSTime *vm) : GraphicsManager(), _vm(vm) {
+ _bmpDecoder = new MohawkBitmap();
+
+ initGraphics(640, 480, true);
+}
+
+CSTimeGraphics::~CSTimeGraphics() {
+ delete _bmpDecoder;
+}
+
+void CSTimeGraphics::drawRect(Common::Rect rect, byte color) {
+ rect.clip(Common::Rect(640, 480));
+
+ // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active.
+ if (!rect.isValidRect() || rect.width() == 0 || rect.height() == 0)
+ return;
+
+ Graphics::Surface *screen = _vm->_system->lockScreen();
+
+ screen->frameRect(rect, color);
+
+ _vm->_system->unlockScreen();
+}
+
+MohawkSurface *CSTimeGraphics::decodeImage(uint16 id) {
+ return _bmpDecoder->decodeImage(_vm->getResource(ID_TBMP, id));
+}
+
+Common::Array<MohawkSurface *> CSTimeGraphics::decodeImages(uint16 id) {
+ return _bmpDecoder->decodeImages(_vm->getResource(ID_TBMH, id));
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h
index 22dcb86aff..89189d442a 100644
--- a/engines/mohawk/graphics.h
+++ b/engines/mohawk/graphics.h
@@ -44,6 +44,7 @@ class MohawkEngine;
class MohawkEngine_Myst;
class MohawkEngine_Riven;
class MohawkEngine_LivingBooks;
+class MohawkEngine_CSTime;
class MohawkBitmap;
class MystBitmap;
@@ -243,6 +244,23 @@ private:
MohawkEngine_LivingBooks *_vm;
};
+class CSTimeGraphics : public GraphicsManager {
+public:
+ CSTimeGraphics(MohawkEngine_CSTime *vm);
+ ~CSTimeGraphics();
+
+ void drawRect(Common::Rect rect, byte color);
+
+protected:
+ MohawkSurface *decodeImage(uint16 id);
+ Common::Array<MohawkSurface *> decodeImages(uint16 id);
+ MohawkEngine *getVM() { return (MohawkEngine *)_vm; }
+
+private:
+ MohawkBitmap *_bmpDecoder;
+ MohawkEngine_CSTime *_vm;
+};
+
} // End of namespace Mohawk
#endif
diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk
index 892b99df50..82e38ad5b3 100644
--- a/engines/mohawk/module.mk
+++ b/engines/mohawk/module.mk
@@ -3,6 +3,11 @@ MODULE := engines/mohawk
MODULE_OBJS = \
bitmap.o \
console.o \
+ cstime.o \
+ cstime_cases.o \
+ cstime_game.o \
+ cstime_ui.o \
+ cstime_view.o \
cursors.o \
detection.o \
dialogs.o \