Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,214 @@
/* 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/>.
*
*/
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
namespace Stark {
ArchiveLoader::LoadedArchive::LoadedArchive(const Common::Path& archiveName) :
_filename(archiveName),
_root(nullptr),
_useCount(0) {
if (!_xarc.open(archiveName)) {
error("Unable to open archive '%s'", archiveName.toString(Common::Path::kNativeSeparator).c_str());
}
}
ArchiveLoader::LoadedArchive::~LoadedArchive() {
// Resource lifecycle update
_root->onPreDestroy();
delete _root;
}
void ArchiveLoader::LoadedArchive::importResources() {
// Import the resource tree
_root = Formats::XRCReader::importTree(&_xarc);
}
ArchiveLoader::~ArchiveLoader() {
for (LoadedArchiveList::iterator it = _archives.begin(); it != _archives.end(); it++) {
delete *it;
}
}
bool ArchiveLoader::load(const Common::Path &archiveName) {
if (hasArchive(archiveName)) {
// Already loaded
return false;
}
LoadedArchive *archive = new LoadedArchive(archiveName);
_archives.push_back(archive);
archive->importResources();
return true;
}
void ArchiveLoader::unloadUnused() {
for (LoadedArchiveList::iterator it = _archives.begin(); it != _archives.end(); it++) {
if (!(*it)->isInUse()) {
delete *it;
it = _archives.erase(it);
it--;
}
}
}
ArchiveReadStream *ArchiveLoader::getFile(const Common::Path &fileName, const Common::Path &archiveName) {
LoadedArchive *archive = findArchive(archiveName);
const Formats::XARCArchive &xarc = archive->getXArc();
Common::SeekableReadStream *stream = xarc.createReadStreamForMember(fileName);
if (!stream) {
return nullptr;
}
return new ArchiveReadStream(stream);
}
bool ArchiveLoader::returnRoot(const Common::Path &archiveName) {
LoadedArchive *archive = findArchive(archiveName);
archive->decUsage();
return !archive->isInUse();
}
bool ArchiveLoader::hasArchive(const Common::Path &archiveName) const {
for (LoadedArchiveList::const_iterator it = _archives.begin(); it != _archives.end(); it++) {
if ((*it)->getFilename() == archiveName) {
return true;
}
}
return false;
}
ArchiveLoader::LoadedArchive *ArchiveLoader::findArchive(const Common::Path &archiveName) const {
for (LoadedArchiveList::const_iterator it = _archives.begin(); it != _archives.end(); it++) {
if ((*it)->getFilename() == archiveName) {
return *it;
}
}
error("The archive with name '%s' is not loaded.", archiveName.toString(Common::Path::kNativeSeparator).c_str());
}
Common::Path ArchiveLoader::buildArchiveName(Resources::Level *level, Resources::Location *location) const {
Common::String archive;
if (!location) {
switch (level->getSubType()) {
case 1:
archive = Common::String::format("%s/%s.xarc", level->getName().c_str(), level->getName().c_str());
break;
case 2:
archive = Common::String::format("%02x/%02x.xarc", level->getIndex(), level->getIndex());
break;
default:
error("Unknown level type %d", level->getSubType());
}
} else {
archive = Common::String::format("%02x/%02x/%02x.xarc", level->getIndex(), location->getIndex(), location->getIndex());
}
return Common::Path(archive, '/');
}
Common::Path ArchiveLoader::getExternalFilePath(const Common::Path &fileName, const Common::Path &archiveName) const {
// Build a path of the type 45/00/
Common::Path filePath = archiveName;
if (!filePath.isSeparatorTerminated()) {
filePath = filePath.getParent();
}
filePath.joinInPlace("xarc");
filePath.joinInPlace(fileName);
return filePath;
}
Common::SeekableReadStream *ArchiveLoader::getExternalFile(const Common::Path &fileName, const Common::Path &archiveName) const {
Common::Path filePath = getExternalFilePath(fileName, archiveName);
return SearchMan.createReadStreamForMember(filePath);
}
ArchiveReadStream::ArchiveReadStream(
Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream) :
SeekableSubReadStream(parentStream, 0, parentStream->size(), disposeParentStream) {
}
ArchiveReadStream::~ArchiveReadStream() {
}
Common::String ArchiveReadStream::readString() {
// Read the string length
uint32 length = readUint32LE();
// Read the string
char *data = new char[length + 1];
read(data, length);
data[length] = '\0';
Common::String string(data);
delete[] data;
return string;
}
Common::String ArchiveReadStream::readString16() {
// Read the string length
uint16 length = readUint16LE();
// Read the string
char *data = new char[length + 1];
read(data, length);
data[length] = '\0';
Common::String string(data);
delete[] data;
return string;
}
Math::Vector3d ArchiveReadStream::readVector3() {
Math::Vector3d v;
v.readFromStream(this);
return v;
}
Math::Quaternion ArchiveReadStream::readQuaternion() {
Math::Quaternion q;
q.readFromStream(this);
return q;
}
float ArchiveReadStream::readFloat() {
float f;
read(&f, sizeof(float));
return f;
}
} // End of namespace Stark

View 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/>.
*
*/
#ifndef STARK_SERVICES_ARCHIVE_LOADER_H
#define STARK_SERVICES_ARCHIVE_LOADER_H
#include "common/list.h"
#include "common/str.h"
#include "common/substream.h"
#include "common/util.h"
#include "math/quat.h"
#include "math/vector3d.h"
#include "engines/stark/formats/xarc.h"
#include "engines/stark/resources/object.h"
namespace Stark {
namespace Resources {
class Level;
class Location;
}
/**
* A read stream with helper functions to read usual data types
*/
class ArchiveReadStream : public Common::SeekableSubReadStream {
public:
ArchiveReadStream(Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::YES);
virtual ~ArchiveReadStream();
Common::String readString();
Common::String readString16();
Math::Vector3d readVector3();
Math::Quaternion readQuaternion();
float readFloat();
};
/**
* XARC Archive loader.
*
* Maintains a list of opened archive files.
* Loads the resources from the XRC tree.
*/
class ArchiveLoader {
public:
~ArchiveLoader();
/** Load a Xarc archive, and add it to the managed archives list */
bool load(const Common::Path &archiveName);
/** Unload all the unused Xarc archives */
void unloadUnused();
/** Retrieve a file from a specified archive */
ArchiveReadStream *getFile(const Common::Path &fileName, const Common::Path &archiveName);
/** Get the resource tree root for an archive, and increment the archive use count */
template <class T>
T *useRoot(const Common::Path &archiveName);
/** Decrement the root's archive use count */
bool returnRoot(const Common::Path &archiveName);
/** Build the archive filename for a level or a location */
Common::Path buildArchiveName(Resources::Level *level, Resources::Location *location = nullptr) const;
/** Retrieve a file relative to a specified archive */
Common::SeekableReadStream *getExternalFile(const Common::Path &fileName, const Common::Path &archiveName) const;
Common::Path getExternalFilePath(const Common::Path &fileName, const Common::Path &archiveName) const;
private:
class LoadedArchive {
public:
explicit LoadedArchive(const Common::Path &archiveName);
~LoadedArchive();
const Common::Path &getFilename() const { return _filename; }
const Formats::XARCArchive &getXArc() const { return _xarc; }
Resources::Object *getRoot() const { return _root; }
void importResources();
bool isInUse() const { return _useCount > 0; }
void incUsage() { _useCount++; }
void decUsage() { _useCount = MAX<int>(_useCount - 1, 0); }
private:
uint _useCount;
Common::Path _filename;
Formats::XARCArchive _xarc;
Resources::Object *_root;
};
typedef Common::List<LoadedArchive *> LoadedArchiveList;
bool hasArchive(const Common::Path &archiveName) const;
LoadedArchive *findArchive(const Common::Path &archiveName) const;
LoadedArchiveList _archives;
};
template <class T>
T *ArchiveLoader::useRoot(const Common::Path &archiveName) {
LoadedArchive *archive = findArchive(archiveName);
archive->incUsage();
return Resources::Object::cast<T>(archive->getRoot());
}
} // End of namespace Stark
#endif // STARK_SERVICES_ARCHIVE_LOADER_H

View File

@@ -0,0 +1,263 @@
/* 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/>.
*
*/
#include "engines/stark/services/dialogplayer.h"
#include "engines/stark/services/diary.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/userinterface.h"
#include "engines/stark/resources/script.h"
#include "engines/stark/resources/speech.h"
namespace Stark {
DialogPlayer::DialogPlayer() :
_currentDialog(nullptr),
_currentReply(nullptr),
_interruptedDialog(nullptr),
_interruptedReply(nullptr),
_speechReady(false),
_singleSpeech(nullptr),
_optionsAvailable(false) {
}
DialogPlayer::~DialogPlayer() {}
void DialogPlayer::run(Resources::Dialog *dialog) {
reset();
StarkUserInterface->setInteractive(false);
if (!_currentDialog) {
Common::String dialogTitle = dialog->getDiaryTitle();
int32 characterId = dialog->getCharacter();
Common::String characterName = StarkGlobal->getCharacterName(characterId);
StarkDiary->openDialog(dialogTitle, characterName, characterId);
}
_currentDialog = dialog;
buildOptions();
}
void DialogPlayer::playSingle(Resources::Speech *speech) {
reset();
_singleSpeech = speech;
_speechReady = true;
}
bool DialogPlayer::isRunning() const {
return _currentDialog != nullptr || _interruptedDialog != nullptr;
}
bool DialogPlayer::isSpeechReady() const {
return _speechReady;
}
bool DialogPlayer::isSpeechReady(Resources::Speech *speech) const {
return _speechReady && _singleSpeech == speech;
}
Resources::Speech *DialogPlayer::acquireReadySpeech() {
assert(_speechReady);
_speechReady = false;
if (_singleSpeech) {
return _singleSpeech;
} else {
return _currentReply->getCurrentSpeech();
}
}
bool DialogPlayer::areOptionsAvailable() const {
return _optionsAvailable;
}
Common::Array<DialogPlayer::Option> DialogPlayer::listOptions() const {
return _options;
}
void DialogPlayer::removeLastOnlyOption() {
int32 lastOnlyOptionIndex = -1;
for (uint i = 0; i < _options.size(); i++) {
Resources::Dialog::Topic *topic = _options[i]._topic;
Resources::Dialog::Reply *reply = topic->getReply(_options[i]._replyIndex);
if (reply->isLastOnly()) {
lastOnlyOptionIndex = i;
break;
}
}
if (lastOnlyOptionIndex >= 0) {
_options.remove_at(lastOnlyOptionIndex);
}
}
void DialogPlayer::buildOptions() {
Resources::Dialog::TopicArray availableTopics = _currentDialog->listAvailableTopics();
for (uint i = 0; i < availableTopics.size(); i++) {
Option option;
option._type = kOptionTypeAsk;
option._topic = availableTopics[i];
option._caption = availableTopics[i]->getCaption();
option._replyIndex = availableTopics[i]->getNextReplyIndex();
Resources::Dialog::Reply *reply = availableTopics[i]->getReply(option._replyIndex);
if (reply->checkCondition()) {
_options.push_back(option);
}
}
if (_options.size() > 1) {
removeLastOnlyOption();
}
if (_options.size() == 1) {
// Only one option, just run it
selectOption(0);
} else {
_optionsAvailable = true;
}
}
void DialogPlayer::selectOption(uint32 index) {
_optionsAvailable = false;
Option &option = _options[index];
//TODO: Complete
switch (option._type) {
case kOptionTypeAsk: {
Resources::Dialog::Topic *topic = option._topic;
// Set the current reply
_currentReply = topic->startReply(option._replyIndex);
Resources::Speech *speech = _currentReply->getCurrentSpeech();
if (speech) {
StarkDiary->logSpeech(speech->getPhrase(), speech->getCharacterId());
_speechReady = true;
} else {
onReplyEnd();
}
break;
}
default:
error("Unhandled option type %d", option._type);
}
}
void DialogPlayer::onReplyEnd() {
Resources::Script *nextScript = _currentDialog->getNextScript(_currentReply);
Resources::Dialog *nextDialog = _currentDialog->getNextDialog(_currentReply);
if (nextScript) {
// Save the dialog player's state before running the script,
// so that we can restore it when the script ends.
// The script might run another dialog.
saveToInterruptionSlot();
nextScript->addReturnObject(_currentDialog);
nextScript->execute(Resources::Script::kCallModeDialogCreateSelections);
} else if (nextDialog) {
run(nextDialog);
} else {
// Quit the dialog
reset();
StarkUserInterface->setInteractive(true);
}
}
void DialogPlayer::reset() {
if (_currentDialog) {
StarkDiary->closeDialog();
}
_currentDialog = nullptr;
_currentReply = nullptr;
_singleSpeech = nullptr;
_speechReady = false;
_optionsAvailable = false;
_options.clear();
}
void DialogPlayer::update() {
if (_singleSpeech || !_currentDialog || !_currentReply) {
return; // Nothing to do
}
//TODO: Complete
Resources::Speech *speech = _currentReply->getCurrentSpeech();
if (speech && _speechReady) {
// A new line is already ready, no need to prepare another one
return;
}
if (!speech || !speech->isPlaying()) {
// A line has ended, play the next one
_currentReply->goToNextLine();
speech = _currentReply->getCurrentSpeech();
if (speech) {
StarkDiary->logSpeech(speech->getPhrase(), speech->getCharacterId());
_speechReady = true;
} else {
onReplyEnd();
}
}
}
void DialogPlayer::resume(Resources::Dialog *dialog) {
assert(_interruptedDialog == dialog);
// Restore our state from before running the script
restoreFromInterruptionSlot();
Resources::Dialog *nextDialog = _currentDialog->getNextDialog(_currentReply);
if (nextDialog) {
run(nextDialog);
} else {
// Quit the dialog
reset();
StarkUserInterface->setInteractive(true);
}
}
void DialogPlayer::saveToInterruptionSlot() {
_interruptedDialog = _currentDialog;
_interruptedReply = _currentReply;
}
void DialogPlayer::restoreFromInterruptionSlot() {
_currentDialog = _interruptedDialog;
_currentReply = _interruptedReply;
_interruptedDialog = nullptr;
_interruptedReply = nullptr;
}
} // End of namespace Stark

View File

