scummvm/engines/mohawk/cstime_ui.cpp
2011-05-25 10:50:46 -04:00

1411 lines
37 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "mohawk/cstime_game.h"
#include "mohawk/cstime_ui.h"
#include "mohawk/cstime_view.h"
#include "mohawk/resource.h"
#include "common/algorithm.h" // find
#include "common/events.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "graphics/fontman.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);
// The demo uses hardcoded system fonts
if (!(_vm->getFeatures() & GF_DEMO)) {
if (!_normalFont.loadFromFON("EvP14.fon"))
error("failed to load normal font");
if (!_dialogFont.loadFromFON("Int1212.fon"))
error("failed to load dialog font");
if (!_rolloverFont.loadFromFON("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;
}
const Graphics::Font &CSTimeInterface::getNormalFont() const {
// HACK: Use a ScummVM GUI font in place of a system one for the demo
if (_vm->getFeatures() & GF_DEMO)
return *FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
return _normalFont;
}
const Graphics::Font &CSTimeInterface::getDialogFont() const {
// HACK: Use a ScummVM GUI font in place of a system one for the demo
if (_vm->getFeatures() & GF_DEMO)
return *FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
return _dialogFont;
}
const Graphics::Font &CSTimeInterface::getRolloverFont() const {
// HACK: Use a ScummVM GUI font in place of a system one for the demo
if (_vm->getFeatures() & GF_DEMO)
return *FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
return _rolloverFont;
}
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) {
_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) {
_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) {
_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) {
_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())
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 behavior 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.
uint16 foundInvObjHotspot = 0xffff;
const Common::Array<CSTimeHotspot> &hotspots = scene->getHotspots();
for (uint i = 0; i < hotspots.size(); i++) {
if (hotspots[i].state != 1)
continue;
if (!hotspots[i].region.containsPoint(mousePos))
continue;
for (uint j = 0; j < invObj->hotspots.size(); j++) {
if (invObj->hotspots[j].sceneId != scene->getId())
continue;
if (invObj->hotspots[j].hotspotId != i)
continue;
if (foundInvObjHotspot != 0xffff && 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 != 0xffff) {
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, 0x600, NULL);
_vm->_haveInvItem[_draggedItem] = 0;
invObj->feature = NULL;
invObj->featureDisabled = true;
_inventoryDisplay->removeItem(_draggedItem);
} else if (!_inventoryDisplay->getCuffsState()) {
// Inactive cuffs.
// TODO: We never actually get here? Which would explain why it makes no sense.
_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;
invObj->feature = NULL;
invObj->featureDisabled = true;
}
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) {
invObj->feature = NULL;
invObj->featureDisabled = true;
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 behavior for torches in the dark tomb, in the first case.
if (_draggedItem == 1 && foundInvObjHotspot == 0xffff) {
// 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 = (uint)~0;
_currEntry = 0xffff;
_currHover = 0xffff;
_nextToProcess = 0xffff;
}
CSTimeHelp::~CSTimeHelp() {
}
void CSTimeHelp::addQaR(uint16 text, uint16 speech) {
CSTimeHelpQaR qar;
qar.text = text;
qar.speech = speech;
_qars.push_back(qar);
}
void CSTimeHelp::start() {
if (_vm->getInterface()->getInventoryDisplay()->getState() == 4)
return;
_state = 2;
uint16 speech = 5900 + _vm->_rnd->getRandomNumberRng(0, 2);
_vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, _vm->getCase()->getCurrScene()->getHelperId(), speech));
if (noHelperChanges())
return;
// Play a NIS, making sure the Good Guide is disabled.
_vm->addEvent(CSTimeEvent(kCSTimeEventCharSetState, _vm->getCase()->getCurrScene()->getHelperId(), 0));
_vm->addEvent(CSTimeEvent(kCSTimeEventCharPlayNIS, _vm->getCase()->getCurrScene()->getHelperId(), 0));
_vm->addEvent(CSTimeEvent(kCSTimeEventCharSetState, _vm->getCase()->getCurrScene()->getHelperId(), 0));
}
void CSTimeHelp::end(bool runEvents) {
_state = (uint)~0;
_currHover = 0xffff;
_vm->getInterface()->clearDialogArea();
_vm->getInterface()->getInventoryDisplay()->show();
if (noHelperChanges())
return;
_vm->addEvent(CSTimeEvent(kCSTimeEventCharSetState, _vm->getCase()->getCurrScene()->getHelperId(), 1));
_vm->addEvent(CSTimeEvent(kCSTimeEventCharSomeNIS55, _vm->getCase()->getCurrScene()->getHelperId(), 1));
}
void CSTimeHelp::cleanupAfterFlapping() {
if (_state == 2) {
// Startup.
_vm->getInterface()->getInventoryDisplay()->hide();
selectStrings();
display();
_state = 1;
return;
}
if (_nextToProcess == 0xffff)
return;
unhighlightLine(_nextToProcess);
_nextToProcess = 0xffff;
// TODO: case 18 hard-coding
}
void CSTimeHelp::mouseDown(Common::Point &pos) {
for (uint i = 0; i < _qars.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);
}
}
void CSTimeHelp::mouseMove(Common::Point &pos) {
bool mouseIsDown = _vm->getEventManager()->getButtonState() & 1;
for (uint i = 0; i < _qars.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 != 0xffff) {
if (_vm->getInterface()->cursorGetShape() != 3) {
unhighlightLine(_currHover);
_vm->getInterface()->cursorSetShape(1);
}
_currHover = 0xffff;
}
}
void CSTimeHelp::mouseUp(Common::Point &pos) {
if (_currEntry == 0xffff || _qars[_currEntry].speech == 0) {
_vm->getInterface()->cursorSetShape(1);
end();
return;
}
Common::Rect thisRect = _vm->getInterface()->_dialogTextRect;
thisRect.top += 1 + _currEntry*15;
thisRect.bottom = thisRect.top + 15;
if (!thisRect.contains(pos))
return;
_vm->addEvent(CSTimeEvent(kCSTimeEventCharStartFlapping, _vm->getCase()->getCurrScene()->getHelperId(), 5900 + _qars[_currEntry].speech));
_nextToProcess = _currEntry;
_askedAlready.push_back(_qars[_currEntry].text);
}
void CSTimeHelp::display() {
_vm->getInterface()->clearDialogArea();
for (uint i = 0; i < _qars.size(); i++) {
uint16 text = _qars[i].text;
bool askedAlready = Common::find(_askedAlready.begin(), _askedAlready.end(), text) != _askedAlready.end();
_vm->getInterface()->displayDialogLine(5900 + text, i, askedAlready ? 13 : 32);
}
}
void CSTimeHelp::highlightLine(uint line) {
uint16 text = _qars[line].text;
_vm->getInterface()->displayDialogLine(5900 + text, line, 244);
}
void CSTimeHelp::unhighlightLine(uint line) {
uint16 text = _qars[line].text;
bool askedAlready = Common::find(_askedAlready.begin(), _askedAlready.end(), text) != _askedAlready.end();
_vm->getInterface()->displayDialogLine(5900 + text, line, askedAlready ? 13 : 32);
}
void CSTimeHelp::selectStrings() {
_qars.clear();
_vm->getCase()->selectHelpStrings();
}
bool CSTimeHelp::noHelperChanges() {
// These are hardcoded.
if (_vm->getCase()->getId() == 4 && _vm->getCase()->getCurrScene()->getId() == 5)
return true;
if (_vm->getCase()->getId() == 5)
return true;
if (_vm->getCase()->getId() == 14 && _vm->getCase()->getCurrScene()->getId() == 4)
return true;
if (_vm->getCase()->getId() == 17 && _vm->getCase()->getCurrScene()->getId() == 2)
return true;
return false;
}
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] == 0xffff)
continue;
CSTimeInventoryObject *invObj = _vm->getCase()->_inventoryObjs[_displayedItems[i]];
if (invObj->featureDisabled)
continue;
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] == 0xffff)
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] == 0xffff)
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] = 0xffff;
// We always start out with the Time Cuffs.
_vm->_haveInvItem[TIME_CUFFS_ID] = 1;
insertItemInDisplay(TIME_CUFFS_ID);
_cuffsState = false;
}
void CSTimeInventoryDisplay::insertItemInDisplay(uint16 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] == 0xffff) {
_displayedItems[i] = id;
return;
}
error("couldn't insert item into display");
}
void CSTimeInventoryDisplay::removeItem(uint16 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] = 0xffff;
}
void CSTimeInventoryDisplay::mouseDown(Common::Point &pos) {
for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
if (_displayedItems[i] == 0xffff)
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] == 0xffff)
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] == 0xffff)
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);
invObj->featureDisabled = false;
}
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);
invObj->featureDisabled = false;
}
bool CSTimeInventoryDisplay::isItemDisplayed(uint16 id) {
for (uint i = 0; i < MAX_DISPLAYED_ITEMS; i++) {
if (_displayedItems[i] == id)
return true;
}
return false;
}
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;
uint16 id = 100;
if (_pieces[2] != 0xffff)
id += 5;
else if (_pieces[1] != 0xffff)
id += 4;
else
id += 2;
if (_feature)
_vm->getView()->removeFeature(_feature, true);
_feature = _vm->getView()->installViewFeature(id, kFeatureSortStatic | kFeatureNewNoLoop, NULL);
}
void CSTimeCarmenNote::drawBigNote() {
if (_vm->getCase()->getCurrConversation()->getState() != (uint)~0) {
_vm->getCase()->getCurrConversation()->end(false);
} else if (_vm->getInterface()->getHelp()->getState() != (uint)~0) {
_vm->getInterface()->getHelp()->end();
}
// TODO: kill symbols too
uint16 id = 100;
if (_pieces[2] != 0xffff)
id += 9;
else if (_pieces[1] != 0xffff)
id += 8;
else
id += 6;
if (_feature)
_vm->getView()->removeFeature(_feature, true);
_feature = _vm->getView()->installViewFeature(id, kFeatureSortStatic | kFeatureNewNoLoop, NULL);
// FIXME: attach note drawing proc
_state = 2;
}
void CSTimeCarmenNote::closeNote() {
_state = 0;
drawSmallNote();
}
CSTimeOptions::CSTimeOptions(MohawkEngine_CSTime *vm) : _vm(vm) {
_state = 0;
}
CSTimeOptions::~CSTimeOptions() {
}
} // End of namespace Mohawk