Initial commit
This commit is contained in:
362
engines/hadesch/rooms/argo.cpp
Normal file
362
engines/hadesch/rooms/argo.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
/* 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"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
static const char *kIslandNames = "islandnames";
|
||||
static const char *kMastHeadAnim = "mastheadanim";
|
||||
|
||||
enum {
|
||||
kSkyZ = 10200,
|
||||
kCloudsZ = 10100,
|
||||
kWavesRightZ = 10050,
|
||||
kWavesLeftZ = 10050,
|
||||
kBackgroundZ = 10000,
|
||||
kFlagsZ = 9000,
|
||||
kMastHeadZ = 8000,
|
||||
kChessPieceZ = 701,
|
||||
kIslandNamesZ = 601
|
||||
};
|
||||
|
||||
static const struct island {
|
||||
const char *hotname;
|
||||
const char *mouseoverAnim;
|
||||
TranscribedSound nameSound;
|
||||
const char *sfxSound;
|
||||
RoomId roomId;
|
||||
int zValue;
|
||||
} islands[] = {
|
||||
{"Phils", "a1030bh0", {"a1030nf0", _hs("Phil's") }, "a1030ef0", kWallOfFameRoom, 901},
|
||||
{"Medusa", "a1030bf0", {"a1030nc0", _hs("Medusa Isle")}, "a1030ed0", kMedIsleRoom, 901},
|
||||
{"Troy", "a1030bd0", {"a1030na0", _hs("Troy")}, "a1030eb0", kTroyRoom, 901},
|
||||
{"Seriphos", "a1030be0", {"a1030nd0", _hs("Seriphos")}, "a1030ec0", kSeriphosRoom, 801},
|
||||
{"Crete", "a1030bc0", {"a1030nb0", _hs("Crete")}, "a1030ea0", kCreteRoom, 801},
|
||||
{"Volcano", "a1030bg0", {"a1030ne0", _hs("Volcano island")}, "a1030ee0", kVolcanoRoom, 801},
|
||||
};
|
||||
|
||||
static const int nislands = ARRAYSIZE(islands);
|
||||
|
||||
static const TranscribedSound intros[] = {
|
||||
{ "a1150na0", _hs("Aye, welcome onboard ladie") },
|
||||
{ "a1150nb0", _hs("So, are you hero yet?") },
|
||||
{ "a1150nc0", _hs("So, are you heroine yet?") },
|
||||
{ "a1150nd0", _hs("So, made it back, you did? Frankly, I'm surprised") },
|
||||
{ "a1150ne0", _hs("Glad I'm, you're still alive. I hate sailing alone") },
|
||||
{ "a1150nf0", _hs("So where will we be headed now?") }
|
||||
};
|
||||
|
||||
static const TranscribedSound defaultOutros[] = {
|
||||
{ "a1170na0", _hs("Heave anchor") },
|
||||
{ "a1170nb0", _hs("Hurry, hoist the main") },
|
||||
{ "a1170nc0", _hs("All hands on deck. Man the sails") },
|
||||
{ "a1170nd0", _hs("Pull her to starboard and bring her around") },
|
||||
{ "a1170ne0", _hs("Pull back on that rudder. Hold her steady") }
|
||||
};
|
||||
|
||||
enum {
|
||||
kPlayIntro2 = 27001,
|
||||
kPlayIntro3 = 27002,
|
||||
kReturnToIdleEvent = 27003,
|
||||
kIdleEvent = 27008,
|
||||
kOutroFinished = 27009,
|
||||
kOutroFinishedCounter = 1027001,
|
||||
kMastSoundFinished = 1027002
|
||||
};
|
||||
|
||||
static const TranscribedSound
|
||||
getOutroName(RoomId dest) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
|
||||
switch (dest) {
|
||||
case kWallOfFameRoom:
|
||||
if (!persistent->_argoSailedInQuest[dest][quest])
|
||||
return TranscribedSound::make("philsfirst", "That'd be where the grand heroes and heroines of the world go to train");
|
||||
break;
|
||||
case kSeriphosRoom:
|
||||
if ((quest == kTroyQuest || quest == kCreteQuest) && !persistent->_argoSailedInQuest[dest][quest])
|
||||
return TranscribedSound::make("seriphoscretetroy", "This place be ruled by the evil tyrant king Polydectes");
|
||||
if (quest == kMedusaQuest && !persistent->_argoSailedInQuest[dest][quest])
|
||||
return TranscribedSound::make("seriphosperseus", "Arr, Perseus be in trouble deep. Could use a hand");
|
||||
break;
|
||||
case kMedIsleRoom:
|
||||
if (quest == kMedusaQuest && !persistent->_argoSailedInQuest[dest][quest])
|
||||
return TranscribedSound::make("medusabeware", "Beware of Medusa. She be one scary looking lady. All her mirrors be made of shatter-proof glass");
|
||||
break;
|
||||
case kTroyRoom:
|
||||
if (!persistent->isRoomVisited(kTroyRoom))
|
||||
return TranscribedSound::make("troytenyears", "For ten years now trojan and greek soldiers have been fighting that trojan war. Talk about job security");
|
||||
if (quest == kTroyQuest && !persistent->_argoSailedInQuest[dest][quest])
|
||||
return TranscribedSound::make("troyregards", "Send me regards to Odysseus");
|
||||
if (quest > kTroyQuest && !persistent->_argoSaidTroyFinally) {
|
||||
persistent->_argoSaidTroyFinally = true;
|
||||
return TranscribedSound::make("troyfinally", "Finally, the trojan war be over and Helen be back with Menelaus. Now those two can fight without an interruption");
|
||||
}
|
||||
break;
|
||||
case kCreteRoom:
|
||||
if (!persistent->isRoomVisited(kCreteRoom))
|
||||
return TranscribedSound::make("cretedaedalus", "This be where Daedalus, the inventor, lives");
|
||||
|
||||
if (quest != kCreteQuest && !persistent->_argoSaidCretePort)
|
||||
return TranscribedSound::make("creteport", "Crete, the famous international port of trade");
|
||||
break;
|
||||
case kVolcanoRoom:
|
||||
if (!persistent->isRoomVisited(kVolcanoRoom))
|
||||
return TranscribedSound::make("volcanotopfirst", "Know this: should you go down there, you may not come back");
|
||||
|
||||
if (quest == kRescuePhilQuest && !!persistent->_argoSailedInQuest[dest][quest])
|
||||
return TranscribedSound::make("volcanotopyoufirst", "Hah, many are monsters down there. Very dangerous. You go first");
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
int rnd = g_vm->getRnd().getRandomNumberRng(0, ARRAYSIZE(defaultOutros) - 1);
|
||||
debug("rnd = %d", rnd);
|
||||
return defaultOutros[rnd];
|
||||
}
|
||||
|
||||
class ArgoHandler : public Handler {
|
||||
public:
|
||||
ArgoHandler() {
|
||||
_prevId = kInvalidRoom;
|
||||
_destination = kInvalidRoom;
|
||||
_mastHeadIsBusy = false;
|
||||
}
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_destination = kInvalidRoom;
|
||||
for (unsigned i = 0; i < nislands; i++) {
|
||||
if (name == islands[i].hotname) {
|
||||
_destination = islands[i].roomId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_destination != kInvalidRoom) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
room->disableMouse();
|
||||
room->stopAnim("idlesound");
|
||||
if (_destination == _prevId) {
|
||||
playMastSound(TranscribedSound::make(
|
||||
"currentlocation",
|
||||
"Here be your current location, matie."),
|
||||
kOutroFinished);
|
||||
return;
|
||||
}
|
||||
|
||||
_outroCounter = 4;
|
||||
_cloudsMoving = true;
|
||||
_cloudsMoveStart = g_vm->getCurrentTime();
|
||||
playMastSound(getOutroName(_destination), kOutroFinishedCounter);
|
||||
room->playAnimWithSFX("wavesleft", "wavesleftSFX", kWavesLeftZ,
|
||||
PlayAnimParams::disappear(),
|
||||
kOutroFinishedCounter);
|
||||
room->playAnimWithSFX("wavesright", "wavesrightSFX", kWavesRightZ,
|
||||
PlayAnimParams::disappear(),
|
||||
kOutroFinishedCounter);
|
||||
room->playSFX("A1030eG0", kOutroFinishedCounter);
|
||||
persistent->_argoSailedInQuest[_destination][persistent->_quest] = true;
|
||||
}
|
||||
}
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
switch (eventId) {
|
||||
case kPlayIntro2:
|
||||
playMastSound(TranscribedSound::make("intro2", "Navigate by clicking on the island you want to go to"), kPlayIntro3);
|
||||
break;
|
||||
case kPlayIntro3:
|
||||
playMastSound(TranscribedSound::make(
|
||||
"intro3",
|
||||
"The map shall always show the location of the Argo in relation to the other islands in the region"),
|
||||
kReturnToIdleEvent);
|
||||
break;
|
||||
case kReturnToIdleEvent:
|
||||
_mastHeadIsBusy = false;
|
||||
room->enableMouse();
|
||||
break;
|
||||
case kOutroFinishedCounter:
|
||||
if (--_outroCounter > 0)
|
||||
break;
|
||||
// Fallthrough
|
||||
case kOutroFinished:
|
||||
room->selectFrame(kMastHeadAnim, kMastHeadZ, 0);
|
||||
g_vm->moveToRoom(_destination);
|
||||
break;
|
||||
case kIdleEvent:
|
||||
g_vm->addTimer(kIdleEvent, 30000);
|
||||
if (_mastHeadIsBusy)
|
||||
break;
|
||||
playMastSound(TranscribedSound::make("idlesound", "And what course lies ahead for you, matie?"), kMastSoundFinished);
|
||||
room->selectFrame(kMastHeadAnim, kMastHeadZ, 1);
|
||||
break;
|
||||
case 27301:
|
||||
room->playAnimWithSpeech(kMastHeadAnim, _mastSound, kMastHeadZ,
|
||||
PlayAnimParams::keepLastFrame().partial(8, 21), 27303);
|
||||
break;
|
||||
// 27302 was for event chaining and frame keeping
|
||||
case 27303:
|
||||
room->playAnim(kMastHeadAnim, kMastHeadZ,
|
||||
PlayAnimParams::keepLastFrame().partial(8, 0), _mastHeadEndEvent);
|
||||
break;
|
||||
case kMastSoundFinished:
|
||||
_mastHeadIsBusy = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void handleMouseOver(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
for (unsigned i = 0; i < nislands; i++) {
|
||||
if (name == islands[i].hotname) {
|
||||
room->selectFrame(kIslandNames, kIslandNamesZ, i);
|
||||
room->playAnimKeepLastFrame(islands[i].mouseoverAnim, islands[i].zValue);
|
||||
playMastSound(islands[i].nameSound);
|
||||
room->playSFXLoop(islands[i].sfxSound);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleMouseOut(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
for (unsigned i = 0; i < nislands; i++)
|
||||
if (name == islands[i].hotname) {
|
||||
if (_destination != islands[i].roomId) {
|
||||
room->stopAnim(kIslandNames);
|
||||
room->stopAnim(islands[i].mouseoverAnim);
|
||||
}
|
||||
room->stopAnim(islands[i].nameSound.soundName);
|
||||
room->stopAnim(islands[i].sfxSound);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
_prevId = g_vm->getPreviousRoomId();
|
||||
room->loadHotZones("argo.HOT");
|
||||
room->addStaticLayer("background", kBackgroundZ);
|
||||
Common::String sky;
|
||||
int chesspiece;
|
||||
Common::String bgsound;
|
||||
|
||||
switch (_prevId) {
|
||||
default:
|
||||
sky = "bluesky";
|
||||
chesspiece = 0;
|
||||
bgsound = "a1180ea0";
|
||||
break;
|
||||
case kSeriphosRoom:
|
||||
sky = "pinksky";
|
||||
chesspiece = 3;
|
||||
bgsound = "A1070eA0";
|
||||
break;
|
||||
case kMedIsleRoom:
|
||||
sky = "mauvesky";
|
||||
chesspiece = 1;
|
||||
bgsound = "a1210ea0";
|
||||
break;
|
||||
case kTroyRoom:
|
||||
sky = "goldsky";
|
||||
chesspiece = 2;
|
||||
bgsound = "a1190eb0";
|
||||
break;
|
||||
case kCreteRoom:
|
||||
sky = "bluesky";
|
||||
chesspiece = 4;
|
||||
bgsound = "a1180ea0";
|
||||
break;
|
||||
case kVolcanoRoom:
|
||||
sky = "pinksky";
|
||||
chesspiece = 5;
|
||||
bgsound = "a1210ea0";
|
||||
break;
|
||||
}
|
||||
room->addStaticLayer(sky, kSkyZ);
|
||||
room->playMusicLoop(bgsound);
|
||||
|
||||
room->selectFrame("chesspiece", kChessPieceZ, chesspiece);
|
||||
|
||||
room->disableMouse();
|
||||
// Originally event 4015
|
||||
if (!persistent->isRoomVisited(kArgoRoom))
|
||||
playMastSound(TranscribedSound::make(
|
||||
"intro1",
|
||||
"Sharpen up now, matie. You'll be on the Argo now. It's a hero of ships. It used to belong to Jason and his crew, the argonauts. And now it'll be here for you"),
|
||||
kPlayIntro2);
|
||||
else {
|
||||
int rnd = g_vm->getRnd().getRandomNumberRng(0, ARRAYSIZE(intros) - 1);
|
||||
debug("rnd = %d", rnd);
|
||||
if (rnd == 1 || rnd == 2)
|
||||
rnd = persistent->_gender == kFemale ? 2 : 1;
|
||||
playMastSound(intros[rnd], kReturnToIdleEvent);
|
||||
}
|
||||
|
||||
room->playAnimWithSFX("flags", "flagsSFX", kFlagsZ, PlayAnimParams::loop());
|
||||
g_vm->addTimer(kIdleEvent, 30000);
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kCool);
|
||||
room->playMusic("intromusic");
|
||||
_cloudsMoving = false;
|
||||
cloudMove(0);
|
||||
}
|
||||
|
||||
void frameCallback() override {
|
||||
if (_cloudsMoving) {
|
||||
cloudMove(g_vm->getCurrentTime() - _cloudsMoveStart);
|
||||
}
|
||||
}
|
||||
|
||||
void cloudMove(int cloudMoveTime) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
double div = cloudMoveTime / 15000.0;
|
||||
room->selectFrame("cloudright", kCloudsZ, 0, Common::Point(450, 0) + Common::Point(650, -50) * div);
|
||||
room->selectFrame("cloudmiddle", kCloudsZ, 1, Common::Point(220, 0) + Common::Point(220, -50) * div);
|
||||
room->selectFrame("cloudleft", kCloudsZ, 2, Common::Point(0, 0) + Common::Point(-200, -50) * div);
|
||||
}
|
||||
|
||||
private:
|
||||
void playMastSound(const TranscribedSound &sound, int event = kMastSoundFinished) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_mastSound = sound;
|
||||
_mastHeadEndEvent = event;
|
||||
_mastHeadIsBusy = true;
|
||||
room->playAnim(kMastHeadAnim, kMastHeadZ, PlayAnimParams::keepLastFrame().partial(1, 8), 27301);
|
||||
}
|
||||
|
||||
RoomId _prevId;
|
||||
RoomId _destination;
|
||||
int _outroCounter;
|
||||
int _cloudsMoveStart;
|
||||
bool _cloudsMoving;
|
||||
int _mastHeadEndEvent;
|
||||
bool _mastHeadIsBusy;
|
||||
TranscribedSound _mastSound;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeArgoHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new ArgoHandler());
|
||||
}
|
||||
|
||||
}
|
||||
434
engines/hadesch/rooms/athena.cpp
Normal file
434
engines/hadesch/rooms/athena.cpp
Normal file
@@ -0,0 +1,434 @@
|
||||
/* 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 {
|
||||
static const char *kAthenaAnim = "c8060ba0";
|
||||
static const char *kLights = "c8110bb0";
|
||||
|
||||
// Keep in order
|
||||
enum {
|
||||
kWonPuzzle = -2,
|
||||
kNowhere = -1,
|
||||
kBubo = 0,
|
||||
kArtist = 1,
|
||||
kOrator = 2,
|
||||
kScholar = 3,
|
||||
kWarrior = 4
|
||||
};
|
||||
static const int kNumPuzzleElements = 5;
|
||||
static const struct {
|
||||
const char *hotname;
|
||||
int lights[2];
|
||||
int rays[2];
|
||||
} puzzleElements[kNumPuzzleElements] = {
|
||||
{"Bubo", { kArtist, kOrator}, {2, 1}},
|
||||
{"Artist", { kNowhere, kOrator}, {6, 5}},
|
||||
{"Orator", { kArtist, kScholar}, {3, 4}},
|
||||
{"Scholar", { kNowhere, kWarrior}, {8, 9}},
|
||||
{"Warrior", { kScholar, kWonPuzzle}, {7, 10}}
|
||||
};
|
||||
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kLightsZ = 201
|
||||
};
|
||||
|
||||
enum {
|
||||
kPhilForgettingEnd = 23009,
|
||||
kPhilHardwareFinished = 23012,
|
||||
kIntroFinished = 1023001
|
||||
};
|
||||
|
||||
class AthenaHandler : public Handler {
|
||||
public:
|
||||
AthenaHandler() {
|
||||
_playAreYouForgetting = true;
|
||||
_playAthenaTempleHardware = true;
|
||||
_isPuzzleWon = false;
|
||||
_hintTimerLength = 20000;
|
||||
memset(_puzzleState, 0, sizeof (_puzzleState));
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
if (name == "Seriphos") {
|
||||
if (quest == kMedusaQuest) {
|
||||
if (persistent->_athenaPuzzleSolved &&
|
||||
(!persistent->_athenaSwordTaken
|
||||
|| !persistent->_athenaShieldTaken)
|
||||
&& persistent->_hintsAreEnabled
|
||||
&& _playAreYouForgetting) {
|
||||
_playAreYouForgetting = false;
|
||||
room->disableMouse();
|
||||
room->playVideo("c8020ba0", 0,
|
||||
kPhilForgettingEnd,
|
||||
Common::Point(0, 216));
|
||||
return;
|
||||
} else if (persistent->_athenaPuzzleSolved &&
|
||||
persistent->_athenaSwordTaken &&
|
||||
persistent->_athenaShieldTaken &&
|
||||
!persistent->_athenaPlayedPainAndPanic
|
||||
) {
|
||||
persistent->_athenaPlayedPainAndPanic = true;
|
||||
room->disableMouse();
|
||||
room->fadeOut(1000, 23019);
|
||||
return;
|
||||
} else if (persistent->_athenaPuzzleSolved &&
|
||||
!_playAthenaTempleHardware
|
||||
&& persistent->_hintsAreEnabled) {
|
||||
_playAthenaTempleHardware = false;
|
||||
room->disableMouse();
|
||||
room->playVideo("c8160ba0", 0,
|
||||
kPhilHardwareFinished,
|
||||
Common::Point(0, 216));
|
||||
return;
|
||||
}
|
||||
}
|
||||
g_vm->moveToRoom(kSeriphosRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Athena") {
|
||||
Common::Array<Common::String> videos;
|
||||
if (quest == kMedusaQuest && !persistent->_athenaPuzzleSolved) {
|
||||
videos.push_back(persistent->_gender == kMale ? "c8060wa0" : "c8060wb0");
|
||||
} else {
|
||||
videos.push_back("c8060wc0");
|
||||
videos.push_back("c8060wd0");
|
||||
}
|
||||
|
||||
room->playStatueSMK(kAthenaStatue,
|
||||
kAthenaAnim,
|
||||
1101,
|
||||
videos, 26, 42);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < kNumPuzzleElements; i++)
|
||||
if (name == puzzleElements[i].hotname) {
|
||||
handlePuzzleClick(i);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Sword") {
|
||||
persistent->_athenaSwordTaken = true;
|
||||
g_vm->getHeroBelt()->placeToInventory(kSword);
|
||||
room->stopAnim("c8130bf0");
|
||||
room->disableHotzone("Sword");
|
||||
room->disableMouse();
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"c8140wa0",
|
||||
"The magic sword will never leave Perseus' hand, "
|
||||
"so he can successfully cut off Medusa's head"),
|
||||
23026);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Shield") {
|
||||
persistent->_athenaShieldTaken = true;
|
||||
g_vm->getHeroBelt()->placeToInventory(kShield);
|
||||
room->stopAnim("c8130be0");
|
||||
room->disableHotzone("Shield");
|
||||
room->disableMouse();
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"c8150wa0",
|
||||
"Medusa can only turn Perseus to stone if he looks directly at her. "
|
||||
"He'll use this shield to block her gaze"),
|
||||
23027);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Athena's Sword") {
|
||||
room->disableMouse();
|
||||
room->playAnimLoop("c8010oc0", 2101);
|
||||
room->playVideo("c8080wa0", 0, 23043);
|
||||
room->playSFX("C8080eA1");
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Athena's Shield") {
|
||||
room->disableMouse();
|
||||
room->playAnimLoop("c8010ob0", 2101);
|
||||
room->playVideo("c8070wa0", 0, 23044);
|
||||
room->playSFX("C8080eA1");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
switch(eventId) {
|
||||
case kIntroFinished:
|
||||
room->stopAnim(kAthenaAnim);
|
||||
room->playAnim("c8110ba0", 0, PlayAnimParams::disappear(), 23035);
|
||||
room->enableMouse();
|
||||
_hintTimerLength = 20000;
|
||||
rescheduleHintTimer();
|
||||
break;
|
||||
case kPhilForgettingEnd:
|
||||
case kPhilHardwareFinished:
|
||||
case 23026:
|
||||
case 23027:
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 23007:
|
||||
handleEvent(23010);
|
||||
_hintTimerLength = 40000;
|
||||
rescheduleHintTimer(true);
|
||||
break;
|
||||
case 23008:
|
||||
room->playAnim("c8140ba0", 1101, PlayAnimParams::disappear(), 23015);
|
||||
room->playAnim("c8150ba0", 1101, PlayAnimParams::disappear(), 23016);
|
||||
room->playMusic("c8130ma0", 23020);
|
||||
room->playSFX("c8130eb0");
|
||||
room->playSFX("c8130ec0");
|
||||
break;
|
||||
case 23010:
|
||||
if (persistent->_hintsAreEnabled && room->isMouseEnabled()) {
|
||||
room->disableMouse();
|
||||
room->playVideo("c8170ba0", 0, 23011, Common::Point(0, 216));
|
||||
}
|
||||
break;
|
||||
case 23011:
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 23015:
|
||||
room->playAnimKeepLastFrame("c8130bf0", 1101, 23017);
|
||||
room->playSFX("c8130ef0");
|
||||
break;
|
||||
case 23016:
|
||||
room->playAnimKeepLastFrame("c8130be0", 1101, 23018);
|
||||
room->playSFX("c8130ee0");
|
||||
break;
|
||||
case 23017:
|
||||
room->enableHotzone("Sword");
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 23018:
|
||||
room->enableHotzone("Shield");
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 23019:
|
||||
room->resetFade();
|
||||
room->disableHeroBelt();
|
||||
room->resetLayers();
|
||||
room->addStaticLayer("c8180pa0", 9000);
|
||||
room->playSFX("g0261ma0", 23031);
|
||||
room->playSpeech(persistent->_gender == kMale
|
||||
? TranscribedSound::make(
|
||||
"c8180wa0",
|
||||
"Oh no. Why did I shine that light in Athena's temple. "
|
||||
"I was just trying to see what the hero was doing")
|
||||
: TranscribedSound::make(
|
||||
"c8180wb0",
|
||||
"Oh no. Why did I shine that light in Athena's temple. "
|
||||
"I was just trying to see what the heroine was doing"),
|
||||
23029);
|
||||
break;
|
||||
case 23029:
|
||||
room->playSpeech(TranscribedSound::make("c8180wc0", "Well now you can see what I'm doing: tomato heads"), 23030);
|
||||
break;
|
||||
case 23030:
|
||||
room->playVideo("c8180ba0", 0, 23032);
|
||||
break;
|
||||
case 23031:
|
||||
break;
|
||||
case 23032:
|
||||
room->selectFrame("c8180bb0", 101, 0);
|
||||
g_vm->moveToRoom(kSeriphosRoom);
|
||||
break;
|
||||
// TODO: lighting up of the beam: 23035/23036 are for lighting up
|
||||
case 23035:
|
||||
case 23036:
|
||||
room->selectFrame(LayerId(kLights, 0, "source"), kLightsZ, 0);
|
||||
/* Fallthrough */
|
||||
case 23037:
|
||||
room->playAnimLoop("c8110bc0", 211);
|
||||
room->enableHotzone("Athena's Sword");
|
||||
room->enableHotzone("Athena's Shield");
|
||||
for (unsigned i = 0; i < kNumPuzzleElements; i++)
|
||||
room->enableHotzone(puzzleElements[i].hotname);
|
||||
break;
|
||||
case 23020:
|
||||
room->playAnim("c8130bd0", 0, PlayAnimParams::disappear(), 23041);
|
||||
/*Fallthrough */
|
||||
case 23038:
|
||||
g_vm->addTimer(23040, 640);
|
||||
g_vm->addTimer(23039, 40, 15);
|
||||
break;
|
||||
case 23039:
|
||||
//TODO progressive beams
|
||||
break;
|
||||
case 23040:
|
||||
for (unsigned i = 0; i < 12; i++)
|
||||
room->stopAnim(LayerId(kLights, i, "internal"));
|
||||
room->stopAnim(LayerId(kLights, 0, "source"));
|
||||
room->stopAnim("c8110bc0");
|
||||
break;
|
||||
case 23043:
|
||||
room->stopAnim("c8010oc0");
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 23044:
|
||||
room->stopAnim("c8010ob0");
|
||||
room->enableMouse();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
room->loadHotZones("Athena.HOT", false);
|
||||
room->addStaticLayer("c8010pa0", kBackgroundZ);
|
||||
room->addStaticLayer("c8010ta0", 601);
|
||||
room->enableHotzone("Athena");
|
||||
room->enableHotzone("Seriphos");
|
||||
|
||||
if (quest == kMedusaQuest && !persistent->_athenaPuzzleSolved) {
|
||||
persistent->_athenaIntroPlayed = true;
|
||||
room->disableMouse();
|
||||
room->playVideo(persistent->_gender == kMale ? "c8040wa0" : "c8040wb0",
|
||||
1101, kIntroFinished);
|
||||
room->playAnim(kAthenaAnim, 1101, PlayAnimParams::loop());
|
||||
room->playMusic("c8040ma0", 23013);
|
||||
}
|
||||
|
||||
if (!persistent->_athenaShieldTaken) {
|
||||
if (persistent->_athenaPuzzleSolved) {
|
||||
room->selectFrame("c8130be0", 1101, 4);
|
||||
room->enableHotzone("Shield");
|
||||
} else {
|
||||
room->selectFrame("c8150ba0", 1101, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!persistent->_athenaSwordTaken) {
|
||||
if (persistent->_athenaPuzzleSolved) {
|
||||
room->selectFrame("c8130bf0", 1101, 7);
|
||||
room->enableHotzone("Sword");
|
||||
} else {
|
||||
room->selectFrame("c8140ba0", 1101, 0);
|
||||
}
|
||||
}
|
||||
|
||||
room->playAnimLoop("c8030ba0", 201);
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kCool);
|
||||
}
|
||||
private:
|
||||
void rescheduleHintTimer(bool isInHandler = false) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
if (!isInHandler)
|
||||
g_vm->cancelTimer(23007);
|
||||
if (!persistent->_athenaPuzzleSolved)
|
||||
g_vm->addTimer(23007, _hintTimerLength);
|
||||
}
|
||||
|
||||
void handlePuzzleClick(int num) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
rescheduleHintTimer();
|
||||
|
||||
_puzzleState[num] = (_puzzleState[num] + 1) % 3;
|
||||
memset(_isPuzzleLit, 0, sizeof (_isPuzzleLit));
|
||||
_isPuzzleLit[0] = true;
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
done = true;
|
||||
for (unsigned i = 0; i < kNumPuzzleElements; i++)
|
||||
if (_puzzleState[i] != 0 && _isPuzzleLit[i]
|
||||
&& puzzleElements[i].lights[_puzzleState[i] - 1] >= 0
|
||||
&& !_isPuzzleLit[puzzleElements[i].lights[_puzzleState[i] - 1]]) {
|
||||
_isPuzzleLit[puzzleElements[i].lights[_puzzleState[i] - 1]] = true;
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < kNumPuzzleElements; i++)
|
||||
if (_puzzleState[i] != 0 && _isPuzzleLit[i]
|
||||
&& puzzleElements[i].lights[_puzzleState[i] - 1] == kWonPuzzle) {
|
||||
_isPuzzleWon = true;
|
||||
break;
|
||||
}
|
||||
for (unsigned i = 0; i < kNumPuzzleElements; i++)
|
||||
if (!_isPuzzleLit[i])
|
||||
_puzzleState[i] = 0;
|
||||
for (unsigned i = 0; i < 11; i++)
|
||||
room->stopAnim(LayerId(kLights, i, "internal"));
|
||||
for (unsigned i = 0; i < kNumPuzzleElements; i++)
|
||||
if (_puzzleState[i] != 0 && _isPuzzleLit[i]) {
|
||||
int ray = puzzleElements[i].rays[_puzzleState[i] - 1];
|
||||
if (ray <= 0)
|
||||
continue;
|
||||
room->selectFrame(LayerId(kLights, ray, "internal"), kLightsZ, ray);
|
||||
}
|
||||
|
||||
if (_isPuzzleLit[kArtist] && _puzzleState[kArtist] == 1)
|
||||
room->playAnimLoop("c8120bg0", 601);
|
||||
else
|
||||
room->stopAnim("c8120bg0");
|
||||
if (_isPuzzleLit[kScholar] && _puzzleState[kScholar] == 1)
|
||||
room->playAnimLoop("c8120bg1", 601);
|
||||
else
|
||||
room->stopAnim("c8120bg1");
|
||||
|
||||
if (_isPuzzleLit[num] && _puzzleState[num])
|
||||
room->playSFX("c8120ea0");
|
||||
|
||||
if (_isPuzzleWon) {
|
||||
room->selectFrame(LayerId(kLights, 11, "internal"), kLightsZ, 11);
|
||||
room->playSFX("C8130eA0");
|
||||
g_vm->addTimer(23008, 1000);
|
||||
room->disableHotzone("Athena's Sword");
|
||||
room->disableHotzone("Athena's Shield");
|
||||
for (unsigned i = 0; i < kNumPuzzleElements; i++)
|
||||
room->disableHotzone(puzzleElements[i].hotname);
|
||||
room->disableMouse();
|
||||
persistent->_athenaPuzzleSolved = true;
|
||||
}
|
||||
|
||||
room->playMusicLoop(
|
||||
persistent->_quest != kMedusaQuest || persistent->_athenaPuzzleSolved
|
||||
? "c8010ea0" : "c8110ea0");
|
||||
}
|
||||
bool _playAreYouForgetting;
|
||||
bool _playAthenaTempleHardware;
|
||||
bool _isPuzzleLit[kNumPuzzleElements];
|
||||
int _puzzleState[kNumPuzzleElements];
|
||||
bool _isPuzzleWon;
|
||||
int _hintTimerLength;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeAthenaHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new AthenaHandler());
|
||||
}
|
||||
|
||||
}
|
||||
499
engines/hadesch/rooms/catacombs.cpp
Normal file
499
engines/hadesch/rooms/catacombs.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
/* 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 {
|
||||
|
||||
static const char *caTxtNames[] = {
|
||||
"CaLeft.txt",
|
||||
"CaCenter.txt",
|
||||
"CaRight.txt"
|
||||
};
|
||||
|
||||
static const char *skullHotzones[] = {
|
||||
"LSkull",
|
||||
"CSkull",
|
||||
"RSkull"
|
||||
};
|
||||
|
||||
static const char *torchHotzones[] = {
|
||||
"LTorch",
|
||||
"CTorch",
|
||||
"RTorch"
|
||||
};
|
||||
|
||||
static const char *signNames[] = {
|
||||
"SignToHelen",
|
||||
"SignToGuards",
|
||||
"SignToPainPanic"
|
||||
};
|
||||
|
||||
static const char *musicNames[] = {
|
||||
"MusicHelen",
|
||||
"MusicGuard",
|
||||
"MusicPainPanic"
|
||||
};
|
||||
|
||||
static const TranscribedSound painSounds[] = {
|
||||
{"SndPainBedtime", _hs("It's bed time")},
|
||||
{"SndPanicBoneHead", _hs("Hey there, bonehead")},
|
||||
{"SndPainRecognize", _hs("Recognize the jewel?")} // Unclear
|
||||
};
|
||||
|
||||
static const TranscribedSound painSounds2[] = {
|
||||
{"SndPanicLightsOut", _hs("He-he. Lights out") },
|
||||
{"SndPainByeBye", _hs("Bye-Bye")},
|
||||
{"SndPanicMaybeHit", _hs("Maybe it will hit ya")}
|
||||
};
|
||||
|
||||
static const TranscribedSound guardSpeeches[] = {
|
||||
{"T3220wA0", _hs("Do you think we were going to let you just walk into Troy?")},
|
||||
// FIXME: Spelling incorrect. noone should be no one. Fixing changes game data and thus may cause issues
|
||||
{"T3220wB0", _hs("So sorry, noone is allowed in. So beat it")},
|
||||
{"T3220wC0", _hs("Hey, Troy is closed to all visitors. Take a hike")}
|
||||
};
|
||||
|
||||
enum {
|
||||
kBackgroundCenterZ = 10001,
|
||||
kBackgroundZ = 10000
|
||||
};
|
||||
|
||||
enum {
|
||||
// Originally 22014 for all 3 but I'd rather avoid passing extra args around
|
||||
kL1TrochLitLeft = 1022001,
|
||||
kL1TrochLitCenter = 1022002,
|
||||
kL1TrochLitRight = 1022003,
|
||||
kBonkVideoFinished = 1022004
|
||||
};
|
||||
|
||||
class CatacombsHandler : public Handler {
|
||||
public:
|
||||
CatacombsHandler() {
|
||||
_philWarnedTorch = false;
|
||||
_philBangPlayed = false;
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
int level = persistent->_catacombLevel;
|
||||
|
||||
if (name == "LExit") {
|
||||
handleExit(kCatacombsLeft);
|
||||
return;
|
||||
}
|
||||
if (name == "CExit") {
|
||||
handleExit(kCatacombsCenter);
|
||||
return;
|
||||
}
|
||||
if (name == "RExit") {
|
||||
handleExit(kCatacombsRight);
|
||||
return;
|
||||
}
|
||||
if (level == 0 && (name == "LTorch" || name == "CTorch" || name == "RTorch")) {
|
||||
g_vm->getHeroBelt()->placeToInventory(kTorch);
|
||||
room->stopAnim(caVariantGet(_torchPosition, "TorchNormal"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (level == 1 && name == "LTorch") {
|
||||
lightTorchL1(kCatacombsLeft);
|
||||
return;
|
||||
}
|
||||
|
||||
if (level == 1 && name == "CTorch") {
|
||||
lightTorchL1(kCatacombsCenter);
|
||||
return;
|
||||
}
|
||||
|
||||
if (level == 1 && name == "RTorch") {
|
||||
lightTorchL1(kCatacombsRight);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "LSkull" || name == "CSkull" || name == "RSkull") {
|
||||
_decoderPosition = 0;
|
||||
renderDecoder();
|
||||
if (!_philBangPlayed) {
|
||||
_philBangPlayed = true;
|
||||
room->playSFX("SndBigBang", 22012);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "DecoderDown" && _decoderPosition < 6) {
|
||||
_decoderPosition++;
|
||||
renderDecoder();
|
||||
room->playAnim("AnimDecoderArrows", 149, PlayAnimParams::disappear().partial(0, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "DecoderUp" && _decoderPosition > 0) {
|
||||
_decoderPosition--;
|
||||
renderDecoder();
|
||||
room->playAnim("AnimDecoderArrows", 149, PlayAnimParams::disappear().partial(1, 1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "DecoderDone") {
|
||||
removeDecoder();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool handleClickWithItem(const Common::String &name, InventoryItem item) override {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
int level = persistent->_catacombLevel;
|
||||
if (item == kTorch && level == 1) {
|
||||
if (name == "LTorch") {
|
||||
lightTorchL1(kCatacombsLeft);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "CTorch") {
|
||||
lightTorchL1(kCatacombsCenter);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "RTorch") {
|
||||
lightTorchL1(kCatacombsRight);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
int level = persistent->_catacombLevel;
|
||||
switch(eventId) {
|
||||
case kL1TrochLitLeft:
|
||||
case kL1TrochLitCenter:
|
||||
case kL1TrochLitRight: {
|
||||
CatacombsPosition side = (CatacombsPosition) (eventId - kL1TrochLitLeft + kCatacombsLeft);
|
||||
bool isHelen = persistent->_catacombPaths[1][side] == kCatacombsHelen;
|
||||
room->playAnimLoop(
|
||||
caVariantGet(side, isHelen ? "TorchLong" : "TorchNormal"),
|
||||
caVariantGet(side, "TorchZ").asUint64());
|
||||
break;
|
||||
}
|
||||
case 22009:
|
||||
room->playVideo("PhilQuickNameThatTune", 0);
|
||||
break;
|
||||
case 22012:
|
||||
room->playVideo("PhilWowLowOnTroops", 0);
|
||||
break;
|
||||
case 22016:
|
||||
room->playSFX("SndGuardTrapDoorOpen", 22017);
|
||||
break;
|
||||
case 22017:
|
||||
room->playSpeech(TranscribedSound::make("SndGuardLaugh", "[laughter]"), 22018);
|
||||
break;
|
||||
case 22018:
|
||||
room->playSpeech(
|
||||
guardSpeeches[g_vm->getRnd().getRandomNumberRng(0, ARRAYSIZE(guardSpeeches) - 1)],
|
||||
22019);
|
||||
break;
|
||||
case 22019:
|
||||
room->playSFX("SndGuardTrapDoorClose", 22020);
|
||||
break;
|
||||
case 22020:
|
||||
persistent->_catacombLevel = kCatacombLevelSign;
|
||||
g_vm->moveToRoom(kTroyRoom);
|
||||
break;
|
||||
case 22022:
|
||||
room->playSpeech(painSounds2[level], 22023);
|
||||
persistent->_catacombLevel = kCatacombLevelSign;
|
||||
break;
|
||||
case 22023:
|
||||
room->playVideo("MovPainPanicBonk", 103, kBonkVideoFinished);
|
||||
break;
|
||||
case kBonkVideoFinished:
|
||||
g_vm->moveToRoom(kTroyRoom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleMouseOver(const Common::String &name) override {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
int level = persistent->_catacombLevel;
|
||||
|
||||
if (level == 2) {
|
||||
if (name == "LExit") {
|
||||
playTune(kCatacombsLeft);
|
||||
return;
|
||||
}
|
||||
if (name == "CExit") {
|
||||
playTune(kCatacombsCenter);
|
||||
return;
|
||||
}
|
||||
if (name == "RExit") {
|
||||
playTune(kCatacombsRight);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleMouseOut(const Common::String &name) override {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
int level = persistent->_catacombLevel;
|
||||
|
||||
if (level == 2 && (name == "LExit" || name == "CExit" || name == "RExit"))
|
||||
stopTune();
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
CatacombsLevel level = persistent->_catacombLevel;
|
||||
|
||||
persistent->_catacombLastLevel = level;
|
||||
|
||||
if (persistent->_catacombPainAndPanic) {
|
||||
persistent->_catacombPainAndPanic = false;
|
||||
room->addStaticLayer("DeadEndBackground", 10001);
|
||||
room->playMusic("SndPainPanicStinger", 22022);
|
||||
room->playSpeech(painSounds[persistent->_catacombLevel]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (level == 0)
|
||||
room->loadHotZones("CaDecode.HOT", false);
|
||||
room->playMusicLoop("T3010eA0");
|
||||
// TODO: tremmors
|
||||
// TODO: handle timer
|
||||
g_vm->addTimer(22007, level == 2 ? 30000 : 40000, -1);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
_caMapTxt[i] = TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(room->openFile(caTxtNames[i])), 13);
|
||||
|
||||
if (persistent->_catacombPaths[0][0] == kCatacombsHelen
|
||||
&& persistent->_catacombPaths[1][0] == kCatacombsHelen) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Common::Array<int> p3 = permute3();
|
||||
persistent->_catacombVariants[0][i] = p3[0];
|
||||
persistent->_catacombVariants[1][i] = p3[1];
|
||||
persistent->_catacombVariants[2][i] = p3[2];
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Common::Array<int> p3 = permute3();
|
||||
persistent->_catacombPaths[i][0] = (CatacombsPath) p3[0];
|
||||
persistent->_catacombPaths[i][1] = (CatacombsPath) p3[1];
|
||||
persistent->_catacombPaths[i][2] = (CatacombsPath) p3[2];
|
||||
}
|
||||
persistent->_catacombDecoderSkullPosition = (CatacombsPosition) g_vm->getRnd().getRandomNumberRng(0, 2);
|
||||
}
|
||||
|
||||
for (CatacombsPosition i = kCatacombsLeft; i <= kCatacombsRight; i = (CatacombsPosition) (i + 1)) {
|
||||
room->loadHotZones(
|
||||
caVariantGet(i, "Hotspots"), false);
|
||||
room->addStaticLayer(
|
||||
caVariantGet(i, "Background"),
|
||||
i == kCatacombsCenter ? kBackgroundCenterZ : kBackgroundZ);
|
||||
}
|
||||
|
||||
if (persistent->_catacombVariants[level][0] == 2) {
|
||||
room->playAnimLoop("GlowingEyes", 900);
|
||||
}
|
||||
|
||||
room->enableHotzone("LExit");
|
||||
room->enableHotzone("CExit");
|
||||
room->enableHotzone("RExit");
|
||||
|
||||
switch (level) {
|
||||
case 0:
|
||||
room->playMusic("IntroMusic");
|
||||
room->enableHotzone(skullHotzones[persistent->_catacombDecoderSkullPosition]);
|
||||
room->selectFrame(
|
||||
caVariantGet(persistent->_catacombDecoderSkullPosition, "SkullDecoder"), 450, 1);
|
||||
for (CatacombsPosition i = kCatacombsLeft; i <= kCatacombsRight; i = (CatacombsPosition) (i + 1)) {
|
||||
room->selectFrame(
|
||||
caVariantGet(i, "SignBoard"), 501, 0);
|
||||
room->selectFrame(
|
||||
caVariantGet(i, signNames[persistent->_catacombPaths[level][i]]), 500, 0);
|
||||
}
|
||||
if (!persistent->isInInventory(kTorch)) {
|
||||
_torchPosition = (CatacombsPosition) g_vm->getRnd().getRandomNumberRng(0, 2);
|
||||
room->enableHotzone(torchHotzones[_torchPosition]);
|
||||
room->playAnimLoop(
|
||||
caVariantGet(_torchPosition, "TorchNormal"),
|
||||
caVariantGet(_torchPosition, "TorchZ").asUint64());
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
room->enableHotzone("LTorch");
|
||||
room->enableHotzone("CTorch");
|
||||
room->enableHotzone("RTorch");
|
||||
for (CatacombsPosition side = kCatacombsLeft; side <= kCatacombsRight; side = (CatacombsPosition) (side + 1)) {
|
||||
room->selectFrame(
|
||||
caVariantGet(side, "TorchNormalBurst"),
|
||||
caVariantGet(side, "TorchZ").asUint64(), 0);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
room->playSFX("CollapseSnd", 22009);
|
||||
for (CatacombsPosition side = kCatacombsLeft; side <= kCatacombsRight; side = (CatacombsPosition) (side + 1)) {
|
||||
room->playAnimLoop(
|
||||
caVariantGet(side, "TorchNormal"),
|
||||
caVariantGet(side, "TorchZ").asUint64());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kCool);
|
||||
}
|
||||
|
||||
private:
|
||||
void stopTune() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
for (int i = 0; i < 3; i++)
|
||||
room->stopAnim(musicNames[i]);
|
||||
}
|
||||
|
||||
// TODO: how can we make tune challenge accessible to deaf people?
|
||||
void playTune(CatacombsPosition side) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
stopTune();
|
||||
room->playMusicLoop(musicNames[persistent->_catacombPaths[2][side]]);
|
||||
}
|
||||
|
||||
void renderDecoder() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
room->selectFrame("AnimDecoderScroll", 151, 0);
|
||||
room->selectFrame("AnimDecoderSymbols", 150, _decoderPosition);
|
||||
room->selectFrame(
|
||||
caVariantGet(persistent->_catacombDecoderSkullPosition, "SkullDecoder"), 450, 0);
|
||||
room->enableHotzone("DecoderDone");
|
||||
room->enableHotzone("DecoderDown");
|
||||
room->enableHotzone("DecoderUp");
|
||||
}
|
||||
|
||||
void removeDecoder() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
room->stopAnim("AnimDecoderScroll");
|
||||
room->stopAnim("AnimDecoderSymbols");
|
||||
room->selectFrame(
|
||||
caVariantGet(persistent->_catacombDecoderSkullPosition, "SkullDecoder"), 450, 1);
|
||||
room->stopAnim("AnimDecoderArrows");
|
||||
room->disableHotzone("DecoderDone");
|
||||
room->disableHotzone("DecoderDown");
|
||||
room->disableHotzone("DecoderUp");
|
||||
}
|
||||
|
||||
void handleExit(CatacombsPosition side) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
int level = persistent->_catacombLevel;
|
||||
|
||||
if (level == 0 && !_philWarnedTorch && !persistent->isInInventory(kTorch) && persistent->_hintsAreEnabled) {
|
||||
_philWarnedTorch = true;
|
||||
room->playVideo("PhilGrabTheTorch", 0, 22003);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (persistent->_catacombPaths[level][side]) {
|
||||
case kCatacombsHelen:
|
||||
room->disableMouse();
|
||||
if (persistent->_catacombLevel == kCatacombLevelMusic) {
|
||||
persistent->_catacombLevel = kCatacombLevelSign;
|
||||
g_vm->moveToRoom(kPriamRoom);
|
||||
} else {
|
||||
persistent->_catacombLevel = (CatacombsLevel) (persistent->_catacombLevel + 1);
|
||||
g_vm->moveToRoom(kCatacombsRoom);
|
||||
}
|
||||
break;
|
||||
case kCatacombsGuards:
|
||||
room->disableMouse();
|
||||
g_vm->cancelTimer(22007);
|
||||
room->fadeOut(1000, 22016);
|
||||
break;
|
||||
case kCatacombsPainAndPanic:
|
||||
room->disableMouse();
|
||||
g_vm->cancelTimer(22007);
|
||||
persistent->_catacombPainAndPanic = true;
|
||||
g_vm->moveToRoom(kCatacombsRoom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void lightTorchL1(CatacombsPosition side) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
bool isHelen = persistent->_catacombPaths[1][side] == kCatacombsHelen;
|
||||
room->playAnim(
|
||||
caVariantGet(side, isHelen ? "TorchLongBurst" : "TorchNormalBurst"),
|
||||
caVariantGet(side, "TorchZ").asUint64(),
|
||||
PlayAnimParams::disappear(), kL1TrochLitLeft + side - kCatacombsLeft);
|
||||
room->playSFX("SndTorchBurst");
|
||||
room->disableHotzone(torchHotzones[side]);
|
||||
}
|
||||
|
||||
Common::String caVariantGet(CatacombsPosition side, const Common::String &property) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
int level = persistent->_catacombLevel;
|
||||
int variant = persistent->_catacombVariants[level][side];
|
||||
Common::String ret = _caMapTxt[side].get(variant, property);
|
||||
if (ret == "") {
|
||||
debug("No attrinute for %d/%s", side, property.c_str());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Common::Array<int> permute3() {
|
||||
Common::Array <int> ret;
|
||||
int x = g_vm->getRnd().getRandomNumberRng(0, 5);
|
||||
int a = x / 2;
|
||||
ret.push_back(a);
|
||||
int cand1 = a == 0 ? 1 : 0;
|
||||
int cand2 = 0;
|
||||
for (cand2 = 0; cand2 == a || cand2 == cand1; cand2++);
|
||||
if (x % 2) {
|
||||
ret.push_back(cand2);
|
||||
ret.push_back(cand1);
|
||||
} else {
|
||||
ret.push_back(cand1);
|
||||
ret.push_back(cand2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CatacombsPosition _torchPosition;
|
||||
TextTable _caMapTxt[3];
|
||||
bool _philWarnedTorch;
|
||||
bool _philBangPlayed;
|
||||
int _decoderPosition;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeCatacombsHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new CatacombsHandler());
|
||||
}
|
||||
|
||||
}
|
||||
83
engines/hadesch/rooms/credits.cpp
Normal file
83
engines/hadesch/rooms/credits.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/* 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"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kCreditsZ = 1000
|
||||
};
|
||||
|
||||
class CreditsHandler : public Handler {
|
||||
public:
|
||||
CreditsHandler(bool inOptions) {
|
||||
_inOptions = inOptions;
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
switch(eventId) {
|
||||
case 31001:
|
||||
if (_inOptions)
|
||||
g_vm->enterOptions();
|
||||
else
|
||||
g_vm->moveToRoom(g_vm->getPreviousRoomId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void frameCallback() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
int timeElapsed = (g_vm->getCurrentTime() - startTime);
|
||||
room->selectFrame("h2030ba0", kCreditsZ, 0,
|
||||
Common::Point(0, 481
|
||||
- timeElapsed * 6151 / 136000
|
||||
));
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->disableHeroBelt();
|
||||
room->disableMouse();
|
||||
room->addStaticLayer("h2030pa0", kBackgroundZ);
|
||||
room->playVideo("c2590ma0", 0, 31001);
|
||||
room->selectFrame("h2030ba0", kCreditsZ, 0,
|
||||
Common::Point(0, 481));
|
||||
startTime = g_vm->getCurrentTime();
|
||||
}
|
||||
private:
|
||||
int startTime;
|
||||
bool _inOptions;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeCreditsHandler(bool inOptions) {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new CreditsHandler(inOptions));
|
||||
}
|
||||
|
||||
}
|
||||
1619
engines/hadesch/rooms/crete.cpp
Normal file
1619
engines/hadesch/rooms/crete.cpp
Normal file
File diff suppressed because it is too large
Load Diff
368
engines/hadesch/rooms/daedalus.cpp
Normal file
368
engines/hadesch/rooms/daedalus.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
/* 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"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "gui/message.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
static const char *kDaedalusStillFrame = "daedalus still frame";
|
||||
static const char *kDaedalusAmbient = "daedalus ambient";
|
||||
static const char *kModelPiece = "model piece";
|
||||
static const char *kLabyrinthWorkers = "labyrinth workers";
|
||||
|
||||
enum {
|
||||
kDaedalusTick = 13901,
|
||||
|
||||
// TODO: Remove this once we have a possibility
|
||||
// to pass an argument to event handler.
|
||||
// Originally: "daedalus intro 1" -> 13002[1] -> "phil intro 1"
|
||||
// -> 13003[1] -> "daedalus intro 2" -> 13002[2]
|
||||
// -> "phil intro 2" -> 13003[2] -> "daedalus intro 3"
|
||||
kIntroStep1 = 1013001,
|
||||
kIntroStep2 = 1013002,
|
||||
kIntroStep3 = 1013003,
|
||||
kIntroStep4 = 1013004,
|
||||
kIntroStep5 = 1013005
|
||||
};
|
||||
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kLabyrinthWorkersZ = 900,
|
||||
kDaedalusZ = 500,
|
||||
kModelPieceZ = 500,
|
||||
kPhilZ = 0
|
||||
};
|
||||
|
||||
class DaedalusHandler : public Handler {
|
||||
public:
|
||||
DaedalusHandler() {
|
||||
_daedalusIsBusy = false;
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
if (name == "minos palace") {
|
||||
g_vm->moveToRoom(kMinosPalaceRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "daedalus") {
|
||||
playDaedalusVideo("daedalus no materials", 13005, Common::Point(76, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "wings") {
|
||||
playDaedalusVideo("daedalus wings", 4009, Common::Point(10, 56));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "labyrinth" && persistent->_quest != kCreteQuest) {
|
||||
room->disableMouse();
|
||||
room->playVideo("phil navigation help", 0, 13007, Common::Point(0, 216));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "brick wall") {
|
||||
daedalusWallMotion();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool handleClickWithItem(const Common::String &name, InventoryItem item) override {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
int labItem = -1;
|
||||
debug("Item is %d", item);
|
||||
switch (item) {
|
||||
case kStone:
|
||||
labItem = 0;
|
||||
break;
|
||||
case kBricks:
|
||||
labItem = 1;
|
||||
break;
|
||||
case kWood:
|
||||
labItem = 2;
|
||||
break;
|
||||
case kStraw:
|
||||
labItem = 3;
|
||||
break;
|
||||
default:
|
||||
labItem = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((name == "daedalus" || name == "chute") && labItem < 0) {
|
||||
playDaedalusVideo("daedalus what to do with that", 13005, Common::Point(10, 40));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "daedalus" && labItem >= 0) {
|
||||
playDaedalusVideo("daedalus put that in the chute", 4009, Common::Point(64, 48));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "chute" && labItem >= 0) {
|
||||
bool hasAll = true;
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
g_vm->getHeroBelt()->removeFromInventory(item);
|
||||
persistent->_daedalusLabItem[labItem] = true;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!persistent->_daedalusLabItem[i]) {
|
||||
hasAll = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
renderCheckMarks();
|
||||
|
||||
room->playAnimWithSFX("dust cloud", "dust cloud sound", 850, PlayAnimParams::disappear());
|
||||
|
||||
if (hasAll)
|
||||
playDaedalusVideo("daedalus exclaims", 13008, Common::Point(0, 2));
|
||||
else {
|
||||
// Original goes to event 4009
|
||||
if (g_vm->getRnd().getRandomBit())
|
||||
playDaedalusVideo("daedalus congrats 1", 4009, Common::Point(70, 30));
|
||||
else
|
||||
playDaedalusVideo("daedalus congrats 2", 4009, Common::Point(68, 32));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
switch(eventId) {
|
||||
case kIntroStep1:
|
||||
room->playVideo("phil intro 1", kPhilZ, kIntroStep2,
|
||||
Common::Point(0, 216));
|
||||
room->selectFrame(kDaedalusAmbient, kDaedalusZ, 0);
|
||||
break;
|
||||
case kIntroStep2:
|
||||
playDaedalusVideo("daedalus intro 2", kIntroStep3,
|
||||
Common::Point(76, 55));
|
||||
break;
|
||||
case kIntroStep3:
|
||||
room->playVideo("phil intro 2", kPhilZ, kIntroStep4,
|
||||
Common::Point(0, 216));
|
||||
room->selectFrame(kDaedalusStillFrame, kDaedalusZ, 0);
|
||||
break;
|
||||
|
||||
case kIntroStep4:
|
||||
playDaedalusVideo("daedalus intro 3", kIntroStep5,
|
||||
Common::Point(76, 60));
|
||||
break;
|
||||
case 13004:
|
||||
room->stopAnim("daedalus note");
|
||||
room->stopAnim("daedalus note text male");
|
||||
room->stopAnim("daedalus note text female");
|
||||
break;
|
||||
case 13005:
|
||||
g_vm->addTimer(13006, 5000, 1);
|
||||
// Fallthrough
|
||||
case kIntroStep5:
|
||||
case 4009:
|
||||
daedalusBecomesIdle();
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 13006:
|
||||
if (!room->isMouseEnabled()) {
|
||||
g_vm->addTimer(13006, 5000, 1);
|
||||
break;
|
||||
}
|
||||
room->disableMouse();
|
||||
room->playVideo("phil coerces", 0, 13007, Common::Point(0, 216));
|
||||
break;
|
||||
case 13007:
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 13008: {
|
||||
room->enableMouse();
|
||||
room->selectFrame("daedalus exclaims still", kDaedalusZ,0);
|
||||
// TODO: for now we skip arcade sequence until it's implemented
|
||||
// g_vm->moveToRoom(kMinotaurPuzzle);
|
||||
GUI::MessageDialog dialog(_("The Minotaur minigame is not supported yet. Skipping"));
|
||||
dialog.runModal();
|
||||
g_vm->moveToRoom(kQuiz);
|
||||
break;
|
||||
}
|
||||
case 13011: {
|
||||
// TODO: use right algorithm
|
||||
int roarNum = g_vm->getRnd().getRandomNumberRng(1, 5);
|
||||
room->playSFX(Common::String::format("ambient minotaur roar %d", roarNum), 13012);
|
||||
break;
|
||||
}
|
||||
case 13012:
|
||||
g_vm->addTimer(13011, g_vm->getRnd().getRandomNumberRng(5000, 10000));
|
||||
break;
|
||||
case kDaedalusTick:
|
||||
if (_daedalusIsBusy)
|
||||
break;
|
||||
_daedalusIsBusy = true;
|
||||
switch (g_vm->getRnd().getRandomNumberRng(1, 6)) {
|
||||
case 1:
|
||||
daedalusWallMotion();
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
room->playAnim(kDaedalusAmbient, kDaedalusZ, PlayAnimParams::keepLastFrame().partial(0, 21), 13904);
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
room->playAnim(kDaedalusAmbient, kDaedalusZ, PlayAnimParams::keepLastFrame().partial(23, 30), 13903);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 13902:
|
||||
room->playAnim(kDaedalusAmbient, kDaedalusZ, PlayAnimParams::keepLastFrame().partial(35, -1), 13904);
|
||||
room->playAnimWithSFX(kLabyrinthWorkers, "labyrinth workers sound", kLabyrinthWorkersZ,
|
||||
PlayAnimParams::keepLastFrame());
|
||||
break;
|
||||
case 13903:
|
||||
room->playAnim(kDaedalusAmbient, kDaedalusZ, PlayAnimParams::keepLastFrame().partial(57, -1), 13904);
|
||||
break;
|
||||
case 13904:
|
||||
_daedalusIsBusy = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
room->loadHotZones("Daedalus.HOT", false);
|
||||
room->addStaticLayer("background", kBackgroundZ);
|
||||
room->addStaticLayer("chute label", 850);
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kWarm);
|
||||
|
||||
if (quest == kCreteQuest) {
|
||||
room->addStaticLayer("wings", 900);
|
||||
room->addStaticLayer("check list", 800);
|
||||
room->addStaticLayer("check list text", 799);
|
||||
room->enableHotzone("brick wall");
|
||||
room->enableHotzone("chute");
|
||||
room->enableHotzone("wings");
|
||||
room->enableHotzone("daedalus");
|
||||
room->selectFrame(kLabyrinthWorkers, kLabyrinthWorkersZ, 0);
|
||||
room->selectFrame(kDaedalusAmbient, kDaedalusZ, 0);
|
||||
g_vm->addTimer(13011, g_vm->getRnd().getRandomNumberRng(5000, 10000));
|
||||
g_vm->addTimer(kDaedalusTick, g_vm->getRnd().getRandomNumberRng(5000, 10000), -1);
|
||||
} else {
|
||||
room->enableHotzone("labyrinth");
|
||||
if (!persistent->_daedalusShowedNote) {
|
||||
persistent->_daedalusShowedNote = true;
|
||||
room->selectFrame("daedalus note", 800, 0);
|
||||
room->selectFrame(persistent->_gender == kMale ? "daedalus note text male"
|
||||
: "daedalus note text female", 799, 0);
|
||||
room->playSpeech(persistent->_gender == kMale ?
|
||||
TranscribedSound::make("daedalus note vo male", "Dear hero, now that we've brought peace to the people of Crete, I've used the wings that I've built for myself and my son Icarus to escape. I'm forever grateful for your help. Your friend, Daedalus") :
|
||||
TranscribedSound::make("daedalus note vo female", "Dear heroine, now that we've brought peace to the people of Crete, I've used the wings that I've built for myself and my son Icarus to escape. I'm forever grateful for your help. Your friend, Daedalus. Au revoir. Salaam. Good bye."),
|
||||
13004);
|
||||
}
|
||||
}
|
||||
|
||||
renderCheckMarks();
|
||||
|
||||
room->enableHotzone("minos palace");
|
||||
|
||||
if (quest == kCreteQuest
|
||||
&& !persistent->isRoomVisited(kDaedalusRoom)) {
|
||||
persistent->_creteIntroAtlantisBoat = true;
|
||||
persistent->_creteShowAtlantisBoat = true;
|
||||
persistent->_creteIntroAtlantisWood = true;
|
||||
persistent->_troyPlayAttack = true;
|
||||
playDaedalusVideo("daedalus intro 1", kIntroStep1,
|
||||
Common::Point(50, 35));
|
||||
room->playMusicLoop("theme music 1");
|
||||
} else if (quest == kCreteQuest &&
|
||||
(persistent->_daedalusLabItem[0] || persistent->isInInventory(kStone)) &&
|
||||
(persistent->_daedalusLabItem[1] || persistent->isInInventory(kBricks)) &&
|
||||
(persistent->_daedalusLabItem[2] || persistent->isInInventory(kWood)) &&
|
||||
(persistent->_daedalusLabItem[3] || persistent->isInInventory(kStraw))) {
|
||||
room->playMusicLoop("theme music 2");
|
||||
} else {
|
||||
room->playMusicLoop("R4010eA0");
|
||||
}
|
||||
AmbientAnim("mouse", "mouse sound", 900, 5000, 10000, AmbientAnim::KEEP_LOOP, Common::Point(0, 0),
|
||||
AmbientAnim::PAN_ANY).start();
|
||||
}
|
||||
private:
|
||||
void playDaedalusVideo(const Common::String &name, int callback, const Common::Point &offset) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
_daedalusIsBusy = true;
|
||||
|
||||
room->stopAnim(kDaedalusStillFrame);
|
||||
room->stopAnim(kDaedalusAmbient);
|
||||
room->selectFrame(kModelPiece, kModelPieceZ, 0);
|
||||
room->disableMouse();
|
||||
room->playVideo(name, kDaedalusZ, callback, offset);
|
||||
}
|
||||
|
||||
void daedalusBecomesIdle() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
room->selectFrame(kDaedalusAmbient, kDaedalusZ, 0);
|
||||
room->stopAnim(kModelPiece);
|
||||
_daedalusIsBusy = false;
|
||||
}
|
||||
|
||||
void renderCheckMarks() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Common::String layer = Common::String::format("check mark %d", i + 1);
|
||||
if (persistent->_daedalusLabItem[i]) {
|
||||
room->selectFrame(layer, 798, 0);
|
||||
} else {
|
||||
room->stopAnim(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void daedalusWallMotion() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->playAnim(kDaedalusAmbient, kDaedalusZ, PlayAnimParams::keepLastFrame().partial(0, 34), 13902);
|
||||
room->playSFX("daedalus ambient sound");
|
||||
_daedalusIsBusy = true;
|
||||
}
|
||||
|
||||
bool _daedalusIsBusy;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeDaedalusHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new DaedalusHandler());
|
||||
}
|
||||
|
||||
}
|
||||
1146
engines/hadesch/rooms/ferry.cpp
Normal file
1146
engines/hadesch/rooms/ferry.cpp
Normal file
File diff suppressed because it is too large
Load Diff
63
engines/hadesch/rooms/hadesthrone.cpp
Normal file
63
engines/hadesch/rooms/hadesthrone.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/* 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"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class HadesThroneHandler : public Handler {
|
||||
public:
|
||||
HadesThroneHandler() {
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
switch(eventId) {
|
||||
case 29001:
|
||||
persistent->_quest = kEndGame;
|
||||
persistent->clearInventory();
|
||||
persistent->_doQuestIntro = true;
|
||||
g_vm->moveToRoom(kWallOfFameRoom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->playVideo("movie", 500, 29001);
|
||||
room->disableHeroBelt();
|
||||
room->playMusicLoop("V6010eA0");
|
||||
room->disableMouse();
|
||||
}
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeHadesThroneHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new HadesThroneHandler());
|
||||
}
|
||||
|
||||
}
|
||||
59
engines/hadesch/rooms/intro.cpp
Normal file
59
engines/hadesch/rooms/intro.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 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"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class IntroHandler : public Handler {
|
||||
public:
|
||||
IntroHandler() {
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
switch(eventId) {
|
||||
// 32002 handles performance testing
|
||||
case 32003:
|
||||
g_vm->moveToRoom(kOlympusRoom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->playVideo("o0010ba0", 101, 32003);
|
||||
room->disableHeroBelt();
|
||||
room->disableMouse();
|
||||
}
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeIntroHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new IntroHandler());
|
||||
}
|
||||
|
||||
}
|
||||
1267
engines/hadesch/rooms/medisle.cpp
Normal file
1267
engines/hadesch/rooms/medisle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
53
engines/hadesch/rooms/medusa.cpp
Normal file
53
engines/hadesch/rooms/medusa.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/* 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"
|
||||
|
||||
namespace Hadesch {
|
||||
enum {
|
||||
kBackgroundZ = 10000
|
||||
};
|
||||
|
||||
class MedusaHandler : public Handler {
|
||||
public:
|
||||
MedusaHandler() {
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->addStaticLayer("m6010pa0", kBackgroundZ);
|
||||
}
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeMedusaHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new MedusaHandler());
|
||||
}
|
||||
|
||||
}
|
||||
341
engines/hadesch/rooms/minos.cpp
Normal file
341
engines/hadesch/rooms/minos.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
/* 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 {
|
||||
|
||||
static const char *kBacchusHighlight = "AnimBacchusStatue";
|
||||
static const char *kGuardLooking = "AnimGuardLooking";
|
||||
static const char *kAnimMinosEating = "AnimMinosEating";
|
||||
static const char *kStatues = "AnimStatueZeroPose";
|
||||
|
||||
enum {
|
||||
kMinosBackToIdleEvent = 14003,
|
||||
// 14005 is the end of statue animation that we handle as functor instead
|
||||
kInstructionMovieCompleted = 14011,
|
||||
kMinosHornedStatue = 14012,
|
||||
kMinosOtherItem = 14013,
|
||||
kMinosToss1 = 14014,
|
||||
kMinosToss2 = 14015,
|
||||
kMinosToss3 = 14016,
|
||||
kMinosToss4 = 14017,
|
||||
kMinosStatueTossed = 14018,
|
||||
kGuardGruntCleanup = 14020,
|
||||
kGuardNagCleanup = 14021,
|
||||
kGuardNag = 14022,
|
||||
kGuardPeriodic = 1014001,
|
||||
kMinosPeriodic = 1014002
|
||||
};
|
||||
|
||||
enum {
|
||||
kBacchusZ = 200,
|
||||
kStatueOnTheTableZ = 300,
|
||||
kMinosZ = 500,
|
||||
kInstalledStatueZ = 500,
|
||||
kGuardZ = 600,
|
||||
kBackgroundZ = 10000
|
||||
};
|
||||
|
||||
class MinosHandler : public Handler {
|
||||
public:
|
||||
MinosHandler() {
|
||||
_guardIsBusy = false;
|
||||
_minosIsBusy = false;
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (name == "Bacchus") {
|
||||
Common::Array<Common::String> videos;
|
||||
videos.push_back("SndBacchusStatueA");
|
||||
videos.push_back("SndBacchusStatueB");
|
||||
videos.push_back("SndBacchusStatueC");
|
||||
|
||||
room->playStatueSMK(kBacchusStatue,
|
||||
kBacchusHighlight,
|
||||
kBacchusZ,
|
||||
videos, 22, 39);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Crete") {
|
||||
room->disableMouse();
|
||||
g_vm->moveToRoom(kCreteRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Daedalus") {
|
||||
room->disableMouse();
|
||||
g_vm->moveToRoom(kDaedalusRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Guard" && !_guardIsBusy) {
|
||||
_guardIsBusy = true;
|
||||
room->playAnimWithSFX("AnimGuardGrunt",
|
||||
"SndGuardGrunt",
|
||||
kGuardZ,
|
||||
PlayAnimParams::keepLastFrame(),
|
||||
kGuardGruntCleanup);
|
||||
room->stopAnim(kGuardLooking);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Minos") {
|
||||
playMinosMovie("MovMinosBeGone", kMinosBackToIdleEvent, Common::Point(202, 229));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Table") {
|
||||
playMinosMovie("MovMinosHavePiece", kMinosBackToIdleEvent, Common::Point(230, 227));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Statue" && !_guardIsBusy) {
|
||||
_guardIsBusy = true;
|
||||
room->playVideo("MovGuardDontTouch", kGuardZ, 14004, Common::Point(432, 142));
|
||||
room->stopAnim(kGuardLooking);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool handleClickWithItem(const Common::String &name, InventoryItem item) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
if (name == "Minos") {
|
||||
if (item >= kHornlessStatue1 && item <= kHornedStatue) {
|
||||
playMinosMovie("MovMinosPutOnTable", kMinosBackToIdleEvent, Common::Point(218, 227));
|
||||
return true;
|
||||
}
|
||||
|
||||
playMinosMovie("MovMinosBeGone", kMinosBackToIdleEvent, Common::Point(202, 229));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "Table") {
|
||||
static const char *sounds[4] = {
|
||||
"R3160eA0",
|
||||
"R3160eB0",
|
||||
"R3160eC0",
|
||||
"R3160eD0",
|
||||
};
|
||||
room->playSFX(sounds[g_vm->getRnd().getRandomNumberRng(0, 3)]);
|
||||
if (item == kHornedStatue) {
|
||||
g_vm->getHeroBelt()->removeFromInventory(item);
|
||||
// room->selectFrame(kStatues, 0);
|
||||
playMinosMovie("MovMinosLoveTheHorns", kMinosHornedStatue, Common::Point(202, 178));
|
||||
persistent->_creteDaedalusRoomAvailable = true;
|
||||
return true;
|
||||
}
|
||||
if (item >= kHornlessStatue1 && item <= kHornedStatue) {
|
||||
g_vm->getHeroBelt()->removeFromInventory(item);
|
||||
static const int mapFrames[] = {
|
||||
2, 4, 1, 3
|
||||
};
|
||||
room->selectFrame(kStatues, kStatueOnTheTableZ, mapFrames[item - kHornlessStatue1]);
|
||||
playMinosMovie("MovMinosWhatTrash", kMinosToss1 + item - kHornlessStatue1, Common::Point(202, 225));
|
||||
persistent->_creteTriedHornless[item - kHornlessStatue1] = true;
|
||||
return true;
|
||||
}
|
||||
playMinosMovie("MovMinosWhatTrash", kMinosOtherItem, Common::Point(202, 225));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
switch (eventId) {
|
||||
case kMinosBackToIdleEvent:
|
||||
minosBackToIdle();
|
||||
break;
|
||||
case kMinosHornedStatue:
|
||||
room->setLayerEnabled(kStatues, false);
|
||||
persistent->_creteShowMerchant = false;
|
||||
persistent->_creteHadesPusnishesPainAndPanic = true;
|
||||
g_vm->moveToRoom(kCreteRoom);
|
||||
break;
|
||||
case kMinosOtherItem:
|
||||
playMinosMovie("MovMinosBeGone", kMinosBackToIdleEvent, Common::Point(202, 229));
|
||||
scheduleNagging();
|
||||
break;
|
||||
case kMinosToss1:
|
||||
room->setLayerEnabled(kStatues, false);
|
||||
playMinosMovie("MovMinosToss1", kMinosStatueTossed, Common::Point(0, 191));
|
||||
break;
|
||||
case kMinosToss2:
|
||||
room->setLayerEnabled(kStatues, false);
|
||||
playMinosMovie("MovMinosToss2", kMinosStatueTossed, Common::Point(0, 188));
|
||||
break;
|
||||
case kMinosToss3:
|
||||
room->setLayerEnabled(kStatues, false);
|
||||
playMinosMovie("MovMinosToss3", kMinosStatueTossed, Common::Point(0, 183));
|
||||
break;
|
||||
case kMinosToss4:
|
||||
room->setLayerEnabled(kStatues, false);
|
||||
playMinosMovie("MovMinosToss4", kMinosStatueTossed, Common::Point(0, 191));
|
||||
break;
|
||||
case kMinosStatueTossed:
|
||||
playMinosMovie("MovMinosBeGone", kMinosBackToIdleEvent, Common::Point(202, 229));
|
||||
scheduleNagging();
|
||||
break;
|
||||
|
||||
case kGuardGruntCleanup:
|
||||
_guardIsBusy = false;
|
||||
room->stopAnim("AnimGuardGrunt");
|
||||
room->selectFrame(kGuardLooking, kGuardZ, 0);
|
||||
break;
|
||||
case kGuardPeriodic:
|
||||
if (!_guardIsBusy)
|
||||
room->playAnimWithSFX(kGuardLooking,
|
||||
"SndGuardLooking",
|
||||
kGuardZ,
|
||||
PlayAnimParams::keepLastFrame());
|
||||
g_vm->addTimer(kGuardPeriodic, g_vm->getRnd().getRandomNumberRng(5000, 10000));
|
||||
break;
|
||||
case kMinosPeriodic:
|
||||
if (!_minosIsBusy)
|
||||
room->playAnimWithSFX(kAnimMinosEating,
|
||||
"SndMinosEating",
|
||||
kMinosZ,
|
||||
PlayAnimParams::keepLastFrame());
|
||||
g_vm->addTimer(kMinosPeriodic, g_vm->getRnd().getRandomNumberRng(5000, 10000));
|
||||
break;
|
||||
case kInstructionMovieCompleted:
|
||||
minosBackToIdle();
|
||||
break;
|
||||
|
||||
case kGuardNagCleanup:
|
||||
room->selectFrame(kGuardLooking, kGuardZ, 0);
|
||||
_guardIsBusy = false;
|
||||
break;
|
||||
|
||||
case kGuardNag:
|
||||
scheduleNagging();
|
||||
if (!_guardIsBusy && !_minosIsBusy) {
|
||||
_guardIsBusy = true;
|
||||
room->playVideo("MovGuardUMustGo", kGuardNagCleanup, kGuardZ, Common::Point(0, 142));
|
||||
room->stopAnim(kGuardLooking);
|
||||
}
|
||||
break;
|
||||
|
||||
case 14004:
|
||||
_guardIsBusy = false;
|
||||
room->selectFrame(kGuardLooking, kGuardZ, 0);
|
||||
break;
|
||||
case 14006:
|
||||
_ambients.tick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
room->loadHotZones("Minos.HOT", false);
|
||||
room->addStaticLayer("Background", kBackgroundZ);
|
||||
room->playAnimLoop("AnimFountain", 250);
|
||||
room->enableHotzone("Bacchus");
|
||||
room->enableHotzone("Crete");
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kWarm);
|
||||
|
||||
if (persistent->_creteDaedalusRoomAvailable) {
|
||||
room->enableHotzone("Statue");
|
||||
room->enableHotzone("Daedalus");
|
||||
} else {
|
||||
room->enableHotzone("Minos");
|
||||
room->selectFrame(kAnimMinosEating, kMinosZ, 0);
|
||||
g_vm->addTimer(kMinosPeriodic, 10000);
|
||||
}
|
||||
|
||||
for (int i = kHornlessStatue1; i <= kHornedStatue; i++)
|
||||
if (persistent->isInInventory((InventoryItem) i))
|
||||
room->enableHotzone("Table");
|
||||
|
||||
room->enableHotzone("Guard");
|
||||
room->selectFrame(kGuardLooking, kGuardZ, 0);
|
||||
g_vm->addTimer(kGuardPeriodic, 5000);
|
||||
|
||||
if (!persistent->_creteMinosInstructed) {
|
||||
playMinosMovie(
|
||||
"MovMinosInstructions", kInstructionMovieCompleted,
|
||||
Common::Point(210, 229));
|
||||
persistent->_creteMinosInstructed = true;
|
||||
persistent->_creteShowMerchant = true;
|
||||
persistent->_creteIntroMerchant = true;
|
||||
persistent->_creteShowHorned = true;
|
||||
persistent->_creteShowHornless1 = true;
|
||||
persistent->_creteShowHornless2 = true;
|
||||
persistent->_creteShowHornless3 = true;
|
||||
persistent->_creteShowHornless4 = true;
|
||||
}
|
||||
|
||||
room->playMusic(persistent->isInInventory(kHornedStatue)
|
||||
? "HornedIntroMusic" : "NormalIntroMusic");
|
||||
|
||||
if (persistent->_creteDaedalusRoomAvailable) {
|
||||
room->selectFrame(kStatues, kInstalledStatueZ, 0, Common::Point(37, -110));
|
||||
}
|
||||
|
||||
TextTable miAmb = TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(room->openFile("MiAmb.txt")), 6);
|
||||
_ambients.readTableFilePriamSFX(miAmb);
|
||||
g_vm->addTimer(14006, 100, -1);
|
||||
_ambients.firstFrame();
|
||||
}
|
||||
|
||||
private:
|
||||
void scheduleNagging() {
|
||||
g_vm->addTimer(kGuardNag, g_vm->getRnd().getRandomNumberRng(5000, 10000));
|
||||
}
|
||||
|
||||
void playMinosMovie(const Common::String &name, int callback,
|
||||
Common::Point offset) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->setLayerEnabled(kAnimMinosEating, false);
|
||||
room->playVideo(name, kMinosZ, callback, offset);
|
||||
room->disableMouse();
|
||||
_minosIsBusy = true;
|
||||
}
|
||||
|
||||
void minosBackToIdle() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_minosIsBusy = false;
|
||||
room->enableMouse();
|
||||
room->selectFrame(kAnimMinosEating, kMinosZ, 0);
|
||||
}
|
||||
|
||||
bool _guardIsBusy;
|
||||
bool _minosIsBusy;
|
||||
AmbientAnimWeightedSet _ambients;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeMinosHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new MinosHandler());
|
||||
}
|
||||
|
||||
}
|
||||
576
engines/hadesch/rooms/minotaur.cpp
Normal file
576
engines/hadesch/rooms/minotaur.cpp
Normal file
@@ -0,0 +1,576 @@
|
||||
/* 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 {
|
||||
static const char *kHighlightImage = "r6010ol0";
|
||||
static const char *kMaterialsImage = "r6010ok0";
|
||||
static const char *kMaterialsMoveImage = "r6020ba0";
|
||||
static const char *kMinotaurImage = "r6040ba0";
|
||||
static const char *kDigits = "0123456789";
|
||||
static const int numSquares = 25;
|
||||
static const char *minotaurStates[] = {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"inactive",
|
||||
"encourage",
|
||||
"trapped",
|
||||
"escaped",
|
||||
"critical",
|
||||
"level",
|
||||
"idle"
|
||||
};
|
||||
|
||||
enum DaedalusDialogState {
|
||||
kMinotaur0 = 0,
|
||||
kMinotaur1 = 1,
|
||||
kMinotaur2 = 2,
|
||||
kMinotaurInactive = 3,
|
||||
kMinotaurEncourage = 4,
|
||||
kMinotaurTrapped = 5,
|
||||
kMinotaurEscaped = 6,
|
||||
kMinotaurCritical = 7,
|
||||
kMinotaurLevel = 8,
|
||||
kMinotaurIdle = 9
|
||||
};
|
||||
|
||||
enum Strength {
|
||||
kStrengthStraw = 1,
|
||||
kStrengthWood = 2,
|
||||
kStrengthBricks = 3,
|
||||
kStrengthStone = 4
|
||||
};
|
||||
|
||||
enum Position {
|
||||
kDown = 0,
|
||||
kLeft = 1,
|
||||
kUp = 2,
|
||||
kRight = 3
|
||||
};
|
||||
|
||||
static const char *dirnames[] = {
|
||||
"down",
|
||||
"left",
|
||||
"up",
|
||||
"right"
|
||||
};
|
||||
|
||||
enum {
|
||||
kRerenderLabyrinth = 1017001
|
||||
};
|
||||
|
||||
static const char *daedalusSoundSMK[] = {
|
||||
"R6100nA0",
|
||||
"R6100wA0",
|
||||
"R6100nB0",
|
||||
"R6100wC0",
|
||||
"R6100nD0",
|
||||
};
|
||||
|
||||
static const TranscribedSound daedalusSoundsAIF[] = {
|
||||
{"R6100nH0", _hs("Help us to move the walls so that they are strong enough to stop the minotaur")},
|
||||
{"R6100nL0", _hs("Click on a square to rotate the walls")},
|
||||
{"R6100nG0", _hs("Some walls are already locked in place and won't rotate")},
|
||||
{"R6100nK0", _hs("If you need help, refer to workman's equations")},
|
||||
{"R6170nA0", _hs("Careful, my friend. Some of the walls are not strong enough")},
|
||||
{"R6150nA0", _hs("You're a brave bullfighter, my friend")},
|
||||
{"R6150nB0", _hs("Keep it up. It looks like he's tiring")},
|
||||
{"R6150nC0", _hs("That's taking the bull by the horns")},
|
||||
{"R6150nD0", _hs("Don't give up. You can't beat him")},
|
||||
{"R6180nA0", _hs("You have beaten the Minotaur. You have the makings of a hero")},
|
||||
{"R6180nC0", _hs("You have beaten the beast at last")},
|
||||
{"R6180nD0", _hs("You have done it. The people of Crete are once again safe")},
|
||||
{"R6170nC0", _hs("Let's try again")},
|
||||
{"R6170nD0", _hs("Warn the people of Crete: the Minotaur has escaped. Workers, keep the Minotaur back in the labyrinth")},
|
||||
{"R6170nE0", _hs("I believe you and the Minotaur have not seen the last of one another")},
|
||||
{"R6170nF0", _hs("Ah that was a nobble effort, my friend")},
|
||||
{"R6160nA0", _hs("The Minotaur has broken though a critical wall. Workers, calm on the beast")},
|
||||
{"R6090eA0", _hs("Eh. Hm")},
|
||||
{"R6190nA0", _hs("Ok. Onto level two")},
|
||||
{"R6190nB0", _hs("Onto level three")},
|
||||
};
|
||||
|
||||
struct Wall {
|
||||
int _id;
|
||||
bool _isCritical;
|
||||
int _inTransit;
|
||||
Strength _strength;
|
||||
Position _position;
|
||||
|
||||
Wall() {
|
||||
_id = -1;
|
||||
_isCritical = false;
|
||||
_strength = kStrengthStraw;
|
||||
_position = kDown;
|
||||
_inTransit = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Cell {
|
||||
Common::Array<Wall> _movableWalls;
|
||||
Common::Array<Wall> _immovableWalls;
|
||||
bool _isRotatable;
|
||||
|
||||
Cell() {
|
||||
_isRotatable = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct Labyrinth {
|
||||
Cell _cells[25];
|
||||
};
|
||||
|
||||
class MinotaurHandler : public Handler {
|
||||
public:
|
||||
MinotaurHandler() {
|
||||
_highlight = -1;
|
||||
|
||||
_dialogState = kMinotaur0;
|
||||
_minotaurX = 1;
|
||||
_minotaurY = 2;
|
||||
_minotaurTileId = 7;
|
||||
_soundCounter = 0;
|
||||
_soundMax = 5;
|
||||
|
||||
_lastChargeSound = -1;
|
||||
_lastTrappedSound = -1;
|
||||
_lastEscapedSound = -1;
|
||||
_levelId = 0;
|
||||
|
||||
// consts
|
||||
xVector = Common::Point(-55, -33);
|
||||
yVector = Common::Point(+55, -33);
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
if (name.firstChar() >= '0' && name.firstChar() <= '9') {
|
||||
rotate(name.asUint64());
|
||||
renderLabyrinth();
|
||||
return;
|
||||
}
|
||||
/* TODO: MNSH: Daedalus */
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
switch (eventId) {
|
||||
case kRerenderLabyrinth:
|
||||
renderLabyrinth();
|
||||
break;
|
||||
case 17953:
|
||||
g_vm->addTimer(17954, 300);
|
||||
break;
|
||||
case 17954:
|
||||
treatDialogState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleMouseOver(const Common::String &name) override {
|
||||
if (name.firstChar() >= '0' && name.firstChar() <= '9')
|
||||
_highlight = name.asUint64();
|
||||
else
|
||||
_highlight = -1;
|
||||
renderLabyrinth();
|
||||
}
|
||||
void handleMouseOut(const Common::String &name) override {
|
||||
_highlight = -1;
|
||||
renderLabyrinth();
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->loadHotZones("minotaur.hot", true);
|
||||
// TODO: load other puzzles
|
||||
loadPuzzle("3x3j");
|
||||
room->addStaticLayer("r6010pA0", 10000);
|
||||
room->addStaticLayer("r6010tA0", 6400);
|
||||
room->addStaticLayer("r6010oA0", 5500);
|
||||
room->addStaticLayer("r6010oB0", 4000);
|
||||
renderLabyrinth();
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kCool);
|
||||
setDialogState(kMinotaur1);
|
||||
}
|
||||
|
||||
bool handleCheat(const Common::String &cheat) override {
|
||||
for (unsigned i = 0; i < ARRAYSIZE(minotaurStates); i++)
|
||||
if (minotaurStates[i][0] && minotaurStates[i] == cheat) {
|
||||
setDialogState(DaedalusDialogState(i));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void setDialogState(DaedalusDialogState state) {
|
||||
if (_dialogState) {
|
||||
// TODO
|
||||
} else {
|
||||
_dialogState = state;
|
||||
}
|
||||
}
|
||||
|
||||
void playDaedalusSound(int index) {
|
||||
// TODO: balance
|
||||
_currentSound = index;
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (index < ARRAYSIZE(daedalusSoundSMK))
|
||||
room->playVideo(daedalusSoundSMK[index], 17953);
|
||||
else
|
||||
room->playSpeech(daedalusSoundsAIF[index-ARRAYSIZE(daedalusSoundSMK)], 17953);
|
||||
}
|
||||
|
||||
void playDaedalusSoundWrap() {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
int index = 0;
|
||||
switch (_dialogState) {
|
||||
case kMinotaur0:
|
||||
treatDialogState();
|
||||
return;
|
||||
case kMinotaur1:
|
||||
index = _soundCounter;
|
||||
break;
|
||||
case kMinotaur2:
|
||||
index = _soundCounter + 5;
|
||||
break;
|
||||
case kMinotaurInactive:
|
||||
index = 9;
|
||||
break;
|
||||
case kMinotaurEncourage:
|
||||
index = randomExcept(10, 13, _lastChargeSound);
|
||||
_lastChargeSound = index;
|
||||
break;
|
||||
case kMinotaurTrapped:
|
||||
if (persistent->_quest == kCreteQuest)
|
||||
index = 14;
|
||||
else
|
||||
index = randomExcept(14, 16, _lastTrappedSound);
|
||||
_lastTrappedSound = index;
|
||||
break;
|
||||
case kMinotaurEscaped:
|
||||
index = randomExcept(17, 20, _lastEscapedSound);
|
||||
_lastEscapedSound = index;
|
||||
break;
|
||||
case kMinotaurCritical:
|
||||
index = 21;
|
||||
break;
|
||||
case kMinotaurLevel:
|
||||
index = 23 + _levelId / 15;
|
||||
break;
|
||||
case kMinotaurIdle:
|
||||
index = 22;
|
||||
break;
|
||||
}
|
||||
|
||||
playDaedalusSound(index);
|
||||
}
|
||||
|
||||
int randomExcept(int from, int to, int except) {
|
||||
Common::RandomSource &r = g_vm->getRnd();
|
||||
if (except < from || except > to)
|
||||
return r.getRandomNumberRng(from, to);
|
||||
int x = r.getRandomNumberRng(from, to - 1);
|
||||
if (x >= except)
|
||||
x++;
|
||||
return x;
|
||||
}
|
||||
|
||||
void treatDialogState() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
switch(_dialogState) {
|
||||
case kMinotaur1:
|
||||
if (_soundCounter < _soundMax) {
|
||||
playDaedalusSoundWrap();
|
||||
_soundCounter++;
|
||||
return;
|
||||
}
|
||||
setDialogState(kMinotaur2);
|
||||
return;
|
||||
case kMinotaur2:
|
||||
if (_soundCounter < _soundMax) {
|
||||
playDaedalusSoundWrap();
|
||||
_soundCounter++;
|
||||
return;
|
||||
}
|
||||
room->enableMouse();
|
||||
setDialogState(kMinotaur0);
|
||||
return;
|
||||
default:
|
||||
// TODO: implement this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void renderLabyrinth() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (_highlight >= 0)
|
||||
room->selectFrame(kHighlightImage, 9990, 0,
|
||||
getTileBase(_highlight)
|
||||
- Common::Point(34, 52));
|
||||
else
|
||||
room->stopAnim(kHighlightImage);
|
||||
|
||||
for (int cell = 0; cell < numSquares; cell++) {
|
||||
for (int j = 0; j < ARRAYSIZE(dirnames); j++) {
|
||||
room->stopAnim(
|
||||
LayerId(kMaterialsImage, cell, Common::String(dirnames[j]) + "outer"));
|
||||
room->stopAnim(
|
||||
LayerId(kMaterialsImage, cell, Common::String(dirnames[j]) + "inner"));
|
||||
room->stopAnim(
|
||||
LayerId(kMaterialsMoveImage, cell, "to-" + Common::String(dirnames[j])));
|
||||
}
|
||||
|
||||
for (int j = 0; j < (int) _current._cells[cell]._movableWalls.size(); j++) {
|
||||
renderWall(cell, _current._cells[cell]._movableWalls[j], false);
|
||||
}
|
||||
|
||||
// Both original engine and us we're not able to handle 3
|
||||
// walls in the same place. The only way to avoid this from every hapenning
|
||||
// is to never put an immovable wall between 2 cells with movable walls.
|
||||
// Hence if we have any movable walls then we can make immovable walls outer
|
||||
// and they will not conflict with existing labyrinths.
|
||||
// If we have no movable walls we can easily put all immovable walls as inner
|
||||
|
||||
bool immovableAreOuter = !_current._cells[cell]._movableWalls.empty();
|
||||
|
||||
for (int j = 0; j < (int) _current._cells[cell]._immovableWalls.size(); j++) {
|
||||
renderWall(cell, _current._cells[cell]._immovableWalls[j], immovableAreOuter);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: which frame?
|
||||
room->selectFrame(kMinotaurImage, getMinotaurZ(), 30,
|
||||
getTileBase(_minotaurX, _minotaurY) - Common::Point(/*114, 117*/30+82, 33*2+52));
|
||||
}
|
||||
|
||||
int getMinotaurZ() {
|
||||
if (_minotaurX >= 5) {
|
||||
return 6500;
|
||||
}
|
||||
|
||||
if (_minotaurX < 0) {
|
||||
return 4500;
|
||||
}
|
||||
|
||||
if (_minotaurY >= 5) {
|
||||
return 5960;
|
||||
}
|
||||
|
||||
if (_minotaurY < 0) {
|
||||
return 4500;
|
||||
}
|
||||
|
||||
return 5000 + 150 * (_minotaurX + _minotaurY) + 60;
|
||||
}
|
||||
|
||||
void renderWall(int cell, Wall &wall, bool outer) {
|
||||
Common::Point delta;
|
||||
if (wall._inTransit) {
|
||||
wall._inTransit--;
|
||||
g_vm->getVideoRoom()->selectFrame(
|
||||
LayerId(kMaterialsMoveImage, cell, Common::String("to-") + dirnames[wall._position]),
|
||||
getWallZ(cell, wall._position, outer),
|
||||
(wall._strength - kStrengthStraw) * 4 + (wall._position + 1) % 4,
|
||||
getTileBase(cell) + Common::Point(-40, -88));
|
||||
g_vm->addTimer(kRerenderLabyrinth, kDefaultSpeed);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (wall._position) {
|
||||
case kUp:
|
||||
delta = xVector + xVector + yVector + Common::Point(-8, -3) +
|
||||
(outer ? Common::Point(0, 0) : Common::Point(+7, +5));
|
||||
break;
|
||||
case kDown:
|
||||
delta = xVector + yVector + Common::Point(-8, -3) +
|
||||
(outer ? Common::Point(+7, +5) : Common::Point(0, 0));
|
||||
break;
|
||||
case kLeft:
|
||||
delta = xVector + Common::Point(0, -33)
|
||||
+ (outer ? Common::Point(-7, +5) : Common::Point(0, 0));
|
||||
break;
|
||||
case kRight:
|
||||
delta = xVector + yVector + Common::Point(0, -33)
|
||||
+ (outer ? Common::Point(0, 0) : Common::Point(-7, +5));
|
||||
break;
|
||||
}
|
||||
|
||||
Common::Point pos = getTileBase(cell) + delta;
|
||||
|
||||
LayerId layer(kMaterialsImage, cell,
|
||||
Common::String(dirnames[wall._position]) +
|
||||
(outer ? "outer" : "inner"));
|
||||
int frame = (wall._strength - kStrengthStraw) * 2 + (wall._position % 2);
|
||||
g_vm->getVideoRoom()->selectFrame(layer, getWallZ(cell, wall._position, outer), frame, pos);
|
||||
}
|
||||
|
||||
int getWallZ(int cell, Position pos, bool outer) {
|
||||
int zValue = 150 * (cell / 5 + cell % 5) + 5000;
|
||||
switch (pos) {
|
||||
case kUp:
|
||||
zValue += outer? 110 : 100;
|
||||
break;
|
||||
case kDown:
|
||||
zValue += outer ? -10 : 0;
|
||||
break;
|
||||
case kLeft:
|
||||
zValue += outer ? 40 : 50;
|
||||
break;
|
||||
case kRight:
|
||||
zValue += outer ? 80 : 70;
|
||||
break;
|
||||
}
|
||||
return zValue;
|
||||
}
|
||||
|
||||
Common::Point getTileBase(int x, int y) {
|
||||
return Common::Point(320, 456) + xVector * x + yVector * y;
|
||||
}
|
||||
|
||||
Common::Point getTileBase(int id) {
|
||||
return getTileBase(id / 5, id % 5);
|
||||
}
|
||||
|
||||
void rotate(int cell) {
|
||||
for (int j = 0;
|
||||
j < (int) _current._cells[cell]._movableWalls.size();
|
||||
j++) {
|
||||
_current._cells[cell]._movableWalls[j]._position
|
||||
= (Position) ((_current._cells[cell]._movableWalls[j]._position + 1) % 4);
|
||||
_current._cells[cell]._movableWalls[j]._inTransit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void loadPuzzle(const Common::String &name) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Common::SharedPtr<Common::SeekableReadStream> gameStream(room->openFile(name + ".mcf"));
|
||||
Common::SharedPtr<Common::SeekableReadStream> solStream(room->openFile(name + ".sol"));
|
||||
Common::SharedPtr<Common::SeekableReadStream> cwStream(room->openFile(name + ".cw"));
|
||||
readLabStream(_current, gameStream);
|
||||
readLabStream(_solution, solStream);
|
||||
for (int cell = 0; cell < numSquares; cell++) {
|
||||
room->setHotzoneEnabled(Common::String::format("%d", cell),
|
||||
!_current._cells[cell]._movableWalls.empty());
|
||||
}
|
||||
}
|
||||
|
||||
static void readLabStream(Labyrinth &lab, Common::SharedPtr<Common::SeekableReadStream> stream) {
|
||||
stream->readLine(); // Level number
|
||||
int gridSize = stream->readLine().asUint64();
|
||||
|
||||
if (gridSize == 0)
|
||||
gridSize = 1;
|
||||
|
||||
stream->readLine(); // ?
|
||||
stream->readLine(); // ?
|
||||
int numLines = stream->readLine().asUint64();
|
||||
// Extra walls
|
||||
if (gridSize == 3 || gridSize == 4) {
|
||||
Wall w;
|
||||
w._isCritical = false;
|
||||
w._id = -1;
|
||||
w._strength = kStrengthStone;
|
||||
w._position = kRight;
|
||||
if (gridSize == 3)
|
||||
lab._cells[4]._immovableWalls.push_back(w);
|
||||
lab._cells[24]._immovableWalls.push_back(w);
|
||||
}
|
||||
for (int i = 0; i < numLines; i++) {
|
||||
Common::String line = stream->readLine();
|
||||
size_t cur = 0;
|
||||
int rawcellid = line.asUint64();
|
||||
int transformedcellid =
|
||||
5 * (rawcellid / gridSize) +
|
||||
(rawcellid % gridSize) + (5 - gridSize)
|
||||
+ 5 * ((5 - gridSize) / 2);
|
||||
cur = line.findFirstNotOf(kDigits, cur);
|
||||
cur = line.findFirstOf(kDigits, cur);
|
||||
int numWalls = line.substr(cur).asUint64();
|
||||
cur = line.findFirstNotOf(kDigits, cur);
|
||||
cur = line.findFirstOf(kDigits, cur);
|
||||
/*int rotatable =*/ line.substr(cur).asUint64();
|
||||
cur = line.findFirstNotOf(kDigits, cur);
|
||||
cur = line.findFirstOf(kDigits, cur);
|
||||
for (int j = 0; j < numWalls; j++) {
|
||||
Wall w;
|
||||
w._isCritical = false;
|
||||
w._id = line.substr(cur).asUint64();
|
||||
cur = line.findFirstNotOf(kDigits, cur);
|
||||
cur = line.findFirstOf(kDigits, cur);
|
||||
int pos = line.substr(cur).asUint64();
|
||||
cur = line.findFirstNotOf(kDigits, cur);
|
||||
cur = line.findFirstOf(kDigits, cur);
|
||||
w._strength = (Strength) line.substr(cur).asUint64();
|
||||
cur = line.findFirstNotOf(kDigits, cur);
|
||||
cur = line.findFirstOf(kDigits, cur);
|
||||
switch (pos % 4) {
|
||||
case 0:
|
||||
w._position = kLeft;
|
||||
break;
|
||||
case 1:
|
||||
w._position = kUp;
|
||||
break;
|
||||
case 2:
|
||||
w._position = kRight;
|
||||
break;
|
||||
case 3:
|
||||
w._position = kDown;
|
||||
break;
|
||||
}
|
||||
if (pos >= 4) {
|
||||
lab._cells[transformedcellid]._immovableWalls.push_back(w);
|
||||
} else {
|
||||
lab._cells[transformedcellid]._movableWalls.push_back(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::Point xVector;
|
||||
Common::Point yVector;
|
||||
int _highlight;
|
||||
|
||||
DaedalusDialogState _dialogState;
|
||||
int _minotaurX;
|
||||
int _minotaurY;
|
||||
int _minotaurTileId;
|
||||
int _lastChargeSound;
|
||||
int _lastTrappedSound;
|
||||
int _lastEscapedSound;
|
||||
int _levelId;
|
||||
int _currentSound;
|
||||
int _soundCounter;
|
||||
int _soundMax;
|
||||
|
||||
Labyrinth _current;
|
||||
Labyrinth _solution;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeMinotaurHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new MinotaurHandler());
|
||||
}
|
||||
|
||||
}
|
||||
402
engines/hadesch/rooms/monster.cpp
Normal file
402
engines/hadesch/rooms/monster.cpp
Normal file
@@ -0,0 +1,402 @@
|
||||
/* 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/rooms/monster.h"
|
||||
|
||||
namespace Hadesch {
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kCyclopsZ = 500,
|
||||
kZeusLightZ = 500,
|
||||
kTyphoonZ = 500
|
||||
};
|
||||
|
||||
static const char *kZeusLight = "V7100BJ0";
|
||||
static const int kLightningCutoff = kVideoWidth / 2;
|
||||
|
||||
TranscribedSound revitalisedSound() {
|
||||
return g_vm->getRnd().getRandomBit()
|
||||
? TranscribedSound::make("v7150wd0", "Your branch of life is revitalized")
|
||||
: TranscribedSound::make("v7150we0", "You're back to full strength");
|
||||
}
|
||||
|
||||
class MonsterHandler : public Handler {
|
||||
public:
|
||||
MonsterHandler() {
|
||||
_playingShootingSound = false;
|
||||
_countOfIntroLightning = 0;
|
||||
_battleground = Common::SharedPtr<Battleground>(new Battleground());
|
||||
_typhoon = Common::SharedPtr<Typhoon>(new Typhoon(_battleground));
|
||||
_cyclops = Common::SharedPtr<Cyclops>(new Cyclops(_battleground));
|
||||
_illusion = Common::SharedPtr<Illusion>(new Illusion(_battleground));
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
if (_battleground->_isInFight && _battleground->_monsterNum == kTyphoon) {
|
||||
_typhoon->handleClick(_typhoon, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_battleground->_isInFight && _battleground->_monsterNum == kIllusion) {
|
||||
_illusion->handleClick(name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
_typhoon->handleEvent(eventId);
|
||||
_cyclops->handleEvent(eventId);
|
||||
_illusion->handleEvent(eventId);
|
||||
|
||||
switch (eventId) {
|
||||
case 524:
|
||||
g_vm->getHeroBelt()->setBranchOfLifeFrame(0);
|
||||
break;
|
||||
case 526:
|
||||
g_vm->getHeroBelt()->setThunderboltFrame(kLightning3);
|
||||
break;
|
||||
|
||||
case 15351:
|
||||
room->playAnimWithMusic(kZeusLight, "G0260MA0", kZeusLightZ,
|
||||
PlayAnimParams::keepLastFrame().partial(0, 4),
|
||||
15379); // 15379(anim), 15381(-1), 15359(sound)
|
||||
break;
|
||||
case 15352:
|
||||
room->playVideo("V7190BA0", 0, 15386, Common::Point(0, 216));
|
||||
break;
|
||||
case 15353:
|
||||
room->playAnim(kZeusLight, kZeusLightZ,
|
||||
PlayAnimParams::disappear().partial(4, -1),
|
||||
15358);
|
||||
break;
|
||||
case 15355:
|
||||
room->playSpeech(TranscribedSound::make("V7100WC0",
|
||||
"I'm giving you these thunderbolts to "
|
||||
"use against Hades' monsters."),
|
||||
15364);
|
||||
g_vm->getHeroBelt()->setThunderboltFrame(kLightning2);
|
||||
g_vm->addTimer(526, 5000);
|
||||
break;
|
||||
case 15356:
|
||||
room->playVideo("V7180BB0", 0, 15361, Common::Point(0, 216));
|
||||
break;
|
||||
case 15357:
|
||||
room->playSpeech(g_vm->getRnd().getRandomBit()
|
||||
? TranscribedSound::make("V7150WC0", "Get back in there. Here is another branch")
|
||||
: TranscribedSound::make("V7150WB0", "Here's another branch. Keep going"),
|
||||
15353);
|
||||
break;
|
||||
case 15358:
|
||||
switch (_battleground->_monsterNum) {
|
||||
case kCyclops:
|
||||
room->playVideo("V7180BB0", 0, 15389, Common::Point(0, 216));
|
||||
break;
|
||||
case kTyphoon:
|
||||
room->playVideo("V7210BB0", 0, 15387, Common::Point(0, 216));
|
||||
break;
|
||||
case kIllusion:
|
||||
room->playVideo("V7220BR0", 0, 15382, Common::Point(0, 216));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15361:
|
||||
g_vm->addTimer(15391, 100);
|
||||
break;
|
||||
case 15364:
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"V7100WD0",
|
||||
"Ah, and this branch of life will let "
|
||||
"you to remain in the underworld until "
|
||||
"all of its leaves have fallen"),
|
||||
15365);
|
||||
_battleground->_leavesRemaining = 9;
|
||||
g_vm->getHeroBelt()->setBranchOfLifeFrame(1);
|
||||
g_vm->addTimer(524, 5000);
|
||||
break;
|
||||
case 15365:
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"V7100WE0",
|
||||
"Use your thunderbolts and your hero powers "
|
||||
"to battle the monsters of the underworld"), 15366);
|
||||
_countOfIntroLightning = 0;
|
||||
introLightning();
|
||||
break;
|
||||
case 15366:
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"V7100WF0",
|
||||
"Move your mouse to aim and click to fire your thunderbolts. "
|
||||
"And don't forget: you can now use your hero powers"),
|
||||
15367);
|
||||
break;
|
||||
case 15367:
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"V7100WH0",
|
||||
"And remember to keep an eye on your branch. "
|
||||
"When the last leaf drops, you'll be "
|
||||
"banished from the underworld"),
|
||||
15368);
|
||||
break;
|
||||
case 15368:
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"V7100WI0",
|
||||
"This is the ultimate test but I know you can do it"),
|
||||
15369);
|
||||
break;
|
||||
case 15369:
|
||||
room->playAnim(kZeusLight, kZeusLightZ,
|
||||
PlayAnimParams::disappear().partial(4, -1),
|
||||
15356);
|
||||
break;
|
||||
case 15370:
|
||||
if (++_countOfIntroLightning < 4)
|
||||
introLightning();
|
||||
break;
|
||||
case 15374:
|
||||
room->playAnimWithMusic(kZeusLight, "G0260MA0", kZeusLightZ,
|
||||
PlayAnimParams::keepLastFrame().partial(0, 4),
|
||||
15375);
|
||||
break;
|
||||
case 15375:
|
||||
room->playSpeech(revitalisedSound(), 15376);
|
||||
replenishPowers();
|
||||
break;
|
||||
case 15376:
|
||||
room->playAnim(kZeusLight, kZeusLightZ,
|
||||
PlayAnimParams::disappear().partial(4, -1),
|
||||
15377);
|
||||
break;
|
||||
case 15377:
|
||||
room->playVideo("V7210BB0", 0, 15387, Common::Point(0, 216));
|
||||
break;
|
||||
case 15378:
|
||||
handleEvent(15390);
|
||||
break;
|
||||
case 15379:
|
||||
room->playSpeech(revitalisedSound(), 15380);
|
||||
replenishPowers();
|
||||
break;
|
||||
case 15380:
|
||||
room->playAnim(kZeusLight, kZeusLightZ,
|
||||
PlayAnimParams::disappear().partial(4, -1),
|
||||
15381);
|
||||
break;
|
||||
case 15381:
|
||||
room->playVideo("V7220BR0", 0, 15388, Common::Point(0, 216));
|
||||
break;
|
||||
case 15382:
|
||||
g_vm->addTimer(15392, 100);
|
||||
break;
|
||||
case 15383:
|
||||
if (persistent->_quest == kRescuePhilQuest)
|
||||
g_vm->moveToRoom(kHadesThroneRoom);
|
||||
else {
|
||||
_battleground->_level++;
|
||||
_cyclops->enterCyclops(_battleground->_level);
|
||||
}
|
||||
break;
|
||||
case 15386:
|
||||
// unclear
|
||||
room->playSpeech(
|
||||
persistent->_gender == kMale
|
||||
? TranscribedSound::make(
|
||||
"V7190WB0",
|
||||
"One more word out of your goat-brain "
|
||||
"and I'm gonna have your face for lambchops, alright? "
|
||||
"This kid's gonna have to make it on his own, ok?")
|
||||
: TranscribedSound::make(
|
||||
"V7190WC0",
|
||||
"One more word out of your goat-brain "
|
||||
"and I'm gonna have your face for lambchops, ok? "
|
||||
"This kid's gonna have to make it on her own."),
|
||||
15374);
|
||||
break;
|
||||
case 15387:
|
||||
room->playSpeech(
|
||||
persistent->_gender == kMale
|
||||
? TranscribedSound::make(
|
||||
"V7210WB0",
|
||||
"Oh, you want to be a hero? "
|
||||
"Well you're gonna die a hero's death. "
|
||||
"Typhoon's gonna chew you up in little "
|
||||
"pieces and spit you out like a meatgrinder, kid")
|
||||
: TranscribedSound::make(
|
||||
"V7210WC0",
|
||||
"Oh, you want to be a heroine? "
|
||||
"Well you're gonna die a gruesome death. "
|
||||
"Typhoon's gonna chew you up in little "
|
||||
"pieces and spit you out like a meatgrinder, little princess."),
|
||||
15378);
|
||||
break;
|
||||
case 15388:
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"V7220WB1",
|
||||
"You dare to think you can outwit me? "
|
||||
"You, my little friend, will be ripped to shreads and slowly digested for eternity "
|
||||
"inside a belly of a thousand hideous creatures. You will die a thousand agonizing "
|
||||
"deaths as I now bring down upon you all the forces of Hades."),
|
||||
15382);
|
||||
break;
|
||||
case 15389:
|
||||
// unclear
|
||||
room->playSpeech(TranscribedSound::make(
|
||||
"V7180WB0",
|
||||
"Hey, there. Hi, there. Hoi, there. "
|
||||
"And welcome to my world. "
|
||||
"You know what they say: \"My world - my rules\". "
|
||||
"So here is the rule number one: No trespassing. "
|
||||
"My bouncer will show you the way out. Have a nice day"),
|
||||
15361);
|
||||
break;
|
||||
case 15390:
|
||||
_typhoon->enterTyphoon(1);
|
||||
break;
|
||||
case 15391:
|
||||
_cyclops->enterCyclops(1);
|
||||
break;
|
||||
case 15392:
|
||||
_illusion->enterIllusion(1);
|
||||
break;
|
||||
case 15465:
|
||||
_playingShootingSound = false;
|
||||
break;
|
||||
case kHitReceived:
|
||||
_battleground->_leavesRemaining--;
|
||||
if (_battleground->_leavesRemaining >= 0)
|
||||
g_vm->getHeroBelt()->setBranchOfLifeFrame(10 - _battleground->_leavesRemaining);
|
||||
if (_battleground->_leavesRemaining <= 0) {
|
||||
_battleground->stopFight();
|
||||
room->disableMouse();
|
||||
room->playAnimWithMusic(kZeusLight, "G0260MA0", kZeusLightZ,
|
||||
PlayAnimParams::keepLastFrame().partial(0, 4),
|
||||
15357);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kCold);
|
||||
room->loadHotZones("Monster.HOT", false);
|
||||
room->addStaticLayer("v7010pa0", kBackgroundZ, Common::Point(-10, -10)); // background
|
||||
// event 15362+15363
|
||||
|
||||
room->disableMouse();
|
||||
_battleground->_monsterNum = kCyclops;
|
||||
room->playAnimWithMusic(kZeusLight, "G0260MA0", kZeusLightZ,
|
||||
PlayAnimParams::keepLastFrame().partial(0, 4),
|
||||
15355);
|
||||
}
|
||||
|
||||
void frameCallback() override {
|
||||
_battleground->tick();
|
||||
_illusion->tick();
|
||||
}
|
||||
|
||||
void handleAbsoluteClick(Common::Point p) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (!_battleground->_isInFight)
|
||||
return;
|
||||
_battleground->handleAbsoluteClick(p);
|
||||
|
||||
HeroPower hp = g_vm->getHeroBelt()->getSelectedStrength();
|
||||
bool isStrong = hp == kPowerStrength;
|
||||
|
||||
if (p.x < kLightningCutoff) {
|
||||
room->playAnim(isStrong ? "v7130ba2" : "v7130ba0", 300, PlayAnimParams::disappear(), EventHandlerWrapper(), p);
|
||||
} else {
|
||||
room->playAnim(isStrong ? "v7130ba3" : "v7130ba1", 300, PlayAnimParams::disappear(),
|
||||
EventHandlerWrapper(), p - Common::Point(kLightningCutoff, 0));
|
||||
}
|
||||
|
||||
if (!_playingShootingSound) {
|
||||
switch (hp) {
|
||||
case kPowerStealth:
|
||||
room->playSFX("v7130ea0");
|
||||
break;
|
||||
case kPowerStrength:
|
||||
room->playSFX("v7130eb0");
|
||||
break;
|
||||
case kPowerWisdom:
|
||||
room->playSFX("v7130ec0");
|
||||
break;
|
||||
case kPowerNone:
|
||||
room->playSFX("v7130ee0");
|
||||
break;
|
||||
}
|
||||
_playingShootingSound = true;
|
||||
g_vm->addTimer(15465, g_vm->getRnd().getRandomNumberRng(300, 600));
|
||||
}
|
||||
|
||||
switch (_battleground->_monsterNum) {
|
||||
case kCyclops:
|
||||
_cyclops->handleClick(p);
|
||||
break;
|
||||
case kTyphoon:
|
||||
break;
|
||||
case kIllusion:
|
||||
_illusion->handleAbsoluteClick(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void replenishPowers() {
|
||||
g_vm->getHeroBelt()->setThunderboltFrame(kLightning2);
|
||||
g_vm->addTimer(526, 5000);
|
||||
_battleground->_leavesRemaining = 9;
|
||||
g_vm->getHeroBelt()->setBranchOfLifeFrame(1);
|
||||
g_vm->addTimer(524, 5000);
|
||||
}
|
||||
|
||||
void introLightning() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Common::Point target = Common::Point(
|
||||
g_vm->getRnd().getRandomNumberRng(150, 450),
|
||||
g_vm->getRnd().getRandomNumberRng(50, 350));
|
||||
if (target.x < kLightningCutoff) {
|
||||
room->playAnim("v7130ba0", 300, PlayAnimParams::disappear(), 15370, target);
|
||||
} else {
|
||||
room->playAnim("v7130ba1", 300, PlayAnimParams::disappear(), 15370,
|
||||
target - Common::Point(kLightningCutoff, 0));
|
||||
}
|
||||
room->playSFX("v7130eb0");
|
||||
}
|
||||
|
||||
bool _playingShootingSound;
|
||||
int _countOfIntroLightning;
|
||||
Common::SharedPtr<Battleground> _battleground;
|
||||
Common::SharedPtr<Typhoon> _typhoon;
|
||||
Common::SharedPtr<Cyclops> _cyclops;
|
||||
Common::SharedPtr<Illusion> _illusion;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeMonsterHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new MonsterHandler());
|
||||
}
|
||||
|
||||
}
|
||||
205
engines/hadesch/rooms/monster.h
Normal file
205
engines/hadesch/rooms/monster.h
Normal file
@@ -0,0 +1,205 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
namespace Hadesch {
|
||||
|
||||
enum {
|
||||
// Splits of 15269 as we don't support intermediate anim callbacks
|
||||
kCyclopsShootingEyeOpenMidAnim = 1015001,
|
||||
kCyclopsShootingEyeClosedMidAnim = 1015002,
|
||||
kHitReceived = 1015003
|
||||
};
|
||||
|
||||
|
||||
enum Monster {
|
||||
kCyclops = 1,
|
||||
kTyphoon = 2,
|
||||
kIllusion = 3
|
||||
};
|
||||
|
||||
struct FlightPosition {
|
||||
Common::Point centerPos;
|
||||
int scale;
|
||||
};
|
||||
|
||||
class Projectile : Common::NonCopyable {
|
||||
public:
|
||||
Projectile(int id, int level, Monster monster, int startScale, Common::Point startPoint, int xmomentum);
|
||||
~Projectile();
|
||||
|
||||
void handleEvent(int ev);
|
||||
void stop();
|
||||
void makeFlightParams(int xmomentum);
|
||||
FlightPosition getFlightPosition(double t);
|
||||
void handleAbsoluteClick(Common::SharedPtr <Projectile> backRef, Common::Point p);
|
||||
|
||||
// Event 15051
|
||||
bool tick(Common::SharedPtr <Projectile> backRef);
|
||||
|
||||
private:
|
||||
int getProjectileFlightLength(int level);
|
||||
int getProjectileHitChance();
|
||||
|
||||
int _level;
|
||||
bool _isMiss;
|
||||
int _flightCounterMs;
|
||||
int _flightStart;
|
||||
int _projectileId;
|
||||
int _pending;
|
||||
bool _isFlightFinished;
|
||||
LayerId _pendingAnim;
|
||||
Common::String _flyAnim;
|
||||
Common::String _interceptAnim;
|
||||
Common::String _hitAnim;
|
||||
int _startScale;
|
||||
int _flightLengthMs;
|
||||
Common::Point _start, _target, _attractor1, _attractor2;
|
||||
};
|
||||
|
||||
class Battleground {
|
||||
public:
|
||||
Battleground();
|
||||
|
||||
int getNumOfProjectiles();
|
||||
void launchProjectile(int startScale, Common::Point startPoint, int xmomentum);
|
||||
void handleAbsoluteClick(Common::Point p);
|
||||
void tick();
|
||||
void stopFight();
|
||||
|
||||
int _level;
|
||||
int _leavesRemaining;
|
||||
Monster _monsterNum;
|
||||
bool _isInFight;
|
||||
|
||||
private:
|
||||
void stopProjectiles();
|
||||
|
||||
Common::Array <Common::SharedPtr<Projectile> > _projectiles;
|
||||
int _projectileId;
|
||||
};
|
||||
|
||||
struct Typhoon {
|
||||
Typhoon(Common::SharedPtr<Battleground> battleground);
|
||||
void handleEvent(int eventId);
|
||||
void enterTyphoon(int level);
|
||||
void handleClick(Common::SharedPtr<Typhoon> backRef,
|
||||
const Common::String &name);
|
||||
void hideHead(int idx);
|
||||
void typhoonA();
|
||||
void schedule15154();
|
||||
int typhonGetNumAliveHeads();
|
||||
void hitTyphoonHead(Common::SharedPtr<Typhoon> backRef, int idx);
|
||||
void showHeadNormal(int idx);
|
||||
|
||||
static void stopAnims();
|
||||
static void disableHotzones();
|
||||
|
||||
bool _headIsAlive[18];
|
||||
bool _playingTyphoonRespawnSound;
|
||||
bool _playingTyphoonDieSound;
|
||||
bool _isKilled;
|
||||
Common::SharedPtr<Battleground> _battleground;
|
||||
};
|
||||
|
||||
class Cyclops {
|
||||
public:
|
||||
Cyclops(Common::SharedPtr<Battleground> battleground);
|
||||
void handleEvent(int eventId);
|
||||
void handleClick(Common::Point p);
|
||||
void enterCyclops(int level);
|
||||
|
||||
private:
|
||||
bool cyclopsIsHit(Common::Point p, int frame);
|
||||
bool cyclopsIsHitBA0(Common::Point p, int frame);
|
||||
unsigned getSquareOfPrecision();
|
||||
void cyclopsState0();
|
||||
void cyclopsState1();
|
||||
void cyclopsState2();
|
||||
void cyclopsState3();
|
||||
void cyclopsState4();
|
||||
void cyclopsState5();
|
||||
void cyclopsState6();
|
||||
|
||||
Common::SharedPtr<Battleground> _battleground;
|
||||
bool _cyclopsIsHiding;
|
||||
int _cyclopsProximityCheckCountdown;
|
||||
int _currentCyclopsState;
|
||||
};
|
||||
|
||||
class Bird : Common::NonCopyable {
|
||||
public:
|
||||
Bird(int id);
|
||||
|
||||
void launch(int level);
|
||||
|
||||
void stop();
|
||||
|
||||
FlightPosition getFlightPosition(double t);
|
||||
|
||||
void handleAbsoluteClick(Common::Point p);
|
||||
|
||||
// Event 15201
|
||||
void tick(Common::SharedPtr <Bird> backRef, Common::SharedPtr<Battleground> battleground);
|
||||
|
||||
void makeFlightParams();
|
||||
|
||||
int _id;
|
||||
int _level;
|
||||
bool _isActive;
|
||||
Common::Point _startPos;
|
||||
int _flightLengthMs;
|
||||
int _flightStart;
|
||||
int _flightCounterMs;
|
||||
int _flightShootAnimFrame;
|
||||
int _flightShootProjectileFrame;
|
||||
int _flightShootEndFrame;
|
||||
bool _hasShot;
|
||||
|
||||
Common::Point _targetPos;
|
||||
Common::Point _attractor1;
|
||||
Common::Point _attractor2;
|
||||
|
||||
int _birdType;
|
||||
|
||||
int _field84; // ?
|
||||
};
|
||||
|
||||
class Illusion {
|
||||
public:
|
||||
Illusion(Common::SharedPtr<Battleground> battleground);
|
||||
void handleEvent(int eventId);
|
||||
void handleAbsoluteClick(Common::Point p);
|
||||
void handleClick(const Common::String &name);
|
||||
void enterIllusion(int level);
|
||||
void tick();
|
||||
static void stopAnims();
|
||||
private:
|
||||
void movePhil();
|
||||
void launchBird();
|
||||
|
||||
Common::SharedPtr<Bird> _birds[3];
|
||||
int _philPosition;
|
||||
bool _illusionIsKilled;
|
||||
Common::SharedPtr<Battleground> _battleground;
|
||||
};
|
||||
|
||||
}
|
||||
386
engines/hadesch/rooms/monster/cyclops.cpp
Normal file
386
engines/hadesch/rooms/monster/cyclops.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
/* 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/rooms/monster.h"
|
||||
|
||||
namespace Hadesch {
|
||||
static const char *kCyclopsShootingEyeOpen = "v7180bh0";
|
||||
static const char *kCyclopsShootingEyeClosed = "v7180bh1";
|
||||
|
||||
enum {
|
||||
kCyclopsZ = 500
|
||||
};
|
||||
|
||||
static const PrePoint cyclopsEyePositions[21] = {
|
||||
{247, 175},
|
||||
{235, 187},
|
||||
{227, 183},
|
||||
{221, 178},
|
||||
{220, 170},
|
||||
{230, 168},
|
||||
{230, 168},
|
||||
{224, 170},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{281, 175},
|
||||
{282, 170},
|
||||
{282, 170},
|
||||
{284, 175},
|
||||
{270, 178},
|
||||
{257, 179},
|
||||
{250, 176},
|
||||
{249, 176},
|
||||
{248, 176},
|
||||
{246, 176}
|
||||
};
|
||||
|
||||
static const PrePoint cyclopsEyePositionsBA0[8] = {
|
||||
{246, 176},
|
||||
{248, 174},
|
||||
{249, 166},
|
||||
{248, 171},
|
||||
{0, 0},
|
||||
{241, 183},
|
||||
{244, 181},
|
||||
{246, 176}
|
||||
};
|
||||
|
||||
void Cyclops::handleClick(Common::Point p) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
int frame;
|
||||
if (g_vm->getHeroBelt()->getSelectedStrength() != kPowerStealth || (_currentCyclopsState != 0 && _currentCyclopsState != 1))
|
||||
return;
|
||||
switch (_currentCyclopsState) {
|
||||
case 0:
|
||||
if (!_cyclopsIsHiding)
|
||||
return;
|
||||
frame = room->getAnimFrameNum("v7180bh0");
|
||||
break;
|
||||
case 1:
|
||||
frame = room->getAnimFrameNum("v7040ba0");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cyclopsIsHit(p, frame))
|
||||
return;
|
||||
room->disableMouse();
|
||||
_battleground->stopFight();
|
||||
room->playAnimWithSFX("v7180bj0", "v7180xc0", 500, PlayAnimParams::disappear(), 15352);
|
||||
}
|
||||
|
||||
bool Cyclops::cyclopsIsHit(Common::Point p, int frame) {
|
||||
if (frame < 0 || frame >= ARRAYSIZE(cyclopsEyePositions) || cyclopsEyePositions[frame].get() == Common::Point(0, 0))
|
||||
return false;
|
||||
return cyclopsEyePositions[frame].get().sqrDist(p) <= getSquareOfPrecision();
|
||||
}
|
||||
|
||||
bool Cyclops::cyclopsIsHitBA0(Common::Point p, int frame) {
|
||||
if (frame < 0 || frame >= ARRAYSIZE(cyclopsEyePositionsBA0) || cyclopsEyePositionsBA0[frame].get() == Common::Point(0, 0))
|
||||
return false;
|
||||
return cyclopsEyePositionsBA0[frame].get().sqrDist(p) <= getSquareOfPrecision();
|
||||
}
|
||||
|
||||
unsigned Cyclops::getSquareOfPrecision() {
|
||||
return 2050 - 50 * _battleground->_level;
|
||||
}
|
||||
|
||||
void Cyclops::cyclopsState0() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_currentCyclopsState = 0;
|
||||
Common::Point mousePos = g_vm->getMousePos();
|
||||
_cyclopsIsHiding = (g_vm->getHeroBelt()->getSelectedStrength() == kPowerStealth || !cyclopsIsHit(mousePos, 0));
|
||||
room->playAnim(kCyclopsShootingEyeClosed, kCyclopsZ, PlayAnimParams::disappear().partial(0, 11), kCyclopsShootingEyeClosedMidAnim);
|
||||
}
|
||||
|
||||
void Cyclops::cyclopsState1() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_currentCyclopsState = 1;
|
||||
|
||||
room->playAnimWithSFX("v7040ba0", "v7040ea0",
|
||||
kCyclopsZ,
|
||||
PlayAnimParams::disappear(),
|
||||
15257);
|
||||
}
|
||||
|
||||
void Cyclops::cyclopsState2() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
room->playAnimWithSFX(
|
||||
"v7180be0", "v7180ee0", kCyclopsZ,
|
||||
PlayAnimParams::disappear()
|
||||
.partial(0, 4), 15258);
|
||||
_currentCyclopsState = 2;
|
||||
}
|
||||
|
||||
void Cyclops::cyclopsState3() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_currentCyclopsState = 3;
|
||||
room->playAnim("v7180be0", kCyclopsZ, PlayAnimParams::disappear().partial(5, 11), 15259);
|
||||
}
|
||||
|
||||
void Cyclops::cyclopsState4() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_currentCyclopsState = 4;
|
||||
room->playAnimWithSFX("v7180bk0", "v7180sc0", kCyclopsZ, PlayAnimParams::disappear(), 15260);
|
||||
}
|
||||
|
||||
void Cyclops::cyclopsState5() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_currentCyclopsState = 5;
|
||||
room->playAnimWithSFX("v7180bi0", "v7180sa0", kCyclopsZ, PlayAnimParams::disappear().partial(0, 4), 15262);
|
||||
}
|
||||
|
||||
void Cyclops::cyclopsState6() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_currentCyclopsState = 6;
|
||||
|
||||
room->playAnimWithSFX("v7180bi0", "v7180ee0", kCyclopsZ, PlayAnimParams::disappear().partial(5, 11), 15264);
|
||||
}
|
||||
|
||||
Cyclops::Cyclops(Common::SharedPtr<Battleground> battleground) {
|
||||
_battleground = battleground;
|
||||
}
|
||||
|
||||
void Cyclops::enterCyclops(int level) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->playAnimKeepLastFrame("v7180oa0", 600);
|
||||
room->playAnimWithSFX("v7180ba0", "v7180ea0", kCyclopsZ,
|
||||
PlayAnimParams::disappear(),
|
||||
15252);
|
||||
Typhoon::disableHotzones();
|
||||
_cyclopsProximityCheckCountdown = 0;
|
||||
_currentCyclopsState = 0;
|
||||
_cyclopsIsHiding = true;
|
||||
_battleground->_level = level;
|
||||
_battleground->_leavesRemaining = 9;
|
||||
_battleground->_monsterNum = kCyclops;
|
||||
g_vm->getHeroBelt()->setBranchOfLifeFrame(0);
|
||||
}
|
||||
|
||||
void Cyclops::handleEvent(int eventId) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
switch (eventId) {
|
||||
case 15252:
|
||||
room->enableMouse();
|
||||
room->playAnimLoop("v7180bl0", 550);
|
||||
_battleground->_isInFight = true;
|
||||
g_vm->addTimer(15266, 100, -1);
|
||||
switch (g_vm->getRnd().getRandomNumberRng(0, 2)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState1();
|
||||
break;
|
||||
case 2:
|
||||
cyclopsState2();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15255: {
|
||||
if (_cyclopsIsHiding) {
|
||||
switch (g_vm->getRnd().getRandomNumberRng(0, 4)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState1();
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
cyclopsState2();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (g_vm->getRnd().getRandomNumberRng(0, 4)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState1();
|
||||
break;
|
||||
case 2:
|
||||
cyclopsState3();
|
||||
break;
|
||||
case 3:
|
||||
cyclopsState4();
|
||||
break;
|
||||
case 4:
|
||||
cyclopsState5();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 15257:
|
||||
switch (g_vm->getRnd().getRandomNumberRng(0, 2)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState1();
|
||||
break;
|
||||
case 2:
|
||||
cyclopsState2();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15258:
|
||||
switch (g_vm->getRnd().getRandomNumberRng(0, 3)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState3();
|
||||
break;
|
||||
case 2:
|
||||
cyclopsState4();
|
||||
break;
|
||||
case 3:
|
||||
cyclopsState5();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15259:
|
||||
switch(g_vm->getRnd().getRandomNumberRng(0, 2)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState1();
|
||||
break;
|
||||
case 2:
|
||||
cyclopsState2();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15260:
|
||||
switch (g_vm->getRnd().getRandomNumberRng(0, 3)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState3();
|
||||
break;
|
||||
case 2:
|
||||
cyclopsState4();
|
||||
break;
|
||||
case 3:
|
||||
cyclopsState5();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15262:
|
||||
switch(g_vm->getRnd().getRandomNumberRng(0, 2)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState3();
|
||||
break;
|
||||
case 2:
|
||||
cyclopsState6();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15264:
|
||||
switch(g_vm->getRnd().getRandomNumberRng(0, 3)) {
|
||||
case 0:
|
||||
cyclopsState0();
|
||||
break;
|
||||
case 1:
|
||||
cyclopsState3();
|
||||
break;
|
||||
case 2:
|
||||
cyclopsState4();
|
||||
break;
|
||||
case 3:
|
||||
cyclopsState5();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15266:
|
||||
if (!_battleground->_isInFight || _battleground->_monsterNum != kCyclops)
|
||||
break;
|
||||
if (_currentCyclopsState == 0) {
|
||||
Common::Point mousePos = g_vm->getMousePos();
|
||||
if (_cyclopsProximityCheckCountdown)
|
||||
_cyclopsProximityCheckCountdown--;
|
||||
if (_cyclopsProximityCheckCountdown == 0) {
|
||||
if (_cyclopsIsHiding) {
|
||||
int frame = room->getAnimFrameNum("v7180bh0");
|
||||
if (g_vm->getHeroBelt()->getSelectedStrength() != kPowerStealth
|
||||
&& cyclopsIsHit(mousePos, frame)) {
|
||||
room->stopAnim("v7180bh0");
|
||||
room->playAnim("v7180bh1", kCyclopsZ, PlayAnimParams::disappear().partial(frame, -1), 15255);
|
||||
_cyclopsProximityCheckCountdown = 5;
|
||||
_cyclopsIsHiding = false;
|
||||
}
|
||||
} else {
|
||||
int frame = room->getAnimFrameNum("v7180bh1");
|
||||
if (frame <= 20 && frame >= 0 && !cyclopsIsHit(mousePos, frame) ) {
|
||||
room->stopAnim("v7180bh1");
|
||||
room->playAnim("v7180bh0", kCyclopsZ, PlayAnimParams::disappear().partial(frame, -1), 15255);
|
||||
_cyclopsIsHiding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_currentCyclopsState == 1) {
|
||||
Common::Point mousePos = g_vm->getMousePos();
|
||||
if (g_vm->getHeroBelt()->getSelectedStrength() != kPowerStealth
|
||||
&& cyclopsIsHitBA0(mousePos, room->getAnimFrameNum("v7040ba0"))) {
|
||||
room->stopAnim("v7040ba0");
|
||||
_currentCyclopsState = 2;
|
||||
room->playAnim(
|
||||
"v7180be0", kCyclopsZ,
|
||||
PlayAnimParams::disappear()
|
||||
.partial(0, 4), 15258);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 15269:
|
||||
for (int i = 0; i < _battleground->getNumOfProjectiles(); i++) {
|
||||
_battleground->launchProjectile(50, Common::Point(60, 203), 0);
|
||||
}
|
||||
room->playSFX("v7140eb0");
|
||||
break;
|
||||
case kCyclopsShootingEyeClosedMidAnim:
|
||||
room->playAnim(kCyclopsShootingEyeClosed, kCyclopsZ, PlayAnimParams::disappear().partial(12, -1), 15255);
|
||||
handleEvent(15269);
|
||||
break;
|
||||
case kCyclopsShootingEyeOpenMidAnim:
|
||||
room->playAnim(kCyclopsShootingEyeOpen, kCyclopsZ, PlayAnimParams::disappear().partial(12, -1), 15255);
|
||||
handleEvent(15269);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
395
engines/hadesch/rooms/monster/illusion.cpp
Normal file
395
engines/hadesch/rooms/monster/illusion.cpp
Normal file
@@ -0,0 +1,395 @@
|
||||
/* 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/rooms/monster.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
struct BirdInfo {
|
||||
int _projectileFrame;
|
||||
const char *_flyAnim;
|
||||
int _birdWidth;
|
||||
int _birdHeight;
|
||||
const char *_interceptAnim;
|
||||
const char *_shootAnim;
|
||||
int _birdShootWidth;
|
||||
int _birdShootHeight;
|
||||
|
||||
Common::Point getBirdSize() const {
|
||||
return Common::Point(_birdWidth, _birdHeight);
|
||||
}
|
||||
|
||||
Common::Point getBirdShootSize() const {
|
||||
return Common::Point(_birdShootWidth, _birdShootHeight);
|
||||
}
|
||||
};
|
||||
|
||||
static const TranscribedSound fakePhilReplics[] = {
|
||||
{"v7220xb0", _hs("unclear utterance")}, // unclear
|
||||
{"v7220xc0", _hs("Hey, this was close, buddy")},
|
||||
{"v7220xd0", _hs("Get hold of thunderbolts")}, // unclear
|
||||
{"v7220xe0", _hs("Keep going, kid. You're doing great job")},
|
||||
{"v7220xf0", _hs("unclear utterance")} // unclear
|
||||
};
|
||||
|
||||
static const BirdInfo birdInfo[] = {
|
||||
{
|
||||
10,
|
||||
"v7220bh2",
|
||||
151, 111,
|
||||
"v7220bp2",
|
||||
"v7220bl2",
|
||||
154, 192,
|
||||
},
|
||||
{
|
||||
6,
|
||||
"v7220bi2",
|
||||
167, 175,
|
||||
"v7220bq2",
|
||||
"v7220bm2",
|
||||
190, 233,
|
||||
},
|
||||
{
|
||||
10,
|
||||
"v7220bh3",
|
||||
151, 111,
|
||||
"v7220bp3",
|
||||
"v7220bl3",
|
||||
154, 192,
|
||||
},
|
||||
{
|
||||
6,
|
||||
"v7220bi3",
|
||||
167, 175,
|
||||
"v7220bq3",
|
||||
"v7220bm3",
|
||||
190, 233,
|
||||
},
|
||||
{
|
||||
10,
|
||||
"v7220bh0",
|
||||
141, 109,
|
||||
"v7220bp0",
|
||||
"v7220bl0",
|
||||
141, 192,
|
||||
},
|
||||
{
|
||||
6,
|
||||
"v7220bi0",
|
||||
110, 172,
|
||||
"v7220bq0",
|
||||
"v7220bm0",
|
||||
94, 233,
|
||||
},
|
||||
{
|
||||
10,
|
||||
"v7220bh1",
|
||||
141, 109,
|
||||
"v7220bp1",
|
||||
"v7220bl1",
|
||||
141, 192,
|
||||
},
|
||||
{
|
||||
6,
|
||||
"v7220bi1",
|
||||
110, 172,
|
||||
"v7220bq1",
|
||||
"v7220bm1",
|
||||
94, 233,
|
||||
}
|
||||
};
|
||||
|
||||
void Illusion::stopAnims() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(birdInfo); i++)
|
||||
for (unsigned j = 0; j < 3; j++) {
|
||||
room->stopAnim(LayerId(birdInfo[i]._flyAnim, j, "bird"));
|
||||
room->stopAnim(LayerId(birdInfo[i]._interceptAnim, j, "bird"));
|
||||
room->stopAnim(LayerId(birdInfo[i]._shootAnim, j, "bird"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Bird::Bird(int id) {
|
||||
_id = id;
|
||||
_isActive = false;
|
||||
}
|
||||
|
||||
void Bird::launch(int level) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_isActive = true;
|
||||
_level = level;
|
||||
makeFlightParams();
|
||||
room->playSFX("v7220eb0");
|
||||
_flightStart = g_vm->getCurrentTime();
|
||||
}
|
||||
|
||||
void Bird::stop() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->stopAnim(LayerId(birdInfo[_birdType]._flyAnim, _id, "bird"));
|
||||
}
|
||||
|
||||
FlightPosition Bird::getFlightPosition(double t) {
|
||||
double t2 = t * t;
|
||||
double t3 = t2 * t;
|
||||
struct FlightPosition fp;
|
||||
|
||||
// Pseudo-Bezier
|
||||
fp.centerPos = ((2 * t3 - 3 * t2 + 1.0) * _startPos
|
||||
+ (t3 - 2 * t2 + t) * _attractor1
|
||||
+ (t3 - t2) * _attractor2
|
||||
+ (-2 * t3 + 3 * t2) * _targetPos);
|
||||
fp.scale = 100 * t;
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
void Bird::makeFlightParams() {
|
||||
Common::RandomSource &rnd = g_vm->getRnd();
|
||||
_startPos = Common::Point(
|
||||
rnd.getRandomNumberRng(250, 350),
|
||||
rnd.getRandomNumberRng(160, 310));
|
||||
|
||||
if (rnd.getRandomBit()) {
|
||||
_targetPos = Common::Point(
|
||||
650, rnd.getRandomNumberRng(100, 300));
|
||||
_field84 = 1;
|
||||
_birdType = rnd.getRandomNumberRng(0, 3);
|
||||
} else {
|
||||
_targetPos = Common::Point(-50, rnd.getRandomNumberRng(100, 300));
|
||||
_field84 = -1;
|
||||
_birdType = 4 + rnd.getRandomNumberRng(0, 3);
|
||||
}
|
||||
|
||||
int _flightLengthFrames;
|
||||
if (_level <= 19) {
|
||||
_flightLengthFrames = 51 - _level;
|
||||
} else {
|
||||
_flightLengthFrames = 50 - _level;
|
||||
}
|
||||
|
||||
_flightLengthMs = _flightLengthFrames * 100;
|
||||
|
||||
_attractor1 = Common::Point(
|
||||
rnd.getRandomNumberRngSigned(-600, 600),
|
||||
rnd.getRandomNumberRngSigned(-600, 600));
|
||||
_attractor2 = Common::Point(
|
||||
rnd.getRandomNumberRngSigned(-600, 600),
|
||||
rnd.getRandomNumberRngSigned(-600, 600));
|
||||
|
||||
unsigned lastGoodShootFrame = 11;
|
||||
for (; (int) lastGoodShootFrame < _flightLengthFrames; lastGoodShootFrame++) {
|
||||
Common::Point p = getFlightPosition(lastGoodShootFrame / (double) _flightLengthFrames).centerPos;
|
||||
if (p.x < 50 || p.x > 550 || p.y < 50 || p.y > 350)
|
||||
break;
|
||||
}
|
||||
lastGoodShootFrame--;
|
||||
_flightShootAnimFrame = rnd.getRandomNumberRng(10, lastGoodShootFrame);
|
||||
_flightShootProjectileFrame = _flightShootAnimFrame + birdInfo[_birdType]._projectileFrame;
|
||||
_flightShootEndFrame = _flightShootAnimFrame + (birdInfo[_birdType]._projectileFrame == 6 ? 13 : 18);
|
||||
_hasShot = false;
|
||||
}
|
||||
|
||||
// 15201
|
||||
void Bird::tick(Common::SharedPtr<Bird> backRef, Common::SharedPtr<Battleground> battleground) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (!_isActive)
|
||||
return;
|
||||
LayerId flyLayer = LayerId(birdInfo[_birdType]._flyAnim, _id, "bird");
|
||||
LayerId shootLayer = LayerId(birdInfo[_birdType]._shootAnim, _id, "bird");
|
||||
_flightCounterMs = (g_vm->getCurrentTime() - _flightStart);
|
||||
if (_flightCounterMs < _flightLengthMs) {
|
||||
int frame = _flightCounterMs / 100;
|
||||
FlightPosition fp = getFlightPosition(_flightCounterMs / (double) _flightLengthMs);
|
||||
int scale = fp.scale;
|
||||
if (frame < _flightShootAnimFrame) {
|
||||
Common::Point cornerPos = fp.centerPos - (scale / 100.0)
|
||||
* birdInfo[_birdType].getBirdSize();
|
||||
room->selectFrame(flyLayer, 500, frame % 5, cornerPos);
|
||||
room->setScale(flyLayer, scale);
|
||||
room->stopAnim(shootLayer);
|
||||
} else if (frame < _flightShootEndFrame) {
|
||||
Common::Point cornerPos = fp.centerPos - (scale / 100.0)
|
||||
* birdInfo[_birdType].getBirdShootSize();
|
||||
room->selectFrame(shootLayer, 500, frame - _flightShootAnimFrame, cornerPos);
|
||||
room->setScale(shootLayer, scale);
|
||||
room->stopAnim(flyLayer);
|
||||
} else { // 15204
|
||||
Common::Point cornerPos = fp.centerPos - (scale / 100.0)
|
||||
* birdInfo[_birdType].getBirdSize();
|
||||
room->selectFrame(flyLayer, 500, (frame - _flightShootEndFrame) % 5, cornerPos);
|
||||
room->setScale(flyLayer, scale);
|
||||
room->stopAnim(shootLayer);
|
||||
}
|
||||
|
||||
if (frame >= _flightShootProjectileFrame && !_hasShot) {
|
||||
_hasShot = true;
|
||||
battleground->launchProjectile(scale / 2, fp.centerPos, _targetPos.x < _startPos.x ? -1 : +1);
|
||||
}
|
||||
} else {
|
||||
room->stopAnim(flyLayer);
|
||||
room->stopAnim(shootLayer);
|
||||
_isActive = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Bird::handleAbsoluteClick(Common::Point p) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (!_isActive || _flightCounterMs >= _flightLengthMs)
|
||||
return;
|
||||
FlightPosition fp = getFlightPosition(_flightCounterMs / (double) _flightLengthMs);
|
||||
int r = fp.scale * 40 / 100;
|
||||
if ((int) p.sqrDist(fp.centerPos) > r * r)
|
||||
return;
|
||||
room->stopAnim(LayerId(birdInfo[_birdType]._flyAnim, _id, "bird"));
|
||||
room->stopAnim(LayerId(birdInfo[_birdType]._shootAnim, _id, "bird"));
|
||||
_isActive = false;
|
||||
LayerId l = LayerId(birdInfo[_birdType]._interceptAnim, _id, "bird");
|
||||
room->playAnimWithSFX(l, "v7220ec0", 500, PlayAnimParams::disappear(),
|
||||
EventHandlerWrapper(),
|
||||
fp.centerPos - birdInfo[_birdType].getBirdSize()
|
||||
* (fp.scale / 100.0));
|
||||
}
|
||||
|
||||
Illusion::Illusion(Common::SharedPtr<Battleground> battleground) {
|
||||
_battleground = battleground;
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
_birds[i] = Common::SharedPtr<Bird>(new Bird(i));
|
||||
}
|
||||
|
||||
void Illusion::launchBird() {
|
||||
for (unsigned i = 0; i < 3; i++) {
|
||||
if (!_birds[i]->_isActive) {
|
||||
_birds[i]->launch(_battleground->_level);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Illusion::handleAbsoluteClick(Common::Point p) {
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_birds); i++)
|
||||
_birds[i]->handleAbsoluteClick(p);
|
||||
}
|
||||
|
||||
void Illusion::movePhil() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (_illusionIsKilled || !_battleground->_isInFight)
|
||||
return;
|
||||
room->disableHotzone(Common::String::format("Phil%d", _philPosition));
|
||||
room->stopAnim(Common::String::format("v7220bt%d", _philPosition));
|
||||
_philPosition = g_vm->getRnd().getRandomNumberRng(0, 5);
|
||||
room->enableHotzone(Common::String::format("Phil%d", _philPosition));
|
||||
room->playAnim(Common::String::format("v7220bt%d", _philPosition), 600,
|
||||
PlayAnimParams::keepLastFrame().partial(0, 12), 15301);
|
||||
}
|
||||
|
||||
void Illusion::enterIllusion(int level) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Typhoon::disableHotzones();
|
||||
for (unsigned i = 0; i < 6; i++)
|
||||
room->enableHotzone(Common::String::format("Phil%d", i));
|
||||
room->playAnimWithSpeech(Common::String::format("v7220bg%d", g_vm->getRnd().getRandomNumberRng(0, 5)),
|
||||
TranscribedSound::make("v7220xc1",
|
||||
"It's me, Phil. These beasts are all that stands between me and freedom"), 600, // unclear
|
||||
PlayAnimParams::disappear(),
|
||||
15306);
|
||||
_battleground->_level = level;
|
||||
_battleground->_leavesRemaining = 9;
|
||||
_battleground->_monsterNum = kIllusion;
|
||||
_philPosition = -1;
|
||||
_illusionIsKilled = false;
|
||||
g_vm->getHeroBelt()->setBranchOfLifeFrame(0);
|
||||
}
|
||||
|
||||
void Illusion::handleClick(const Common::String &name) {
|
||||
if (_battleground->_isInFight && _battleground->_monsterNum == kIllusion && g_vm->getHeroBelt()->getSelectedStrength() == kPowerWisdom && !_illusionIsKilled
|
||||
&& _philPosition >= 0 && name == Common::String::format("Phil%d", _philPosition)) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_illusionIsKilled = true;
|
||||
_battleground->stopFight();
|
||||
room->disableMouse();
|
||||
room->playAnimKeepLastFrame(Common::String::format("v7220bv%d", _philPosition), 600);
|
||||
room->playSFX("v7220eg0", 15307);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Illusion::handleEvent(int eventId) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
switch (eventId) {
|
||||
case 15301:
|
||||
g_vm->addTimer(15302, 3500 - (_battleground->_level - 1) * 100);
|
||||
break;
|
||||
case 15302:
|
||||
movePhil();
|
||||
break;
|
||||
case 15306:
|
||||
room->enableMouse();
|
||||
_battleground->_isInFight = true;
|
||||
movePhil();
|
||||
g_vm->addTimer(15312, g_vm->getRnd().getRandomNumberRng(2000, 4000));
|
||||
room->playAnimKeepLastFrame("v7220oa0", 600);
|
||||
g_vm->addTimer(15313, g_vm->getRnd().getRandomNumberRng(500, 5000));
|
||||
launchBird();
|
||||
break;
|
||||
case 15307:
|
||||
room->playSpeech(TranscribedSound::make("v7220wg0", "Oh no, we're gonna fry"), 15308);
|
||||
break;
|
||||
case 15308:
|
||||
room->playSpeech(TranscribedSound::make("v7220wh0", "Let's get outta here"), 15309);
|
||||
break;
|
||||
case 15309:
|
||||
g_vm->getCurrentHandler()->handleEvent(15383);
|
||||
break;
|
||||
case 15312:
|
||||
if (!_battleground->_isInFight || _illusionIsKilled || _battleground->_monsterNum != kIllusion)
|
||||
return;
|
||||
room->playSpeech(fakePhilReplics[g_vm->getRnd().getRandomNumberRng(0, ARRAYSIZE(fakePhilReplics) - 1)]);
|
||||
g_vm->addTimer(15312, g_vm->getRnd().getRandomNumberRng(6000, 10000));
|
||||
break;
|
||||
case 15313:
|
||||
if (!_battleground->_isInFight || _illusionIsKilled || _battleground->_monsterNum != kIllusion)
|
||||
return;
|
||||
g_vm->addTimer(15313, g_vm->getRnd().getRandomNumberRng(500, 5000));
|
||||
launchBird();
|
||||
break;
|
||||
// TODO: 15300, 15304, 15305, 15311
|
||||
}
|
||||
}
|
||||
|
||||
void Illusion::tick() {
|
||||
if (!_battleground->_isInFight) {
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
_birds[i]->_isActive = false;
|
||||
return;
|
||||
}
|
||||
for (unsigned i = 0; i < 3; i++) {
|
||||
_birds[i]->tick(_birds[i], _battleground);
|
||||
}
|
||||
}
|
||||
}
|
||||
303
engines/hadesch/rooms/monster/projectile.cpp
Normal file
303
engines/hadesch/rooms/monster/projectile.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/* 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/rooms/monster.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
Projectile::Projectile(int id, int level, Monster monster, int startScale, Common::Point startPoint, int xmomentum) {
|
||||
_level = level;
|
||||
switch (monster) {
|
||||
case kCyclops:
|
||||
_flyAnim = "V7140BA0";
|
||||
_interceptAnim = "V7130BD0";
|
||||
_hitAnim = "V7140BD0";
|
||||
break;
|
||||
case kTyphoon:
|
||||
_flyAnim = "V7140BB0";
|
||||
_interceptAnim = "V7130BD1";
|
||||
_hitAnim = "V7140BE0";
|
||||
break;
|
||||
case kIllusion:
|
||||
_flyAnim = "V7140BC0";
|
||||
_interceptAnim = "V7130BD2";
|
||||
_hitAnim = "V7140BF0";
|
||||
break;
|
||||
}
|
||||
_isMiss = g_vm->getRnd().getRandomNumberRng(0, getProjectileHitChance()) == 0;
|
||||
_isFlightFinished = false;
|
||||
_flightCounterMs = -1;
|
||||
_projectileId = id;
|
||||
_pending = 0;
|
||||
|
||||
_flightStart = g_vm->getCurrentTime();
|
||||
_startScale = startScale;
|
||||
_start = startPoint;
|
||||
makeFlightParams(xmomentum);
|
||||
}
|
||||
|
||||
void Projectile::handleEvent(int ev) {
|
||||
switch (ev) {
|
||||
case 15053:
|
||||
g_vm->handleEvent(kHitReceived);
|
||||
// TODO: stop red
|
||||
_pending--;
|
||||
break;
|
||||
case 15054:
|
||||
_pending--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Projectile::stop() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->stopAnim(LayerId(_flyAnim, _projectileId, "projectile"));
|
||||
room->stopAnim(LayerId(_hitAnim, _projectileId, "projectile"));
|
||||
room->stopAnim(LayerId(_interceptAnim, _projectileId, "projectile"));
|
||||
}
|
||||
|
||||
void Projectile::makeFlightParams(int xmomentum) {
|
||||
Common::RandomSource &rnd = g_vm->getRnd();
|
||||
_flightLengthMs = getProjectileFlightLength(_level) * 100;
|
||||
|
||||
if (_isMiss) {
|
||||
switch (rnd.getRandomNumberRng(0, 2)) {
|
||||
case 0:
|
||||
_target = Common::Point(
|
||||
-50, rnd.getRandomNumberRngSigned(-50, 400));
|
||||
break;
|
||||
case 1:
|
||||
_target = Common::Point(
|
||||
rnd.getRandomNumberRngSigned(-50, 650), -50);
|
||||
break;
|
||||
case 2:
|
||||
_target = Common::Point(
|
||||
650, rnd.getRandomNumberRngSigned(-50, 400));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_target = Common::Point(
|
||||
rnd.getRandomNumberRng(100, 500),
|
||||
rnd.getRandomNumberRng(100, 300));
|
||||
}
|
||||
|
||||
switch (xmomentum) {
|
||||
case 1:
|
||||
_attractor1 = Common::Point(
|
||||
rnd.getRandomNumberRng(0, 600),
|
||||
rnd.getRandomNumberRng(0, 300));
|
||||
break;
|
||||
case -1:
|
||||
_attractor1 = Common::Point(
|
||||
rnd.getRandomNumberRngSigned(-600, 0),
|
||||
rnd.getRandomNumberRng(0, 300));
|
||||
break;
|
||||
case 0:
|
||||
_attractor1 = Common::Point(
|
||||
rnd.getRandomNumberRngSigned(-600, 600),
|
||||
rnd.getRandomNumberRngSigned(-600, 600));
|
||||
break;
|
||||
}
|
||||
_attractor2 = Common::Point(
|
||||
rnd.getRandomNumberRngSigned(-600, 600),
|
||||
rnd.getRandomNumberRng(0, 600));
|
||||
}
|
||||
|
||||
FlightPosition Projectile::getFlightPosition(double t) {
|
||||
double t2 = t * t;
|
||||
double t3 = t2 * t;
|
||||
struct FlightPosition fp;
|
||||
|
||||
// Pseudo-Bezier
|
||||
fp.centerPos = ((2 * t3 - 3 * t2 + 1.0) * _start
|
||||
+ (t3 - 2 * t2 + t) * _attractor1
|
||||
+ (t3 - t2) * _attractor2
|
||||
+ (-2 * t3 + 3 * t2) * _target);
|
||||
fp.scale = _startScale + (120 - _startScale) * t;
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
Projectile::~Projectile() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->purgeAnim(LayerId(_flyAnim, _projectileId, "projectile"));
|
||||
room->purgeAnim(LayerId(_hitAnim, _projectileId, "projectile"));
|
||||
room->purgeAnim(LayerId(_interceptAnim, _projectileId, "projectile"));
|
||||
}
|
||||
|
||||
int Projectile::getProjectileFlightLength(int level) {
|
||||
return 41 - _level;
|
||||
}
|
||||
|
||||
int Projectile::getProjectileHitChance() {
|
||||
if (_level >= 26)
|
||||
return 6;
|
||||
if (_level >= 17)
|
||||
return 5;
|
||||
if (_level >= 12)
|
||||
return 4;
|
||||
if (_level >= 7)
|
||||
return 3;
|
||||
return 2;
|
||||
}
|
||||
|
||||
Battleground::Battleground() {
|
||||
_level = 1;
|
||||
_projectileId = 0;
|
||||
_isInFight = false;
|
||||
}
|
||||
|
||||
int Battleground::getNumOfProjectiles() {
|
||||
return (_level - 1) / 10 + 1;
|
||||
}
|
||||
|
||||
void Battleground::launchProjectile(int startScale, Common::Point startPoint, int xmomentum) {
|
||||
++_projectileId;
|
||||
Common::SharedPtr<Projectile> pj(new Projectile(_projectileId, _level, _monsterNum, startScale, startPoint, xmomentum));
|
||||
_projectiles.push_back(pj);
|
||||
pj->tick(pj);
|
||||
}
|
||||
|
||||
void Battleground::handleAbsoluteClick(Common::Point p) {
|
||||
for (auto &projectile : _projectiles) {
|
||||
projectile.operator->()->handleAbsoluteClick(projectile, p);
|
||||
}
|
||||
}
|
||||
|
||||
void Battleground::tick() {
|
||||
if (!_isInFight)
|
||||
_projectiles.clear();
|
||||
else
|
||||
for (Common::Array<Common::SharedPtr<Projectile> >::iterator it = _projectiles.begin(); it != _projectiles.end();) {
|
||||
if (it->operator->()->tick(*it)) {
|
||||
it++;
|
||||
} else {
|
||||
it = _projectiles.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Battleground::stopFight() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_isInFight = false;
|
||||
room->stopAnim("v7040ba0");
|
||||
room->stopAnim("V7100BJ0");
|
||||
room->stopAnim("v7180ba0");
|
||||
room->stopAnim("V7180BB0");
|
||||
room->stopAnim("v7180be0");
|
||||
room->stopAnim("v7180bh0");
|
||||
room->stopAnim("v7180bh1");
|
||||
room->stopAnim("v7180bi0");
|
||||
room->stopAnim("v7180bk0");
|
||||
room->stopAnim("v7180bl0");
|
||||
room->stopAnim("v7180oa0");
|
||||
room->stopAnim("v7210bx0");
|
||||
|
||||
stopProjectiles();
|
||||
|
||||
Typhoon::stopAnims();
|
||||
Illusion::stopAnims();
|
||||
|
||||
for (unsigned i = 0; i < 6; i++) {
|
||||
room->stopAnim(Common::String::format("v7220bt%d", i));
|
||||
room->stopAnim(Common::String::format("v7220bg%d", i));
|
||||
}
|
||||
|
||||
room->dumpLayers();
|
||||
}
|
||||
|
||||
void Battleground::stopProjectiles() {
|
||||
for (auto &projectile : _projectiles)
|
||||
projectile.operator->()->stop();
|
||||
}
|
||||
|
||||
class HandlerProjectile : public EventHandler {
|
||||
public:
|
||||
void operator()() override {
|
||||
_projectile->handleEvent(_event);
|
||||
}
|
||||
|
||||
HandlerProjectile(Common::SharedPtr <Projectile> projectile,
|
||||
int event) {
|
||||
_projectile = projectile;
|
||||
_event = event;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::SharedPtr <Projectile> _projectile;
|
||||
int _event;
|
||||
};
|
||||
|
||||
bool Projectile::tick(Common::SharedPtr <Projectile> backRef) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (_isFlightFinished)
|
||||
return _pending > 0;
|
||||
LayerId flyLayer = LayerId(_flyAnim, _projectileId, "projectile");
|
||||
_flightCounterMs = (g_vm->getCurrentTime() - _flightStart);
|
||||
if (_flightCounterMs < _flightLengthMs) {
|
||||
FlightPosition fp = getFlightPosition(_flightCounterMs / (double) _flightLengthMs);
|
||||
int scale = fp.scale;
|
||||
Common::Point cornerPos = fp.centerPos - (scale / 100.0)
|
||||
* Common::Point(186, 210);
|
||||
room->selectFrame(flyLayer, 400, (_flightCounterMs / 100) % 8, cornerPos);
|
||||
room->setScale(flyLayer, scale);
|
||||
} else {
|
||||
room->stopAnim(flyLayer);
|
||||
_isFlightFinished = true;
|
||||
if (_isMiss) {
|
||||
_pending = 0;
|
||||
} else {
|
||||
FlightPosition fp = getFlightPosition(_flightCounterMs / (double) _flightLengthMs);
|
||||
LayerId l = LayerId(_hitAnim, _projectileId, "projectile");
|
||||
room->playAnimWithSFX(l, "v7130ea0", 400, PlayAnimParams::disappear(),
|
||||
Common::SharedPtr<EventHandler>(new HandlerProjectile(backRef, 15053)),
|
||||
fp.centerPos - Common::Point(182, 205));
|
||||
_pending = 1;
|
||||
// TODO: fade to red, in 100 ms, callback 15055
|
||||
// TODO: shake camera for 1s
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Projectile::handleAbsoluteClick(Common::SharedPtr <Projectile> backRef, Common::Point p) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (_isFlightFinished || _flightCounterMs >= _flightLengthMs)
|
||||
return;
|
||||
FlightPosition fp = getFlightPosition(_flightCounterMs / (double) _flightLengthMs);
|
||||
int r = fp.scale * 40 / 100;
|
||||
if ((int) p.sqrDist(fp.centerPos) > r * r)
|
||||
return;
|
||||
room->stopAnim(LayerId(_flyAnim, _projectileId, "projectile"));
|
||||
_isFlightFinished = true;
|
||||
_pending = 1;
|
||||
LayerId l = LayerId(_interceptAnim, _projectileId, "projectile");
|
||||
room->playAnimWithSFX(l, "v7130eg0", 400, PlayAnimParams::disappear(),
|
||||
Common::SharedPtr<EventHandler>(new HandlerProjectile(backRef, 15054)),
|
||||
fp.centerPos - Common::Point(186, 210) * (fp.scale / 100.0));
|
||||
}
|
||||
|
||||
}
|
||||
356
engines/hadesch/rooms/monster/typhoon.cpp
Normal file
356
engines/hadesch/rooms/monster/typhoon.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
/* 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/rooms/monster.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
enum {
|
||||
kTyphoonZ = 500
|
||||
};
|
||||
|
||||
struct TyphoonHeadInfo {
|
||||
const char *_animDie;
|
||||
const char *_animRespawn;
|
||||
const char * _animNormal;
|
||||
const char * _hotZone;
|
||||
int _xVal;
|
||||
int _yVal;
|
||||
int _zVal;
|
||||
|
||||
Common::Point getPosition() const {
|
||||
return Common::Point(_xVal, _yVal);
|
||||
}
|
||||
};
|
||||
|
||||
static const TyphoonHeadInfo typhonHeadInfo[] = {
|
||||
{"V7210BO1", "V7210BS1", "V7210BC1", "head00c1", 275, 186, 480},
|
||||
{"V7210BO0", "V7210BS0", "V7210BC0", "head01c0", 320, 166, 481},
|
||||
{"V7210BO0", "V7210BS0", "V7210BC0", "head02c0", 313, 221, 482},
|
||||
{"V7210BO1", "V7210BS1", "V7210BC1", "head03c1", 279, 223, 483},
|
||||
{"V7210BP1", "V7210BT1", "V7210BD1", "head04d1", 237, 221, 484},
|
||||
{"V7210BP0", "V7210BT0", "V7210BD0", "head05d0", 234, 189, 485},
|
||||
{"V7210BP1", "V7210BT1", "V7210BD1", "head06d1", 234, 160, 486},
|
||||
{"V7210BP0", "V7210BT0", "V7210BD0", "head07d0", 289, 137, 487},
|
||||
{"V7210BO0", "V7210BS0", "V7210BC0", "head08c0", 253, 135, 488},
|
||||
{"V7210BP0", "V7210BT0", "V7210BD0", "head09d0", 355, 219, 489},
|
||||
{"V7210BP0", "V7210BT0", "V7210BD0", "head10d0", 368, 182, 490},
|
||||
{"V7210BP0", "V7210BT0", "V7210BD0", "head11d0", 351, 152, 491},
|
||||
{"V7210BP0", "V7210BT0", "V7210BD0", "head12d0", 329, 126, 492},
|
||||
{"V7210BO0", "V7210BS0", "V7210BC0", "head13c0", 289, 99, 493},
|
||||
{"V7210BP0", "V7210BT0", "V7210BD0", "head14d0", 333, 107, 494},
|
||||
{"V7210BO0", "V7210BS0", "V7210BC0", "head15c0", 360, 135, 495},
|
||||
{"V7210BO1", "V7210BS1", "V7210BC1", "head16c1", 226, 147, 496},
|
||||
{"V7210BP0", "V7210BT0", "V7210BD0", "head17d0", 257, 107, 497}
|
||||
};
|
||||
|
||||
Typhoon::Typhoon(Common::SharedPtr<Battleground> battleground) {
|
||||
_battleground = battleground;
|
||||
_playingTyphoonRespawnSound = false;
|
||||
_playingTyphoonDieSound = false;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_headIsAlive); i++)
|
||||
_headIsAlive[i] = false;
|
||||
}
|
||||
|
||||
void Typhoon::handleEvent(int eventId) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
switch (eventId) {
|
||||
case 15104:
|
||||
_playingTyphoonDieSound = false;
|
||||
break;
|
||||
case 15105:
|
||||
_playingTyphoonRespawnSound = false;
|
||||
break;
|
||||
case 15152:
|
||||
room->enableMouse();
|
||||
room->playAnimLoop("v7210bx0", 490);
|
||||
room->playSFX("v7210ed0");
|
||||
_battleground->_isInFight = true;
|
||||
typhoonA();
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_headIsAlive); i++) {
|
||||
showHeadNormal(i);
|
||||
_headIsAlive[i] = true;
|
||||
}
|
||||
schedule15154();
|
||||
handleEvent(15163);
|
||||
break;
|
||||
case 15153:
|
||||
typhoonA();
|
||||
break;
|
||||
case 15154:
|
||||
if (!_battleground->_isInFight || _isKilled || _battleground->_monsterNum != kTyphoon)
|
||||
return;
|
||||
room->playSFX("v7050ea0");
|
||||
schedule15154();
|
||||
break;
|
||||
case 15159:
|
||||
room->playAnim("v7210bj0", 500, PlayAnimParams::disappear().partial(7, -1), 15153);
|
||||
if (!_isKilled && _battleground->_isInFight) {
|
||||
for (int y = 351, i = 0; i < _battleground->getNumOfProjectiles(); y++, i++)
|
||||
_battleground->launchProjectile(80, Common::Point(
|
||||
220, g_vm->getRnd().getRandomNumberRng(351, y)), 0);
|
||||
}
|
||||
break;
|
||||
case 15160:
|
||||
room->playAnim("v7210bi0", 500, PlayAnimParams::disappear().partial(7, -1), 15153);
|
||||
if (!_isKilled && _battleground->_isInFight) {
|
||||
for (int y = 359, i = 0; i < _battleground->getNumOfProjectiles(); y++, i++)
|
||||
_battleground->launchProjectile(80, Common::Point(
|
||||
456, g_vm->getRnd().getRandomNumberRng(359, y)), 0);
|
||||
}
|
||||
break;
|
||||
case 15163:
|
||||
if (!_battleground->_isInFight || _isKilled || _battleground->_monsterNum != kTyphoon)
|
||||
return;
|
||||
room->playSFX(g_vm->getHeroBelt()->getSelectedStrength() == kPowerStrength
|
||||
? "v7210eb0" : "v7210ea0");
|
||||
g_vm->addTimer(15163, g_vm->getRnd().getRandomNumberRng(3000, 7000));
|
||||
break;
|
||||
/*
|
||||
TODO:
|
||||
15167
|
||||
*/
|
||||
case 15168:
|
||||
g_vm->getCurrentHandler()->handleEvent(15351);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Typhoon::enterTyphoon(int level) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->playAnimKeepLastFrame("v7210oa0", 600);
|
||||
room->playAnim("v7210ba0", kTyphoonZ,
|
||||
PlayAnimParams::disappear(),
|
||||
15152);
|
||||
room->playSFX("v7050eb0");
|
||||
for (unsigned i = 0; i < ARRAYSIZE(typhonHeadInfo); i++) {
|
||||
room->enableHotzone(typhonHeadInfo[i]._hotZone);
|
||||
room->setHotZoneOffset(typhonHeadInfo[i]._hotZone, typhonHeadInfo[i].getPosition());
|
||||
}
|
||||
for (unsigned i = 0; i < 6; i++)
|
||||
room->disableHotzone(Common::String::format("Phil%d", i));
|
||||
_battleground->_level = level;
|
||||
_battleground->_leavesRemaining = 9;
|
||||
_battleground->_monsterNum = kTyphoon;
|
||||
_isKilled = false;
|
||||
_playingTyphoonDieSound = false;
|
||||
g_vm->getHeroBelt()->setBranchOfLifeFrame(0);
|
||||
}
|
||||
|
||||
void Typhoon::handleClick(Common::SharedPtr<Typhoon> backRef,
|
||||
const Common::String &name) {
|
||||
if (_battleground->_isInFight && _battleground->_monsterNum == kTyphoon
|
||||
&& g_vm->getHeroBelt()->getSelectedStrength() == kPowerStrength && !_isKilled) {
|
||||
for (unsigned i = 0; i < ARRAYSIZE(typhonHeadInfo); i++)
|
||||
if (name == typhonHeadInfo[i]._hotZone) {
|
||||
hitTyphoonHead(backRef, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Typhoon::typhoonA() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (_isKilled)
|
||||
return;
|
||||
|
||||
if (g_vm->getRnd().getRandomNumberRng(0, 3)) {
|
||||
room->playAnim("v7050ba0", 500, PlayAnimParams::disappear(), 15153);
|
||||
} else if (g_vm->getRnd().getRandomBit()) {
|
||||
room->playAnim("v7210bi0", 500, PlayAnimParams::disappear().partial(0, 6), 15160);
|
||||
room->playSFX("v7140ec0");
|
||||
}
|
||||
else {
|
||||
room->playAnim("v7210bj0", 500, PlayAnimParams::disappear().partial(0, 6), 15159);
|
||||
room->playSFX("v7140ec0");
|
||||
}
|
||||
}
|
||||
|
||||
void Typhoon::schedule15154() {
|
||||
int ha = typhonGetNumAliveHeads() * 50;
|
||||
g_vm->addTimer(15154, g_vm->getRnd().getRandomNumberRng(1100-ha, 1200 - ha));
|
||||
}
|
||||
|
||||
int Typhoon::typhonGetNumAliveHeads() {
|
||||
int v = 0;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_headIsAlive); i++)
|
||||
v += !!_headIsAlive[i];
|
||||
return v;
|
||||
}
|
||||
|
||||
void Typhoon::hideHead(int idx) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->stopAnim(LayerId(typhonHeadInfo[idx]._animNormal, idx, "head"));
|
||||
room->stopAnim(LayerId(typhonHeadInfo[idx]._animDie, idx, "head"));
|
||||
room->stopAnim(LayerId(typhonHeadInfo[idx]._animRespawn, idx, "head"));
|
||||
}
|
||||
|
||||
void Typhoon::showHeadNormal(int idx) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
hideHead(idx);
|
||||
room->playAnimLoop(LayerId(typhonHeadInfo[idx]._animNormal, idx, "head"),
|
||||
typhonHeadInfo[idx]._zVal,
|
||||
typhonHeadInfo[idx].getPosition());
|
||||
}
|
||||
|
||||
// 15103
|
||||
class TyphoonHeadRespawnComplete : public EventHandler {
|
||||
public:
|
||||
void operator()() override {
|
||||
_typhoon->showHeadNormal(_idx);
|
||||
}
|
||||
|
||||
TyphoonHeadRespawnComplete(Common::SharedPtr<Typhoon> typhoon, int idx) {
|
||||
_idx = idx;
|
||||
_typhoon = typhoon;
|
||||
}
|
||||
private:
|
||||
int _idx;
|
||||
Common::SharedPtr<Typhoon> _typhoon;
|
||||
};
|
||||
|
||||
// 15102
|
||||
class TyphoonHeadRespawnEvent : public EventHandler {
|
||||
public:
|
||||
void operator()() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (_typhoon->_headIsAlive[_idx] || _typhoon->_isKilled)
|
||||
return;
|
||||
room->enableHotzone(typhonHeadInfo[_idx]._hotZone);
|
||||
_typhoon->_headIsAlive[_idx] = true;
|
||||
if (!_typhoon->_playingTyphoonRespawnSound) {
|
||||
_typhoon->_playingTyphoonRespawnSound = true;
|
||||
room->playSFX("v7050ed0", 15105);
|
||||
}
|
||||
_typhoon->hideHead(_idx);
|
||||
room->playAnim(LayerId(typhonHeadInfo[_idx]._animRespawn, _idx, "head"),
|
||||
typhonHeadInfo[_idx]._zVal,
|
||||
PlayAnimParams::disappear(),
|
||||
Common::SharedPtr<EventHandler>(new TyphoonHeadRespawnComplete(_typhoon, _idx)),
|
||||
typhonHeadInfo[_idx].getPosition());
|
||||
}
|
||||
|
||||
TyphoonHeadRespawnEvent(Common::SharedPtr<Typhoon> typhoon, int idx) {
|
||||
_idx = idx;
|
||||
_typhoon = typhoon;
|
||||
}
|
||||
private:
|
||||
int _idx;
|
||||
Common::SharedPtr<Typhoon> _typhoon;
|
||||
};
|
||||
|
||||
// 15101
|
||||
class TyphoonHeadDieAnimFinishedEvent : public EventHandler {
|
||||
public:
|
||||
void operator()() override {
|
||||
int minRespawnInterval = 10000;
|
||||
int maxRespawnInterval = 10000;
|
||||
|
||||
if (_level <= 21)
|
||||
minRespawnInterval = 15000 - 500 * (_level - 1);
|
||||
else if (_level == 22)
|
||||
minRespawnInterval = 4600;
|
||||
else if (_level <= 25)
|
||||
minRespawnInterval = 4200 - 200 * (_level - 23);
|
||||
else if (_level == 26)
|
||||
minRespawnInterval = 3700;
|
||||
else
|
||||
minRespawnInterval = 3600 - 200 * (_level - 27);
|
||||
|
||||
if (_level <= 21)
|
||||
maxRespawnInterval = 20000 - 500 * (_level - 1);
|
||||
else
|
||||
maxRespawnInterval = 9600 - 200 * (_level - 22);
|
||||
|
||||
g_vm->addTimer(Common::SharedPtr<EventHandler>(new TyphoonHeadRespawnEvent(_typhoon, _idx)),
|
||||
g_vm->getRnd().getRandomNumberRng(minRespawnInterval, maxRespawnInterval));
|
||||
}
|
||||
|
||||
TyphoonHeadDieAnimFinishedEvent(Common::SharedPtr<Typhoon> typhoon, int idx, int level) {
|
||||
_idx = idx;
|
||||
_level = level;
|
||||
_typhoon = typhoon;
|
||||
}
|
||||
private:
|
||||
int _idx;
|
||||
int _level;
|
||||
Common::SharedPtr<Typhoon> _typhoon;
|
||||
};
|
||||
|
||||
void Typhoon::hitTyphoonHead(Common::SharedPtr<Typhoon> backRef, int idx) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (!_headIsAlive[idx])
|
||||
return;
|
||||
|
||||
if (!_playingTyphoonDieSound) {
|
||||
room->playSFX("v7050ec0", 15104);
|
||||
_playingTyphoonDieSound = true;
|
||||
}
|
||||
_headIsAlive[idx] = false;
|
||||
hideHead(idx);
|
||||
room->playAnimKeepLastFrame(LayerId(typhonHeadInfo[idx]._animDie, idx, "head"),
|
||||
typhonHeadInfo[idx]._zVal,
|
||||
Common::SharedPtr<EventHandler>(new TyphoonHeadDieAnimFinishedEvent(backRef, idx, _battleground->_level)),
|
||||
typhonHeadInfo[idx].getPosition());
|
||||
room->disableHotzone(typhonHeadInfo[idx]._hotZone);
|
||||
bool isKilled = true;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_headIsAlive); i++) {
|
||||
if (_headIsAlive[i])
|
||||
isKilled = false;
|
||||
}
|
||||
|
||||
if (!isKilled)
|
||||
return;
|
||||
_isKilled = true;
|
||||
_battleground->stopFight();
|
||||
|
||||
room->disableMouse();
|
||||
room->playAnimWithSFX("v7210bw0", "v7050ee0", 500, PlayAnimParams::disappear(), 15168);
|
||||
}
|
||||
|
||||
void Typhoon::stopAnims() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(typhonHeadInfo); i++) {
|
||||
room->stopAnim(LayerId(typhonHeadInfo[i]._animNormal, i, "head"));
|
||||
room->stopAnim(LayerId(typhonHeadInfo[i]._animDie, i, "head"));
|
||||
room->stopAnim(LayerId(typhonHeadInfo[i]._animRespawn, i, "head"));
|
||||
room->stopAnim("v7050ba0");
|
||||
room->stopAnim("v7210bi0");
|
||||
room->stopAnim("v7140ec0");
|
||||
room->stopAnim("v7210bj0");
|
||||
room->stopAnim("v7140ec0");
|
||||
}
|
||||
}
|
||||
|
||||
void Typhoon::disableHotzones() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
for (unsigned i = 0; i < ARRAYSIZE(typhonHeadInfo); i++)
|
||||
room->disableHotzone(typhonHeadInfo[i]._hotZone);
|
||||
}
|
||||
|
||||
}
|
||||
130
engines/hadesch/rooms/olympus.cpp
Normal file
130
engines/hadesch/rooms/olympus.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/* 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"
|
||||
|
||||
namespace Hadesch {
|
||||
static const char *kNewButton = "newbutton";
|
||||
static const char *kRestoreButton = "restorebutton";
|
||||
static const char *kQuitButton = "quitbutton";
|
||||
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kWaterFallZ = 9000,
|
||||
kButtonZ = 2101,
|
||||
kLogoZ = 1101
|
||||
};
|
||||
|
||||
class OlympusHandler : public Handler {
|
||||
public:
|
||||
void handleClick(const Common::String &hotname) override {
|
||||
if (hotname == "new") {
|
||||
g_vm->newGame();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "restore") {
|
||||
g_vm->enterOptions();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "quit") {
|
||||
g_vm->quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
void handleMouseOver(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (name == "new") {
|
||||
room->selectFrame(kNewButton, kButtonZ, 6);
|
||||
return;
|
||||
}
|
||||
if (name == "restore") {
|
||||
room->selectFrame(kRestoreButton, kButtonZ, 6);
|
||||
return;
|
||||
}
|
||||
if (name == "quit") {
|
||||
room->selectFrame(kQuitButton, kButtonZ, 6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
void handleMouseOut(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (name == "new") {
|
||||
room->selectFrame(kNewButton, kButtonZ, 5);
|
||||
return;
|
||||
}
|
||||
if (name == "restore") {
|
||||
room->selectFrame(kRestoreButton, kButtonZ, 5);
|
||||
return;
|
||||
}
|
||||
if (name == "quit") {
|
||||
room->selectFrame(kQuitButton, kButtonZ, 5);
|
||||
return;
|
||||
}
|
||||
}
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
switch (eventId) {
|
||||
case 21001:
|
||||
room->playSFX("o1010ea0");
|
||||
break;
|
||||
case 21002:
|
||||
room->enableMouse();
|
||||
room->selectFrame("logo", kLogoZ, 0);
|
||||
room->playAnimLoop("waterfall1", kWaterFallZ);
|
||||
room->playAnimLoop("waterfall2", kWaterFallZ);
|
||||
room->playAnimLoop("waterfall3", kWaterFallZ);
|
||||
room->playAnimLoop("waterfall4", kWaterFallZ);
|
||||
room->playAnim(kNewButton, kButtonZ, PlayAnimParams::keepLastFrame().partial(0, 5));
|
||||
room->playAnim(kQuitButton, kButtonZ, PlayAnimParams::keepLastFrame().partial(0, 5));
|
||||
if (g_vm->hasAnySaves())
|
||||
room->playAnim(kRestoreButton, kButtonZ, PlayAnimParams::keepLastFrame().partial(0, 5));
|
||||
else
|
||||
room->disableHotzone("restore");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
~OlympusHandler() override {}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->loadHotZones("Olympus.HOT");
|
||||
room->addStaticLayer("background", kBackgroundZ);
|
||||
room->disableMouse();
|
||||
if (g_vm->getPreviousRoomId() == kOptionsRoom) {
|
||||
room->playSFX("o1010ea0", 21002);
|
||||
} else {
|
||||
room->playVideo("movie", 201, 21002);
|
||||
g_vm->addTimer(21001, 40000);
|
||||
}
|
||||
room->disableHeroBelt();
|
||||
}
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeOlympusHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new OlympusHandler());
|
||||
}
|
||||
|
||||
}
|
||||
736
engines/hadesch/rooms/options.cpp
Normal file
736
engines/hadesch/rooms/options.cpp
Normal file
@@ -0,0 +1,736 @@
|
||||
/* 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 "common/util.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kScrollZ = 9900,
|
||||
kScrollBarZ = 9000,
|
||||
kThumbnailZ = 5000,
|
||||
kArrowsZ = 2900,
|
||||
kThumbZ = 2800,
|
||||
kButtonZ = 2000,
|
||||
kTitleZ = 2000
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const char *image;
|
||||
const char *hotname;
|
||||
} buttons[] = {
|
||||
{"return", "returntogame"},
|
||||
{"credits", "credits"},
|
||||
{"quit", "quitgame"},
|
||||
{"new", "new"},
|
||||
{"savegame", "savegame"},
|
||||
{"restoregame", "restoregame"},
|
||||
{"cancel", "cancel"},
|
||||
{"save", "save"},
|
||||
{"delete", "delete"},
|
||||
{"yes", "yes"},
|
||||
{"no", "no"},
|
||||
{"ok", "ok"}
|
||||
};
|
||||
|
||||
static const char *saveDescs[] = {
|
||||
"",
|
||||
"%s in intro scene",
|
||||
"%s on mount olympus",
|
||||
"%s in wall of fame",
|
||||
"%s on Seriphos",
|
||||
"%s in Athena temple",
|
||||
"%s on Medusa Isle",
|
||||
"%s in Medusa fight",
|
||||
"%s on Argo",
|
||||
"%s in Troy",
|
||||
"%s in Catacombs",
|
||||
"%s in Priam's Castle",
|
||||
"%s in Trojan horse puzzle",
|
||||
"%s on Crete",
|
||||
"%s in Minos' Palace",
|
||||
"%s in Daedalus' Room",
|
||||
"%s in Minotaur puzzle",
|
||||
"%s on Volcano top",
|
||||
"%s near river Styx",
|
||||
"%s in Hades' throne room",
|
||||
"%s in ferryman puzzle",
|
||||
"%s in monster puzzle",
|
||||
"%s in Hades' Challenge",
|
||||
"%s in credits scene",
|
||||
""
|
||||
};
|
||||
|
||||
class OptionsHandler : public Handler {
|
||||
public:
|
||||
enum AlertType {
|
||||
kAlertSaveBeforeLoad,
|
||||
kAlertSaveBeforeExit,
|
||||
kAlertSaveBeforeNew,
|
||||
kDeleteFromLoad,
|
||||
kDeleteFromSave,
|
||||
kDeleteUser
|
||||
};
|
||||
enum SaveMenuVariant {
|
||||
kSaveFromMainMenu,
|
||||
kSaveBeforeLoad,
|
||||
kSaveBeforeExit,
|
||||
kSaveBeforeNew
|
||||
};
|
||||
OptionsHandler() {
|
||||
_savesLoaded = false;
|
||||
_showPos = 0;
|
||||
_selectedSave = -1;
|
||||
_isLast = false;
|
||||
}
|
||||
void handleClick(const Common::String &hotname) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
if (hotname == "returntogame") {
|
||||
g_vm->exitOptions();
|
||||
return;
|
||||
}
|
||||
if (hotname == "credits") {
|
||||
g_vm->enterOptionsCredits();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "savegame") {
|
||||
g_vm->resetOptionsRoom();
|
||||
saveMenu(kSaveFromMainMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "restoregame") {
|
||||
g_vm->resetOptionsRoom();
|
||||
if (gameInProgress())
|
||||
alertMenu(kAlertSaveBeforeLoad);
|
||||
else
|
||||
loadMenuUser();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "cancel") {
|
||||
switch (_currentMenu) {
|
||||
case kLoadSlotMenu:
|
||||
g_vm->resetOptionsRoom();
|
||||
loadMenuUser();
|
||||
break;
|
||||
case kSaveMenu:
|
||||
case kLoadUserMenu:
|
||||
if (gameInProgress()) {
|
||||
g_vm->resetOptionsRoom();
|
||||
gameMenu();
|
||||
} else {
|
||||
g_vm->exitOptions();
|
||||
}
|
||||
break;
|
||||
// No cancel in game menu
|
||||
case kGameMenu:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "save" && _currentMenu == kSaveMenu) {
|
||||
performSave();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname.matchString("nameslot#")) {
|
||||
_selectedSave = _showPos + hotname.substr(8).asUint64();
|
||||
renderUserNames();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname.matchString("saveslot#")) {
|
||||
_selectedSave = _showPos + hotname.substr(8).asUint64();
|
||||
renderSaveSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname.matchString("restoreslot#")) {
|
||||
_selectedSave = _showPos + hotname.substr(11).asUint64();
|
||||
renderLoadSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "arrowup" && _currentMenu == kLoadSlotMenu) {
|
||||
if (_showPos < 6)
|
||||
_showPos = 0;
|
||||
else
|
||||
_showPos -= 6;
|
||||
renderLoadSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "arrowdown" && _currentMenu == kLoadSlotMenu) {
|
||||
if (_showPos + 6 < (int) _userNames.size())
|
||||
_showPos += 6;
|
||||
renderLoadSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "arrowup" && _currentMenu == kLoadUserMenu) {
|
||||
if (_showPos < 6)
|
||||
_showPos = 0;
|
||||
else
|
||||
_showPos -= 6;
|
||||
renderUserNames();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "arrowdown" && _currentMenu == kLoadUserMenu) {
|
||||
if (_showPos + 6 < (int) _userNames.size())
|
||||
_showPos += 6;
|
||||
renderUserNames();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "arrowup" && _currentMenu == kSaveMenu) {
|
||||
if (_showPos < 3)
|
||||
_showPos = 0;
|
||||
else
|
||||
_showPos -= 3;
|
||||
renderSaveSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "arrowdown" && _currentMenu == kSaveMenu) {
|
||||
if (_showPos + 3 < (int) _filteredSaves.size())
|
||||
_showPos += 3;
|
||||
renderSaveSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "no") {
|
||||
switch (_alertType) {
|
||||
case kAlertSaveBeforeLoad:
|
||||
case kDeleteUser:
|
||||
g_vm->resetOptionsRoom();
|
||||
loadMenuUser();
|
||||
break;
|
||||
case kAlertSaveBeforeExit:
|
||||
g_vm->quit();
|
||||
break;
|
||||
case kAlertSaveBeforeNew:
|
||||
g_vm->newGame();
|
||||
g_vm->exitOptions();
|
||||
break;
|
||||
case kDeleteFromLoad:
|
||||
g_vm->resetOptionsRoom();
|
||||
loadMenuSlot();
|
||||
break;
|
||||
case kDeleteFromSave:
|
||||
g_vm->resetOptionsRoom();
|
||||
saveMenu(_saveVariant);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "yes") {
|
||||
switch (_alertType) {
|
||||
case kAlertSaveBeforeLoad:
|
||||
g_vm->resetOptionsRoom();
|
||||
saveMenu(kSaveBeforeLoad);
|
||||
break;
|
||||
case kAlertSaveBeforeExit:
|
||||
g_vm->resetOptionsRoom();
|
||||
saveMenu(kSaveBeforeExit);
|
||||
break;
|
||||
case kAlertSaveBeforeNew:
|
||||
g_vm->resetOptionsRoom();
|
||||
saveMenu(kSaveBeforeNew);
|
||||
break;
|
||||
case kDeleteFromLoad:
|
||||
g_vm->deleteSave(_filteredSaves[_selectedSave]._slot);
|
||||
_savesLoaded = false; // Invalidate cache
|
||||
g_vm->resetOptionsRoom();
|
||||
loadMenuSlot();
|
||||
break;
|
||||
case kDeleteFromSave:
|
||||
g_vm->deleteSave(_filteredSaves[_selectedSave]._slot);
|
||||
_savesLoaded = false; // Invalidate cache
|
||||
g_vm->resetOptionsRoom();
|
||||
saveMenu(_saveVariant);
|
||||
break;
|
||||
case kDeleteUser: {
|
||||
Common::U32String username = _userNames[_selectedSave];
|
||||
for (unsigned i = 0; i < _saves.size(); i++)
|
||||
if (_saves[i]._heroName == username)
|
||||
g_vm->deleteSave(_saves[i]._slot);
|
||||
_savesLoaded = false; // Invalidate cache
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "ok" && _currentMenu == kLoadUserMenu) {
|
||||
g_vm->resetOptionsRoom();
|
||||
_chosenName = _userNames[_selectedSave];
|
||||
loadMenuSlot();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "restore") {
|
||||
int slot = _filteredSaves[_selectedSave]._slot;
|
||||
g_vm->loadGameState(slot);
|
||||
g_vm->exitOptions();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (hotname == "quitgame") {
|
||||
g_vm->resetOptionsRoom();
|
||||
if (gameInProgress())
|
||||
alertMenu(kAlertSaveBeforeExit);
|
||||
else
|
||||
g_vm->quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "new") {
|
||||
g_vm->resetOptionsRoom();
|
||||
if (gameInProgress())
|
||||
alertMenu(kAlertSaveBeforeNew);
|
||||
else
|
||||
g_vm->newGame();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "delete" && _currentMenu == kLoadUserMenu) {
|
||||
g_vm->resetOptionsRoom();
|
||||
alertMenu(kDeleteUser);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "delete" && _currentMenu == kLoadSlotMenu) {
|
||||
g_vm->resetOptionsRoom();
|
||||
alertMenu(kDeleteFromLoad);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "delete" && _currentMenu == kSaveMenu) {
|
||||
g_vm->resetOptionsRoom();
|
||||
alertMenu(kDeleteFromSave);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void handleMouseOver(const Common::String &hotname) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
for (unsigned i = 0; i < ARRAYSIZE(buttons); i++)
|
||||
if (hotname == buttons[i].hotname) {
|
||||
room->selectFrame(buttons[i].image, kButtonZ, 1);
|
||||
return;
|
||||
}
|
||||
if (hotname == "arrowup" && _showPos > 0) {
|
||||
room->selectFrame("arrows", kArrowsZ, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "arrowdown" && !_isLast) {
|
||||
room->selectFrame("arrows", kArrowsZ, 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void handleMouseOut(const Common::String &hotname) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
for (unsigned i = 0; i < ARRAYSIZE(buttons); i++)
|
||||
if (hotname == buttons[i].hotname) {
|
||||
room->selectFrame(buttons[i].image, kButtonZ, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotname == "arrowup" || hotname == "arrowdown") {
|
||||
room->selectFrame("arrows", kArrowsZ, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void handleKeypress(uint32 ucode) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (_currentMenu == kSaveMenu) {
|
||||
if (ucode == '\n' || ucode == '\r') {
|
||||
performSave();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ucode == '\b' && _typedSlotName.size() > 0) {
|
||||
_typedSlotName.deleteLastChar();
|
||||
room->playSFX("keyclick");
|
||||
renderSaveName();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ucode == '\0' || ucode < ' ')
|
||||
return;
|
||||
|
||||
if (_typedSlotName.size() < 11) {
|
||||
_typedSlotName += ucode;
|
||||
room->playSFX("keyclick");
|
||||
renderSaveName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
}
|
||||
|
||||
~OptionsHandler() override {}
|
||||
|
||||
void prepareRoom() override {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
if (persistent->_currentRoomId == kOlympusRoom)
|
||||
loadMenuUser();
|
||||
else
|
||||
gameMenu();
|
||||
}
|
||||
|
||||
private:
|
||||
void performSave() {
|
||||
int slot = g_vm->firstAvailableSlot();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Common::String heroNameUTF8 = persistent->_heroName.encode(Common::kUtf8);
|
||||
// UTF-8
|
||||
Common::String descPos = Common::String::format(
|
||||
saveDescs[persistent->_currentRoomId],
|
||||
heroNameUTF8.c_str());
|
||||
// UTF-8
|
||||
Common::String desc = _typedSlotName.empty() ? descPos
|
||||
: _typedSlotName + " (" + descPos + ")";
|
||||
|
||||
persistent->_slotDescription = _typedSlotName;
|
||||
Common::Error res = g_vm->saveGameState(slot, desc);
|
||||
debug("%d, %s->[%d, %s]", slot, desc.c_str(), res.getCode(), res.getDesc().c_str());
|
||||
_savesLoaded = false; // Invalidate cache
|
||||
switch (_saveVariant) {
|
||||
case kSaveFromMainMenu:
|
||||
g_vm->exitOptions();
|
||||
break;
|
||||
case kSaveBeforeLoad:
|
||||
g_vm->resetOptionsRoom();
|
||||
loadMenuUser();
|
||||
break;
|
||||
case kSaveBeforeExit:
|
||||
g_vm->quit();
|
||||
break;
|
||||
case kSaveBeforeNew:
|
||||
g_vm->newGame();
|
||||
g_vm->exitOptions();
|
||||
break;
|
||||
}
|
||||
}
|
||||
void renderSaveName() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
// One character more to handle possible backspace clicks.
|
||||
room->hideString("smallascii", _typedSlotName.size() + 1);
|
||||
room->renderString("smallascii", _typedSlotName, Common::Point(150, 266), 4000);
|
||||
}
|
||||
|
||||
void gameMenu() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
_currentMenu = kGameMenu;
|
||||
room->loadHotZones("OPGame.HOT");
|
||||
room->addStaticLayer("black", kBackgroundZ);
|
||||
room->disableHeroBelt();
|
||||
room->selectFrame("gamemenu", kScrollBarZ, 0);
|
||||
room->selectFrame("return", kButtonZ, 0);
|
||||
room->selectFrame("credits", kButtonZ, 0);
|
||||
room->selectFrame("quit", kButtonZ, 0);
|
||||
room->selectFrame("new", kButtonZ, 0);
|
||||
room->selectFrame("savegame", kButtonZ, 0);
|
||||
if (g_vm->hasAnySaves())
|
||||
room->selectFrame("restoregame", kButtonZ, 0);
|
||||
else
|
||||
room->disableHotzone("restoregame");
|
||||
}
|
||||
|
||||
bool gameInProgress() {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
return persistent->_currentRoomId != kOlympusRoom && persistent->_currentRoomId != kIntroRoom;
|
||||
}
|
||||
|
||||
void loadMenuUser() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Common::HashMap<Common::U32String, bool> userset;
|
||||
|
||||
loadSaves();
|
||||
_currentMenu = kLoadUserMenu;
|
||||
_userNames.clear();
|
||||
for (unsigned i = 0; i < _saves.size(); i++)
|
||||
if (!userset[_saves[i]._heroName]) {
|
||||
userset[_saves[i]._heroName] = true;
|
||||
_userNames.push_back(_saves[i]._heroName);
|
||||
}
|
||||
Common::sort(_userNames.begin(), _userNames.end());
|
||||
|
||||
room->loadHotZones("OPRest1.HOT");
|
||||
room->addStaticLayer("black", kBackgroundZ);
|
||||
room->disableHeroBelt();
|
||||
room->selectFrame("scroll", kScrollZ, 0);
|
||||
room->selectFrame("restorescroll", kScrollBarZ, 0);
|
||||
|
||||
room->selectFrame("cancel", kButtonZ, 0);
|
||||
room->selectFrame("delete", kButtonZ, 0);
|
||||
room->selectFrame("ok", kButtonZ, 0);
|
||||
room->selectFrame("choosename", kTitleZ, 0);
|
||||
if (_userNames.size() > 6)
|
||||
room->selectFrame("arrows", kArrowsZ, 0);
|
||||
else {
|
||||
room->disableHotzone("arrowup");
|
||||
room->disableHotzone("arrowdown");
|
||||
}
|
||||
_showPos = 0;
|
||||
_selectedSave = -1;
|
||||
renderUserNames();
|
||||
}
|
||||
|
||||
void renderUserNames() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
bool selectedIsShown = false;
|
||||
for (int i = 0; i < 6 && _showPos + i < (int) _userNames.size(); i++) {
|
||||
Common::U32String name = _userNames[_showPos + i];
|
||||
if (name == "")
|
||||
name = "No name";
|
||||
room->renderString("largeascii", name,
|
||||
Common::Point(150, 134 + 36 * i), 4000, 0,
|
||||
Common::String::format("username%d", i));
|
||||
if (_showPos + i == _selectedSave) {
|
||||
selectedIsShown = true;
|
||||
room->selectFrame("thumb", kThumbZ, 0, Common::Point(109, 134 + 36 * i));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 6; i++) {
|
||||
room->setHotzoneEnabled(Common::String::format("nameslot%d", i), _showPos + i < _userNames.size());
|
||||
}
|
||||
|
||||
_isLast = _showPos + 3 >= (int) _userNames.size();
|
||||
|
||||
room->setHotzoneEnabled("delete", selectedIsShown);
|
||||
room->setHotzoneEnabled("ok", selectedIsShown);
|
||||
room->setHotzoneEnabled("arrowdown", !_isLast);
|
||||
room->setHotzoneEnabled("arrowup", _showPos > 0);
|
||||
}
|
||||
|
||||
void loadMenuSlot() {
|
||||
loadSaves();
|
||||
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
loadFilteredSaves(_chosenName);
|
||||
|
||||
_currentMenu = kLoadSlotMenu;
|
||||
room->loadHotZones("OPRest2.HOT");
|
||||
room->addStaticLayer("black", kBackgroundZ);
|
||||
room->disableHeroBelt();
|
||||
room->selectFrame("scroll", kScrollZ, 0);
|
||||
room->selectFrame("restore2scroll", kScrollBarZ, 0);
|
||||
room->renderStringCentered("largeascii", _chosenName, Common::Point(320, 77), 4000);
|
||||
if (_filteredSaves.size() > 6)
|
||||
room->selectFrame("arrows", kArrowsZ, 0);
|
||||
else {
|
||||
room->disableHotzone("arrowup");
|
||||
room->disableHotzone("arrowdown");
|
||||
}
|
||||
|
||||
room->selectFrame("cancel", kButtonZ, 0);
|
||||
room->selectFrame("restore", kButtonZ, 0);
|
||||
room->selectFrame("delete", kButtonZ, 0);
|
||||
room->disableHotzone("delete");
|
||||
_selectedSave = -1;
|
||||
_showPos = 0;
|
||||
|
||||
renderLoadSlots();
|
||||
}
|
||||
|
||||
void alertMenu(AlertType alertType) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->loadHotZones("OPAlert.HOT");
|
||||
room->addStaticLayer("black", kBackgroundZ);
|
||||
room->disableHeroBelt();
|
||||
// Original uses other Z values but it generates the same
|
||||
// resulting image and we can keep button code consistent
|
||||
room->selectFrame("alert", 4000, 0);
|
||||
switch (alertType) {
|
||||
case kAlertSaveBeforeLoad:
|
||||
case kAlertSaveBeforeExit:
|
||||
case kAlertSaveBeforeNew:
|
||||
room->selectFrame("exit", 3800, 0);
|
||||
break;
|
||||
case kDeleteFromLoad:
|
||||
case kDeleteFromSave:
|
||||
room->selectFrame("deletegame", 3800, 0);
|
||||
break;
|
||||
case kDeleteUser:
|
||||
room->selectFrame("deletename", 3800, 0);
|
||||
break;
|
||||
}
|
||||
room->selectFrame("yes", kButtonZ, 0);
|
||||
room->selectFrame("no", kButtonZ, 0);
|
||||
_alertType = alertType;
|
||||
}
|
||||
|
||||
void saveMenu(SaveMenuVariant saveVariant) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
loadFilteredSaves(persistent->_heroName);
|
||||
|
||||
_currentMenu = kSaveMenu;
|
||||
_saveVariant = saveVariant;
|
||||
room->loadHotZones("OPSave.HOT");
|
||||
room->addStaticLayer("black", kBackgroundZ);
|
||||
room->disableHeroBelt();
|
||||
room->selectFrame("scroll", kScrollZ, 0);
|
||||
room->selectFrame("savescroll", kScrollBarZ, 0);
|
||||
room->renderStringCentered("largeascii", persistent->_heroName, Common::Point(320, 77), 4000);
|
||||
if (_filteredSaves.size() > 3)
|
||||
room->selectFrame("arrows", kArrowsZ, 0);
|
||||
else {
|
||||
room->disableHotzone("arrowup");
|
||||
room->disableHotzone("arrowdown");
|
||||
}
|
||||
|
||||
room->selectFrame("cancel", kButtonZ, 0);
|
||||
room->selectFrame("save", kButtonZ, 0);
|
||||
room->selectFrame("delete", kButtonZ, 0);
|
||||
room->disableHotzone("delete");
|
||||
_selectedSave = -1;
|
||||
_showPos = 0;
|
||||
|
||||
_typedSlotName = "";
|
||||
|
||||
room->selectFrame("saveas", kTitleZ, 0);
|
||||
room->selectFrame(LayerId("thumbnails", 0, "save"), 5000,
|
||||
persistent->_currentRoomId - 1, Common::Point(184, 204));
|
||||
renderSaveSlots();
|
||||
}
|
||||
|
||||
void renderSaveSlots() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
bool selectedIsShown = false;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Common::Point base = Common::Point(310, 128+76*i);
|
||||
bool isValid = _showPos + i < (int)_filteredSaves.size();
|
||||
room->hideString("smallascii", 30, Common::String::format("saveslots%d", i));
|
||||
room->setHotzoneEnabled(Common::String::format("saveslot%d", i), isValid);
|
||||
if (isValid) {
|
||||
room->selectFrame(LayerId("thumbnails", i, "right"), 5000,
|
||||
_filteredSaves[_showPos + i]._room - 1, base + Common::Point(31, 0));
|
||||
room->renderString("smallascii", _filteredSaves[_showPos + i]._slotName,
|
||||
base + Common::Point(31, 62), 5000,
|
||||
0, Common::String::format("saveslots%d", i));
|
||||
if (_showPos + i == _selectedSave) {
|
||||
// TODO: original uses 300 - thumbWidth, I ust hardcode thumbWidth
|
||||
room->selectFrame("thumb", kThumbZ, 0, base - Common::Point(31+10, 0));
|
||||
selectedIsShown = true;
|
||||
}
|
||||
} else {
|
||||
room->stopAnim(LayerId("thumbnails", i, "right"));
|
||||
}
|
||||
}
|
||||
|
||||
_isLast = _showPos + 3 >= (int) _filteredSaves.size();
|
||||
|
||||
room->setHotzoneEnabled("delete", selectedIsShown);
|
||||
room->setHotzoneEnabled("arrowdown", !_isLast);
|
||||
room->setHotzoneEnabled("arrowup", _showPos > 0);
|
||||
}
|
||||
|
||||
void renderLoadSlots() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
bool selectedIsShown = false;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
bool isValid = _showPos + i < (int) _filteredSaves.size();
|
||||
room->hideString("smallascii", 30, Common::String::format("loadslots%d", i));
|
||||
room->setHotzoneEnabled(Common::String::format("restoreslot%d", i), isValid);
|
||||
if (isValid) {
|
||||
Common::Point base = Common::Point(153 + 157 * (i % 2), 128 + 76 * (i / 2));
|
||||
room->selectFrame(LayerId("thumbnails", i, "right"), 5000,
|
||||
_filteredSaves[_showPos + i]._room - 1, base + Common::Point(31, 0));
|
||||
room->renderString("smallascii", _filteredSaves[_showPos + i]._slotName,
|
||||
base + Common::Point(31, 62), 5000,
|
||||
0, Common::String::format("loadslots%d", i));
|
||||
if (_showPos + i == _selectedSave) {
|
||||
// TODO: original uses 300 - thumbWidth, I ust hardcode thumbWidth
|
||||
room->selectFrame("thumb", kThumbZ, 0, base);
|
||||
selectedIsShown = true;
|
||||
}
|
||||
} else {
|
||||
room->stopAnim(LayerId("thumbnails", i, "right"));
|
||||
}
|
||||
}
|
||||
|
||||
_isLast = _showPos + 6 >= (int) _filteredSaves.size();
|
||||
|
||||
room->setHotzoneEnabled("arrowdown", !_isLast);
|
||||
room->setHotzoneEnabled("arrowup", _showPos > 0);
|
||||
room->setHotzoneEnabled("restore", selectedIsShown);
|
||||
room->setHotzoneEnabled("delete", selectedIsShown);
|
||||
}
|
||||
|
||||
void loadSaves() {
|
||||
if (_savesLoaded)
|
||||
return;
|
||||
_saves = g_vm->getHadeschSavesList();
|
||||
}
|
||||
|
||||
void loadFilteredSaves(const Common::U32String &heroname) {
|
||||
loadSaves();
|
||||
_filteredSaves.clear();
|
||||
for (unsigned i = 0; i < _saves.size(); i++)
|
||||
if (_saves[i]._heroName == heroname)
|
||||
_filteredSaves.push_back(_saves[i]);
|
||||
}
|
||||
|
||||
enum {
|
||||
kGameMenu,
|
||||
kSaveMenu,
|
||||
kLoadUserMenu,
|
||||
kLoadSlotMenu
|
||||
} _currentMenu;
|
||||
SaveMenuVariant _saveVariant;
|
||||
AlertType _alertType;
|
||||
|
||||
Common::Array<HadeschSaveDescriptor> _saves;
|
||||
Common::Array<HadeschSaveDescriptor> _filteredSaves;
|
||||
Common::Array<Common::U32String> _userNames;
|
||||
Common::U32String _chosenName;
|
||||
Common::U32String _typedSlotName;
|
||||
int _showPos;
|
||||
int _selectedSave;
|
||||
bool _savesLoaded;
|
||||
bool _isLast;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeOptionsHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new OptionsHandler());
|
||||
}
|
||||
|
||||
}
|
||||
286
engines/hadesch/rooms/priam.cpp
Normal file
286
engines/hadesch/rooms/priam.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
/* 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 {
|
||||
// 20006 is the end of statue animation that we handle as functor instead
|
||||
kAnimPigeonsEnd = 20009,
|
||||
kSpecialPigeonTick = 20014,
|
||||
kSpecialPigeonUpDownEnd = 20015
|
||||
};
|
||||
|
||||
class PriamHandler : public Handler {
|
||||
public:
|
||||
PriamHandler() {
|
||||
_philExitWarning = 0;
|
||||
_bigGuardCounter = false;
|
||||
_specialPigeonCounter = 0;
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
|
||||
if (name == "Ares") {
|
||||
Common::Array<Common::String> videos;
|
||||
videos.push_back("T4240nA0");
|
||||
videos.push_back("T4240nB0");
|
||||
videos.push_back("T4240nC0");
|
||||
|
||||
room->playStatueSMK(kAresStatue,
|
||||
"AnimAresGlow",
|
||||
500,
|
||||
videos, 25, 42);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Aphrodite") {
|
||||
Common::Array<Common::String> videos;
|
||||
videos.push_back("T4250nA0");
|
||||
videos.push_back("T4250nB0");
|
||||
|
||||
room->playStatueSMK(kAphroditeStatue,
|
||||
"AnimAphroditeGlow",
|
||||
600,
|
||||
videos, 25, 42);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "DoorGuard") {
|
||||
room->disableMouse();
|
||||
_ambients.hide("AmbSmallGuard");
|
||||
room->playVideo("MovDoorGuardNoPass", 700, 20018, Common::Point(508, 414));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "TowerGuard") {
|
||||
if (persistent->_troyMessageIsDelivered) {
|
||||
_ambients.play("AmbBigGuard", true);
|
||||
return;
|
||||
}
|
||||
|
||||
room->disableMouse();
|
||||
if (_bigGuardCounter) {
|
||||
playPhilVideo("PhilTowerGuard");
|
||||
} else {
|
||||
_ambients.hide("AmbBigGuard");
|
||||
room->playVideo("MovTowerGuard", 200, 20017, Common::Point(0, 58));
|
||||
}
|
||||
_bigGuardCounter = !_bigGuardCounter;
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Helen") {
|
||||
playPhilVideo("PhilSheCantHearYou");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool handleClickWithItem(const Common::String &name, InventoryItem item) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
if (name == "DoorGuard" && item == kDecree) {
|
||||
room->disableMouse();
|
||||
if (!persistent->_troyMessageIsDelivered && _philExitWarning < (persistent->_hintsAreEnabled ? 2 : 1)) {
|
||||
playPhilVideo(_philExitWarning ? "PhilHint" : "PhilNoDuckingOut");
|
||||
_philExitWarning++;
|
||||
return true;
|
||||
}
|
||||
_ambients.hide("AmbSmallGuard");
|
||||
room->playVideo("MovDoorGuardPass", 700, 20019, Common::Point(508, 414));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "Helen" && item == kMessage) {
|
||||
playPhilVideo(
|
||||
persistent->_gender == kMale
|
||||
? "PhilEvenAHero" : "PhilEvenAHeroine");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "SpecialPigeon" && item == kMessage) {
|
||||
g_vm->getHeroBelt()->removeFromInventory(kMessage);
|
||||
persistent->_troyMessageIsDelivered = true;
|
||||
room->disableMouse();
|
||||
room->disableHotzone("Helen");
|
||||
room->disableHotzone("SpecialPigeon");
|
||||
room->playVideo("MovSpecialPigeonNote", 500, 20016);
|
||||
_ambients.hide("AmbSpecialPigeon");
|
||||
_ambients.hide("AmbHelen");
|
||||
_specialPigeonIsBusy = 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 20001:
|
||||
_ambients.tick();
|
||||
break;
|
||||
case 20003:
|
||||
room->enableMouse();
|
||||
break;
|
||||
case kAnimPigeonsEnd:
|
||||
if (!persistent->_troyMessageIsDelivered)
|
||||
room->playAnim("AnimPigeons", 600, PlayAnimParams::disappear(), kAnimPigeonsEnd);
|
||||
else
|
||||
room->playAnim("AnimPigeonsFlyAway", 600, PlayAnimParams::disappear(), 20010);
|
||||
break;
|
||||
case kSpecialPigeonTick: {
|
||||
if (_specialPigeonIsBusy || persistent->_troyMessageIsDelivered)
|
||||
break;
|
||||
if (_specialPigeonIsDown) {
|
||||
_specialPigeonCounter++;
|
||||
if (_specialPigeonCounter > 2) {
|
||||
room->playAnimWithSFX(
|
||||
"AnimSpecialPigeonUp",
|
||||
"SndSpecialPigeonUp",
|
||||
500,
|
||||
PlayAnimParams::disappear(),
|
||||
kSpecialPigeonUpDownEnd);
|
||||
_ambients.hide("AmbSpecialPigeon");
|
||||
room->disableHotzone("SpecialPigeon");
|
||||
_specialPigeonIsBusy = true;
|
||||
_specialPigeonCounter = 0;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
room->playAnimWithSFX(
|
||||
"AnimSpecialPigeonDown",
|
||||
"SndSpecialPigeonDown",
|
||||
500,
|
||||
PlayAnimParams::disappear(),
|
||||
kSpecialPigeonUpDownEnd);
|
||||
_specialPigeonIsBusy = true;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSpecialPigeonUpDownEnd:
|
||||
_specialPigeonIsDown = !_specialPigeonIsDown;
|
||||
_specialPigeonIsBusy = false;
|
||||
if (_specialPigeonIsDown) {
|
||||
_ambients.unpauseAndFirstFrame("AmbSpecialPigeon");
|
||||
room->enableHotzone("SpecialPigeon");
|
||||
}
|
||||
break;
|
||||
case 20016:
|
||||
room->playMusic("HelenMusic", 20022);
|
||||
room->playAnimKeepLastFrame("AnimCrackedWallOverlay",
|
||||
999, 20011,
|
||||
Common::Point(-10, -10));
|
||||
g_vm->addTimer(20021, 2000, 1);
|
||||
break;
|
||||
case 20017:
|
||||
_ambients.unpauseAndFirstFrame("AmbBigGuard");
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 20018:
|
||||
room->enableMouse();
|
||||
_ambients.unpauseAndFirstFrame("AmbSmallGuard");
|
||||
break;
|
||||
case 20019:
|
||||
_ambients.unpauseAndFirstFrame("AmbSmallGuard");
|
||||
room->playAnimKeepLastFrame("AnimGuardDoorOpen", 701, 20020);
|
||||
break;
|
||||
case 20020:
|
||||
g_vm->moveToRoom(kTroyRoom);
|
||||
break;
|
||||
case 20021:
|
||||
room->playAnimLoop("AnimHelenScarf", 600);
|
||||
break;
|
||||
case 20022:
|
||||
playPhilVideo("PhilYouDidIt", 20023);
|
||||
break;
|
||||
case 20023:
|
||||
// TODO: repeated 20024 timer 1/frame
|
||||
_collapseCounter = 2;
|
||||
room->playAnim("AnimCollapseL", 111,
|
||||
PlayAnimParams::disappear(), 20026);
|
||||
room->playAnim("AnimCollapseR", 111,
|
||||
PlayAnimParams::disappear(), 20027);
|
||||
room->playSFX("BigCollapseSnd");
|
||||
break;
|
||||
case 20026:
|
||||
case 20027:
|
||||
// TODO: timer 20024
|
||||
_collapseCounter--;
|
||||
if (_collapseCounter == 0) {
|
||||
playPhilVideo("PhilOhCollapse");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->loadHotZones("priam.HOT");
|
||||
room->addStaticLayer("Background", 10000, Common::Point(-10, -10));
|
||||
room->playMusic("IntroMusic");
|
||||
room->playMusicLoop("T4010eA0");
|
||||
TextTable prAmb = TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(room->openFile("PrAmb.txt")), 6);
|
||||
_ambients.readTableFilePriamSFX(prAmb);
|
||||
g_vm->addTimer(20001, 100, -1);
|
||||
g_vm->addTimer(20014, 3000, -1);
|
||||
_ambients.firstFrame();
|
||||
room->selectFrame("AnimGuardDoorOpen", 701, 0);
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kCool);
|
||||
_specialPigeonIsDown = true;
|
||||
_specialPigeonIsBusy = false;
|
||||
room->playAnim("AnimPigeons", 600, PlayAnimParams::disappear(), kAnimPigeonsEnd);
|
||||
}
|
||||
|
||||
bool handleCheat(const Common::String &cheat) override {
|
||||
// TODO: flybox
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void playPhilVideo(const Common::String &name, int callback = 20003) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->playVideo(name, 0, callback, Common::Point(102, 216));
|
||||
}
|
||||
|
||||
AmbientAnimWeightedSet _ambients;
|
||||
int _philExitWarning;
|
||||
int _collapseCounter;
|
||||
bool _bigGuardCounter;
|
||||
bool _specialPigeonIsBusy;
|
||||
bool _specialPigeonIsDown;
|
||||
int _specialPigeonCounter;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makePriamHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new PriamHandler());
|
||||
}
|
||||
|
||||
}
|
||||
476
engines/hadesch/rooms/quiz.cpp
Normal file
476
engines/hadesch/rooms/quiz.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
/* 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 {
|
||||
|
||||
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<VideoRoom> 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<VideoRoom> 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<VideoRoom> 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<Common::SeekableReadStream>(room->openFile("HcQuest.txt")), 5);
|
||||
_hcQList = TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(room->openFile("HcQList.txt")), 12);
|
||||
_hchadesx = TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(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<Common::String, bool> touchedStatues;
|
||||
for (int statue = kBacchusStatue; statue < kNumStatues;
|
||||
statue++)
|
||||
if (persistent->_statuesTouched[statue])
|
||||
touchedStatues[statueNames[statue]] = true;
|
||||
|
||||
Common::Array<int> untouchedQuestions;
|
||||
Common::Array<int> 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<int> 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<VideoRoom> 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<VideoRoom> 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<VideoRoom> 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<VideoRoom> 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<VideoRoom> 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<VideoRoom> 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<VideoRoom> 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<int> _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<Hadesch::Handler> makeQuizHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new QuizHandler());
|
||||
}
|
||||
|
||||
}
|
||||
435
engines/hadesch/rooms/riverstyx.cpp
Normal file
435
engines/hadesch/rooms/riverstyx.cpp
Normal file
@@ -0,0 +1,435 @@
|
||||
/* 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());
|
||||
}
|
||||
|
||||
}
|
||||
328
engines/hadesch/rooms/seriphos.cpp
Normal file
328
engines/hadesch/rooms/seriphos.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/* 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 {
|
||||
static const char *kApolloHighlight = "c7400ba0";
|
||||
static const char *kArtemisHighlight = "c7410ba0";
|
||||
static const char *kDemeterHighlight = "c7420ba0";
|
||||
static const char *kStrawCartFull = "c7100ba0";
|
||||
static const char *kStrawCartEmpty = "c7100bb0";
|
||||
static const char *kStrawCartHotzone = "Straw Cart";
|
||||
|
||||
static const char *questHovelNames[] = {
|
||||
"",
|
||||
"HovelsCrete",
|
||||
"HovelsTroy",
|
||||
"HovelsMedusa",
|
||||
"HovelsPhil"
|
||||
};
|
||||
|
||||
// TODO: fill this
|
||||
static const TranscribedSound seClickTranscript[] = {
|
||||
{"c7320wb0", _hs("You know, you've got to say one thing for that Oedipus: he loved his mother")},
|
||||
{"c7320wc0", _hs("Did you ever wonder: if Atlas is holding up the world, what's the heck is he standing on?") },
|
||||
{"c7320wd0", _hs("Do you know ho many narcisses it takes to screw in an oil lamp? One. He holds the lamp and the world revolves around him") },
|
||||
{"c7320we0", _hs("My dear, these hovels are small. Even the mice are crammed") },
|
||||
{"c7320wf0", _hs("Happiness is seeing king Polydectes' picture on the side of a milk bucket") },
|
||||
{"c7330xa0", _hs("You know what would look really good on king Polydectes? A pitbull")}, // unclear: I'm unable to hear beginning of the utterance
|
||||
{"c7330xc0", _hs("That Perseus kid: brave, strong, steady as rock. And if he takes one look at Medusa and it's where he's gonna be. Yeah, well, keep your fingers crossed, he's all over in snakes right now.")},
|
||||
{"c7340xa0", _hs("Did you hear Daedalus is building a huge labyrinth to catch Minotaur? Works great except now he can't find his way out. I heard he's building some wings made of wax. He-he. Good luch getting that idea off the ground")},
|
||||
{"c7340xc0", _hs("Boy our king is mean. Did you know that when Oedipus went blind the king rearranged the furniture? But at least we're not as bad off as the Crete: they have a rotten king, the dangerous Minotaur and lousy parking. Good luck finding a place for your chariot")},
|
||||
{"c7350xa0", _hs("That beautiful Helen is still being held captive in Troy. How awful for her. They've got such a lousy shopping there. When is this trojan war going to be over? Then maybe we'll start peloponesean war.")},
|
||||
{"c7350xc0", _hs("Gee, Odysseus failed to get into the city. Helen is still a prisonner and morale is low. What else can go wrong? He's just found out his chariot needs new shocks. That's gonna be expensive")},
|
||||
{"c7360xa0", _hs("Oh, it's a good thing Perseus killed Medusa otherwise Polydectes would still be king. Perseus is a much better king than Polydectes was. Now that I think of it, my dog would be a much better king than Polydectes was")},
|
||||
// "c7360wc0"
|
||||
// "c7360wd0"
|
||||
// "c7310xc0"
|
||||
// "c7310xa0"
|
||||
// "c7310xb0"
|
||||
// "c7310xd0"
|
||||
// "c7310xe0"
|
||||
// "c7310xf0"
|
||||
// "c7310xh0"
|
||||
// "c7310xg0"
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
enum {
|
||||
kAnimationCompleted = 26007,
|
||||
// 26008 is the end of statue animation that we handle as functor instead
|
||||
kStrawTaken = 26020,
|
||||
kStrawTakenCleanup = 26021,
|
||||
|
||||
kIdlesTick = 1026001,
|
||||
kHovelsCompleted = 1026002
|
||||
};
|
||||
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kCartZ = 101,
|
||||
kStatuesZ = 101,
|
||||
kPhilZ = 0
|
||||
};
|
||||
|
||||
class SeriphosHandler : public Handler {
|
||||
public:
|
||||
SeriphosHandler() {
|
||||
_hovelsCounter = -1;
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
if (name == "Apollo") {
|
||||
Common::Array<Common::String> videos;
|
||||
videos.push_back("c7400na0");
|
||||
videos.push_back("c7400nb0");
|
||||
videos.push_back("c7400nc0");
|
||||
|
||||
room->playStatueSMK(kApolloStatue,
|
||||
kApolloHighlight,
|
||||
kStatuesZ,
|
||||
videos, 33, 49);
|
||||
return;
|
||||
}
|
||||
if (name == "Artemis") {
|
||||
Common::Array<Common::String> videos;
|
||||
videos.push_back("c7410na0");
|
||||
videos.push_back("c7410nb0");
|
||||
|
||||
room->playStatueSMK(kArtemisStatue,
|
||||
kArtemisHighlight,
|
||||
kStatuesZ,
|
||||
videos, 25, 46);
|
||||
return;
|
||||
}
|
||||
if (name == "Demeter") {
|
||||
Common::Array<Common::String> videos;
|
||||
videos.push_back("c7420na0");
|
||||
videos.push_back("c7420nb0");
|
||||
|
||||
room->playStatueSMK(kDemeterStatue,
|
||||
kDemeterHighlight,
|
||||
kStatuesZ,
|
||||
videos, 25, 45);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Argo") {
|
||||
if (quest == kMedusaQuest && !persistent->_seriphosPhilWarnedAthena
|
||||
&& (!persistent->_athenaSwordTaken || !persistent->_athenaShieldTaken)
|
||||
&& persistent->_hintsAreEnabled) {
|
||||
room->disableMouse();
|
||||
persistent->_seriphosPhilWarnedAthena = true;
|
||||
room->playVideo("c7300ba0", 0, 26022, Common::Point(0, 216));
|
||||
return;
|
||||
}
|
||||
g_vm->moveToRoom(kArgoRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Athena's Temple") {
|
||||
g_vm->moveToRoom(kAthenaRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == kStrawCartHotzone) {
|
||||
room->selectFrame(kStrawCartEmpty, kCartZ, 0);
|
||||
_seIdles.hide(kStrawCartFull);
|
||||
room->playMusic("c7380mb0");
|
||||
g_vm->getHeroBelt()->placeToInventory(kStraw, kStrawTaken);
|
||||
room->disableHotzone(kStrawCartHotzone);
|
||||
room->disableMouse();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((name == "Curtains" || name == "Hovels")
|
||||
&& (quest == kMedusaQuest
|
||||
&& (persistent->_athenaSwordTaken && persistent->_athenaShieldTaken)
|
||||
&& g_vm->getPreviousRoomId() == kAthenaRoom
|
||||
&& !persistent->_seriphosPhilCurtainsItems)) {
|
||||
room->disableMouse();
|
||||
persistent->_seriphosPhilCurtainsItems = true;
|
||||
room->playVideo("c7370ba0", 0, 26023, Common::Point(0, 216));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Curtains") {
|
||||
_ambients.hide("c7160ba0");
|
||||
|
||||
if (quest < kMedusaQuest) {
|
||||
_seClick.playNext("CurtainsBeforeMedusa", kAnimationCompleted);
|
||||
} else if (quest == kMedusaQuest) {
|
||||
_seClick.playNext("CurtainsDuringMedusa", kAnimationCompleted);
|
||||
} else if (quest > kMedusaQuest) {
|
||||
_seClick.playNext("CurtainsAfterMedusa", kAnimationCompleted);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Hovels") {
|
||||
int genericidx = -1;
|
||||
_hovelsCounter++;
|
||||
room->disableMouse();
|
||||
room->playAnimWithSFX("c7320ba0", "C7320EA0", 3101, PlayAnimParams::loop());
|
||||
switch(persistent->_quest) {
|
||||
case kRescuePhilQuest:
|
||||
if (_hovelsCounter == 1) {
|
||||
_seClick.playChosen(questHovelNames[persistent->_quest],
|
||||
persistent->_gender == kFemale ? 2 : 1, kHovelsCompleted);
|
||||
return;
|
||||
}
|
||||
// Fallthrough
|
||||
case kCreteQuest:
|
||||
case kTroyQuest:
|
||||
case kMedusaQuest:
|
||||
if (_hovelsCounter < 2) {
|
||||
_seClick.playChosen(questHovelNames[persistent->_quest], _hovelsCounter, kHovelsCompleted);
|
||||
return;
|
||||
}
|
||||
genericidx = _hovelsCounter - 2;
|
||||
break;
|
||||
default:
|
||||
genericidx = _hovelsCounter;
|
||||
break;
|
||||
}
|
||||
|
||||
_seClick.playChosen("HovelsGeneric", genericidx, kHovelsCompleted);
|
||||
if (genericidx == 4)
|
||||
_hovelsCounter = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
switch (eventId) {
|
||||
case 2803:
|
||||
_ambients.tick();
|
||||
break;
|
||||
case 26009:
|
||||
room->playMusic("c7290ma0", 26012);
|
||||
room->playVideo("c7290ba0", 111, 26010, Common::Point(90, 76));
|
||||
break;
|
||||
case 26010:
|
||||
room->playSFX("c7160ea0", 26013);
|
||||
break;
|
||||
case 26013:
|
||||
room->playVideo("c7290bd0", 111, 26014, Common::Point(92, 76));
|
||||
break;
|
||||
case 26014:
|
||||
room->enableMouse();
|
||||
startIdles();
|
||||
break;
|
||||
case kStrawTaken:
|
||||
room->playVideo("c7380ba0", kPhilZ, kStrawTakenCleanup, Common::Point(0, 216));
|
||||
break;
|
||||
case 26022:
|
||||
case 26023:
|
||||
case kStrawTakenCleanup:
|
||||
room->enableMouse();
|
||||
break;
|
||||
case kAnimationCompleted:
|
||||
room->enableMouse();
|
||||
_ambients.unpauseAndFirstFrame("c7160ba0");
|
||||
break;
|
||||
case kIdlesTick:
|
||||
_seIdles.tick();
|
||||
break;
|
||||
case kHovelsCompleted:
|
||||
room->enableMouse();
|
||||
room->stopAnim("c7320ba0");
|
||||
room->selectFrame("c7320ba0", 3101, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
room->loadHotZones("Seriphos.HOT", true);
|
||||
room->addStaticLayer("c7010pa0", kBackgroundZ);
|
||||
|
||||
Common::String seAmbFn = quest > kMedusaQuest ? "SeAmb2.txt" : "SeAmb.txt";
|
||||
TextTable seAmb = TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(room->openFile(seAmbFn)), 9);
|
||||
_ambients.readTableFileSFX(seAmb, AmbientAnim::PAN_ANY);
|
||||
|
||||
if (quest == kMedusaQuest && !persistent->_seriphosPlayedMedusa) {
|
||||
g_vm->addTimer(26009, 500, 1);
|
||||
persistent->_seriphosPlayedMedusa = true;
|
||||
} else
|
||||
startIdles();
|
||||
if (quest > kMedusaQuest) {
|
||||
room->selectFrame("c7010oa0", 102, 0);
|
||||
room->selectFrame("c7010ta0", 101, 0);
|
||||
room->selectFrame("c7010ob0", 3101, 0);
|
||||
room->playAnimLoop("c7320bb0", 1101);
|
||||
}
|
||||
|
||||
room->playMusicLoop(quest > kMedusaQuest ? "c7010eb0" : "c7010ea0");
|
||||
|
||||
_seClick.readTable(room, "SeClick.txt", seClickTranscript);
|
||||
|
||||
g_vm->getHeroBelt()->setColour(quest > kMedusaQuest ? HeroBelt::kWarm : HeroBelt::kCool);
|
||||
|
||||
TextTable seIdles = TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(room->openFile("SeIdles.txt")), 14);
|
||||
_seIdles.readTableFileSFX(seIdles, AmbientAnim::PAN_ANY);
|
||||
_seIdles.firstFrame();
|
||||
|
||||
if (quest == kCreteQuest && !persistent->_seriphosStrawCartTaken) {
|
||||
_seIdles.unpauseAndFirstFrame(kStrawCartFull);
|
||||
room->stopAnim(kStrawCartEmpty);
|
||||
} else {
|
||||
room->selectFrame(kStrawCartEmpty, kCartZ, 0);
|
||||
_seIdles.hide(kStrawCartFull);
|
||||
room->disableHotzone(kStrawCartHotzone);
|
||||
}
|
||||
|
||||
room->playAnimLoop("c7110bb0", 2101);
|
||||
room->playAnimLoop("c7110bc0", 2101);
|
||||
room->playAnimLoop("c7180ba0", 3101);
|
||||
room->selectFrame("c7320ba0", 3101, 0);
|
||||
}
|
||||
private:
|
||||
void startIdles() {
|
||||
g_vm->addTimer(2803, 10000, -1);
|
||||
_ambients.firstFrame();
|
||||
g_vm->addTimer(kIdlesTick, 6000, -1);
|
||||
_seIdles.firstFrame();
|
||||
}
|
||||
|
||||
AmbientAnimWeightedSet _ambients;
|
||||
AmbientAnimWeightedSet _seIdles;
|
||||
AnimClickables _seClick;
|
||||
int _hovelsCounter;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeSeriphosHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new SeriphosHandler());
|
||||
}
|
||||
|
||||
}
|
||||
53
engines/hadesch/rooms/trojan.cpp
Normal file
53
engines/hadesch/rooms/trojan.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/* 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"
|
||||
|
||||
namespace Hadesch {
|
||||
enum {
|
||||
kBackgroundZ = 10000
|
||||
};
|
||||
|
||||
class TrojanHandler : public Handler {
|
||||
public:
|
||||
TrojanHandler() {
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->addStaticLayer("t6010pa0", kBackgroundZ);
|
||||
}
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeTrojanHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new TrojanHandler());
|
||||
}
|
||||
|
||||
}
|
||||
851
engines/hadesch/rooms/troy.cpp
Normal file
851
engines/hadesch/rooms/troy.cpp
Normal file
@@ -0,0 +1,851 @@
|
||||
/* 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"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "gui/message.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
static const char *kDamagedWall = "t1010ob0";
|
||||
static const char *kBricksImage = "g0120oB0";
|
||||
static const char *kHephaestusHighlight = "t2330ba0";
|
||||
static const char *kHeraHighlight = "t2300ba0";
|
||||
static const char *kOdysseusIdle = "t2150ba0";
|
||||
static const char *kOdysseusWithMessage = "t2140bb0";
|
||||
static const char *kOdysseusFateOfGreece = "t2150bb0";
|
||||
static const char *kPrisoner = "t2130ba0";
|
||||
static const char *kKeyAndDecreeImage = "g0150ob0";
|
||||
static const char *kKeyAndDecreePopup = "t2010of0";
|
||||
static const char *kMenelausImage = "t2070ba0";
|
||||
static const char *kHelenImage = "t1230ba0";
|
||||
|
||||
// TODO: fill this
|
||||
static const TranscribedSound trClickTranscript[] = {
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
enum {
|
||||
kAnimationCompleted = 10011,
|
||||
kPrisonerVideoCompleteted = 1010001,
|
||||
kKeyPlaced = 1010002,
|
||||
kMenelausAnimCompleteted = 1010003,
|
||||
kBgSoldiersAnimCompleteted = 1010004,
|
||||
kHelenAnimCompleteted = 1010005,
|
||||
kCatacombAnimCompleteted = 1010006,
|
||||
kPlayOutro = 1010007,
|
||||
kHorseCounter = 1010008
|
||||
};
|
||||
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kDamagedWallZ = 1101,
|
||||
kMenelausZ = 1000,
|
||||
kHelenZ = 500,
|
||||
kSoldier1Z = 101,
|
||||
kSolsier2Z = 101,
|
||||
kOdysseusZ = 131,
|
||||
kPrisonerZ = 151,
|
||||
kSoldier3Z = 161,
|
||||
|
||||
kPopupZ = 0
|
||||
};
|
||||
|
||||
#define PLACEHOLDER_MIN_INTERVAL 7000
|
||||
#define PLACEHOLDER_MAX_INTERVAL 12000
|
||||
|
||||
class TroyHandler : public Handler {
|
||||
public:
|
||||
TroyHandler() {
|
||||
_philUseSecondInsistance = false;
|
||||
_prisonerCounter = 0;
|
||||
_bgSoldierCount = 0;
|
||||
_prisonerTouched = false;
|
||||
_philWarnedFishing = false;
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
|
||||
if (name == "Argo") {
|
||||
if (quest == kTroyQuest && !_philWarnedFishing) {
|
||||
room->disableMouse();
|
||||
room->playVideo("t1030ba0", 0, 10015, Common::Point(0, 216));
|
||||
return;
|
||||
}
|
||||
g_vm->moveToRoom(kArgoRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Bricks") {
|
||||
room->stopAnim(kBricksImage);
|
||||
g_vm->getHeroBelt()->placeToInventory(kBricks, 10069);
|
||||
room->disableHotzone("Bricks");
|
||||
room->disableMouse();
|
||||
room->playVideo("t1270ma0", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Hephaestus' Statue") {
|
||||
Common::Array<Common::String> videos;
|
||||
videos.push_back("t2330na0");
|
||||
videos.push_back("t2330nb0");
|
||||
videos.push_back("t2330nc0");
|
||||
|
||||
room->playStatueSMK(kHephaestusStatue,
|
||||
kHephaestusHighlight,
|
||||
201,
|
||||
videos,
|
||||
16, 29,
|
||||
kOffsetRightRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Hera's Statue") {
|
||||
Common::Array<Common::String> videos;
|
||||
videos.push_back("t2300na0");
|
||||
if (quest > kTroyQuest || (quest == kTroyQuest && persistent->_troyIsDefeated)) {
|
||||
videos.push_back("t2320na0");
|
||||
} else if (quest == kTroyQuest) {
|
||||
videos.push_back("t2310na0");
|
||||
videos.push_back("t2310nb0");
|
||||
videos.push_back("t2310nc0");
|
||||
}
|
||||
|
||||
room->playStatueSMK(kHeraStatue,
|
||||
kHeraHighlight,
|
||||
101,
|
||||
videos,
|
||||
17, 34,
|
||||
kOffsetRightRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Background2") {
|
||||
if (_philUseSecondInsistance)
|
||||
room->playVideo("t2140bi0", 0, 10072, Common::Point(640, 216));
|
||||
else
|
||||
room->playVideo("t2150bc0", 0, 10071, Common::Point(640, 216));
|
||||
_philUseSecondInsistance = true;
|
||||
room->disableMouse();
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Odysseus' Scroll") {
|
||||
room->disableHotzone("Odysseus' Scroll");
|
||||
room->disableHotzone("Background2");
|
||||
room->playSFX("T2150eA1", 10049);
|
||||
hideOdysseus();
|
||||
showIdleOdysseus();
|
||||
_philUseSecondInsistance = false;
|
||||
room->selectFrame("t2010oe0", 101, 0, Common::Point(695, 0));
|
||||
g_vm->cancelTimer(10047);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Scroll PopUp") {
|
||||
room->cancelVideo();
|
||||
room->disableHotzone("Scroll PopUp");
|
||||
room->disableHotzone("Background2");
|
||||
room->playSFX("T2150eB0");
|
||||
room->stopAnim("t2010oe0");
|
||||
room->disableMouse();
|
||||
g_vm->getHeroBelt()->placeToInventory(kMessage, 10052);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Crazy Soldier 1") {
|
||||
room->stopAnim(soldier1());
|
||||
_trClick.playNext("CampBurntOutSoldier", kAnimationCompleted);
|
||||
_soldier1Ambient.pause();
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Crazy Soldier 2") {
|
||||
room->stopAnim(soldier2());
|
||||
_trClick.playNext("CampCrazySoldier", kAnimationCompleted);
|
||||
_soldier2Ambient.pause();
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Crazy Soldier 3") {
|
||||
room->stopAnim(soldier3());
|
||||
_trClick.playNext("CampShellShockedSoldier", kAnimationCompleted);
|
||||
_soldier3Ambient.pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// Alternate Soldier * use TrClick.txt in the original
|
||||
if (name == "Alternate Soldier 1") {
|
||||
_soldier1Ambient.play(false);
|
||||
return;
|
||||
}
|
||||
if (name == "Alternate Soldier 2") {
|
||||
_soldier2Ambient.play(false);
|
||||
return;
|
||||
}
|
||||
if (name == "Alternate Soldier 3") {
|
||||
_soldier3Ambient.play(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Prisoner") {
|
||||
room->stopAnim(kPrisoner);
|
||||
room->disableMouse();
|
||||
_prisonerAmbient.pause();
|
||||
_trClick.playChosen("Prisoner", _prisonerCounter, kPrisonerVideoCompleteted);
|
||||
_prisonerTouched = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Menelaus' Tent") {
|
||||
_menelausTent1Ambient.hide();
|
||||
room->disableMouse();
|
||||
_trClick.playNext(quest == kCreteQuest ? "MenelausTentBeforeNote"
|
||||
: "MenelausTentAfterNote", kMenelausAnimCompleteted);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Helen") {
|
||||
_helenAmbient.hide();
|
||||
room->disableMouse();
|
||||
_trClick.playNext("Helen", kHelenAnimCompleteted);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: is targetCounter persistent?
|
||||
if (name == "Background Soldiers") {
|
||||
room->stopAnim("t2250ba0");
|
||||
_bgSoldiersAmbient.pause();
|
||||
room->disableMouse();
|
||||
int targetCounter = _bgSoldierCount;
|
||||
if (quest == kCreteQuest) {
|
||||
if (targetCounter < 2) {
|
||||
_trClick.playChosen("GroupSoldiersCrete", targetCounter, kBgSoldiersAnimCompleteted);
|
||||
_bgSoldierCount++;
|
||||
return;
|
||||
} else
|
||||
targetCounter -= 2;
|
||||
}
|
||||
if (quest == kMedusaQuest) {
|
||||
if (targetCounter < 2) {
|
||||
_trClick.playChosen("GroupSoldiersMedusa", targetCounter, kBgSoldiersAnimCompleteted);
|
||||
_bgSoldierCount++;
|
||||
return;
|
||||
} else
|
||||
targetCounter -= 2;
|
||||
}
|
||||
if (quest == kTroyQuest) {
|
||||
if (targetCounter < 9) {
|
||||
_trClick.playChosen("GroupSoldiersBeforeTroy", targetCounter, kBgSoldiersAnimCompleteted);
|
||||
_bgSoldierCount++;
|
||||
return;
|
||||
} else
|
||||
targetCounter -= 9;
|
||||
}
|
||||
|
||||
if (quest > kTroyQuest) {
|
||||
if (targetCounter < 6) {
|
||||
_trClick.playChosen("GroupSoldiersAfterTroy", targetCounter, kBgSoldiersAnimCompleteted);
|
||||
_bgSoldierCount++;
|
||||
return;
|
||||
} else
|
||||
targetCounter -= 6;
|
||||
}
|
||||
|
||||
_trClick.playChosen("GroupSoldiersAll", targetCounter, kBgSoldiersAnimCompleteted);
|
||||
_bgSoldierCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Key And Decree") {
|
||||
room->disableMouse();
|
||||
persistent->_troyKeyAndDecreeState = Persistent::KEY_AND_DECREE_TAKEN;
|
||||
room->disableHotzone("Key And Decree");
|
||||
room->stopAnim(kKeyAndDecreeImage);
|
||||
room->selectFrame(kKeyAndDecreePopup, kPopupZ, 0, kOffsetRightRoom);
|
||||
g_vm->addTimer(10057, 2000, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Catacomb") {
|
||||
if (quest == kCreteQuest) {
|
||||
room->disableMouse();
|
||||
room->playVideo("t1280ba0", 0, 10017, Common::Point(0, 217));
|
||||
return;
|
||||
}
|
||||
|
||||
if (persistent->_troyCatacombCounter == 0 || persistent->_troyCatacombCounter == 1) {
|
||||
room->stopAnim(persistent->_troyCatacombCounter == 1 ? "t1280bc0" : "t1280bb0");
|
||||
_trClick.playChosen("Catacomb", persistent->_troyCatacombCounter, kCatacombAnimCompleteted);
|
||||
persistent->_troyCatacombCounter++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (quest > kTroyQuest) {
|
||||
room->playVideo("T1280BD0", 0, 10016, Common::Point(0, 216));
|
||||
return;
|
||||
}
|
||||
|
||||
room->enableHotzone("Background");
|
||||
room->enableHotzone("Catacomb PopUp");
|
||||
room->selectFrame("t1010oe0", 106, 0);
|
||||
room->selectFrame("t1290bb0", 105, persistent->_troyCatacombsUnlocked ? 12 : 0);
|
||||
room->enableHotzone(persistent->_troyCatacombsUnlocked ? "Link To Catacombs" : "Catacomb PopUp Grate");
|
||||
|
||||
// TODO: unschedule 10023
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Background") {
|
||||
room->disableHotzone("Background");
|
||||
room->disableHotzone("Catacomb PopUp");
|
||||
room->stopAnim("t1010oe0");
|
||||
room->stopAnim("t1290bb0");
|
||||
room->disableHotzone("Link To Catacombs");
|
||||
room->disableHotzone("Catacomb PopUp Grate");
|
||||
// TODO: schedule 10023
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Link To Catacombs") {
|
||||
room->disableMouse();
|
||||
g_vm->moveToRoom(kCatacombsRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "Catacomb PopUp Grate") {
|
||||
room->disableMouse();
|
||||
room->playVideo(
|
||||
!persistent->isInInventory(kKey) && _prisonerTouched ? "t1290ba0"
|
||||
: "t1290bd0", 0, 10020,
|
||||
Common::Point(0, 216));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool handleClickWithItem(const Common::String &name, InventoryItem item) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
if (name == "Catacomb PopUp Grate") {
|
||||
if (item == kKey) {
|
||||
room->disableMouse();
|
||||
g_vm->getHeroBelt()->removeFromInventory(kKey);
|
||||
room->playAnimWithSFX("t1290bb0", "t1290xa0", 105, PlayAnimParams::keepLastFrame(),
|
||||
10060);
|
||||
persistent->_troyCatacombsUnlocked = true;
|
||||
room->disableHotzone("Catacomb PopUp Grate");
|
||||
room->enableHotzone("Link To Catacombs");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_prisonerTouched || !persistent->isInInventory(kKey)) {
|
||||
room->disableMouse();
|
||||
room->playVideo(
|
||||
_prisonerTouched ? "t1290ba0" : "t1290bd0", 0, 10020,
|
||||
Common::Point(0, 216));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
switch(eventId) {
|
||||
case 2803:
|
||||
_leftAmbients.tick();
|
||||
break;
|
||||
case kAnimationCompleted:
|
||||
room->stopAnim(kHephaestusHighlight);
|
||||
room->stopAnim(kHeraHighlight);
|
||||
soldiersDisplay();
|
||||
room->enableMouse();
|
||||
_soldier1Ambient.unpause();
|
||||
_soldier2Ambient.unpause();
|
||||
_soldier3Ambient.unpause();
|
||||
break;
|
||||
case kCatacombAnimCompleteted:
|
||||
room->enableMouse();
|
||||
showCatacombStones();
|
||||
break;
|
||||
case 10015:
|
||||
_philWarnedFishing = true;
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 10013:
|
||||
case 10017:
|
||||
case 10018:
|
||||
case 10019:
|
||||
case 10020:
|
||||
case 10060:
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 10022:
|
||||
// Attack on castle
|
||||
room->playAnimWithSFX("t1250bb0",
|
||||
"t1250eb0", 131,
|
||||
PlayAnimParams::disappear(),
|
||||
10027);
|
||||
break;
|
||||
case 10027:
|
||||
room->selectFrame(kDamagedWall, kDamagedWallZ, 0);
|
||||
showBricks();
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 10044:
|
||||
g_vm->addTimer(10045, 500, 1);
|
||||
break;
|
||||
case 10045:
|
||||
hideOdysseus();
|
||||
room->playVideo("t2140ba0", kOdysseusZ, 10046, Common::Point(649, 17));
|
||||
break;
|
||||
case 10046:
|
||||
room->playAnimLoop(kOdysseusWithMessage, kOdysseusZ, Common::Point(800, 0));
|
||||
room->setLayerParallax(kOdysseusWithMessage, -160);
|
||||
g_vm->addTimer(10047, 20000, -1);
|
||||
room->enableHotzone("Odysseus' Scroll");
|
||||
room->enableHotzone("Background2");
|
||||
room->enableMouse();
|
||||
room->setPannable(false);
|
||||
break;
|
||||
case 10047:
|
||||
if (room->isMouseEnabled()) {
|
||||
room->stopAnim(kOdysseusWithMessage);
|
||||
room->playVideo("t2140bc0", kOdysseusZ, 10048, Common::Point(650, 17));
|
||||
}
|
||||
break;
|
||||
case 10048:
|
||||
room->playAnimLoop(kOdysseusWithMessage, kOdysseusZ, Common::Point(800, 0));
|
||||
break;
|
||||
case 10049:
|
||||
room->enableHotzone("Scroll PopUp");
|
||||
room->enableHotzone("Background2");
|
||||
room->playVideo("t2150xb0", 0, 10050);
|
||||
break;
|
||||
// 10050 is cleanup
|
||||
case 10052:
|
||||
hideOdysseus();
|
||||
room->playVideo(kOdysseusFateOfGreece, kOdysseusZ, 10053, Common::Point(640, 8));
|
||||
break;
|
||||
case 10053:
|
||||
room->enableMouse();
|
||||
room->setPannable(true);
|
||||
break;
|
||||
case 10055:
|
||||
hideOdysseus();
|
||||
room->playVideo("T2340BB0", kOdysseusZ, 10056, Common::Point(649, 18));
|
||||
break;
|
||||
case 10056:
|
||||
showIdleOdysseus();
|
||||
// TODO: for now we skip arcade sequence until it's implemented
|
||||
if (0) {
|
||||
g_vm->moveToRoom(kTrojanHorsePuzzle);
|
||||
} else {
|
||||
persistent->_troyPlayFinish = true;
|
||||
g_vm->moveToRoom(kTroyRoom);
|
||||
}
|
||||
break;
|
||||
case 10057:
|
||||
room->playSpeech(
|
||||
TranscribedSound::make("T2240wA0", "Official orders from king Priam: messenger is granted permissions to leave the city walls"), 10058);
|
||||
break;
|
||||
case 10058:
|
||||
room->enableMouse();
|
||||
room->stopAnim(kKeyAndDecreePopup);
|
||||
g_vm->getHeroBelt()->placeToInventory(kKey, kKeyPlaced);
|
||||
break;
|
||||
case kKeyPlaced:
|
||||
g_vm->getHeroBelt()->placeToInventory(kDecree);
|
||||
break;
|
||||
case kHorseCounter:
|
||||
_horseCounter--;
|
||||
if (_horseCounter != 0)
|
||||
break;
|
||||
/* Fallthrough */
|
||||
case 10063:
|
||||
room->playSFX("t1350ec0", 10064);
|
||||
break;
|
||||
case 10064:
|
||||
room->playAnimKeepLastFrame("t1350bb0", 501, 10065);
|
||||
room->playSFX("t1350ed0", 10066);
|
||||
break;
|
||||
case 10065:
|
||||
room->playAnim("t1350bb0", 501, PlayAnimParams::loop().partial(8, 11));
|
||||
break;
|
||||
case 10066: {
|
||||
GUI::MessageDialog dialog(_("The Troy minigame is not supported yet. Skipping"));
|
||||
dialog.runModal();
|
||||
g_vm->moveToRoom(kQuiz);
|
||||
break;
|
||||
}
|
||||
case 10069:
|
||||
// TODO: check this
|
||||
room->playVideo("T1270BA0", 0, 10070, Common::Point(0, 216));
|
||||
break;
|
||||
case 10070:
|
||||
case 10071:
|
||||
case 10072:
|
||||
room->enableMouse();
|
||||
break;
|
||||
case kPrisonerVideoCompleteted:
|
||||
room->enableMouse();
|
||||
_prisonerAmbient.unpauseAndFirstFrame();
|
||||
if (_prisonerCounter == 2) {
|
||||
room->selectFrame(kKeyAndDecreeImage, kPrisonerZ, 0, kOffsetRightRoom);
|
||||
room->enableHotzone("Key And Decree");
|
||||
persistent->_troyKeyAndDecreeState = Persistent::KEY_AND_DECREE_THROWN;
|
||||
}
|
||||
if (_prisonerCounter < 7)
|
||||
_prisonerCounter++;
|
||||
break;
|
||||
case kMenelausAnimCompleteted:
|
||||
_menelausTent1Ambient.unpauseAndFirstFrame();
|
||||
room->enableMouse();
|
||||
break;
|
||||
case kHelenAnimCompleteted:
|
||||
_helenAmbient.unpauseAndFirstFrame();
|
||||
room->enableMouse();
|
||||
break;
|
||||
case kBgSoldiersAnimCompleteted:
|
||||
_bgSoldiersAmbient.unpauseAndFirstFrame();
|
||||
room->enableMouse();
|
||||
break;
|
||||
case kPlayOutro:
|
||||
_menelausTent1Ambient.hide();
|
||||
_trClick.playNext("MenelausClosingStatement", 10055);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
Common::RandomSource &rnd = g_vm->getRnd();
|
||||
room->loadHotZones("troy.HOT", false);
|
||||
room->addStaticLayer("t1010pa0", kBackgroundZ);
|
||||
room->setPannable(true);
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kWarm);
|
||||
room->enableHotzone("Argo");
|
||||
room->enableHotzone("Hephaestus' Statue");
|
||||
room->enableHotzone("Hera's Statue");
|
||||
room->enableHotzone("Catacomb");
|
||||
room->playAnimLoop("T1110BA0", 501);
|
||||
|
||||
_trClick.readTable(room, "TrClick.txt", trClickTranscript);
|
||||
|
||||
if (persistent->_troyWallDamaged && quest == kCreteQuest) {
|
||||
room->selectFrame(kDamagedWall, kDamagedWallZ, 0);
|
||||
}
|
||||
|
||||
if (persistent->_troyShowBricks && quest == kCreteQuest) {
|
||||
showBricks();
|
||||
}
|
||||
|
||||
if (persistent->_troyPlayAttack && quest == kCreteQuest) {
|
||||
room->disableMouse();
|
||||
persistent->_troyPlayAttack = false;
|
||||
persistent->_troyShowBricks = true;
|
||||
persistent->_troyWallDamaged = true;
|
||||
room->playVideo("t1060ba0", 0, 10022,
|
||||
Common::Point(0, 201));
|
||||
}
|
||||
|
||||
room->enableHotzone("Background Soldiers");
|
||||
room->selectFrame("t2250ba0", 601, 0, kOffsetRightRoom);
|
||||
_bgSoldiersAmbient = AmbientAnim("t2250ba0", "", 601, PLACEHOLDER_MIN_INTERVAL,
|
||||
PLACEHOLDER_MAX_INTERVAL, AmbientAnim::KEEP_LOOP, kOffsetRightRoom,
|
||||
AmbientAnim::PAN_RIGHT);
|
||||
_bgSoldiersAmbient.start();
|
||||
|
||||
if (!persistent->_troyPlayedOdysseus && quest == kTroyQuest) {
|
||||
room->panRightAnim(10044);
|
||||
room->disableMouse();
|
||||
showIdleOdysseus();
|
||||
}
|
||||
|
||||
if (quest <= kTroyQuest) {
|
||||
_soldier1IsCrazy = rnd.getRandomBit();
|
||||
_soldier2IsCrazy = rnd.getRandomBit();
|
||||
_soldier3IsCrazy = rnd.getRandomBit();
|
||||
room->addStaticLayer("t1010pd0", 121, Common::Point(712, 187));
|
||||
room->setLayerParallax("t1010pd0", -160);
|
||||
soldiersDisplay();
|
||||
|
||||
// TODO: check times
|
||||
_soldier1Ambient =
|
||||
AmbientAnim(soldier1(), soldier1Sound(),
|
||||
kSoldier1Z, PLACEHOLDER_MIN_INTERVAL, PLACEHOLDER_MAX_INTERVAL,
|
||||
AmbientAnim::KEEP_LOOP,
|
||||
Common::Point(980, 0),
|
||||
AmbientAnim::PAN_RIGHT);
|
||||
_soldier1Ambient.start();
|
||||
_soldier2Ambient = AmbientAnim(soldier2(), soldier2Sound(), kSolsier2Z, PLACEHOLDER_MIN_INTERVAL,
|
||||
PLACEHOLDER_MAX_INTERVAL, AmbientAnim::KEEP_LOOP, kOffsetRightRoom,
|
||||
AmbientAnim::PAN_RIGHT);
|
||||
_soldier2Ambient.start();
|
||||
_soldier3Ambient = AmbientAnim(soldier3(), soldier3Sound(), kSoldier3Z, PLACEHOLDER_MIN_INTERVAL,
|
||||
PLACEHOLDER_MAX_INTERVAL, AmbientAnim::KEEP_LOOP, kOffsetRightRoom,
|
||||
AmbientAnim::PAN_RIGHT);
|
||||
_soldier3Ambient.start();
|
||||
|
||||
room->enableHotzone(_soldier1IsCrazy ? "Crazy Soldier 1" : "Alternate Soldier 1");
|
||||
room->enableHotzone(_soldier2IsCrazy ? "Crazy Soldier 2" : "Alternate Soldier 2");
|
||||
room->enableHotzone(_soldier3IsCrazy ? "Crazy Soldier 3" : "Alternate Soldier 3");
|
||||
room->enableHotzone("Menelaus' Tent");
|
||||
|
||||
_menelausTent1Ambient = AmbientAnim(kMenelausImage, "", kMenelausZ, PLACEHOLDER_MIN_INTERVAL,
|
||||
PLACEHOLDER_MAX_INTERVAL, AmbientAnim::KEEP_LOOP, kOffsetRightRoom,
|
||||
AmbientAnim::PAN_RIGHT);
|
||||
_menelausTent1Ambient.start();
|
||||
room->selectFrame(kMenelausImage, kMenelausZ, 0, kOffsetRightRoom);
|
||||
|
||||
Common::Array<AmbientAnim::AmbientDesc> tent2Ambients;
|
||||
tent2Ambients.push_back(AmbientAnim::AmbientDesc("t2070bb0", ""));
|
||||
tent2Ambients.push_back(AmbientAnim::AmbientDesc("t2070bm0", ""));
|
||||
tent2Ambients.push_back(AmbientAnim::AmbientDesc("t2070bn0", ""));
|
||||
tent2Ambients.push_back(AmbientAnim::AmbientDesc("t2070bo0", ""));
|
||||
|
||||
_menelausTent2Ambient = AmbientAnim(
|
||||
tent2Ambients, kMenelausZ, PLACEHOLDER_MIN_INTERVAL,
|
||||
PLACEHOLDER_MAX_INTERVAL, AmbientAnim::KEEP_LOOP, kOffsetRightRoom,
|
||||
AmbientAnim::PAN_RIGHT);
|
||||
_menelausTent2Ambient.start();
|
||||
room->selectFrame("t2070bb0", kMenelausZ, 0, kOffsetRightRoom);
|
||||
|
||||
room->selectFrame(Common::String::format("t2010o%c0",
|
||||
rnd.getRandomNumberRng('g', 'i')),
|
||||
141, 0, kOffsetRightRoom);
|
||||
|
||||
if (!persistent->_troyMessageIsDelivered) {
|
||||
room->enableHotzone("Helen");
|
||||
_helenAmbient = AmbientAnim(
|
||||
kHelenImage, "", kHelenZ, PLACEHOLDER_MIN_INTERVAL,
|
||||
PLACEHOLDER_MAX_INTERVAL, AmbientAnim::KEEP_LOOP,
|
||||
Common::Point(0, 0),
|
||||
AmbientAnim::PAN_LEFT);
|
||||
_helenAmbient.start();
|
||||
room->selectFrame(kHelenImage, kHelenZ, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (quest == kTroyQuest) {
|
||||
room->enableHotzone("Prisoner");
|
||||
if (!persistent->_troyPlayedOdysseus) {
|
||||
persistent->_troyPlayedOdysseus = true;
|
||||
}
|
||||
|
||||
room->selectFrame(kPrisoner, kPrisonerZ, 0, kOffsetRightRoom);
|
||||
_prisonerAmbient = AmbientAnim(kPrisoner, "t2130ea0", kPrisonerZ,
|
||||
PLACEHOLDER_MIN_INTERVAL, PLACEHOLDER_MAX_INTERVAL,
|
||||
AmbientAnim::KEEP_LOOP, kOffsetRightRoom,
|
||||
AmbientAnim::PAN_RIGHT);
|
||||
_prisonerAmbient.start();
|
||||
|
||||
if (persistent->_troyKeyAndDecreeState == Persistent::KEY_AND_DECREE_THROWN) {
|
||||
room->selectFrame(kKeyAndDecreeImage, kPrisonerZ, 0, kOffsetRightRoom);
|
||||
room->enableHotzone("Key And Decree");
|
||||
}
|
||||
|
||||
if (persistent->_troyKeyAndDecreeState >= Persistent::KEY_AND_DECREE_THROWN) {
|
||||
_prisonerCounter = 7;
|
||||
}
|
||||
}
|
||||
|
||||
room->playMusicLoop(persistent->_troyMessageIsDelivered || quest > kTroyQuest ? "t1010eb0" : "t1010ea0");
|
||||
|
||||
if (quest <= kTroyQuest && !persistent->_troyPlayFinish) {
|
||||
TextTable trLftAmb = TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(room->openFile("TrLftAmb.txt")), 9);
|
||||
_leftAmbients.readTableFileSFX(trLftAmb, AmbientAnim::PAN_LEFT);
|
||||
g_vm->addTimer(2803, 10000, -1);
|
||||
_leftAmbients.firstFrame();
|
||||
}
|
||||
|
||||
room->playAnimLoop("t2055bb0", 501, kOffsetRightRoom);
|
||||
room->playAnimLoop("t2055bc0", 501, kOffsetRightRoom);
|
||||
room->playAnimLoop("t2055bd0", 501, kOffsetRightRoom);
|
||||
room->playAnimLoop("t2055be0", 501, kOffsetRightRoom);
|
||||
|
||||
showCatacombStones();
|
||||
|
||||
if (persistent->_previousRoomId == kCatacombsRoom) {
|
||||
room->disableMouse();
|
||||
room->playVideo(selectReturnFromCatacombs(), 0, 10013, Common::Point(0, 216));
|
||||
}
|
||||
|
||||
if (persistent->_previousRoomId == kPriamRoom
|
||||
&& !persistent->_troyMessageIsDelivered
|
||||
&& quest == kTroyQuest) {
|
||||
room->playVideo("t1290bc0", 0, 10018,
|
||||
Common::Point(0, 216));
|
||||
}
|
||||
|
||||
if (persistent->_previousRoomId == kPriamRoom
|
||||
&& persistent->_troyMessageIsDelivered
|
||||
&& !persistent->_troyPlayedOdysseusCongrats
|
||||
&& quest == kTroyQuest) {
|
||||
persistent->_troyPlayedOdysseusCongrats = true;
|
||||
room->panRightAnim(kPlayOutro);
|
||||
room->disableMouse();
|
||||
showIdleOdysseus();
|
||||
}
|
||||
|
||||
if (persistent->_troyPlayFinish && quest == kTroyQuest) {
|
||||
persistent->_troyPlayFinish = false;
|
||||
room->disableMouse();
|
||||
_horseCounter = 2;
|
||||
room->playMusic("T1350mA0", kHorseCounter);
|
||||
room->playVideo("t1350ba0", 501, kHorseCounter, Common::Point(288, 211));
|
||||
|
||||
room->playAnimLoop("t1090ba0", 501);
|
||||
room->playAnimLoop("t1090bb0", 501);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Common::String selectReturnFromCatacombs() const {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
if (persistent->_catacombLastLevel == 2) {
|
||||
return "t1310bg0";
|
||||
}
|
||||
|
||||
switch (g_vm->getRnd().getRandomNumberRng(0, 4)) {
|
||||
case 0:
|
||||
return "t1310ba0";
|
||||
case 1:
|
||||
return persistent->_gender == kMale ? "t1310bb0" : "t1310bc0";
|
||||
case 2:
|
||||
return "t1310bd0";
|
||||
case 3:
|
||||
return persistent->_gender == kMale ? "t1310be0" : "t1310bf0";
|
||||
case 4:
|
||||
default:
|
||||
return "t1310bh0";
|
||||
}
|
||||
}
|
||||
void showCatacombStones() const {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
if (persistent->_troyMessageIsDelivered || persistent->_quest > kTroyQuest) {
|
||||
room->selectFrame("t1010og0", 115, 0);
|
||||
return;
|
||||
}
|
||||
room->selectFrame("t1280bc0", 212, (persistent->_troyCatacombCounter == 2) ? 4 : 0);
|
||||
room->selectFrame("t1280bb0", 211, (persistent->_troyCatacombCounter >= 1) ? 3 : 0);
|
||||
|
||||
if (persistent->_troyCatacombsUnlocked) {
|
||||
room->stopAnim("t1010of0");
|
||||
} else {
|
||||
room->selectFrame("t1010of0", 421, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void soldiersDisplay() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->selectFrame(soldier1(), kSoldier1Z, 0, Common::Point(980, 0));
|
||||
room->setLayerParallax(soldier1(), -340);
|
||||
room->selectFrame(soldier2(), kSolsier2Z, 0, kOffsetRightRoom);
|
||||
room->selectFrame(soldier3(), kSoldier3Z, 0, kOffsetRightRoom);
|
||||
}
|
||||
|
||||
void showIdleOdysseus() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->playAnimLoop(kOdysseusIdle, kOdysseusZ, Common::Point(800, 0));
|
||||
room->setLayerParallax(kOdysseusIdle, -160);
|
||||
}
|
||||
|
||||
void hideOdysseus() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->stopAnim(kOdysseusWithMessage);
|
||||
room->stopAnim(kOdysseusIdle);
|
||||
}
|
||||
|
||||
void showBricks() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->selectFrame(kBricksImage, 121, 0);
|
||||
room->enableHotzone("Bricks");
|
||||
}
|
||||
|
||||
Common::String soldier1() {
|
||||
return _soldier1IsCrazy ? "t2080ba0" : "t2120ba0";
|
||||
}
|
||||
|
||||
Common::String soldier2() {
|
||||
return _soldier2IsCrazy ? "t2090ba0" : "t2120bb0";
|
||||
}
|
||||
|
||||
Common::String soldier3() {
|
||||
return _soldier3IsCrazy ? "t2100ba0" : "t2120bc0";
|
||||
}
|
||||
|
||||
Common::String soldier1Sound() {
|
||||
return _soldier1IsCrazy ? "t2080ea0" : "t2120ea0";
|
||||
}
|
||||
|
||||
Common::String soldier2Sound() {
|
||||
return _soldier2IsCrazy ? "t2090ea0" : "t2120eb0";
|
||||
}
|
||||
|
||||
Common::String soldier3Sound() {
|
||||
return _soldier3IsCrazy ? "t2100ea0" : "t2120ec0";
|
||||
}
|
||||
|
||||
bool _philUseSecondInsistance;
|
||||
bool _philWarnedFishing;
|
||||
bool _soldier1IsCrazy;
|
||||
bool _soldier2IsCrazy;
|
||||
bool _soldier3IsCrazy;
|
||||
bool _prisonerTouched;
|
||||
|
||||
// TODO: read ambients from file
|
||||
AmbientAnim _soldier1Ambient;
|
||||
AmbientAnim _soldier2Ambient;
|
||||
AmbientAnim _soldier3Ambient;
|
||||
AmbientAnim _prisonerAmbient;
|
||||
AmbientAnim _menelausTent1Ambient;
|
||||
AmbientAnim _menelausTent2Ambient;
|
||||
AmbientAnim _bgSoldiersAmbient;
|
||||
AmbientAnim _helenAmbient;
|
||||
|
||||
AmbientAnimWeightedSet _leftAmbients;
|
||||
AnimClickables _trClick;
|
||||
int _prisonerCounter;
|
||||
int _bgSoldierCount;
|
||||
int _horseCounter;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeTroyHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new TroyHandler());
|
||||
}
|
||||
|
||||
}
|
||||
280
engines/hadesch/rooms/volcano.cpp
Normal file
280
engines/hadesch/rooms/volcano.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/* 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 {
|
||||
|
||||
static const char * eyeOfFateVolcanoToStyx[] = {
|
||||
"eye of fates too good to be true",
|
||||
"eye of fates never ever",
|
||||
"eye of fates this place again"
|
||||
};
|
||||
|
||||
enum {
|
||||
kBackgroundZ = 10000,
|
||||
kHelmetZ = 550
|
||||
};
|
||||
|
||||
class VolcanoHandler : public Handler {
|
||||
public:
|
||||
VolcanoHandler() {
|
||||
}
|
||||
|
||||
void handleClick(const Common::String &name) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
if (name == "argo") {
|
||||
room->disableMouse();
|
||||
g_vm->moveToRoom(kArgoRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "first boulder") {
|
||||
if (persistent->_quest != kMedusaQuest || persistent->_volcanoPuzzleState != Persistent::VOLCANO_NO_BOULDERS_THROWN) {
|
||||
_boulder1Anim.play(false);
|
||||
return;
|
||||
}
|
||||
|
||||
room->disableMouse();
|
||||
room->disableHotzone("first boulder");
|
||||
room->stopAnim("lever gem");
|
||||
room->stopAnim("pain still");
|
||||
room->stopAnim("panic still");
|
||||
_boulder1Anim.hide();
|
||||
room->playAnimWithSFX("first boulder falls", "first boulder falls sound", 400,
|
||||
PlayAnimParams::disappear(), 16023);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "second boulder") {
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
room->stopAnim(Common::String::format("lava flow %d", i));
|
||||
}
|
||||
room->stopAnim("second boulder");
|
||||
room->stopAnim("lever gem");
|
||||
_painAnim.hide();
|
||||
_panicAnim.hide();
|
||||
room->playVideo("plug volcano movie", 0, 16025);
|
||||
persistent->_volcanoPuzzleState = Persistent::VOLCANO_BOULDER_ON_VOLCANO;
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "helmet") {
|
||||
persistent->_volcanoPuzzleState = Persistent::VOLCANO_HELMET_SHOWN;
|
||||
g_vm->getHeroBelt()->placeToInventory(kHelmet);
|
||||
room->playSFX("skeleton revealed sfx");
|
||||
room->playAnimKeepLastFrame("helmet", kHelmetZ);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "near river styx" || name == "near river styx bg") {
|
||||
if (persistent->_quest != kRescuePhilQuest
|
||||
&& persistent->_volcanoToStyxCounter < ARRAYSIZE(eyeOfFateVolcanoToStyx)) {
|
||||
room->disableMouse();
|
||||
room->playVideo(eyeOfFateVolcanoToStyx[persistent->_volcanoToStyxCounter], 0, 16007, Common::Point(0, 216));
|
||||
persistent->_volcanoToStyxCounter++;
|
||||
} else {
|
||||
handleEvent(16007);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void handleEvent(int eventId) override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
switch (eventId) {
|
||||
case 16005:
|
||||
room->playAnimLoop("black lava pool", 500);
|
||||
AmbientAnim("bubble", "bubble sound", 500, 1000, 2000,
|
||||
AmbientAnim::DISAPPEAR, Common::Point(0, 0), AmbientAnim::PAN_ANY).start();
|
||||
g_vm->addTimer(16008, 4000); // TODO: pan down
|
||||
break;
|
||||
case 16007:
|
||||
room->stopAnim("gem overlay");
|
||||
room->playMusic("morph music", 16009);
|
||||
room->playVideo("morphing gems", 500, 16005, Common::Point(10, 10));
|
||||
break;
|
||||
case 16008:
|
||||
g_vm->moveToRoom(kRiverStyxRoom);
|
||||
break;
|
||||
case 16011:
|
||||
room->enableMouse();
|
||||
break;
|
||||
case 16016:
|
||||
if (!persistent->_volcanoHeyKid
|
||||
&& persistent->_hintsAreEnabled) {
|
||||
persistent->_volcanoHeyKid = true;
|
||||
room->playVideo("eye of fates hey kid", 0, 16011, Common::Point(0, 216));
|
||||
} else {
|
||||
room->enableMouse();
|
||||
}
|
||||
room->selectFrame("panic still", 424, 0);
|
||||
room->selectFrame("pain still", 425, 0);
|
||||
break;
|
||||
case 16023:
|
||||
squashedPanic();
|
||||
room->playAnimWithSFX("second boulder", "second boulder sound",
|
||||
401, PlayAnimParams::keepLastFrame(), 16024);
|
||||
persistent->_volcanoPuzzleState = Persistent::VOLCANO_SQUASHED_PANIC;
|
||||
break;
|
||||
case 16024:
|
||||
if (persistent->_hintsAreEnabled)
|
||||
room->playVideo("eye of fates on ta something", 0, 16011, Common::Point(0, 216));
|
||||
else
|
||||
room->enableMouse();
|
||||
room->enableHotzone("second boulder");
|
||||
break;
|
||||
case 16025:
|
||||
room->playSFX("volcanic rumble", 16028);
|
||||
room->selectFrame("volcano plug boulder", 400, 0);
|
||||
g_vm->addTimer(16026, 500);
|
||||
break;
|
||||
case 16026:
|
||||
// TODO: screen shake
|
||||
g_vm->addTimer(16030, 2500);
|
||||
break;
|
||||
case 16030:
|
||||
room->playSFX("explosion sound", 16033);
|
||||
g_vm->addTimer(16031, 1000);
|
||||
break;
|
||||
case 16031:
|
||||
room->playAnim("explosion", 400, PlayAnimParams::disappear(), 16032);
|
||||
break;
|
||||
// TODO: what's the difference?
|
||||
case 16032:
|
||||
case 16034:
|
||||
room->selectFrame("helmet", kHelmetZ, 0);
|
||||
room->enableHotzone("helmet");
|
||||
// TODO: 16035 timer
|
||||
// TODO: Or is it "lava flow sound 1"
|
||||
room->playAnimWithSFX("final lava flow", "final lava flow sound", 500,
|
||||
PlayAnimParams::loop());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prepareRoom() override {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Quest quest = persistent->_quest;
|
||||
room->loadHotZones("Volcano.hot", false);
|
||||
room->addStaticLayer("background", kBackgroundZ);
|
||||
room->selectFrame("gem overlay", 500, 0);
|
||||
room->selectFrame("argo", 1000, 0);
|
||||
room->enableHotzone("argo");
|
||||
if (quest == kMedusaQuest && persistent->_medisleShowFates && !persistent->_volcanoPainAndPanicIntroDone) {
|
||||
persistent->_volcanoPainAndPanicIntroDone = true;
|
||||
room->playVideo("pain and panic intro movie", 425, 16016, Common::Point(422, 165));
|
||||
}
|
||||
|
||||
if (quest < kMedusaQuest) {
|
||||
if (!persistent->isRoomVisited(kVolcanoRoom)) {
|
||||
room->playVideo("eye of fates we're rich", 0, 16010, Common::Point(0, 216));
|
||||
}
|
||||
}
|
||||
|
||||
if (quest < kMedusaQuest || (quest == kMedusaQuest && persistent->_volcanoPuzzleState == Persistent::VOLCANO_NO_BOULDERS_THROWN)) {
|
||||
room->selectFrame("lever gem", 450, 0);
|
||||
_boulder1Anim = AmbientAnim("first boulder", "first boulder sound", 400, 5000, 10000,
|
||||
AmbientAnim::KEEP_LOOP, Common::Point(0, 0),
|
||||
AmbientAnim::PAN_ANY);
|
||||
_boulder1Anim.start();
|
||||
room->selectFrame("second boulder", 401, 0);
|
||||
}
|
||||
|
||||
if (quest < kMedusaQuest || (quest == kMedusaQuest && persistent->_volcanoPuzzleState <= Persistent::VOLCANO_SQUASHED_PANIC)) {
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
room->playAnimWithSFX(Common::String::format("lava flow %d", i),
|
||||
Common::String::format("lava flow sound %d", i),
|
||||
500, PlayAnimParams::loop());
|
||||
}
|
||||
}
|
||||
|
||||
if (quest == kMedusaQuest && persistent->_volcanoPuzzleState == Persistent::VOLCANO_NO_BOULDERS_THROWN) {
|
||||
room->enableHotzone("first boulder");
|
||||
}
|
||||
|
||||
if (quest == kMedusaQuest && persistent->_volcanoPuzzleState == Persistent::VOLCANO_SQUASHED_PANIC) {
|
||||
room->enableHotzone("second boulder");
|
||||
squashedPanic();
|
||||
room->selectFrame("second boulder", 401, -1);
|
||||
}
|
||||
|
||||
if (quest == kMedusaQuest && persistent->_volcanoPuzzleState == Persistent::VOLCANO_BOULDER_ON_VOLCANO) {
|
||||
room->selectFrame("helmet", kHelmetZ, 0);
|
||||
room->enableHotzone("helmet");
|
||||
// TODO: Or is it "lava flow sound 1"
|
||||
room->playAnimWithSFX("final lava flow", "final lava flow sound", 500,
|
||||
PlayAnimParams::loop());
|
||||
room->selectFrame("volcano plug boulder", 400, 0);
|
||||
}
|
||||
|
||||
if (quest > kMedusaQuest || (quest == kMedusaQuest && persistent->_volcanoPuzzleState == Persistent::VOLCANO_HELMET_SHOWN)) {
|
||||
room->selectFrame("helmet", kHelmetZ, -1);
|
||||
room->playAnimWithSFX("final lava flow", "final lava flow sound", 500,
|
||||
PlayAnimParams::loop());
|
||||
room->selectFrame("volcano plug boulder", 400, 0);
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
AmbientAnim(Common::String::format("sparkle %d", i), "", 475, 5000, 10000,
|
||||
AmbientAnim::DISAPPEAR,
|
||||
Common::Point(0,0), AmbientAnim::PAN_ANY).start();
|
||||
}
|
||||
|
||||
if (quest == kRescuePhilQuest) {
|
||||
room->playMusic("theme music");
|
||||
} else {
|
||||
room->playMusicLoop("W1010eA0");
|
||||
}
|
||||
|
||||
room->playAnimLoop("waves", 900);
|
||||
room->enableHotzone("near river styx");
|
||||
room->enableHotzone("near river styx bg");
|
||||
|
||||
g_vm->getHeroBelt()->setColour(HeroBelt::kWarm);
|
||||
}
|
||||
|
||||
private:
|
||||
void squashedPanic() {
|
||||
// TODO: transcribe and change to speech sound type
|
||||
_painAnim = AmbientAnim("squashed pain", "squashed pain sound", 425, 5000, 10000, AmbientAnim::KEEP_LOOP,
|
||||
Common::Point(0, 0), AmbientAnim::PAN_ANY);
|
||||
_panicAnim = AmbientAnim("squashed panic", "squashed panic sound", 425, 5000, 10000, AmbientAnim::KEEP_LOOP,
|
||||
Common::Point(0, 0), AmbientAnim::PAN_ANY);
|
||||
_painAnim.start();
|
||||
_panicAnim.start();
|
||||
}
|
||||
|
||||
AmbientAnim _boulder1Anim;
|
||||
AmbientAnim _painAnim, _panicAnim;
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeVolcanoHandler() {
|
||||
return Common::SharedPtr<Hadesch::Handler>(new VolcanoHandler());
|
||||
}
|
||||
|
||||
}
|
||||
1133
engines/hadesch/rooms/walloffame.cpp
Normal file
1133
engines/hadesch/rooms/walloffame.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user