@@ -0,0 +1,119 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_DIALOG_PLAYER_H
#define STARK_SERVICES_DIALOG_PLAYER_H
#include "common/array.h"
#include "engines/stark/resources/dialog.h"
namespace Stark {
namespace Resources {
class Speech;
}
/**
* Dialog player
*
* Handles the state of the currently running dialog, and implements the
* associated logic.
*/
class DialogPlayer {
public:
DialogPlayer();
virtual ~DialogPlayer();
enum OptionType {
kOptionTypeAsk = 0
};
struct Option {
uint32 _type;
Common::String _caption;
Resources::Dialog::Topic *_topic;
int32 _replyIndex;
};
/** Enter a dialog */
void run(Resources::Dialog *dialog);
/** Play a one-shot sentence */
void playSingle(Resources::Speech *speech);
/** Check if a dialog is running */
bool isRunning() const;
/** Update the currently running dialog */
void update();
/** Select a dialog option */
void selectOption(uint32 index);
/** Can a speech be played? */
bool isSpeechReady() const;
bool isSpeechReady(Resources::Speech *speech) const;
/** Return the speech to be played */
Resources::Speech *acquireReadySpeech();
/** Does the player need to choose between options? */
bool areOptionsAvailable() const;
/** List the currently available dialog options */
Common::Array<DialogPlayer::Option> listOptions() const;
/** Resume the dialog after it was interrupted to run a script */
void resume(Resources::Dialog *dialog);
/** Clear the currently running dialog */
void reset();
protected:
/** Build a list of available dialog options */
void buildOptions();
/** Removes the last only option from the options list */
void removeLastOnlyOption();
/** Initiate the next action after the end of a reply */
void onReplyEnd();
void saveToInterruptionSlot();
void restoreFromInterruptionSlot();
Resources::Dialog *_currentDialog;
Resources::Dialog::Reply *_currentReply;
Resources::Dialog *_interruptedDialog;
Resources::Dialog::Reply *_interruptedReply;
Resources::Speech *_singleSpeech;
bool _speechReady;
bool _optionsAvailable;
Common::Array<Option> _options;
};
} // End of namespace Stark
#endif // STARK_SERVICES_DIALOG_PLAYER_H

View File

@@ -0,0 +1,180 @@
/* 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/>.
*
*/
#include "engines/stark/services/diary.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/stateprovider.h"
#include "engines/stark/services/userinterface.h"
namespace Stark {
Diary::Diary() {
clear();
}
Diary::~Diary() {}
void Diary::clear() {
_diaryEntries.clear();
_fmvEntries.clear();
_conversationEntries.clear();
_hasUnreadEntries = false;
_pageIndex = 0;
}
void Diary::addDiaryEntry(const Common::String &name) {
_diaryEntries.push_back(name);
_hasUnreadEntries = true;
StarkUserInterface->notifyDiaryEntryEnabled();
}
void Diary::addFMVEntry(const Common::Path &filename, const Common::String &title, int gameDisc) {
if (!hasFMVEntry(filename)) {
FMVEntry entry;
entry.filename = filename;
entry.title = title;
entry.gameDisc = gameDisc;
_fmvEntries.push_back(entry);
}
}
bool Diary::hasFMVEntry(const Common::Path &filename) const {
for (uint i = 0; i < _fmvEntries.size(); i++) {
if (_fmvEntries[i].filename == filename) {
return true;
}
}
return false;
}
void Diary::readStateFromStream(Common::SeekableReadStream *stream, uint32 version) {
clear();
if (version <= 6) {
return; //Early save versions did not persist the diary
}
ResourceSerializer serializer(stream, nullptr, version);
saveLoad(&serializer);
}
void Diary::writeStateToStream(Common::WriteStream *stream) {
ResourceSerializer serializer(nullptr, stream, StateProvider::kSaveVersion);
saveLoad(&serializer);
}
void Diary::saveLoad(ResourceSerializer *serializer) {
// Diary entries
serializer->syncArraySize(_diaryEntries);
for (uint i = 0; i < _diaryEntries.size(); i++) {
serializer->syncAsString32(_diaryEntries[i]);
}
// FMV entries
serializer->syncArraySize(_fmvEntries);
for (uint i = 0; i < _fmvEntries.size(); i++) {
if (serializer->isSaving()) {
Common::String filename(_fmvEntries[i].filename.toString('/'));
serializer->syncAsString32(filename);
} else {
Common::String filename;
serializer->syncAsString32(filename);
_fmvEntries[i].filename = Common::Path(filename, '/');
}
serializer->syncAsString32(_fmvEntries[i].title);
serializer->syncAsUint32LE(_fmvEntries[i].gameDisc);
}
// Conversations
serializer->syncArraySize(_conversationEntries, 8);
for (uint i = 0; i < _conversationEntries.size(); i++) {
ConversationLog &entry = _conversationEntries[i];
serializer->syncAsSint32LE(entry.chapter);
serializer->syncAsSint32LE(entry.characterId);
serializer->syncAsString32(entry.characterName);
serializer->syncAsString32(entry.title);
serializer->syncArraySize(entry.lines);
for (uint j = 0; j < entry.lines.size(); j++) {
ConversationLogLine &logLine = entry.lines[j];
serializer->syncAsString32(logLine.line);
serializer->syncAsSint32LE(logLine.characterId);
}
}
// Misc
serializer->syncAsByte(_hasUnreadEntries);
serializer->syncAsUint32LE(_pageIndex);
}
void Diary::openDialog(const Common::String &title, const Common::String &characterName, int32 characterId) {
// Reuse the previous dialog if it has the same title
if (_conversationEntries.empty() || _conversationEntries.back().title != title) {
ConversationLog conversation;
conversation.title = title;
conversation.characterName = characterName;
conversation.characterId = characterId;
conversation.chapter = StarkGlobal->getCurrentChapter();
_conversationEntries.push_back(conversation);
}
_conversationEntries.back().dialogActive = true;
}
void Diary::closeDialog() {
if (!_conversationEntries.empty()) {
_conversationEntries.back().dialogActive = false;
}
}
void Diary::logSpeech(const Common::String &line, int32 characterId) {
ConversationLog &conversationLog = _conversationEntries.back();
if (conversationLog.dialogActive) {
ConversationLogLine logLine;
logLine.line = line;
logLine.characterId = characterId;
conversationLog.lines.push_back(logLine);
}
}
bool Diary::isEnabled() const {
return StarkGlobal->getInventory() && StarkGlobal->hasInventoryItem("Diary");
}
bool Diary::hasUnreadEntries() const {
return _hasUnreadEntries;
}
Diary::ConversationLog::ConversationLog() :
dialogActive(false),
chapter(0),
characterId(0) {
}
Diary::ConversationLogLine::ConversationLogLine() :
characterId(0) {
}
} // End of namespace Stark

View File

@@ -0,0 +1,136 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_DIARY_H
#define STARK_SERVICES_DIARY_H
#include "common/path.h"
#include "common/str.h"
#include "common/str-array.h"
namespace Common {
class SeekableReadStream;
class WriteStream;
}
namespace Stark {
class ResourceSerializer;
/**
* Diary state storage
*
* Stores references to all the unlocked data available from the diary menu
*/
class Diary {
public:
struct ConversationLogLine {
Common::String line;
int32 characterId;
ConversationLogLine();
};
struct ConversationLog {
Common::String title;
Common::String characterName;
int32 characterId;
int32 chapter;
bool dialogActive;
Common::Array<ConversationLogLine> lines;
ConversationLog();
};
Diary();
virtual ~Diary();
/** Does the player have the diary in their inventory? */
bool isEnabled() const;
/** Does the diary contain entries that have not been read yet? */
bool hasUnreadEntries() const;
/** Mark all the diary entries read */
void setDiaryAllRead() { _hasUnreadEntries = false; }
/** Add an entry to the list of available diary pages */
void addDiaryEntry(const Common::String &name);
/** Get and set the current diary page index */
uint32 getPageIndex() const { return _pageIndex; };
void setPageIndex(uint32 pageIndex) { _pageIndex = pageIndex; }
/** Add a FMV entry to the list of movies available to play from the diary */
void addFMVEntry(const Common::Path &filename, const Common::String &title, int gameDisc);
/** Get info of added FMV entries */
uint countFMV() const { return _fmvEntries.size(); }
const Common::Path &getFMVFilename(uint index) const { return _fmvEntries[index].filename; }
const Common::String &getFMVTitle(uint index) const { return _fmvEntries[index].title; }
/** Get info of added Diary entries */
uint countDiary() const { return _diaryEntries.size(); }
const Common::String &getDiary(uint index) const { return _diaryEntries[index]; }
/** Get added Dialog entries */
uint countDialog() const { return _conversationEntries.size(); }
const ConversationLog &getDialog(uint index) const { return _conversationEntries[index]; }
/** Start recording speech lines for a dialog */
void openDialog(const Common::String &title, const Common::String &characterName, int32 characterId);
/** Record a speech line for the previously opened dialog */
void logSpeech(const Common::String &line, int32 characterId);
/** Close the currently active dialog */
void closeDialog();
/** Reset all the game state data */
void clear();
/** Replace the current state by that read from the stream */
void readStateFromStream(Common::SeekableReadStream *stream, uint32 version);
/** Write the state to a stream */
void writeStateToStream(Common::WriteStream *stream);
private:
struct FMVEntry {
Common::Path filename;
Common::String title;
int gameDisc;
};
bool hasFMVEntry(const Common::Path &filename) const;
void saveLoad(ResourceSerializer *serializer);
Common::Array<Common::String> _diaryEntries;
Common::Array<FMVEntry> _fmvEntries;
Common::Array<ConversationLog> _conversationEntries;
bool _hasUnreadEntries;
uint32 _pageIndex;
};
} // End of namespace Stark
#endif // STARK_SERVICES_DIARY_H

View 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/>.
*
*/
#include "engines/stark/services/fontprovider.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/settings.h"
#include "engines/stark/gfx/driver.h"
#include "common/archive.h"
#include "common/formats/ini-file.h"
#include "graphics/font.h"
#include "graphics/fontman.h"
#include "graphics/fonts/ttf.h"
namespace Stark {
FontProvider::FontProvider() {
}
FontProvider::~FontProvider() {
}
void FontProvider::initFonts() {
// TODO: Use SystemFontMan instead when it gets merged
_ttfFileMap["Garamond"] = "Gara.ttf";
_ttfFileMap["Florentine Script"] = "flornt.TTF";
_ttfFileMap["Folkard"] = "folkard.ttf";
_ttfFileMap["Folkard\231"] = "folkard.ttf";
_ttfFileMap["Arial"] = "ARIAL.TTF";
_ttfFileMap["Bradley Hand ITC"] = "bradhitc.ttf";
_ttfFileMap["Slurry"] = "SLURRY.TTF";
_ttfFileMap["President Cyr"] = "President Cyr Regular.Ttf";
_ttfFileMap["VictorianCyr"] = "Victorian Cyr.ttf";
_ttfFileMap["Zapf Chance Italic"] = "Zapf Chance Italic.Ttf";
_ttfFileMap["Arial_tlj"] = "arial_tlj.ttf";
// Clear any previously used fonts
_smallFont = FontHolder();
_bigFont = FontHolder();
for (uint i = 0; i < ARRAYSIZE(_customFonts); i++) {
_customFonts[i] = FontHolder();
}
// Load the font settings from gui.ini when possible
if (!StarkSettings->shouldIgnoreFontSettings()) {
Common::INIFile gui;
if (gui.loadFromFile("gui.ini")) {
readFontEntry(&gui, _smallFont, "smallfont", "smallheight");
readFontEntry(&gui, _bigFont, "bigfont", "bigheight");
readFontEntry(&gui, _customFonts[0], "font0", "fontsize0");
readFontEntry(&gui, _customFonts[1], "font1", "fontsize1");
readFontEntry(&gui, _customFonts[2], "font2", "fontsize2");
readFontEntry(&gui, _customFonts[3], "font3", "fontsize3");
readFontEntry(&gui, _customFonts[4], "font4", "fontsize4");
readFontEntry(&gui, _customFonts[5], "font5", "fontsize5");
readFontEntry(&gui, _customFonts[6], "font6", "fontsize6");
readFontEntry(&gui, _customFonts[7], "font7", "fontsize7");
} else {
warning("Unable to open 'gui.ini' to read the font settings");
}
}
// Default fonts
if (!_smallFont._font) _smallFont = FontHolder(this, "Garamond", 12);
if (!_bigFont._font) _bigFont = FontHolder(this, "Florentine Script", 19);
if (!_customFonts[0]._font) _customFonts[0] = FontHolder(this, "Folkard", 20);
if (!_customFonts[1]._font) _customFonts[1] = FontHolder(this, "Folkard", 12);
if (!_customFonts[2]._font) _customFonts[2] = FontHolder(this, "Arial", 14);
if (!_customFonts[3]._font) _customFonts[3] = FontHolder(this, "Bradley Hand ITC", 16);
if (!_customFonts[4]._font) _customFonts[4] = FontHolder(this, "Bradley Hand ITC", 20);
if (!_customFonts[5]._font) _customFonts[5] = FontHolder(this, "Bradley Hand ITC", 16);
if (!_customFonts[6]._font) _customFonts[6] = FontHolder(this, "Bradley Hand ITC", 15);
if (!_customFonts[7]._font) _customFonts[7] = FontHolder(this, "Florentine Script", 13);
}
void FontProvider::readFontEntry(const Common::INIFile *gui, FontHolder &holder, const char *nameKey, const char *sizeKey) {
Common::String section = "TEXT95";
if (gui->hasSection("Western")) {
section = "Western";
}
Common::String name, sizeStr;
bool gotName = gui->getKey(nameKey, section, name);
bool gotSize = gui->getKey(sizeKey, section, sizeStr);
long size = strtol(sizeStr.c_str(), nullptr, 10);
// WORKAROUND: In the GOG.com release the computer font (location 36 00)
// is too small, preventing the "White Cardinal" label from being clickable.
if (strcmp(nameKey, "font2") == 0 && name.equalsIgnoreCase("Arial") && size < 14) {
size = 14;
}
if (gotName && gotSize && size > 0) {
holder = FontHolder(this, name, size);
} else {
warning("Unable to read font entry '%s' from 'gui.ini'", nameKey);
}
}
FontProvider::FontHolder::FontHolder(FontProvider *fontProvider, const Common::String &name, uint32 height) {
_name = name;
_originalHeight = height;
_scaledHeight = StarkGfx->scaleHeightOriginalToCurrent(_originalHeight);
// Fetch the font file name
Common::Path ttfFileName("fonts");
ttfFileName.joinInPlace(fontProvider->_ttfFileMap[_name]);
// Initialize the font
Common::SeekableReadStream *s = SearchMan.createReadStreamForMember(ttfFileName);
if (s) {
Graphics::TTFRenderMode renderMode = StarkSettings->isFontAntialiasingEnabled() ?
Graphics::kTTFRenderModeLight : Graphics::kTTFRenderModeMonochrome;
bool stemDarkening = StarkSettings->isFontAntialiasingEnabled();
_font = Common::SharedPtr<Graphics::Font>(
Graphics::loadTTFFont(s, DisposeAfterUse::YES, _scaledHeight, Graphics::kTTFSizeModeCell, 0, 0, renderMode, nullptr, stemDarkening)
);
} else {
warning("Unable to load the font '%s'", ttfFileName.toString().c_str());
}
}
FontProvider::FontHolder *FontProvider::getFontHolder(FontProvider::FontType type, int32 customFontIndex) {
if (type == kSmallFont) {
return &_smallFont;
} else if (type == kBigFont) {
return &_bigFont;
} else {
assert(customFontIndex >= 0 && customFontIndex < 8);
return &_customFonts[customFontIndex];
}
}
const Graphics::Font *FontProvider::getScaledFont(FontProvider::FontType type, int32 customFontIndex) {
FontHolder *holder = getFontHolder(type, customFontIndex);
if (holder->_font) {
return holder->_font.get();
} else {
// Fallback to a default font
return FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
}
}
uint FontProvider::getScaledFontHeight(FontProvider::FontType type, int32 customFontIndex) {
FontHolder *holder = getFontHolder(type, customFontIndex);
return holder->_scaledHeight;
}
uint FontProvider::getOriginalFontHeight(FontProvider::FontType type, int32 customFontIndex) {
FontHolder *holder = getFontHolder(type, customFontIndex);
return holder->_originalHeight;
}
} // End of namespace Stark

