mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-07 10:21:31 +00:00
436 lines
12 KiB
C++
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());
|
|
}
|
|
|
|
}
|