diff options
Diffstat (limited to 'engines/tsage/converse.cpp')
-rw-r--r-- | engines/tsage/converse.cpp | 948 |
1 files changed, 948 insertions, 0 deletions
diff --git a/engines/tsage/converse.cpp b/engines/tsage/converse.cpp new file mode 100644 index 0000000000..fd1dfe9e9d --- /dev/null +++ b/engines/tsage/converse.cpp @@ -0,0 +1,948 @@ +/* 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 "common/str-array.h" + +#include "tsage/tsage.h" +#include "tsage/globals.h" +#include "tsage/staticres.h" + +namespace tSage { + +#define STRIP_WORD_DELAY 30 + + +SequenceManager::SequenceManager() : Action() { + Common::set_to(&_objectList[0], &_objectList[6], (SceneObject *)NULL); + _sequenceData.clear(); + _field24 = 0; + _sequenceOffset = 0; + _resNum = 0; + _field26 = 0; + _objectIndex = 0; + _keepActive = false; + setup(); +} + +void SequenceManager::setup() { + _sequenceOffset = 0; + _objectIndex = 0; + _sceneObject = _objectList[0]; +} + +void SequenceManager::synchronise(Serialiser &s) { + s.syncAsSint32LE(_resNum); + s.syncAsSint32LE(_sequenceOffset); + s.syncAsByte(_keepActive); + s.syncAsSint32LE(_field24); + s.syncAsSint32LE(_field26); + + s.syncAsSint32LE(_objectIndex); + SYNC_POINTER(_sceneObject); + for (int i = 0; i < 6; ++i) + SYNC_POINTER(_objectList[i]); + + int seqSize = _sequenceData.size(); + s.syncAsUint32LE(seqSize); + if (s.isLoading()) + _sequenceData.resize(seqSize); + if (seqSize > 0) + s.syncBytes(&_sequenceData[0], seqSize); +} + +void SequenceManager::remove() { + if ((!_sequenceData.empty()) && !_keepActive) { + _sequenceData.clear(); + } + + if (_globals->_sceneObjects->contains(&_sceneText)) + _sceneText.remove(); + + Common::set_to(&_objectList[0], &_objectList[6], (SceneObject *)NULL); + Action::remove(); +} + +void SequenceManager::signal() { + if (_globals->_sceneObjects->contains(&_sceneText)) + _sceneText.hide(); + + bool continueFlag = true; + while (continueFlag) { + if (_sequenceOffset >=_sequenceData.size()) { + // Reached the end of the sequence + if (!_keepActive) + remove(); + break; + } + + uint16 idx = static_cast<uint16>(getNextValue() - 32000); + if (idx > 34) + continue; + + int16 v1, v2, v3; + switch (idx) { + case 0: + // Stop sequence + continueFlag = false; + break; + case 1: + _sceneObject->animate(ANIM_MODE_1, NULL); + break; + case 2: + _sceneObject->animate(ANIM_MODE_2, NULL); + break; + case 3: + _sceneObject->animate(ANIM_MODE_3); + break; + case 4: + v1 = getNextValue(); + v2 = getNextValue(); + _sceneObject->animate(ANIM_MODE_8, v1, v2 ? this : NULL); + break; + case 5: + v1 = getNextValue(); + v2 = getNextValue(); + _sceneObject->animate(ANIM_MODE_7, v1, v2 ? this : NULL); + break; + case 6: + v2 = getNextValue(); + _sceneObject->animate(ANIM_MODE_5, v2 ? this : NULL); + break; + case 7: + v2 = getNextValue(); + _sceneObject->animate(ANIM_MODE_6, v2 ? this : NULL); + break; + case 8: + v1 = getNextValue(); + v3 = getNextValue(); + v2 = getNextValue(); + _sceneObject->animate(ANIM_MODE_4, v1, v3, v2 ? this : NULL); + break; + case 9: + v1 = getNextValue(); + v3 = getNextValue(); + v2 = getNextValue(); + _globals->_sceneManager._scene->_sceneBounds.moveTo(v3, v2); + _globals->_sceneManager._scene->loadScene(v1); + break; + case 10: { + int resNum= getNextValue(); + int lineNum = getNextValue(); + int colour = getNextValue(); + int xp = getNextValue(); + int yp = getNextValue(); + int width = getNextValue(); + setMessage(resNum, lineNum, colour, Common::Point(xp, yp), width); + break; + } + case 11: + v1 = getNextValue(); + v2 = getNextValue(); + setAction(globalManager(), v2 ? this : NULL, v1, _objectList[0], _objectList[1], _objectList[2], _objectList[3], NULL); + break; + case 12: + v1 = getNextValue(); + setDelay(v1); + break; + case 13: { + v1 = getNextValue(); + v3 = getNextValue(); + v2 = getNextValue(); + NpcMover *mover = new NpcMover(); + Common::Point destPos(v1, v3); + _sceneObject->addMover(mover, &destPos, v2 ? this : NULL); + break; + } + case 14: + v1 = getNextValue(); + _sceneObject->_numFrames = v1; + break; + case 15: + v1 = getNextValue(); + _sceneObject->_field7A = v1; + break; + case 16: + v1 = getNextValue(); + v2 = getNextValue(); + _sceneObject->_moveDiff = Common::Point(v1, v2); + break; + case 17: + _sceneObject->hide(); + break; + case 18: + _sceneObject->show(); + break; + case 19: + v1 = getNextValue(); + _sceneObject->setVisage(v1); + break; + case 20: + v1 = getNextValue(); + _sceneObject->setStrip(v1); + break; + case 21: + v1 = getNextValue(); + _sceneObject->setFrame(v1); + break; + case 22: + v1 = getNextValue(); + _sceneObject->setPriority2(v1); + break; + case 23: + v1 = getNextValue(); + _sceneObject->changeZoom(v1); + break; + case 24: + v1 = getNextValue(); + v2 = getNextValue(); + v3 = getNextValue(); + _sceneObject->setPosition(Common::Point(v1, v2), v3); + break; + case 25: { + int yStart = getNextValue(); + int minPercent = getNextValue(); + int yEnd = getNextValue(); + int maxPercent = getNextValue(); + _globals->_sceneManager._scene->setZoomPercents(yStart, minPercent, yEnd, maxPercent); + break; + } + case 26: + v1 = getNextValue(); + v2 = getNextValue(); + _soundHandler.startSound(v1, v2 ? this : NULL, 127); + break; + case 27: { + v1 = getNextValue(); + v3 = getNextValue(); + v2 = getNextValue(); + PlayerMover *mover = new PlayerMover(); + Common::Point destPos(v1, v3); + _sceneObject->addMover(mover, &destPos, v2 ? this : NULL); + break; + } + case 28: + _objectIndex = getNextValue(); + _sceneObject = _objectList[_objectIndex]; + assert(_sceneObject); + break; + case 29: + _sceneObject->animate(ANIM_MODE_NONE); + break; + case 30: + v1 = getNextValue(); + _globals->_scrollFollower = (v1 == -1) ? NULL : _objectList[v1]; + break; + case 31: + _sceneObject->setObjectWrapper(new SceneObjectWrapper()); + break; + case 32: + _sceneObject->setObjectWrapper(NULL); + break; + case 33: + v1 = getNextValue(); + if (_keepActive) + setDelay(1); + else { + _sceneText.remove(); + _globals->_sceneManager._scene->_stripManager.start(v1, this); + } + break; + case 34: { + v1 = getNextValue(); + v2 = getNextValue(); + int objIndex1 = getNextValue(); + int objIndex2 = getNextValue(); + int objIndex3 = getNextValue(); + int objIndex4 = getNextValue(); + int objIndex5 = getNextValue(); + + setAction(globalManager(), v2 ? this : NULL, v1, _objectList[objIndex1], _objectList[objIndex2], + _objectList[objIndex3], _objectList[objIndex4], _objectList[objIndex5]); + break; + } + default: + error("SequenceManager::signal - Unknown action %d at offset %xh", idx, _sequenceOffset - 2); + break; + } + } +} + +void SequenceManager::process(Event &event) { + if (((event.eventType == EVENT_BUTTON_DOWN) || (event.eventType == EVENT_KEYPRESS)) && + !event.handled && _globals->_sceneObjects->contains(&_sceneText)) { + // Remove the text item + _sceneText.remove(); + } else { + Action::process(event); + } +} + + +void SequenceManager::attached(EventHandler *newOwner, EventHandler *fmt, va_list va) { + // Get the sequence number to use + _resNum = va_arg(va, int); + + byte *seqData = _vm->_dataManager->getResource(RES_SEQUENCE, _resNum, 0); + uint seqSize = _vm->_memoryManager.getSize(seqData); + + _sequenceData.resize(seqSize); + Common::copy(seqData, seqData + seqSize, &_sequenceData[0]); + + DEALLOCATE(seqData); + + Common::set_to(&_objectList[0], &_objectList[6], (SceneObject *)NULL); + for (int idx = 0; idx < 6; ++idx) { + _objectList[idx] = va_arg(va, SceneObject *); + if (!_objectList[idx]) + break; + } + + setup(); + // TODO: This is not particulary nice, since dummy is uninitialized. + // Since the default Action implementation does not access the va_list + // parameter it should be fine though. Still it would be nice to find + // a better solution to this. + va_list dummy; + Action::attached(newOwner, fmt, dummy); +} + +/** + * Returns the next Id in the sequence + */ +uint16 SequenceManager::getNextValue() { + uint16 result = READ_LE_UINT16(&_sequenceData[0] + _sequenceOffset); + _sequenceOffset += 2; + return result; +} + +void SequenceManager::setMessage(int resNum, int lineNum, int colour, const Common::Point &pt, int width) { + _sceneText._colour1 = colour; + _sceneText._colour2 = 0; + _sceneText._colour3 = 0; + _sceneText._fontNumber = 2; + _sceneText._width = width; + + // Get the display message + Common::String msg = _vm->_dataManager->getMessage(resNum, lineNum); + + // Get the needed rect, and move it to the desired position + Rect textRect; + _globals->gfxManager().getStringBounds(msg.c_str(), textRect, width); + Rect sceneBounds = _globals->_sceneManager._scene->_sceneBounds; + sceneBounds.collapse(4, 2); + textRect.moveTo(pt); + textRect.contain(sceneBounds); + + // Set the text message + _sceneText.setup(msg); + _sceneText.setPosition(Common::Point(textRect.left, textRect.top)); + _sceneText.setPriority2(255); + _sceneText.show(); + + // Set the delay based on the number of words + int numWords = 0; + const char *msgP = msg.c_str(); + while (*msgP) { + if (*msgP++ == ' ') + ++numWords; + } + + setDelay(numWords * 18 + 120); +} + +SequenceManager *SequenceManager::globalManager() { + return &_globals->_sequenceManager; +} + +/*--------------------------------------------------------------------------*/ + +ConversationChoiceDialog::ConversationChoiceDialog() { + _stdColour = 23; + _highlightColour = _globals->_scenePalette._colours.background; + _fontNumber = 1; +} + +int ConversationChoiceDialog::execute(const Common::StringArray &choiceList) { + _gfxManager._font.setFontNumber(_fontNumber); + + _bounds = Rect(20, 0, 20, 0); + _choiceList.clear(); + + // Set up the list of choices + int yp = 0; + for (uint idx = 0; idx < choiceList.size(); ++idx) { + Rect tempRect; + _gfxManager._font.getStringBounds(choiceList[idx].c_str(), tempRect, 265); + tempRect.moveTo(25, yp + 10); + + _choiceList.push_back(ChoiceEntry(choiceList[idx], tempRect)); + yp += tempRect.height() + 5; + _bounds.extend(tempRect); + } + _selectedIndex = _choiceList.size(); + + // Set the position for the dialog + _bounds.bottom -= 10; + yp = 180 - _bounds.height(); + _bounds.translate(0, yp); + _bounds.right = _bounds.left + 280; + + // Draw the dialog + draw(); + _globals->_events.showCursor(); + + // Event handling loop + Event event; + while (!_vm->getEventManager()->shouldQuit()) { + while (!_globals->_events.getEvent(event, EVENT_KEYPRESS | EVENT_BUTTON_DOWN | EVENT_MOUSE_MOVE) && + !_vm->getEventManager()->shouldQuit()) + ; + if (_vm->getEventManager()->shouldQuit()) + break; + + if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode >= Common::KEYCODE_1) && + (event.kbd.keycode <= (Common::KEYCODE_0 + (int)_choiceList.size()))) { + // Selected an option by number + _selectedIndex = event.kbd.keycode - Common::KEYCODE_1; + break; + } else if ((_selectedIndex != _choiceList.size()) && ((event.eventType == EVENT_BUTTON_DOWN) || + (event.eventType == EVENT_BUTTON_UP))) { + // Item selected + break; + } else { + // Check if any item is highlighted + event.mousePos.x -= _gfxManager._bounds.left; + event.mousePos.y -= _gfxManager._bounds.top; + + uint idx = 0; + while ((idx < _choiceList.size()) && !_choiceList[idx]._bounds.contains(event.mousePos.x, event.mousePos.y)) + ++idx; + + if (idx != _selectedIndex) { + if (_selectedIndex != _choiceList.size()) { + // De-highlight previously selected item + _gfxManager._font._colours.foreground = _stdColour; + _gfxManager._font.writeLines(_choiceList[_selectedIndex]._msg.c_str(), + _choiceList[_selectedIndex]._bounds, ALIGN_LEFT); + } + + _selectedIndex = idx; + + if (_selectedIndex != _choiceList.size()) { + // Highlight the new item + _gfxManager._font._colours.foreground = _highlightColour; + _gfxManager._font.writeLines(_choiceList[idx]._msg.c_str(), _choiceList[idx]._bounds, ALIGN_LEFT); + } + + } + } + } + + // Remove the dialog + remove(); + + return _selectedIndex; +} + +void ConversationChoiceDialog::draw() { + // Make a backup copy of the area the dialog will occupy + Rect tempRect = _bounds; + tempRect.collapse(-10, -10); + _savedArea = Surface_getArea(_globals->_gfxManagerInstance.getSurface(), tempRect); + + // Fill in the contents of the entire dialog + _gfxManager._bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + drawFrame(); + + _gfxManager._bounds = tempRect; + _gfxManager._font._colours.foreground = _stdColour; + _gfxManager.activate(); + + // Loop through writing the conversation choices + for (uint idx = 0; idx < _choiceList.size(); ++idx) { + Common::String strNum = Common::String::format("%d", idx + 1); + + // Write the choice number + _gfxManager._font.setPosition(13, _choiceList[idx]._bounds.top); + _gfxManager._font.writeString(strNum.c_str()); + + _gfxManager._font.writeLines(_choiceList[idx]._msg.c_str(), _choiceList[idx]._bounds, ALIGN_LEFT); + } + + _gfxManager.deactivate(); +} + +/*--------------------------------------------------------------------------*/ + +void Obj44::load(const byte *dataP) { + _id = READ_LE_UINT16(dataP); + for (int idx = 0; idx < OBJ44_LIST_SIZE; ++idx) + _field2[idx] = READ_LE_UINT16(dataP + 2 + idx * 2); + + const byte *listP = dataP + 0x10; + for (int idx = 0; idx < OBJ44_LIST_SIZE; ++idx, listP += 10) { + _list[idx]._id = READ_LE_UINT16(listP); + _list[idx]._scriptOffset = READ_LE_UINT16(listP + 2); + } + + _speakerOffset = READ_LE_UINT16(dataP + 0x42); +} + +void Obj44::synchronise(Serialiser &s) { + s.syncAsSint32LE(_id); + for (int i = 0; i < OBJ44_LIST_SIZE; ++i) + s.syncAsSint32LE(_field2[i]); + for (int i = 0; i < OBJ44_LIST_SIZE; ++i) + _list[OBJ44_LIST_SIZE].synchronise(s); + s.syncAsUint32LE(_speakerOffset); +} + +/*--------------------------------------------------------------------------*/ + +StripManager::StripManager() { + _callbackObject = NULL; + _activeSpeaker = NULL; + reset(); +} + +StripManager::~StripManager() { +} + +void StripManager::start(int stripNum, EventHandler *owner, StripCallback *callback) { + reset(); + + _stripNum = stripNum; + _callbackObject = callback; + _sceneNumber = _globals->_sceneManager._sceneNumber; + _sceneBounds = _globals->_sceneManager._scene->_sceneBounds; + _script.clear(); + + assert(owner); + owner->setAction(this, owner); +} + +void StripManager::reset() { + _actionIndex = 0; + _delayFrames = 0; + _owner = NULL; + _fmt = NULL; + _field2E6 = false; + _stripNum = -1; + _obj44Index = 0; + _field2E8 = 0; + _field20 = 0; + _activeSpeaker = NULL; + _textShown = false; + _callbackObject = NULL; + + _obj44List.clear(); + if (!_script.empty()) { + _script.clear(); + } +} + +void StripManager::load() { + // Get the script + byte *script = _vm->_dataManager->getResource(RES_STRIP, _stripNum, 2); + uint scriptSize = _vm->_memoryManager.getSize(script); + + _script.resize(scriptSize); + Common::copy(script, script + scriptSize, &_script[0]); + + DEALLOCATE(script); + + // Get the object list + byte *obj44List = _vm->_dataManager->getResource(RES_STRIP, _stripNum, 1); + int dataSize = _vm->_memoryManager.getSize(obj44List); + assert((dataSize % 0x44) == 0); + + byte *dataP = obj44List; + for (int idx = 0; idx < (dataSize / 0x44); ++idx, dataP += 0x44) { + Obj44 obj; + obj.load(dataP); + _obj44List.push_back(obj); + } + + DEALLOCATE(obj44List); +} + +void StripManager::synchronise(Serialiser &s) { + s.syncAsSint32LE(_stripNum); + s.syncAsSint32LE(_obj44Index); + s.syncAsSint32LE(_field20); + s.syncAsSint32LE(_sceneNumber); + _sceneBounds.synchronise(s); + SYNC_POINTER(_activeSpeaker); + s.syncAsByte(_textShown); + s.syncAsByte(_field2E6); + s.syncAsSint32LE(_field2E8); + + // Synchronise the item list + int arrSize = _obj44List.size(); + s.syncAsUint16LE(arrSize); + if (s.isLoading()) + _obj44List.resize(arrSize); + for (int i = 0; i < arrSize; ++i) + _obj44List[i].synchronise(s); + + // Synhcronise script data + int scriptSize = _script.size(); + s.syncAsUint16LE(scriptSize); + if (s.isLoading()) + _script.resize(scriptSize); + if (scriptSize > 0) + s.syncBytes(&_script[0], scriptSize); + + // Add speaker list + arrSize = _speakerList.size(); + s.syncAsUint16LE(arrSize); + if (s.isLoading()) + _speakerList.resize(arrSize); + for (int i = 0; i < arrSize; ++i) + SYNC_POINTER(_speakerList[i]); + + SYNC_POINTER(_callbackObject); +} + +void StripManager::remove() { + if (_textShown) { + if (_activeSpeaker) + _activeSpeaker->removeText(); + _textShown = false; + } + + if (_activeSpeaker) + _activeSpeaker->remove(); + + if (_sceneNumber != _globals->_sceneManager._scene->_sceneNumber) { + _globals->_sceneManager._scene->_sceneBounds = _sceneBounds; + _globals->_sceneManager._scene->loadScene(_sceneNumber); + } + + Action::remove(); +} + +void StripManager::signal() { + if (_textShown) { + _activeSpeaker->removeText(); + _textShown = false; + } + + if (_obj44Index < 0) { + EventHandler *owner = _fmt; + int stripNum = ABS(_obj44Index); + remove(); + + start(stripNum, owner); + return; + } else if (_obj44Index == 10000) { + // Reached end of strip + remove(); + return; + } + + // Run strip + + if (_obj44List.size() == 0) + // Load the data for the strip + load(); + + Obj44 &obj44 = _obj44List[_obj44Index]; + _field2E8 = obj44._id; + Common::StringArray choiceList; + + // Build up a list of script entries + int idx; + for (idx = 0; idx < OBJ44_LIST_SIZE; ++idx) { + if (!obj44._list[idx]._id) + break; + + // Get the next one + choiceList.push_back((const char *)&_script[0] + obj44._list[idx]._scriptOffset); + } + + int strIndex = 0; + if (choiceList.size() > 1) + // Get the user to select a conversation option + strIndex = _choiceDialog.execute(choiceList); + + if ((choiceList.size() != 1) && !_field2E6) + _delayFrames = 1; + else { + Speaker *speakerP = getSpeaker((const char *)&_script[0] + obj44._speakerOffset); + if (!speakerP) + error("Speaker not found. Screenplay: %s %d", (const char *)&_script[0] + obj44._speakerOffset, _stripNum); + + if (speakerP != _activeSpeaker) { + if (_activeSpeaker) + _activeSpeaker->remove(); + _activeSpeaker = speakerP; + + if ((_activeSpeaker->_newSceneNumber == -1) && (_globals->_sceneManager._scene->_sceneNumber != _sceneNumber)) { + _globals->_sceneManager._scene->_sceneBounds = _sceneBounds; + _globals->_sceneManager._scene->loadScene(_sceneNumber); + } + + _activeSpeaker->proc12(this); + } + + if (_callbackObject) { + for (idx = 0; idx < OBJ44_LIST_SIZE; ++idx) { + if (!obj44._field2[idx]) + break; + + _callbackObject->stripCallback(obj44._field2[idx]); + } + } + + _textShown = true; + _activeSpeaker->setText(choiceList[strIndex]); + } + + _obj44Index = getNewIndex(obj44._list[strIndex]._id); + if (_obj44Index == 10001) { + MessageDialog::show("Strip Failure: Node not found", OK_BTN_STRING); + _obj44Index = 0; + } +} + +void StripManager::process(Event &event) { + Action::process(event); + if (event.handled) + return; + + if ((event.eventType == EVENT_KEYPRESS) && (event.kbd.keycode == Common::KEYCODE_ESCAPE)) { + if (_obj44Index != 10000) { + int currIndex = _obj44Index; + while (!_obj44List[_obj44Index + 1]._id) { + _obj44Index = getNewIndex(_obj44List[_obj44Index]._id); + if ((_obj44Index < 0) || (_obj44Index == 10000)) + break; + currIndex = _obj44Index; + } + + _field2E8 = _obj44List[currIndex]._id; + } + + // Signal the end of the strip + _delayFrames = 0; + event.handled = true; + signal(); + } else if (event.eventType & (EVENT_BUTTON_DOWN | EVENT_KEYPRESS)) { + // Move to next sequence in the strip + _delayFrames = 0; + event.handled = true; + signal(); + } +} + +void StripManager::addSpeaker(Speaker *speaker) { + assert(_speakerList.size() < 100); + _speakerList.push_back(speaker); +} + +Speaker *StripManager::getSpeaker(const char *speakerName) { + for (uint idx = 0; idx < _speakerList.size(); ++idx) { + if (!strcmp(_speakerList[idx]->_speakerName.c_str(), speakerName)) + return _speakerList[idx]; + } + + return NULL; +} + +int StripManager::getNewIndex(int id) { + if (id == 10000) + return id; + + for (uint idx = 0; idx < _obj44List.size(); ++idx) { + if (_obj44List[idx]._id == id) { + return (id == 0) ? 10001 : idx; + } + } + + return 10001; +} + +/*--------------------------------------------------------------------------*/ + +Speaker::Speaker() : EventHandler() { + _newSceneNumber = -1; + _hideObjects = true; + _field18 = 0; + _textWidth = 140; + _textPos = Common::Point(10, 20); + _fontNumber = 2; + _textMode = ALIGN_LEFT; + _colour1 = _colour2 = _colour3 = _globals->_scenePalette._colours.foreground; + _action = NULL; + _speakerName = "SPEAKER"; +} + +void Speaker::synchronise(Serialiser &s) { + _fieldA.synchronise(s); + SYNC_POINTER(_field18); + s.syncString(_speakerName); + s.syncAsSint32LE(_newSceneNumber); + s.syncAsSint32LE(_oldSceneNumber); + _sceneBounds.synchronise(s); + s.syncAsSint32LE(_textWidth); + s.syncAsSint16LE(_textPos.x); s.syncAsSint16LE(_textPos.y); + s.syncAsSint32LE(_fontNumber); + SYNC_ENUM(_textMode, TextAlign); + s.syncAsSint16LE(_colour1); + s.syncAsSint16LE(_colour2); + s.syncAsSint16LE(_colour3); + s.syncAsByte(_hideObjects); +} + +void Speaker::remove() { + if (_hideObjects) + SceneObjectList::deactivate(); +} + +void Speaker::proc12(Action *action) { + _action = action; + if (_newSceneNumber != -1) { + _oldSceneNumber = _globals->_sceneManager._sceneNumber; + _sceneBounds = _globals->_sceneManager._scene->_sceneBounds; + _globals->_sceneManager._scene->loadScene(_newSceneNumber); + } + + if (_hideObjects) + // Activate the object list for display + _objectList.activate(); + + // Draw the speaker objects without any fading + FadeMode fadeMode = _globals->_sceneManager._fadeMode; + _globals->_sceneManager._fadeMode = FADEMODE_IMMEDIATE; + _globals->_sceneObjects->draw(); + _globals->_sceneManager._fadeMode = fadeMode; +} + +void Speaker::setText(const Common::String &msg) { + _globals->_sceneObjects->draw(); + + _sceneText._colour1 = _colour1; + _sceneText._colour2 = _colour2; + _sceneText._colour3 = _colour3; + _sceneText._width = _textWidth; + _sceneText._fontNumber = _fontNumber; + _sceneText._textMode = _textMode; + _sceneText.setup(msg); + _sceneText.setPosition(_textPos); + _sceneText.setPriority2(256); + + // Count the number of words (by spaces) in the string + const char *msgP = msg.c_str(); + int spaceCount = 0; + while (*msgP) { + if (*msgP++ == ' ') + ++spaceCount; + } + + int numFrames = spaceCount * STRIP_WORD_DELAY + 120; + if (_action) + _action->setDelay(numFrames); +} + +void Speaker::removeText() { + _sceneText.remove(); +} + +/*--------------------------------------------------------------------------*/ + +SpeakerGameText::SpeakerGameText() : Speaker() { + _speakerName = "GAMETEXT"; + _textPos = Common::Point(40, 40); + _textMode = ALIGN_CENTRE; + _colour1 = 7; + _textWidth = 230; + _hideObjects = false; +} + +/*--------------------------------------------------------------------------*/ + +ScreenSpeaker::ScreenSpeaker() : Speaker() { + _npc = NULL; + _textMode = ALIGN_CENTRE; +} + +void ScreenSpeaker::setText(const Common::String &msg) { + GfxManager gfxMan; + gfxMan.activate(); + gfxMan._font.setFontNumber(_fontNumber); + Rect textRect; + + _globals->gfxManager().getStringBounds(msg.c_str(), textRect, _textWidth); + if (_npc) { + textRect.centre(_npc->_position.x, _npc->_bounds.top - (textRect.height() / 2 + 10)); + } else { + textRect.centre(_globals->_sceneManager._scene->_sceneBounds.left + + (_globals->_sceneManager._scene->_sceneBounds.width() / 2), + _globals->_sceneManager._scene->_sceneBounds.top); + } + + Rect rect2 = _globals->_sceneManager._scene->_sceneBounds; + rect2.collapse(10, 6); + textRect.contain(rect2); + + _textPos.x = textRect.left; + _textPos.y = textRect.top; + Speaker::setText(msg); + + gfxMan.deactivate(); +} + +/*--------------------------------------------------------------------------*/ + +void SpeakerAction::signal() { + switch (_actionIndex++) { + case 0: + setDelay(_globals->_randomSource.getRandomNumber(60) + 60); + break; + case 1: + static_cast<SceneObject *>(_owner)->setFrame(1); + static_cast<SceneObject *>(_owner)->animate(ANIM_MODE_5, this, NULL); + break; + case 2: + setDelay(_globals->_randomSource.getRandomNumber(10)); + _actionIndex = 0; + break; + default: + break; + } +} + +/*--------------------------------------------------------------------------*/ + +void AnimatedSpeaker::removeText() { + Speaker::removeText(); + _object1.remove(); + _object2.remove(); + + _objectList.draw(); +} + +} // end of namespace tSage |