View File

@@ -0,0 +1,92 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_FONT_PROVIDER_H
#define STARK_SERVICES_FONT_PROVIDER_H
#include "common/hash-str.h"
#include "common/ptr.h"
namespace Common {
class INIFile;
}
namespace Graphics {
class Font;
}
namespace Stark {
/**
* The font provider offers a set of predefined fonts for the game to use
*/
class FontProvider {
public:
FontProvider();
~FontProvider();
enum FontType {
kSmallFont,
kBigFont,
kCustomFont
};
/**
* Request a font matching the specified parameters
*/
const Graphics::Font *getScaledFont(FontType type, int32 customFontIndex);
/**
* Get the height of the font matching the specified parameters
*/
uint getScaledFontHeight(FontType type, int32 customFontIndex);
uint getOriginalFontHeight(FontType type, int32 customFontIndex);
/** Load all the fonts to memory */
void initFonts();
private:
struct FontHolder {
Common::String _name;
uint32 _originalHeight;
uint32 _scaledHeight;
Common::SharedPtr<Graphics::Font> _font;
FontHolder() : _originalHeight(0), _scaledHeight(0) {}
FontHolder(FontProvider *fontProvider, const Common::String &name, uint32 height);
};
void readFontEntry(const Common::INIFile *gui, FontHolder &holder, const char *nameKey, const char *sizeKey);
FontHolder *getFontHolder(FontType type, int32 customFontIndex);
FontHolder _smallFont;
FontHolder _bigFont;
FontHolder _customFonts[8];
Common::StringMap _ttfFileMap;
};
} // End of namespace Stark
#endif // STARK_SERVICES_FONT_PROVIDER_H

View File

@@ -0,0 +1,72 @@
/* 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/>.
*
*/
#include "engines/stark/services/gamechapter.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/global.h"
#include "common/path.h"
#include "common/tokenizer.h"
#include "common/formats/ini-file.h"
namespace Stark {
GameChapter::GameChapter() : _errorText("Unknown Chapter") {
Common::INIFile file;
if (!file.loadFromFile("chapters.ini")) {
error("Opening file 'chapters.ini' failed");
return;
}
Common::String section = file.getSections().front().name;
int index = 0;
Common::String key = Common::String::format("%02d", index);
Common::String value;
while (file.hasKey(key, section)) {
file.getKey(key, section, value);
_chapterEntries.push_back(ChapterEntry());
Common::StringTokenizer tokens(value, ":");
_chapterEntries.back().title = tokens.nextToken();
_chapterEntries.back().title.trim();
_chapterEntries.back().subtitle = tokens.nextToken();
_chapterEntries.back().subtitle.trim();
++index;
key = Common::String::format("%02d", index);
}
if (index < _numChapter) {
error("File 'chapters.ini' is incomplete");
}
}
const Common::String &GameChapter::getCurrentChapterTitle() const {
return getChapterTitle(StarkGlobal->getCurrentChapter());
}
const Common::String &GameChapter::getCurrentChapterSubtitle() const {
return getChapterSubtitle(StarkGlobal->getCurrentChapter());
}
} // End of namespace Stark

View File

@@ -0,0 +1,74 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_GAME_CHAPTER_H
#define STARK_SERVICES_GAME_CHAPTER_H
#include "common/str.h"
#include "common/array.h"
namespace Stark {
/**
* Game chapter services
*
* Provide the game chapter's title and subtitle
*/
class GameChapter {
public:
GameChapter();
~GameChapter() {}
const Common::String &getChapterTitle(uint chapter) const {
if (chapter >= _numChapter * 10) {
return _errorText;
} else {
return _chapterEntries[chapter / 10].title;
}
}
const Common::String &getCurrentChapterTitle() const;
const Common::String &getChapterSubtitle(uint chapter) const {
if (chapter >= _numChapter * 10) {
return _errorText;
} else {
return _chapterEntries[chapter / 10].subtitle;
}
}
const Common::String &getCurrentChapterSubtitle() const;
private:
static const int _numChapter = 15;
struct ChapterEntry {
Common::String title;
Common::String subtitle;
};
Common::Array<ChapterEntry> _chapterEntries;
Common::String _errorText;
};
} // End of namespace Stark
#endif // STARK_SERVICES_GAME_CHAPTER_H

View File

@@ -0,0 +1,275 @@
/* 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/>.
*
*/
#include "engines/stark/services/gameinterface.h"
#include "engines/stark/movement/walk.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/speech.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/resources/floorface.h"
#include "engines/stark/resources/script.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/visual/image.h"
#include "engines/stark/scene.h"
#include "engines/stark/services/userinterface.h"
namespace Stark {
GameInterface::GameInterface() {
}
GameInterface::~GameInterface() {
}
bool GameInterface::skipCurrentSpeeches() {
Current *current = StarkGlobal->getCurrent();
if (!current) {
return false; // No current location, nothing to do
}
// Get all speeches
Common::Array<Resources::Speech *> speeches;
speeches.push_back(StarkGlobal->getLevel()->listChildrenRecursive<Resources::Speech>());
speeches.push_back(current->getLevel()->listChildrenRecursive<Resources::Speech>());
speeches.push_back(current->getLocation()->listChildrenRecursive<Resources::Speech>());
// Stop them
bool skippedSpeeches = false;
for (uint i = 0; i < speeches.size(); i++) {
Resources::Speech *speech = speeches[i];
if (speech->isPlaying()) {
speech->stop();
skippedSpeeches = true;
}
}
return skippedSpeeches;
}
void GameInterface::walkTo(const Common::Point &mouse) {
Resources::Floor *floor = StarkGlobal->getCurrent()->getFloor();
Resources::ModelItem *april = StarkGlobal->getCurrent()->getInteractive();
if (!floor || !april) {
return;
}
Math::Ray mouseRay = StarkScene->makeRayFromMouse(mouse);
// First look for a direct intersection with the floor
Math::Vector3d destinationPosition;
int32 destinationFloorFaceIndex = floor->findFaceHitByRay(mouseRay, destinationPosition);
// Otherwise fall back to the floor face center closest to the ray
if (destinationFloorFaceIndex < 0) {
destinationFloorFaceIndex = floor->findFaceClosestToRay(mouseRay, destinationPosition);
}
if (destinationFloorFaceIndex < 0) {
// No destination was found
return;
}
Walk *walk = new Walk(april);
walk->setDestination(destinationPosition);
walk->start();
april->setMovement(walk);
}
VisualImageXMG *GameInterface::getActionImage(uint32 itemIndex, bool active) {
// Lookup the action's item in the inventory
Resources::KnowledgeSet *inventory = StarkGlobal->getLevel()->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory, true);
// Get the visual for the action
Resources::InventoryItem *action = inventory->findChildWithIndex<Resources::InventoryItem>(itemIndex);
Visual *visual = action->getActionVisual(active);
return visual->get<VisualImageXMG>();
}
VisualImageXMG *GameInterface::getCursorImage(uint32 itemIndex) {
// Lookup the item's item in the inventory
Resources::KnowledgeSet *inventory = StarkGlobal->getLevel()->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory, true);
// Get the visual for the item
Resources::InventoryItem *item = inventory->findChildWithIndex<Resources::InventoryItem>(itemIndex);
Visual *visual = item->getCursorVisual();
return visual->get<VisualImageXMG>();
}
bool GameInterface::itemHasAction(Resources::ItemVisual *item, int32 action) {
if (action != -1) {
return item->canPerformAction(action, 0);
} else {
Resources::ActionArray actions = listActionsPossibleForObject(item);
return !actions.empty();
}
}
bool GameInterface::itemHasActionAt(Resources::ItemVisual *item, const Common::Point &position, int32 action) {
int32 hotspotIndex = item->getHotspotIndexForPoint(position);
if (action != -1) {
return item->canPerformAction(action, hotspotIndex);
} else {
Resources::ActionArray actions = listActionsPossibleForObjectAt(item, position);
return !actions.empty();
}
}
int32 GameInterface::itemGetDefaultActionAt(Resources::ItemVisual *item, const Common::Point &position) {
int32 hotspotIndex = item->getHotspotIndexForPoint(position);
Resources::PATTable *table = item->findChildWithOrder<Resources::PATTable>(hotspotIndex);
if (table) {
return table->getDefaultAction();
} else {
return -1;
}
}
void GameInterface::itemDoAction(Resources::ItemVisual *item, uint32 action) {
item->doAction(action, 0);
}
void GameInterface::itemDoActionAt(Resources::ItemVisual *item, uint32 action, const Common::Point &position) {
int32 hotspotIndex = item->getHotspotIndexForPoint(position);
item->doAction(action, hotspotIndex);
}
Common::String GameInterface::getItemTitle(Resources::ItemVisual *item) {
return item->getHotspotTitle(0);
}
Common::String GameInterface::getItemTitleAt(Resources::ItemVisual *item, const Common::Point &pos) {
int32 hotspotIndex = item->getHotspotIndexForPoint(pos);
return item->getHotspotTitle(hotspotIndex);
}
Resources::ActionArray GameInterface::listActionsPossibleForObject(Resources::ItemVisual *item) {
if (item == nullptr) {
return Resources::ActionArray();
}
Resources::PATTable *table = item->findChildWithOrder<Resources::PATTable>(0);
if (table) {
return table->listPossibleActions();
} else {
return Resources::ActionArray();
}
}
Resources::ActionArray GameInterface::listActionsPossibleForObjectAt(Resources::ItemVisual *item,
const Common::Point &pos) {
if (item == nullptr) {
return Resources::ActionArray();
}
int index = item->getHotspotIndexForPoint(pos);
if (index < 0) {
return Resources::ActionArray();
}
Resources::PATTable *table = item->findChildWithOrder<Resources::PATTable>(index);
if (table) {
return table->listPossibleActions();
} else {
return Resources::ActionArray();
}
}
Resources::ActionArray GameInterface::listStockActionsPossibleForObject(Resources::ItemVisual *item) {
Resources::ActionArray actions = listActionsPossibleForObject(item);
Resources::ActionArray stockActions;
for (uint i = 0; i < actions.size(); i++) {
if (actions[i] < 4) {
stockActions.push_back(actions[i]);
}
}
return stockActions;
}
Resources::ActionArray GameInterface::listStockActionsPossibleForObjectAt(Resources::ItemVisual *item,
const Common::Point &pos) {
Resources::ActionArray actions = listActionsPossibleForObjectAt(item, pos);
Resources::ActionArray stockActions;
for (uint i = 0; i < actions.size(); i++) {
if (actions[i] < 4) {
stockActions.push_back(actions[i]);
}
}
return stockActions;
}
bool GameInterface::isAprilWalking() const {
Current *current = StarkGlobal->getCurrent();
if (!current) {
return false;
}
Resources::ModelItem *april = current->getInteractive();
if (!april) {
return false;
}
Movement *movement = april->getMovement();
if (!movement) {
return false;
}
Walk *walk = dynamic_cast<Walk *>(movement);
if (!walk) {
return false;
}
return !walk->hasEnded();
}
void GameInterface::setAprilRunning() {
Current *current = StarkGlobal->getCurrent();
Resources::ModelItem *april = current->getInteractive();
Movement *movement = april->getMovement();
Walk *walk = dynamic_cast<Walk *>(movement);
assert(walk);
walk->setRunning();
}
Common::Array<Common::Point> GameInterface::listExitPositions() {
return StarkGlobal->getCurrent()->getLocation()->listExitPositions();
}
} // End of namespace Stark

View File

@@ -0,0 +1,94 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_GAME_INTERFACE_H
#define STARK_SERVICES_GAME_INTERFACE_H
#include "engines/stark/resources/pattable.h"
#include "common/scummsys.h"
#include "common/rect.h"
namespace Stark {
class VisualImageXMG;
namespace Resources {
class ItemVisual;
}
/**
* Facade object for the user interface to interact with the game world
*/
class GameInterface {
public:
GameInterface();
~GameInterface();
/**
* Skip currently playing speeches
*
* @return true if at least one speech was skipped
*/
bool skipCurrentSpeeches();
/** Make April try to go to the location under the cursor */
void walkTo(const Common::Point &mouse);
VisualImageXMG *getActionImage(uint32 itemIndex, bool active);
VisualImageXMG *getCursorImage(uint32 itemIndex);
/** Can the item be used for an action. -1 for all actions */
bool itemHasAction(Resources::ItemVisual *item, int32 action);
bool itemHasActionAt(Resources::ItemVisual *item, const Common::Point &position, int32 action);
/** Get the item's default action */
int32 itemGetDefaultActionAt(Resources::ItemVisual *item, const Common::Point &position);
/** Do an action on the item */
void itemDoAction(Resources::ItemVisual *item, uint32 action);
void itemDoActionAt(Resources::ItemVisual *item, uint32 action, const Common::Point &position);
/** Get the item's name */
Common::String getItemTitle(Resources::ItemVisual *object);
Common::String getItemTitleAt(Resources::ItemVisual *object, const Common::Point &pos);
/** List the actions available for an item in the current game state */
Resources::ActionArray listActionsPossibleForObject(Resources::ItemVisual *item);
Resources::ActionArray listActionsPossibleForObjectAt(Resources::ItemVisual *item, const Common::Point &pos);
/** List the stock actions available for an item in the current game state (hand, mouth, eye) */
Resources::ActionArray listStockActionsPossibleForObject(Resources::ItemVisual *item);
Resources::ActionArray listStockActionsPossibleForObjectAt(Resources::ItemVisual *item, const Common::Point &pos);
/** Check if April is doing a walk movement */
bool isAprilWalking() const;
/** Make April run if she is walking */
void setAprilRunning();
/** List all the exit positions */
Common::Array<Common::Point> listExitPositions();
};
} // End of namespace Stark
#endif // STARK_SERVICES_GAME_INTERFACE_H

View File

