/* 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 . * * Copyright 2020 Google * */ #include "hadesch/hadesch.h" #include "hadesch/video.h" #include "hadesch/ambient.h" namespace Hadesch { static const char *kQuestionBackground = "OverlayAnim"; static const char *kHadesEyes = "HadesEyesAnim"; static const char *kCounter = "CounterAnim"; enum { kBackgroundZ = 10000, kZeusLightAnimZ = 900, kHadesEyesZ = 850, kHadesAndZeusAnimZ = 850, kBigItemZ = 800, kSmallItemZ = 800, kFlameAnimZ = 800, kFlameBurstAnimZ = 799, kOverlayAnimZ = 550, kQuestionZ = 500, kCounterAnimZ = 549 }; static const char *questNames[] = { "None", "Crete", "Troy", "Medusa", "RescuePhil", "EndGame" }; static const char *statueNames[] = { "Bacchus", "Hermes", "Zeus", "Poseidon", "Ares", "Aphrodite", "Apollo", "Artemis", "Demeter", "Athena", "Hera", "Hephaestus", }; static const char *hadesIntroVideos[] = { "H0020bA0", "H0020bG0", "H0020bH0", "H0020bD0", "H0020bE0", "H0020bF0" }; static const TranscribedSound h0090_names[] = { { "H0090wF0", _hs("Congratulations. You've shown Mr Sour Grapes") }, { "H0090wA0", _hs("The enveloppe, please. And the winner is ... you. Hey, good job. That's showing him") }, { "H0090wB0", _hs("Way to go") }, // Difficult to hear. Please someone check after me { "H0090wE0", _hs("You're amazing. Or Hades is hard under the gollar") } }; static const int kNumQuestions = 4; static const int kNumAnswers = 5; enum { kZeusStingerFinished = 30006, kZeusGreatFinished = 30007, kHadesVideoFinished = 30008, kFirstQuestion = 30010, kHadesQuestionVideoFinished = 30011, kHadesJokeVideoFinished = 30012, kHadesNagging = 30013, kHadesNaggingCleanup = 30014, kNextQuestion = 30015, kNextQuestionAfterFail = 30016, kBuzzerFinished = 30017, kDingFinished = 30018, kHadesFirstLaugh = 30019, kHadesInstructions = 30020, kFinished = 30028, kBigItemSwitchToLoop = 1030001 }; class QuizHandler : public Handler { public: QuizHandler() { } void handleClick(const Common::String &name) override { Common::SharedPtr room = g_vm->getVideoRoom(); for (int ansidx = 0; ansidx < kNumAnswers; ansidx++) { if (name == Common::String::format("A%d", ansidx + 1)) { nextQuestion(ansidx); return; } } } void handleEvent(int eventId) override { Common::SharedPtr room = g_vm->getVideoRoom(); Persistent *persistent = g_vm->getPersistent(); switch (eventId) { case kBigItemSwitchToLoop: room->playAnimWithSFX( _bigItem, "SpinningItemSnd", kBigItemZ, PlayAnimParams::loop().partial(4, -1)); break; case kZeusStingerFinished: room->playVideo(_zeusGreat, 0, kZeusGreatFinished); break; case kZeusGreatFinished: room->stopAnim(_bigItem); room->playVideo(_hcQuest.get(_questName, "HadesBurst"), kHadesEyesZ, kHadesVideoFinished); break; case kHadesVideoFinished: room->selectFrame(kQuestionBackground, kOverlayAnimZ, 0); room->playAnimLoop("FlameAnim", kFlameAnimZ); room->playSFXLoop("FlameSnd"); room->playMusicLoop("AmbientQuestionMusic"); smallAnim(); playHadesVideo(hadesIntroVideos[g_vm->getRnd().getRandomNumberRng(0, ARRAYSIZE(hadesIntroVideos) - 1)], kFirstQuestion); break; case kNextQuestion: case kNextQuestionAfterFail: killQuestion(); if (_currentQuestion == kNumQuestions - 1) { room->stopAnim("AmbientQuestionMusic"); switch (_rightAnswerCount) { case 0: playHadesVideo(Common::String::format("H0090b%c0", 'E' + g_vm->getRnd().getRandomNumberRng(0, 2)), kFinished); break; case 1: case 2: playHadesVideo(Common::String::format("H0090b%c0", 'B' + g_vm->getRnd().getRandomNumberRng(0, 2)), kFinished); break; case 3: case 4: int v6 = g_vm->getRnd().getRandomNumberRng(0, 3); hadesAndZeus(h0090_names[v6], kFinished); break; } return; } _currentQuestion++; /* Fallthrough */ case kFirstQuestion: _pauseMouseover = false; memset(_frames, 0, sizeof (_frames)); smallAnim(); renderQuestion(); room->enableMouse(); _hadesCancelableVideo = true; playHadesVideo(getQuestionAttribute("HQuestion"), kHadesQuestionVideoFinished); break; case kHadesQuestionVideoFinished: playHadesVideo(getQuestionAttribute("HJoke"), kHadesJokeVideoFinished); break; case kHadesJokeVideoFinished: room->selectFrame(kHadesEyes, kHadesEyesZ, 0); _hadesIsFree = true; _hadesCancelableVideo = false; break; case kDingFinished: playHadesVideo(Common::String::format("H0080b%c0", 'A' + (_hades_dislike_counter % 5)), kNextQuestion); _hades_dislike_counter++; _rightAnswerCount++; break; case kHadesFirstLaugh: hadesAndZeus(TranscribedSound::make("ZeusNotFair", "Hold on, Hades. That's not fair. You've never explained the rules. That doesn't count"), kHadesInstructions); _hadesIsFree = false; break; case kHadesInstructions: hadesAndZeusEnd(); room->playAnimWithSFX("FlameBurstAnim", "FlameBurstSnd", kFlameBurstAnimZ, PlayAnimParams::disappear(), 30021); _shrinkLevel--; break; case kHadesNagging: if (_hadesIsFree) { playHadesVideo(Common::String::format("H0050b%c0", _naggingCounter + 'A'), kHadesNaggingCleanup); _naggingCounter = (_naggingCounter + 1) % 8; } break; case 30021: playHadesVideo("HadesInstructions", 30022); break; case 30022: room->selectFrame(kHadesEyes, kHadesEyesZ, 0); room->enableMouse(); _hadesIsFree = true; memset(_frames, 0, sizeof (_frames)); renderQuestion(); break; case kHadesNaggingCleanup: room->selectFrame(kHadesEyes, kHadesEyesZ, 0); _hadesIsFree = true; break; case kBuzzerFinished: room->playAnimWithSFX("FlameBurstAnim", "FlameBurstSnd", kFlameBurstAnimZ, PlayAnimParams::disappear()); _shrinkLevel++; if (_wrongAnswerCount == 0) { playHadesVideo("HadesLaugh", kHadesFirstLaugh); } else { playHadesVideo(Common::String::format("H0040b%c0", 'A' + (_hades_like_counter % 5)), kNextQuestionAfterFail); _hades_like_counter++; } _wrongAnswerCount++; break; case kFinished: persistent->_powerLevel[persistent->_quest-kCreteQuest] = countLevel(); g_vm->moveToRoom(kWallOfFameRoom); persistent->_quest = (Quest) (persistent->_quest + 1); persistent->clearInventory(); persistent->_doQuestIntro = true; break; } } void handleMouseOver(const Common::String &name) override { if (_pauseMouseover) return; for (int ansidx = 0; ansidx < kNumAnswers; ansidx++) { _frames[ansidx] = (name == Common::String::format("A%d", ansidx + 1)) ? 1 : 0; } renderQuestion(); } void handleMouseOut(const Common::String &name) override { if (_pauseMouseover) return; memset(_frames, 0, sizeof (_frames)); renderQuestion(); } void prepareRoom() override { Common::SharedPtr room = g_vm->getVideoRoom(); Persistent *persistent = g_vm->getPersistent(); Quest quest = persistent->_quest; room->loadHotZones("HadesCh.HOT", true); room->addStaticLayer("Background", kBackgroundZ); room->disableHeroBelt(); room->disableMouse(); _questName = questNames[quest]; _hcQuest = TextTable( Common::SharedPtr(room->openFile("HcQuest.txt")), 5); _hcQList = TextTable( Common::SharedPtr(room->openFile("HcQList.txt")), 12); _hchadesx = TextTable( Common::SharedPtr(room->openFile("HcHadesX.txt")), 2); _bigItem = _hcQuest.get(_questName, "BigItem"); _smallItem = _hcQuest.get(_questName, "SmallItem"); _zeusGreat = _hcQuest.get(_questName, "ZeusGreat"); room->playAnim(_bigItem, kBigItemZ, PlayAnimParams::keepLastFrame().partial(0, 4), kBigItemSwitchToLoop); room->playVideo("ZeusStingerSnd", 0, kZeusStingerFinished); Common::HashMap touchedStatues; for (int statue = kBacchusStatue; statue < kNumStatues; statue++) if (persistent->_statuesTouched[statue]) touchedStatues[statueNames[statue]] = true; Common::Array untouchedQuestions; Common::Array touchedQuestions; for (int i = 0; i < _hcQList.size(); i++) if (_hcQList.get(i, "Quest") == _questName) { Common::String statue = _hcQList.get(i, "Statue"); if (touchedStatues[statue]) touchedQuestions.push_back(i); else untouchedQuestions.push_back(i); } Common::Array candidates; candidates.push_back(untouchedQuestions); if (candidates.size() <= kNumQuestions) { candidates.push_back(touchedQuestions); } while (_chosenQuestions.size() <= kNumQuestions) { int x = candidates[g_vm->getRnd().getRandomNumberRng(0, candidates.size() - 1)]; bool valid = true; for (unsigned i = 0; i < _chosenQuestions.size(); i++) { if (_chosenQuestions[i] == x) { valid = false; break; } } if (!valid) continue; _chosenQuestions.push_back(x); } _currentQuestion = 0; _shrinkLevel = 0; _hadesCancelableVideo = false; memset(_frames, 0, sizeof (_frames)); _hades_dislike_counter = g_vm->getRnd().getRandomBit(); _hades_like_counter = g_vm->getRnd().getRandomBit(); _naggingCounter = g_vm->getRnd().getRandomBit(); g_vm->addTimer(kHadesNagging, 5000, -1); _rightAnswerCount = 0; _wrongAnswerCount = 0; _pauseMouseover = false; _hadesIsFree = false; } private: int countLevel() { switch (_rightAnswerCount) { case 0: return 1; case 1: case 2: return 2; case 3: case 4: default: return 3; } } void killQuestion() { Common::SharedPtr room = g_vm->getVideoRoom(); room->stopAnim(getQuestionAttribute("Question")); for (int ansidx = 0; ansidx < kNumAnswers; ansidx++) { room->stopAnim(getQuestionAttribute(Common::String::format("A%d", ansidx + 1))); } } void renderQuestion() { Common::SharedPtr room = g_vm->getVideoRoom(); room->selectFrame(kCounter, kCounterAnimZ, _currentQuestion); room->selectFrame(getQuestionAttribute("Question"), kQuestionZ, 0); Common::Point q0, qstep; if (getQuestionAttribute("PrePlaced") == "0") { q0 = Common::Point(0, 256); qstep = Common::Point(0, 22); } for (int ansidx = 0; ansidx < kNumAnswers; ansidx++) { room->selectFrame(getQuestionAttribute(Common::String::format("A%d", ansidx + 1)), kQuestionZ, _frames[ansidx], q0 + ansidx * qstep); } } void smallAnim() { Common::SharedPtr room = g_vm->getVideoRoom(); room->playAnim(_smallItem, kSmallItemZ, PlayAnimParams::loop().partial(_shrinkLevel * 30, _shrinkLevel * 30 + 29), EventHandlerWrapper()); } Common::String getQuestionAttribute(const Common::String &name) { return _hcQList.get(_chosenQuestions[_currentQuestion], name); } void playHadesVideo(const Common::String &name, int eventId) { Common::SharedPtr room = g_vm->getVideoRoom(); int x = 0; if (name == "HadesInstructions" || name == "HadesLaugh") { x = 110; } else x = _hchadesx.get(name, "X").asUint64(); room->stopAnim(kHadesEyes); room->stopAnim("HadesAndZeusAnim"); room->playVideo(name, kHadesEyesZ, eventId, Common::Point(x, 0)); _hadesIsFree = false; } int getRightAnswer() { return (int) getQuestionAttribute("RightAnswer").asUint64() - 1; } void nextQuestion(int selected) { Common::SharedPtr room = g_vm->getVideoRoom(); room->disableMouse(); if (_hadesCancelableVideo) room->cancelVideo(); _hadesCancelableVideo = false; _hadesIsFree = false; room->selectFrame(kHadesEyes, kHadesEyesZ, 0); if (selected == getRightAnswer()) room->playSFX("DingSnd", kDingFinished); else room->playSFX("BuzzerSnd", kBuzzerFinished); memset(_frames, 0, sizeof (_frames)); for (int ansidx = 0; ansidx < kNumAnswers; ansidx++) { _frames[ansidx] = 5; } if (selected == getRightAnswer() || _wrongAnswerCount != 0) { _frames[getRightAnswer()] = 1; } _pauseMouseover = true; renderQuestion(); } void hadesAndZeus(const TranscribedSound &name, int event) { Common::SharedPtr room = g_vm->getVideoRoom(); room->playAnimWithSpeech("HadesAndZeusAnim", name, kHadesAndZeusAnimZ, PlayAnimParams::keepLastFrame().partial(0, 5), event); room->playAnim("ZeusLightAnim", kZeusLightAnimZ, PlayAnimParams::keepLastFrame()); _hadesIsFree = false; } void hadesAndZeusEnd() { Common::SharedPtr room = g_vm->getVideoRoom(); room->playAnim("HadesAndZeusAnim", kHadesAndZeusAnimZ, PlayAnimParams::keepLastFrame().partial(6, 11)); room->playAnim("ZeusLightAnim", kZeusLightAnimZ, PlayAnimParams::disappear().backwards()); } TextTable _hcQuest; TextTable _hcQList; TextTable _hchadesx; Common::Array _chosenQuestions; int _currentQuestion; int _shrinkLevel; int _frames[kNumAnswers]; bool _pauseMouseover; int _hades_dislike_counter; int _hades_like_counter; int _rightAnswerCount; int _wrongAnswerCount; bool _hadesCancelableVideo; bool _hadesIsFree; int _naggingCounter; Common::String _questName; Common::String _bigItem, _smallItem, _zeusGreat; }; Common::SharedPtr makeQuizHandler() { return Common::SharedPtr(new QuizHandler()); } }