/* 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. * */ #ifdef ENABLE_IHNM // Scripting module script function component #include "saga/saga.h" #include "saga/gfx.h" #include "saga/actor.h" #include "saga/animation.h" #include "saga/console.h" #include "saga/events.h" #include "saga/font.h" #include "saga/interface.h" #include "saga/music.h" #include "saga/itedata.h" #include "saga/puzzle.h" #include "saga/render.h" #include "saga/sound.h" #include "saga/sndres.h" #include "saga/resource.h" #include "saga/script.h" #include "saga/objectmap.h" #include "saga/scene.h" #include "saga/isomap.h" #include "common/config-manager.h" namespace Saga { void Script::setupIHNMScriptFuncList() { static const ScriptFunctionDescription IHNMScriptFunctionsList[IHNM_SCRIPT_FUNCTION_MAX] = { OPCODE(sfNull), OPCODE(sfWait), OPCODE(sfTakeObject), OPCODE(sfIsCarried), OPCODE(sfStatusBar), OPCODE(sfMainMode), OPCODE(sfScriptWalkTo), OPCODE(sfScriptDoAction), OPCODE(sfSetActorFacing), OPCODE(sfStartBgdAnim), OPCODE(sfStopBgdAnim), OPCODE(sfLockUser), OPCODE(sfPreDialog), OPCODE(sfKillActorThreads), OPCODE(sfFaceTowards), OPCODE(sfSetFollower), OPCODE(sfScriptGotoScene), OPCODE(sfSetObjImage), OPCODE(sfSetObjName), OPCODE(sfGetObjImage), OPCODE(sfGetNumber), OPCODE(sfScriptOpenDoor), OPCODE(sfScriptCloseDoor), OPCODE(sfSetBgdAnimSpeed), OPCODE(sfCycleColors), OPCODE(sfDoCenterActor), OPCODE(sfStartBgdAnimSpeed), OPCODE(sfScriptWalkToAsync), OPCODE(sfEnableZone), OPCODE(sfSetActorState), OPCODE(sfScriptMoveTo), OPCODE(sfSceneEq), OPCODE(sfDropObject), OPCODE(sfFinishBgdAnim), OPCODE(sfSwapActors), OPCODE(sfSimulSpeech), OPCODE(sfScriptWalk), OPCODE(sfCycleFrames), OPCODE(sfSetFrame), OPCODE(sfSetPortrait), OPCODE(sfSetProtagPortrait), OPCODE(sfChainBgdAnim), OPCODE(sfScriptSpecialWalk), OPCODE(sfPlaceActor), OPCODE(sfCheckUserInterrupt), OPCODE(sfScriptWalkRelative), OPCODE(sfScriptMoveRelative), OPCODE(sfSimulSpeech2), OPCODE(sfPsychicProfile), OPCODE(sfPsychicProfileOff), OPCODE(sfSetProtagState), OPCODE(sfResumeBgdAnim), OPCODE(sfThrowActor), OPCODE(sfWaitWalk), OPCODE(sfScriptSceneID), OPCODE(sfChangeActorScene), OPCODE(sfScriptClimb), OPCODE(sfSetDoorState), OPCODE(sfSetActorZ), OPCODE(sfScriptText), OPCODE(sfGetActorX), OPCODE(sfGetActorY), OPCODE(sfEraseDelta), OPCODE(sfPlayMusic), OPCODE(sfNull), OPCODE(sfEnableEscape), OPCODE(sfPlaySound), OPCODE(sfPlayLoopedSound), OPCODE(sfGetDeltaFrame), OPCODE(sfNull), OPCODE(sfNull), OPCODE(sfRand), OPCODE(sfFadeMusic), OPCODE(sfNull), OPCODE(sfSetChapterPoints), OPCODE(sfSetPortraitBgColor), OPCODE(sfScriptStartCutAway), OPCODE(sfReturnFromCutAway), OPCODE(sfEndCutAway), OPCODE(sfGetMouseClicks), OPCODE(sfResetMouseClicks), OPCODE(sfWaitFrames), OPCODE(sfScriptFade), OPCODE(sfScriptStartVideo), OPCODE(sfScriptReturnFromVideo), OPCODE(sfScriptEndVideo), OPCODE(sfSetActorZ), OPCODE(sfShowIHNMDemoHelpBg), OPCODE(sfAddIHNMDemoHelpTextLine), OPCODE(sfShowIHNMDemoHelpPage), OPCODE(sfVstopFX), OPCODE(sfVstopLoopedFX), OPCODE(sfDemoSetInteractive), // only used in the demo version of IHNM OPCODE(sfDemoIsInteractive), OPCODE(sfVsetTrack), OPCODE(sfGetPoints), OPCODE(sfSetGlobalFlag), OPCODE(sfClearGlobalFlag), OPCODE(sfTestGlobalFlag), OPCODE(sfSetPoints), OPCODE(sfSetSpeechBox), OPCODE(sfDebugShowData), OPCODE(sfWaitFramesEsc), OPCODE(sfQueueMusic), OPCODE(sfDisableAbortSpeeches) }; _scriptFunctionsList = IHNMScriptFunctionsList; } void Script::sfSetChapterPoints(SCRIPTFUNC_PARAMS) { int chapter = _vm->_scene->currentChapterNumber(); _vm->_ethicsPoints[chapter] = thread->pop(); int16 barometer = thread->pop(); static PalEntry cur_pal[PAL_ENTRIES]; PalEntry portraitBgColor = _vm->_interface->_portraitBgColor; byte portraitColor = (_vm->getLanguage() == Common::ES_ESP) ? 253 : 254; _vm->_spiritualBarometer = _vm->_ethicsPoints[chapter] * 256 / barometer; _vm->_scene->setChapterPointsChanged(true); // don't save this music when saving in IHNM // Set the portrait bg color, in case a saved state is restored from the // launcher. In this case, sfSetPortraitBgColor is not called, thus the // portrait color will always be 0 (black). if (portraitBgColor.red == 0 && portraitBgColor.green == 0 && portraitBgColor.blue == 0) portraitBgColor.green = 255; if (_vm->_spiritualBarometer > 255) _vm->_gfx->setPaletteColor(portraitColor, 0xff, 0xff, 0xff); else _vm->_gfx->setPaletteColor(portraitColor, _vm->_spiritualBarometer * portraitBgColor.red / 256, _vm->_spiritualBarometer * portraitBgColor.green / 256, _vm->_spiritualBarometer * portraitBgColor.blue / 256); _vm->_gfx->getCurrentPal(cur_pal); _vm->_gfx->setPalette(cur_pal); } void Script::sfSetPortraitBgColor(SCRIPTFUNC_PARAMS) { int16 red = thread->pop(); int16 green = thread->pop(); int16 blue = thread->pop(); _vm->_interface->setPortraitBgColor(red, green, blue); } void Script::sfScriptStartCutAway(SCRIPTFUNC_PARAMS) { int16 cut = thread->pop(); thread->pop(); // Not used int16 fade = thread->pop(); _vm->_anim->setCutAwayMode(kPanelCutaway); _vm->_anim->playCutaway(cut, fade != 0); } void Script::sfReturnFromCutAway(SCRIPTFUNC_PARAMS) { _vm->_anim->returnFromCutaway(); thread->wait(kWaitTypeWakeUp); } void Script::sfEndCutAway(SCRIPTFUNC_PARAMS) { _vm->_anim->endCutaway(); } void Script::sfGetMouseClicks(SCRIPTFUNC_PARAMS) { thread->_returnValue = _vm->getMouseClickCount(); } void Script::sfResetMouseClicks(SCRIPTFUNC_PARAMS) { _vm->resetMouseClickCount(); } void Script::sfWaitFrames(SCRIPTFUNC_PARAMS) { int16 frames = thread->pop(); if (!_skipSpeeches) thread->waitFrames(_vm->_frameCount + frames); } void Script::sfScriptFade(SCRIPTFUNC_PARAMS) { int16 firstPalEntry = thread->pop(); int16 lastPalEntry = thread->pop(); int16 startingBrightness = thread->pop(); int16 endingBrightness = thread->pop(); Event event; static PalEntry cur_pal[PAL_ENTRIES]; _vm->_gfx->getCurrentPal(cur_pal); event.type = kEvTImmediate; event.code = kPalEvent; event.op = kEventPalFade; event.time = 0; event.duration = kNormalFadeDuration; event.data = cur_pal; event.param = startingBrightness; event.param2 = endingBrightness; event.param3 = firstPalEntry; event.param4 = lastPalEntry - firstPalEntry + 1; _vm->_events->queue(event); } void Script::sfScriptStartVideo(SCRIPTFUNC_PARAMS) { int16 vid = thread->pop(); int16 fade = thread->pop(); _vm->_anim->setCutAwayMode(kPanelVideo); _vm->_anim->startVideo(vid, fade != 0); } void Script::sfScriptReturnFromVideo(SCRIPTFUNC_PARAMS) { _vm->_anim->returnFromVideo(); } void Script::sfScriptEndVideo(SCRIPTFUNC_PARAMS) { _vm->_anim->endVideo(); } void Script::sfShowIHNMDemoHelpBg(SCRIPTFUNC_PARAMS) { _ihnmDemoCurrentY = 0; _vm->_scene->_textList.clear(); _vm->_interface->setMode(kPanelConverse); _vm->_scene->showPsychicProfile(NULL); } void Script::sfAddIHNMDemoHelpTextLine(SCRIPTFUNC_PARAMS) { int stringId = thread->pop(); TextListEntry textEntry; Event event; textEntry.knownColor = kKnownColorBlack; textEntry.useRect = true; textEntry.rect.left = 245; textEntry.rect.setHeight(210 + 76); textEntry.rect.setWidth(226); textEntry.rect.top = 76 + _ihnmDemoCurrentY; textEntry.font = kKnownFontVerb; textEntry.flags = (FontEffectFlags)(kFontCentered); textEntry.text = thread->_strings->getString(stringId); TextListEntry *_psychicProfileTextEntry = _vm->_scene->_textList.addEntry(textEntry); event.type = kEvTOneshot; event.code = kTextEvent; event.op = kEventDisplay; event.data = _psychicProfileTextEntry; _vm->_events->queue(event); _ihnmDemoCurrentY += _vm->_font->getHeight(kKnownFontVerb, thread->_strings->getString(stringId), 226, kFontCentered); } void Script::sfShowIHNMDemoHelpPage(SCRIPTFUNC_PARAMS) { // Note: The IHNM demo changes panel mode to 8 (kPanelProtect in ITE) // when changing pages _vm->_interface->setMode(kPanelPlacard); _ihnmDemoCurrentY = 0; } void Script::sfVstopFX(SCRIPTFUNC_PARAMS) { _vm->_sound->stopSound(); } void Script::sfVstopLoopedFX(SCRIPTFUNC_PARAMS) { _vm->_sound->stopSound(); } void Script::sfDemoSetInteractive(SCRIPTFUNC_PARAMS) { if (thread->pop() == 0) { _vm->_interface->deactivate(); _vm->_interface->setMode(kPanelNull); } // Note: the original also sets an appropriate flag here, but we don't, // as we don't use it } void Script::sfDemoIsInteractive(SCRIPTFUNC_PARAMS) { thread->_returnValue = 0; } void Script::sfVsetTrack(SCRIPTFUNC_PARAMS) { int16 chapter = thread->pop(); int16 sceneNumber = thread->pop(); int16 actorsEntrance = thread->pop(); debug(2, "sfVsetTrrack(%d, %d, %d)", chapter, sceneNumber, actorsEntrance); _vm->_scene->changeScene(sceneNumber, actorsEntrance, kTransitionFade, chapter); } void Script::sfGetPoints(SCRIPTFUNC_PARAMS) { int16 index = thread->pop(); if (index >= 0 && index < ARRAYSIZE(_vm->_ethicsPoints)) thread->_returnValue = _vm->_ethicsPoints[index]; else thread->_returnValue = 0; } void Script::sfSetGlobalFlag(SCRIPTFUNC_PARAMS) { int16 flag = thread->pop(); if (flag >= 0 && flag < 32) _vm->_globalFlags |= (1 << flag); } void Script::sfClearGlobalFlag(SCRIPTFUNC_PARAMS) { int16 flag = thread->pop(); if (flag >= 0 && flag < 32) _vm->_globalFlags &= ~(1 << flag); } void Script::sfTestGlobalFlag(SCRIPTFUNC_PARAMS) { int16 flag = thread->pop(); if (flag >= 0 && flag < 32 && _vm->_globalFlags & (1 << flag)) thread->_returnValue = 1; else thread->_returnValue = 0; } void Script::sfSetPoints(SCRIPTFUNC_PARAMS) { int16 index = thread->pop(); int16 points = thread->pop(); if (index >= 0 && index < ARRAYSIZE(_vm->_ethicsPoints)) _vm->_ethicsPoints[index] = points; } void Script::sfSetSpeechBox(SCRIPTFUNC_PARAMS) { int16 param1 = thread->pop(); int16 param2 = thread->pop(); int16 param3 = thread->pop(); int16 param4 = thread->pop(); _vm->_actor->_speechBoxScript.left = param1; _vm->_actor->_speechBoxScript.top = param2; _vm->_actor->_speechBoxScript.setWidth(param3 - param1); _vm->_actor->_speechBoxScript.setHeight(param4 - param2); } void Script::sfDebugShowData(SCRIPTFUNC_PARAMS) { int16 param = thread->pop(); Common::String buf = Common::String::format("Reached breakpoint %d", param); _vm->_interface->setStatusText(buf.c_str()); } void Script::sfWaitFramesEsc(SCRIPTFUNC_PARAMS) { thread->_returnValue = _vm->_framesEsc; } void Script::sfQueueMusic(SCRIPTFUNC_PARAMS) { int16 param1 = thread->pop(); int16 param2 = thread->pop(); Event event; if (param1 < 0) { _vm->_music->stop(); return; } if (uint(param1) >= _vm->_music->_songTable.size()) { warning("sfQueueMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTable.size() - 1); } else { _vm->_music->setVolume(_vm->_musicVolume, 1); _vm->_events->queueMusic(_vm->_music->_songTable[param1], param2, _vm->ticksToMSec(1000)); if (!_vm->_scene->haveChapterPointsChanged()) { _vm->_scene->setCurrentMusicTrack(param1); _vm->_scene->setCurrentMusicRepeat(param2); } else { // Don't save this music track when saving in IHNM _vm->_scene->setChapterPointsChanged(false); } } } void Script::sfDisableAbortSpeeches(SCRIPTFUNC_PARAMS) { _vm->_interface->disableAbortSpeeches(thread->pop() != 0); } void Script::sfPsychicProfile(SCRIPTFUNC_PARAMS) { thread->wait(kWaitTypePlacard); _vm->_scene->showPsychicProfile(thread->_strings->getString(thread->pop())); } void Script::sfPsychicProfileOff(SCRIPTFUNC_PARAMS) { // This is called a while after the psychic profile is // opened, to close it automatically _vm->_scene->clearPsychicProfile(); } } // End of namespace Saga #endif