From b10f072c9e735b85edc297d243b12ae50a9fb833 Mon Sep 17 00:00:00 2001 From: Alyssa Milburn Date: Thu, 20 Jan 2011 21:35:00 +0000 Subject: MOHAWK: Add CSTime engine. svn-id: r55362 --- engines/mohawk/console.cpp | 51 ++ engines/mohawk/console.h | 15 + engines/mohawk/cstime.cpp | 529 +++++++++++++++++ engines/mohawk/cstime.h | 191 ++++++ engines/mohawk/cstime_cases.cpp | 241 ++++++++ engines/mohawk/cstime_cases.h | 47 ++ engines/mohawk/cstime_game.cpp | 1236 +++++++++++++++++++++++++++++++++++++++ engines/mohawk/cstime_game.h | 287 +++++++++ engines/mohawk/cstime_ui.cpp | 1186 +++++++++++++++++++++++++++++++++++++ engines/mohawk/cstime_ui.h | 254 ++++++++ engines/mohawk/cstime_view.cpp | 537 +++++++++++++++++ engines/mohawk/cstime_view.h | 97 +++ engines/mohawk/detection.cpp | 5 +- engines/mohawk/graphics.cpp | 33 ++ engines/mohawk/graphics.h | 18 + engines/mohawk/module.mk | 5 + 16 files changed, 4731 insertions(+), 1 deletion(-) create mode 100644 engines/mohawk/cstime.cpp create mode 100644 engines/mohawk/cstime.h create mode 100644 engines/mohawk/cstime_cases.cpp create mode 100644 engines/mohawk/cstime_cases.h create mode 100644 engines/mohawk/cstime_game.cpp create mode 100644 engines/mohawk/cstime_game.h create mode 100644 engines/mohawk/cstime_ui.cpp create mode 100644 engines/mohawk/cstime_ui.h create mode 100644 engines/mohawk/cstime_view.cpp create mode 100644 engines/mohawk/cstime_view.h (limited to 'engines') 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 \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 \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 \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 &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 &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 _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 &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 &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 &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 _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 _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 events; + Region region; +}; + +struct CSTimeLocation { + uint16 sceneId, hotspotId; +}; + +struct CSTimeInventoryHotspot { + uint16 sceneId, hotspotId, stringId; + Common::Array events; +}; + +#define TIME_CUFFS_ID 0 +struct CSTimeInventoryObject { + uint16 id, stringId, hotspotId, featureId, canTake; + Feature *feature; + Common::Array locations; + Common::Array hotspots; + Common::Array events; +}; + +struct CSTimeQaR { + bool finished; + uint16 id; + uint16 unknown1; + uint16 questionStringId; + uint16 responseStringId; + uint16 unknown2; + uint16 nextQaRsId; + Common::Array 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 _qars; + Common::Array _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 &getEvents(bool second); + const Common::Array &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 _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 _events, _events2; + Common::Array _chars; + Common::Array _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 _inventoryObjs; + +protected: + MohawkEngine_CSTime *_vm; + uint _id; + + uint _currScene; + uint16 _noteFeatureId[3]; + Common::Array _rolloverText; + Common::Array _scenes; + Common::Array _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 &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 &getDialogLines() { return _dialogLines; } + const Common::Array &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 _dialogLines; + Common::Array _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 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 &lines = _vm->getInterface()->getDialogLines(); + const Common::Array &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 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 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 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 \ -- cgit v1.2.3