scummvm/engines/hadesch/rooms/riverstyx.cpp
2021-12-26 18:48:43 +01:00

436 lines
12 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
* Copyright 2020 Google
*
*/
#include "hadesch/hadesch.h"
#include "hadesch/video.h"
#include "hadesch/ambient.h"
namespace Hadesch {
enum {
kBackgroundZ = 10000
};
enum {
kDeadManEndAnim = 28014
};
struct StyxShadeInternal {
StyxShadeInternal(Common::String name) {
_name = name;
_counter = 0;
}
void resume() {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
_ambient.unpause();
room->enableMouse();
}
Common::String _name;
int _counter;
AmbientAnim _ambient;
Common::Array<Common::String> _sounds;
};
class StyxShadeEndSound : public EventHandler {
public:
StyxShadeEndSound(Common::SharedPtr<StyxShadeInternal> internal) {
_internal = internal;
}
void operator()() override {
_internal->resume();
}
private:
Common::SharedPtr<StyxShadeInternal> _internal;
};
// TODO: transparency and shimmering
class StyxShade {
public:
StyxShade(const Common::String &name, int zVal, int minInt, int maxInt,
const Common::String &ambient) {
_internal = makeInternal(name, zVal, minInt, maxInt, ambient);
}
StyxShade(const Common::String &name, int zVal, int minInt, int maxInt) {
_internal = makeInternal(name, zVal, minInt, maxInt, name + " ambient");
}
StyxShade() {
}
static Common::SharedPtr<StyxShadeInternal> makeInternal(const Common::String &name, int zVal, int minInt, int maxInt,
const Common::String &ambient) {
Common::SharedPtr<StyxShadeInternal> ret(new StyxShadeInternal(name));
ret->_ambient = AmbientAnim(ambient, ambient + " sound", zVal, minInt, maxInt,
AmbientAnim::KEEP_LOOP, Common::Point(0, 0), AmbientAnim::PAN_ANY);
return ret;
}
void start() {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
_internal->_ambient.start();
room->enableHotzone(_internal->_name);
}
void click() {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
if (_internal->_sounds.empty())
return;
_internal->_ambient.pause();
room->playVideo(_internal->_sounds[_internal->_counter % _internal->_sounds.size()],
800, EventHandlerWrapper(Common::SharedPtr<EventHandler>(new StyxShadeEndSound(_internal))));
_internal->_counter++;
room->disableMouse();
}
void addSound(const Common::String &snd) {
_internal->_sounds.push_back(snd);
}
private:
Common::SharedPtr<StyxShadeInternal> _internal;
};
class RiverStyxHandler : public Handler {
public:
RiverStyxHandler() {
_charonSound = false;
_cameraMovingDown = false;
_cameraMovingUp = false;
}
void handleClick(const Common::String &name) override {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
Persistent *persistent = g_vm->getPersistent();
if (name == "volcano top") {
room->disableMouse();
room->playAnimWithSFX("morphing gems", "morphing gems sound",
1000, PlayAnimParams::keepLastFrame().backwards(), 28018);
return;
}
if (name == "charon") {
// Originally it goes through event 28002, 1
if (persistent->_styxCharonUsedPotion) {
room->playVideo("charon assumes you have gold sound", 0, 28004);
} else {
room->playVideo(_charonSound ? "charon says away 2 sound" : "charon says away 1 sound", 0, 28004);
}
return;
}
if (name == "ax head") {
_axHead.click();
return;
}
if (name == "statue") {
_statue.click();
return;
}
if (name == "pillar") {
_pillar.click();
return;
}
if (name == "dog") {
_dog.click();
return;
}
if (name == "drowned man") {
_drownedMan.click();
return;
}
if (name == "trojan soldier") {
_trojanSoldier.click();
return;
}
if (name == "greek soldier") {
_greekSoldier.click();
return;
}
if (name == "alchemist") {
_alchemist.click();
return;
}
}
bool handleClickWithItem(const Common::String &name, InventoryItem item) override {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
Persistent *persistent = g_vm->getPersistent();
if (name == "charon" && item == kPotion) {
// Originally event 28002, kPotion
room->disableMouse();
g_vm->getHeroBelt()->removeFromInventory(item);
_charon.hide();
room->playVideo("charon glow", 549, 28005, Common::Point(516, 93));
g_vm->addTimer(28006, 2000, 1);
persistent->_styxCharonUsedPotion = true;
return true;
}
if (name == "charon" && item == kCoin) {
// Originally event 28002, kCoin
room->disableMouse();
g_vm->getHeroBelt()->removeFromInventory(item);
_charon.hide();
room->playVideo("change purse", 549, 28010, Common::Point(524, 100));
g_vm->addTimer(28008, 1000, 1);
persistent->_styxCharonUsedCoin = true;
return true;
}
return false;
}
void handleEvent(int eventId) override {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
Persistent *persistent = g_vm->getPersistent();
switch(eventId) {
case 28004:
stopCharonTalk();
if (persistent->_styxCharonUsedPotion && persistent->_styxCharonUsedCoin) {
_charon.hide();
room->playVideo("charon asks for help", 549, 28011, Common::Point(452, 96));
} else {
room->enableMouse();
}
break;
case 28005:
playCharonTalk("charon says quite dead sound", 28004);
break;
case 28006:
room->playMusic("charon glow sting", 28007);
break;
case 28008:
room->playMusic("charon accepts coin sting", 28009);
break;
case 28009:
if (persistent->_styxCharonUsedPotion && persistent->_styxCharonUsedCoin) {
handleEvent(28004);
} else {
playCharonTalk("charon takes an advance sound", 28004);
}
break;
case 28010:
_charon.unpauseAndFirstFrame();
break;
case 28011:
_charon.unpauseAndFirstFrame();
g_vm->moveToRoom(kFerrymanPuzzle);
break;
case 28017:
if (persistent->_quest == kRescuePhilQuest && !persistent->_styxAlchemistSaidIntro) {
persistent->_styxAlchemistSaidIntro = true;
_alchemist.click();
} else
room->enableMouse();
break;
case kDeadManEndAnim:
break;
case 28018:
room->selectFrame("overlay fade to volcano top", 1000, 0, Common::Point(0, -50));
room->stopAnim("overlay fade from volcano top");
_cameraMovingUp = true;
_cameraMovingStart = g_vm->getCurrentTime();
break;
case 28019:
g_vm->moveToRoom(kVolcanoRoom);
break;
}
}
void prepareRoom() override {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
Persistent *persistent = g_vm->getPersistent();
Quest quest = persistent->_quest;
room->loadHotZones("NearRivr.HOT", false);
room->enableHotzone("volcano top");
room->addStaticLayer("background", kBackgroundZ, Common::Point(0, -50));
room->selectFrame("sign text", 900, 0, Common::Point(0, -50));
AmbientAnim("bats 1", "bats 1 sound", 925, 8000, 12000,
AmbientAnim::DISAPPEAR, Common::Point(0, 0), AmbientAnim::PAN_ANY)
.start();
AmbientAnim("bats 2", "bats 2 sound", 925, 8000, 12000,
AmbientAnim::DISAPPEAR, Common::Point(0, 0), AmbientAnim::PAN_ANY)
.start();
AmbientAnim("terminal bats", "terminal bats sound", 925, 8000, 12000,
AmbientAnim::BACK_AND_FORTH, Common::Point(0, 0), AmbientAnim::PAN_ANY)
.start();
room->playAnimLoop("mist 1", 950);
room->playAnimLoop("mist 2", 950);
room->playAnimLoop("water", 951);
room->playAnimLoop("group of shades", 950);
room->selectFrame("morphing gems", 1000, -1);
if (quest == kRescuePhilQuest) {
room->selectFrame("ferry", 975, 0, Common::Point(0, -50));
_charon = AmbientAnim("charon", "charon sound", 550,
5000, 10000, AmbientAnim::KEEP_LOOP, Common::Point(0, 0),
AmbientAnim::PAN_ANY);
_charon.start();
room->enableHotzone("charon");
}
room->playMusicLoop(quest == kRescuePhilQuest ? "V4010eB0" : "V4010eA0");
_axHead = StyxShade("ax head", 800, 5000, 10000);
_axHead.addSound("ax head click sound 1");
_axHead.addSound("ax head click sound 2");
_axHead.addSound("ax head click sound 3");
_axHead.start();
if (quest == kRescuePhilQuest || quest == kCreteQuest) {
_pillar = StyxShade("pillar", 550, 8000, 12000);
if (quest == kRescuePhilQuest)
_pillar.addSound("pillar quest speech");
_pillar.addSound("pillar click sound");
_pillar.start();
}
if (quest == kCreteQuest || quest == kTroyQuest || quest == kMedusaQuest) {
_dog = StyxShade("dog", 600, 5000, 10000);
if (quest == kCreteQuest)
_dog.addSound("dog quest speech");
_dog.addSound("dog click sound 1");
_dog.addSound("dog click sound 2");
_dog.start();
}
if (quest == kCreteQuest || quest == kTroyQuest) {
_greekSoldier = StyxShade("greek soldier", 550, 5000, 10000);
if (quest == kTroyQuest)
_greekSoldier.addSound("greek soldier quest speech");
_greekSoldier.addSound("greek soldier click sound");
_greekSoldier.start();
}
if (quest == kTroyQuest) {
_trojanSoldier = StyxShade("trojan soldier", 650, 5000, 10000);
_trojanSoldier.addSound("trojan soldier quest speech");
_trojanSoldier.start();
}
if (quest == kMedusaQuest) {
_statue = StyxShade("statue", 700, 5000, 10000);
_statue.addSound("statue quest speech");
_statue.start();
_drownedMan = StyxShade("drowned man", 550, 5000, 10000);
_drownedMan.addSound("drowned man click sound 1");
_drownedMan.addSound("drowned man click sound 2");
_drownedMan.start();
}
if (quest == kRescuePhilQuest) {
_alchemist = StyxShade("alchemist", 750, 5000, 10000, "alchemist");
if (!persistent->_styxAlchemistSaidIntro)
_alchemist.addSound("alchemist intro");
if (persistent->_hintsAreEnabled) {
if ((persistent->isInInventory(kCoin) || persistent->_styxCharonUsedCoin)
&& (persistent->isInInventory(kPotion) || persistent->_styxCharonUsedPotion)) {
_alchemist.addSound("alchemist hint 2");
_alchemist.addSound("alchemist hint 3");
} else if (persistent->_creteVisitedAfterAlchemistIntro) {
_alchemist.addSound("alchemist hint 1");
}
}
if (persistent->_styxAlchemistSaidIntro)
_alchemist.addSound("alchemist intro");
_alchemist.addSound("alchemist click");
_alchemist.start();
if (!persistent->_styxAlchemistSaidIntro)
room->disableMouse();
}
// TODO: condition it on not restoring from save
room->setViewportOffset(Common::Point(0, -50));
_cameraMovingDown = true;
_cameraMovingStart = g_vm->getCurrentTime();
room->selectFrame("overlay fade from volcano top", 1000, 0, Common::Point(0, -50));
g_vm->getHeroBelt()->setColour(HeroBelt::kCold);
}
void frameCallback() override {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
if (_cameraMovingDown) {
int pos = -50 + 50 * (g_vm->getCurrentTime() - _cameraMovingStart) / 4000;
if (pos >= 0) {
handleEvent(28017);
pos = 0;
}
room->setViewportOffset(Common::Point(0, pos));
}
if (_cameraMovingUp) {
int pos = -50 * (g_vm->getCurrentTime() - _cameraMovingStart) / 4000;
if (pos < -50) {
handleEvent(28019);
pos = -50;
}
room->setViewportOffset(Common::Point(0, pos));
}
}
private:
void playCharonTalk(const Common::String &name, int event) {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
room->playVideo(name, 0, event);
_charon.hide();
room->playAnimLoop("charon talks", 550);
}
void stopCharonTalk() {
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
_charon.unpauseAndFirstFrame();
room->stopAnim("charon talks");
}
AmbientAnim _charon;
bool _charonSound;
bool _cameraMovingDown;
bool _cameraMovingUp;
int _cameraMovingStart;
StyxShade _axHead;
StyxShade _pillar;
StyxShade _dog;
StyxShade _drownedMan;
StyxShade _statue;
StyxShade _greekSoldier;
StyxShade _trojanSoldier;
StyxShade _alchemist;
};
Common::SharedPtr<Hadesch::Handler> makeRiverStyxHandler() {
return Common::SharedPtr<Hadesch::Handler>(new RiverStyxHandler());
}
}