From 03caed6ce3303150d75185ca94334759901fe344 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 19 Apr 2008 22:22:00 +0000 Subject: Implemented some kyra opcodes and some chat handling code, now Malcolm should say something after crawling out of the dump. svn-id: r31581 --- engines/kyra/animator_v3.cpp | 32 +++++ engines/kyra/kyra_v3.cpp | 79 +++++++++- engines/kyra/kyra_v3.h | 81 ++++++++++- engines/kyra/resource.cpp | 2 +- engines/kyra/script_v3.cpp | 98 ++++++++++++- engines/kyra/text.h | 2 +- engines/kyra/text_v2.cpp | 2 +- engines/kyra/text_v3.cpp | 333 +++++++++++++++++++++++++++++++++++++++++++ engines/kyra/text_v3.h | 8 ++ 9 files changed, 626 insertions(+), 11 deletions(-) (limited to 'engines') diff --git a/engines/kyra/animator_v3.cpp b/engines/kyra/animator_v3.cpp index f9137d4c7e..8a88f24b3c 100644 --- a/engines/kyra/animator_v3.cpp +++ b/engines/kyra/animator_v3.cpp @@ -289,6 +289,12 @@ void KyraEngine_v3::refreshAnimObjectsIfNeed() { } } +void KyraEngine_v3::flagAnimObjsForRefresh() { + debugC(9, kDebugLevelAnimator, "KyraEngine_v3::flagAnimObjsForRefresh()"); + for (AnimObj *curEntry = _animList; curEntry; curEntry = curEntry->nextObject) + curEntry->needRefresh = true; +} + void KyraEngine_v3::updateCharacterAnim(int charId) { debugC(9, kDebugLevelAnimator, "KyraEngine_v3::updateCharacterAnim(%d)", charId); @@ -388,4 +394,30 @@ void KyraEngine_v3::removeSceneAnimObject(int anim, int refresh) { _sceneAnimMovie[anim]->close(); } +void KyraEngine_v3::setCharacterAnimDim(int w, int h) { + debugC(9, kDebugLevelAnimator, "KyraEngine_v3::setCharacterAnimDim(%d, %d)", w, h); + restorePage3(); + _charBackUpWidth = _animObjects[0].width; + _charBackUpWidth2 = _animObjects[0].width2; + _charBackUpHeight = _animObjects[0].height; + _charBackUpHeight2 = _animObjects[0].height2; + + _animObjects[0].width2 = (w - _charBackUpWidth) / 2; + _animObjects[0].height2 = h - _charBackUpHeight; + _animObjects[0].width = w; + _animObjects[0].height = h; +} + +void KyraEngine_v3::resetCharacterAnimDim() { + debugC(9, kDebugLevelAnimator, "KyraEngine_v3::resetCharacterAnimDim()"); + restorePage3(); + _animObjects[0].width2 = _charBackUpWidth2; + _animObjects[0].height2 = _charBackUpHeight2; + _animObjects[0].width = _charBackUpWidth; + _animObjects[0].height = _charBackUpHeight; + _charBackUpWidth2 = _charBackUpHeight2 = -1; + _charBackUpWidth = _charBackUpHeight = -1; +} + } // end of namespace Kyra + diff --git a/engines/kyra/kyra_v3.cpp b/engines/kyra/kyra_v3.cpp index ebf958d556..b61f82f9dc 100644 --- a/engines/kyra/kyra_v3.cpp +++ b/engines/kyra/kyra_v3.cpp @@ -61,7 +61,7 @@ KyraEngine_v3::KyraEngine_v3(OSystem *system, const GameFlags &flags) : KyraEngi _sceneList = 0; memset(&_mainCharacter, 0, sizeof(_mainCharacter)); _mainCharacter.sceneId = 9; - _mainCharacter.unk4 = 0x4C; + _mainCharacter.height = 0x4C; _mainCharacter.facing = 5; _mainCharacter.animFrame = 0x57; _mainCharacter.walkspeed = 5; @@ -96,6 +96,16 @@ KyraEngine_v3::KyraEngine_v3(OSystem *system, const GameFlags &flags) : KyraEngi _lastProcessedSceneScript = 0; _specialSceneScriptRunFlag = false; _pathfinderFlag = 0; + _talkObjectList = 0; + _chatText = 0; + _chatObject = -1; + memset(&_chatScriptState, 0, sizeof(_chatScriptState)); + memset(&_chatScriptData, 0, sizeof(_chatScriptData)); + _voiceSoundChannel = -1; + _charBackUpWidth2 = _charBackUpHeight2 = -1; + _charBackUpWidth = _charBackUpHeight = -1; + _useActorBuffer = false; + _curStudioSFX = 283; } KyraEngine_v3::~KyraEngine_v3() { @@ -138,6 +148,7 @@ KyraEngine_v3::~KyraEngine_v3() { delete _wsaSlots[i]; delete [] _sceneStrings; + delete [] _talkObjectList; } int KyraEngine_v3::init() { @@ -401,6 +412,42 @@ void KyraEngine_v3::playSoundEffect(int item, int volume) { } } +void KyraEngine_v3::playVoice(int high, int low) { + debugC(9, kDebugLevelMain, "KyraEngine_v3::playVoice(%d, %d)", high, low); + snd_playVoiceFile(high * 1000 + low); +} + +void KyraEngine_v3::snd_playVoiceFile(int file) { + debugC(9, kDebugLevelMain, "KyraEngine_v3::snd_playVoiceFile(%d)", file); + char filename[16]; + snprintf(filename, 16, "%u.AUD", (uint)file); + + Common::SeekableReadStream *stream = _res->getFileStream(filename); + if (stream) + _voiceSoundChannel = _soundDigital->playSound(stream, SoundDigital::kSoundTypeSpeech, 255); +} + +bool KyraEngine_v3::snd_voiceIsPlaying() { + debugC(9, kDebugLevelMain, "KyraEngine_v3::snd_voiceIsPlaying()"); + return _soundDigital->isPlaying(_voiceSoundChannel); +} + +void KyraEngine_v3::snd_stopVoice() { + debugC(9, kDebugLevelMain, "KyraEngine_v3::snd_stopVoice()"); + _soundDigital->stopSound(_voiceSoundChannel); +} + +void KyraEngine_v3::playStudioSFX() { + debugC(9, kDebugLevelMain, "KyraEngine_v3::playStudioSFX()"); + if (_rnd.getRandomNumberRng(1, 2) != 2) + return; + + playSoundEffect(_curStudioSFX++, 128); + + if (_curStudioSFX > 291) + _curStudioSFX = 283; +} + #pragma mark - void KyraEngine_v3::preinit() { @@ -491,7 +538,10 @@ void KyraEngine_v3::startup() { _screen->_curPage = 0; - //XXX + _talkObjectList = new TalkObject[88]; + memset(_talkObjectList, 0, sizeof(TalkObject)*88); + for (int i = 0; i < 88; ++i) + _talkObjectList[i].unk14 = -1; musicUpdate(0); updateMalcolmShapes(); @@ -941,6 +991,31 @@ void KyraEngine_v3::update() { _screen->updateScreen(); } +void KyraEngine_v3::updateWithText() { + debugC(9, kDebugLevelMain, "KyraEngine_v3::update()"); + updateInput(); + + musicUpdate(0); + updateMouse(); + //XXX + updateSpecialSceneScripts(); + updateCommandLine(); + //XXX + musicUpdate(0); + + restorePage3(); + drawAnimObjects(); + if (textEnabled() && _chatText) { + int curPage = _screen->_curPage; + _screen->_curPage = 2; + objectChatPrintText(_chatText, _chatObject); + _screen->_curPage = curPage; + } + refreshAnimObjects(0); + + _screen->updateScreen(); +} + void KyraEngine_v3::updateMouse() { debugC(9, kDebugLevelMain, "KyraEngine_v3::updateMouse()"); int shape = 0, offsetX = 0, offsetY = 0; diff --git a/engines/kyra/kyra_v3.h b/engines/kyra/kyra_v3.h index 3641aeb838..739da1ee9c 100644 --- a/engines/kyra/kyra_v3.h +++ b/engines/kyra/kyra_v3.h @@ -43,12 +43,14 @@ class TextDisplayer_v3; struct Button; class KyraEngine_v3 : public KyraEngine { +friend class TextDisplayer_v3; public: KyraEngine_v3(OSystem *system, const GameFlags &flags); ~KyraEngine_v3(); Screen *screen() { return _screen; } SoundDigital *soundDigital() { return _soundDigital; } + int language() const { return _lang; } int go(); @@ -74,6 +76,7 @@ private: void handleInput(int x, int y); void update(); + void updateWithText(); void updateMouse(); void delay(uint32 millis, bool update = false, bool isMainLoop = false); @@ -127,7 +130,15 @@ private: static const char *_sfxFileList[]; static const int _sfxFileListSize; - void snd_playVoiceFile(int) {} + int _voiceSoundChannel; + + void playVoice(int high, int low); + void snd_playVoiceFile(int file); + bool snd_voiceIsPlaying(); + void snd_stopVoice(); + + int _curStudioSFX; + void playStudioSFX(); // main menu void initMainMenu(); @@ -199,12 +210,20 @@ private: void refreshAnimObjects(int force); void refreshAnimObjectsIfNeed(); + void flagAnimObjsForRefresh(); + bool _loadingState; void updateCharacterAnim(int charId); void updateSceneAnim(int anim, int newFrame); void removeSceneAnimObject(int anim, int refresh); + int _charBackUpWidth2, _charBackUpHeight2; + int _charBackUpWidth, _charBackUpHeight; + + void setCharacterAnimDim(int w, int h); + void resetCharacterAnimDim(); + // interface uint8 *_interface; uint8 *_interfaceCommandLine; @@ -373,7 +392,7 @@ private: struct Character { uint16 sceneId; uint16 dlgIndex; - uint8 unk4; + uint8 height; uint8 facing; uint16 animFrame; //uint8 unk8, unk9; @@ -407,12 +426,66 @@ private: int _lastCharPalLayer; bool _charPalUpdate; + + // talk object + struct TalkObject { + char filename[13]; + int8 unkD; + int8 unkE; + int16 x, y; + uint8 color; + int8 unk14; + }; + + TalkObject *_talkObjectList; + + // chat + int _vocHigh; + + const char *_chatText; + int _chatObject; + uint32 _chatEndTime; + int _chatVocHigh, _chatVocLow; + + ScriptData _chatScriptData; + ScriptState _chatScriptState; + + int chatGetType(const char *text); + int chatCalcDuration(const char *text); + + void objectChat(const char *text, int object, int vocHigh, int vocLow); + void objectChatInit(const char *text, int object, int vocHigh, int vocLow); + void objectChatPrintText(const char *text, int object); + void objectChatProcess(const char *script); + void objectChatWaitToFinish(); + + // special script code + bool _temporaryScriptExecBit; + bool _useFrameTable; + + Common::Array _opcodesTemporary; + + int o3t_defineNewShapes(ScriptState *script); + int o3t_setCurrentFrame(ScriptState *script); + int o3t_playSoundEffect(ScriptState *script); + int o3t_getMalcolmShapes(ScriptState *script); + + // special shape code + char _newShapeFilename[13]; + int _newShapeLastEntry; + int _newShapeWidth, _newShapeHeight; + int _newShapeXAdd, _newShapeYAdd; + + int _newShapeAnimFrame; + int _newShapeDelay; + // unk uint8 *_unkBuffer1040Bytes; uint8 *_costPalBuffer; uint8 *_screenBuffer; uint8 *_gfxBackUpRect; uint8 *_paletteOverlay; + bool _useActorBuffer; int _unk3, _unk4, _unk5; @@ -422,8 +495,10 @@ private: // opcodes int o3_setCharacterPos(ScriptState *script); + int o3_defineObject(ScriptState *script); int o3_refreshCharacter(ScriptState *script); int o3_showSceneFileMessage(ScriptState *script); + int o3_objectChat(ScriptState *script); int o3_defineItem(ScriptState *script); int o3_queryGameFlag(ScriptState *script); int o3_resetGameFlag(ScriptState *script); @@ -446,6 +521,8 @@ private: int o3_setHiddenItemsEntry(ScriptState *script); int o3_getHiddenItemsEntry(ScriptState *script); int o3_removeSceneAnimObject(ScriptState *script); + int o3_setVocHigh(ScriptState *script); + int o3_getVocHigh(ScriptState *script); int o3_dummy(ScriptState *script); // misc diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp index 9d15100b65..8d09e2fa59 100644 --- a/engines/kyra/resource.cpp +++ b/engines/kyra/resource.cpp @@ -714,7 +714,7 @@ bool ResLoaderTlk::loadFile(const Common::String &filename, Common::SeekableRead entry.offset = resOffset+4; char realFilename[20]; - snprintf(realFilename, 20, "%.08u.AUD", resFilename); + snprintf(realFilename, 20, "%u.AUD", resFilename); uint32 curOffset = stream.pos(); stream.seek(entry.offset+2, SEEK_SET); diff --git a/engines/kyra/script_v3.cpp b/engines/kyra/script_v3.cpp index 789d2bf06e..b7f0e80acc 100644 --- a/engines/kyra/script_v3.cpp +++ b/engines/kyra/script_v3.cpp @@ -48,6 +48,20 @@ int KyraEngine_v3::o3_setCharacterPos(ScriptState *script) { return 0; } +int KyraEngine_v3::o3_defineObject(ScriptState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3_defineObject(%p) (%d, '%s', %d, %d, %d, %d, %d, %d)", (const void *)script, + stackPos(0), stackPosString(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + TalkObject &obj = _talkObjectList[stackPos(0)]; + strcpy(obj.filename, stackPosString(1)); + obj.unkD = stackPos(2); + obj.unkE = stackPos(3); + obj.x = stackPos(4); + obj.y = stackPos(5); + obj.color = stackPos(6); + obj.unk14 = stackPos(7); + return 0; +} + int KyraEngine_v3::o3_refreshCharacter(ScriptState *script) { debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3_refreshCharacter(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); const int frame = stackPos(0); @@ -75,6 +89,17 @@ int KyraEngine_v3::o3_showSceneFileMessage(ScriptState *script) { return 0; } +int KyraEngine_v3::o3_objectChat(ScriptState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3_objectChat(%p) (%d)", (const void *)script, stackPos(0)); + int id = stackPos(0); + const char *str = (const char*)getTableEntry(_useActorBuffer ? _actorFile : _sceneStrings, id); + if (str) { + objectChat(str, 0, _vocHigh, id); + playStudioSFX(); + } + return 0; +} + int KyraEngine_v3::o3_defineItem(ScriptState *script) { debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3_defineItem(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); int freeItem = findFreeItem(); @@ -312,11 +337,62 @@ int KyraEngine_v3::o3_removeSceneAnimObject(ScriptState *script) { return 0; } +int KyraEngine_v3::o3_setVocHigh(ScriptState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3_setVocHigh(%p) (%d)", (const void *)script, stackPos(0)); + _vocHigh = stackPos(0); + return 0; +} + +int KyraEngine_v3::o3_getVocHigh(ScriptState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3_getVocHigh(%p) ()", (const void *)script); + return _vocHigh; +} + int KyraEngine_v3::o3_dummy(ScriptState *script) { debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3_dummy(%p) ()", (const void *)script); return 0; } +#pragma mark - + +int KyraEngine_v3::o3t_defineNewShapes(ScriptState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3t_defineNewShapes(%p) ('%s', %d, %d, %d, %d, %d)", (const void *)script, + stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5)); + strcpy(_newShapeFilename, stackPosString(0)); + _newShapeLastEntry = stackPos(1); + _newShapeWidth = stackPos(2); + _newShapeHeight = stackPos(3); + _newShapeXAdd = stackPos(4); + _newShapeYAdd = stackPos(5); + return 0; +} + +int KyraEngine_v3::o3t_setCurrentFrame(ScriptState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3t_setCurrentFrame(%p) (%d, %d)", (const void *)script, stackPos(0), stackPos(1)); + static const uint8 frameTable[] = { + 0x58, 0xD8, 0xD8, 0x98, 0x78, 0x78, 0xB8, 0xB8 + }; + + _newShapeAnimFrame = stackPos(0); + if (_useFrameTable) + _newShapeAnimFrame += frameTable[_mainCharacter.facing]; + + _newShapeDelay = stackPos(1); + _temporaryScriptExecBit = true; + return 0; +} + +int KyraEngine_v3::o3t_playSoundEffect(ScriptState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3t_playSoundEffect(%p) (%d)", (const void *)script, stackPos(0)); + playSoundEffect(stackPos(0), 200); + return 0; +} + +int KyraEngine_v3::o3t_getMalcolmShapes(ScriptState *script) { + debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v3::o3t_getMalcolmShapes(%p) ()", (const void *)script); + return _malcolmShapes; +} + typedef Functor1Mem OpcodeV3; #define Opcode(x) OpcodeV3(this, &KyraEngine_v3::x) #define OpcodeUnImpl() OpcodeV3(this, 0) @@ -325,7 +401,7 @@ void KyraEngine_v3::setupOpcodeTable() { // 0x00 OpcodeUnImpl(), Opcode(o3_setCharacterPos), - OpcodeUnImpl(), + Opcode(o3_defineObject), Opcode(o3_refreshCharacter), // 0x04 OpcodeUnImpl(), @@ -361,7 +437,7 @@ void KyraEngine_v3::setupOpcodeTable() { OpcodeUnImpl(), OpcodeUnImpl(), OpcodeUnImpl(), - OpcodeUnImpl(), + Opcode(o3_objectChat), // 0x20 OpcodeUnImpl(), Opcode(o3_dummy), @@ -531,9 +607,9 @@ void KyraEngine_v3::setupOpcodeTable() { OpcodeUnImpl(), OpcodeUnImpl(), OpcodeUnImpl(), - OpcodeUnImpl(), + Opcode(o3_setVocHigh), // 0xa8 - OpcodeUnImpl(), + Opcode(o3_getVocHigh), OpcodeUnImpl(), OpcodeUnImpl(), OpcodeUnImpl(), @@ -546,6 +622,20 @@ void KyraEngine_v3::setupOpcodeTable() { for (int i = 0; i < ARRAYSIZE(opcodeTable); ++i) _opcodes.push_back(&opcodeTable[i]); + + static const OpcodeV3 tempOpcodeTable[] = { + Opcode(o3t_defineNewShapes), + Opcode(o3t_setCurrentFrame), + Opcode(o3t_playSoundEffect), + Opcode(o3_dummy), + // 0x0a + OpcodeUnImpl(), + Opcode(o3_getRand), + Opcode(o3_dummy) + }; + + for (int i = 0; i < ARRAYSIZE(tempOpcodeTable); ++i) + _opcodesTemporary.push_back(&tempOpcodeTable[i]); } } // end of namespace Kyra diff --git a/engines/kyra/text.h b/engines/kyra/text.h index d7cc5e9f81..5ae37e32ef 100644 --- a/engines/kyra/text.h +++ b/engines/kyra/text.h @@ -68,7 +68,7 @@ protected: enum { TALK_SUBSTRING_LEN = 80, - TALK_SUBSTRING_NUM = 5 + TALK_SUBSTRING_NUM = 6 }; char _talkBuffer[1040]; diff --git a/engines/kyra/text_v2.cpp b/engines/kyra/text_v2.cpp index c857a806e3..828371407d 100644 --- a/engines/kyra/text_v2.cpp +++ b/engines/kyra/text_v2.cpp @@ -48,7 +48,7 @@ void TextDisplayer_v2::restoreScreen() { _vm->restorePage3(); _vm->drawAnimObjects(); _screen->hideMouse(); - _screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, 2, 0); + _screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, 2, 0, Screen::CR_NO_P_CHECK); _screen->showMouse(); _vm->flagAnimObjsForRefresh(); _vm->refreshAnimObjects(0); diff --git a/engines/kyra/text_v3.cpp b/engines/kyra/text_v3.cpp index b02599faa4..fd621b879d 100644 --- a/engines/kyra/text_v3.cpp +++ b/engines/kyra/text_v3.cpp @@ -33,6 +33,101 @@ TextDisplayer_v3::TextDisplayer_v3(KyraEngine_v3 *vm, Screen_v3 *screen) : TextDisplayer(vm, screen), _vm(vm), _screen(screen) { } +char *TextDisplayer_v3::preprocessString(const char *str) { + debugC(9, kDebugLevelMain, "TextDisplayer_v3::preprocessString('%s')", str); + strcpy(_talkBuffer, str); + + char *p = _talkBuffer; + while (*p) { + if (*p++ == '\r') + return _talkBuffer; + } + + p = _talkBuffer; + Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT); + _screen->_charWidth = -2; + + const int maxTextWidth = (_vm->language() == 0) ? 176 : 240; + int textWidth = _screen->getTextWidth(p); + + if (textWidth > maxTextWidth) { + int count = 0, offs = 0; + if (textWidth > (3*maxTextWidth)) { + count = getCharLength(p, textWidth/4); + offs = dropCRIntoString(p, getCharLength(p, maxTextWidth), count); + p += count + offs; + } + + if (textWidth > (2*maxTextWidth)) { + count = getCharLength(p, textWidth/3); + offs = dropCRIntoString(p, getCharLength(p, maxTextWidth), count); + p += count + offs; + textWidth = _screen->getTextWidth(p); + } + + count = getCharLength(p, textWidth/2); + offs = dropCRIntoString(p, getCharLength(p, maxTextWidth), count); + p += count + offs; + textWidth = _screen->getTextWidth(p); + + if (textWidth > maxTextWidth) { + count = getCharLength(p, textWidth/2); + offs = dropCRIntoString(p, getCharLength(p, maxTextWidth), count); + } + } + + _screen->setFont(curFont); + return _talkBuffer; +} + +int TextDisplayer_v3::dropCRIntoString(char *str, int minOffs, int maxOffs) { + debugC(9, kDebugLevelMain, "TextDisplayer_v3::dropCRIntoString('%s', %d, %d)", str, maxOffs, minOffs); + + int offset = 0; + char *proc = str + minOffs; + + for (int i = minOffs; i < maxOffs; ++i) { + if (*proc == ' ') { + *proc = '\r'; + return offset; + } else if (*proc == '-') { + memmove(proc+1, proc, strlen(proc)+1); + *(++proc) = '\r'; + ++offset; + return offset; + } + + ++offset; + ++proc; + + if (!*proc) + return 0; + } + + offset = 0; + proc = str + minOffs; + for (int i = minOffs; i >= 0; --i) { + if (*proc == ' ') { + *proc = '\r'; + return offset; + } else if (*proc == '-') { + memmove(proc+1, proc, strlen(proc)+1); + *(++proc) = '\r'; + ++offset; + return offset; + } + + --offset; + --proc; + + if (!*proc) + return 0; + } + + *(str + minOffs) = '\r'; + return 0; +} + void TextDisplayer_v3::printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2, Screen::FontId font) { debugC(9, kDebugLevelMain, "TextDisplayer_v3::printText('%s', %d, %d, %d, %d, %d)", str, x, y, c0, c1, c2); uint8 colorMap[] = { 0, 255, 240, 240 }; @@ -45,4 +140,242 @@ void TextDisplayer_v3::printText(const char *str, int x, int y, uint8 c0, uint8 _screen->setFont(curFont); } +void TextDisplayer_v3::restoreScreen() { + debugC(9, kDebugLevelMain, "TextDisplayer_v3::restoreScreen()"); + _vm->restorePage3(); + _vm->drawAnimObjects(); + _screen->hideMouse(); + _screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, 2, 0, Screen::CR_NO_P_CHECK); + _screen->showMouse(); + _vm->flagAnimObjsForRefresh(); + _vm->refreshAnimObjects(0); +} + +void TextDisplayer_v3::calcWidestLineBounds(int &x1, int &x2, int w, int x) { + debugC(9, kDebugLevelMain, "TextDisplayer_v3::calcWidestLineBounds(%d, %d)", w, x); + x1 = x; + x1 -= (w >> 1); + x2 = x1 + w + 1; + + if (x1 + w >= 311) + x1 = 311 - w - 1; + + if (x1 < 8) + x1 = 8; + + x2 = x1 + w + 1; +} + +#pragma mark - + +int KyraEngine_v3::chatGetType(const char *str) { + debugC(9, kDebugLevelMain, "KyraEngine_v3::chatGetType('%s')", str); + while (*str) + ++str; + --str; + switch (*str) { + case '!': + return 2; + + case ')': + return 3; + + case '?': + return 1; + + case '.': + default: + return 0; + } +} + +int KyraEngine_v3::chatCalcDuration(const char *str) { + debugC(9, kDebugLevelMain, "KyraEngine_v3::chatCalcDuration('%s')", str); + return MAX(120, strlen(str)*6); +} + +void KyraEngine_v3::objectChat(const char *str, int object, int vocHigh, int vocLow) { + debugC(9, kDebugLevelMain, "KyraEngine_v3::objectChat('%s', %d, %d, %d)", str, object, vocHigh, vocLow); + + if (_mainCharacter.animFrame == 87 || _mainCharacter.animFrame == 0xFFFF || _mainCharacter.x1 <= 0 || _mainCharacter.y1 <= 0) + return; + + _chatVocLow = _chatVocHigh = -1; + objectChatInit(str, object, vocHigh, vocLow); + _chatText = str; + _chatObject = object; + int chatType = chatGetType(str); + + if (_mainCharacter.facing > 7) + _mainCharacter.facing = 5; + + static const uint8 talkScriptTable[] = { + 0x10, 0x11, 0x12, 0x13, + 0x0C, 0x0D, 0x0E, 0x0F, + 0x0C, 0x0D, 0x0E, 0x0F, + 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, + 0x08, 0x09, 0x0A, 0x0B, + 0x08, 0x09, 0x0A, 0x0B + }; + + static const char *talkFilenameTable[] = { + "MTFL00S.EMC", "MTFL00Q.EMC", "MTFL00E.EMC", "MTFL00T.EMC", + "MTFR00S.EMC", "MTFR00Q.EMC", "MTFR00E.EMC", "MTRF00T.EMC", + "MTL00S.EMC", "MTL00Q.EMC", "MTL00E.EMC", "MTL00T.EMC", + "MTR00S.EMC", "MTR00Q.EMC", "MTR00E.EMC", "MTR00T.EMC", + "MTA00S.EMC", "MTA00Q.EMC", "MTA00Q.EMC", "MTA00T.EMC" + }; + + int chat = talkScriptTable[chatType + _mainCharacter.facing * 4]; + objectChatProcess(talkFilenameTable[chat]); + _text->restoreScreen(); + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + updateCharacterAnim(0); + _chatText = 0; + _chatObject = -1; + //setNextIdleAnimTimer(); +} + +void KyraEngine_v3::objectChatInit(const char *str, int object, int vocHigh, int vocLow) { + debugC(9, kDebugLevelMain, "KyraEngine_v3::objectChatInit('%s', %d, %d, %d)", str, object, vocHigh, vocLow); + str = _text->preprocessString(str); + int lineNum = _text->buildMessageSubstrings(str); + + int xPos = 0, yPos = 0; + + if (!object) { + int scale = getScale(_mainCharacter.x1, _mainCharacter.y1); + yPos = _mainCharacter.y1 - ((_mainCharacter.height * scale) >> 8) - 8; + xPos = _mainCharacter.x1; + } else { + yPos = _talkObjectList[object].y; + xPos = _talkObjectList[object].x; + } + + yPos -= lineNum * 10; + yPos = MAX(yPos, 0); + _text->_talkMessageY = yPos; + _text->_talkMessageH = lineNum*10; + + int width = _text->getWidestLineWidth(lineNum); + _text->calcWidestLineBounds(xPos, yPos, width, xPos); + _text->_talkCoords.x = xPos; + _text->_talkCoords.w = width + 2; + + restorePage3(); + + _screen->hideMouse(); + + if (textEnabled()) { + objectChatPrintText(str, object); + _chatEndTime = _system->getMillis() + chatCalcDuration(str) * _tickLength; + } else { + _chatEndTime = _system->getMillis(); + } + + if (speechEnabled()) { + _chatVocHigh = vocHigh; + _chatVocLow = vocLow; + } else { + _chatVocHigh = _chatVocLow = -1; + } + + _screen->showMouse(); +} + +void KyraEngine_v3::objectChatPrintText(const char *str, int object) { + debugC(9, kDebugLevelMain, "KyraEngine_v3::objectChatPrintText('%s', %d)", str, object); + int c1 = _talkObjectList[object].color; + str = _text->preprocessString(str); + int lineNum = _text->buildMessageSubstrings(str); + int maxWidth = _text->getWidestLineWidth(lineNum); + int x = (object == 0) ? _mainCharacter.x1 : _talkObjectList[object].x; + int cX1 = 0, cX2 = 0; + _text->calcWidestLineBounds(cX1, cX2, maxWidth, x); + + for (int i = 0; i < lineNum; ++i) { + str = &_text->_talkSubstrings[i*_text->maxSubstringLen()]; + + int y = _text->_talkMessageY + i * 10; + x = _text->getCenterStringX(str, cX1, cX2); + + _text->printText(str, x, y, c1, 0xF0, 0); + } +} + +void KyraEngine_v3::objectChatProcess(const char *script) { + debugC(9, kDebugLevelMain, "KyraEngine_v3::objectChatProcess('%s')", script); + + memset(&_chatScriptData, 0, sizeof(_chatScriptData)); + memset(&_chatScriptState, 0, sizeof(_chatScriptState)); + + _scriptInterpreter->loadScript(script, &_chatScriptData, &_opcodesTemporary); + _scriptInterpreter->initScript(&_chatScriptState, &_chatScriptData); + _scriptInterpreter->startScript(&_chatScriptState, 0); + while (_scriptInterpreter->validScript(&_chatScriptState)) + _scriptInterpreter->runScript(&_chatScriptState); + + if (_chatVocHigh >= 0) { + playVoice(_chatVocHigh, _chatVocLow); + _chatVocHigh = _chatVocLow = -1; + } + + _useFrameTable = true; + objectChatWaitToFinish(); + _useFrameTable = false; + + _scriptInterpreter->unloadScript(&_chatScriptData); +} + +void KyraEngine_v3::objectChatWaitToFinish() { + debugC(9, kDebugLevelMain, "KyraEngine_v3::objectChatWaitToFinish()"); + int charAnimFrame = _mainCharacter.animFrame; + setCharacterAnimDim(_newShapeWidth, _newShapeHeight); + + _scriptInterpreter->initScript(&_chatScriptState, &_chatScriptData); + _scriptInterpreter->startScript(&_chatScriptState, 1); + + bool running = true; + const uint32 endTime = _chatEndTime; + resetSkipFlag(); + + while (running && !_quitFlag) { + if (!_scriptInterpreter->validScript(&_chatScriptState)) + _scriptInterpreter->startScript(&_chatScriptState, 1); + + _temporaryScriptExecBit = false; + while (!_temporaryScriptExecBit && _scriptInterpreter->validScript(&_chatScriptState)) { + musicUpdate(0); + _scriptInterpreter->runScript(&_chatScriptState); + } + + int curFrame = _newShapeAnimFrame; + uint32 delayTime = _newShapeDelay; + + _mainCharacter.animFrame = curFrame; + updateCharacterAnim(0); + + uint32 nextFrame = _system->getMillis() + delayTime * _tickLength; + + while (_system->getMillis() < nextFrame && !_quitFlag) { + updateWithText(); + + const uint32 curTime = _system->getMillis(); + if ((textEnabled() && !speechEnabled() && curTime > endTime) || (speechEnabled() && !snd_voiceIsPlaying()) || skipFlag()) { + resetSkipFlag(); + nextFrame = curTime; + running = false; + } + + delay(10); + } + } + + _mainCharacter.animFrame = charAnimFrame; + updateCharacterAnim(0); + resetCharacterAnimDim(); +} + } // end of namespace Kyra diff --git a/engines/kyra/text_v3.h b/engines/kyra/text_v3.h index c8c9be8e46..9271c6b520 100644 --- a/engines/kyra/text_v3.h +++ b/engines/kyra/text_v3.h @@ -33,10 +33,18 @@ namespace Kyra { class TextDisplayer_v3 : public TextDisplayer { +friend class KyraEngine_v3; public: TextDisplayer_v3(KyraEngine_v3 *vm, Screen_v3 *screen); + char *preprocessString(const char *str); + int dropCRIntoString(char *str, int minOffs, int maxOffs); + void printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2, Screen::FontId font=Screen::FID_8_FNT); + + void restoreScreen(); + + void calcWidestLineBounds(int &x1, int &x2, int w, int x); protected: KyraEngine_v3 *_vm; Screen_v3 *_screen; -- cgit v1.2.3