mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
1133 lines
34 KiB
C++
1133 lines
34 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Copyright 2020 Google
|
|
*
|
|
*/
|
|
#include "hadesch/hadesch.h"
|
|
#include "hadesch/video.h"
|
|
#include "hadesch/ambient.h"
|
|
|
|
namespace Hadesch {
|
|
|
|
static const char *kBackground = "background";
|
|
static const char *kHotspots = "HallTrph.hot";
|
|
static const char *kArgo = "argo";
|
|
|
|
static const char *kZeusYooHoo = "zeus yoo-hoo";
|
|
static const char *kZeusYellsPhil = "zeus yells phil";
|
|
static const char *kZeusVacationIsOver = "zeus vacation's over";
|
|
static const char *kZeusFineChoiceHero = "zeus fine choice hero";
|
|
static const char *kZeusShaftOfLightLeft = "zeus shaft of light left";
|
|
|
|
static const char *kRope = "rope";
|
|
static const char *kHerculesLintel = "hercules lintel";
|
|
|
|
static const char *kApplicationHotspots = "applicat.hot";
|
|
static const char *kApplication = "application";
|
|
static const char *kApplicationHeroine = "application heroine";
|
|
static const char *kApplicationHeroineHotzone = "heroine";
|
|
static const char *kApplicationHero = "application hero";
|
|
static const char *kApplicationHeroHotzone = "hero";
|
|
static const char *kApplicationEnterButton = "application enter button";
|
|
static const char *kApplicationEnterHotzone = "enter";
|
|
static const char *kApplicationBurntHole = "application burnt hole";
|
|
static const char *kApplicationText = "application text";
|
|
static const char *kApplicationHeroineText = "application heroine text";
|
|
static const char *kApplicationHeroText = "application hero text";
|
|
static const char *kApplicationNameText = "application name text";
|
|
static const char *kApplicationUnfurls = "application unfurls";
|
|
static const char *kApplicationUnfurlsSound = "application unfurls sound";
|
|
static const char *kApplicationRollsUpHeroine = "application rolls up heroine";
|
|
static const char *kApplicationRollsUpHero = "application rolls up hero";
|
|
static const char *kApplicationChooseHeroSound = "application choose hero sound";
|
|
static const char *kApplicationChooseHeroineSound = "application choose heroine sound";
|
|
|
|
static const char *kHadesBurstsIn = "hades bursts in";
|
|
static const char *kHadesNoMoreHeroes = "hades no more heroes got it";
|
|
static const char *kHadesNoMoreHeroines = "hades no more heroines got it";
|
|
|
|
#define kPhilWalksCenterToLeft "phil walks center to left"
|
|
#define kPhilSighsLeft "phil sighs left"
|
|
|
|
#define kPhilSnores "phil snores"
|
|
static const char *kPhilSnoresSound = "phil snores sound";
|
|
#define kPhilRollsOver "phil rolls over"
|
|
#define kPhilJumpsOffPillow "phil jumps off pillow"
|
|
static const char *kPhilOfCourseIdo = "phil of course i do";
|
|
static const char *kPhilWhatAHothead = "phil what a hothead";
|
|
static const char *kPhilTimeToGetStarted = "phil time to get started";
|
|
static const char *kPhilTreasuresOfHercules = "phil treasures of hercules";
|
|
static const char *kPhilBasicInformation = "phil basic information";
|
|
static const char *kPhilFlakyPlaster = "phil flaky plaster";
|
|
static const char *kPhilGettinStarted = "phil gettin started";
|
|
static const char *kPhilToolsOfTrade = "phil tools of the trade";
|
|
static const char *kPhilHeroBelt = "phil hero belt";
|
|
static const char *kPhilHeroBeltBuckle = "phil left belt buckle";
|
|
static const char *kPhilClickAnItem = "phil click an item";
|
|
static const char *kPhilHeroPowers = "phil hero powers";
|
|
static const char *kPhilRightNotches = "phil right notches";
|
|
static const char *kPhilEyeOfFates = "phil eye of fates";
|
|
static const char *kPhilBadNews = "phil bad news";
|
|
static const char *kPhilFirstQuest = "phil first quest";
|
|
#define kPhilTakesScroll "phil takes scroll"
|
|
#define kPhilDropsScroll "phil drops scroll"
|
|
static const char *kPhilDaedalusNeedsHelp = "phil daedalus needs help";
|
|
static const char *kPhilOffToCrete = "phil off to crete";
|
|
static const char *kPhilArgo = "phil argo";
|
|
#define kPhilTapsFootLeft "phil taps foot left"
|
|
|
|
#define kPhilYapsWithoutSound "phil yaps without sound"
|
|
|
|
static const char *kMugshot = "mug shot";
|
|
static const char *kMugshotSound = "mug shot sound";
|
|
static const char *kPanicAndPainSting = "pain and panic sting";
|
|
|
|
#define kHeroStatue "hero statue"
|
|
#define kHeroineStatue "heroine statue"
|
|
static const char *kScroll = "scroll";
|
|
static const char *kScrollSound = "scroll sound";
|
|
|
|
static const char *kHeroBelt = "hero belt";
|
|
static const char *kHeroBeltUpSound = "hero belt up sound";
|
|
static const char *kHeroBeltDownSound = "hero belt down sound";
|
|
|
|
static const char *herculesRoomElements[] = {
|
|
"mares",
|
|
"cattle",
|
|
"girdle",
|
|
"horns",
|
|
"hydra",
|
|
"bull",
|
|
"lion",
|
|
"apples",
|
|
"stables",
|
|
"boar",
|
|
"birds",
|
|
"cerberus",
|
|
"hercules",
|
|
};
|
|
|
|
static const char *allPhils[] = {
|
|
"phil walks left to center",
|
|
kPhilWalksCenterToLeft,
|
|
"phil faces backward left",
|
|
kPhilSighsLeft,
|
|
kPhilTapsFootLeft,
|
|
"phil dusts left",
|
|
"phil dusts center",
|
|
kPhilSnores,
|
|
kPhilRollsOver,
|
|
kPhilJumpsOffPillow,
|
|
kPhilYapsWithoutSound,
|
|
kPhilTakesScroll,
|
|
kPhilDropsScroll,
|
|
kPhilSighsLeft,
|
|
"phil scans left"
|
|
};
|
|
|
|
static const char *powerLevelNames[] = {
|
|
"thunderbolt",
|
|
"trojan horse",
|
|
"owl"
|
|
};
|
|
|
|
static const char *zeusComments[] = {
|
|
"minotaur",
|
|
"medusa",
|
|
"trojan armor",
|
|
"cyclops",
|
|
kHeroStatue,
|
|
kHeroineStatue
|
|
};
|
|
|
|
enum {
|
|
kPhilJokeEvent = 19003,
|
|
kPhilNewQuestScroll = 1019020,
|
|
kPhilBecomesIdle = 1019025,
|
|
kPhilJokeEventCleanup = 1019029,
|
|
kPhilRereadDropScroll = 1019030
|
|
};
|
|
|
|
enum {
|
|
kBackgroundZ = 10000,
|
|
kQuestOverlayZ = 925,
|
|
kMugshotZ = 900,
|
|
kScrollZ = 900,
|
|
kShaftOfLightLeftZ = 500,
|
|
kShaftOfLightRightZ = 500,
|
|
kTrophyZ = 450,
|
|
kPhilZ = 400,
|
|
kApplicationZ = 300,
|
|
kApplicationButtonZ = 299,
|
|
|
|
kSoundOnlyZ = 0,
|
|
|
|
// TODO: figure proper values
|
|
kHadesVideoZ = 0,
|
|
kHeroBeltZ = 0,
|
|
kGlowZ = 0
|
|
};
|
|
|
|
Common::String nameQuest() {
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
switch(persistent->_quest) {
|
|
case kNoQuest:
|
|
return "";
|
|
case kCreteQuest:
|
|
return "crete quest";
|
|
case kTroyQuest:
|
|
return "troy quest";
|
|
case kMedusaQuest:
|
|
return "medusa quest";
|
|
case kRescuePhilQuest:
|
|
return "rescue phil quest";
|
|
case kEndGame:
|
|
return "finished game";
|
|
// To silence warning
|
|
case kNumQuests:
|
|
return "";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
Common::String getShaftOfLightRight() {
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
if (persistent->_quest == kEndGame)
|
|
return "zeus shaft of light right";
|
|
return "zeus shaft of light " + nameQuest();
|
|
}
|
|
|
|
class WallOfFameHandler : public Handler {
|
|
public:
|
|
WallOfFameHandler() {
|
|
bool isNewGame = !g_vm->isRoomVisited(kWallOfFameRoom);
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
if (isNewGame) {
|
|
_gender = kUnknown;
|
|
_philIsOnTheRight = false;
|
|
} else {
|
|
_gender = persistent->_gender;
|
|
_philIsOnTheRight = true;
|
|
}
|
|
_isEnterMouseover = false;
|
|
_philWalkPhase = 0;
|
|
_philIsBusy = isNewGame;
|
|
_endGameOutro = false;
|
|
_applicationIsActive = false;
|
|
_philSaidDoingGreat = false;
|
|
}
|
|
|
|
void handleClick(const Common::String &hotname) override {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
Quest quest = persistent->_quest;
|
|
if (hotname == kApplicationHeroineHotzone) {
|
|
room->selectFrame(kApplicationHeroine, kApplicationButtonZ, 1);
|
|
room->selectFrame(kApplicationHero, kApplicationButtonZ, 0);
|
|
room->playSFX(kApplicationChooseHeroineSound);
|
|
_gender = kFemale;
|
|
computeEnter();
|
|
return;
|
|
}
|
|
if (hotname == kApplicationHeroHotzone) {
|
|
room->selectFrame(kApplicationHeroine, kApplicationButtonZ, 0);
|
|
room->selectFrame(kApplicationHero, kApplicationButtonZ, 1);
|
|
room->playSFX(kApplicationChooseHeroSound);
|
|
_gender = kMale;
|
|
computeEnter();
|
|
return;
|
|
}
|
|
if (hotname == kApplicationEnterHotzone) {
|
|
pressEnter();
|
|
return;
|
|
}
|
|
|
|
if (hotname == "argo") {
|
|
g_vm->moveToRoom(kArgoRoom);
|
|
return;
|
|
}
|
|
|
|
for (unsigned i = 0; i < sizeof (herculesRoomElements) / sizeof(herculesRoomElements[0]); i++)
|
|
if (hotname == herculesRoomElements[i]) {
|
|
room->disableMouse();
|
|
room->playAnimKeepLastFrame(hotname + " glow", kGlowZ);
|
|
room->playSFX("click");
|
|
room->playVideo("zeus " + hotname, kSoundOnlyZ, 1019026);
|
|
return;
|
|
}
|
|
|
|
for (unsigned power = 0; power < ARRAYSIZE(powerLevelNames); power++) {
|
|
if (hotname.matchString(Common::String::format("%s#", powerLevelNames[power]))) {
|
|
Common::String pl(powerLevelNames[power]);
|
|
room->playSFX("click");
|
|
if (strcmp(powerLevelNames[power], "thunderbolt") == 0)
|
|
room->playSFX("thunder sound");
|
|
zeusCommentRight("zeus " + pl);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < sizeof (zeusComments) / sizeof(zeusComments[0]); i++)
|
|
if (hotname == zeusComments[i]) {
|
|
room->playSFX("click");
|
|
zeusCommentRight(Common::String("zeus ") + hotname);
|
|
return;
|
|
}
|
|
|
|
if (hotname == "typhon") {
|
|
room->playSFX("click");
|
|
zeusCommentRight(persistent->_gender == kFemale ? "zeus typhon heroine" : "zeus typhon hero");
|
|
return;
|
|
}
|
|
|
|
if (hotname == "phil" && quest >= kCreteQuest && quest <= kMedusaQuest) {
|
|
philBecomesListening();
|
|
playPhilVideo(Common::String("phil reread ") + nameQuest(), kPhilRereadDropScroll,
|
|
Common::Point(22, 301));
|
|
return;
|
|
}
|
|
|
|
if (hotname == "hades note") {
|
|
g_vm->cancelTimer(19048);
|
|
handleEvent(19048);
|
|
return;
|
|
}
|
|
|
|
if (hotname == "hades note popup background" ||
|
|
hotname == "hades note popup background2") {
|
|
room->popHotZones();
|
|
room->setPannable(true);
|
|
room->stopAnim("hades note popup");
|
|
room->stopAnim("hades note text");
|
|
room->stopAnim("hades note");
|
|
if (persistent->_hintsAreEnabled) {
|
|
zeusCommentRight("zeus counting on ya");
|
|
}
|
|
room->setHotzoneEnabled("argo", true);
|
|
return;
|
|
}
|
|
|
|
if (hotname == "monster frieze") {
|
|
g_vm->moveToRoom(kMonsterPuzzle);
|
|
return;
|
|
}
|
|
|
|
if (hotname == "ferryman frieze") {
|
|
g_vm->moveToRoom(kFerrymanPuzzle);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
TODO:
|
|
MNSH: medusa frieze
|
|
MNSH: trojan horse frieze
|
|
MNSH: labyrinth frieze
|
|
*/
|
|
}
|
|
|
|
void handleMouseOver(const Common::String &hotname) override {
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
|
|
if (hotname == kApplicationEnterHotzone) {
|
|
_isEnterMouseover = true;
|
|
computeEnter();
|
|
return;
|
|
}
|
|
if (persistent->_quest <= kMedusaQuest
|
|
&& (hotname == "labyrinth frieze"
|
|
|| hotname == "trojan horse frieze")) {
|
|
if (_philIsBusy)
|
|
return;
|
|
_philIsBusy = true;
|
|
if (hotname == "labyrinth frieze")
|
|
playPhilVideo("phil labyrinth frieze", kPhilBecomesIdle, Common::Point(40, 324)); // state 51
|
|
else
|
|
playPhilVideo("phil trojan horse frieze", kPhilBecomesIdle, Common::Point(14, 320)); // state 52
|
|
return;
|
|
}
|
|
|
|
if (hotname.hasSuffix(" frieze")) {
|
|
zeusCommentRight("zeus " + hotname, true);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
void handleMouseOut(const Common::String &hotname) override {
|
|
if (hotname == kApplicationEnterHotzone) {
|
|
_isEnterMouseover = false;
|
|
computeEnter();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void handleEvent(int eventId) override {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
Quest quest = persistent->_quest;
|
|
switch (eventId) {
|
|
case 19004:
|
|
g_vm->addSkippableTimer(19005, 7000);
|
|
room->playAnim(kZeusShaftOfLightLeft, kShaftOfLightLeftZ,
|
|
PlayAnimParams::keepLastFrame().speed(500));
|
|
room->playVideo(kZeusYooHoo, kSoundOnlyZ, 19701); // zeus yoo-hoo
|
|
break;
|
|
case 19005:
|
|
playPhilAnimSFX(kPhilRollsOver, 1019001, Common::Point(-26, 2)); // state 1
|
|
break;
|
|
case 19011:
|
|
room->playVideo(kZeusVacationIsOver, kSoundOnlyZ, 19012);
|
|
break;
|
|
case 19012:
|
|
playPhilVideo(kPhilOfCourseIdo, 1019002, Common::Point(30, 304)); // state 5
|
|
break;
|
|
case 1019002:
|
|
displayPhilIdle();
|
|
room->playVideo(kZeusFineChoiceHero, kSoundOnlyZ, 19013);
|
|
break;
|
|
case 19013:
|
|
room->playAnim(kZeusShaftOfLightLeft, kShaftOfLightLeftZ, PlayAnimParams::disappear().backwards().speed(500));
|
|
room->setLayerEnabled(kRope, false);
|
|
playPhilVideo(kPhilBasicInformation, 1019003, Common::Point(-4, 2)); // state 6
|
|
break;
|
|
case 19016:
|
|
room->addStaticLayer(kApplicationBurntHole, kApplicationButtonZ);
|
|
room->playVideo(persistent->_gender == kFemale ? kHadesNoMoreHeroines : kHadesNoMoreHeroes,
|
|
kHadesVideoZ, 19018,
|
|
Common::Point(207, 103));
|
|
break;
|
|
case 19018:
|
|
applicationClose();
|
|
break;
|
|
case 19019:
|
|
playPhilVideo(kPhilWhatAHothead, 1019005, Common::Point(40, 327)); // state 7
|
|
break;
|
|
case 1019001:
|
|
cancelAllPhils();
|
|
playPhilAnimSFX(kPhilJumpsOffPillow, -1, Common::Point(-26, 2)); // state 4
|
|
room->playVideo(kZeusYellsPhil, kSoundOnlyZ, 19011);
|
|
break;
|
|
case 1019003:
|
|
room->setLayerEnabled(kRope, true);
|
|
displayPhilIdle();
|
|
room->playAnimWithSFX(
|
|
kApplicationUnfurls, kApplicationUnfurlsSound,
|
|
kApplicationZ, PlayAnimParams::disappear().speed(200), 1019004);
|
|
break;
|
|
case 1019004:
|
|
room->enableMouse();
|
|
startApplication();
|
|
break;
|
|
case 1019005:
|
|
playPhilVideo(kPhilTimeToGetStarted, 1019006,
|
|
Common::Point(36, 319)); // state 8
|
|
break;
|
|
case 1019006:
|
|
playPhilVideo(kPhilTreasuresOfHercules, 1019007,
|
|
Common::Point(40, 323)); // state 9
|
|
break;
|
|
case 1019007:
|
|
displayPhilIdle();
|
|
room->panRightAnim(1019008);
|
|
break;
|
|
case 1019008:
|
|
_philIsOnTheRight = true;
|
|
updatePhilHotzone();
|
|
playPhilVideo(kPhilFlakyPlaster, 1019009,
|
|
Common::Point(0, 0)); //state 10
|
|
break;
|
|
case 1019009:
|
|
playPhilVideo(kPhilGettinStarted, 1019010,
|
|
Common::Point(14, 324)); //state 11
|
|
break;
|
|
case 1019010:
|
|
room->disableHeroBelt();
|
|
playPhilVideo(kPhilToolsOfTrade, 1019011,
|
|
Common::Point(40, 320)); //state 12
|
|
break;
|
|
case 1019011:
|
|
displayPhilYap();
|
|
room->playAnimWithSFX(
|
|
kHeroBelt, kHeroBeltUpSound, kHeroBeltZ,
|
|
PlayAnimParams::keepLastFrame().partial(0, 9),
|
|
19022, kOffsetRightRoom);
|
|
break;
|
|
case 19022:
|
|
room->playVideo(kPhilHeroBelt, kPhilZ, 1019012); // state 13
|
|
break;
|
|
case 1019012:
|
|
room->playVideo(kPhilHeroBeltBuckle, kPhilZ, 1019013); // state 14
|
|
break;
|
|
case 1019013:
|
|
room->selectFrame(kHeroBelt, kHeroBeltZ, 10,
|
|
kOffsetRightRoom);
|
|
room->playSFX("hero belt items highlight sound");
|
|
room->playVideo(kPhilClickAnItem, kPhilZ, 1019014); // state 15
|
|
break;
|
|
case 1019014:
|
|
room->selectFrame(kHeroBelt, kHeroBeltZ, 11,
|
|
kOffsetRightRoom);
|
|
room->playSFX("hero belt items highlight sound");
|
|
room->playVideo(kPhilHeroPowers, kPhilZ, 1019015); // state 16
|
|
break;
|
|
case 1019015:
|
|
room->selectFrame(kHeroBelt, kHeroBeltZ, 12,
|
|
kOffsetRightRoom);
|
|
room->playSFX("hero belt items highlight sound");
|
|
g_vm->addTimer(19029, 4000);
|
|
room->playVideo(kPhilRightNotches, kPhilZ, 1019016); // state 17
|
|
break;
|
|
case 1019016:
|
|
g_vm->cancelTimer(19029);
|
|
room->playAnimWithSFX(
|
|
kHeroBelt, kHeroBeltDownSound, kHeroBeltZ, PlayAnimParams::disappear().partial(9, 0),
|
|
1019017, kOffsetRightRoom);
|
|
break;
|
|
case 1019017:
|
|
room->enableHeroBelt();
|
|
playPhilVideo(kPhilEyeOfFates, 1019018,
|
|
Common::Point(-8, 144)); // state 18
|
|
break;
|
|
case 1019018:
|
|
room->playSFX(kPanicAndPainSting);
|
|
playPhilVideo(kPhilBadNews, 1019019, Common::Point(14, 320)); // state 19
|
|
break;
|
|
case 1019019:
|
|
displayPhilIdle();
|
|
room->playAnimWithSFX(kMugshot, kMugshotSound,
|
|
kMugshotZ, PlayAnimParams::keepLastFrame(),
|
|
19006, kOffsetRightRoom);
|
|
break;
|
|
case 19006:
|
|
room->playAnimWithSFX(kScroll, kScrollSound,
|
|
kScrollZ, PlayAnimParams::keepLastFrame(),
|
|
19009, kOffsetRightRoom);
|
|
break;
|
|
case 19009:
|
|
playPhilVideo(kPhilFirstQuest, kPhilNewQuestScroll, Common::Point(40, 324)); // state 21
|
|
break;
|
|
case kPhilNewQuestScroll:
|
|
room->selectFrame(kScroll, kScrollZ, 0, kOffsetRightRoom);
|
|
room->playMusic("quest intro music");
|
|
playPhilAnimSFX(kPhilTakesScroll, 1019021); // state 22
|
|
break;
|
|
case 1019021:
|
|
switch (quest) {
|
|
default:
|
|
case kCreteQuest:
|
|
playPhilVideo(kPhilDaedalusNeedsHelp, 1019022, Common::Point(28, 312)); // state 24
|
|
break;
|
|
case kTroyQuest:
|
|
playPhilVideo("phil holy hera", 1019022, Common::Point(28, 312)); // state 29
|
|
break;
|
|
case kMedusaQuest:
|
|
playPhilVideo("phil who writes these things", 1019022, Common::Point(28, 312)); // state 36
|
|
break;
|
|
}
|
|
break;
|
|
case 1019022:
|
|
playPhilAnimSFX(kPhilDropsScroll, 1019023); // state 23
|
|
break;
|
|
case 1019023:
|
|
switch (quest) {
|
|
default:
|
|
case kCreteQuest:
|
|
playPhilVideo(kPhilOffToCrete, 1019024, Common::Point(40, 324)); // state 25
|
|
break;
|
|
case kTroyQuest:
|
|
playPhilVideo("phil hourly rates", 1019033, Common::Point(28, 312)); // state 30
|
|
break;
|
|
case kMedusaQuest:
|
|
playPhilVideo("phil off to seriphos", kPhilBecomesIdle, Common::Point(14, 320)); // state 37
|
|
}
|
|
break;
|
|
case 1019024:
|
|
playPhilVideo(kPhilArgo, kPhilBecomesIdle, Common::Point(40, 324)); // state 26
|
|
break;
|
|
case kPhilBecomesIdle:
|
|
philBecomesIdle();
|
|
break;
|
|
case 1019026:
|
|
room->enableMouse();
|
|
for (unsigned i = 0; i < sizeof (herculesRoomElements) / sizeof(herculesRoomElements[0]); i++)
|
|
room->stopAnim(Common::String(herculesRoomElements[i]) + " glow");
|
|
break;
|
|
case 1019027:
|
|
if (!_philIsBusy)
|
|
schedulePhilJoke();
|
|
break;
|
|
case 1019028:
|
|
g_vm->cancelTimer(kPhilJokeEvent);
|
|
break;
|
|
case kPhilJokeEvent: {
|
|
if (_philIsBusy) {
|
|
schedulePhilJoke();
|
|
break;
|
|
}
|
|
|
|
if (!_philSaidDoingGreat && quest != kCreteQuest) {
|
|
_philSaidDoingGreat = true;
|
|
// state 50
|
|
playPhilVideo("phil doing great", kPhilJokeEventCleanup,
|
|
Common::Point(14, 320));
|
|
break;
|
|
}
|
|
|
|
int rnd = g_vm->getRnd().getRandomNumberRng(1, 10);
|
|
debug("rnd = %d", rnd);
|
|
switch(rnd) {
|
|
case 1:
|
|
// state 40
|
|
playPhilVideo("phil break time over", kPhilJokeEventCleanup,
|
|
Common::Point(40, 320));
|
|
break;
|
|
case 2:
|
|
// state 41
|
|
playPhilVideo("phil back to work", kPhilJokeEventCleanup,
|
|
Common::Point(14, 320));
|
|
break;
|
|
case 3:
|
|
// state 42
|
|
playPhilVideo("phil lets go", kPhilJokeEventCleanup,
|
|
Common::Point(16, 320));
|
|
break;
|
|
case 4:
|
|
// state 43
|
|
playPhilAnimWithoutSound("phil scans left", kPhilJokeEventCleanup,
|
|
Common::Point(-26, 2));
|
|
break;
|
|
case 5:
|
|
// state 44, go right
|
|
if (_philWalkPhase == 3) {
|
|
schedulePhilJoke();
|
|
break;
|
|
}
|
|
_philWalkPhase++;
|
|
updatePhilHotzone();
|
|
playPhilAnimSFX("phil walks left to center", kPhilJokeEventCleanup,
|
|
Common::Point(-166, +2));
|
|
break;
|
|
case 6:
|
|
// state 45, go left
|
|
if (_philWalkPhase == 0) {
|
|
schedulePhilJoke();
|
|
break;
|
|
}
|
|
_philWalkPhase--;
|
|
updatePhilHotzone();
|
|
playPhilAnimSFX(kPhilWalksCenterToLeft, kPhilJokeEventCleanup);
|
|
break;
|
|
case 7:
|
|
// state 46
|
|
playPhilAnimSFX("phil faces backward left", kPhilJokeEventCleanup, Common::Point(-640, 0));
|
|
break;
|
|
case 8:
|
|
// state 47
|
|
playPhilAnimSFX(kPhilSighsLeft, kPhilJokeEventCleanup);
|
|
break;
|
|
case 9:
|
|
// state 48
|
|
playPhilAnimSFX(kPhilTapsFootLeft, kPhilJokeEventCleanup);
|
|
break;
|
|
case 10:
|
|
// state 49, dusts
|
|
switch (_philWalkPhase) {
|
|
case 0:
|
|
playPhilAnimSFX("phil dusts left", kPhilJokeEventCleanup);
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
playPhilAnimSFX("phil dusts center", kPhilJokeEventCleanup, Common::Point(-166, 2));
|
|
break;
|
|
case 3:
|
|
schedulePhilJoke();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 19029:
|
|
room->selectFrame(kHeroBelt, kHeroBeltZ, 13,
|
|
kOffsetRightRoom);
|
|
room->playSFX("hero belt items highlight sound");
|
|
break;
|
|
case kPhilJokeEventCleanup:
|
|
schedulePhilJoke();
|
|
break;
|
|
case 19045:
|
|
philBecomesIdle();
|
|
room->playAnim(
|
|
getShaftOfLightRight(),
|
|
kShaftOfLightRightZ,
|
|
PlayAnimParams::disappear().speed(500));
|
|
room->enableMouse();
|
|
break;
|
|
case kPhilRereadDropScroll:
|
|
playPhilAnimSFX(kPhilDropsScroll, 19047);
|
|
break;
|
|
case 19047:
|
|
philBecomesIdle();
|
|
break;
|
|
case 19048:
|
|
room->disableMouse();
|
|
room->disableHotzone("hades note");
|
|
room->enableHotzone("hades note popup");
|
|
room->disableHotzone("hades note popup background");
|
|
room->enableHotzone("hades note popup background2");
|
|
room->selectFrame("hades note popup", 150, 0, kOffsetRightRoom);
|
|
room->selectFrame("hades note text", 149, 0, kOffsetRightRoom);
|
|
room->playVideo("hades reads note", 0, 19049);
|
|
break;
|
|
case 19049:
|
|
room->enableMouse();
|
|
break;
|
|
case 19051:
|
|
_hercules = AmbientAnim("herc blinks", "", 0, 2000, 5000,
|
|
AmbientAnim::KEEP_LOOP, kOffsetRightRoom,
|
|
AmbientAnim::PAN_ANY);
|
|
_hercules.start();
|
|
playPhilVideo("phil that was awesome", 19052,
|
|
Common::Point(14, 320)); // state 56
|
|
break;
|
|
case 19052:
|
|
philBecomesListening();
|
|
room->fadeOut(250, 19053);
|
|
break;
|
|
case 19053:
|
|
_hercules.hide();
|
|
room->resetFade();
|
|
room->disableHeroBelt();
|
|
room->resetLayers();
|
|
room->disableMouse();
|
|
room->setPannable(false);
|
|
room->addStaticLayer("black background", kBackgroundZ, kOffsetRightRoom);
|
|
room->playVideo("hades eye of fates", 0, 19054, kOffsetRightRoom);
|
|
room->playSFX("herc laughs");
|
|
room->playSFX("phil laughs");
|
|
break;
|
|
case 19054:
|
|
g_vm->moveToRoom(kCreditsRoom);
|
|
break;
|
|
case 1019031:
|
|
_philWalkPhase--;
|
|
updatePhilHotzone();
|
|
playPhilAnimSFX(kPhilWalksCenterToLeft, 1019032);
|
|
break;
|
|
case 1019032:
|
|
playPhilVideo("phil lots more to do", kPhilNewQuestScroll, Common::Point(14, 312)); // state 28
|
|
break;
|
|
case 1019033:
|
|
playPhilVideo(persistent->_gender == kFemale ? "phil go to it heroine" : "phil go to it hero",
|
|
1019034, Common::Point(40, 324)); // state 32/31
|
|
break;
|
|
case 1019034:
|
|
playPhilVideo("phil off to troy", kPhilBecomesIdle, Common::Point(40, 324)); // state 33
|
|
break;
|
|
case 1019035:
|
|
playPhilVideo("phil sneaky devil", 1019036, Common::Point(40, 324)); // state 34
|
|
break;
|
|
case 1019036:
|
|
_philWalkPhase--;
|
|
updatePhilHotzone();
|
|
playPhilAnimSFX(kPhilWalksCenterToLeft, 1019037);
|
|
break;
|
|
case 1019037:
|
|
playPhilVideo("phil good work", kPhilNewQuestScroll, Common::Point(40, 324)); // state 35
|
|
break;
|
|
case 19900:
|
|
if (_gender != kUnknown && !_heroName.empty()) {
|
|
break;
|
|
}
|
|
if (!_heroName.empty()) {
|
|
room->playVideo("phil pick a statue", 0, 19901);
|
|
break;
|
|
}
|
|
|
|
if (g_vm->getRnd().getRandomBit()) {
|
|
room->playVideo("phil not a mind reader", 0, 19901);
|
|
} else {
|
|
room->playVideo("phil type in your name", 0, 19901);
|
|
}
|
|
break;
|
|
case 19901:
|
|
g_vm->addTimer(19900, 12000);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void handleKeypress(uint32 code) override {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
if (_applicationIsActive && room->isMouseEnabled()) {
|
|
if (_heroName.size() < 18 &&
|
|
(('a' <= code && code <= 'z')
|
|
|| ('A' <= code && code <= 'Z')
|
|
|| code == '.' || code == '-' || code == ' ')
|
|
&& room->computeStringWidth("application sign-in text", _heroName + code) <= 318) {
|
|
_heroName += code;
|
|
room->playSFX("application click");
|
|
renderNameInApplication();
|
|
computeEnter();
|
|
return;
|
|
}
|
|
|
|
if (_heroName.size() > 0 && code == '\b') {
|
|
_heroName.deleteLastChar();
|
|
room->playSFX("application click");
|
|
renderNameInApplication();
|
|
computeEnter();
|
|
return;
|
|
}
|
|
|
|
if (_heroName.size() > 0 && (code == '\r' || code == '\n')) {
|
|
pressEnter();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void prepareRoom() override {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
bool newGame = !g_vm->isRoomVisited(kWallOfFameRoom);
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
room->loadHotZones(kHotspots, false);
|
|
room->addStaticLayer(kBackground, kBackgroundZ);
|
|
room->addStaticLayer(kHerculesLintel, 900);
|
|
room->addStaticLayer(kRope, 900);
|
|
Quest quest = persistent->_quest;
|
|
if (quest != kEndGame) {
|
|
room->addStaticLayer(kArgo, 950, kOffsetRightRoom);
|
|
room->enableHotzone("argo");
|
|
room->playAnimLoop("argo ambient", 949, kOffsetRightRoom);
|
|
}
|
|
room->playAnimLoop("water ambient", 975, kOffsetRightRoom);
|
|
room->playAnimLoop("hercules hall ambients", 900);
|
|
room->addStaticLayer(nameQuest() + " overlay", kQuestOverlayZ, kOffsetRightRoom);
|
|
if (quest == kEndGame) {
|
|
const char *heroStatue = persistent->_gender == kFemale ? kHeroineStatue : kHeroStatue;
|
|
room->selectFrame(heroStatue, 450, 0, kOffsetRightRoom);
|
|
room->enableHotzone(heroStatue);
|
|
room->setHotZoneOffset(heroStatue, kOffsetRightRoom);
|
|
room->enableHotzone("monster frieze");
|
|
room->enableHotzone("ferryman frieze");
|
|
room->enableHotzone("cyclops");
|
|
room->enableHotzone("typhon");
|
|
}
|
|
|
|
if (quest > kMedusaQuest) {
|
|
room->enableHotzone("medusa frieze");
|
|
room->enableHotzone("medusa");
|
|
}
|
|
if (quest > kTroyQuest) {
|
|
room->enableHotzone("trojan horse frieze");
|
|
room->enableHotzone("trojan armor");
|
|
}
|
|
if (quest > kCreteQuest) {
|
|
room->enableHotzone("labyrinth frieze");
|
|
room->enableHotzone("minotaur");
|
|
}
|
|
|
|
if (quest < kRescuePhilQuest) {
|
|
room->enableHotzone("phil");
|
|
room->setHotZoneOffset("phil", kOffsetRightRoom);
|
|
}
|
|
|
|
// Hercules room
|
|
for (unsigned i = 0; i < sizeof (herculesRoomElements) / sizeof(herculesRoomElements[0]); i++)
|
|
room->enableHotzone(herculesRoomElements[i]);
|
|
|
|
for (unsigned power = 0; power < sizeof(powerLevelNames) / sizeof(powerLevelNames[0]); power++) {
|
|
int level = persistent->_powerLevel[power];
|
|
if (level > 0) {
|
|
int off = (4 - level) * 30;
|
|
Common::String hz = Common::String::format("%s%d", powerLevelNames[power], level);
|
|
room->playAnim(powerLevelNames[power], kTrophyZ,
|
|
PlayAnimParams::loop().partial(off, off + 29),
|
|
-1, kOffsetRightRoom);
|
|
room->enableHotzone(hz);
|
|
room->setHotZoneOffset(hz, kOffsetRightRoom);
|
|
}
|
|
}
|
|
|
|
room->selectFrame(kScroll, kScrollZ, 0, kOffsetRightRoom);
|
|
room->setPannable(true);
|
|
if (newGame) {
|
|
room->disableMouse();
|
|
room->playAnimWithSFX(kPhilSnores, kPhilSnoresSound, kPhilZ, PlayAnimParams::loop()); //state 0
|
|
room->selectFrame(kMugshot, kMugshotZ, 0, kOffsetRightRoom);
|
|
g_vm->addSkippableTimer(19004, 4000);
|
|
} else if (persistent->_doQuestIntro) {
|
|
persistent->_doQuestIntro = false;
|
|
room->disableMouse();
|
|
switch (quest) {
|
|
case kTroyQuest:
|
|
_philWalkPhase = 1;
|
|
playPhilVideo("phil congrats trapped minotaur", 1019031, Common::Point(14, 320)); // state 27
|
|
room->playMusic("done crete quest theme");
|
|
break;
|
|
case kMedusaQuest:
|
|
_philWalkPhase = 1;
|
|
playPhilVideo("phil dances", 1019035, Common::Point(36, 257)); // state 38
|
|
room->playMusic("done troy quest theme");
|
|
break;
|
|
case kRescuePhilQuest:
|
|
g_vm->addTimer(19048, 5000, 1);
|
|
room->pushHotZones("HTNote.HOT", true, kOffsetRightRoom);
|
|
room->disableHotzone("hades note popup background2");
|
|
room->setPannable(false);
|
|
room->enableMouse();
|
|
room->playAnimWithSFX("hades note", "hades note sound", 150, PlayAnimParams::loop(),
|
|
EventHandlerWrapper(), kOffsetRightRoom);
|
|
room->setHotzoneEnabled("argo", false);
|
|
room->playMusic("done medusa quest theme");
|
|
_endGameOutro = true;
|
|
break;
|
|
|
|
case kNumQuests: // To silence warning
|
|
case kEndGame:
|
|
case kNoQuest:
|
|
_philWalkPhase = 1;
|
|
philBecomesListening();
|
|
room->playVideo("herc congratulates", 0, 19051, kOffsetRightRoom);
|
|
break;
|
|
case kCreteQuest:
|
|
break;
|
|
}
|
|
|
|
room->panRightInstant();
|
|
} else {
|
|
if (persistent->_quest < kRescuePhilQuest)
|
|
schedulePhilJoke();
|
|
room->panRightInstant();
|
|
}
|
|
|
|
renderLintel();
|
|
|
|
room->playMusicLoop("C1010eA0");
|
|
|
|
g_vm->getHeroBelt()->setColour(HeroBelt::kWarm);
|
|
}
|
|
|
|
~WallOfFameHandler() override {}
|
|
|
|
private:
|
|
void clearNameInApplication() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
// One character more to handle possible backspace clicks.
|
|
room->hideString("application sign-in text", _heroName.size() + 1);
|
|
}
|
|
|
|
void renderNameInApplication() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
clearNameInApplication();
|
|
room->renderString("application sign-in text", _heroName, Common::Point(202, 338), 298);
|
|
}
|
|
|
|
void philBecomesIdle() {
|
|
_philIsBusy = false;
|
|
schedulePhilJoke();
|
|
}
|
|
|
|
void philBecomesBusy() {
|
|
_philIsBusy = true;
|
|
g_vm->cancelTimer(kPhilJokeEvent);
|
|
}
|
|
|
|
void philBecomesListening() {
|
|
philBecomesBusy();
|
|
displayPhilIdle();
|
|
}
|
|
|
|
void schedulePhilJoke() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
displayPhilIdle();
|
|
room->enableMouse();
|
|
g_vm->addTimer(kPhilJokeEvent, g_vm->getRnd().getRandomNumberRng(10000, 15000));
|
|
room->setUserPanCallback(1019028, -1, -1, 1019027);
|
|
}
|
|
|
|
void pressEnter() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
|
|
// originally event 19015
|
|
if (_gender != kUnknown /*&& name != ""*/) {
|
|
persistent->_gender = _gender;
|
|
persistent->_heroName = _heroName;
|
|
room->disableMouse();
|
|
// Or should it be music? Unclear to me
|
|
room->playSFX("hades evil intro theme");
|
|
room->playVideo(kHadesBurstsIn, kHadesVideoZ, 19016);
|
|
}
|
|
}
|
|
|
|
void zeusCommentRight(const Common::String &id, bool mouseIsEnabled = false) {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
if (!mouseIsEnabled)
|
|
room->disableMouse();
|
|
room->playVideo(id, kSoundOnlyZ, 19045);
|
|
room->playAnim(
|
|
getShaftOfLightRight(), kShaftOfLightRightZ, PlayAnimParams::keepLastFrame().speed(500), -1, kOffsetRightRoom);
|
|
philBecomesListening();
|
|
}
|
|
|
|
Common::Point getPhilBase() const {
|
|
Common::Point v;
|
|
if (_philIsOnTheRight)
|
|
v = Common::Point(640, 0);
|
|
else
|
|
v = Common::Point(26, -2);
|
|
if (_philWalkPhase >= 0 && _philWalkPhase <= 3)
|
|
v += Common::Point(166 * _philWalkPhase, -2 * _philWalkPhase);
|
|
return v;
|
|
}
|
|
void playPhilVideo(const Common::String &name, int callback, const Common::Point videoOffset) {
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
cancelAllPhils();
|
|
if (persistent->_quest == kRescuePhilQuest)
|
|
return;
|
|
g_vm->getVideoRoom()->playVideo(name, kPhilZ, callback,
|
|
videoOffset + getPhilBase());
|
|
}
|
|
|
|
void playPhilAnimSFX(const Common::String &name,
|
|
int callback, const Common::Point videoOffset = Common::Point(0, 0)) {
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
cancelAllPhils();
|
|
if (persistent->_quest == kRescuePhilQuest)
|
|
return;
|
|
g_vm->getVideoRoom()->playAnimWithSFX(
|
|
name, name + " sound", kPhilZ, PlayAnimParams::keepLastFrame(), callback,
|
|
videoOffset + getPhilBase());
|
|
}
|
|
|
|
void playPhilAnimWithoutSound(const Common::String &name,
|
|
int callback, const Common::Point videoOffset = Common::Point(0, 0)) {
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
cancelAllPhils();
|
|
if (persistent->_quest == kRescuePhilQuest)
|
|
return;
|
|
g_vm->getVideoRoom()->playAnimKeepLastFrame(
|
|
name, kPhilZ, callback,
|
|
videoOffset + getPhilBase());
|
|
}
|
|
|
|
void displayPhilIdle() {
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
cancelAllPhils();
|
|
if (persistent->_quest == kRescuePhilQuest
|
|
|| (!_endGameOutro && persistent->_quest == kEndGame))
|
|
return;
|
|
g_vm->getVideoRoom()->selectFrame(kPhilSighsLeft, kPhilZ, 0,
|
|
getPhilBase());
|
|
}
|
|
|
|
void displayPhilYap() {
|
|
cancelAllPhils();
|
|
g_vm->getVideoRoom()->playAnimLoop(
|
|
kPhilYapsWithoutSound, kPhilZ, Common::Point(640-166, 0));
|
|
}
|
|
|
|
void computeEnter() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
if (_gender == kUnknown /*|| name == ""*/) {
|
|
room->selectFrame(kApplicationEnterButton, kApplicationButtonZ, 0);
|
|
room->disableHotzone(kApplicationEnterHotzone);
|
|
return;
|
|
}
|
|
room->selectFrame(kApplicationEnterButton, kApplicationButtonZ, _isEnterMouseover ? 2 : 1);
|
|
room->enableHotzone(kApplicationEnterHotzone);
|
|
}
|
|
|
|
void startApplication() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
room->addStaticLayer(kApplication, kApplicationZ);
|
|
room->selectFrame(kApplicationHeroine, kApplicationButtonZ, 0); // heroine
|
|
room->selectFrame(kApplicationHero, kApplicationButtonZ, 0); // hero
|
|
room->selectFrame(kApplicationEnterButton, kApplicationButtonZ, 0);
|
|
room->addStaticLayer(kApplicationText, kApplicationButtonZ);
|
|
room->addStaticLayer(kApplicationHeroineText, kApplicationButtonZ);
|
|
room->addStaticLayer(kApplicationHeroText, kApplicationButtonZ);
|
|
room->addStaticLayer(kApplicationNameText, kApplicationButtonZ);
|
|
room->pushHotZones(kApplicationHotspots);
|
|
room->disableHotzone(kApplicationEnterHotzone);
|
|
_applicationIsActive = true;
|
|
g_vm->addTimer(19900, 5000);
|
|
}
|
|
|
|
void applicationClose() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
room->setLayerEnabled(kApplication, false);
|
|
room->setLayerEnabled(kApplicationHeroine, false);
|
|
room->setLayerEnabled(kApplicationHero, false);
|
|
room->setLayerEnabled(kApplicationEnterButton, false);
|
|
room->setLayerEnabled(kApplicationText, false);
|
|
room->setLayerEnabled(kApplicationHeroineText, false);
|
|
room->setLayerEnabled(kApplicationHeroText, false);
|
|
room->setLayerEnabled(kApplicationNameText, false);
|
|
room->setLayerEnabled(kApplicationBurntHole, false);
|
|
room->setLayerEnabled(kApplicationUnfurls, false);
|
|
room->popHotZones();
|
|
clearNameInApplication();
|
|
_applicationIsActive = false;
|
|
// TODO: does it have any sound?
|
|
room->playAnim(persistent->_gender == kFemale ? kApplicationRollsUpHeroine : kApplicationRollsUpHero,
|
|
kApplicationZ, PlayAnimParams::disappear(), 19019);
|
|
renderLintel();
|
|
}
|
|
|
|
void cancelAllPhils() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(allPhils); i++)
|
|
room->stopAnim(allPhils[i]);
|
|
}
|
|
|
|
void updatePhilHotzone() {
|
|
g_vm->getVideoRoom()->setHotZoneOffset(
|
|
"phil", getPhilBase());
|
|
}
|
|
|
|
void renderLintel() {
|
|
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
|
Persistent *persistent = g_vm->getPersistent();
|
|
room->renderStringCentered("player lintel text", persistent->_heroName, Common::Point(942, 19), 900, -1);
|
|
}
|
|
|
|
Gender _gender;
|
|
bool _isEnterMouseover;
|
|
bool _philIsOnTheRight;
|
|
int _philWalkPhase;
|
|
bool _philIsBusy;
|
|
Common::U32String _heroName;
|
|
AmbientAnim _hercules;
|
|
bool _endGameOutro;
|
|
bool _applicationIsActive;
|
|
bool _philSaidDoingGreat;
|
|
};
|
|
|
|
Common::SharedPtr<Hadesch::Handler> makeWallOfFameHandler() {
|
|
return Common::SharedPtr<Hadesch::Handler>(new WallOfFameHandler());
|
|
}
|
|
|
|
}
|