@@ -0,0 +1,99 @@
/* 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/>.
*
*/
#include "engines/stark/services/gamemessage.h"
#include "common/file.h"
#include "common/formats/ini-file.h"
#include "gui/message.h"
namespace Stark {
GameMessage::GameMessage() :
_texts() {
Common::File tmp;
if (!tmp.open("language.ini")) {
warning("Unable to open 'language.ini'");
return;
}
// Skip the head of the language.ini that is in an unsupported format
Common::String line;
while (!tmp.eos() && !tmp.err()) {
line = tmp.readLine();
if (line.size() > 2 && line[0] == '-' && line[1] == '-') {
break;
}
}
Common::INIFile file;
if(file.loadFromStream(tmp)) {
Common::String section = "Language";
Common::String key, text;
for (uint i = 2; i <= 400; ++i) {
key = Common::String::format("%03d", i);
if (file.hasKey(key, section)) {
file.getKey(key, section, text);
_texts[i] = text;
}
}
}
// Pre-process some of the texts
if (_texts.contains(kYes)) {
Common::replace(_texts[kYes], "&", "");
}
if (_texts.contains(kNo)) {
Common::replace(_texts[kNo], "&", "");
}
if (_texts.contains(kOverwriteSave)) {
Common::replace(_texts[kOverwriteSave], "\\n", "\n");
}
}
Common::String GameMessage::getDefaultText(TextKey key) {
switch(key) {
case kOverwriteSave:
return "Are you sure you want to overwrite the savegame:\n'%s' ?";
case kEndAndLoad:
return "Are you sure you want to end your current game and load a new one ?";
case kInventory:
return "Inventory";
case kOptions:
return "Options";
case kQuit:
return "Quit";
case kQuitGamePrompt:
return "Are you sure you want to quit this game ?";
case kQuitPrompt:
return "Are you sure you want to quit ?";
case kYes:
return "Yes";
case kNo:
return "No";
default:
return "Unimplemented message text";
}
}
} // End of namespace Stark

View File

@@ -0,0 +1,67 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_GAME_MESSAGE_H
#define STARK_SERVICES_GAME_MESSAGE_H
#include "common/str.h"
#include "common/hashmap.h"
namespace Stark {
/**
* Game message service
*
* Provide message texts and dialogs used in various points
*/
class GameMessage {
public:
GameMessage();
~GameMessage() {}
enum TextKey {
kOverwriteSave = 10,
kEndAndLoad = 13,
kInventory = 353,
kOptions = 354,
kQuit = 355,
kQuitGamePrompt = 356,
kQuitPrompt = 357,
kYes = 358,
kNo = 359
};
/** Acquire a message text by a given key */
Common::String getTextByKey(TextKey key) {
if (_texts.contains(key)) return _texts[key];
return getDefaultText(key);
}
private:
Common::HashMap<uint, Common::String> _texts;
Common::String getDefaultText(TextKey key);
};
} // End of namespace Stark
#endif // STARK_SERVICES_GAME_MESSAGE_H

View 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/>.
*
*/
#include "engines/stark/services/global.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/knowledge.h"
#include "engines/stark/resources/knowledgeset.h"
namespace Stark {
Global::Global() :
_millisecondsPerGameloop(10),
_april(nullptr),
_root(nullptr),
_level(nullptr),
_current(nullptr),
_fastForward(false),
_inventory(nullptr) {
}
int32 Global::getCurrentChapter() {
Resources::KnowledgeSet *globalState = _level->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kState);
Resources::Knowledge *chapter = globalState->findChildWithIndex<Resources::Knowledge>(0);
return chapter->getIntegerValue();
}
void Global::setCurrentChapter(int32 value) {
Resources::KnowledgeSet *globalState = _level->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kState);
Resources::Knowledge *chapter = globalState->findChildWithIndex<Resources::Knowledge>(0);
chapter->setIntegerValue(value);
}
Common::String Global::getCharacterName(int32 id) {
Resources::KnowledgeSet *characters = _level->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kPersons);
Resources::Knowledge *character = characters->findChildWithIndex<Resources::Knowledge>(id);
return character->getName();
}
bool Global::hasInventoryItem(const Common::String &itemName) const {
Common::Array<Resources::Item*> inventoryItems = _inventory->listChildren<Resources::Item>(Resources::Item::kItemInventory);
for (uint i = 0; i < inventoryItems.size(); i++) {
if (inventoryItems[i]->getName() == itemName && inventoryItems[i]->isEnabled()) {
return true;
}
}
return false;
}
} // End of namespace Stark

View File

@@ -0,0 +1,122 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_GLOBAL_H
#define STARK_SERVICES_GLOBAL_H
#include "common/scummsys.h"
#include "common/array.h"
namespace Stark {
namespace Resources {
class Camera;
class Floor;
class GlobalItemTemplate;
class ModelItem;
class KnowledgeSet;
class Level;
class Location;
class Root;
}
/**
* Current level / location holder object
*/
class Current {
public:
Current() :
_level(nullptr),
_location(nullptr),
_floor(nullptr),
_camera(nullptr),
_interactive(nullptr) {
}
Resources::Level *getLevel() const { return _level; }
Resources::Location *getLocation() const { return _location; }
Resources::Floor *getFloor() const { return _floor; }
Resources::Camera *getCamera() const { return _camera; }
Resources::ModelItem *getInteractive() const { return _interactive; }
void setLevel(Resources::Level *level) { _level = level; }
void setLocation(Resources::Location *location) { _location = location; }
void setFloor(Resources::Floor *floor) { _floor = floor; }
void setCamera(Resources::Camera *camera) { _camera = camera; }
void setInteractive(Resources::ModelItem *interactive) { _interactive = interactive; }
private:
Resources::Level *_level;
Resources::Location *_location;
Resources::ModelItem *_interactive;
Resources::Floor *_floor;
Resources::Camera *_camera;
};
/**
* Global resources holder object
*/
class Global {
public:
Global();
Resources::Root *getRoot() const { return _root; }
Resources::Level *getLevel() const { return _level; }
Current *getCurrent() const { return _current; }
bool isFastForward() const { return _fastForward; }
uint getMillisecondsPerGameloop() const { return _millisecondsPerGameloop; }
Resources::GlobalItemTemplate *getApril() const { return _april; }
Resources::KnowledgeSet *getInventory() const { return _inventory; }
void setRoot(Resources::Root *root) { _root = root; }
void setLevel(Resources::Level *level) { _level = level; }
void setCurrent(Current *current) { _current = current; }
void setFastForward() { _fastForward = true; }
void setNormalSpeed() { _fastForward = false; }
void setMillisecondsPerGameloop(uint millisecondsPerGameloop) { _millisecondsPerGameloop = millisecondsPerGameloop; }
void setApril(Resources::GlobalItemTemplate *april) { _april = april; }
void setInventory(Resources::KnowledgeSet *inventory) { _inventory = inventory; }
/** Retrieve the current chapter number from the global resource tree */
int32 getCurrentChapter();
/** Check if the player has an inventory item using its name */
bool hasInventoryItem(const Common::String &itemName) const;
/** Change the current chapter */
void setCurrentChapter(int32 value);
/** Get the name of a character by its id */
Common::String getCharacterName(int32 id);
private:
uint _millisecondsPerGameloop;
Resources::Root *_root;
Resources::Level *_level;
Resources::KnowledgeSet *_inventory;
Resources::GlobalItemTemplate *_april;
Current *_current;
bool _fastForward;
};
} // End of namespace Stark
#endif // STARK_SERVICES_GLOBAL_H

View 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/>.
*
*/
#include "engines/stark/services/resourceprovider.h"
#include "engines/stark/resources/bookmark.h"
#include "engines/stark/resources/camera.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/layer.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/root.h"
#include "engines/stark/resources/script.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/stateprovider.h"
#include "engines/stark/services/userinterface.h"
namespace Stark {
ResourceProvider::ResourceProvider(ArchiveLoader *archiveLoader, StateProvider *stateProvider, Global *global) :
_archiveLoader(archiveLoader),
_stateProvider(stateProvider),
_global(global),
_locationChangeRequest(false),
_restoreCurrentState(false),
_nextDirection(0) {
}
void ResourceProvider::initGlobal() {
// Load the root archive
_archiveLoader->load("x.xarc");
// Set the root tree
Resources::Root *root = _archiveLoader->useRoot<Resources::Root>("x.xarc");
_global->setRoot(root);
// Resources lifecycle update
root->onAllLoaded();
// Find the global level node
Resources::Level *global = root->findChildWithSubtype<Resources::Level>(Resources::Level::kGlobal);
// Load the global archive
Common::Path globalArchiveName = _archiveLoader->buildArchiveName(global);
_archiveLoader->load(globalArchiveName);
// Set the global tree
global = _archiveLoader->useRoot<Resources::Level>(globalArchiveName);
_global->setLevel(global);
// Resources lifecycle update
global->onAllLoaded();
// Load the state
_stateProvider->restoreLevelState(global);
_global->setInventory(global->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory));
_global->setApril(global->findChildWithSubtype<Resources::GlobalItemTemplate>(Resources::Item::kItemGlobalTemplate));
}
Current *ResourceProvider::findLevel(uint16 level) const {
for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) {
if ((*it)->getLevel()->getIndex() == level) {
return *it;
}
}
return nullptr;
}
Current *ResourceProvider::findLocation(uint16 level, uint16 location) const {
for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) {
if ((*it)->getLevel()->getIndex() == level
&& (*it)->getLocation()->getIndex() == location) {
return *it;
}
}
return nullptr;
}
Resources::Level *ResourceProvider::getLevel(uint16 level) const {
Current *current = findLevel(level);
if (current) {
return current->getLevel();
}
return nullptr;
}
Resources::Location *ResourceProvider::getLocation(uint16 level, uint16 location) const {
Current *current = findLocation(level, location);
if (current) {
return current->getLocation();
}
return nullptr;
}
void ResourceProvider::pushAndChangeLocation(int16 level, int16 location) {
pushCurrentLocation();
requestLocationChange(level, location);
}
void ResourceProvider::returnToPushedLocation() {
popCurrentLocation();
}
void ResourceProvider::pushCurrentLocation() {
PreviousLocation current;
current.level = _global->getCurrent()->getLevel()->getIndex();
current.location = _global->getCurrent()->getLocation()->getIndex();
current.inventoryOpen = StarkUserInterface->isInventoryOpen();
_locationStack.push_back(current);
StarkUserInterface->inventoryOpen(false);
}
void ResourceProvider::popCurrentLocation() {
if (_locationStack.empty()) {
error("Empty location stack");
} else {
PreviousLocation previous = _locationStack.back();
_locationStack.pop_back();
requestLocationChange(previous.level, previous.location);
StarkUserInterface->inventoryOpen(previous.inventoryOpen);
}
}
void ResourceProvider::requestLocationChange(uint16 level, uint16 location) {
Current *currentLocation = new Current();
_locations.push_back(currentLocation);
// Retrieve the level archive name
Resources::Root *root = _global->getRoot();
Resources::Level *rootLevelResource = root->findChildWithIndex<Resources::Level>(level);
Common::Path levelArchive = _archiveLoader->buildArchiveName(rootLevelResource);
// Load the archive, and get the resource sub-tree root
bool newlyLoaded = _archiveLoader->load(levelArchive);
currentLocation->setLevel(_archiveLoader->useRoot<Resources::Level>(levelArchive));
// If we just loaded a resource tree, restore its state
if (newlyLoaded) {
currentLocation->getLevel()->onAllLoaded();
_stateProvider->restoreLevelState(currentLocation->getLevel());
}
// Retrieve the location archive name
Resources::Level *levelResource = currentLocation->getLevel();
Resources::Location *levelLocationResource = levelResource->findChildWithIndex<Resources::Location>(location);
Common::Path locationArchive = _archiveLoader->buildArchiveName(levelResource, levelLocationResource);
// Load the archive, and get the resource sub-tree root
newlyLoaded = _archiveLoader->load(locationArchive);
currentLocation->setLocation(_archiveLoader->useRoot<Resources::Location>(locationArchive));
if (currentLocation->getLocation()->has3DLayer()) {
Resources::Layer3D *layer = currentLocation->getLocation()->findChildWithSubtype<Resources::Layer3D>(Resources::Layer::kLayer3D);
currentLocation->setFloor(layer->findChild<Resources::Floor>());
currentLocation->setCamera(layer->findChild<Resources::Camera>());
} else {
currentLocation->setFloor(nullptr);
currentLocation->setCamera(nullptr);
}
// If we just loaded a resource tree, restore its state
if (newlyLoaded) {
currentLocation->getLocation()->onAllLoaded();
_stateProvider->restoreLocationState(currentLocation->getLevel(), currentLocation->getLocation());
}
_locationChangeRequest = true;
}
void ResourceProvider::performLocationChange() {
Current *current = _locations.back();
Current *previous = _global->getCurrent();
bool levelChanged = !previous || previous->getLevel() != current->getLevel();
// Exit the previous location
if (previous) {
// Trigger location change scripts
if (levelChanged) {
runLocationChangeScripts(previous->getLevel(), Resources::Script::kCallModeExitLocation);
}
runLocationChangeScripts(previous->getLocation(), Resources::Script::kCallModeExitLocation);
// Resources lifecycle update
previous->getLocation()->onExitLocation();
previous->getLevel()->onExitLocation();
_global->getLevel()->onExitLocation();
}
// Clear all pointers to location objects in the UI instances
StarkUserInterface->clearLocationDependentState();
// Set the new current location
_global->setCurrent(current);
// Resources lifecycle update
_global->getLevel()->onEnterLocation();
current->getLevel()->onEnterLocation();
current->getLocation()->onEnterLocation();
if (current->getLocation()->has3DLayer()) {
// Fetch the scene item for April
current->setInteractive(Resources::Object::cast<Resources::ModelItem>(_global->getApril()->getSceneInstance()));
}
if (_restoreCurrentState) {
_stateProvider->restoreGlobalState(_global->getLevel());
_stateProvider->restoreCurrentLevelState(current->getLevel());
_stateProvider->restoreCurrentLocationState(current->getLevel(), current->getLocation());
_restoreCurrentState = false;
} else {
setAprilInitialPosition();
setScrollInitialPosition();
// Trigger location change scripts
if (levelChanged) {
runLocationChangeScripts(current->getLevel(), Resources::Script::kCallModeEnterLocation);
}
runLocationChangeScripts(current->getLocation(), Resources::Script::kCallModeEnterLocation);
}
current->getLocation()->resetAnimationBlending();
purgeOldLocations();
_locationChangeRequest = false;
}
void ResourceProvider::runLocationChangeScripts(Resources::Object *resource, uint32 scriptCallMode) {
Common::Array<Resources::Script *> scripts = resource->listChildrenRecursive<Resources::Script>();
if (scriptCallMode == Resources::Script::kCallModeEnterLocation) {
for (uint i = 0; i < scripts.size(); i++) {
scripts[i]->reset();
}
}
for (uint i = 0; i < scripts.size(); i++) {
scripts[i]->execute(scriptCallMode);
}
if (scriptCallMode == Resources::Script::kCallModeExitLocation) {
Common::Array<Resources::Sound *> sounds = resource->listChildrenRecursive<Resources::Sound>();
for (uint i = 0; i < sounds.size(); i++) {
sounds[i]->stop();
}
}
}
void ResourceProvider::setNextLocationPosition(const ResourceReference &bookmark, int32 direction) {
_nextPositionBookmarkReference = bookmark;
_nextDirection = direction;
}
void ResourceProvider::setAprilInitialPosition() {
Current *current = _global->getCurrent();
Resources::ModelItem *april = current->getInteractive();
if (!april) {
return; // No character
}
// Set the initial position for April
if (!_nextPositionBookmarkReference.empty()) {
Resources::Bookmark *position = _nextPositionBookmarkReference.resolve<Resources::Bookmark>();
april->placeOnBookmark(position);
Resources::Camera *camera = current->getCamera();
Math::Angle cameraAngle = camera->getHorizontalAngle();
april->setDirection(_nextDirection + cameraAngle);
} else if (april->getFloorFaceIndex() <= 0) {
// No target location provided, place April on the first floor face
april->placeDefaultPosition();
}
_nextPositionBookmarkReference = ResourceReference();
_nextDirection = 0;
}
void ResourceProvider::setScrollInitialPosition() {
Current *current = _global->getCurrent();
Resources::Location *location = current->getLocation();
location->scrollToCharacterImmediate();
}
void ResourceProvider::purgeOldLocations() {
while (_locations.size() > 2) {
Current *location = _locations.front();
_stateProvider->saveLocationState(location->getLevel(), location->getLocation());
_stateProvider->saveLevelState(location->getLevel());
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel(), location->getLocation()));
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel()));
delete location;
_locations.pop_front();
}
_archiveLoader->unloadUnused();
}
void ResourceProvider::commitActiveLocationsState() {
// Save active location states
for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) {
_stateProvider->saveLocationState((*it)->getLevel(), (*it)->getLocation());
_stateProvider->saveLevelState((*it)->getLevel());
}
_stateProvider->saveLevelState(_global->getLevel());
// Save the current location "extended" state, to be able to restore them to the exact same state.
Current *location = _global->getCurrent();
_stateProvider->saveCurrentLocationState(location->getLevel(), location->getLocation());
_stateProvider->saveCurrentLevelState(location->getLevel());
_stateProvider->saveGlobalState(_global->getLevel());
}
void ResourceProvider::shutdown() {
_stateProvider->clear();
_locationStack.clear();
// Flush the locations list
for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) {
Current *location = *it;
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel(), location->getLocation()));
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(location->getLevel()));
delete location;
}
_locations.clear();
// Return the global resources
if (_global->getLevel()) {
_archiveLoader->returnRoot(_archiveLoader->buildArchiveName(_global->getLevel()));
_global->setLevel(nullptr);
}
if (_global->getRoot()) {
_archiveLoader->returnRoot("x.xarc");
_global->setRoot(nullptr);
}
_global->setCurrent(nullptr);
_global->setInventory(nullptr);
_global->setApril(nullptr);
_archiveLoader->unloadUnused();
}
Resources::Level *ResourceProvider::getLevelFromLocation(Resources::Location *location) const {
for (CurrentList::const_iterator it = _locations.begin(); it != _locations.end(); it++) {
if ((*it)->getLocation() == location) {
return (*it)->getLevel();
}
}
return nullptr;
}
void ResourceProvider::readLocationStack(Common::SeekableReadStream *stream, uint32 version) {
ResourceSerializer serializer(stream, nullptr, version);
saveLoadLocationStack(serializer);
}
void ResourceProvider::writeLocationStack(Common::WriteStream *stream) {
ResourceSerializer serializer(nullptr, stream, StateProvider::kSaveVersion);
saveLoadLocationStack(serializer);
}
void ResourceProvider::saveLoadLocationStack(ResourceSerializer &serializer) {
serializer.syncArraySize(_locationStack, 12);
for (uint i = 0; i < _locationStack.size(); i++) {
serializer.syncAsUint16LE(_locationStack[i].level);
serializer.syncAsUint16LE(_locationStack[i].location);
serializer.syncAsUint32LE(_locationStack[i].inventoryOpen);
}
}
} // End of namespace Stark

