Initial commit
This commit is contained in:
4
engines/hadesch/POTFILES
Normal file
4
engines/hadesch/POTFILES
Normal file
@@ -0,0 +1,4 @@
|
||||
engines/hadesch/hadesch.cpp
|
||||
engines/hadesch/rooms/daedalus.cpp
|
||||
engines/hadesch/rooms/medisle.cpp
|
||||
engines/hadesch/rooms/troy.cpp
|
||||
360
engines/hadesch/ambient.cpp
Normal file
360
engines/hadesch/ambient.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
/* 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 {
|
||||
class AmbientAnimStarter : public EventHandler {
|
||||
public:
|
||||
void operator()() override {
|
||||
_anim.play(true);
|
||||
}
|
||||
|
||||
AmbientAnimStarter(AmbientAnim anim) {
|
||||
_anim = anim;
|
||||
}
|
||||
private:
|
||||
AmbientAnim _anim;
|
||||
};
|
||||
|
||||
class AmbientAnimPlayEnded : public EventHandler {
|
||||
public:
|
||||
void operator()() override {
|
||||
_anim.playFinished(_reschedule);
|
||||
}
|
||||
|
||||
AmbientAnimPlayEnded(AmbientAnim anim, bool reschedule) {
|
||||
_anim = anim;
|
||||
_reschedule = reschedule;
|
||||
}
|
||||
private:
|
||||
AmbientAnim _anim;
|
||||
bool _reschedule;
|
||||
};
|
||||
|
||||
AmbientAnim::AmbientAnim(const Common::String &animName,
|
||||
const Common::String &sound, int zValue,
|
||||
int minint, int maxint, AnimType loop,
|
||||
Common::Point offset, PanType pan) {
|
||||
_internal = Common::SharedPtr<AmbiantAnimInternal>(
|
||||
new AmbiantAnimInternal());
|
||||
_internal->_descs.push_back(AmbientDesc(animName, sound));
|
||||
_internal->_minInterval = minint;
|
||||
_internal->_maxInterval = maxint;
|
||||
_internal->_offset = offset;
|
||||
_internal->_loopType = loop;
|
||||
_internal->_zValue = zValue;
|
||||
_internal->_paused = false;
|
||||
_internal->_playing = false;
|
||||
_internal->_pan = pan;
|
||||
_internal->_isFwd = true;
|
||||
}
|
||||
|
||||
AmbientAnim::AmbientAnim(const Common::Array<AmbientDesc> &descs, int zValue,
|
||||
int minint, int maxint, AnimType loop,
|
||||
Common::Point offset, PanType pan) {
|
||||
_internal = Common::SharedPtr<AmbiantAnimInternal>(
|
||||
new AmbiantAnimInternal());
|
||||
_internal->_descs = descs;
|
||||
_internal->_minInterval = minint;
|
||||
_internal->_maxInterval = maxint;
|
||||
_internal->_offset = offset;
|
||||
_internal->_loopType = loop;
|
||||
_internal->_zValue = zValue;
|
||||
_internal->_paused = false;
|
||||
_internal->_playing = false;
|
||||
_internal->_pan = pan;
|
||||
_internal->_isFwd = true;
|
||||
}
|
||||
|
||||
AmbientAnim::AmbientAnim() {
|
||||
_internal = Common::SharedPtr<AmbiantAnimInternal>(
|
||||
new AmbiantAnimInternal());
|
||||
_internal->_minInterval = 0;
|
||||
_internal->_maxInterval = 0;
|
||||
_internal->_loopType = KEEP_LOOP;
|
||||
_internal->_zValue = 0;
|
||||
_internal->_paused = false;
|
||||
_internal->_playing = false;
|
||||
_internal->_isFwd = true;
|
||||
}
|
||||
|
||||
void AmbientAnim::pause() {
|
||||
_internal->_paused = true;
|
||||
}
|
||||
|
||||
void AmbientAnim::unpause() {
|
||||
_internal->_paused = false;
|
||||
}
|
||||
|
||||
void AmbientAnim::hide() {
|
||||
pause();
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->stopAnim(_internal->_descs[0]._animName);
|
||||
_internal->_playing = false;
|
||||
_internal->_paused = true;
|
||||
}
|
||||
|
||||
void AmbientAnim::selectFirstFrame() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
room->selectFrame(_internal->_descs[0]._animName, _internal->_zValue,
|
||||
0, _internal->_offset);
|
||||
}
|
||||
|
||||
void AmbientAnim::unpauseAndFirstFrame() {
|
||||
unpause();
|
||||
selectFirstFrame();
|
||||
}
|
||||
|
||||
bool AmbientAnim::isPanOK() {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
if (_internal->_pan == PAN_ANY)
|
||||
return true;
|
||||
|
||||
if (_internal->_pan == PAN_LEFT && room->isPanLeft())
|
||||
return true;
|
||||
|
||||
if (_internal->_pan == PAN_RIGHT && room->isPanRight())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AmbientAnim::isReady() {
|
||||
return !_internal->_paused && !_internal->_playing && isPanOK();
|
||||
}
|
||||
|
||||
void AmbientAnim::play(bool reschedule) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
if (_internal->_paused || _internal->_playing || !isPanOK()) {
|
||||
if (reschedule)
|
||||
schedule();
|
||||
return;
|
||||
}
|
||||
_internal->_playing = true;
|
||||
unsigned variant = 0;
|
||||
if (_internal->_descs.size() > 1) {
|
||||
variant = g_vm->getRnd().getRandomNumberRng(0, _internal->_descs.size() - 1);
|
||||
for (unsigned i = 0; i < _internal->_descs.size(); i++) {
|
||||
if (i != variant)
|
||||
room->stopAnim(_internal->_descs[i]._animName);
|
||||
}
|
||||
}
|
||||
|
||||
PlayAnimParams params = PlayAnimParams::disappear();
|
||||
|
||||
switch (_internal->_loopType) {
|
||||
case DISAPPEAR:
|
||||
params = PlayAnimParams::disappear();
|
||||
break;
|
||||
case KEEP_LOOP:
|
||||
params = PlayAnimParams::keepLastFrame();
|
||||
break;
|
||||
case BACK_AND_FORTH:
|
||||
if (_internal->_isFwd) {
|
||||
params = PlayAnimParams::keepLastFrame();
|
||||
} else {
|
||||
params = PlayAnimParams::disappear().backwards();
|
||||
}
|
||||
_internal->_isFwd = !_internal->_isFwd;
|
||||
break;
|
||||
}
|
||||
|
||||
room->playAnim(_internal->_descs[variant]._animName, _internal->_zValue,
|
||||
params,
|
||||
Common::SharedPtr<EventHandler>(new AmbientAnimPlayEnded(*this, reschedule)),
|
||||
_internal->_offset);
|
||||
|
||||
if (_internal->_descs[variant]._soundName != "")
|
||||
room->playSFX(_internal->_descs[variant]._soundName, -1);
|
||||
}
|
||||
|
||||
void AmbientAnim::schedule() {
|
||||
if (_internal->_minInterval >= 0 && _internal->_maxInterval >= 0)
|
||||
g_vm->addTimer(
|
||||
Common::SharedPtr<EventHandler>(new AmbientAnimStarter(*this)),
|
||||
g_vm->getRnd().getRandomNumberRng(_internal->_minInterval,
|
||||
_internal->_maxInterval));
|
||||
}
|
||||
|
||||
void AmbientAnim::playFinished(bool reschedule) {
|
||||
_internal->_playing = false;
|
||||
if (reschedule)
|
||||
schedule();
|
||||
}
|
||||
|
||||
void AmbientAnim::start() {
|
||||
if (_internal->_loopType == KEEP_LOOP) {
|
||||
selectFirstFrame();
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::readTableFilePriamSFX(const TextTable &table) {
|
||||
for (int row = 0; row < table.size(); row++) {
|
||||
AmbientAnimWeightedSetElement el;
|
||||
el.name = table.get(row, "name");
|
||||
el.weight = table.get(row, "weight").asUint64();
|
||||
el.valid = table.get(row, "anim") != "";
|
||||
if (el.valid)
|
||||
el.anim = AmbientAnim(table.get(row, "anim"),
|
||||
table.get(row, "sound"),
|
||||
table.get(row, "depth").asUint64(),
|
||||
-1, -1, AmbientAnim::KEEP_LOOP, Common::Point(0, 0),
|
||||
AmbientAnim::PAN_ANY);
|
||||
// TODO: volume
|
||||
_elements.push_back(el);
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::readTableFileSFX(const TextTable &table, AmbientAnim::PanType pan) {
|
||||
for (int row = 0; row < table.size(); row++) {
|
||||
AmbientAnimWeightedSetElement el;
|
||||
el.name = table.get(row, "anim");
|
||||
el.weight = 1;
|
||||
el.valid = table.get(row, "anim") != "";
|
||||
if (el.valid)
|
||||
el.anim = AmbientAnim(table.get(row, "anim"),
|
||||
table.get(row, "sound"),
|
||||
table.get(row, "Z").asUint64(),
|
||||
-1, -1, AmbientAnim::KEEP_LOOP, Common::Point(
|
||||
table.get(row, "X").asUint64(),
|
||||
table.get(row, "Y").asUint64()),
|
||||
pan);
|
||||
// TODO: volume
|
||||
_elements.push_back(el);
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::firstFrame() {
|
||||
for (unsigned i = 0; i < _elements.size(); i++) {
|
||||
if (_elements[i].valid)
|
||||
_elements[i].anim.selectFirstFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::tick() {
|
||||
int chosen = -1, chosenWeight = -1;
|
||||
// This is not how weighted random should be
|
||||
// but this is howoriginal game generates it and
|
||||
// it's probably slightly wrong from mathematical
|
||||
// point of view
|
||||
for (unsigned i = 0; i < _elements.size(); i++) {
|
||||
if (!_elements[i].anim.isReady())
|
||||
continue;
|
||||
int curWeight = _elements[i].weight * g_vm->getRnd().getRandomNumberRng(
|
||||
0, 100);
|
||||
if (curWeight > chosenWeight) {
|
||||
chosen = i;
|
||||
chosenWeight = curWeight;
|
||||
}
|
||||
}
|
||||
if (chosen < 0 || !_elements[chosen].valid)
|
||||
return;
|
||||
_elements[chosen].anim.play(false);
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::pause(const Common::String &name) {
|
||||
for (unsigned i = 0; i < _elements.size(); i++) {
|
||||
if (_elements[i].name == name && _elements[i].valid) {
|
||||
_elements[i].anim.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::unpause(const Common::String &name) {
|
||||
for (unsigned i = 0; i < _elements.size(); i++) {
|
||||
if (_elements[i].name == name && _elements[i].valid) {
|
||||
_elements[i].anim.unpause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::play(const Common::String &name, bool reschedule) {
|
||||
for (unsigned i = 0; i < _elements.size(); i++) {
|
||||
if (_elements[i].name == name && _elements[i].valid) {
|
||||
_elements[i].anim.play(reschedule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::unpauseAndFirstFrame(const Common::String &name) {
|
||||
for (unsigned i = 0; i < _elements.size(); i++) {
|
||||
if (_elements[i].name == name && _elements[i].valid) {
|
||||
_elements[i].anim.unpauseAndFirstFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientAnimWeightedSet::hide(const Common::String &name) {
|
||||
for (unsigned i = 0; i < _elements.size(); i++) {
|
||||
if (_elements[i].name == name && _elements[i].valid) {
|
||||
_elements[i].anim.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimClickables::playChosen(const Common::String &name, int counter, const EventHandlerWrapper &event) {
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
int rk = _table.rowCount(name);
|
||||
if (rk == 0) {
|
||||
event();
|
||||
return;
|
||||
}
|
||||
counter %= rk;
|
||||
Common::String smacker = _table.get(name, "smacker", counter);
|
||||
Common::String anim = _table.get(name, "anim", counter);
|
||||
Common::String sound = _table.get(name, "sound", counter);
|
||||
int zValue = _table.get(name, "Z", counter).asUint64();
|
||||
if (smacker != "")
|
||||
room->playVideo(smacker.substr(1), zValue, event,
|
||||
Common::Point(_table.get(name, "smackerX", counter).asUint64(),
|
||||
_table.get(name, "smackerY", counter).asUint64()));
|
||||
else if (anim != "")
|
||||
room->playAnimWithSpeech(
|
||||
anim, TranscribedSound::make(sound.c_str(), _transcriptions[sound].c_str()), zValue, PlayAnimParams::disappear(), event,
|
||||
Common::Point(_table.get(name, "X", counter).asUint64(),
|
||||
_table.get(name, "Y", counter).asUint64()));
|
||||
else if (sound != "")
|
||||
room->playSpeech(TranscribedSound::make(sound.c_str(), _transcriptions[sound].c_str()), event);
|
||||
else
|
||||
event();
|
||||
}
|
||||
|
||||
// TODO: should counters be persistent?
|
||||
void AnimClickables::playNext(const Common::String &name, const EventHandlerWrapper &event) {
|
||||
playChosen(name, _counters[name], event);
|
||||
_counters[name]++;
|
||||
}
|
||||
|
||||
void AnimClickables::readTable(Common::SharedPtr<Hadesch::VideoRoom> room, const Common::String &name, const TranscribedSound *transcriptions) {
|
||||
_table = TextTable(Common::SharedPtr<Common::SeekableReadStream>(room->openFile(name)), 14);
|
||||
|
||||
for (const TranscribedSound *t = transcriptions; t->soundName; t++) {
|
||||
_transcriptions[t->soundName] = t->transcript;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
132
engines/hadesch/ambient.h
Normal file
132
engines/hadesch/ambient.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/* 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 "common/str.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/noncopyable.h"
|
||||
|
||||
#ifndef HADESCH_AMBIENT_H
|
||||
#define HADESCH_AMBIENT_H
|
||||
|
||||
namespace Hadesch {
|
||||
class AmbientAnim {
|
||||
public:
|
||||
struct AmbientDesc {
|
||||
Common::String _animName;
|
||||
Common::String _soundName;
|
||||
AmbientDesc(Common::String animName, Common::String soundName) {
|
||||
_animName = animName;
|
||||
_soundName = soundName;
|
||||
}
|
||||
};
|
||||
|
||||
enum PanType {
|
||||
PAN_ANY,
|
||||
PAN_LEFT,
|
||||
PAN_RIGHT
|
||||
};
|
||||
|
||||
enum AnimType {
|
||||
DISAPPEAR,
|
||||
KEEP_LOOP,
|
||||
BACK_AND_FORTH
|
||||
};
|
||||
|
||||
AmbientAnim(const Common::String &animName,
|
||||
const Common::String &sound, int zValue,
|
||||
int minint, int maxint, AnimType loop,
|
||||
Common::Point offset, PanType pan);
|
||||
AmbientAnim(const Common::Array<AmbientDesc> &descs, int zValue,
|
||||
int minint, int maxint, AnimType loop,
|
||||
Common::Point offset, PanType pan);
|
||||
AmbientAnim();
|
||||
void play(bool reschedule);
|
||||
void schedule();
|
||||
void start();
|
||||
void pause();
|
||||
void unpause();
|
||||
void hide();
|
||||
void unpauseAndFirstFrame();
|
||||
void selectFirstFrame();
|
||||
void playFinished(bool reschedule);
|
||||
bool isReady();
|
||||
private:
|
||||
class AmbiantAnimInternal : Common::NonCopyable {
|
||||
public:
|
||||
Common::Array<AmbientDesc> _descs;
|
||||
int _minInterval, _maxInterval;
|
||||
int _zValue;
|
||||
AnimType _loopType;
|
||||
bool _isFwd;
|
||||
Common::Point _offset;
|
||||
bool _playing;
|
||||
bool _paused;
|
||||
PanType _pan;
|
||||
};
|
||||
|
||||
bool isPanOK();
|
||||
|
||||
Common::SharedPtr<AmbiantAnimInternal> _internal;
|
||||
};
|
||||
|
||||
|
||||
class AmbientAnimWeightedSet {
|
||||
public:
|
||||
void readTableFilePriamSFX(const TextTable &table);
|
||||
void readTableFileSFX(const TextTable &table, AmbientAnim::PanType pan);
|
||||
void tick();
|
||||
void firstFrame();
|
||||
void pause(const Common::String &name);
|
||||
void unpause(const Common::String &name);
|
||||
void unpauseAndFirstFrame(const Common::String &name);
|
||||
void hide(const Common::String &name);
|
||||
void play(const Common::String &name, bool reschedule);
|
||||
private:
|
||||
struct AmbientAnimWeightedSetElement {
|
||||
AmbientAnim anim;
|
||||
int weight;
|
||||
bool valid;
|
||||
Common::String name;
|
||||
};
|
||||
Common::Array<AmbientAnimWeightedSetElement> _elements;
|
||||
};
|
||||
|
||||
class AnimClickables {
|
||||
public:
|
||||
void playNext(const Common::String &name, const EventHandlerWrapper &event);
|
||||
void playChosen(const Common::String &name, int counter, const EventHandlerWrapper &event);
|
||||
void setTable(const TextTable table) {
|
||||
_table = table;
|
||||
}
|
||||
void readTable(Common::SharedPtr<Hadesch::VideoRoom> room,
|
||||
const Common::String &name,
|
||||
const TranscribedSound *transcriptionTable);
|
||||
|
||||
private:
|
||||
TextTable _table;
|
||||
Common::HashMap<Common::String, Common::String> _transcriptions;
|
||||
Common::HashMap<Common::String, int> _counters;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
34
engines/hadesch/baptr.cpp
Normal file
34
engines/hadesch/baptr.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/* 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/baptr.h"
|
||||
|
||||
namespace Hadesch {
|
||||
class ByteArrayDeleter {
|
||||
public:
|
||||
void operator()(byte *ptr) { delete [] ptr; }
|
||||
};
|
||||
|
||||
Common::SharedPtr<byte> sharedPtrByteAlloc(size_t sz) {
|
||||
return Common::SharedPtr<byte>(new (std::nothrow) byte[sz], ByteArrayDeleter());
|
||||
}
|
||||
}
|
||||
31
engines/hadesch/baptr.h
Normal file
31
engines/hadesch/baptr.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_BAPTR_H
|
||||
#define HADESCH_BAPTR_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
|
||||
namespace Hadesch {
|
||||
Common::SharedPtr<byte> sharedPtrByteAlloc(size_t sz);
|
||||
}
|
||||
#endif
|
||||
3
engines/hadesch/configure.engine
Normal file
3
engines/hadesch/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine hadesch "Hades Challenge" yes "" "" "highres"
|
||||
3
engines/hadesch/credits.pl
Normal file
3
engines/hadesch/credits.pl
Normal file
@@ -0,0 +1,3 @@
|
||||
begin_section("Hades Challenge");
|
||||
add_person("Vladimir Serbinenko/Google", "phcoder", "");
|
||||
end_section();
|
||||
97
engines/hadesch/detection.cpp
Normal file
97
engines/hadesch/detection.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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 "common/system.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "hadesch/hadesch.h"
|
||||
|
||||
#include "detection_tables.h"
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{Hadesch::kHadeschDebugGeneral, "general", "General issues"},
|
||||
{Hadesch::kHadeschDebugResources, "resources", "Resources"},
|
||||
{Hadesch::kHadeschDebugMessagingSystem, "message_system", "Engine message system"},
|
||||
{Hadesch::kHadeschDebugDialogs, "dialogs", "Dialogs"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
namespace Hadesch {
|
||||
static const PlainGameDescriptor hadeschGames[] = {
|
||||
{"hadesch", "Hades Challenge"},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
// The list is pretty long but it's because we need just a few files but
|
||||
// in pretty deep paths:
|
||||
// * Setup.exe [Russian-Windows]
|
||||
// * WIN9x/WORLD/wd.pod [English-Windows]
|
||||
// * WIN95/WORLD/wd.pod [English-Windows, alternative]
|
||||
// * WIN95/HADESCH.EXE [English-Windows]
|
||||
// * CDAssets/OLYMPUS/ol.pod [English-Windows]
|
||||
// * Scenes/OLYMPUS/ol.pod [English-Mac and Russian-Windows]
|
||||
// * Hades_-_Copy_To_Hard_Drive/Hades_Challenge/World/wd.pod [English-Mac]
|
||||
// * Hades - Copy To Hard Drive/Hades Challenge/World/wd.pod [English-Mac]
|
||||
// The difference between 2 last one is how the files were copied
|
||||
static const char *const directoryGlobs[] = {
|
||||
"WIN95",
|
||||
"WIN9x",
|
||||
"WORLD",
|
||||
"CDAssets",
|
||||
"OLYMPUS",
|
||||
"Scenes",
|
||||
"Hades_-_Copy_To_Hard_Drive",
|
||||
"Hades - Copy To Hard Drive",
|
||||
"Hades Challenge",
|
||||
"Hades_Challenge",
|
||||
nullptr
|
||||
};
|
||||
}
|
||||
|
||||
class HadeschMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
|
||||
public:
|
||||
HadeschMetaEngineDetection() : AdvancedMetaEngineDetection(Hadesch::gameDescriptions, Hadesch::hadeschGames) {
|
||||
// mac puts wd.pod in Hades - Copy To Hard Drive/Hades Challenge/World. So we need 4 levels
|
||||
_maxScanDepth = 4;
|
||||
_directoryGlobs = Hadesch::directoryGlobs;
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "hadesch";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Hades Challenge";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Hades Challenge (C) Disney's Interactive";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN_STATIC(HADESCH_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, HadeschMetaEngineDetection);
|
||||
152
engines/hadesch/detection_tables.h
Normal file
152
engines/hadesch/detection_tables.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HADESCH_DETECTION_TABLES_H
|
||||
#define HADESCH_DETECTION_TABLES_H
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
static const ADGameDescription gameDescriptions[] = {
|
||||
|
||||
// Hades Challenge
|
||||
{
|
||||
"hadesch",
|
||||
0,
|
||||
{
|
||||
{"hadesch.exe", 0, "178b3a69171cb5a4eeeddd0d5993b8c5", 1134592},
|
||||
{"WD.POD", 0, "be7030fc4229e69e719ee2c756eb6ba1", 7479768},
|
||||
{"ol.pod", 0, "7cabba8d1d4f1239e312e045ef4e9735", 5621074},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
{
|
||||
"hadesch",
|
||||
0,
|
||||
{
|
||||
{"Hades Challenge PPC", 0, "r:0a1dd550e5efe7f36370e689811fac73", 28945},
|
||||
{"WD.POD", 0, "d:be7030fc4229e69e719ee2c756eb6ba1", 7479768},
|
||||
{"ol.pod", 0, "d:7cabba8d1d4f1239e312e045ef4e9735", 5621074},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_DROPPLATFORM,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
{
|
||||
"hadesch",
|
||||
0,
|
||||
{
|
||||
{"hadesch.exe", 0, "660735787346ab1bfe0d219bea441486", 1007616},
|
||||
{"WD.POD", 0, "be7030fc4229e69e719ee2c756eb6ba1", 7479768},
|
||||
{"ol.pod", 0, "7cabba8d1d4f1239e312e045ef4e9735", 5621074},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
{
|
||||
"hadesch",
|
||||
0,
|
||||
{
|
||||
{"hadesch.exe", 0, "660735787346ab1bfe0d219bea441486", 1007616},
|
||||
{"WD.POD", 0, "5098edae755135814bb86f2676c41cc2", 8691909},
|
||||
{"ol.pod", 0, "c82e105d9013edc2cc20f0a630e304d5", 5684953},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
|
||||
},
|
||||
{
|
||||
"hadesch",
|
||||
0,
|
||||
{
|
||||
{"setup.exe", 0, "853c199f1ef35d576213f71092bcd0c3", 7491209},
|
||||
{"ol.pod", 0, "c82e105d9013edc2cc20f0a630e304d5", 5684953},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
|
||||
},
|
||||
|
||||
// Bad dumps from archive.org
|
||||
{
|
||||
"hadesch",
|
||||
0,
|
||||
{
|
||||
{"hadesch.exe", 0, "178b3a69171cb5a4eeeddd0d5993b8c5", 1134592},
|
||||
{"WD.POD", 0, "be7030fc4229e69e719ee2c756eb6ba1", 7479768},
|
||||
{"ol.pod", 0, "d41d8cd98f00b204e9800998ecf8427e", 5621074},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_PIRATED,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
{
|
||||
"hadesch",
|
||||
0,
|
||||
{
|
||||
{"Hades Challenge PPC", 0, "r:0a1dd550e5efe7f36370e689811fac73", 28945},
|
||||
{"WD.POD", 0, "d:be7030fc4229e69e719ee2c756eb6ba1", 7479768},
|
||||
{"ol.pod", 0, "d:6bf95a48f366bdf8af3a198c7b723c77", 5621074},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformMacintosh,
|
||||
ADGF_DROPPLATFORM | ADGF_PIRATED,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
{
|
||||
"hadesch",
|
||||
0,
|
||||
{
|
||||
{"hadesch.exe", 0, "178b3a69171cb5a4eeeddd0d5993b8c5", 1134592},
|
||||
{"WD.POD", 0, "be7030fc4229e69e719ee2c756eb6ba1", 7479768},
|
||||
{"ol.pod", 0, "6bf95a48f366bdf8af3a198c7b723c77", 5621074},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_PIRATED,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
AD_TABLE_END_MARKER
|
||||
};
|
||||
|
||||
} // End of namespace Hadesch
|
||||
|
||||
#endif
|
||||
176
engines/hadesch/enums.h
Normal file
176
engines/hadesch/enums.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_ENUMS_H
|
||||
#define HADESCH_ENUMS_H
|
||||
|
||||
namespace Hadesch {
|
||||
enum {
|
||||
kHadeschDebugGeneral = 1 << 0,
|
||||
kHadeschDebugResources = 1 << 1,
|
||||
kHadeschDebugMessagingSystem = 1 << 2,
|
||||
kHadeschDebugDialogs = 1 << 3
|
||||
};
|
||||
|
||||
enum Gender {
|
||||
kFemale = 0,
|
||||
kMale = 1,
|
||||
// Make it 2, rather than -1, so that we can serialize it in one 1
|
||||
// byte.
|
||||
kUnknown = 2
|
||||
};
|
||||
|
||||
enum Quest {
|
||||
kNoQuest,
|
||||
kCreteQuest,
|
||||
kTroyQuest,
|
||||
kMedusaQuest,
|
||||
kRescuePhilQuest,
|
||||
kEndGame,
|
||||
kNumQuests
|
||||
};
|
||||
|
||||
enum RoomId {
|
||||
kInvalidRoom = 0,
|
||||
kIntroRoom = 1,
|
||||
kOlympusRoom = 2,
|
||||
kWallOfFameRoom = 3,
|
||||
kSeriphosRoom = 4,
|
||||
kAthenaRoom = 5,
|
||||
kMedIsleRoom = 6,
|
||||
kMedusaPuzzle = 7,
|
||||
kArgoRoom = 8,
|
||||
kTroyRoom = 9,
|
||||
kCatacombsRoom = 10,
|
||||
kPriamRoom = 11,
|
||||
kTrojanHorsePuzzle = 12,
|
||||
kCreteRoom = 13,
|
||||
kMinosPalaceRoom = 14,
|
||||
kDaedalusRoom = 15,
|
||||
kMinotaurPuzzle = 16,
|
||||
kVolcanoRoom = 17,
|
||||
kRiverStyxRoom = 18,
|
||||
kHadesThroneRoom = 19,
|
||||
kFerrymanPuzzle = 20,
|
||||
kMonsterPuzzle = 21,
|
||||
kQuiz = 22,
|
||||
kCreditsRoom = 23,
|
||||
kOptionsRoom = 24,
|
||||
kNumRooms
|
||||
};
|
||||
|
||||
enum StatueId {
|
||||
kBacchusStatue = 0,
|
||||
kHermesStatue = 1,
|
||||
kZeusStatue = 2,
|
||||
kPoseidonStatue = 3,
|
||||
kAresStatue = 4,
|
||||
kAphroditeStatue = 5,
|
||||
kApolloStatue = 6,
|
||||
kArtemisStatue = 7,
|
||||
kDemeterStatue = 8,
|
||||
kAthenaStatue = 9,
|
||||
kHeraStatue = 10,
|
||||
kHephaestusStatue = 11,
|
||||
kNumStatues
|
||||
};
|
||||
|
||||
enum InventoryItem {
|
||||
kNone = 0,
|
||||
kStraw = 2,
|
||||
kStone = 3,
|
||||
kBricks = 4,
|
||||
kMessage = 5,
|
||||
kKey = 6,
|
||||
kDecree = 7,
|
||||
kWood = 8,
|
||||
kHornlessStatue1 = 9,
|
||||
kHornlessStatue2 = 10,
|
||||
kHornlessStatue3 = 11,
|
||||
kHornlessStatue4 = 12,
|
||||
kHornedStatue = 13,
|
||||
kCoin = 14,
|
||||
kPotion = 15,
|
||||
kShield = 16,
|
||||
kSword = 17,
|
||||
kBag = 18,
|
||||
kHelmet = 19,
|
||||
kSandals = 20,
|
||||
kTorch = 21
|
||||
};
|
||||
|
||||
// Also includes InventoryItem - 1
|
||||
enum HeroBeltFrame {
|
||||
kLightning1 = 21,
|
||||
kLightning2 = 22,
|
||||
kLightning3 = 23,
|
||||
kNumberI = 24,
|
||||
kNumberII = 25,
|
||||
kNumberIII = 26,
|
||||
kQuestScroll = 27,
|
||||
kQuestScrollHighlighted = 28,
|
||||
kHadesScroll = 29,
|
||||
kHadesScrollHighlighted = 30,
|
||||
kOptionsButton = 31,
|
||||
kInactiveHints = 32,
|
||||
kActiveHints = 33,
|
||||
kBranchOfLife = 34,
|
||||
kReturnToWall = 35,
|
||||
kPowerOfWisdom = 38,
|
||||
kPowerOfStrength = 39,
|
||||
kPowerOfStealth = 40
|
||||
};
|
||||
|
||||
enum FateId {
|
||||
kLachesis,
|
||||
kAtropos,
|
||||
kClotho,
|
||||
kNumFates
|
||||
};
|
||||
|
||||
enum CatacombsPosition {
|
||||
kCatacombsLeft = 0,
|
||||
kCatacombsCenter = 1,
|
||||
kCatacombsRight = 2
|
||||
};
|
||||
|
||||
enum CatacombsPath {
|
||||
kCatacombsHelen = 0,
|
||||
kCatacombsGuards = 1,
|
||||
kCatacombsPainAndPanic = 2
|
||||
};
|
||||
|
||||
enum CatacombsLevel {
|
||||
kCatacombLevelSign,
|
||||
kCatacombLevelTorch,
|
||||
kCatacombLevelMusic
|
||||
};
|
||||
|
||||
enum HeroPower {
|
||||
kPowerNone = -1,
|
||||
kPowerStrength = 0,
|
||||
kPowerStealth = 1,
|
||||
kPowerWisdom = 2
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
63
engines/hadesch/event.h
Normal file
63
engines/hadesch/event.h
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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_EVENT_H
|
||||
#define HADESCH_EVENT_H
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class EventHandler {
|
||||
public:
|
||||
virtual void operator()() = 0;
|
||||
virtual ~EventHandler() {}
|
||||
};
|
||||
|
||||
class EventHandlerWrapper {
|
||||
public:
|
||||
void operator()() const;
|
||||
bool operator==(int eventId) const;
|
||||
|
||||
EventHandlerWrapper(int eventId) {
|
||||
_eventId = eventId;
|
||||
}
|
||||
|
||||
EventHandlerWrapper() {
|
||||
_eventId = -1;
|
||||
}
|
||||
|
||||
EventHandlerWrapper(Common::SharedPtr<EventHandler> handler) {
|
||||
_handler = handler;
|
||||
_eventId = -1;
|
||||
}
|
||||
|
||||
Common::String getDebugString() const {
|
||||
return Common::String::format("eventid=%d, handler is %s", _eventId,
|
||||
!!_handler ? "valid" : "null");
|
||||
}
|
||||
|
||||
private:
|
||||
Common::SharedPtr<EventHandler> _handler;
|
||||
int _eventId;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
142
engines/hadesch/gfx_context.cpp
Normal file
142
engines/hadesch/gfx_context.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/* 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/gfx_context.h"
|
||||
#include "hadesch/video.h"
|
||||
#include "hadesch/baptr.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/paletteman.h"
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/fontman.h"
|
||||
#include "hadesch/hadesch.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
void blendVideo8To8(byte *targetPixels, int targetPitch, int targetW, int targetH,
|
||||
byte *sourcePixels, int sourceW, int sourceH, Common::Point offset) {
|
||||
int ymin = MAX(0, -offset.y);
|
||||
int ymax = MIN(sourceH, targetH - offset.y);
|
||||
for (int y = ymin; y < ymax; y++) {
|
||||
int xmin = MAX(0, -offset.x);
|
||||
int xmax = MIN(sourceW, targetW - offset.x);
|
||||
const byte *inptr = sourcePixels + sourceW * y + xmin;
|
||||
byte *outptr = targetPixels + targetPitch * (y + offset.y) + offset.x + xmin;
|
||||
for (int x = xmin; x < xmax; x++, inptr++, outptr++) {
|
||||
if (*inptr)
|
||||
*outptr = *inptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GfxContext8Bit::blitPodImage(byte *sourcePixels, int sourcePitch, int sourceW, int sourceH,
|
||||
byte *sourcePalette, size_t ncolours, Common::Point offset) {
|
||||
|
||||
blendVideo8To8((byte *) surf.getPixels(), surf.pitch,
|
||||
surf.w, surf.h, sourcePixels, sourceW, sourceH,
|
||||
offset);
|
||||
for (unsigned i = 0; i < ncolours; i++) {
|
||||
int col = sourcePalette[4 * i] & 0xff;
|
||||
|
||||
_palette[3 * col ] = sourcePalette[4 * i + 1];
|
||||
_palette[3 * col + 1] = sourcePalette[4 * i + 2];
|
||||
_palette[3 * col + 2] = sourcePalette[4 * i + 3];
|
||||
_paletteUsed[col] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GfxContext8Bit::blitVideo(byte *sourcePixels, int sourcePitch, int sourceW, int sourceH,
|
||||
byte *sourcePalette, Common::Point offset) {
|
||||
blendVideo8To8((byte *) surf.getPixels(), surf.pitch, surf.w, surf.h, sourcePixels, sourceW, sourceH, offset);
|
||||
for (int i = 0; i < 256; i++)
|
||||
if (!_paletteUsed[i]) {
|
||||
_palette[3 * i] = sourcePalette[3 * i];
|
||||
_palette[3 * i + 1] = sourcePalette[3 * i + 1];
|
||||
_palette[3 * i + 2] = sourcePalette[3 * i + 2];
|
||||
}
|
||||
}
|
||||
|
||||
void GfxContext8Bit::fade(int val) {
|
||||
if (val == 0x100)
|
||||
return;
|
||||
for (int i = 0; i < 256 * 3; i++) {
|
||||
_palette[i] = ((_palette[i] & 0xff) * val) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
void GfxContext8Bit::clear() {
|
||||
surf.clearPalette();
|
||||
surf.clear();
|
||||
memset(_palette, 0, sizeof(_palette));
|
||||
memset(_paletteUsed, 0, sizeof(_paletteUsed));
|
||||
}
|
||||
|
||||
GfxContext8Bit::GfxContext8Bit(int canvasW, int canvasH) : surf(canvasW, canvasH, Graphics::PixelFormat::createFormatCLUT8()) {
|
||||
clear();
|
||||
}
|
||||
|
||||
void GfxContext8Bit::renderToScreen(Common::Point viewPoint) {
|
||||
g_system->getPaletteManager()->setPalette(_palette, 0, 256);
|
||||
g_system->copyRectToScreen(surf.getBasePtr(viewPoint.x, viewPoint.y), surf.w, 0, 0,
|
||||
kVideoWidth, kVideoHeight);
|
||||
}
|
||||
|
||||
byte GfxContext8Bit::findColor(byte r, byte g, byte b) {
|
||||
for (uint i = 1; i < 256; i++)
|
||||
if (_paletteUsed[i] && _palette[3 * i] == r && _palette[3 * i + 1] == g && _palette[3 * i + 2] == b) {
|
||||
return i;
|
||||
}
|
||||
for (uint i = 1; i < 256; i++)
|
||||
if (!_paletteUsed[i]) {
|
||||
_palette[3 * i] = r;
|
||||
_palette[3 * i + 1] = g;
|
||||
_palette[3 * i + 2] = b;
|
||||
_paletteUsed[i] = true;
|
||||
return i;
|
||||
}
|
||||
int diff = 0x40000; int c = 0;
|
||||
for (uint i = 1; i < 256; i++) {
|
||||
int cDiff = (_palette[3 * i] - r) * (_palette[3 * i] - r) + (_palette[3 * i + 1] - g) * (_palette[3 * i + 1] - g)
|
||||
+ (_palette[3 * i + 2] - b) * (_palette[3 * i + 2] - b);
|
||||
if (cDiff < diff) {
|
||||
diff = cDiff;
|
||||
c = i;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void GfxContext8Bit::renderSubtitle(Common::U32String const& line, Common::Point viewPoint) {
|
||||
int fgColor = findColor(0xff, 0xff, 0xff);
|
||||
int bgColor = findColor(0, 0, 0);
|
||||
const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont));
|
||||
Common::Rect rect(70 + viewPoint.x, 420 + viewPoint.y, 570 + viewPoint.x, 420 + viewPoint.y + font.getFontHeight());
|
||||
surf.fillRect(rect, bgColor);
|
||||
font.drawString(&surf, line, rect.left, rect.top, rect.width() - 10,
|
||||
fgColor, Graphics::kTextAlignCenter);
|
||||
}
|
||||
|
||||
void HadeschEngine::wrapSubtitles(const Common::U32String &str, Common::Array<Common::U32String> &lines) {
|
||||
const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont));
|
||||
font.wordWrapText(str, 500, lines);
|
||||
}
|
||||
}
|
||||
71
engines/hadesch/gfx_context.h
Normal file
71
engines/hadesch/gfx_context.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_GFX_CONTEXT_H
|
||||
#define HADESCH_GFX_CONTEXT_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace Hadesch {
|
||||
class GfxContext {
|
||||
public:
|
||||
virtual void blitVideo(byte *sourcePixels, int sourcePitch, int sourceW, int sourceH,
|
||||
byte *palette, Common::Point offset) = 0;
|
||||
// Use only in pod_image.cpp because of palette format
|
||||
virtual void blitPodImage(byte *sourcePixels, int sourcePitch, int sourceW, int sourceH,
|
||||
byte *palette, size_t ncolours, Common::Point offset) = 0;
|
||||
virtual void clear() = 0;
|
||||
virtual void renderSubtitle(const Common::U32String &, Common::Point viewPoint) = 0;
|
||||
virtual void fade(int val) = 0;
|
||||
virtual void renderToScreen(Common::Point viewPoint) = 0;
|
||||
virtual ~GfxContext() {}
|
||||
};
|
||||
|
||||
class GfxContext8Bit : public GfxContext, Common::NonCopyable {
|
||||
public:
|
||||
void blitVideo(byte *sourcePixels, int sourcePitch, int sourceW, int sourceH,
|
||||
byte *palette, Common::Point offset) override;
|
||||
void blitPodImage(byte *sourcePixels, int sourcePitch, int sourceW, int sourceH,
|
||||
byte *palette, size_t ncolours, Common::Point offset) override;
|
||||
void clear() override;
|
||||
void fade(int val) override;
|
||||
void renderToScreen(Common::Point viewPoint) override;
|
||||
void renderSubtitle(const Common::U32String &, Common::Point viewPoint) override;
|
||||
byte findColor(byte r, byte g, byte b);
|
||||
|
||||
GfxContext8Bit(int canvasW, int canvasH);
|
||||
~GfxContext8Bit() {}
|
||||
|
||||
private:
|
||||
Graphics::ManagedSurface surf;
|
||||
byte _palette[256 * 4];
|
||||
bool _paletteUsed[256];
|
||||
};
|
||||
|
||||
void blendVideo8To8(byte *targetPixels, int targetPitch, int targetW, int targetH,
|
||||
byte *sourcePixels, int sourceW, int sourceH, Common::Point offset);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
1006
engines/hadesch/hadesch.cpp
Normal file
1006
engines/hadesch/hadesch.cpp
Normal file
File diff suppressed because it is too large
Load Diff
246
engines/hadesch/hadesch.h
Normal file
246
engines/hadesch/hadesch.h
Normal file
@@ -0,0 +1,246 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_HADESCH_H
|
||||
#define HADESCH_HADESCH_H
|
||||
|
||||
#include "common/random.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/list.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "engines/savestate.h"
|
||||
|
||||
#include "gui/debugger.h"
|
||||
#include "graphics/cursor.h"
|
||||
#include "hadesch/pod_file.h"
|
||||
#include "hadesch/video.h"
|
||||
#include "hadesch/enums.h"
|
||||
#include "hadesch/event.h"
|
||||
#include "hadesch/herobelt.h"
|
||||
#include "hadesch/persistent.h"
|
||||
|
||||
#define _hs(x) (x)
|
||||
|
||||
struct ADGameDescription;
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
class PEResources;
|
||||
class TranslationManager;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct WinCursorGroup;
|
||||
class MacCursor;
|
||||
}
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class VideoRoom;
|
||||
|
||||
class Handler {
|
||||
public:
|
||||
virtual void handleClick(const Common::String &name) = 0;
|
||||
virtual void handleAbsoluteClick(Common::Point pnt) {}
|
||||
virtual bool handleClickWithItem(const Common::String &name, InventoryItem item) {
|
||||
return false;
|
||||
}
|
||||
virtual void handleEvent(int eventId) = 0;
|
||||
virtual void handleMouseOver(const Common::String &name) {}
|
||||
virtual void handleMouseOut(const Common::String &name) {}
|
||||
virtual void frameCallback() {}
|
||||
virtual void handleKeypress(uint32 ucode) {}
|
||||
virtual void prepareRoom() = 0;
|
||||
virtual bool handleCheat(const Common::String &cheat) {
|
||||
return false;
|
||||
}
|
||||
virtual void handleUnclick(const Common::String &name, const Common::Point &pnt) {}
|
||||
virtual ~Handler() {}
|
||||
};
|
||||
|
||||
Common::SharedPtr<Hadesch::Handler> makeOlympusHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeWallOfFameHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeArgoHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeCreteHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeMinosHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeDaedalusHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeSeriphosHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeMedIsleHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeTroyHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeMinotaurHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeQuizHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeCatacombsHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makePriamHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeAthenaHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeVolcanoHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeRiverStyxHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeHadesThroneHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeCreditsHandler(bool inOptions);
|
||||
Common::SharedPtr<Hadesch::Handler> makeIntroHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeFerryHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeOptionsHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeMonsterHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeMedusaHandler();
|
||||
Common::SharedPtr<Hadesch::Handler> makeTrojanHandler();
|
||||
|
||||
class HadeschEngine : public Engine, Common::NonCopyable {
|
||||
public:
|
||||
HadeschEngine(OSystem *syst, const ADGameDescription *desc);
|
||||
~HadeschEngine() override;
|
||||
|
||||
Common::Error run() override;
|
||||
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { return true; }
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { return _persistent._currentRoomId != 0; }
|
||||
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
|
||||
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
|
||||
|
||||
Common::SeekableReadStream *openFile(const Common::String &name, bool addCurrentPath);
|
||||
|
||||
Common::RandomSource &getRnd();
|
||||
|
||||
const Common::String &getCDScenesPath() const;
|
||||
|
||||
Common::SharedPtr<VideoRoom> getVideoRoom();
|
||||
|
||||
void moveToRoom(RoomId id) {
|
||||
_nextRoom.push_back(id);
|
||||
_heroBelt->clearHold();
|
||||
}
|
||||
|
||||
void handleEvent(EventHandlerWrapper event);
|
||||
int32 getCurrentTime() {
|
||||
return _currentTime;
|
||||
}
|
||||
Common::Point getMousePos();
|
||||
|
||||
void addTimer(EventHandlerWrapper event, int period, int repeat = 1);
|
||||
void addSkippableTimer(EventHandlerWrapper event, int period, int repeat = 1);
|
||||
void cancelTimer(int eventId);
|
||||
|
||||
Common::SharedPtr<PodFile> getWdPodFile() {
|
||||
return _wdPodFile;
|
||||
}
|
||||
|
||||
RoomId getPreviousRoomId() const {
|
||||
return _persistent._previousRoomId;
|
||||
}
|
||||
|
||||
bool isRoomVisited(RoomId id) const {
|
||||
return _persistent._roomVisited[id];
|
||||
}
|
||||
|
||||
Persistent *getPersistent() {
|
||||
return &_persistent;
|
||||
}
|
||||
|
||||
Common::SharedPtr<Handler> getCurrentHandler();
|
||||
|
||||
Common::SharedPtr<HeroBelt> getHeroBelt() {
|
||||
return _heroBelt;
|
||||
}
|
||||
|
||||
int firstAvailableSlot();
|
||||
|
||||
void newGame();
|
||||
void enterOptions();
|
||||
void resetOptionsRoom();
|
||||
void exitOptions();
|
||||
void enterOptionsCredits();
|
||||
void quit();
|
||||
bool hasAnySaves();
|
||||
|
||||
Common::Array<HadeschSaveDescriptor> getHadeschSavesList();
|
||||
void deleteSave(int slot);
|
||||
int genSubtitleID();
|
||||
uint32 getSubtitleDelayPerChar() const;
|
||||
void wrapSubtitles(const Common::U32String &str, Common::Array<Common::U32String> &lines);
|
||||
Common::U32String translate(const Common::String &str);
|
||||
void fallbackClick();
|
||||
|
||||
private:
|
||||
void addTimer(EventHandlerWrapper event, int32 start_time, int period,
|
||||
int repeat, bool skippable);
|
||||
void moveToRoomReal(RoomId id);
|
||||
void setVideoRoom(Common::SharedPtr<VideoRoom> scene,
|
||||
Common::SharedPtr<Handler> handler,
|
||||
RoomId roomId);
|
||||
Common::ErrorCode loadCursors();
|
||||
bool handleGenericCheat(const Common::String &cheat);
|
||||
Common::ErrorCode loadWindowsCursors(const Common::ScopedPtr<Common::PEResources>& exe);
|
||||
|
||||
struct Timer {
|
||||
int32 next_time;
|
||||
int32 period;
|
||||
int32 period_count;
|
||||
EventHandlerWrapper event;
|
||||
bool skippable;
|
||||
};
|
||||
const ADGameDescription *_desc;
|
||||
|
||||
Common::RandomSource _rnd;
|
||||
|
||||
Common::String _cdScenesPath;
|
||||
|
||||
Common::SharedPtr<VideoRoom> _sceneVideoRoom;
|
||||
Common::SharedPtr<Handler> _sceneHandler;
|
||||
Common::SharedPtr<VideoRoom> _optionsRoom;
|
||||
Common::SharedPtr<Handler> _optionsHandler;
|
||||
bool _isInOptions;
|
||||
uint32 _optionsEnterTime;
|
||||
uint32 _sceneStartTime;
|
||||
int32 _currentTime;
|
||||
Common::Array<Graphics::Cursor *> _cursors;
|
||||
Common::List<Timer> _sceneTimers;
|
||||
Common::SharedPtr<PodFile> _wdPodFile;
|
||||
Common::SharedPtr<HeroBelt> _heroBelt;
|
||||
Common::String _cheat;
|
||||
Common::SharedPtr<GfxContext8Bit> _gfxContext;
|
||||
bool _cheatsEnabled;
|
||||
Common::Point _mousePos;
|
||||
|
||||
Persistent _persistent;
|
||||
Common::Array<RoomId> _nextRoom;
|
||||
bool _isRestoring;
|
||||
bool _isQuitting;
|
||||
int _subtitleID;
|
||||
int _subtitleDelayPerChar;
|
||||
int _lastFallbackSound;
|
||||
|
||||
#ifdef USE_TRANSLATION
|
||||
Common::TranslationManager *_transMan;
|
||||
#endif
|
||||
|
||||
// For freeing purposes
|
||||
Common::Array <Graphics::MacCursor *> _macCursors;
|
||||
Common::Array <Graphics::WinCursorGroup *> _winCursors;
|
||||
};
|
||||
|
||||
extern HadeschEngine *g_vm;
|
||||
|
||||
} // End of namespace Hadesch
|
||||
|
||||
#endif
|
||||
584
engines/hadesch/herobelt.cpp
Normal file
584
engines/hadesch/herobelt.cpp
Normal file
@@ -0,0 +1,584 @@
|
||||
/* 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 "common/file.h"
|
||||
#include "hadesch/video.h"
|
||||
#include "hadesch/pod_image.h"
|
||||
#include "hadesch/tag_file.h"
|
||||
#include "hadesch/hadesch.h"
|
||||
#include "common/system.h"
|
||||
#include "video/smk_decoder.h"
|
||||
#include "audio/decoders/aiff.h"
|
||||
#include "hadesch/pod_file.h"
|
||||
#include "hadesch/baptr.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
static const int kHeroBeltMinY = 378;
|
||||
static const int kHeroBeltMaxY = 471;
|
||||
|
||||
static const TranscribedSound powerSounds[3][2] = {
|
||||
{
|
||||
{ "g0280nc0", _hs("That's where hero power of strength will be stored when you earn it") },
|
||||
{ "g0280ng0", _hs("The power of strength will let you overcome obstacles but you'll need a complete set of hero powers to use it") }
|
||||
},
|
||||
{
|
||||
{ "g0280nb0", _hs("That's where hero power of stealth will be stored when you earn it") },
|
||||
{ "g0280nf0", _hs("The power of stealth allows you to sneak past things but you'll need a complete set of hero powers to use it") }
|
||||
},
|
||||
{
|
||||
{ "g0280ne0", _hs("That's where hero power of wisdom will be stored when you earn it") },
|
||||
{ "g0280nh0", _hs("The power of wisdom will let you outwit and avoid deception but you'll need a complete set of hero powers to use it") }
|
||||
},
|
||||
};
|
||||
|
||||
static Common::Array <PodImage> loadImageArray(const Common::String &name) {
|
||||
Common::SharedPtr<Common::SeekableReadStream> rs(g_vm->getWdPodFile()->getFileStream(name + ".pod"));
|
||||
PodFile pf2(name);
|
||||
pf2.openStore(rs);
|
||||
return pf2.loadImageArray();
|
||||
}
|
||||
|
||||
static PodImage loadImage(const Common::String &name) {
|
||||
return loadImageArray(name)[0];
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *background;
|
||||
const char *iconNames;
|
||||
const char *icons;
|
||||
|
||||
// TODO: figure out how the original handles
|
||||
// cursors.
|
||||
const char *iconCursors;
|
||||
const char *iconCursorsBright;
|
||||
|
||||
const char *scrollBg;
|
||||
const char *scrollBgHades;
|
||||
const char *scrollTextCrete;
|
||||
const char *scrollTextTroyFemale;
|
||||
const char *scrollTextTroyMale;
|
||||
const char *scrollTextMedusa;
|
||||
const char *scrollTextHades;
|
||||
const char *powerImageWisdom;
|
||||
const char *powerImageStrength;
|
||||
const char *powerImageStealth;
|
||||
} assetTable[3] = {
|
||||
// Warm
|
||||
{
|
||||
"g0010oa0",
|
||||
"g0091ba0",
|
||||
"g0091bb0",
|
||||
"g0091bc0",
|
||||
"g0093bc0",
|
||||
"g0015oy0",
|
||||
"g0015oy3",
|
||||
"g0015td0",
|
||||
"g0015te0",
|
||||
"g0015te3",
|
||||
"g0015tf0",
|
||||
"g0015tg0",
|
||||
"g0290oa0",
|
||||
"g0290ob0",
|
||||
"g0290oc0"
|
||||
},
|
||||
|
||||
// Cold
|
||||
{
|
||||
"g0010ob0",
|
||||
"g0092ba0",
|
||||
"g0092bb0",
|
||||
"g0091bc0",
|
||||
"g0093bc0",
|
||||
"g0015oy2",
|
||||
"g0015oy5",
|
||||
"g0015td2",
|
||||
"g0015te2",
|
||||
"g0015te5",
|
||||
"g0015tf2",
|
||||
"g0015tg2",
|
||||
"g0092oa0",
|
||||
"g0092ob0",
|
||||
"g0092oc0"
|
||||
},
|
||||
|
||||
// Cool
|
||||
{
|
||||
"g0010oc0",
|
||||
"g0093ba0",
|
||||
"g0093bb0",
|
||||
"g0091bc0",
|
||||
"g0093bc0",
|
||||
"g0015oy1",
|
||||
"g0015oy4",
|
||||
"g0015td1",
|
||||
"g0015te1",
|
||||
"g0015te4",
|
||||
"g0015tf1",
|
||||
"g0015tg1",
|
||||
"g0093oa0",
|
||||
"g0093ob0",
|
||||
"g0093oc0"
|
||||
}
|
||||
};
|
||||
|
||||
HeroBelt::HeroBelt() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
_background[i] = loadImage(assetTable[i].background);
|
||||
_iconNames[i] = loadImageArray(assetTable[i].iconNames);
|
||||
_icons[i] = loadImageArray(assetTable[i].icons);
|
||||
_iconCursors[i] = loadImageArray(assetTable[i].iconCursors);
|
||||
_iconCursorsBright[i] = loadImageArray(assetTable[i].iconCursorsBright);
|
||||
_powerImages[0][i] = loadImageArray(assetTable[i].powerImageStrength);
|
||||
_powerImages[1][i] = loadImageArray(assetTable[i].powerImageStealth);
|
||||
_powerImages[2][i] = loadImageArray(assetTable[i].powerImageWisdom);
|
||||
|
||||
_scrollBg[i] = loadImage(assetTable[i].scrollBg);
|
||||
_scrollBgHades[i] = loadImage(assetTable[i].scrollBgHades);
|
||||
_scrollTextCrete[i] = loadImage(assetTable[i].scrollTextCrete);
|
||||
_scrollTextTroyFemale[i] = loadImage(assetTable[i].scrollTextTroyFemale);
|
||||
_scrollTextTroyMale[i] = loadImage(assetTable[i].scrollTextTroyMale);
|
||||
_scrollTextMedusa[i] = loadImage(assetTable[i].scrollTextMedusa);
|
||||
_scrollTextHades[i] = loadImage(assetTable[i].scrollTextHades);
|
||||
}
|
||||
|
||||
_branchOfLife = loadImageArray("v7150ba0");
|
||||
_overHeroBelt = false;
|
||||
_heroBeltY = kHeroBeltMaxY;
|
||||
_bottomEdge = false;
|
||||
_heroBeltSpeed = 0;
|
||||
_edgeStartTime = 0;
|
||||
_animateItem = kNone;
|
||||
_animateItemTargetSlot = -1;
|
||||
_hotZone = -1;
|
||||
_holdingItem = kNone;
|
||||
_holdingSlot = -1;
|
||||
_highlightTextIdx = -1;
|
||||
_showScroll = false;
|
||||
_hotZones.readHotzones(
|
||||
Common::SharedPtr<Common::SeekableReadStream>(g_vm->getWdPodFile()->getFileStream("HeroBelt.HOT")),
|
||||
true);
|
||||
}
|
||||
|
||||
void HeroBelt::computeHotZone(int time, Common::Point mousePos, bool mouseEnabled) {
|
||||
bool wasBottomEdge = _bottomEdge;
|
||||
|
||||
_overHeroBelt = false;
|
||||
_bottomEdge = false;
|
||||
|
||||
_mousePos = mousePos;
|
||||
|
||||
if (!mouseEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
_bottomEdge = mousePos.y > 460;
|
||||
_overHeroBelt = (_bottomEdge && _heroBeltSpeed < 0) || mousePos.y > _heroBeltY;
|
||||
|
||||
if (!wasBottomEdge && _bottomEdge)
|
||||
_edgeStartTime = time;
|
||||
|
||||
_currentTime = time;
|
||||
|
||||
int wasHotZone = _hotZone;
|
||||
_hotZone = _hotZones.pointToIndex(mousePos);
|
||||
if (_hotZone >= 0 && wasHotZone < 0) {
|
||||
_startHotTime = time;
|
||||
}
|
||||
|
||||
computeHighlight();
|
||||
}
|
||||
|
||||
static bool isInFrieze() {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
return (persistent->_currentRoomId == kMinotaurPuzzle && persistent->_quest != kCreteQuest)
|
||||
|| (persistent->_currentRoomId == kTrojanHorsePuzzle && persistent->_quest != kTroyQuest)
|
||||
|| (persistent->_currentRoomId == kMedusaPuzzle && persistent->_quest != kMedusaQuest)
|
||||
|| (persistent->_currentRoomId == kFerrymanPuzzle && persistent->_quest != kRescuePhilQuest)
|
||||
|| (persistent->_currentRoomId == kMonsterPuzzle && persistent->_quest != kRescuePhilQuest);
|
||||
}
|
||||
|
||||
void HeroBelt::render(Common::SharedPtr<GfxContext> context, int time, Common::Point viewPoint) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Common::Point beltPoint;
|
||||
|
||||
// TODO: use beltopen hotzone instead?
|
||||
if (persistent->_currentRoomId == kMonsterPuzzle) {
|
||||
_heroBeltY = kHeroBeltMinY;
|
||||
} else {
|
||||
if (_bottomEdge && _heroBeltY == kHeroBeltMaxY
|
||||
&& (time > _edgeStartTime + 500)) {
|
||||
_heroBeltSpeed = -10;
|
||||
}
|
||||
|
||||
if (!_overHeroBelt && _heroBeltY != kHeroBeltMaxY
|
||||
&& _animateItemTargetSlot == -1) {
|
||||
_showScroll = false;
|
||||
_heroBeltSpeed = +10;
|
||||
}
|
||||
|
||||
if (_heroBeltSpeed != 0) {
|
||||
_heroBeltY += _heroBeltSpeed;
|
||||
if (_heroBeltY <= kHeroBeltMinY) {
|
||||
_heroBeltY = kHeroBeltMinY;
|
||||
_heroBeltSpeed = 0;
|
||||
}
|
||||
if (_heroBeltY >= kHeroBeltMaxY) {
|
||||
_heroBeltY = kHeroBeltMaxY;
|
||||
_heroBeltSpeed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_currentTime = time;
|
||||
|
||||
beltPoint = Common::Point(0, _heroBeltY) + viewPoint;
|
||||
|
||||
_background[_colour].render(context, beltPoint);
|
||||
|
||||
if (_animateItem != kNone) {
|
||||
if (_currentTime > _animateItemStartTime + _animItemTime) {
|
||||
_animateItem = kNone;
|
||||
_animateItemTargetSlot = -1;
|
||||
_animItemCallbackEvent();
|
||||
}
|
||||
}
|
||||
|
||||
if (_animateItem != kNone) {
|
||||
Common::Point target = computeSlotPoint(_animateItemTargetSlot, true);
|
||||
Common::Point delta1 = target - _animateItemStartPoint;
|
||||
int elapsed = _currentTime - _animateItemStartTime;
|
||||
double fraction = ((elapsed + 0.0) / _animItemTime);
|
||||
Common::Point deltaPartial = fraction * delta1;
|
||||
Common::Point cur = _animateItemStartPoint + deltaPartial;
|
||||
_iconCursors[_colour][_animateItem - 1].render(
|
||||
context, cur + viewPoint);
|
||||
}
|
||||
|
||||
if (persistent->_currentRoomId == kMonsterPuzzle) {
|
||||
_icons[_colour][kNumberI].render(
|
||||
context,
|
||||
computeSlotPoint(0, false) + viewPoint
|
||||
+ Common::Point(0, 4));
|
||||
_icons[_colour][kNumberII].render(
|
||||
context,
|
||||
computeSlotPoint(1, false) + viewPoint);
|
||||
_icons[_colour][kNumberIII].render(
|
||||
context,
|
||||
computeSlotPoint(2, false) + viewPoint
|
||||
+ Common::Point(0, 4));
|
||||
for (unsigned i = 0; i < 3; i++) {
|
||||
_icons[_colour][_thunderboltFrame].render(
|
||||
context,
|
||||
computeSlotPoint(3 + i, false) + viewPoint);
|
||||
}
|
||||
|
||||
_branchOfLife[_branchOfLifeFrame].render(
|
||||
context, beltPoint);
|
||||
|
||||
} else {
|
||||
for (int i = 0; i < inventorySize; i++) {
|
||||
if (i == _animateItemTargetSlot || i == _holdingSlot
|
||||
|| persistent->_inventory[i] == kNone) {
|
||||
continue;
|
||||
}
|
||||
_icons[_colour][persistent->_inventory[i] - 1].render(
|
||||
context,
|
||||
computeSlotPoint(i, false) + viewPoint);
|
||||
}
|
||||
}
|
||||
|
||||
_icons[_colour][persistent->_hintsAreEnabled ? kActiveHints : kInactiveHints].render(
|
||||
context,
|
||||
computeSlotPoint(6, false) + viewPoint);
|
||||
|
||||
if (isInFrieze())
|
||||
_icons[_colour][kReturnToWall].render(
|
||||
context,
|
||||
computeSlotPoint(7, false) + viewPoint);
|
||||
else
|
||||
_icons[_colour][kQuestScroll].render(
|
||||
context,
|
||||
computeSlotPoint(7, false) + viewPoint);
|
||||
|
||||
_icons[_colour][kOptionsButton].render(
|
||||
context,
|
||||
computeSlotPoint(8, false) + viewPoint);
|
||||
|
||||
if (_highlightTextIdx >= 0) {
|
||||
_iconNames[_colour][_highlightTextIdx].render(
|
||||
context, beltPoint);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(persistent->_powerLevel); i++)
|
||||
if (persistent->_powerLevel[i] > 0) {
|
||||
_powerImages[i][_colour][(int)i == _selectedPower].render(
|
||||
context, beltPoint);
|
||||
}
|
||||
|
||||
if (_showScroll) {
|
||||
PodImage *bg = _scrollBg, *text = nullptr;
|
||||
switch (persistent->_quest) {
|
||||
case kCreteQuest:
|
||||
text = _scrollTextCrete;
|
||||
break;
|
||||
case kTroyQuest:
|
||||
if (persistent->_gender == kMale)
|
||||
text = _scrollTextTroyMale;
|
||||
else
|
||||
text = _scrollTextTroyFemale;
|
||||
break;
|
||||
case kMedusaQuest:
|
||||
text = _scrollTextMedusa;
|
||||
break;
|
||||
case kRescuePhilQuest:
|
||||
text = _scrollTextHades;
|
||||
bg = _scrollBgHades;
|
||||
break;
|
||||
case kEndGame:
|
||||
case kNoQuest:
|
||||
case kNumQuests:
|
||||
break;
|
||||
}
|
||||
bg[_colour].render(context, viewPoint);
|
||||
if (text != nullptr)
|
||||
text[_colour].render(
|
||||
context, viewPoint);
|
||||
}
|
||||
}
|
||||
|
||||
bool HeroBelt::isPositionOverHeroBelt(Common::Point mousePos) const {
|
||||
return mousePos.y > _heroBeltY;
|
||||
}
|
||||
|
||||
void HeroBelt::handleClick(Common::Point mousePos) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Common::String q = _hotZones.pointToName(mousePos);
|
||||
debug("handling belt click on <%s>", q.c_str());
|
||||
for (int i = 0; i < inventorySize; i++) {
|
||||
if (q == inventoryName(i)) {
|
||||
if (_holdingItem != kNone) {
|
||||
if (persistent->_inventory[i] != kNone &&
|
||||
_holdingSlot != i) {
|
||||
g_vm->fallbackClick();
|
||||
return;
|
||||
}
|
||||
persistent->_inventory[_holdingSlot] = kNone;
|
||||
persistent->_inventory[i] = _holdingItem;
|
||||
_holdingItem = kNone;
|
||||
_holdingSlot = -1;
|
||||
return;
|
||||
}
|
||||
if (i == _animateItemTargetSlot
|
||||
|| persistent->_inventory[i] == kNone)
|
||||
return;
|
||||
_holdingItem = persistent->_inventory[i];
|
||||
_holdingSlot = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (q == "quest scroll") {
|
||||
if (isInFrieze())
|
||||
g_vm->moveToRoom(kWallOfFameRoom);
|
||||
else
|
||||
_showScroll = true;
|
||||
}
|
||||
|
||||
if (q == "hints") {
|
||||
persistent->_hintsAreEnabled = !persistent->_hintsAreEnabled;
|
||||
}
|
||||
|
||||
if (q == "options") {
|
||||
g_vm->enterOptions();
|
||||
}
|
||||
|
||||
if (q == "strength")
|
||||
clickPower(kPowerStrength);
|
||||
if (q == "stealth")
|
||||
clickPower(kPowerStealth);
|
||||
if (q == "wisdom")
|
||||
clickPower(kPowerWisdom);
|
||||
}
|
||||
|
||||
void HeroBelt::clickPower(HeroPower pwr) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
Common::SharedPtr<VideoRoom> room = g_vm->getVideoRoom();
|
||||
|
||||
if (persistent->_currentRoomId == kMonsterPuzzle) {
|
||||
_selectedPower = pwr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (persistent->_quest == kRescuePhilQuest)
|
||||
return;
|
||||
|
||||
room->playSpeech(powerSounds[pwr][!!persistent->_powerLevel[pwr]]);
|
||||
}
|
||||
|
||||
void HeroBelt::computeHighlight() {
|
||||
Common::String hotZone = _hotZones.indexToName(_hotZone);
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
for (int i = 0; i < inventorySize; i++) {
|
||||
if (hotZone == inventoryName(i)) {
|
||||
if (persistent->_currentRoomId == kMonsterPuzzle) {
|
||||
_highlightTextIdx = i < 3 ? kNumberI + i : kLightning1 + i - 3;
|
||||
return;
|
||||
} else {
|
||||
if (persistent->_inventory[i] != kNone &&
|
||||
_holdingSlot != i) {
|
||||
_highlightTextIdx = persistent->_inventory[i] - 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hotZone == "quest scroll") {
|
||||
// TODO: what's with 30 and 28?
|
||||
if (isInFrieze())
|
||||
_highlightTextIdx = kReturnToWall;
|
||||
else if (persistent->_quest == kRescuePhilQuest)
|
||||
_highlightTextIdx = kHadesScroll;
|
||||
else
|
||||
_highlightTextIdx = kQuestScroll;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotZone == "stealth") {
|
||||
_highlightTextIdx = kPowerOfStealth;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotZone == "strength") {
|
||||
_highlightTextIdx = kPowerOfStrength;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotZone == "wisdom") {
|
||||
_highlightTextIdx = kPowerOfWisdom;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotZone == "hints") {
|
||||
_highlightTextIdx = persistent->_hintsAreEnabled ? kActiveHints : kInactiveHints;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotZone == "options") {
|
||||
_highlightTextIdx = kOptionsButton;
|
||||
return;
|
||||
}
|
||||
|
||||
_highlightTextIdx = -1;
|
||||
}
|
||||
|
||||
void HeroBelt::placeToInventory(InventoryItem item, EventHandlerWrapper callbackEvent) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
unsigned i;
|
||||
for (i = 0; i < inventorySize; i++) {
|
||||
if (persistent->_inventory[i] == kNone)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == inventorySize) {
|
||||
debug("Out of inventory space");
|
||||
return;
|
||||
}
|
||||
|
||||
persistent->_inventory[i] = item;
|
||||
_animateItem = item;
|
||||
_animItemCallbackEvent = callbackEvent;
|
||||
_animateItemStartPoint = _mousePos;
|
||||
_animateItemTargetSlot = i;
|
||||
_animateItemStartTime = _currentTime;
|
||||
_animItemTime = 2000;
|
||||
_heroBeltSpeed = -10;
|
||||
}
|
||||
|
||||
void HeroBelt::removeFromInventory(InventoryItem item) {
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
for (unsigned i = 0; i < inventorySize; i++) {
|
||||
if (persistent->_inventory[i] == item) {
|
||||
persistent->_inventory[i] = kNone;
|
||||
}
|
||||
}
|
||||
|
||||
if (_holdingItem == item) {
|
||||
_holdingItem = kNone;
|
||||
_holdingSlot = -1;
|
||||
}
|
||||
|
||||
if (_animateItem == item) {
|
||||
_animateItem = kNone;
|
||||
_animateItemTargetSlot = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void HeroBelt::clearHold() {
|
||||
_holdingItem = kNone;
|
||||
_holdingSlot = -1;
|
||||
}
|
||||
|
||||
Common::Point HeroBelt::computeSlotPoint(int slot, bool fullyExtended) {
|
||||
Common::Point ret = Common::Point(19, 35);
|
||||
ret += Common::Point(0, (fullyExtended ? kHeroBeltMinY : _heroBeltY));
|
||||
ret += Common::Point(39 * slot, slot % 2 ? 4 : 0);
|
||||
if (slot >= 6) {
|
||||
ret += Common::Point(253, 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int HeroBelt::getCursor(int time) {
|
||||
Common::String q = _hotZones.indexToName(_hotZone);
|
||||
Persistent *persistent = g_vm->getPersistent();
|
||||
if (q == "")
|
||||
return 0;
|
||||
for (unsigned i = 0; i < inventorySize; i++) {
|
||||
if (q == inventoryName(i)) {
|
||||
if ((int) i != _animateItemTargetSlot
|
||||
&& persistent->_inventory[i] != kNone)
|
||||
return (time - _startHotTime) / kDefaultSpeed % 3;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (time - _startHotTime) / kDefaultSpeed % 3;
|
||||
}
|
||||
|
||||
Common::String HeroBelt::inventoryName(int slot) {
|
||||
return Common::String::format("inventory%d", slot);
|
||||
}
|
||||
|
||||
bool HeroBelt::isHoldingItem() const {
|
||||
return _holdingItem != kNone;
|
||||
}
|
||||
|
||||
const Graphics::Cursor *HeroBelt::getHoldingItemCursor(int cursorAnimationFrame) const {
|
||||
if ((cursorAnimationFrame / 2) % 2 == 1)
|
||||
return &_iconCursorsBright[_colour][_holdingItem - 1];
|
||||
else
|
||||
return &_iconCursors[_colour][_holdingItem - 1];
|
||||
}
|
||||
|
||||
}
|
||||
132
engines/hadesch/herobelt.h
Normal file
132
engines/hadesch/herobelt.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_HEROBELT_H
|
||||
#define HADESCH_HEROBELT_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/ptr.h"
|
||||
#include "hadesch/pod_file.h"
|
||||
#include "hadesch/pod_image.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/str.h"
|
||||
#include "hadesch/enums.h"
|
||||
#include "hadesch/event.h"
|
||||
|
||||
namespace Hadesch {
|
||||
class HeroBelt {
|
||||
public:
|
||||
HeroBelt();
|
||||
|
||||
enum HeroBeltColour {
|
||||
kWarm,
|
||||
kCold,
|
||||
kCool,
|
||||
kNumColours
|
||||
};
|
||||
|
||||
void render(Common::SharedPtr<GfxContext> context, int time, Common::Point viewPoint);
|
||||
void computeHotZone(int time, Common::Point mousePos, bool mouseEnabled);
|
||||
void placeToInventory(InventoryItem item, EventHandlerWrapper callbackEvent = EventHandlerWrapper());
|
||||
void removeFromInventory(InventoryItem item);
|
||||
bool isOverHeroBelt() const {
|
||||
return _overHeroBelt;
|
||||
}
|
||||
bool isPositionOverHeroBelt(Common::Point mousePos) const;
|
||||
void handleClick(Common::Point mousePos);
|
||||
int getCursor(int time);
|
||||
bool isHoldingItem() const;
|
||||
InventoryItem getHoldingItem() const {
|
||||
return _holdingItem;
|
||||
}
|
||||
const Graphics::Cursor *getHoldingItemCursor(int cursorAnimationFrame) const;
|
||||
void clearHold();
|
||||
void setColour(HeroBeltColour colour) {
|
||||
_colour = colour;
|
||||
}
|
||||
void reset() {
|
||||
_colour = HeroBelt::kWarm;
|
||||
_selectedPower = kPowerNone;
|
||||
_branchOfLifeFrame = 0;
|
||||
_thunderboltFrame = kLightning1;
|
||||
}
|
||||
void setBranchOfLifeFrame(int frame) {
|
||||
_branchOfLifeFrame = frame;
|
||||
}
|
||||
void setThunderboltFrame(HeroBeltFrame frame) {
|
||||
_thunderboltFrame = frame;
|
||||
}
|
||||
HeroPower getSelectedStrength() {
|
||||
return _selectedPower;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::Point computeSlotPoint(int slot, bool fullyExtended);
|
||||
Common::String inventoryName(int slot);
|
||||
void computeHighlight();
|
||||
void clickPower(HeroPower pwr);
|
||||
|
||||
PodImage _background[kNumColours];
|
||||
Common::Array<PodImage> _iconNames[kNumColours];
|
||||
Common::Array<PodImage> _icons[kNumColours];
|
||||
Common::Array<PodImage> _iconCursors[kNumColours];
|
||||
Common::Array<PodImage> _iconCursorsBright[kNumColours];
|
||||
PodImage _scrollBg[kNumColours];
|
||||
PodImage _scrollBgHades[kNumColours];
|
||||
PodImage _scrollTextCrete[kNumColours];
|
||||
PodImage _scrollTextTroyMale[kNumColours];
|
||||
PodImage _scrollTextTroyFemale[kNumColours];
|
||||
PodImage _scrollTextMedusa[kNumColours];
|
||||
PodImage _scrollTextHades[kNumColours];
|
||||
Common::Array<PodImage> _powerImages[3][kNumColours];
|
||||
Common::Array<PodImage> _branchOfLife;
|
||||
|
||||
Common::Point _mousePos;
|
||||
EventHandlerWrapper _animItemCallbackEvent;
|
||||
HotZoneArray _hotZones;
|
||||
HeroBeltColour _colour;
|
||||
int _heroBeltY;
|
||||
int _heroBeltSpeed;
|
||||
bool _overHeroBelt;
|
||||
bool _bottomEdge;
|
||||
int _edgeStartTime;
|
||||
InventoryItem _animateItem;
|
||||
Common::Point _animateItemStartPoint;
|
||||
int _animateItemTargetSlot;
|
||||
int _animateItemStartTime;
|
||||
int _currentTime;
|
||||
int _animItemTime;
|
||||
int _hotZone;
|
||||
int _startHotTime;
|
||||
int _branchOfLifeFrame;
|
||||
InventoryItem _holdingItem;
|
||||
int _holdingSlot;
|
||||
int _highlightTextIdx;
|
||||
bool _showScroll;
|
||||
HeroPower _selectedPower;
|
||||
HeroBeltFrame _thunderboltFrame;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
200
engines/hadesch/hotzone.cpp
Normal file
200
engines/hadesch/hotzone.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/* 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 "common/file.h"
|
||||
|
||||
#include "hadesch/hotzone.h"
|
||||
|
||||
namespace Hadesch {
|
||||
bool HotZone::isEnabled() const {
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
void HotZone::setEnabled(bool val) {
|
||||
_enabled = val;
|
||||
}
|
||||
|
||||
|
||||
void HotZone::setOffset(Common::Point offset) {
|
||||
_offset = offset;
|
||||
}
|
||||
|
||||
// Following function copied from sword 2.5 and simplified
|
||||
bool HotZone::isInside(const Common::Point &point_in) const {
|
||||
Common::Point point = point_in - _offset;
|
||||
int rcross = 0; // Number of right-side overlaps
|
||||
|
||||
// Each edge is checked whether it cuts the outgoing stream from the point
|
||||
for (unsigned i = 0; i < _polygon.size(); i++) {
|
||||
const Common::Point &edgeStart = _polygon[i];
|
||||
const Common::Point &edgeEnd = _polygon[(i + 1) % _polygon.size()];
|
||||
|
||||
// A vertex is a point? Then it lies on one edge of the polygon
|
||||
if (point == edgeStart)
|
||||
return true;
|
||||
|
||||
if ((edgeStart.y > point.y) != (edgeEnd.y > point.y)) {
|
||||
int term1 = (edgeStart.x - point.x) * (edgeEnd.y - point.y) - (edgeEnd.x - point.x) * (edgeStart.y - point.y);
|
||||
int term2 = (edgeEnd.y - point.y) - (edgeStart.y - edgeEnd.y);
|
||||
if ((term1 > 0) == (term2 >= 0))
|
||||
rcross++;
|
||||
}
|
||||
}
|
||||
|
||||
// The point is strictly inside the polygon if and only if the number of overlaps is odd
|
||||
return ((rcross % 2) == 1);
|
||||
}
|
||||
|
||||
const Common::String &HotZone::getID() const {
|
||||
return _hotid;
|
||||
}
|
||||
|
||||
int HotZone::getICSH() const {
|
||||
return _icsh;
|
||||
}
|
||||
|
||||
HotZone::HotZone(const Common::Array<Common::Point> &polygon,
|
||||
const Common::String &hotid, bool enabled,
|
||||
int icsh) : _polygon(polygon), _hotid(hotid),
|
||||
_enabled(enabled), _icsh(icsh) {
|
||||
}
|
||||
|
||||
void HotZoneArray::readHotzones(Common::SharedPtr<Common::SeekableReadStream> hzFile, bool enable, Common::Point offset) {
|
||||
if (!hzFile) {
|
||||
debug("Invalid hzFile");
|
||||
return;
|
||||
}
|
||||
TagFile tf;
|
||||
tf.openStoreHot(hzFile);
|
||||
Common::ScopedPtr<Common::SeekableReadStream> tcshStream(tf.getFileStream(MKTAG('T', 'C', 'S', 'H')));
|
||||
int hzCnt = tcshStream->readUint32LE();
|
||||
|
||||
for (int idx = 0; idx < hzCnt; idx++) {
|
||||
Common::SharedPtr<Common::SeekableReadStream> tdshFile(tf.getFileStream(MKTAG('T', 'D', 'S', 'H'), idx));
|
||||
TagFile tdsh;
|
||||
tdsh.openStoreHotSub(tdshFile);
|
||||
|
||||
Common::SharedPtr<Common::SeekableReadStream> tvshFile(tdsh.getFileStream(MKTAG('T', 'V', 'S', 'H')));
|
||||
|
||||
Common::Array<Common::Point> polygon;
|
||||
|
||||
for (int j = 0; j < tvshFile->size() / 8; j++) {
|
||||
uint32 x = tvshFile->readUint32LE();
|
||||
uint32 y = tvshFile->readUint32LE();
|
||||
polygon.push_back(Common::Point(x, y) + offset);
|
||||
}
|
||||
|
||||
Common::SharedPtr<Common::SeekableReadStream> mnshFile(tdsh.getFileStream(MKTAG('M', 'N', 'S', 'H')));
|
||||
|
||||
int mnshSize = mnshFile->size();
|
||||
char *name = new (std::nothrow) char[mnshSize + 1];
|
||||
mnshFile->read(name, mnshSize);
|
||||
name[mnshSize] = 0;
|
||||
|
||||
Common::SharedPtr<Common::SeekableReadStream> icshFile(tdsh.getFileStream(MKTAG('I', 'C', 'S', 'H')));
|
||||
|
||||
int icsh = icshFile->readUint32LE();
|
||||
|
||||
_hotZones.push_back(HotZone(polygon, name, enable, icsh));
|
||||
|
||||
delete[] name;
|
||||
}
|
||||
}
|
||||
|
||||
HotZoneArray::HotZoneArray() {
|
||||
}
|
||||
|
||||
HotZoneArray::HotZoneArray(Common::SharedPtr<Common::SeekableReadStream> hzFile, bool enable) {
|
||||
readHotzones(hzFile, enable);
|
||||
}
|
||||
|
||||
void HotZoneArray::setHotzoneEnabled(Common::String const &name, bool val) {
|
||||
for (unsigned i = 0; i < _hotZones.size(); i++) {
|
||||
if (_hotZones[i].getID() == name)
|
||||
_hotZones[i].setEnabled(val);
|
||||
}
|
||||
}
|
||||
|
||||
int HotZoneArray::pointToIndex(Common::Point point) {
|
||||
for (unsigned i = 0; i < _hotZones.size(); i++) {
|
||||
if (_hotZones[i].isEnabled() && _hotZones[i].isInside(point)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void HotZoneArray::setHotZoneOffset(const Common::String &name, Common::Point offset) {
|
||||
for (unsigned i = 0; i < _hotZones.size(); i++) {
|
||||
if (_hotZones[i].getID() == name) {
|
||||
_hotZones[i].setOffset(offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::String HotZoneArray::pointToName(Common::Point point) {
|
||||
for (unsigned i = 0; i < _hotZones.size(); i++) {
|
||||
if (_hotZones[i].isEnabled() && _hotZones[i].isInside(point)) {
|
||||
return _hotZones[i].getID();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Common::String HotZoneArray::indexToName(int idx) {
|
||||
if (idx >= 0 && idx < (int)_hotZones.size()) {
|
||||
return _hotZones[idx].getID();
|
||||
} else
|
||||
return "";
|
||||
}
|
||||
|
||||
int HotZoneArray::indexToICSH(int idx) {
|
||||
if (idx < 0 || idx >= (int)_hotZones.size()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _hotZones[idx].getICSH();
|
||||
}
|
||||
|
||||
int HotZoneArray::indexToCursor(int idx, int frame) {
|
||||
if (idx < 0 || idx >= (int)_hotZones.size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (_hotZones[idx].getICSH()) {
|
||||
default:
|
||||
return frame % 3;
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 14; // left arrow
|
||||
case 3:
|
||||
return 16; // right arrow
|
||||
case 4:
|
||||
return 13; // up arrow
|
||||
case 5:
|
||||
return 15; // down arrow, never used, just a guess
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
73
engines/hadesch/hotzone.h
Normal file
73
engines/hadesch/hotzone.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_HOTZONE_H
|
||||
#define HADESCH_HOTZONE_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/str.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "hadesch/tag_file.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class HotZone {
|
||||
public:
|
||||
HotZone(const Common::Array<Common::Point> &polygon,
|
||||
const Common::String &hotid, bool enabled,
|
||||
int icsh);
|
||||
bool isInside(const Common::Point &pnt) const;
|
||||
const Common::String &getID() const;
|
||||
void load(const TagFile &file, int idx);
|
||||
void setEnabled(bool enabled);
|
||||
bool isEnabled() const;
|
||||
void setOffset(Common::Point offset);
|
||||
int getICSH() const;
|
||||
private:
|
||||
Common::String _hotid;
|
||||
Common::Array<Common::Point> _polygon;
|
||||
Common::Point _offset;
|
||||
bool _enabled;
|
||||
int _icsh;
|
||||
};
|
||||
|
||||
class HotZoneArray {
|
||||
public:
|
||||
HotZoneArray();
|
||||
HotZoneArray(Common::SharedPtr<Common::SeekableReadStream> hzFile, bool enable);
|
||||
|
||||
void setHotzoneEnabled(const Common::String &name, bool enabled);
|
||||
void readHotzones(Common::SharedPtr<Common::SeekableReadStream> hzFile,
|
||||
bool enable, Common::Point offset = Common::Point(0, 0));
|
||||
int pointToIndex(Common::Point point);
|
||||
Common::String pointToName(Common::Point point);
|
||||
void setHotZoneOffset(const Common::String &name, Common::Point offset);
|
||||
Common::String indexToName(int idx);
|
||||
int indexToCursor(int idx, int frame);
|
||||
int indexToICSH(int idx);
|
||||
private:
|
||||
Common::Array<HotZone> _hotZones;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
51
engines/hadesch/metaengine.cpp
Normal file
51
engines/hadesch/metaengine.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/* 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 "common/system.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
#include "hadesch/hadesch.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
class HadeschMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
|
||||
public:
|
||||
bool hasFeature(MetaEngineFeature f) const override {
|
||||
return
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
checkExtendedSaves(f);
|
||||
}
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override {
|
||||
*engine = new Hadesch::HadeschEngine(syst, desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "hadesch";
|
||||
}
|
||||
};
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(HADESCH)
|
||||
REGISTER_PLUGIN_DYNAMIC(HADESCH, PLUGIN_TYPE_ENGINE, HadeschMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(HADESCH, PLUGIN_TYPE_ENGINE, HadeschMetaEngine);
|
||||
#endif
|
||||
107
engines/hadesch/module.mk
Normal file
107
engines/hadesch/module.mk
Normal file
@@ -0,0 +1,107 @@
|
||||
MODULE := engines/hadesch
|
||||
|
||||
MODULE_OBJS = \
|
||||
metaengine.o \
|
||||
pod_file.o \
|
||||
tag_file.o \
|
||||
pod_image.o \
|
||||
video.o \
|
||||
hadesch.o \
|
||||
baptr.o \
|
||||
rooms/olympus.o \
|
||||
rooms/walloffame.o \
|
||||
rooms/argo.o \
|
||||
rooms/crete.o \
|
||||
rooms/minos.o \
|
||||
rooms/daedalus.o \
|
||||
rooms/seriphos.o \
|
||||
rooms/medisle.o \
|
||||
rooms/troy.o \
|
||||
rooms/quiz.o \
|
||||
rooms/minotaur.o \
|
||||
rooms/catacombs.o \
|
||||
rooms/priam.o \
|
||||
rooms/athena.o \
|
||||
rooms/volcano.o \
|
||||
rooms/riverstyx.o \
|
||||
rooms/hadesthrone.o \
|
||||
rooms/credits.o \
|
||||
rooms/intro.o \
|
||||
rooms/ferry.o \
|
||||
rooms/options.o \
|
||||
rooms/monster.o \
|
||||
rooms/monster/projectile.o \
|
||||
rooms/monster/typhoon.o \
|
||||
rooms/monster/cyclops.o \
|
||||
rooms/monster/illusion.o \
|
||||
rooms/medusa.o \
|
||||
rooms/trojan.o \
|
||||
gfx_context.o \
|
||||
ambient.o \
|
||||
herobelt.o \
|
||||
hotzone.o \
|
||||
table.o \
|
||||
persistent.o
|
||||
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_HADESCH), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
ifneq "$(HADESCH_RULES_INCLUDED)" "1"
|
||||
|
||||
HADESCH_RULES_INCLUDED := 1
|
||||
HADESCH_POTFILE := $(srcdir)/engines/hadesch/po/hadesch.pot
|
||||
HADESCH_POFILES := $(wildcard $(srcdir)/engines/hadesch/po/*.po)
|
||||
|
||||
hadesch-makepotfiles:
|
||||
find $(srcdir)/engines/hadesch -type f \( -iname '*.cpp' -or -iname '*.h' \) | sed s@^$(srcdir)/@@ > $(srcdir)/engines/hadesch/po/POTFILES_hadesch
|
||||
|
||||
hadesch-updatepot:
|
||||
cat $(srcdir)/engines/hadesch/po/POTFILES_hadesch | \
|
||||
xgettext -f - -D $(srcdir) -d hadesch --c++ -k_hs -kmake:2 --add-comments=I18N\
|
||||
-kDECLARE_TRANSLATION_ADDITIONAL_CONTEXT:1,2c \
|
||||
--copyright-holder="ScummVM Team" --package-name=ScummVM \
|
||||
--package-version=$(VERSION) --msgid-bugs-address=scummvm-devel@lists.scummvm.org -o $(HADESCH_POTFILE)_
|
||||
|
||||
sed -e 's/SOME DESCRIPTIVE TITLE/LANGUAGE translation for Hadesch transcriptions in ScummVM/' \
|
||||
-e 's/UTF-8/CHARSET/' -e 's/PACKAGE/ScummVM/' $(HADESCH_POTFILE)_ > $(HADESCH_POTFILE).new
|
||||
|
||||
rm $(HADESCH_POTFILE)_
|
||||
if test -f $(HADESCH_POTFILE); then \
|
||||
sed -f $(srcdir)/po/remove-potcdate.sed < $(HADESCH_POTFILE) > $(HADESCH_POTFILE).1 && \
|
||||
sed -f $(srcdir)/po/remove-potcdate.sed < $(HADESCH_POTFILE).new > $(HADESCH_POTFILE).2 && \
|
||||
if cmp $(HADESCH_POTFILE).1 $(HADESCH_POTFILE).2 >/dev/null 2>&1; then \
|
||||
rm -f $(HADESCH_POTFILE).new; \
|
||||
else \
|
||||
rm -f $(HADESCH_POTFILE) && \
|
||||
mv -f $(HADESCH_POTFILE).new $(HADESCH_POTFILE); \
|
||||
fi; \
|
||||
rm -f $(HADESCH_POTFILE).1 $(HADESCH_POTFILE).2; \
|
||||
else \
|
||||
mv -f $(HADESCH_POTFILE).new $(HADESCH_POTFILE); \
|
||||
fi;
|
||||
|
||||
engines/hadesch/po/%.po: $(HADESCH_POTFILE)
|
||||
msgmerge $@ $(HADESCH_POTFILE) -o $@.new
|
||||
if cmp $@ $@.new >/dev/null 2>&1; then \
|
||||
rm -f $@.new; \
|
||||
else \
|
||||
mv -f $@.new $@; \
|
||||
fi;
|
||||
|
||||
hadesch-translations-dat: devtools/create_translations
|
||||
devtools/create_translations/create_translations hadesch_translations.dat $(HADESCH_POFILES)
|
||||
mv hadesch_translations.dat $(srcdir)/dists/engine-data/hadesch_translations.dat
|
||||
|
||||
update-hadesch-translations: hadesch-updatepot $(HADESCH_POFILES) hadesch-translations-dat
|
||||
@$(foreach file, $(HADESCH_POFILES), echo -n $(notdir $(basename $(file)))": ";msgfmt --statistic $(file);)
|
||||
@rm -f messages.mo
|
||||
|
||||
.PHONY: updatehadeschpot hadesch-translations-dat update-hadesch-translations
|
||||
endif # HADESCH_RULES_INCLUDED
|
||||
312
engines/hadesch/persistent.cpp
Normal file
312
engines/hadesch/persistent.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/* 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 "common/debug-channels.h"
|
||||
#include "common/error.h"
|
||||
#include "common/events.h"
|
||||
#include "common/formats/ini-file.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/file.h"
|
||||
#include "common/keyboard.h"
|
||||
#include "common/macresman.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "hadesch/persistent.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
Persistent::Persistent() {
|
||||
_currentRoomId = kInvalidRoom;
|
||||
_previousRoomId = kInvalidRoom;
|
||||
_quest = kNoQuest;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_powerLevel); i++)
|
||||
_powerLevel[i] = 0;
|
||||
_hintsAreEnabled = true;
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_roomVisited); i++)
|
||||
_roomVisited[i] = false;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_argoSailedInQuest); i++)
|
||||
for (unsigned j = 0; j < ARRAYSIZE(_argoSailedInQuest[0]); j++)
|
||||
_argoSailedInQuest[i][j] = false;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_statuesTouched); i++)
|
||||
_statuesTouched[i] = false;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_statuePhase); i++)
|
||||
_statuePhase[i] = 0;
|
||||
|
||||
_argoSaidTroyFinally = false;
|
||||
_argoSaidCretePort = false;
|
||||
|
||||
_creteShowMerchant = false;
|
||||
_creteShowAtlantisBoat = false;
|
||||
_creteShowHorned = false;
|
||||
_creteShowHornless1 = false;
|
||||
_creteShowHornless2 = false;
|
||||
_creteShowHornless3 = false;
|
||||
_creteShowHornless4 = false;
|
||||
_creteDaedalusRoomAvailable = false;
|
||||
_creteMinosInstructed = false;
|
||||
_creteIntroMerchant = false;
|
||||
_cretePlayedEyeGhostTown = false;
|
||||
_creteIntroAtlantisBoat = false;
|
||||
_creteIntroAtlantisWood = false;
|
||||
_creteSandalsState = SANDALS_NOT_SOLVED;
|
||||
_creteStrongBoxState = BOX_CLOSED;
|
||||
_creteAlchemistExploded = false;
|
||||
_cretePlayedPhilAlchemist = false;
|
||||
_cretePlayedZeusCheckOutThatBox = false;
|
||||
_creteHadesPusnishesPainAndPanic = false;
|
||||
_creteVisitedAfterAlchemistIntro = false;
|
||||
_creteSaidHelenPermanentResident = false;
|
||||
|
||||
_daedalusShowedNote = false;
|
||||
|
||||
_seriphosStrawCartTaken = false;
|
||||
_seriphosPlayedMedusa = false;
|
||||
_seriphosPhilWarnedAthena = false;
|
||||
_seriphosPhilCurtainsItems = false;
|
||||
|
||||
_athenaPuzzleSolved = false;
|
||||
_athenaSwordTaken = false;
|
||||
_athenaShieldTaken = false;
|
||||
_athenaPlayedPainAndPanic = false;
|
||||
_athenaIntroPlayed = false;
|
||||
|
||||
_medisleStoneTaken = false;
|
||||
_medislePlayedPerseusIntro = false;
|
||||
_medisleShowFates = false;
|
||||
_medisleShowFatesIntro = false;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_medislePlacedItems); i++)
|
||||
_medislePlacedItems[i] = false;
|
||||
_medisleEyeballIsActive = false;
|
||||
_medisleEyePosition = kLachesis;
|
||||
_medisleBagPuzzleState = BAG_NOT_STARTED;
|
||||
_medislePlayedPhilFatesDesc = false;
|
||||
|
||||
_troyPlayAttack = false;
|
||||
_troyWallDamaged = false;
|
||||
_troyShowBricks = false;
|
||||
_troyShowBricks = false;
|
||||
_troyIsDefeated = false;
|
||||
_troyPlayedOdysseus = false;
|
||||
_troyKeyAndDecreeState = KEY_AND_DECREE_NOT_GIVEN;
|
||||
_troyMessageIsDelivered = false;
|
||||
_troyCatacombCounter = 0;
|
||||
_troyCatacombsUnlocked = false;
|
||||
_troyPlayedOdysseusCongrats = false;
|
||||
_troyPlayFinish = false;
|
||||
_doQuestIntro = false;
|
||||
_gender = kUnknown;
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_catacombVariants); i++)
|
||||
for (unsigned j = 0; j < ARRAYSIZE(_catacombVariants[0]); j++)
|
||||
_catacombVariants[i][j] = 0;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_catacombPaths); i++)
|
||||
for (unsigned j = 0; j < ARRAYSIZE(_catacombPaths[0]); j++)
|
||||
_catacombPaths[i][j] = kCatacombsHelen;
|
||||
_catacombLevel = kCatacombLevelSign;
|
||||
_catacombLastLevel = kCatacombLevelSign;
|
||||
_catacombDecoderSkullPosition = kCatacombsLeft;
|
||||
_catacombPainAndPanic = false;
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_creteTriedHornless); i++)
|
||||
_creteTriedHornless[i] = false;
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_daedalusLabItem); i++)
|
||||
_daedalusLabItem[i] = false;
|
||||
|
||||
_volcanoPainAndPanicIntroDone = false;
|
||||
_volcanoPuzzleState = Persistent::VOLCANO_NO_BOULDERS_THROWN;
|
||||
_volcanoHeyKid = false;
|
||||
_volcanoToStyxCounter = 0;
|
||||
|
||||
_styxCharonUsedPotion = false;
|
||||
_styxCharonUsedCoin = false;
|
||||
_styxAlchemistSaidIntro = false;
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_inventory); i++)
|
||||
_inventory[i] = kNone;
|
||||
}
|
||||
|
||||
void Persistent::clearInventory() {
|
||||
memset(_inventory, 0, sizeof (_inventory));
|
||||
}
|
||||
|
||||
bool Persistent::isInInventory(InventoryItem item) {
|
||||
for (unsigned i = 0; i < inventorySize; i++) {
|
||||
if (_inventory[i] == item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HadeschSaveDescriptor::HadeschSaveDescriptor(Common::Serializer &s, int slot) {
|
||||
s.matchBytes("hadesch", 7);
|
||||
s.syncVersion(2);
|
||||
if (s.getVersion() < 2) {
|
||||
Common::String str;
|
||||
s.syncString(str);
|
||||
_heroName = str;
|
||||
s.syncString(str);
|
||||
_slotName = str;
|
||||
} else {
|
||||
s.syncString32(_heroName);
|
||||
s.syncString32(_slotName);
|
||||
}
|
||||
s.syncAsByte(_room);
|
||||
_slot = slot;
|
||||
}
|
||||
|
||||
bool Persistent::syncGameStream(Common::Serializer &s) {
|
||||
if(!s.matchBytes("hadesch", 7))
|
||||
return false;
|
||||
if (!s.syncVersion(2))
|
||||
return false;
|
||||
|
||||
if (s.getVersion() < 2) {
|
||||
Common::String str;
|
||||
s.syncString(str);
|
||||
_heroName = str;
|
||||
s.syncString(str);
|
||||
_slotDescription = str;
|
||||
} else {
|
||||
s.syncString32(_heroName);
|
||||
s.syncString32(_slotDescription);
|
||||
}
|
||||
|
||||
s.syncAsByte(_currentRoomId);
|
||||
s.syncAsByte(_previousRoomId);
|
||||
|
||||
s.syncAsByte(_quest);
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_powerLevel); i++)
|
||||
s.syncAsByte(_powerLevel[i]);
|
||||
s.syncAsByte(_hintsAreEnabled);
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_roomVisited); i++)
|
||||
s.syncAsByte(_roomVisited[i]);
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_argoSailedInQuest); i++)
|
||||
for (unsigned j = 0; j < ARRAYSIZE(_argoSailedInQuest[0]); j++)
|
||||
s.syncAsByte(_argoSailedInQuest[i][j]);
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_statuesTouched); i++)
|
||||
s.syncAsByte(_statuesTouched[i]);
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_statuePhase); i++)
|
||||
s.syncAsByte(_statuePhase[i]);
|
||||
|
||||
s.syncAsByte(_argoSaidTroyFinally);
|
||||
s.syncAsByte(_argoSaidCretePort);
|
||||
|
||||
s.syncAsByte(_creteShowMerchant);
|
||||
s.syncAsByte(_creteShowAtlantisBoat);
|
||||
s.syncAsByte(_creteShowHorned);
|
||||
s.syncAsByte(_creteShowHornless1);
|
||||
s.syncAsByte(_creteShowHornless2);
|
||||
s.syncAsByte(_creteShowHornless3);
|
||||
s.syncAsByte(_creteShowHornless4);
|
||||
s.syncAsByte(_creteDaedalusRoomAvailable);
|
||||
s.syncAsByte(_creteMinosInstructed);
|
||||
s.syncAsByte(_creteIntroMerchant);
|
||||
s.syncAsByte(_cretePlayedEyeGhostTown);
|
||||
s.syncAsByte(_creteIntroAtlantisBoat);
|
||||
s.syncAsByte(_creteIntroAtlantisWood);
|
||||
s.syncAsByte(_creteSandalsState);
|
||||
s.syncAsByte(_creteStrongBoxState);
|
||||
s.syncAsByte(_creteAlchemistExploded);
|
||||
s.syncAsByte(_cretePlayedPhilAlchemist);
|
||||
s.syncAsByte(_cretePlayedZeusCheckOutThatBox);
|
||||
s.syncAsByte(_creteHadesPusnishesPainAndPanic);
|
||||
s.syncAsByte(_creteVisitedAfterAlchemistIntro);
|
||||
s.syncAsByte(_creteSaidHelenPermanentResident);
|
||||
|
||||
s.syncAsByte(_daedalusShowedNote);
|
||||
|
||||
s.syncAsByte(_seriphosStrawCartTaken);
|
||||
s.syncAsByte(_seriphosPlayedMedusa);
|
||||
s.syncAsByte(_seriphosPhilWarnedAthena);
|
||||
s.syncAsByte(_seriphosPhilCurtainsItems);
|
||||
|
||||
s.syncAsByte(_athenaPuzzleSolved);
|
||||
s.syncAsByte(_athenaSwordTaken);
|
||||
s.syncAsByte(_athenaShieldTaken);
|
||||
s.syncAsByte(_athenaPlayedPainAndPanic);
|
||||
s.syncAsByte(_athenaIntroPlayed);
|
||||
|
||||
s.syncAsByte(_medisleStoneTaken);
|
||||
s.syncAsByte(_medislePlayedPerseusIntro);
|
||||
s.syncAsByte(_medisleShowFates);
|
||||
s.syncAsByte(_medisleShowFatesIntro);
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_statuePhase); i++)
|
||||
s.syncAsByte(_medislePlacedItems[i]);
|
||||
s.syncAsByte(_medisleEyeballIsActive);
|
||||
s.syncAsByte(_medisleEyePosition);
|
||||
s.syncAsByte(_medisleBagPuzzleState);
|
||||
s.syncAsByte(_medislePlayedPhilFatesDesc);
|
||||
|
||||
s.syncAsByte(_troyPlayAttack);
|
||||
s.syncAsByte(_troyWallDamaged);
|
||||
s.syncAsByte(_troyShowBricks);
|
||||
s.syncAsByte(_troyShowBricks);
|
||||
s.syncAsByte(_troyIsDefeated);
|
||||
s.syncAsByte(_troyPlayedOdysseus);
|
||||
s.syncAsByte(_troyKeyAndDecreeState);
|
||||
s.syncAsByte(_troyMessageIsDelivered);
|
||||
s.syncAsByte(_troyCatacombCounter);
|
||||
s.syncAsByte(_troyCatacombsUnlocked);
|
||||
s.syncAsByte(_troyPlayedOdysseusCongrats);
|
||||
s.syncAsByte(_troyPlayFinish);
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_catacombVariants); i++)
|
||||
for (unsigned j = 0; j < ARRAYSIZE(_catacombVariants[0]); j++)
|
||||
s.syncAsByte(_catacombVariants[i][j]);
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_catacombPaths); i++)
|
||||
for (unsigned j = 0; j < ARRAYSIZE(_catacombPaths[0]); j++)
|
||||
s.syncAsByte(_catacombPaths[i][j]);
|
||||
s.syncAsByte(_catacombLevel);
|
||||
s.syncAsByte(_catacombLastLevel);
|
||||
s.syncAsByte(_catacombDecoderSkullPosition);
|
||||
s.syncAsByte(_catacombPainAndPanic);
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_creteTriedHornless); i++)
|
||||
s.syncAsByte(_creteTriedHornless[i]);
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_daedalusLabItem); i++)
|
||||
s.syncAsByte(_daedalusLabItem[i]);
|
||||
|
||||
s.syncAsByte(_volcanoPainAndPanicIntroDone);
|
||||
s.syncAsByte(_volcanoPuzzleState);
|
||||
s.syncAsByte(_volcanoHeyKid);
|
||||
s.syncAsByte(_volcanoToStyxCounter);
|
||||
|
||||
s.syncAsByte(_styxCharonUsedPotion);
|
||||
s.syncAsByte(_styxCharonUsedCoin);
|
||||
s.syncAsByte(_styxAlchemistSaidIntro);
|
||||
|
||||
for (unsigned i = 0; i < ARRAYSIZE(_inventory); i++)
|
||||
s.syncAsByte(_inventory[i]);
|
||||
|
||||
s.syncAsByte(_gender, 1);
|
||||
|
||||
debug("serialized");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
188
engines/hadesch/persistent.h
Normal file
188
engines/hadesch/persistent.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/* 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 "common/serializer.h"
|
||||
|
||||
#include "hadesch/enums.h"
|
||||
|
||||
#ifndef HADESCH_PERSISTENT_H
|
||||
#define HADESCH_PERSISTENT_H
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
struct HadeschSaveDescriptor {
|
||||
HadeschSaveDescriptor(Common::Serializer &s, int slot);
|
||||
|
||||
int _slot;
|
||||
Common::U32String _heroName;
|
||||
Common::U32String _slotName;
|
||||
RoomId _room;
|
||||
};
|
||||
|
||||
struct HadeschSaveDescriptorSlotComparator {
|
||||
bool operator()(const HadeschSaveDescriptor &x, const HadeschSaveDescriptor &y) const {
|
||||
return x._slot < y._slot;
|
||||
}
|
||||
};
|
||||
|
||||
static const int inventorySize = 6;
|
||||
|
||||
struct Persistent {
|
||||
// Generic
|
||||
Gender _gender;
|
||||
Common::U32String _heroName;
|
||||
Common::U32String _slotDescription; // valid only in saves
|
||||
Quest _quest;
|
||||
int _powerLevel[3];
|
||||
RoomId _currentRoomId;
|
||||
RoomId _previousRoomId;
|
||||
bool _roomVisited[kNumRooms];
|
||||
bool _statuesTouched[kNumStatues];
|
||||
int _statuePhase[kNumStatues];
|
||||
bool _doQuestIntro;
|
||||
InventoryItem _inventory[inventorySize];
|
||||
bool _hintsAreEnabled;
|
||||
|
||||
// Argo
|
||||
bool _argoSailedInQuest[kNumRooms][kNumQuests];
|
||||
bool _argoSaidTroyFinally;
|
||||
bool _argoSaidCretePort;
|
||||
|
||||
// Crete and Minos
|
||||
bool _creteShowMerchant;
|
||||
bool _creteShowAtlantisBoat;
|
||||
bool _creteShowHorned;
|
||||
bool _creteShowHornless1;
|
||||
bool _creteShowHornless2;
|
||||
bool _creteShowHornless3;
|
||||
bool _creteShowHornless4;
|
||||
bool _creteDaedalusRoomAvailable;
|
||||
bool _creteMinosInstructed;
|
||||
bool _creteIntroMerchant;
|
||||
bool _cretePlayedEyeGhostTown;
|
||||
bool _creteTriedHornless[4];
|
||||
bool _creteIntroAtlantisBoat;
|
||||
bool _creteIntroAtlantisWood;
|
||||
bool _creteAlchemistExploded;
|
||||
enum CreteSandalsState {
|
||||
SANDALS_NOT_SOLVED,
|
||||
SANDALS_SOLVED,
|
||||
SANDALS_TAKEN
|
||||
} _creteSandalsState;
|
||||
enum CreteStrongBoxState {
|
||||
BOX_CLOSED, BOX_OPEN,
|
||||
BOX_OPEN_POTION, BOX_OPEN_NO_POTION } _creteStrongBoxState;
|
||||
bool _cretePlayedPhilAlchemist;
|
||||
bool _cretePlayedZeusCheckOutThatBox;
|
||||
bool _creteHadesPusnishesPainAndPanic;
|
||||
bool _creteVisitedAfterAlchemistIntro;
|
||||
bool _creteSaidHelenPermanentResident;
|
||||
|
||||
// Daedalus
|
||||
bool _daedalusShowedNote;
|
||||
bool _daedalusLabItem[4];
|
||||
|
||||
// Seriphos
|
||||
bool _seriphosStrawCartTaken;
|
||||
bool _seriphosPlayedMedusa;
|
||||
bool _seriphosPhilWarnedAthena;
|
||||
bool _seriphosPhilCurtainsItems;
|
||||
|
||||
// Athena
|
||||
bool _athenaPuzzleSolved;
|
||||
bool _athenaSwordTaken;
|
||||
bool _athenaShieldTaken;
|
||||
bool _athenaPlayedPainAndPanic;
|
||||
bool _athenaIntroPlayed;
|
||||
|
||||
// Medusa Island
|
||||
bool _medisleStoneTaken;
|
||||
bool _medislePlacedItems[5];
|
||||
bool _medislePlayedPerseusIntro;
|
||||
bool _medisleShowFates;
|
||||
bool _medisleShowFatesIntro;
|
||||
bool _medisleEyeballIsActive;
|
||||
FateId _medisleEyePosition;
|
||||
enum MedisleBagPuzzleState {
|
||||
BAG_NOT_STARTED,
|
||||
BAG_STARTED,
|
||||
BAG_SOLVED,
|
||||
BAG_TAKEN
|
||||
} _medisleBagPuzzleState;
|
||||
bool _medislePlayedPhilFatesDesc;
|
||||
|
||||
// Troy
|
||||
bool _troyPlayAttack;
|
||||
bool _troyWallDamaged;
|
||||
bool _troyShowBricks;
|
||||
bool _troyIsDefeated;
|
||||
bool _troyPlayedOdysseus;
|
||||
bool _troyMessageIsDelivered;
|
||||
enum TroyKeyAndDecreeState {
|
||||
KEY_AND_DECREE_NOT_GIVEN,
|
||||
KEY_AND_DECREE_THROWN,
|
||||
KEY_AND_DECREE_TAKEN
|
||||
} _troyKeyAndDecreeState;
|
||||
int _troyCatacombCounter;
|
||||
bool _troyCatacombsUnlocked;
|
||||
bool _troyPlayedOdysseusCongrats;
|
||||
bool _troyPlayFinish;
|
||||
|
||||
// Catacombs
|
||||
int _catacombVariants[3][3];
|
||||
CatacombsPath _catacombPaths[3][3];
|
||||
CatacombsLevel _catacombLevel;
|
||||
CatacombsPosition _catacombDecoderSkullPosition;
|
||||
CatacombsLevel _catacombLastLevel;
|
||||
bool _catacombPainAndPanic;
|
||||
|
||||
// Volcano
|
||||
bool _volcanoPainAndPanicIntroDone;
|
||||
bool _volcanoHeyKid;
|
||||
enum VolcanoPuzzleState {
|
||||
VOLCANO_NO_BOULDERS_THROWN,
|
||||
VOLCANO_SQUASHED_PANIC,
|
||||
VOLCANO_BOULDER_ON_VOLCANO,
|
||||
VOLCANO_HELMET_SHOWN
|
||||
} _volcanoPuzzleState;
|
||||
int _volcanoToStyxCounter;
|
||||
|
||||
// River Styx
|
||||
bool _styxCharonUsedPotion;
|
||||
bool _styxCharonUsedCoin;
|
||||
bool _styxAlchemistSaidIntro;
|
||||
|
||||
Persistent();
|
||||
|
||||
bool isInInventory(InventoryItem item);
|
||||
|
||||
bool isRoomVisited(RoomId id) const {
|
||||
return _roomVisited[id];
|
||||
}
|
||||
|
||||
void clearInventory();
|
||||
|
||||
bool syncGameStream(Common::Serializer &s);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
58
engines/hadesch/po/POTFILES_hadesch
Normal file
58
engines/hadesch/po/POTFILES_hadesch
Normal file
@@ -0,0 +1,58 @@
|
||||
engines/hadesch/herobelt.h
|
||||
engines/hadesch/pod_image.h
|
||||
engines/hadesch/gfx_context.h
|
||||
engines/hadesch/persistent.h
|
||||
engines/hadesch/ambient.h
|
||||
engines/hadesch/table.cpp
|
||||
engines/hadesch/gfx_context.cpp
|
||||
engines/hadesch/event.h
|
||||
engines/hadesch/herobelt.cpp
|
||||
engines/hadesch/tag_file.cpp
|
||||
engines/hadesch/detection.cpp
|
||||
engines/hadesch/tag_file.h
|
||||
engines/hadesch/hotzone.h
|
||||
engines/hadesch/video.cpp
|
||||
engines/hadesch/detection_tables.h
|
||||
engines/hadesch/enums.h
|
||||
engines/hadesch/hadesch.h
|
||||
engines/hadesch/ambient.cpp
|
||||
engines/hadesch/hadesch.cpp
|
||||
engines/hadesch/video.h
|
||||
engines/hadesch/pod_file.cpp
|
||||
engines/hadesch/pod_file.h
|
||||
engines/hadesch/table.h
|
||||
engines/hadesch/rooms/credits.cpp
|
||||
engines/hadesch/rooms/monster.h
|
||||
engines/hadesch/rooms/quiz.cpp
|
||||
engines/hadesch/rooms/monster.cpp
|
||||
engines/hadesch/rooms/trojan.cpp
|
||||
engines/hadesch/rooms/priam.cpp
|
||||
engines/hadesch/rooms/options.cpp
|
||||
engines/hadesch/rooms/medusa.cpp
|
||||
engines/hadesch/rooms/medisle.cpp
|
||||
engines/hadesch/rooms/seriphos.cpp
|
||||
engines/hadesch/rooms/catacombs.cpp
|
||||
engines/hadesch/rooms/riverstyx.cpp
|
||||
engines/hadesch/rooms/volcano.cpp
|
||||
engines/hadesch/rooms/minos.cpp
|
||||
engines/hadesch/rooms/olympus.cpp
|
||||
engines/hadesch/rooms/intro.cpp
|
||||
engines/hadesch/rooms/crete.cpp
|
||||
engines/hadesch/rooms/athena.cpp
|
||||
engines/hadesch/rooms/daedalus.cpp
|
||||
engines/hadesch/rooms/walloffame.cpp
|
||||
engines/hadesch/rooms/hadesthrone.cpp
|
||||
engines/hadesch/rooms/troy.cpp
|
||||
engines/hadesch/rooms/ferry.cpp
|
||||
engines/hadesch/rooms/minotaur.cpp
|
||||
engines/hadesch/rooms/monster/projectile.cpp
|
||||
engines/hadesch/rooms/monster/cyclops.cpp
|
||||
engines/hadesch/rooms/monster/illusion.cpp
|
||||
engines/hadesch/rooms/monster/typhoon.cpp
|
||||
engines/hadesch/rooms/argo.cpp
|
||||
engines/hadesch/baptr.h
|
||||
engines/hadesch/baptr.cpp
|
||||
engines/hadesch/pod_image.cpp
|
||||
engines/hadesch/metaengine.cpp
|
||||
engines/hadesch/hotzone.cpp
|
||||
engines/hadesch/persistent.cpp
|
||||
126
engines/hadesch/pod_file.cpp
Normal file
126
engines/hadesch/pod_file.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/* 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 "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/macresman.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
#include "hadesch/hadesch.h"
|
||||
#include "hadesch/pod_file.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
PodFile::PodFile(const Common::String &debugName) {
|
||||
_debugName = debugName;
|
||||
}
|
||||
|
||||
Common::String PodFile::getDebugName() const {
|
||||
return _debugName;
|
||||
}
|
||||
|
||||
bool PodFile::openStore(const Common::SharedPtr<Common::SeekableReadStream> &parentStream) {
|
||||
byte buf[16];
|
||||
if (!parentStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parentStream->read(buf, 12) != 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(buf, "Pod File\0\0\0\0", 12) != 0 &&
|
||||
memcmp(buf, "Pod\0file\0\0\0\0", 12) != 0 &&
|
||||
memcmp(buf, "Pod\0\0\0\0\0\0\0\0\0", 12) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32 numFiles = parentStream->readUint32LE();
|
||||
uint32 offset = 16 + 16 * numFiles;
|
||||
|
||||
_descriptions.resize(numFiles);
|
||||
|
||||
for (uint i = 0; i < _descriptions.size(); ++i) {
|
||||
parentStream->read(buf, 12);
|
||||
buf[12] = '\0';
|
||||
const uint32 size = parentStream->readUint32LE();
|
||||
|
||||
_descriptions[i].name = (const char *) buf;
|
||||
_descriptions[i].offset = offset;
|
||||
_descriptions[i].size = size;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
_file = parentStream;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PodFile::openStore(const Common::Path &name) {
|
||||
if (name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::SharedPtr<Common::SeekableReadStream> stream(Common::MacResManager::openFileOrDataFork(name));
|
||||
if (!stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return openStore(stream);
|
||||
}
|
||||
|
||||
// It's tempting to use substream but substream is not thread safe
|
||||
Common::SeekableReadStream *memSubstream(Common::SharedPtr<Common::SeekableReadStream> file,
|
||||
uint32 offset, uint32 size) {
|
||||
if (size == 0)
|
||||
return new Common::MemoryReadStream(new byte[1], 0, DisposeAfterUse::YES);
|
||||
file->seek(offset);
|
||||
return file->readStream(size);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *PodFile::getFileStream(const Common::String &name) const {
|
||||
for (uint j = 0; j < _descriptions.size(); ++j) {
|
||||
const Description &desc = _descriptions[j];
|
||||
if (desc.name.compareToIgnoreCase(name) == 0) {
|
||||
return memSubstream(
|
||||
_file, desc.offset, desc.size);
|
||||
}
|
||||
}
|
||||
debugC(kHadeschDebugResources, "PodFile: %s not found", name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::Array <PodImage> PodFile::loadImageArray() const {
|
||||
Common::Array <PodImage> pis;
|
||||
|
||||
for (int idx = 1; ; idx++) {
|
||||
PodImage pi;
|
||||
if (!pi.loadImage(*this, idx)) {
|
||||
break;
|
||||
}
|
||||
pis.push_back(pi);
|
||||
}
|
||||
|
||||
return pis;
|
||||
}
|
||||
|
||||
} // End of namespace Hadesch
|
||||
64
engines/hadesch/pod_file.h
Normal file
64
engines/hadesch/pod_file.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_FILE_MGR_H
|
||||
#define HADESCH_FILE_MGR_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
class Path;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class PodImage;
|
||||
|
||||
Common::SeekableReadStream *memSubstream(Common::SharedPtr<Common::SeekableReadStream> file,
|
||||
uint32 offset, uint32 size);
|
||||
class PodFile {
|
||||
public:
|
||||
PodFile(const Common::String &debugName);
|
||||
bool openStore(const Common::Path &name);
|
||||
bool openStore(const Common::SharedPtr<Common::SeekableReadStream> &parentstream);
|
||||
|
||||
Common::SeekableReadStream *getFileStream(const Common::String &name) const;
|
||||
Common::String getDebugName() const;
|
||||
Common::Array <PodImage> loadImageArray() const;
|
||||
|
||||
private:
|
||||
struct Description {
|
||||
Common::String name;
|
||||
uint32 offset;
|
||||
uint32 size;
|
||||
};
|
||||
Common::SharedPtr<Common::SeekableReadStream> _file;
|
||||
Common::Array<Description> _descriptions;
|
||||
Common::String _debugName;
|
||||
};
|
||||
|
||||
} // End of namespace Hadesch
|
||||
|
||||
#endif
|
||||
270
engines/hadesch/pod_image.cpp
Normal file
270
engines/hadesch/pod_image.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/* 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 "common/debug.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "hadesch/pod_image.h"
|
||||
#include "hadesch/tag_file.h"
|
||||
#include "hadesch/baptr.h"
|
||||
#include "hadesch/gfx_context.h"
|
||||
|
||||
namespace Hadesch {
|
||||
PodImage::PodImage() {
|
||||
_w = 0;
|
||||
_h = 0;
|
||||
_pos = Common::Point(0, 0);
|
||||
_ncolors = 0;
|
||||
}
|
||||
|
||||
PodImage::~PodImage() {
|
||||
}
|
||||
|
||||
bool PodImage::loadImage(const PodFile &col, int index) {
|
||||
char bufname[256];
|
||||
snprintf (bufname, sizeof(bufname) - 1, "%d", index);
|
||||
Common::SharedPtr<Common::SeekableReadStream> dataStream(col.getFileStream(bufname));
|
||||
if (!dataStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::SharedPtr<Common::SeekableReadStream> palStream(col.getFileStream("0"));
|
||||
TagFile palTags;
|
||||
if (!palStream || !palTags.openStoreCel(palStream)) {
|
||||
debug("Couldn't open palette");
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::SharedPtr<Common::SeekableReadStream> palTagStream(palTags.getFileStream(MKTAG('P', 'A', 'L', ' ')));
|
||||
|
||||
if (!palTagStream) {
|
||||
debug("Couldn't open PAL palette in image %s", col.getDebugName().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint palSize = palTagStream->size();
|
||||
if (palSize > 256 * 4) {
|
||||
debug("Palette unexpectedly large");
|
||||
palSize = 256 * 4;
|
||||
}
|
||||
|
||||
_palette = sharedPtrByteAlloc(256 * 4);
|
||||
memset(_palette.get(), 0, 256 * 4);
|
||||
_paletteCursor = sharedPtrByteAlloc(256 * 3);
|
||||
memset(_paletteCursor.get(), 0, 256 * 3);
|
||||
|
||||
palTagStream->read(_palette.get(), palSize);
|
||||
_ncolors = palSize / 4;
|
||||
|
||||
for (int i = 0; i < _ncolors; i++) {
|
||||
int color = _palette.get()[4 * i] & 0xff;
|
||||
|
||||
_paletteCursor.get()[3 * color ] = _palette.get()[4 * i + 1];
|
||||
_paletteCursor.get()[3 * color + 1] = _palette.get()[4 * i + 2];
|
||||
_paletteCursor.get()[3 * color + 2] = _palette.get()[4 * i + 3];
|
||||
}
|
||||
|
||||
TagFile dataTags;
|
||||
if (!dataTags.openStoreCel(dataStream)) {
|
||||
debug("Couldn't open data for image %d", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> infoTagStream(dataTags.getFileStream(MKTAG('I', 'N', 'F', 'O')));
|
||||
|
||||
if (!infoTagStream) {
|
||||
debug("Couldn't open INFO");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (infoTagStream->size() < 0x1c) {
|
||||
debug("INFO section too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
infoTagStream->skip(0xc);
|
||||
int x = -(int32)infoTagStream->readUint32BE();
|
||||
int y = -(int32)infoTagStream->readUint32BE();
|
||||
_pos = Common::Point(x,y);
|
||||
_w = infoTagStream->readUint32BE();
|
||||
_h = infoTagStream->readUint32BE();
|
||||
|
||||
// Empty image
|
||||
if (_w < 0 || _h < 0) {
|
||||
_w = 0;
|
||||
_h = 0;
|
||||
_pixels = sharedPtrByteAlloc(1);
|
||||
memset(_pixels.get(), 0, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
_pixels = sharedPtrByteAlloc(_w * _h);
|
||||
memset(_pixels.get(), 0, _w * _h);
|
||||
// TODO: check this
|
||||
_hotspot = Common::Point(_w / 2, _h / 2);
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> dataTagStream(dataTags.getFileStream(MKTAG('D', 'A', 'T', 'A')));
|
||||
|
||||
if (!dataTagStream) {
|
||||
debug("Couldn't open DATA in image %s, index %d", col.getDebugName().c_str(), index);
|
||||
return false;
|
||||
}
|
||||
|
||||
int linerem = _w;
|
||||
int line = 0;
|
||||
|
||||
for (int pos = 0; pos < _w * _h && !dataTagStream->eos(); ) {
|
||||
byte rlelen = dataTagStream->readByte();
|
||||
byte rleval = dataTagStream->readByte();
|
||||
if (dataTagStream->eos()) {
|
||||
break;
|
||||
}
|
||||
if (rlelen != 0) {
|
||||
int len = rlelen;
|
||||
if (len > linerem) {
|
||||
len = linerem;
|
||||
}
|
||||
memset(_pixels.get() + pos, rleval, len);
|
||||
linerem -= len;
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rleval != 0) {
|
||||
int len = rleval;
|
||||
if (len > linerem) {
|
||||
len = linerem;
|
||||
}
|
||||
dataTagStream->read(_pixels.get() + pos, len);
|
||||
linerem -= len;
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
// End of line
|
||||
line++;
|
||||
linerem = _w;
|
||||
pos = line * _w;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Naive implementation as it's used very rarely
|
||||
// Nearest neighbour, unoptimized
|
||||
void PodImage::makeScale(int scale) const {
|
||||
struct ScaledVersion sv;
|
||||
sv._w = _w * scale / 100;
|
||||
sv._h = _h * scale / 100;
|
||||
sv._pixels = sharedPtrByteAlloc(sv._h * sv._w);
|
||||
for (int x = 0; x < sv._w; x++) {
|
||||
int ox = x * _w / sv._w;
|
||||
if (ox >= _w)
|
||||
ox = _w - 1;
|
||||
if (ox < 0)
|
||||
ox = 0;
|
||||
for (int y = 0; y < sv._h; y++) {
|
||||
int oy = y * _h / sv._h;
|
||||
if (oy >= _h)
|
||||
oy = _h - 1;
|
||||
if (oy < 0)
|
||||
oy = 0;
|
||||
sv._pixels.get()[x + y * sv._w] = _pixels.get()[ox + oy * _w];
|
||||
}
|
||||
}
|
||||
_scales[scale] = sv;
|
||||
}
|
||||
|
||||
void PodImage::render(Common::SharedPtr<GfxContext> context,
|
||||
Common::Point offset,
|
||||
int colourScale,
|
||||
int scale) const {
|
||||
byte *originalPalette = _palette.get();
|
||||
byte *scaledPalette = nullptr;
|
||||
if (colourScale != 0x100) {
|
||||
scaledPalette = new byte[_ncolors * 4];
|
||||
for (unsigned i = 0; (int)i < _ncolors; i++) {
|
||||
scaledPalette[4 * i] = originalPalette[4 * i];
|
||||
scaledPalette[4 * i + 1] = (originalPalette[4 * i + 1] * colourScale) >> 8;
|
||||
scaledPalette[4 * i + 2] = (originalPalette[4 * i + 2] * colourScale) >> 8;
|
||||
scaledPalette[4 * i + 3] = (originalPalette[4 * i + 3] * colourScale) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (scale == 100)
|
||||
context->blitPodImage(_pixels.get(), _w, _w, _h,
|
||||
scaledPalette ? scaledPalette : originalPalette, _ncolors, _pos + offset);
|
||||
else {
|
||||
if (!_scales.contains(scale))
|
||||
makeScale(scale);
|
||||
context->blitPodImage(_scales[scale]._pixels.get(), _scales[scale]._w, _scales[scale]._w, _scales[scale]._h,
|
||||
scaledPalette ? scaledPalette : originalPalette, _ncolors, _pos * (scale / 100.0) + offset);
|
||||
}
|
||||
if (scaledPalette)
|
||||
delete [] scaledPalette;
|
||||
}
|
||||
|
||||
Common::Point PodImage::getOffset() const {
|
||||
return _pos;
|
||||
}
|
||||
|
||||
uint16 PodImage::getWidth() const {
|
||||
return _w;
|
||||
}
|
||||
|
||||
uint16 PodImage::getHeight() const {
|
||||
return _h;
|
||||
}
|
||||
|
||||
uint16 PodImage::getHotspotX() const {
|
||||
return _hotspot.x;
|
||||
}
|
||||
|
||||
uint16 PodImage::getHotspotY() const {
|
||||
return _hotspot.y;
|
||||
}
|
||||
|
||||
byte PodImage::getKeyColor() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const byte *PodImage::getSurface() const {
|
||||
return _pixels.get();
|
||||
}
|
||||
|
||||
const byte *PodImage::getPalette() const {
|
||||
return _paletteCursor.get();
|
||||
}
|
||||
|
||||
byte PodImage::getPaletteStartIndex() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 PodImage::getPaletteCount() const {
|
||||
return 256;
|
||||
}
|
||||
|
||||
void PodImage::setHotspot(Common::Point hotspot) {
|
||||
_hotspot = hotspot;
|
||||
}
|
||||
|
||||
}
|
||||
73
engines/hadesch/pod_image.h
Normal file
73
engines/hadesch/pod_image.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_POD_IMAGE_H
|
||||
#define HADESCH_POD_IMAGE_H
|
||||
|
||||
#include "hadesch/pod_file.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "hadesch/gfx_context.h"
|
||||
#include "graphics/cursor.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class PodImage : public Graphics::Cursor {
|
||||
public:
|
||||
PodImage();
|
||||
bool loadImage(const PodFile &col, int index);
|
||||
void render(Common::SharedPtr<GfxContext>, Common::Point offset,
|
||||
int colourScale = 0x100, int scale = 100) const;
|
||||
bool isValid() const;
|
||||
void setHotspot(Common::Point pnt);
|
||||
Common::Point getOffset() const;
|
||||
|
||||
uint16 getWidth() const override;
|
||||
uint16 getHeight() const override;
|
||||
uint16 getHotspotX() const override;
|
||||
uint16 getHotspotY() const override;
|
||||
byte getKeyColor() const override;
|
||||
const byte *getSurface() const override;
|
||||
const byte *getPalette() const override;
|
||||
byte getPaletteStartIndex() const override;
|
||||
uint16 getPaletteCount() const override;
|
||||
|
||||
~PodImage();
|
||||
private:
|
||||
struct ScaledVersion {
|
||||
Common::SharedPtr<byte> _pixels;
|
||||
int _w, _h;
|
||||
};
|
||||
void makeScale(int scale) const;
|
||||
|
||||
mutable Common::HashMap<int, ScaledVersion> _scales;
|
||||
int _w, _h;
|
||||
Common::Point _pos, _hotspot;
|
||||
int _ncolors;
|
||||
Common::SharedPtr<byte> _pixels;
|
||||
Common::SharedPtr<byte> _palette;
|
||||
Common::SharedPtr<byte> _paletteCursor;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
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
115
engines/hadesch/table.cpp
Normal file
115
engines/hadesch/table.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/* 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 "common/file.h"
|
||||
#include "hadesch/table.h"
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
TableLine::TableLine(Common::SharedPtr<Common::SeekableReadStream> stream, int numcols) {
|
||||
Common::String line = stream->readLine();
|
||||
Common::Array<size_t> quotes;
|
||||
size_t cur = 0;
|
||||
_valid = false;
|
||||
if (line.find("//") < line.find('"')) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 2 * numcols; i++) {
|
||||
size_t next = line.find('"', cur);
|
||||
if (next == Common::String::npos) {
|
||||
return;
|
||||
}
|
||||
cur = next + 1;
|
||||
quotes.push_back(next);
|
||||
}
|
||||
if (line.substr(quotes[0] + 1, quotes[1] - quotes[0] - 1) == "sentinel") {
|
||||
return;
|
||||
}
|
||||
_valid = true;
|
||||
for (int i = 0; i < numcols; i++) {
|
||||
_cells.push_back(line.substr(quotes[2 * i] + 1, quotes[2 * i + 1] - quotes[2 * i] - 1));
|
||||
}
|
||||
}
|
||||
|
||||
TableLine::TableLine() {
|
||||
_valid = false;
|
||||
}
|
||||
|
||||
bool TableLine::isValid() const {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
Common::String TableLine::operator[](int idx) const {
|
||||
return _cells[idx];
|
||||
}
|
||||
|
||||
TextTable::TextTable() {
|
||||
}
|
||||
|
||||
TextTable::TextTable(
|
||||
Common::SharedPtr<Common::SeekableReadStream> stream, int numcols) {
|
||||
while (!stream->eos() && !stream->err()) {
|
||||
TableLine tl(stream, numcols);
|
||||
if (!tl.isValid())
|
||||
continue;
|
||||
if (!_header.isValid()) {
|
||||
_header = tl;
|
||||
for (int i = 0; i < numcols; i++) {
|
||||
_colMap[tl[i]] = i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_lines.push_back(tl);
|
||||
_rowMap[tl[0]].push_back(_lines.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
Common::String TextTable::get(const Common::String &row, int col, int rowidx) const {
|
||||
if (!_rowMap.contains(row))
|
||||
return "";
|
||||
return _lines[_rowMap[row][rowidx]][col];
|
||||
}
|
||||
|
||||
int TextTable::rowCount(const Common::String &row) const {
|
||||
if (!_rowMap.contains(row))
|
||||
return 0;
|
||||
return _rowMap[row].size();
|
||||
}
|
||||
|
||||
Common::String TextTable::get(const Common::String &row, const Common::String &col, int rowidx) const {
|
||||
if (!_colMap.contains(col))
|
||||
return "";
|
||||
return _lines[_rowMap[row][rowidx]][_colMap[col]];
|
||||
}
|
||||
|
||||
Common::String TextTable::get(int row, const Common::String &col) const {
|
||||
if (!_colMap.contains(col))
|
||||
return "";
|
||||
return _lines[row][_colMap[col]];
|
||||
}
|
||||
|
||||
int TextTable::size() const {
|
||||
return _lines.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
60
engines/hadesch/table.h
Normal file
60
engines/hadesch/table.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
#ifndef HADESCH_TABLE_H
|
||||
#define HADESCH_TABLE_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/str.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
namespace Hadesch {
|
||||
class TableLine {
|
||||
public:
|
||||
TableLine(Common::SharedPtr<Common::SeekableReadStream> stream, int numcols);
|
||||
TableLine();
|
||||
bool isValid() const;
|
||||
Common::String operator[](int idx) const;
|
||||
private:
|
||||
bool _valid;
|
||||
Common::Array <Common::String> _cells;
|
||||
};
|
||||
|
||||
class TextTable {
|
||||
public:
|
||||
TextTable(Common::SharedPtr<Common::SeekableReadStream> stream, int numcols);
|
||||
TextTable();
|
||||
Common::String get(int row, int col) const;
|
||||
Common::String get(const Common::String &row, int col, int rowidx = 0) const;
|
||||
Common::String get(const Common::String &row, const Common::String &col, int rowidx = 0) const;
|
||||
Common::String get(int row, const Common::String &col) const;
|
||||
int size() const;
|
||||
int rowCount(const Common::String &row) const;
|
||||
private:
|
||||
TableLine _header;
|
||||
Common::HashMap<Common::String, int> _colMap;
|
||||
Common::HashMap<Common::String, Common::Array<int> > _rowMap;
|
||||
Common::Array<TableLine> _lines;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
103
engines/hadesch/tag_file.cpp
Normal file
103
engines/hadesch/tag_file.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/* 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 "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/substream.h"
|
||||
|
||||
#include "hadesch/hadesch.h"
|
||||
#include "hadesch/tag_file.h"
|
||||
#include "hadesch/pod_file.h" // for memSubstream
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
bool TagFile::openStoreHotSub(const Common::SharedPtr<Common::SeekableReadStream> &parentStream) {
|
||||
return openStoreReal(parentStream, 0, parentStream->size(), true, false);
|
||||
}
|
||||
|
||||
bool TagFile::openStoreHot(const Common::SharedPtr<Common::SeekableReadStream> &parentStream) {
|
||||
if (parentStream->readUint32BE() != MKTAG('F', 'F', 'I', 'D')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parentStream->readUint32BE() != MKTAG('S', 'T', 'O', 'H')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return openStoreReal(parentStream, 8, parentStream->size() - 8, true, false);
|
||||
}
|
||||
|
||||
bool TagFile::openStoreCel(const Common::SharedPtr<Common::SeekableReadStream> &parentStream) {
|
||||
if (parentStream->readUint32BE() != MKTAG('C', 'E', 'L', ' ')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 len = parentStream->readUint32BE();
|
||||
|
||||
return openStoreReal(parentStream, 8, len - 8, false, true);
|
||||
}
|
||||
|
||||
bool TagFile::openStoreReal(const Common::SharedPtr<Common::SeekableReadStream> &parentStream, uint32 offset, int32 len, bool isLittleEndian, bool sizeIncludesHeader) {
|
||||
int32 sectionSize = 0;
|
||||
|
||||
for (int32 lenRemaining = len; lenRemaining >= 8; lenRemaining -= sectionSize + 8, offset += sectionSize + 8) {
|
||||
uint32 type = parentStream->readUint32BE();
|
||||
sectionSize = isLittleEndian ? parentStream->readUint32LE() : parentStream->readUint32BE();
|
||||
|
||||
if (sizeIncludesHeader)
|
||||
sectionSize -= 8;
|
||||
|
||||
if (sectionSize < 0) {
|
||||
debug("invalid section size");
|
||||
return false;
|
||||
}
|
||||
|
||||
Description desc;
|
||||
desc.name = type;
|
||||
desc.offset = offset + 8;
|
||||
desc.size = sectionSize;
|
||||
|
||||
_descriptions.push_back(desc);
|
||||
if (sectionSize != 0)
|
||||
parentStream->skip(sectionSize);
|
||||
}
|
||||
|
||||
_file = parentStream;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *TagFile::getFileStream(uint32 name, int idx) {
|
||||
int curIdx = 0;
|
||||
for (uint j = 0; j < _descriptions.size(); ++j) {
|
||||
const Description &desc = _descriptions[j];
|
||||
if (desc.name == name) {
|
||||
if (curIdx == idx)
|
||||
return memSubstream(_file, desc.offset, desc.size);
|
||||
curIdx++;
|
||||
}
|
||||
}
|
||||
debugC(kHadeschDebugResources, "TagFile: %x not found", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Hadesch
|
||||
60
engines/hadesch/tag_file.h
Normal file
60
engines/hadesch/tag_file.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HADESCH_TAG_FILE_H
|
||||
#define HADESCH_TAG_FILE_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class TagFile {
|
||||
public:
|
||||
|
||||
bool openStoreCel(const Common::SharedPtr<Common::SeekableReadStream> &parentstream);
|
||||
bool openStoreHot(const Common::SharedPtr<Common::SeekableReadStream> &parentstream);
|
||||
bool openStoreHotSub(const Common::SharedPtr<Common::SeekableReadStream> &parentstream);
|
||||
|
||||
Common::SeekableReadStream *getFileStream(uint32 name, int idx = 0);
|
||||
|
||||
private:
|
||||
bool openStoreReal(const Common::SharedPtr<Common::SeekableReadStream> &parentstream, uint32 offset,
|
||||
int32 len, bool isLittleEndian, bool sizeIncludesHeader);
|
||||
|
||||
struct Description {
|
||||
uint32 name;
|
||||
uint32 offset;
|
||||
uint32 size;
|
||||
};
|
||||
Common::SharedPtr<Common::SeekableReadStream> _file;
|
||||
Common::Array<Description> _descriptions;
|
||||
};
|
||||
|
||||
} // End of namespace Hadesch
|
||||
|
||||
#endif
|
||||
1114
engines/hadesch/video.cpp
Normal file
1114
engines/hadesch/video.cpp
Normal file
File diff suppressed because it is too large
Load Diff
416
engines/hadesch/video.h
Normal file
416
engines/hadesch/video.h
Normal file
@@ -0,0 +1,416 @@
|
||||
/* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HADESCH_VIDEOROOM_H
|
||||
#define HADESCH_VIDEOROOM_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/array.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/ptr.h"
|
||||
#include "hadesch/pod_file.h"
|
||||
#include "hadesch/pod_image.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/str.h"
|
||||
#include "hadesch/enums.h"
|
||||
#include "hadesch/event.h"
|
||||
#include "hadesch/hotzone.h"
|
||||
#include "hadesch/table.h"
|
||||
#include "common/queue.h"
|
||||
|
||||
namespace Video {
|
||||
class SmackerDecoder;
|
||||
}
|
||||
|
||||
namespace Hadesch {
|
||||
|
||||
class PodImage;
|
||||
class HadeschEngine;
|
||||
class TagFile;
|
||||
|
||||
static const int kDefaultSpeed = 100;
|
||||
|
||||
class Renderable {
|
||||
public:
|
||||
Renderable(Common::Array<PodImage> images);
|
||||
const PodImage &getFrame(int time);
|
||||
void startAnimation(int startms, int msperframe,
|
||||
bool loop, int first, int last);
|
||||
bool isAnimationFinished(int time);
|
||||
void selectFrame(int frame);
|
||||
int getAnimationFrameNum(int time);
|
||||
int getNumFrames() {
|
||||
return _images.size();
|
||||
}
|
||||
|
||||
private:
|
||||
int getLen();
|
||||
Common::Array<PodImage> _images;
|
||||
int _msperframe;
|
||||
int _startms;
|
||||
int _first;
|
||||
int _last;
|
||||
bool _loop;
|
||||
};
|
||||
|
||||
class LayerId {
|
||||
public:
|
||||
LayerId(const Common::String &name) {
|
||||
_name = name;
|
||||
_idx = -1;
|
||||
}
|
||||
|
||||
LayerId() {
|
||||
_idx = -1;
|
||||
}
|
||||
|
||||
LayerId(const char *name) {
|
||||
_name = name;
|
||||
_idx = -1;
|
||||
}
|
||||
|
||||
LayerId(const Common::String &name, int idx, const Common::String &qualifier) {
|
||||
_qualifier = qualifier;
|
||||
_name = name;
|
||||
_idx = idx;
|
||||
}
|
||||
|
||||
Common::String getFilename() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
Common::String getDebug() const;
|
||||
|
||||
bool operator== (const LayerId &b) const;
|
||||
|
||||
private:
|
||||
Common::String _name;
|
||||
int _idx;
|
||||
Common::String _qualifier;
|
||||
};
|
||||
|
||||
struct Animation {
|
||||
Audio::SoundHandle _soundHandle;
|
||||
LayerId _animName;
|
||||
EventHandlerWrapper _callbackEvent;
|
||||
bool _finished;
|
||||
bool _keepLastFrame;
|
||||
bool _skippable;
|
||||
int _subtitleID;
|
||||
};
|
||||
|
||||
class PlayAnimParams {
|
||||
public:
|
||||
static PlayAnimParams loop();
|
||||
static PlayAnimParams keepLastFrame();
|
||||
static PlayAnimParams disappear();
|
||||
bool getKeepLastFrame();
|
||||
bool isLoop();
|
||||
int getSpeed();
|
||||
int getFirstFrame();
|
||||
int getLastFrame();
|
||||
PlayAnimParams partial(int first, int last) const;
|
||||
PlayAnimParams speed(int msperframe) const;
|
||||
PlayAnimParams backwards() const;
|
||||
private:
|
||||
PlayAnimParams(bool loop, bool keepLastFrame);
|
||||
bool _loop;
|
||||
bool _keepLastFrame;
|
||||
int _firstFrame;
|
||||
int _lastFrame;
|
||||
int _msperframe;
|
||||
};
|
||||
|
||||
struct TranscribedSound {
|
||||
const char *soundName;
|
||||
const char *transcript;
|
||||
|
||||
static TranscribedSound make(const char *s, const char *t) {
|
||||
TranscribedSound res;
|
||||
res.soundName = s;
|
||||
res.transcript = t;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoRoom {
|
||||
public:
|
||||
VideoRoom(const Common::String &dir, const Common::String &pod,
|
||||
const Common::String &assetMapFile);
|
||||
~VideoRoom();
|
||||
|
||||
void nextFrame(Common::SharedPtr<GfxContext> context, int time, bool stopVideo);
|
||||
uint getWidth();
|
||||
uint getHeight();
|
||||
int getCursor();
|
||||
|
||||
// Hotzones and mouse
|
||||
void setHotzoneEnabled(const Common::String &name, bool enabled);
|
||||
void enableHotzone(const Common::String &name);
|
||||
void disableHotzone(const Common::String &name);
|
||||
void pushHotZones(const Common::String &hotzoneFile, bool enable = true,
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
void popHotZones();
|
||||
void loadHotZones(const Common::String &hotzoneFile, bool enable = true,
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
void computeHotZone(int time, Common::Point mousePos);
|
||||
Common::String getHotZone();
|
||||
void setHotZoneOffset(const Common::String &name, Common::Point offset);
|
||||
Common::String mapClick(Common::Point mousePos);
|
||||
void enableMouse() {
|
||||
_mouseEnabled = true;
|
||||
}
|
||||
void disableMouse() {
|
||||
_mouseEnabled = false;
|
||||
}
|
||||
bool isMouseEnabled() {
|
||||
return _mouseEnabled;
|
||||
}
|
||||
int getCursorAnimationFrame(int time);
|
||||
|
||||
// Animations and layers
|
||||
void setLayerEnabled(const LayerId &name, bool enabled);
|
||||
void setLayerParallax(const LayerId &name, int val);
|
||||
void setColorScale(const LayerId &name, int val);
|
||||
void setScale(const LayerId &name, int val);
|
||||
int getNumFrames(const LayerId &animName);
|
||||
|
||||
// Main animation API
|
||||
void playAnimWithSpeech(const LayerId &animName,
|
||||
const TranscribedSound &sound,
|
||||
int zValue,
|
||||
PlayAnimParams params,
|
||||
EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
void playAnimWithSFX(const LayerId &animName,
|
||||
const Common::String &soundName,
|
||||
int zValue,
|
||||
PlayAnimParams params,
|
||||
EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
void playAnimWithMusic(const LayerId &animName,
|
||||
const Common::String &soundName,
|
||||
int zValue,
|
||||
PlayAnimParams params,
|
||||
EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
void playAnim(const LayerId &animName, int zValue,
|
||||
PlayAnimParams params,
|
||||
EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
|
||||
void stopAnim(const LayerId &animName);
|
||||
// Like stopAnim but also remove layer altogether
|
||||
void purgeAnim(const LayerId &animName);
|
||||
bool isAnimationFinished(const LayerId &name, int time);
|
||||
void addStaticLayer(const LayerId &name, int zValue, Common::Point offset = Common::Point(0, 0));
|
||||
void selectFrame(const LayerId &name, int zValue, int val, Common::Point offset = Common::Point(0, 0));
|
||||
bool doesLayerExist(const LayerId &name);
|
||||
PodImage getLayerFrame(const LayerId &name);
|
||||
int getAnimFrameNum(const LayerId &name);
|
||||
void dumpLayers();
|
||||
|
||||
// Convenience wrappers
|
||||
void playAnimLoop(const LayerId &animName, int zValue, Common::Point offset = Common::Point(0, 0));
|
||||
void playAnimKeepLastFrame(const LayerId &animName, int zValue, EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
|
||||
// Videos
|
||||
void playVideo(const Common::String &name, int zValue,
|
||||
EventHandlerWrapper callbackEvent = EventHandlerWrapper(),
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
void cancelVideo();
|
||||
bool isVideoPlaying();
|
||||
|
||||
// Panning
|
||||
void panLeftAnim(EventHandlerWrapper callbackEvent = EventHandlerWrapper());
|
||||
void panRightAnim(EventHandlerWrapper callbackEvent = EventHandlerWrapper());
|
||||
void panRightInstant();
|
||||
void setPannable(bool pannable);
|
||||
void setUserPanCallback(EventHandlerWrapper leftStart,
|
||||
EventHandlerWrapper leftEnd,
|
||||
EventHandlerWrapper rightStart,
|
||||
EventHandlerWrapper rightEnd);
|
||||
bool isPanLeft() {
|
||||
return _pan == 0;
|
||||
}
|
||||
|
||||
bool isPanRight() {
|
||||
return _pan == 640;
|
||||
}
|
||||
|
||||
// Hero belt
|
||||
void enableHeroBelt() {
|
||||
_heroBeltEnabled = true;
|
||||
}
|
||||
void disableHeroBelt() {
|
||||
_heroBeltEnabled = false;
|
||||
}
|
||||
bool isHeroBeltEnabled() {
|
||||
return _heroBeltEnabled;
|
||||
}
|
||||
|
||||
// Font
|
||||
void renderString(const Common::String &font, const Common::U32String &str,
|
||||
Common::Point startPos, int zVal, int fontDelta = 0, const Common::String &extraId = "letter");
|
||||
void renderStringCentered(const Common::String &font, const Common::U32String &str,
|
||||
Common::Point centerPos, int zVal, int fontDelta = 0, const Common::String &extraId = "letter");
|
||||
void hideString(const Common::String &font, size_t maxLen, const Common::String &extraId = "letter");
|
||||
int computeStringWidth(const Common::String &font, const Common::U32String &str, int fontDelta = 0);
|
||||
|
||||
// Misc
|
||||
void playSFX(const Common::String &soundName,
|
||||
EventHandlerWrapper callbackEvent = EventHandlerWrapper());
|
||||
void playMusic(const Common::String &soundName,
|
||||
EventHandlerWrapper callbackEvent = EventHandlerWrapper());
|
||||
void playSFXLoop(const Common::String &soundName);
|
||||
void playMusicLoop(const Common::String &soundName);
|
||||
void playSpeech(const TranscribedSound &sound,
|
||||
EventHandlerWrapper callbackEvent = EventHandlerWrapper());
|
||||
void playStatueSMK(StatueId id, const LayerId &animName, int zValue,
|
||||
const Common::Array<Common::String> &smkNames,
|
||||
int startOfLoop, int startOfEnd,
|
||||
Common::Point offset = Common::Point(0, 0));
|
||||
Common::SeekableReadStream *openFile(const Common::String &name);
|
||||
void fadeOut(int ms, const EventHandlerWrapper &callback);
|
||||
void resetFade();
|
||||
void resetLayers();
|
||||
void drag(const Common::String &name, int frame, Common::Point hotspot);
|
||||
PodImage *getDragged();
|
||||
void clearDrag();
|
||||
void pause();
|
||||
void unpause();
|
||||
void finish();
|
||||
void cancelAllSubtitles();
|
||||
void setViewportOffset(Common::Point vp) {
|
||||
_viewportOffset = vp;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Layer {
|
||||
Common::SharedPtr<Renderable> renderable;
|
||||
LayerId name;
|
||||
Common::Point offset;
|
||||
bool isEnabled;
|
||||
int genCounter;
|
||||
int zValue;
|
||||
int parallax;
|
||||
int colorScale; // From 0 to 0x100
|
||||
int scale; // From 0 to 100
|
||||
};
|
||||
|
||||
struct SubtitleLine {
|
||||
Common::U32String line;
|
||||
int32 maxTime;
|
||||
int ID;
|
||||
};
|
||||
|
||||
void playAnimWithSoundInternal(const LayerId &animName,
|
||||
const Common::String &soundName,
|
||||
Audio::Mixer::SoundType soundType,
|
||||
int zValue,
|
||||
PlayAnimParams params,
|
||||
EventHandlerWrapper callbackEvent,
|
||||
Common::Point offset,
|
||||
int subID = -1);
|
||||
void playSubtitles(const char *text, int subID);
|
||||
void addLayer(Renderable *renderable, const LayerId &name,
|
||||
int zValue,
|
||||
bool isEnabled = true, Common::Point offset = Common::Point(0, 0));
|
||||
void startAnimationInternal(const LayerId &name, int zValue, int msperframe, bool loop,
|
||||
bool fixedFrame, int first, int last, Common::Point offset);
|
||||
Audio::RewindableAudioStream *getAudioStream(const Common::String &soundName);
|
||||
Common::String mapAsset(const Common::String &name);
|
||||
Common::String mapAsset(const LayerId &name);
|
||||
void addAnimLayerInternal(const LayerId &name, int zValue, Common::Point offset = Common::Point(0, 0));
|
||||
void playSoundInternal(const Common::String &soundName, EventHandlerWrapper callbackEvent, bool loop,
|
||||
bool skippable, Audio::Mixer::SoundType soundType, int subtitleID = -1);
|
||||
static int layerComparator (const Layer &a, const Layer &b);
|
||||
void loadFontWidth(const Common::String &font);
|
||||
|
||||
uint _videoW, _videoH;
|
||||
Common::Point _videoOffset, _videoSurfOffset;
|
||||
Common::SharedPtr<byte> _videoPixels;
|
||||
byte _videoPalette[256 * 3];
|
||||
|
||||
HotZoneArray _hotZones;
|
||||
Common::Array<HotZoneArray> _hotZoneStack;
|
||||
Common::SortedArray<Layer, const Layer&> _layers;
|
||||
int _layerGenCounter;
|
||||
|
||||
int _startHotTime;
|
||||
int _hotZone;
|
||||
int _cursor;
|
||||
// We need to keep cursor pointer valid for at
|
||||
// least one more frame. Hence use circular buffer
|
||||
PodImage _draggedImage[5];
|
||||
int _draggingPtr;
|
||||
bool _isDragging;
|
||||
int _pan, _panSpeed;
|
||||
EventHandlerWrapper _panCallback;
|
||||
EventHandlerWrapper _userPanStartLeftCallback;
|
||||
EventHandlerWrapper _userPanStartRightCallback;
|
||||
EventHandlerWrapper _userPanEndLeftCallback;
|
||||
EventHandlerWrapper _userPanEndRightCallback;
|
||||
|
||||
bool _pannable;
|
||||
bool _leftEdge;
|
||||
bool _rightEdge;
|
||||
bool _heroBeltEnabled;
|
||||
int _edgeStartTime;
|
||||
Common::String _smkPath;
|
||||
Common::String _podPath;
|
||||
|
||||
Common::SharedPtr<Video::SmackerDecoder> _videoDecoder;
|
||||
Common::Point _viewportOffset;
|
||||
Common::Array<Animation> _anims;
|
||||
EventHandlerWrapper _videoDecoderEndEvent;
|
||||
int _videoZ;
|
||||
Common::SharedPtr<PodFile> _podFile;
|
||||
Common::HashMap<Common::String, Common::Array<int> > _fontWidths;
|
||||
Common::Queue<SubtitleLine> _subtitles;
|
||||
Common::HashMap<int, int> _countQueuedSubtitles;
|
||||
TextTable _assetMap;
|
||||
bool _mouseEnabled;
|
||||
|
||||
int _finalFade, _finalFadeSpeed;
|
||||
EventHandlerWrapper _finalFadeCallback;
|
||||
};
|
||||
|
||||
static const int kVideoWidth = 640;
|
||||
static const int kVideoHeight = 480;
|
||||
#define kOffsetRightRoom (Common::Point(kVideoWidth, 0))
|
||||
#define kZeroPoint (Common::Point(10, 50))
|
||||
|
||||
struct PrePoint {
|
||||
int x, y;
|
||||
|
||||
Common::Point get() const {
|
||||
return Common::Point(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user