/* 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 3 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, see . * */ #include "engines/nancy/nancy.h" #include "engines/nancy/sound.h" #include "engines/nancy/resource.h" #include "engines/nancy/util.h" #include "engines/nancy/action/miscrecords.h" #include "engines/nancy/state/scene.h" #include "common/events.h" #include "common/config-manager.h" namespace Nancy { namespace Action { void PaletteThisScene::readData(Common::SeekableReadStream &stream) { _paletteID = stream.readByte(); _unknownEnum = stream.readByte(); _paletteStart = stream.readUint16LE(); _paletteSize = stream.readUint16LE(); } void PaletteThisScene::execute() { NancySceneState.getSceneInfo().paletteID = _paletteID; const State::Scene::SceneSummary &ssum = NancySceneState.getSceneSummary(); if (_unknownEnum > 1 && _unknownEnum < 3) { // Not sure what the difference is between the 3 types NancySceneState.getViewport().setPalette(ssum.palettes[_paletteID], _paletteStart, _paletteSize); } else { NancySceneState.getViewport().setPalette(ssum.palettes[_paletteID]); } finishExecution(); } void PaletteNextScene::readData(Common::SeekableReadStream &stream) { // Structure is the same as PaletteThisScene, but the original engine only uses the palette ID _paletteID = stream.readByte(); stream.skip(5); } void PaletteNextScene::execute() { NancySceneState.getNextSceneInfo().paletteID = _paletteID; _isDone = true; } void LightningOn::readData(Common::SeekableReadStream &stream) { _distance = stream.readSint16LE(); _pulseTime = stream.readUint16LE(); _rgbPercent = stream.readSint16LE(); stream.skip(4); } void SpecialEffect::readData(Common::SeekableReadStream &stream) { if (g_nancy->getGameType() <= kGameTypeNancy6) { _type = stream.readByte(); _fadeToBlackTime = stream.readUint16LE(); _frameTime = stream.readUint16LE(); } else { _type = stream.readByte(); _totalTime = stream.readUint16LE(); _fadeToBlackTime = stream.readUint16LE(); readRect(stream, _rect); } } void SpecialEffect::execute() { if (g_nancy->getGameType() <= kGameTypeNancy6) { NancySceneState.specialEffect(_type, _fadeToBlackTime, _frameTime); } else { NancySceneState.specialEffect(_type, _totalTime, _fadeToBlackTime, _rect); } _isDone = true; } void LightningOn::execute() { NancySceneState.beginLightning(_distance, _pulseTime, _rgbPercent); _isDone = true; } void TextBoxWrite::readData(Common::SeekableReadStream &stream) { int16 size = stream.readSint16LE(); if (size > 10000) { error("Action Record atTextboxWrite has too many text box chars: %d", size); } if (size == -1) { Common::String stringID; readFilename(stream, stringID); const CVTX *autotext = (const CVTX *)g_nancy->getEngineData("AUTOTEXT"); assert(autotext); _text = autotext->texts[stringID]; } else { char *buf = new char[size]; stream.read(buf, size); buf[size - 1] = '\0'; assembleTextLine(buf, _text, size); delete[] buf; } } void TextBoxWrite::execute() { auto &tb = NancySceneState.getTextbox(); tb.clear(); tb.addTextLine(_text); tb.setVisible(true); finishExecution(); } void TextboxClear::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void TextboxClear::execute() { NancySceneState.getTextbox().clear(); finishExecution(); } void BumpPlayerClock::readData(Common::SeekableReadStream &stream) { _relative = stream.readByte(); _hours = stream.readUint16LE(); _minutes = stream.readUint16LE(); } void BumpPlayerClock::execute() { NancySceneState.setPlayerTime(_hours * 3600000 + _minutes * 60000, _relative); finishExecution(); } void SaveContinueGame::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void SaveContinueGame::execute() { g_nancy->secondChance(); _isDone = true; } void TurnOffMainRendering::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void TurnOnMainRendering::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void ResetAndStartTimer::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void ResetAndStartTimer::execute() { NancySceneState.resetAndStartTimer(); _isDone = true; } void StopTimer::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void StopTimer::execute() { NancySceneState.stopTimer(); _isDone = true; } void GotoMenu::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void GotoMenu::execute() { //NancySceneState.setDestroyOnExit(); g_nancy->setState(NancyState::kMainMenu); _isDone = true; } void LoseGame::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void LoseGame::execute() { g_nancy->_sound->stopAndUnloadSceneSpecificSounds(); NancySceneState.setDestroyOnExit(); if (!ConfMan.hasKey("original_menus") || ConfMan.getBool("original_menus")) { g_nancy->setState(NancyState::kMainMenu); } else { Common::Event ev; ev.type = Common::EVENT_RETURN_TO_LAUNCHER; g_system->getEventManager()->pushEvent(ev); } _isDone = true; } void PushScene::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void PushScene::execute() { NancySceneState.pushScene(); _isDone = true; } void PopScene::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void PopScene::execute() { NancySceneState.popScene(); _isDone = true; } void WinGame::readData(Common::SeekableReadStream &stream) { stream.skip(1); } void WinGame::execute() { // Set ConfMan value that will stay persistent across future playthroughs. // Default value in original is StillWorkingOnIt, but we just don't set it instead. ConfMan.set("PlayerWonTheGame", "AcedTheGame", ConfMan.getActiveDomainName()); ConfMan.flushToDisk(); g_nancy->_sound->stopAndUnloadSceneSpecificSounds(); NancySceneState.setDestroyOnExit(); g_nancy->setState(NancyState::kCredits, NancyState::kMainMenu); _isDone = true; } void HintSystem::readData(Common::SeekableReadStream &stream) { _characterID = stream.readByte(); _genericSound.readNormal(stream); } void HintSystem::execute() { switch (_state) { case kBegin: { uint16 difficulty = NancySceneState.getDifficulty(); selectHint(); _genericSound.name = selectedHint->soundIDs[difficulty]; NancySceneState.getTextbox().clear(); NancySceneState.getTextbox().addTextLine(g_nancy->getStaticData().hintTexts[selectedHint->textID * 3 + difficulty]); g_nancy->_sound->loadSound(_genericSound); g_nancy->_sound->playSound(_genericSound); _state = kRun; break; } case kRun: if (!g_nancy->_sound->isSoundPlaying(_genericSound)) { g_nancy->_sound->stopSound(_genericSound); _state = kActionTrigger; } else { break; } // fall through case kActionTrigger: NancySceneState.useHint(_characterID, _hintID); NancySceneState.getTextbox().clear(); NancySceneState.changeScene(selectedHint->sceneChange); _isDone = true; break; } } void HintSystem::selectHint() { if (NancySceneState.getHintsRemaining() == 0) { selectedHint = &g_nancy->getStaticData().hints[_characterID][0]; } // Start from 1 since the first hint is always the "I give up" option for (uint i = 1; i < g_nancy->getStaticData().hints[_characterID].size(); ++i) { const auto &hint = g_nancy->getStaticData().hints[_characterID][i]; bool isSatisfied = true; for (const auto &cond : hint.conditions) { switch (cond.type) { case (byte)StaticDataConditionType::kEvent : if (!NancySceneState.getEventFlag(cond.label, cond.flag)) { isSatisfied = false; } break; case (byte)StaticDataConditionType::kInventory : if (NancySceneState.hasItem(cond.label) != cond.flag) { isSatisfied = false; } break; case (byte)StaticDataConditionType::kDifficulty : if ( (NancySceneState.getDifficulty() != cond.label && cond.flag != 0) || (NancySceneState.getDifficulty() == cond.label && cond.flag == 0) ) { isSatisfied = false; } break; } if (!isSatisfied) { break; } } if (isSatisfied) { selectedHint = &hint; break; } } } } // End of namespace Action } // End of namespace Nancy