View File

@@ -0,0 +1,134 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_RESOURCE_PROVIDER_H
#define STARK_SERVICES_RESOURCE_PROVIDER_H
#include "common/list.h"
#include "engines/stark/resourcereference.h"
namespace Stark {
namespace Resources {
class Level;
class Location;
class Object;
}
class ArchiveLoader;
class Current;
class Global;
class StateProvider;
/**
* Game Resource provider.
*
* Maintains a list of resource trees.
* Maintained trees are the global and the current ones.
*/
class ResourceProvider {
public:
ResourceProvider(ArchiveLoader *archiveLoader, StateProvider *stateProvider, Global *global);
/** Load the global archives and fill the global object */
void initGlobal();
/** Load the resources for the specified location */
void requestLocationChange(uint16 level, uint16 location);
/** Is a location change pending? */
bool hasLocationChangeRequest() const { return _locationChangeRequest; }
void setShouldRestoreCurrentState() { _restoreCurrentState = true; }
void pushAndChangeLocation(int16 level, int16 location);
void returnToPushedLocation();
/** Save and restore the previous location stack */
void readLocationStack(Common::SeekableReadStream *stream, uint32 version);
void writeLocationStack(Common::WriteStream *stream);
/**
* Apply a location change request.
*
* Update the global object with the new location.
* Perform the necessary resource lifecycle updates
*/
void performLocationChange();
/** Set the initial position and direction for the next location change */
void setNextLocationPosition(const ResourceReference &bookmark, int32 direction);
/** Save the current location state to the state store. */
void commitActiveLocationsState();
/** Release the global and current resources */
void shutdown();
/** Obtain the root resource for a loaded level */
Resources::Level *getLevel(uint16 level) const;
/** Obtain the root resource for a loaded location */
Resources::Location *getLocation(uint16 level, uint16 location) const;
/** Get the parent level from a currently loaded location */
Resources::Level *getLevelFromLocation(Resources::Location *location) const;
private:
struct PreviousLocation {
uint16 location;
uint16 level;
bool inventoryOpen;
};
void pushCurrentLocation();
void popCurrentLocation();
void saveLoadLocationStack(ResourceSerializer &serializer);
Common::Array<PreviousLocation> _locationStack;
typedef Common::List<Current *> CurrentList;
Current *findLevel(uint16 level) const;
Current *findLocation(uint16 level, uint16 location) const;
void purgeOldLocations();
void runLocationChangeScripts(Resources::Object *resource, uint32 scriptCallMode);
void setAprilInitialPosition();
void setScrollInitialPosition();
Global *_global;
ArchiveLoader *_archiveLoader;
StateProvider *_stateProvider;
bool _locationChangeRequest;
bool _restoreCurrentState;
CurrentList _locations;
ResourceReference _nextPositionBookmarkReference;
int32 _nextDirection;
};
} // End of namespace Stark
#endif // STARK_SERVICES_RESOURCE_PROVIDER_H

View File

@@ -0,0 +1,30 @@
/* 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/>.
*
*/
#include "engines/stark/services/services.h"
namespace Common {
DECLARE_SINGLETON(Stark::StarkServices);
}
namespace Stark {
} // End of namespace Stark

View 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/>.
*
*/
#ifndef STARK_SERVICES_SERVICES_H
#define STARK_SERVICES_SERVICES_H
#include "common/singleton.h"
#include "common/scummsys.h"
namespace Common {
class RandomSource;
}
namespace Stark {
namespace Gfx {
class Driver;
}
class ArchiveLoader;
class DialogPlayer;
class Diary;
class FontProvider;
class GameInterface;
class Global;
class ResourceProvider;
class StaticProvider;
class Scene;
class UserInterface;
class Settings;
class StateProvider;
class GameChapter;
class GameMessage;
/**
* Public services available as a singleton
*/
class StarkServices : public Common::Singleton<StarkServices> {
public:
StarkServices() {
archiveLoader = nullptr;
dialogPlayer = nullptr;
diary = nullptr;
gfx = nullptr;
global = nullptr;
resourceProvider = nullptr;
randomSource = nullptr;
scene = nullptr;
staticProvider = nullptr;
gameInterface = nullptr;
userInterface = nullptr;
fontProvider = nullptr;
settings = nullptr;
gameChapter = nullptr;
gameMessage = nullptr;
stateProvider = nullptr;
}
ArchiveLoader *archiveLoader;
DialogPlayer *dialogPlayer;
Diary *diary;
Gfx::Driver *gfx;
Global *global;
ResourceProvider *resourceProvider;
Common::RandomSource *randomSource;
Scene *scene;
StaticProvider *staticProvider;
GameInterface *gameInterface;
UserInterface *userInterface;
FontProvider *fontProvider;
Settings *settings;
GameChapter *gameChapter;
GameMessage *gameMessage;
StateProvider *stateProvider;
};
/** Shortcuts for accessing the services. */
#define StarkArchiveLoader StarkServices::instance().archiveLoader
#define StarkDialogPlayer StarkServices::instance().dialogPlayer
#define StarkDiary StarkServices::instance().diary
#define StarkGfx StarkServices::instance().gfx
#define StarkGlobal StarkServices::instance().global
#define StarkResourceProvider StarkServices::instance().resourceProvider
#define StarkRandomSource StarkServices::instance().randomSource
#define StarkScene StarkServices::instance().scene
#define StarkStaticProvider StarkServices::instance().staticProvider
#define StarkGameInterface StarkServices::instance().gameInterface
#define StarkUserInterface StarkServices::instance().userInterface
#define StarkFontProvider StarkServices::instance().fontProvider
#define StarkSettings StarkServices::instance().settings
#define StarkGameChapter StarkServices::instance().gameChapter
#define StarkGameMessage StarkServices::instance().gameMessage
#define StarkStateProvider StarkServices::instance().stateProvider
} // End of namespace Stark
#endif // STARK_SERVICES_SERVICES_H

View 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/>.
*
*/
#include "engines/stark/services/settings.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/archiveloader.h"
#include "common/config-manager.h"
#include "common/debug.h"
#include "audio/mixer.h"
#include "engines/advancedDetector.h"
namespace Stark {
Settings::Settings(Audio::Mixer *mixer, const ADGameDescription *gd) :
_mixer(mixer),
_isDemo(gd->flags & ADGF_DEMO),
_language(gd->language) {
// Initialize keys
_boolKey[kHighModel] = "enable_high_resolution_models";
_boolKey[kSubtitle] = "subtitles";
_boolKey[kSpecialFX] = "enable_special_effects";
_boolKey[kShadow] = "enable_shadows";
_boolKey[kHighFMV] = "play_high_resolution_videos";
_boolKey[kTimeSkip] = "enable_time_skip";
_intKey[kVoice] = "speech_volume";
_intKey[kMusic] = "music_volume";
_intKey[kSfx] = "sfx_volume";
_intKey[kSaveLoadPage] = "saveload_lastpage";
// Register default settings
ConfMan.registerDefault(_boolKey[kHighModel], true);
ConfMan.registerDefault(_boolKey[kSubtitle], true);
ConfMan.registerDefault(_boolKey[kSpecialFX], true);
ConfMan.registerDefault(_boolKey[kShadow], true);
ConfMan.registerDefault(_boolKey[kHighFMV], true);
ConfMan.registerDefault(_boolKey[kTimeSkip], false);
ConfMan.registerDefault(_intKey[kSaveLoadPage], 0);
ConfMan.registerDefault("replacement_png_premultiply_alpha", false);
ConfMan.registerDefault("ignore_font_settings", true);
// Use the FunCom logo video to check low-resolution fmv
Common::SeekableReadStream *lowResFMV = StarkArchiveLoader->getExternalFile("1402_lo_res.bbb", "Global/");
_hasLowRes = lowResFMV;
delete lowResFMV;
}
void Settings::setIntSetting(IntSettingIndex index, int value) {
ConfMan.setInt(_intKey[index], value);
Audio::Mixer::SoundType type;
switch (index) {
case kVoice:
type = Audio::Mixer::kSpeechSoundType;
break;
case kMusic:
type = Audio::Mixer::kMusicSoundType;
break;
case kSfx:
type = Audio::Mixer::kSFXSoundType;
break;
default:
return;
}
_mixer->setVolumeForSoundType(type, value);
}
bool Settings::isAssetsModEnabled() const {
return ConfMan.getBool("enable_assets_mod");
}
bool Settings::shouldPreMultiplyReplacementPNGs() const {
return ConfMan.getBool("replacement_png_premultiply_alpha");
}
Gfx::Bitmap::SamplingFilter Settings::getImageSamplingFilter() const {
return ConfMan.getBool("use_linear_filtering") ? Gfx::Bitmap::kLinear : Gfx::Bitmap::kNearest;
}
bool Settings::isFontAntialiasingEnabled() const {
return ConfMan.getBool("enable_font_antialiasing");
}
Common::CodePage Settings::getTextCodePage() const {
switch (_language) {
case Common::PL_POL:
return Common::kWindows1250;
case Common::RU_RUS:
return Common::kWindows1251;
case Common::HE_ISR:
return Common::kWindows1255;
default:
return Common::kWindows1252;
}
}
bool Settings::shouldIgnoreFontSettings() const {
return ConfMan.getBool("ignore_font_settings") && _language == Common::EN_ANY;
}
Common::Language Settings::getLanguage() const {
return _language;
}
} // End of namespace Stark

View File

@@ -0,0 +1,148 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_SETTINGS_H
#define STARK_SERVICES_SETTINGS_H
#include "common/config-manager.h"
#include "common/language.h"
#include "common/ustr.h"
#include "engines/stark/gfx/bitmap.h"
#include "engines/stark/services/services.h"
struct ADGameDescription;
namespace Audio {
class Mixer;
}
namespace Stark {
/**
* Settings services.
*
* Maintains the settings of the game.
*/
class Settings {
public:
enum BoolSettingIndex {
kHighModel,
kSubtitle,
kSpecialFX,
kShadow,
kHighFMV,
kTimeSkip
};
enum IntSettingIndex {
kVoice,
kMusic,
kSfx,
kSaveLoadPage
};
Settings(Audio::Mixer *mixer, const ADGameDescription *gd);
~Settings() {}
/**
* Is this a demo version of the game?
*
* This is true either for 4-CD or 2-CD style demos
*/
bool isDemo() const {
return _isDemo;
}
/** Get the settings value */
bool getBoolSetting(BoolSettingIndex index) { return ConfMan.getBool(_boolKey[index]); }
int getIntSetting(IntSettingIndex index) { return ConfMan.getInt(_intKey[index]); }
/** Flip the boolean settings */
void flipSetting(BoolSettingIndex index) {
ConfMan.setBool(_boolKey[index], !getBoolSetting(index));
}
/** Set the integer settings */
void setIntSetting(IntSettingIndex index, int value);
/** Check whether low-resolution fmv is available */
bool hasLowResFMV() { return _hasLowRes; }
/** Enable the book of secrets */
void enableBookOfSecrets() {
ConfMan.setBool("xoBfOsterceS", true);
ConfMan.flushToDisk();
}
/** Check whether the book of secrets is enabled */
bool hasBookOfSecrets() { return ConfMan.hasKey("xoBfOsterceS"); }
/** Should the game try to load external replacement assets? */
bool isAssetsModEnabled() const;
/**
* Should the engine apply alpha pre-multiplication when loading replacement PNGs
*
* When rendering, bitmaps are expected to be in pre-multiplied alpha format.
* It's best to have the PNGs in that format on file to speed up loading by removing
* the need to convert them. However this option enables the conversion when loading
* the files to they can be stored with regular alpha transparency for convenience
* when testing.
*/
bool shouldPreMultiplyReplacementPNGs() const;
/** Should linear filtering be used when sampling the background image bitmaps? */
Gfx::Bitmap::SamplingFilter getImageSamplingFilter() const;
/** The codepage text is encoded in or this version of the game */
Common::CodePage getTextCodePage() const;
/** Should TrueType fonts be rendered with anti-aliasing? */
bool isFontAntialiasingEnabled() const;
/**
* Should the font settings from 'gui.ini' be ignored.
*
* Some versions of the game, especially the GOG.com version have a version of 'gui.ini'
* that causes poor visuals. We just ignore the settings from the game and use the
* default values from ScummVM.
*/
bool shouldIgnoreFontSettings() const;
/**
* Return the game language (which is currently initialized with the Advanced Detector description language field value)
*/
Common::Language getLanguage() const;
private:
Audio::Mixer *_mixer;
bool _hasLowRes;
const bool _isDemo;
const Common::Language _language;
const char *_boolKey[6];
const char *_intKey[4];
};
} // End of namespace Stark
#endif // STARK_SERVICES_SETTINGS_H

View File

@@ -0,0 +1,283 @@
/* 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/>.
*
*/
#include "engines/stark/services/stateprovider.h"
#include "common/memstream.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/object.h"
namespace Stark {
StateReadStream::StateReadStream(Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream) :
SeekableSubReadStream(parentStream, 0, parentStream->size(), disposeParentStream) {
}
StateReadStream::~StateReadStream() {
}
Common::String StateReadStream::readString() {
// Read the string length
uint32 length = readUint32LE();
// Read the string
char *data = new char[length];
read(data, length);
Common::String string(data, length);
delete[] data;
return string;
}
StateProvider::ResourceTreeState::ResourceTreeState(uint32 size, byte *data, uint32 version) :
_size(size),
_data(data),
_version(version) {
}
StateProvider::ResourceTreeState::~ResourceTreeState() {
free(_data);
}
StateProvider::~StateProvider() {
clear();
}
void StateProvider::clear() {
for (ResourceTreeStateMap::iterator it = _stateStore.begin(); it != _stateStore.end(); it++) {
delete it->_value;
}
_stateStore.clear();
}
void StateProvider::restoreLevelState(Resources::Level *level) {
Common::String storeKey = level->getName();
restoreResourceTreeState(storeKey, level, false);
}
void StateProvider::restoreCurrentLevelState(Resources::Level *level) {
restoreResourceTreeState("Current", level, true);
}
void StateProvider::restoreLocationState(Resources::Level *level, Resources::Location *location) {
Common::String storeKey = level->getName() + location->getName();
restoreResourceTreeState(storeKey, location, false);
}
void StateProvider::restoreCurrentLocationState(Resources::Level *level, Resources::Location *location) {
restoreResourceTreeState("CurrentCurrent", location, true);
}
void StateProvider::restoreGlobalState(Resources::Level *level) {
restoreResourceTreeState("CurrentGlobal", level, true);
}
void StateProvider::restoreResourceTreeState(const Common::String &storeKey, Resources::Object *root, bool current) {
if (_stateStore.contains(storeKey)) {
ResourceTreeState *state = _stateStore[storeKey];
Common::MemoryReadStream stream(state->getData(), state->getSize(), DisposeAfterUse::NO);
readResourceTree(root, &stream, current, state->getVersion());
}
}
void StateProvider::readResourceTree(Resources::Object *resource, Common::SeekableReadStream *stream, bool current, uint32 version) {
// Read the resource to the source stream
/* byte type = */ stream->readByte();
/* byte subType = */ stream->readByte();
uint32 size = stream->readUint32LE();
if (size > 0) {
Common::SeekableReadStream *resourceStream = stream->readStream(size);
ResourceSerializer serializer(resourceStream, nullptr, version);
// Deserialize the resource state from stream
if (current) {
resource->saveLoadCurrent(&serializer);
} else {
resource->saveLoad(&serializer);
}
delete resourceStream;
}
// Deserialize the resource children
Common::Array<Resources::Object *> children = resource->listChildren<Resources::Object>();
for (uint i = 0; i < children.size(); i++) {
readResourceTree(children[i], stream, current, version);
}
}
void StateProvider::saveLevelState(Resources::Level *level) {
Common::String storeKey = level->getName();
saveResourceTreeState(storeKey, level, false);
}
void StateProvider::saveCurrentLevelState(Resources::Level *level) {
saveResourceTreeState("Current", level, true);
}
void StateProvider::saveLocationState(Resources::Level *level, Resources::Location *location) {
Common::String storeKey = level->getName() + location->getName();
saveResourceTreeState(storeKey, location, false);
}
void StateProvider::saveCurrentLocationState(Resources::Level *level, Resources::Location *location) {
saveResourceTreeState("CurrentCurrent", location, true);
}
void StateProvider::saveGlobalState(Resources::Level *level) {
saveResourceTreeState("CurrentGlobal", level, true);
}
void StateProvider::saveResourceTreeState(const Common::String &storeKey, Resources::Object *root, bool current) {
// Delete any previous data
if (_stateStore.contains(storeKey)) {
delete _stateStore[storeKey];
_stateStore.erase(storeKey);
}
// Write the tree state to memory
Common::MemoryWriteStreamDynamic stream(DisposeAfterUse::NO);
writeResourceTree(root, &stream, current);
// Add the state to the store
_stateStore[storeKey] = new ResourceTreeState(stream.size(), stream.getData(), kSaveVersion);
}
void StateProvider::writeResourceTree(Resources::Object *resource, Common::WriteStream *stream, bool current) {
// Explicit scope to control the lifespan of the memory stream
{
Common::MemoryWriteStreamDynamic resourceStream(DisposeAfterUse::YES);
ResourceSerializer serializer(nullptr, &resourceStream, kSaveVersion);
// Serialize the resource to a memory stream
if (current) {
resource->saveLoadCurrent(&serializer);
} else {
resource->saveLoad(&serializer);
}
// Write the resource to the target stream
stream->writeByte(resource->getType().get());
stream->writeByte(resource->getSubType());
stream->writeUint32LE(resourceStream.size());
stream->write(resourceStream.getData(), resourceStream.size());
}
// Serialize the resource children
Common::Array<Resources::Object *> children = resource->listChildren<Resources::Object>();
for (uint i = 0; i < children.size(); i++) {
writeResourceTree(children[i], stream, current);
}
}
void StateProvider::readStateFromStream(StateReadStream *stream, uint saveVersion) {
clear();
uint32 treeCount = stream->readUint32LE();
for (uint i = 0; i < treeCount; i++) {
// Read the store key
Common::String key = stream->readString();
// Each resource tree state needs to have its own version because
// some may never be made active again and serialized in the latest version.
// In that case they stay in a previous version.
uint treeVersion = 6;
if (saveVersion > 6) {
treeVersion = stream->readUint32LE();
}
// Read the data size
uint32 dataSize = stream->readUint32LE();
// Read the data
byte *data = (byte *) malloc(dataSize);
stream->read(data, dataSize);
_stateStore[key] = new ResourceTreeState(dataSize, data, treeVersion);
}
}
void StateProvider::writeStateToStream(Common::WriteStream *stream) {
stream->writeUint32LE(_stateStore.size());
for (ResourceTreeStateMap::iterator it = _stateStore.begin(); it != _stateStore.end(); it++) {
stream->writeUint32LE(it->_key.size());
stream->writeString(it->_key);
stream->writeUint32LE(it->_value->getVersion());
stream->writeUint32LE(it->_value->getSize());
stream->write(it->_value->getData(), it->_value->getSize());
}
}
ResourceSerializer::ResourceSerializer(Common::SeekableReadStream *in, Common::WriteStream *out, uint32 version) :
Common::Serializer(in, out) {
_version = version;
}
void ResourceSerializer::syncAsFloat(float &value) {
if (isLoading()) {
value = _loadStream->readFloatLE();
} else {
_saveStream->writeFloatLE(value);
}
}
void ResourceSerializer::syncAsVector3d(Math::Vector3d &value) {
syncAsFloat(value.x());
syncAsFloat(value.y());
syncAsFloat(value.z());
}
void ResourceSerializer::syncAsResourceReference(ResourceReference &reference) {
if (isLoading()) {
reference.loadFromStream(_loadStream);
} else {
reference.saveToStream(_saveStream);
}
}
void ResourceSerializer::syncAsString32(Common::String &string) {
if (isLoading()) {
string.clear();
uint32 length = _loadStream->readUint32LE();
for (uint i = 0; i < length; i++) {
char c = _loadStream->readByte();
string += c;
}
_bytesSynced += 4 + length;
} else {
_saveStream->writeUint32LE(string.size());
_saveStream->writeString(string);
_bytesSynced += 4 + string.size();
}
}
} // End of namespace Stark

View File

@@ -0,0 +1,160 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_STATE_PROVIDER_H
#define STARK_SERVICES_STATE_PROVIDER_H
#include "common/hashmap.h"
#include "common/serializer.h"
#include "common/hash-str.h"
#include "common/stream.h"
#include "common/substream.h"
#include "math/mathfwd.h"
#include "engines/stark/resourcereference.h"
namespace Stark {
namespace Resources {
class Object;
class Level;
class Location;
}
class StateReadStream : public Common::SeekableSubReadStream {
public:
explicit StateReadStream(Common::SeekableReadStream *parentStream, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::YES);
virtual ~StateReadStream();
Common::String readString();
};
class ResourceSerializer : public Common::Serializer {
public:
ResourceSerializer(Common::SeekableReadStream *in, Common::WriteStream *out, uint32 version);
void syncAsFloat(float &value);
void syncAsVector3d(Math::Vector3d &value);
void syncAsResourceReference(ResourceReference &reference);
void syncAsString32(Common::String &string);
template<typename T>
void syncAsResourceReference(T **object, Version minVersion = 0, Version maxVersion = kLastVersion);
template<typename T>
void syncArraySize(Common::Array<T> &array, Version minVersion = 0, Version maxVersion = kLastVersion);
};
template<typename T>
void ResourceSerializer::syncAsResourceReference(T **object, Version minVersion, Version maxVersion) {
assert(object);
if (_version < minVersion || _version > maxVersion)
return; // Ignore anything which is not supposed to be present in this save game version
if (isLoading()) {
ResourceReference reference;
reference.loadFromStream(_loadStream);
*object = reference.resolve<T>();
} else {
ResourceReference reference;
reference.buildFromResource(*object);
reference.saveToStream(_saveStream);
}
}
template<typename T>
void ResourceSerializer::syncArraySize(Common::Array<T> &array, Version minVersion, Version maxVersion) {
if (_version < minVersion || _version > maxVersion)
return; // Ignore anything which is not supposed to be present in this save game version
uint32 size = array.size();
syncAsUint32LE(size);
if (isLoading()) {
array.resize(size);
}
}
/**
* Resource state provider.
*
* Maintains a serialized version of the state of the resource trees.
*/
class StateProvider {
public:
~StateProvider();
void restoreLevelState(Resources::Level *level);
void restoreCurrentLevelState(Resources::Level *level);
void restoreLocationState(Resources::Level *level, Resources::Location *location);
void restoreCurrentLocationState(Resources::Level *level, Resources::Location *location);
void restoreGlobalState(Resources::Level *level);
void saveLevelState(Resources::Level *level);
void saveCurrentLevelState(Resources::Level *level);
void saveLocationState(Resources::Level *level, Resources::Location *location);
void saveCurrentLocationState(Resources::Level *level, Resources::Location *location);
void saveGlobalState(Resources::Level *level);
/** Replace the current states by those read from the stream */
void readStateFromStream(StateReadStream *stream, uint saveVersion);
/** Write the states in the store to a stream */
void writeStateToStream(Common::WriteStream *stream);
/** Clear all the state, effectively preparing to start a new game */
void clear();
static const uint kMinSaveVersion = 6;
static const uint kSaveVersion = 13;
private:
class ResourceTreeState {
public:
ResourceTreeState(uint32 size, byte *data, uint32 version);
~ResourceTreeState();
uint32 getVersion() const { return _version; }
uint32 getSize() const { return _size; }
byte *getData() const { return _data; }
private:
uint32 _version;
uint32 _size;
byte *_data;
};
typedef Common::HashMap<Common::String, ResourceTreeState *> ResourceTreeStateMap;
void restoreResourceTreeState(const Common::String &storeKey, Resources::Object *root, bool current);
void saveResourceTreeState(const Common::String &storeKey, Resources::Object *root, bool current);
void readResourceTree(Resources::Object *resource, Common::SeekableReadStream *stream, bool current, uint32 version);
void writeResourceTree(Resources::Object *resource, Common::WriteStream *stream, bool current);
ResourceTreeStateMap _stateStore;
};
} // End of namespace Stark
#endif // STARK_SERVICES_STATE_PROVIDER_H

View File

@@ -0,0 +1,170 @@
/* 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/>.
*
*/
#include "engines/stark/services/staticprovider.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/animscript.h"
#include "engines/stark/resources/container.h"
#include "engines/stark/resources/image.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/services/archiveloader.h"
#include "engines/stark/services/global.h"
#include "engines/stark/visual/image.h"
namespace Stark {
StaticProvider::StaticProvider(ArchiveLoader *archiveLoader) :
_archiveLoader(archiveLoader),
_level(nullptr),
_location(nullptr) {
}
void StaticProvider::init() {
// Load the static archive
_archiveLoader->load("static/static.xarc");
// Set the root tree
_level = _archiveLoader->useRoot<Resources::Level>("static/static.xarc");
// Resources lifecycle update
_level->onAllLoaded();
Resources::Item *staticItem = _level->findChild<Resources::Item>();
_stockAnims = staticItem->listChildren<Resources::Anim>();
for (uint i = 0; i< _stockAnims.size(); i++) {
_stockAnims[i]->applyToItem(nullptr);
}
Resources::Anim *imagesAnim = _stockAnims[kImages];
_stockImages = imagesAnim->listChildrenRecursive<Resources::Image>();
}
void StaticProvider::onGameLoop() {
_level->onGameLoop();
}
void StaticProvider::shutdown() {
if (_location) {
unloadLocation(_location);
}
_level = nullptr;
_archiveLoader->returnRoot("static/static.xarc");
_archiveLoader->unloadUnused();
}
VisualImageXMG *StaticProvider::getCursorImage(uint32 cursor) const {
Resources::Anim *anim = _stockAnims[cursor];
return anim->getVisual()->get<VisualImageXMG>();
}
VisualImageXMG *StaticProvider::getUIElement(UIElement element) const {
return getCursorImage(element);
}
VisualImageXMG *StaticProvider::getUIElement(UIElement element, uint32 index) const {
Resources::Anim *anim = _stockAnims[element];
uint32 prevIndex = anim->getCurrentFrame();
anim->selectFrame(index);
VisualImageXMG *visualImage = anim->getVisual()->get<VisualImageXMG>();
anim->selectFrame(prevIndex);
return visualImage;
}
VisualImageXMG *StaticProvider::getUIImage(UIImage image) const {
Resources::Image *anim = _stockImages[image];
return anim->getVisual()->get<VisualImageXMG>();
}
void StaticProvider::goToAnimScriptStatement(StaticProvider::UIElement stockUIElement, int animScriptItemIndex) {
Resources::Anim *anim = _stockAnims[stockUIElement];
Resources::AnimScript *animScript = anim->findChild<Resources::AnimScript>();
Resources::AnimScriptItem *animScriptItem = animScript->findChildWithIndex<Resources::AnimScriptItem>(animScriptItemIndex);
animScript->goToScriptItem(animScriptItem);
}
Resources::Sound *StaticProvider::getUISound(UISound sound) const {
Resources::Item *staticLevelItem = _level->findChild<Resources::Item>();
Resources::Anim *anim = staticLevelItem->findChildWithOrder<Resources::Anim>(4);
Resources::Container *sounds = anim->findChildWithSubtype<Resources::Container>(Resources::Container::kSounds);
return sounds->findChildWithOrder<Resources::Sound>(sound);
}
Common::Path StaticProvider::buildLocationArchiveName(const char *locationName) const {
return Common::Path(Common::String::format("static/%s/%s.xarc", locationName, locationName));
}
Resources::Location *StaticProvider::loadLocation(const char *locationName) {
assert(!_location);
Common::Path archiveName = buildLocationArchiveName(locationName);
_archiveLoader->load(archiveName);
_location = _archiveLoader->useRoot<Resources::Location>(archiveName);
_location->onAllLoaded();
_location->onEnterLocation();
// Start background music
Common::Array<Resources::Sound *> sounds = _location->listChildren<Resources::Sound>(Resources::Sound::kSoundBackground);
for (uint i = 0; i < sounds.size(); i++) {
sounds[i]->play();
}
return _location;
}
void StaticProvider::unloadLocation(Resources::Location *location) {
assert(_location == location);
location->onExitLocation();
Common::Path archiveName = buildLocationArchiveName(location->getName().c_str());
_archiveLoader->returnRoot(archiveName);
_archiveLoader->unloadUnused();
_location = nullptr;
}
bool StaticProvider::isStaticLocation() const {
return _location != nullptr;
}
Resources::Location *StaticProvider::getLocation() const {
return _location;
}
Resources::Sound *StaticProvider::getLocationSound(uint16 index) const {
assert(_location);
Resources::Container *sounds = _location->findChildWithSubtype<Resources::Container>(Resources::Container::kSounds);
return sounds->findChildWithIndex<Resources::Sound>(index);
}
} // End of namespace Stark

View File

@@ -0,0 +1,140 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_STATIC_PROVIDER_H
#define STARK_SERVICES_STATIC_PROVIDER_H
#include "common/array.h"
#include "common/path.h"
#include "common/scummsys.h"
namespace Stark {
namespace Resources {
class Anim;
class Image;
class Level;
class Location;
class Sound;
}
class ArchiveLoader;
class Global;
class VisualImageXMG;
/**
* Static Resource provider.
*
* Maintains the static resource trees.
* Maintained trees are the level and the location ones.
*/
class StaticProvider {
public:
explicit StaticProvider(ArchiveLoader *archiveLoader);
enum UIElement {
kInventoryScrollUpArrow = 1,
kInventoryScrollDownArrow = 2,
kImages = 4,
kActionMenuBg = 5,
kTextScrollUpArrow = 6,
kTextScrollDownArrow = 7,
kQuit = 8,
kCheckMark = 13,
kVolume = 14,
kDiaryNormal = 15,
kInventory = 16,
kExitArrow = 17,
kExitArrowLeft = 18,
kExitArrowRight = 19,
kTextBackgroundActive = 20,
kTextBackgroundPassive = 21,
kDiaryTabbed = 22
};
enum UIImage {
kInventoryBg = 0,
kDialogOptionBullet = 4
};
enum UISound {
kActionMouthHover = 0,
kActionHover = 1,
kInventoryNewItem = 2
};
/** Load the static level archive */
void init();
/** State update loop */
void onGameLoop();
/** Release the static resources */
void shutdown();
/** Obtain the static level */
Resources::Level *getLevel() const { return _level; }
/** Get an image for a static cursor */
VisualImageXMG *getCursorImage(uint32 cursor) const;
/** Get an image for a static UI element */
VisualImageXMG *getUIElement(UIElement element) const;
VisualImageXMG *getUIElement(UIElement element, uint32 index) const;
/** Get an image for a static UI element */
VisualImageXMG *getUIImage(UIImage image) const;
/** Get a static UI sound resource */
Resources::Sound *getUISound(UISound sound) const;
/** Move execution of a static UI element anim script to the specified item */
void goToAnimScriptStatement(UIElement stockUIElement, int animScriptItemIndex);
/** Load a static location and set it as current */
Resources::Location *loadLocation(const char *locationName);
/** Is a static location currently loaded? */
bool isStaticLocation() const;
/** Obtain the currently loaded static location, if any */
Resources::Location *getLocation() const;
/** Look up a sound resource in the current static location by its index */
Resources::Sound *getLocationSound(uint16 index) const;
/** Unload the current static location */
void unloadLocation(Resources::Location *location);
private:
ArchiveLoader *_archiveLoader;
Resources::Level *_level;
Resources::Location *_location;
Common::Array<Resources::Anim *> _stockAnims;
Common::Array<Resources::Image *> _stockImages;
Common::Path buildLocationArchiveName(const char *locationName) const;
};
} // End of namespace Stark
#endif // STARK_SERVICES_STATIC_PROVIDER_H

View File

@@ -0,0 +1,572 @@
/* 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/>.
*
*/
#include "common/events.h"
#include "common/stream.h"
#include "common/system.h"
#include "engines/engine.h"
#include "engines/stark/services/userinterface.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/services/diary.h"
#include "engines/stark/services/gameinterface.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/staticprovider.h"
#include "engines/stark/services/resourceprovider.h"
#include "engines/stark/services/settings.h"
#include "engines/stark/ui/cursor.h"
#include "engines/stark/ui/dialogbox.h"
#include "engines/stark/ui/menu/diaryindex.h"
#include "engines/stark/ui/menu/mainmenu.h"
#include "engines/stark/ui/menu/settingsmenu.h"
#include "engines/stark/ui/menu/saveloadmenu.h"
#include "engines/stark/ui/menu/fmvmenu.h"
#include "engines/stark/ui/menu/diarypages.h"
#include "engines/stark/ui/menu/dialogmenu.h"
#include "engines/stark/ui/world/inventorywindow.h"
#include "engines/stark/ui/world/fmvscreen.h"
#include "engines/stark/ui/world/gamescreen.h"
#include "engines/stark/ui/world/gamewindow.h"
#include "engines/stark/ui/world/dialogpanel.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/item.h"
#include "gui/message.h"
namespace Stark {
UserInterface::UserInterface(StarkEngine *vm, Gfx::Driver *gfx) :
_gfx(gfx),
_cursor(nullptr),
_diaryIndexScreen(nullptr),
_mainMenuScreen(nullptr),
_settingsMenuScreen(nullptr),
_saveMenuScreen(nullptr),
_loadMenuScreen(nullptr),
_fmvMenuScreen(nullptr),
_diaryPagesScreen(nullptr),
_dialogScreen(nullptr),
_exitGame(false),
_quitToMainMenu(false),
_shouldToggleSubtitle(false),
_shouldGoBackToPreviousScreen(false),
_fmvScreen(nullptr),
_gameScreen(nullptr),
_interactive(true),
_interactionAttemptDenied(false),
_currentScreen(nullptr),
_gameWindowThumbnail(nullptr),
_modalDialog(nullptr) {
_vm = vm;
}
UserInterface::~UserInterface() {
freeGameScreenThumbnail();
delete _modalDialog;
delete _gameScreen;
delete _fmvScreen;
delete _diaryIndexScreen;
delete _cursor;
delete _mainMenuScreen;
delete _settingsMenuScreen;
delete _saveMenuScreen;
delete _loadMenuScreen;
delete _fmvMenuScreen;
delete _diaryPagesScreen;
delete _dialogScreen;
}
void UserInterface::init() {
_cursor = new Cursor(_gfx);
_mainMenuScreen = new MainMenuScreen(_gfx, _cursor);
_gameScreen = new GameScreen(_gfx, _cursor);
_diaryIndexScreen = new DiaryIndexScreen(_gfx, _cursor);
_settingsMenuScreen = new SettingsMenuScreen(_gfx, _cursor);
_saveMenuScreen = new SaveMenuScreen(_gfx, _cursor);
_loadMenuScreen = new LoadMenuScreen(_gfx, _cursor);
_fmvMenuScreen = new FMVMenuScreen(_gfx, _cursor);
_diaryPagesScreen = new DiaryPagesScreen(_gfx, _cursor);
_dialogScreen = new DialogScreen(_gfx, _cursor);
_fmvScreen = new FMVScreen(_gfx, _cursor);
_modalDialog = new DialogBox(_vm, _gfx, _cursor);
_prevScreenNameStack.push(Screen::kScreenMainMenu);
_currentScreen = _fmvScreen;
// Play the FunCom logo video
_fmvScreen->play("1402.bbb");
}
void UserInterface::onGameLoop() {
StarkStaticProvider->onGameLoop();
if (_modalDialog->isVisible()) {
_modalDialog->handleGameLoop();
_modalDialog->handleMouseMove();
} else {
_currentScreen->handleGameLoop();
// Check for UI mouse overs
// TODO: Call mouse move only if the mouse position actually changed
// probably some code will need to be moved to gameloop handling to
// account for the case where hotspots move and the mouse cursor needs
// to be updated regardless of the mouse actually moved.
_currentScreen->handleMouseMove();
}
}
void UserInterface::handleMouseMove(const Common::Point &pos) {
_cursor->setMousePosition(pos);
}
void UserInterface::handleMouseUp() {
// Only the settings menu needs to handle this event
// TODO: Clean this up
_settingsMenuScreen->handleMouseUp();
}
void UserInterface::handleClick() {
if (_modalDialog->isVisible()) {
_modalDialog->handleClick();
} else {
_currentScreen->handleClick();
}
}
void UserInterface::handleRightClick() {
if (_modalDialog->isVisible()) {
_modalDialog->handleRightClick();
} else {
_currentScreen->handleRightClick();
}
}
void UserInterface::handleDoubleClick() {
if (_modalDialog->isVisible()) {
_modalDialog->handleDoubleClick();
} else {
_currentScreen->handleDoubleClick();
}
}
void UserInterface::handleEscape() {
if (StarkGameInterface->skipCurrentSpeeches()) {
return;
}
if (skipFMV()) {
return;
}
Screen::Name curScreenName = _currentScreen->getName();
if (curScreenName != Screen::kScreenGame && curScreenName != Screen::kScreenMainMenu) {
backPrevScreen();
} else if (StarkSettings->getBoolSetting(Settings::kTimeSkip)) {
StarkGlobal->setFastForward();
}
}
void UserInterface::inventoryOpen(bool open) {
// Make the inventory update its contents.
if (open) {
_gameScreen->getInventoryWindow()->open();
} else {
_gameScreen->getInventoryWindow()->close();
}
}
int16 UserInterface::getSelectedInventoryItem() const {
if (_gameScreen) {
return _gameScreen->getInventoryWindow()->getSelectedInventoryItem();
} else {
return -1;
}
}
void UserInterface::selectInventoryItem(int16 itemIndex) {
_gameScreen->getInventoryWindow()->setSelectedInventoryItem(itemIndex);
}
void UserInterface::requestFMVPlayback(const Common::Path &name) {
_shouldPlayFmv = name;
}
void UserInterface::onFMVStopped() {
_shouldGoBackToPreviousScreen = true;
}
void UserInterface::changeScreen(Screen::Name screenName) {
if (screenName == _currentScreen->getName()) {
return;
}
_prevScreenNameStack.push(_currentScreen->getName());
_currentScreen->close();
_currentScreen = getScreenByName(screenName);
_currentScreen->open();
}
void UserInterface::backPrevScreen() {
// No need to check the stack since at least there will be a MainMenuScreen in it
// and MainMenuScreen will not request to go back
changeScreen(_prevScreenNameStack.pop());
// No need to push for going back
_prevScreenNameStack.pop();
}
void UserInterface::restoreScreenHistory() {
_shouldGoBackToPreviousScreen = false;
_prevScreenNameStack.clear();
_prevScreenNameStack.push(Screen::kScreenMainMenu);
}
Screen *UserInterface::getScreenByName(Screen::Name screenName) const {
switch (screenName) {
case Screen::kScreenFMV:
return _fmvScreen;
case Screen::kScreenDiaryIndex:
return _diaryIndexScreen;
case Screen::kScreenGame:
return _gameScreen;
case Screen::kScreenMainMenu:
return _mainMenuScreen;
case Screen::kScreenSettingsMenu:
return _settingsMenuScreen;
case Screen::kScreenSaveMenu:
return _saveMenuScreen;
case Screen::kScreenLoadMenu:
return _loadMenuScreen;
case Screen::kScreenFMVMenu:
return _fmvMenuScreen;
case Screen::kScreenDiaryPages:
return _diaryPagesScreen;
case Screen::kScreenDialog:
return _dialogScreen;
default:
error("Unhandled screen name '%d'", screenName);
}
}
bool UserInterface::isInGameScreen() const {
return _currentScreen && (_currentScreen->getName() == Screen::kScreenGame);
}
bool UserInterface::isInSaveLoadMenuScreen() const {
Screen::Name name = _currentScreen->getName();
return name == Screen::kScreenSaveMenu || name == Screen::kScreenLoadMenu;
}
bool UserInterface::isInDiaryIndexScreen() const {
return _currentScreen->getName() == Screen::kScreenDiaryIndex;
}
bool UserInterface::isInventoryOpen() const {
return _gameScreen->getInventoryWindow()->isVisible();
}
bool UserInterface::skipFMV() {
if (_currentScreen->getName() == Screen::kScreenFMV) {
_fmvScreen->stop();
return true;
}
return false;
}
void UserInterface::render() {
_currentScreen->render();
if (_modalDialog->isVisible()) {
_modalDialog->render();
}
// The cursor depends on the UI being done.
if (_currentScreen->getName() != Screen::kScreenFMV) {
_cursor->render();
}
}
bool UserInterface::isInteractive() const {
return _interactive;
}
void UserInterface::setInteractive(bool interactive) {
if (interactive && !_interactive) {
StarkGlobal->setNormalSpeed();
} else if (!interactive && _interactive) {
_interactionAttemptDenied = false;
}
_interactive = interactive;
}
void UserInterface::markInteractionDenied() {
if (!_interactive) {
_interactionAttemptDenied = true;
}
}
bool UserInterface::wasInteractionDenied() const {
return !_interactive && _interactionAttemptDenied;
}
void UserInterface::clearLocationDependentState() {
_gameScreen->reset();
}
void UserInterface::optionsOpen() {
changeScreen(Screen::kScreenDiaryIndex);
}
void UserInterface::saveGameScreenThumbnail() {
freeGameScreenThumbnail();
if (StarkGlobal->getLevel() && StarkGlobal->getCurrent()) {
// Re-render the screen to exclude the cursor
StarkGfx->clearScreen();
_gameScreen->render();
}
Graphics::Surface *big = _gameScreen->getGameWindow()->getScreenshot();
assert(big->format.bytesPerPixel == 4);
_gameWindowThumbnail = new Graphics::Surface();
_gameWindowThumbnail->create(kThumbnailWidth, kThumbnailHeight, big->format);
uint32 *dst = (uint32 *)_gameWindowThumbnail->getPixels();
for (int i = 0; i < _gameWindowThumbnail->h; i++) {
for (int j = 0; j < _gameWindowThumbnail->w; j++) {
uint32 srcX = big->w * j / _gameWindowThumbnail->w;
uint32 srcY = big->h * i / _gameWindowThumbnail->h;
uint32 *src = (uint32 *)big->getBasePtr(srcX, srcY);
// Copy RGBA pixel
*dst++ = *src;
}
}
big->free();
delete big;
}
void UserInterface::freeGameScreenThumbnail() {
if (_gameWindowThumbnail) {
_gameWindowThumbnail->free();
delete _gameWindowThumbnail;
_gameWindowThumbnail = nullptr;
}
}
const Graphics::Surface *UserInterface::getGameWindowThumbnail() const {
return _gameWindowThumbnail;
}
void UserInterface::onScreenChanged() {
_gameScreen->onScreenChanged();
if (_modalDialog->isVisible()) {
_modalDialog->onScreenChanged();
}
if (!isInGameScreen()) {
_currentScreen->onScreenChanged();
}
}
void UserInterface::notifyInventoryItemEnabled(uint16 itemIndex) {
_gameScreen->notifyInventoryItemEnabled(itemIndex);
}
void UserInterface::notifyDiaryEntryEnabled() {
_gameScreen->notifyDiaryEntryEnabled();
}
void UserInterface::toggleScreen(Screen::Name screenName) {
Screen::Name currentName = _currentScreen->getName();
if (currentName == screenName
|| (currentName == Screen::kScreenSaveMenu && screenName == Screen::kScreenLoadMenu)
|| (currentName == Screen::kScreenLoadMenu && screenName == Screen::kScreenSaveMenu)) {
backPrevScreen();
} else if (currentName == Screen::kScreenGame
|| currentName == Screen::kScreenDiaryIndex
|| (currentName == Screen::kScreenMainMenu && screenName == Screen::kScreenLoadMenu)
|| (currentName == Screen::kScreenMainMenu && screenName == Screen::kScreenSettingsMenu)) {
changeScreen(screenName);
}
}
void UserInterface::performToggleSubtitle() {
StarkSettings->flipSetting(Settings::kSubtitle);
_shouldToggleSubtitle = false;
}
void UserInterface::cycleInventory(bool forward) {
int16 curItem = getSelectedInventoryItem();
int16 nextItem = StarkGlobal->getInventory()->getNeighborInventoryItem(curItem, forward);
selectInventoryItem(nextItem);
}
void UserInterface::doQueuedScreenChange() {
if (_quitToMainMenu) {
clearLocationDependentState();
changeScreen(Screen::kScreenGame);
StarkResourceProvider->shutdown();
changeScreen(Screen::kScreenMainMenu);
_prevScreenNameStack.clear();
_quitToMainMenu = false;
}
if (_shouldGoBackToPreviousScreen) {
backPrevScreen();
_shouldGoBackToPreviousScreen = false;
}
if (!_shouldPlayFmv.empty()) {
changeScreen(Screen::kScreenFMV);
_fmvScreen->play(_shouldPlayFmv);
_shouldPlayFmv.clear();
}
}
void UserInterface::handleActions(Common::CustomEventType customType) {
if (_modalDialog->isVisible()) {
_modalDialog->onKeyPress(customType);
return;
}
if (customType == kActionSkip) {
handleEscape();
} else if (customType == kActionSelectDialogue) {
if (isInGameScreen()) {
_gameScreen->getDialogPanel()->selectFocusedOption();
}
} else if (customType == kActionDiaryMenu) {
toggleScreen(Screen::kScreenDiaryIndex);
} else if (customType == kActionSaveGame) {
if (isInSaveLoadMenuScreen() || g_engine->canSaveGameStateCurrently()) {
toggleScreen(Screen::kScreenSaveMenu);
}
} else if (customType == kActionLoadGame) {
toggleScreen(Screen::kScreenLoadMenu);
} else if (customType == kActionConversationLog) {
toggleScreen(Screen::kScreenDialog);
} else if (customType == kActionAprilsDiary) {
if (StarkDiary->isEnabled()) {
toggleScreen(Screen::kScreenDiaryPages);
}
} else if (customType == kActionVideoReplay) {
toggleScreen(Screen::kScreenFMVMenu);
} else if (customType == kActionGameSettings) {
toggleScreen(Screen::kScreenSettingsMenu);
} else if (customType == kActionSaveScreenshot) {
g_system->saveScreenshot();
} else if (customType == kActionToggleSubtitles) {
if (isInGameScreen()) {
_shouldToggleSubtitle = !_shouldToggleSubtitle;
}
} else if (customType == kActionQuitToMenu) {
if (isInGameScreen() || isInDiaryIndexScreen()) {
confirm(GameMessage::kQuitGamePrompt, this, &UserInterface::requestQuitToMainMenu);
}
} else if (customType == kActionCycleForwardInventory) {
if (isInGameScreen() && isInteractive()) {
cycleInventory(false);
}
} else if (customType == kActionCycleBackInventory) {
if (isInGameScreen() && isInteractive()) {
cycleInventory(true);
}
} else if (customType == kActionInventory) {
if (isInGameScreen() && isInteractive()) {
inventoryOpen(!isInventoryOpen());
}
} else if (customType == kActionDisplayExits) {
if (isInGameScreen() && isInteractive()) {
_gameScreen->getGameWindow()->toggleExitDisplay();
}
} else if (customType == kActionExitGame) {
confirm(GameMessage::kQuitPrompt, this, &UserInterface::notifyShouldExit);
} else if (customType == kActionPause) {
if (isInGameScreen()) {
if (g_engine->isPaused()) {
_gamePauseToken.clear();
} else {
_gamePauseToken = g_engine->pauseEngine();
debug("The game is paused");
}
}
}
if (isInGameScreen()) {
if (isInventoryOpen()) {
if (customType == kActionInventoryScrollUp) {
_gameScreen->getInventoryWindow()->scrollUp();
} else if (customType == kActionInventoryScrollDown) {
_gameScreen->getInventoryWindow()->scrollDown();
}
} else {
if (customType == kActionDialogueScrollUp) {
_gameScreen->getDialogPanel()->scrollUp();
} else if (customType == kActionDialogueScrollDown) {
_gameScreen->getDialogPanel()->scrollDown();
} else if (customType == kActionNextDialogue) {
_gameScreen->getDialogPanel()->focusNextOption();
} else if (customType == kActionPrevDialogue) {
_gameScreen->getDialogPanel()->focusPrevOption();
}
}
}
}
void UserInterface::handleKeyPress(const Common::KeyState &keyState) {
// TODO: Delegate keypress handling to the screens
if (keyState.keycode >= Common::KEYCODE_1 && keyState.keycode <= Common::KEYCODE_9) {
if (isInGameScreen()) {
uint index = keyState.keycode - Common::KEYCODE_1;
_gameScreen->getDialogPanel()->selectOption(index);
}
}
}
void UserInterface::confirm(const Common::String &message, Common::Functor0<void> *confirmCallBack) {
Common::String textYes = StarkGameMessage->getTextByKey(GameMessage::kYes);
Common::String textNo = StarkGameMessage->getTextByKey(GameMessage::kNo);
_modalDialog->open(message, confirmCallBack, textYes, textNo);
}
void UserInterface::confirm(GameMessage::TextKey key, Common::Functor0<void> *confirmCallBack) {
Common::String message = StarkGameMessage->getTextByKey(key);
confirm(message, confirmCallBack);
}
} // End of namespace Stark

View File

@@ -0,0 +1,250 @@
/* 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/>.
*
*/
#ifndef STARK_SERVICES_USER_INTERFACE_H
#define STARK_SERVICES_USER_INTERFACE_H
#include "engines/stark/stark.h"
#include "engines/stark/ui/screen.h"
#include "engines/stark/services/gamemessage.h"
#include "engines/engine.h"
#include "common/keyboard.h"
#include "common/rect.h"
#include "common/str-array.h"
#include "common/stack.h"
namespace Common {
class SeekableReadStream;
class WriteStream;
}
namespace Graphics {
struct Surface;
}
namespace Stark {
namespace Gfx {
class Driver;
}
class DialogBox;
class DiaryIndexScreen;
class GameScreen;
class MainMenuScreen;
class SettingsMenuScreen;
class SaveMenuScreen;
class LoadMenuScreen;
class FMVMenuScreen;
class DiaryPagesScreen;
class DialogScreen;
class Cursor;
class FMVScreen;
enum {
kThumbnailWidth = 160,
kThumbnailHeight = 92,
kThumbnailSize = kThumbnailWidth * kThumbnailHeight * 4
};
/**
* Facade object for interacting with the user interface from the rest of the engine
*/
class UserInterface {
public:
explicit UserInterface(StarkEngine *vm, Gfx::Driver *gfx);
virtual ~UserInterface();
void init();
/** Called once per game loop. */
void onGameLoop();
void render();
void handleMouseMove(const Common::Point &pos);
void handleMouseUp();
void handleClick();
void handleRightClick();
void handleDoubleClick();
void handleEscape();
void notifyShouldExit() { _exitGame = true; }
void inventoryOpen(bool open);
bool shouldExit() { return _exitGame; }
/** Start playing a FMV */
void requestFMVPlayback(const Common::Path &name);
/** FMV playback has just ended */
void onFMVStopped();
/**
* Abort the currently playing FMV, if any
*
* @return true if a FMV was skipped
*/
bool skipFMV();
/** Set the currently displayed screen */
void changeScreen(Screen::Name screenName);
/** Back to the previous displayed screen */
void backPrevScreen();
/** Apply the scheduled screen change if any */
void doQueuedScreenChange();
/** Back to the main menu screen and rest resources */
void requestQuitToMainMenu() { _quitToMainMenu = true; }
/** Restore the screen travelling history to the initial state*/
void restoreScreenHistory();
/** Is the game screen currently displayed? */
bool isInGameScreen() const;
/** Is the save & load menu screen currently displayed? */
bool isInSaveLoadMenuScreen() const;
/** Is the diary index screen currently displayed? */
bool isInDiaryIndexScreen() const;
/** Is the inventory panel being displayed? */
bool isInventoryOpen() const;
/** Can the player interact with the game world? */
bool isInteractive() const;
/** Allow or forbid interaction with the game world */
void setInteractive(bool interactive);
/** A new item has been added to the player's inventory */
void notifyInventoryItemEnabled(uint16 itemIndex);
/** A new entry has been added to the player's diary */
void notifyDiaryEntryEnabled();
/** Access the selected inventory item */
int16 getSelectedInventoryItem() const;
void selectInventoryItem(int16 itemIndex);
/** Clears all the pointers to data that may be location dependent */
void clearLocationDependentState();
/** Open the in game options menu */
void optionsOpen();
/** Signal a denied interaction that occurred during a non interactive period */
void markInteractionDenied();
/** Was a player interaction with the world denied during this non interactive period? */
bool wasInteractionDenied() const;
/** The screen resolution just changed, rebuild resolution dependent data */
void onScreenChanged();
/** Grab a screenshot of the game screen and store it in the class context as a thumbnail */
void saveGameScreenThumbnail();
/** Clear the currently stored game screen thumbnail, if any */
void freeGameScreenThumbnail();
/** Get the currently stored game screen thumbnail, returns nullptr if there is not thumbnail stored */
const Graphics::Surface *getGameWindowThumbnail() const;
/**
* Display a confirmation dialog
*
* Close the dialog when the cancel button is pressed,
* call a callback when the confirm button is pressed.
*/
template<class T>
void confirm(const Common::String &message, T *instance, void (T::*confirmCallBack)());
template<class T>
void confirm(GameMessage::TextKey key, T *instance, void (T::*confirmCallBack)());
void confirm(const Common::String &message, Common::Functor0<void> *confirmCallBack);
void confirm(GameMessage::TextKey key, Common::Functor0<void> *confirmCallBack);
/** Directly open or close a screen */
void toggleScreen(Screen::Name screenName);
/** Toggle subtitles on and off */
bool hasToggleSubtitleRequest() { return _shouldToggleSubtitle; }
void performToggleSubtitle();
/** Perform an action after a keypress */
void handleActions(Common::CustomEventType customType);
void handleKeyPress(const Common::KeyState &keyState);
private:
Screen *getScreenByName(Screen::Name screenName) const;
void cycleInventory(bool forward);
StarkEngine *_vm;
GameScreen *_gameScreen;
FMVScreen *_fmvScreen;
DiaryIndexScreen *_diaryIndexScreen;
MainMenuScreen *_mainMenuScreen;
SettingsMenuScreen *_settingsMenuScreen;
SaveMenuScreen *_saveMenuScreen;
LoadMenuScreen *_loadMenuScreen;
FMVMenuScreen *_fmvMenuScreen;
DiaryPagesScreen *_diaryPagesScreen;
DialogScreen *_dialogScreen;
Screen *_currentScreen;
Common::Stack<Screen::Name> _prevScreenNameStack;
DialogBox *_modalDialog;
Cursor *_cursor;
Gfx::Driver *_gfx;
bool _exitGame;
bool _quitToMainMenu;
PauseToken _gamePauseToken;
bool _interactive;
bool _interactionAttemptDenied;
bool _shouldToggleSubtitle;
// TODO: Generalize to all screen changes
bool _shouldGoBackToPreviousScreen;
Common::Path _shouldPlayFmv;
Graphics::Surface *_gameWindowThumbnail;
};
template<class T>
void UserInterface::confirm(GameMessage::TextKey key, T *instance, void (T::*confirmCallBack)()) {
confirm(key, new Common::Functor0Mem<void, T>(instance, confirmCallBack));
}
template<class T>
void UserInterface::confirm(const Common::String &message, T *instance, void (T::*confirmCallBack)()) {
confirm(message, new Common::Functor0Mem<void, T>(instance, confirmCallBack));
}
} // End of namespace Stark
#endif // STARK_SERVICES_USER_INTERFACE_H