CRYOMNI3D: Add engine for Versailles 1685

This commit is contained in:
Le Philousophe 2019-03-05 19:28:12 +01:00 committed by Eugene Sandulenko
parent 97397bdaff
commit 531aa8392e
42 changed files with 14077 additions and 0 deletions

View File

@ -0,0 +1,4 @@
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
add_engine cryomni3d "Cryo Omni3D games" no "versailles" "" "highres"
add_engine versailles "Versailles 1685" no

View File

@ -0,0 +1,411 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/scummsys.h"
#include "common/error.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/file.h"
#include "audio/mixer.h"
#include "graphics/palette.h"
#include "cryomni3d/cryomni3d.h"
#include "cryomni3d/image/hlz.h"
#include "cryomni3d/video/hnm_decoder.h"
#include <stdarg.h> // For va_list etc.
namespace CryOmni3D {
CryOmni3DEngine::CryOmni3DEngine(OSystem *syst,
const CryOmni3DGameDescription *gamedesc) : Engine(syst), _gameDescription(gamedesc),
_fontManager(), _sprites(), _dragStatus(kDragStatus_NoDrag), _autoRepeatNextEvent(-1u) {
if (!_mixer->isReady()) {
error("Sound initialization failed");
}
// Setup mixer
syncSoundSettings();
unlockPalette();
DebugMan.addDebugChannel(kDebugFile, "File", "Track File Accesses");
DebugMan.addDebugChannel(kDebugVariable, "Variable", "Track Variable Accesses");
DebugMan.addDebugChannel(kDebugSaveLoad, "SaveLoad", "Track Save/Load Function");
}
CryOmni3DEngine::~CryOmni3DEngine() {
DebugMan.clearAllDebugChannels();
}
Common::Error CryOmni3DEngine::run() {
return Common::kNoError;
}
void CryOmni3DEngine::pauseEngineIntern(bool pause) {
Engine::pauseEngineIntern(pause);
/*
if (pause) {
_video->pauseVideos();
} else {
_video->resumeVideos();
_system->updateScreen();
}
*/
}
void CryOmni3DEngine::playHNM(const Common::String &filename, Audio::Mixer::SoundType soundType) {
Common::String fname(filename);
Video::VideoDecoder *videoDecoder = new Video::HNMDecoder();
videoDecoder->setSoundType(soundType);
int lastDotPos = fname.size() - 1;
for (; lastDotPos >= 0; --lastDotPos) {
if (fname[lastDotPos] == '.') {
break;
}
}
if (lastDotPos > -1) {
fname.erase(lastDotPos);
} else {
lastDotPos = fname.size();
}
fname += ".hnm";
if (!Common::File::exists(fname)) {
fname.erase(lastDotPos);
fname += ".hns";
}
if (!videoDecoder->loadFile(fname)) {
warning("Failed to open movie file %s/%s", filename.c_str(), fname.c_str());
delete videoDecoder;
return;
}
videoDecoder->start();
uint16 width = videoDecoder->getWidth();
uint16 height = videoDecoder->getHeight();
bool skipVideo = false;
while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (frame) {
if (videoDecoder->hasDirtyPalette()) {
const byte *palette = videoDecoder->getPalette();
setPalette(palette, 0, 256);
}
// TODO: beforeDraw
g_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, width, height);
// TODO: afterDraw
}
}
g_system->updateScreen();
if (pollEvents() && checkKeysPressed()) {
skipVideo = true;
}
}
delete videoDecoder;
}
Image::ImageDecoder *CryOmni3DEngine::loadHLZ(const Common::String &filename) {
Common::String fname(filename);
Image::ImageDecoder *imageDecoder = new Image::HLZFileDecoder();
Common::File file;
int lastDotPos = fname.size() - 1;
for (; lastDotPos >= 0; --lastDotPos) {
if (fname[lastDotPos] == '.') {
break;
}
}
if (lastDotPos > -1) {
fname.erase(lastDotPos);
}
fname += ".hlz";
if (!file.open(fname)) {
warning("Failed to open hlz file %s/%s", filename.c_str(), fname.c_str());
return nullptr;
}
if (!imageDecoder->loadStream(file)) {
warning("Failed to open hlz file %s", fname.c_str());
delete imageDecoder;
imageDecoder = 0;
return nullptr;
}
return imageDecoder;
}
void CryOmni3DEngine::displayHLZ(const Common::String &filename) {
Image::ImageDecoder *imageDecoder = loadHLZ(filename);
if (!imageDecoder) {
return;
}
if (imageDecoder->hasPalette()) {
const byte *palette = imageDecoder->getPalette();
setPalette(palette, imageDecoder->getPaletteStartIndex(), imageDecoder->getPaletteColorCount());
}
const Graphics::Surface *frame = imageDecoder->getSurface();
g_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
g_system->updateScreen();
bool exitImg = false;
while (!g_engine->shouldQuit() && !exitImg) {
if (pollEvents()) {
if (checkKeysPressed(1, Common::KEYCODE_ESCAPE) || getCurrentMouseButton() == 1) {
exitImg = true;
}
}
g_system->updateScreen();
}
delete imageDecoder;
}
void CryOmni3DEngine::setCursor(const Graphics::Cursor &cursor) const {
g_system->setMouseCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(),
cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor());
}
void CryOmni3DEngine::setCursor(unsigned int cursorId) const {
const Graphics::Cursor &cursor = _sprites.getCursor(cursorId);
g_system->setMouseCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(),
cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor());
}
bool CryOmni3DEngine::pollEvents() {
Common::Event event;
bool hasEvents = false;
unsigned int oldMouseButton = getCurrentMouseButton();
while (g_system->getEventManager()->pollEvent(event)) {
if (event.type == Common::EVENT_KEYDOWN) {
_keysPressed.push(event.kbd);
}
hasEvents = true;
}
g_system->delayMillis(10);
_dragStatus = kDragStatus_NoDrag;
unsigned int currentMouseButton = getCurrentMouseButton();
if (!oldMouseButton && currentMouseButton == 1) {
// Starting the drag
_dragStatus = kDragStatus_Pressed;
_dragStart = getMousePos();
} else if (oldMouseButton == 1) {
// We were already pressing
if (currentMouseButton == 1) {
// We are still pressing
Common::Point delta = _dragStart - getMousePos();
if (ABS(delta.x) > 2 || ABS(delta.y) > 2) {
// We moved from the start point
_dragStatus = kDragStatus_Dragging;
} else if (_autoRepeatNextEvent != -1u) {
// Check for auto repeat duration
if (_autoRepeatNextEvent < g_system->getMillis()) {
_dragStatus = kDragStatus_Pressed;
}
}
} else {
// We just finished dragging
_dragStatus = kDragStatus_Finished;
// Cancel auto repeat
_autoRepeatNextEvent = -1;
}
}
// Else we weren't dragging and still aren't
return hasEvents;
}
void CryOmni3DEngine::setAutoRepeatClick(unsigned int millis) {
_autoRepeatNextEvent = g_system->getMillis() + millis;
}
unsigned int CryOmni3DEngine::getCurrentMouseButton() {
int mask = g_system->getEventManager()->getButtonState();
if (mask & 0x1) {
return 1;
} else if (mask & 0x2) {
return 2;
} else {
return 0;
}
}
void CryOmni3DEngine::waitMouseRelease() {
while (g_system->getEventManager()->getButtonState() != 0 && !g_engine->shouldQuit()) {
pollEvents();
}
}
void CryOmni3DEngine::setMousePos(const Common::Point &point) {
g_system->warpMouse(point.x, point.y);
// Ensure to update mouse position in event manager
pollEvents();
}
Common::Point CryOmni3DEngine::getMousePos() {
return g_system->getEventManager()->getMousePos();
}
Common::KeyState CryOmni3DEngine::getNextKey() {
if (_keysPressed.empty()) {
return Common::KeyState();
} else {
return _keysPressed.pop();
}
}
bool CryOmni3DEngine::checkKeysPressed() {
Common::KeyCode kc = getNextKey().keycode;
if (kc != Common::KEYCODE_INVALID) {
clearKeys();
return true;
} else {
return false;
}
}
bool CryOmni3DEngine::checkKeysPressed(unsigned int numKeys, ...) {
bool found = false;
Common::KeyCode kc = getNextKey().keycode;
while (!found && kc != Common::KEYCODE_INVALID) {
va_list va;
va_start(va, numKeys);
for (unsigned int i = 0; i < numKeys; i++) {
// Compiler says that KeyCode is promoted to int, so we need this ugly cast
Common::KeyCode match = (Common::KeyCode) va_arg(va, int);
if (match == kc) {
found = true;
break;
}
}
va_end(va);
kc = getNextKey().keycode;
}
clearKeys();
return found;
}
void CryOmni3DEngine::copySubPalette(byte *dst, const byte *src, uint start, uint num) {
memcpy(&dst[3 * start], &src[3 * start], 3 * num * sizeof(*dst));
}
void CryOmni3DEngine::setPalette(const byte *colors, uint start, uint num) {
if (start < _lockPaletteStartRW) {
colors = colors + 3 * (_lockPaletteStartRW - start);
start = _lockPaletteStartRW;
}
uint end = start + num - 1;
if (end > _lockPaletteEndRW) {
num = num - (end - _lockPaletteEndRW);
end = _lockPaletteEndRW;
}
g_system->getPaletteManager()->setPalette(colors, start, num);
// Don't update screen there: palette will be updated with next updateScreen call
}
void CryOmni3DEngine::fadeOutPalette() {
byte palOut[256 * 3];
uint16 palWork[256 * 3];
uint16 delta[256 * 3];
g_system->getPaletteManager()->grabPalette(palOut, 0, 256);
for (unsigned int i = 0; i < 256 * 3; i++) {
palWork[i] = palOut[i] << 8;
delta[i] = palWork[i] / 25;
}
for (unsigned int step = 0; step < 25 && !g_engine->shouldQuit(); step++) {
for (unsigned int i = 0; i < 256 * 3; i++) {
palWork[i] -= delta[i];
palOut[i] = palWork[i] >> 8;
}
setPalette(palOut, 0, 256);
g_system->updateScreen();
g_system->delayMillis(50);
}
setBlackPalette();
}
void CryOmni3DEngine::fadeInPalette(const byte *palette) {
byte palOut[256 * 3];
uint16 palWork[256 * 3];
uint16 delta[256 * 3];
memset(palOut, 0, sizeof(palOut));
memset(palWork, 0, sizeof(palWork));
for (unsigned int i = 0; i < 256 * 3; i++) {
delta[i] = (palette[i] << 8) / 25;
}
setBlackPalette();
for (unsigned int step = 0; step < 25 && !g_engine->shouldQuit(); step++) {
for (unsigned int i = 0; i < 256 * 3; i++) {
palWork[i] += delta[i];
palOut[i] = palWork[i] >> 8;
}
setPalette(palOut, 0, 256);
g_system->updateScreen();
g_system->delayMillis(50);
}
setPalette(palette, 0, 256);
g_system->updateScreen();
}
void CryOmni3DEngine::setBlackPalette() {
byte pal[256 * 3];
memset(pal, 0, 256 * 3);
g_system->getPaletteManager()->setPalette(pal, 0, 256);
g_system->updateScreen();
}
void CryOmni3DEngine::fillSurface(byte color) {
g_system->fillScreen(color);
g_system->updateScreen();
}
} // End of namespace CryOmni3D

View File

@ -0,0 +1,164 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_CRYOMNI3D_H
#define CRYOMNI3D_CRYOMNI3D_H
#include "audio/mixer.h"
#include "common/array.h"
#include "common/keyboard.h"
#include "common/queue.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "engines/engine.h"
#include "font_manager.h"
#include "objects.h"
#include "sprites.h"
class OSystem;
namespace Common {
class Point;
class SeekableReadStream;
}
namespace Image {
class ImageDecoder;
}
/**
* This is the namespace of the Cryo Omni3D engine.
*
* Status of this engine: ???
*
* Games using this engine:
* - Versailles
* - ...
*/
namespace CryOmni3D {
enum CryOmni3DGameType {
GType_VERSAILLES
};
struct CryOmni3DGameDescription;
// Engine Debug Flags
enum {
kDebugFile = (1 << 0),
kDebugVariable = (1 << 1),
kDebugSaveLoad = (1 << 2)
};
enum DragStatus {
kDragStatus_NoDrag = 0,
kDragStatus_Pressed,
kDragStatus_Finished,
kDragStatus_Dragging
};
class CryOmni3DEngine : public ::Engine {
protected:
virtual Common::Error run();
public:
CryOmni3DEngine(OSystem *syst, const CryOmni3DGameDescription *gamedesc);
virtual ~CryOmni3DEngine();
// Detection related functions
const CryOmni3DGameDescription *_gameDescription;
const char *getGameId() const;
uint32 getFeatures() const;
const char *getAppName() const;
uint16 getVersion() const;
Common::Platform getPlatform() const;
uint8 getGameType() const;
Common::Language getLanguage() const;
bool hasFeature(EngineFeature f) const;
private:
void pauseEngineIntern(bool);
public:
Image::ImageDecoder *loadHLZ(const Common::String &filename);
void fillSurface(byte color);
void setCursor(const Graphics::Cursor &cursor) const;
void setCursor(unsigned int cursorId) const;
void playHNM(const Common::String &filename,
Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType);
void displayHLZ(const Common::String &filename);
bool pollEvents();
Common::Point getMousePos();
void setMousePos(const Common::Point &point);
unsigned int getCurrentMouseButton();
Common::KeyState getNextKey();
bool checkKeysPressed();
bool checkKeysPressed(unsigned int numKeys, ...);
void clearKeys() { _keysPressed.clear(); }
void waitMouseRelease();
void setAutoRepeatClick(unsigned int millis);
DragStatus getDragStatus() { return _dragStatus; }
virtual bool displayToolbar(const Graphics::Surface *original) = 0;
virtual bool hasPlaceDocumentation() = 0;
virtual bool displayPlaceDocumentation() = 0;
virtual unsigned int displayOptions() = 0;
virtual bool shouldAbort() { return g_engine->shouldQuit(); }
virtual void makeTranslucent(Graphics::Surface &dst, const Graphics::Surface &src) const = 0;
virtual void setupPalette(const byte *colors, uint start, uint num) = 0;
protected:
void copySubPalette(byte *dst, const byte *src, uint start, uint num);
void setPalette(const byte *colors, uint start, uint num);
void lockPalette(uint startRW, uint endRW) { _lockPaletteStartRW = startRW; _lockPaletteEndRW = endRW; }
void unlockPalette() { _lockPaletteStartRW = 0; _lockPaletteEndRW = 255; }
void fadeOutPalette();
void fadeInPalette(const byte *colors);
void setBlackPalette();
protected:
FontManager _fontManager;
Sprites _sprites;
Objects _objects;
Inventory _inventory;
Common::Queue<Common::KeyState> _keysPressed;
DragStatus _dragStatus;
Common::Point _dragStart;
unsigned int _autoRepeatNextEvent;
private:
unsigned int _lockPaletteStartRW;
unsigned int _lockPaletteEndRW;
};
} // End of namespace CryOmni3D
#endif

View File

@ -0,0 +1,255 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "cryomni3d/cryomni3d.h"
#ifdef ENABLE_VERSAILLES
#include "cryomni3d/versailles/engine.h"
#endif
namespace CryOmni3D {
struct CryOmni3DGameDescription {
ADGameDescription desc;
uint8 gameType;
uint32 features;
const char *appName;
};
const char *CryOmni3DEngine::getGameId() const {
return _gameDescription->desc.gameId;
}
uint32 CryOmni3DEngine::getFeatures() const {
return _gameDescription->features;
}
Common::Platform CryOmni3DEngine::getPlatform() const {
return _gameDescription->desc.platform;
}
const char *CryOmni3DEngine::getAppName() const {
return _gameDescription->appName;
}
uint8 CryOmni3DEngine::getGameType() const {
return _gameDescription->gameType;
}
Common::Language CryOmni3DEngine::getLanguage() const {
return _gameDescription->desc.language;
}
bool CryOmni3DEngine::hasFeature(EngineFeature f) const {
return false;
// (f == kSupportsRTL);
}
/*
#ifdef ENABLE_MYST
bool MohawkEngine_Myst::hasFeature(EngineFeature f) const {
return
MohawkEngine::hasFeature(f)
|| (f == kSupportsLoadingDuringRuntime)
|| (f == kSupportsSavingDuringRuntime);
}
#endif
*/
} // End of Namespace CryOmni3D
static const PlainGameDescriptor cryomni3DGames[] = {
{"versailles", "Versailles 1685"},
{0, 0}
};
#include "cryomni3d/detection_tables.h"
/*
static const char *directoryGlobs[] = {
"all",
"assets1",
"data",
"program",
"95instal",
"Rugrats Adventure Game",
0
};
*/
static const ADExtraGuiOptionsMap optionsList[] = {
/*{
GAMEOPTION_PLAY_MYST_FLYBY,
{
_s("Play the Myst fly by movie"),
_s("The Myst fly by movie was not played by the original engine."),
"playmystflyby",
false
}
},*/
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
class CryOmni3DMetaEngine : public AdvancedMetaEngine {
public:
CryOmni3DMetaEngine() : AdvancedMetaEngine(CryOmni3D::gameDescriptions,
sizeof(CryOmni3D::CryOmni3DGameDescription), cryomni3DGames, optionsList) {
//_singleId = "cryomni3d";
_maxScanDepth = 2;
//_directoryGlobs = directoryGlobs;
}
ADDetectedGame fallbackDetect(const FileMap &allFiles,
const Common::FSList &fslist) const override {
return detectGameFilebased(allFiles, fslist, CryOmni3D::fileBased);
}
virtual const char *getName() const {
return "Cryo Omni3D";
}
virtual const char *getOriginalCopyright() const {
return "Cryo game Engine (C) 1997-2002 Cryo Interactive";
}
virtual bool hasFeature(MetaEngineFeature f) const;
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
virtual SaveStateList listSaves(const char *target) const;
SaveStateList listSavesForPrefix(const char *prefix, const char *extension) const;
virtual int getMaximumSaveSlot() const { return 999; }
virtual void removeSaveState(const char *target, int slot) const;
virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
};
bool CryOmni3DMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves)
|| (f == kSupportsLoadingDuringStartup)
|| (f == kSupportsDeleteSave)
|| (f == kSavesSupportMetaInfo)
|| (f == kSavesSupportThumbnail)
|| (f == kSavesSupportCreationDate)
|| (f == kSavesSupportPlayTime);
}
SaveStateList CryOmni3DMetaEngine::listSavesForPrefix(const char *prefix,
const char *extension) const {
Common::String pattern = Common::String::format("%s-###.%s", prefix, extension);
Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern);
size_t prefixLen = strlen(prefix);
SaveStateList saveList;
for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end();
++filename) {
// Extract the slot number from the filename
char slot[4];
slot[0] = (*filename)[prefixLen + 1];
slot[1] = (*filename)[prefixLen + 2];
slot[2] = (*filename)[prefixLen + 3];
slot[3] = '\0';
int slotNum = atoi(slot);
saveList.push_back(SaveStateDescriptor(slotNum, ""));
}
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
SaveStateList CryOmni3DMetaEngine::listSaves(const char *target) const {
SaveStateList saveList;
/*
// Loading games is only supported in Myst/Riven currently.
saveList = listSavesForPrefix("myst", "mys");
for (SaveStateList::iterator save = saveList.begin(); save != saveList.end(); ++save) {
// Read the description from the save
int slot = save->getSaveSlot();
Common::String description = Mohawk::MystGameState::querySaveDescription(slot);
save->setDescription(description);
}
*/
return saveList;
}
void CryOmni3DMetaEngine::removeSaveState(const char *target, int slot) const {
/*
// Removing saved games is only supported in Myst/Riven currently.
if (strstr(target, "myst")) {
Mohawk::MystGameState::deleteSave(slot);
}
*/
}
SaveStateDescriptor CryOmni3DMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
/*
if (strstr(target, "myst")) {
return Mohawk::MystGameState::querySaveMetaInfos(slot);
}
*/
return SaveStateDescriptor();
}
bool CryOmni3DMetaEngine::createInstance(OSystem *syst, Engine **engine,
const ADGameDescription *desc) const {
const CryOmni3D::CryOmni3DGameDescription *gd = (const CryOmni3D::CryOmni3DGameDescription *)desc;
if (gd) {
switch (gd->gameType) {
case CryOmni3D::GType_VERSAILLES:
#ifdef ENABLE_VERSAILLES
*engine = new CryOmni3D::Versailles::CryOmni3DEngine_Versailles(syst, gd);
break;
#else
warning("Versailles support not compiled in");
return false;
#endif
default:
error("Unknown Cryo Omni3D Engine");
}
}
return (gd != 0);
}
#if PLUGIN_ENABLED_DYNAMIC(CRYOMNI3D)
REGISTER_PLUGIN_DYNAMIC(CRYOMNI3D, PLUGIN_TYPE_ENGINE, CryOmni3DMetaEngine);
#else
REGISTER_PLUGIN_STATIC(CRYOMNI3D, PLUGIN_TYPE_ENGINE, CryOmni3DMetaEngine);
#endif

View File

@ -0,0 +1,114 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
namespace CryOmni3D {
//#define GAMEOPTION_PLAY_MYST_FLYBY GUIO_GAMEOPTIONS1
//#define GUI_OPTIONS_MYST GUIO3(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI)
static const CryOmni3DGameDescription gameDescriptions[] = {
// Versailles 1685
// French Windows 95
// From lePhilousophe
{
{
"versailles",
"",
AD_ENTRY1s("VERSAILL.EXE", "3775004b96f056716ce615b458b1f394", 372736),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_UNSTABLE,
GUIO1(GUIO_NOASPECT)
},
GType_VERSAILLES,
0,
0,
},
// Versailles 1685
// French Windows 95 compressed
// From lePhilousophe
{
{
"versailles",
"",
AD_ENTRY1s("PROGRAM.Z", "a07b5d86af5f3a8883ba97db2bade87d", 293223),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_UNSTABLE,
GUIO1(GUIO_NOASPECT)
},
GType_VERSAILLES,
0,
0,
},
// Versailles 1685
// French DOS
// From lePhilousophe
{
{
"versailles",
"",
AD_ENTRY1s("VERSAILL.PGM", "1c992f034f43418a5da2e8ebd0b92620", 630431),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO1(GUIO_NOASPECT)
},
GType_VERSAILLES,
0,
0,
},
{ AD_TABLE_END_MARKER, 0, 0, 0 }
};
//////////////////////////////
//Fallback detection
//////////////////////////////
static const CryOmni3DGameDescription fallbackDescs[] = {
{
{
"versailles",
"unknown",
AD_ENTRY1(0, 0),
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_UNSTABLE,
GUIO1(GUIO_NOASPECT)
},
GType_VERSAILLES,
0,
0
},
};
static const ADFileBasedFallback fileBased[] = {
{ &fallbackDescs[0].desc, { "VERSAILL.EXE", 0 } },
{ &fallbackDescs[0].desc, { "VERSAILL.PGM", 0 } },
{ 0, { 0 } }
};
} // End of Namespace CryOmni3D

View File

@ -0,0 +1,555 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "cryomni3d/dialogs_manager.h"
#include "common/debug.h"
#include "common/file.h"
namespace CryOmni3D {
DialogsManager::~DialogsManager() {
delete[] _gtoBuffer;
}
void DialogsManager::loadGTO(const Common::String &gtoFileName) {
Common::File gtoFile;
if (!gtoFile.open(gtoFileName)) {
error("Can't open GTO file '%s'", gtoFileName.c_str());
}
_labels.clear();
_gtoEnd = nullptr;
delete[] _gtoBuffer;
_gtoBuffer = nullptr;
unsigned int gtoSize = gtoFile.size();
_gtoBuffer = new char[gtoSize];
gtoFile.read(_gtoBuffer, gtoSize);
gtoFile.close();
_gtoEnd = _gtoBuffer + gtoSize;
populateLabels();
}
void DialogsManager::populateLabels() {
/* Get labels count and populate the labels array */
unsigned int numLabels;
const char *labelsP = strstr(_gtoBuffer, "LABELS=");
if (labelsP) {
labelsP += sizeof("LABELS=") - 1;
for (; *labelsP == ' '; labelsP++) { }
numLabels = atoi(labelsP);
} else {
numLabels = 0;
}
for (const char *labelP = _gtoBuffer; labelP != nullptr; labelP = nextLine(labelP)) {
if (*labelP == ':') {
/* Line starting with ':', it's a label */
_labels.push_back(nextChar(labelP));
}
}
if (_labels.size() != numLabels) {
error("Bad labels number in GTO");
}
}
const char *DialogsManager::findLabel(const char *label, const char **realLabel) const {
unsigned int labelLen = 0;
/* Truncate input label */
for (const char *labelP = label;
*labelP != '\0' &&
*labelP != ' ' &&
*labelP != '.' &&
*labelP != '\r'; labelP++, labelLen++) { }
Common::Array<const char *>::const_iterator labelsIt;
for (labelsIt = _labels.begin(); labelsIt != _labels.end(); labelsIt++) {
if (!strncmp(*labelsIt, label, labelLen)) {
break;
}
}
if (labelsIt == _labels.end()) {
error("Label not found");
}
if (realLabel) {
*realLabel = *labelsIt;
}
return nextLine(*labelsIt);
}
Common::String DialogsManager::getLabelSound(const char *label) const {
/* Remove starting : if any */
if (*label == ':') {
label++;
}
const char *labelEnd;
for (labelEnd = label; *labelEnd >= '0' && *labelEnd <= 'Z'; labelEnd++) { }
return Common::String(label, labelEnd);
}
const char *DialogsManager::findSequence(const char *sequence) const {
unsigned int sequenceLen = strlen(sequence);
const char *lineP;
for (lineP = _gtoBuffer; lineP != nullptr; lineP = nextLine(lineP)) {
if (!strncmp(lineP, sequence, sequenceLen)) {
/* Line starting with the sequence name */
break;
}
}
if (!lineP) {
return nullptr;
}
/* Find next label */
for (; lineP != nullptr && *lineP != ':'; lineP = nextLine(lineP)) { }
/* Return the label name without it's ':' */
return nextChar(lineP);
}
Common::String DialogsManager::findVideo(const char *data) const {
data = previousMatch(data, ".FLC");
if (data == nullptr) {
return nullptr;
}
// Video name is without the extension
const char *end = data;
for (; data >= _gtoBuffer && *data != '\r'; data--) { }
data++;
if (data < _gtoBuffer || *data == '.') {
return Common::String();
}
return Common::String(data, end);
}
Common::String DialogsManager::getText(const char *text) const {
/* Skip '<' */
text = nextChar(text);
if (text == nullptr) {
return Common::String();
}
const char *end;
for (end = text; end < _gtoEnd && *end != '>'; end++) { }
if (end == _gtoEnd) {
return Common::String();
}
return Common::String(text, end);
}
void DialogsManager::reinitVariables() {
for (Common::Array<DialogVariable>::iterator it = _dialogsVariables.begin();
it != _dialogsVariables.end(); it++) {
it->value = 'N';
}
}
const DialogsManager::DialogVariable &DialogsManager::find(const Common::String &name) const {
for (Common::Array<DialogVariable>::const_iterator it = _dialogsVariables.begin();
it != _dialogsVariables.end(); it++) {
if (it->name == name) {
return *it;
}
}
error("Can't find dialog variable %s", name.c_str());
}
DialogsManager::DialogVariable &DialogsManager::find(const Common::String &name) {
for (Common::Array<DialogVariable>::iterator it = _dialogsVariables.begin();
it != _dialogsVariables.end(); it++) {
if (it->name == name) {
return *it;
}
}
error("Can't find dialog variable %s", name.c_str());
}
const char *DialogsManager::nextLine(const char *currentPtr) const {
for (; currentPtr < _gtoEnd && *currentPtr != '\r'; currentPtr++) { }
/* Go after the \r */
return nextChar(currentPtr);
}
const char *DialogsManager::nextChar(const char *currentPtr) const {
if (currentPtr == nullptr || currentPtr < _gtoBuffer || currentPtr >= _gtoEnd) {
return nullptr;
}
currentPtr++;
if (currentPtr >= _gtoEnd) {
return nullptr;
} else {
return currentPtr;
}
}
const char *DialogsManager::previousMatch(const char *currentPtr, const char *str) const {
if (currentPtr == nullptr || currentPtr >= _gtoEnd || currentPtr < _gtoBuffer) {
return nullptr;
}
unsigned int matchLen = strlen(str);
for (; currentPtr >= _gtoBuffer; currentPtr--) {
if (*currentPtr == str[0]) {
if (!strncmp(currentPtr, str, matchLen)) {
break;
}
}
}
if (currentPtr < _gtoBuffer) {
return nullptr;
} else {
return currentPtr;
}
}
bool DialogsManager::play(const Common::String &sequence, bool &slowStop) {
const char *label = findSequence(sequence.c_str());
if (!label) {
error("Can't find sequence '%s' in GTO", sequence.c_str());
}
Common::String video = sequence;
const char *text = findLabel(label);
slowStop = false;
bool playerLabel = !strncmp(label, "JOU", 3);
bool didSomething = false;
bool finished = false;
while (!finished) {
const char *actions;
if (playerLabel) {
/* If sequence begins with a player label go to action directly */
playerLabel = false;
actions = text;
// Maybe a bug in original game, we should go to next line
} else if (!strncmp(text, "<#>", 3)) {
/* Text is empty: go to action directly */
actions = nextLine(text);
} else {
/* Real text, play video */
video = findVideo(text);
Common::String properText = getText(text);
Common::String sound = getLabelSound(label);
Common::HashMap<Common::String, SubtitlesSettings>::const_iterator settingsIt =
_subtitlesSettings.find(video);
if (settingsIt == _subtitlesSettings.end()) {
settingsIt = _subtitlesSettings.find("default");
}
if (settingsIt == _subtitlesSettings.end()) {
error("No video settings for %s", video.c_str());
}
playDialog(video, sound, properText, settingsIt->_value);
didSomething = true;
actions = nextLine(text);
}
Common::Array<DialogsManager::Goto> gotoList = executeAfterPlayAndBuildGotoList(actions);
Common::StringArray questions;
bool endOfConversationFound = false;;
if (_ignoreNoEndOfConversation) {
// Don't check if there is an end, so, there is one
endOfConversationFound = true;
}
for (Common::Array<DialogsManager::Goto>::iterator it = gotoList.begin(); it != gotoList.end();
it++) {
if (!endOfConversationFound && it->label.hasPrefix("JOU")) {
// No need to get the real label here, we just need to know if the question ends up
if (!executePlayerQuestion(it->text, true)) {
endOfConversationFound = true;
}
}
assert(it->text);
const char *questionStart = it->text + 1;
const char *questionEnd = questionStart;
for (; *questionEnd != '>'; questionEnd++) { }
questions.push_back(Common::String(questionStart, questionEnd));
}
unsigned int eocInserted = -1;
if (!endOfConversationFound && questions.size() > 0) {
eocInserted = questions.size();
questions.push_back(_endOfConversationText);
}
if (questions.size() == 0) {
// There are no choices, just quit with a pause to avoid abrupt ending
slowStop = true;
break;
}
if (gotoList[0].label.hasPrefix("JOU")) {
// We must give a subject
unsigned int playerChoice = askPlayerQuestions(video, questions);
didSomething = true;
// -1 when shouldQuit
if (playerChoice == -1u || playerChoice == eocInserted) {
break;
}
text = executePlayerQuestion(gotoList[playerChoice].text, false, &label);
if (!text) {
break;
}
} else if (gotoList[0].label.hasPrefix("MES")) {
// Display a simple message
const char *messageStart = gotoList[0].text + 1;
const char *messageEnd = messageStart;
for (; *messageEnd != '>'; messageEnd++) { }
displayMessage(Common::String(messageStart, messageEnd));
break;
} else {
// Unattended conversation: two NPC speak
label = gotoList[0].label.c_str();
text = gotoList[0].text;
}
}
return didSomething;
}
Common::Array<DialogsManager::Goto> DialogsManager::executeAfterPlayAndBuildGotoList(
const char *actions) {
Common::Array<DialogsManager::Goto> gotos;
for (; actions && *actions != ':'; actions = nextLine(actions)) {
if (!strncmp(actions, "GOTO ", 5)) {
buildGotoGoto(actions, gotos);
break;
} else if (!strncmp(actions, "IF ", 3)) {
if (buildGotoIf(actions, gotos)) {
break;
}
} else if (!strncmp(actions, "LET ", 4)) {
executeLet(actions);
} else if (!strncmp(actions, "SHOW ", 5)) {
executeShow(actions);
}
}
return gotos;
}
void DialogsManager::buildGotoGoto(const char *gotoLine, Common::Array<Goto> &gotos) {
Common::String label;
gotoLine = gotoLine + 5;
while (true) {
const char *labelEnd = gotoLine;
for (labelEnd = gotoLine; *labelEnd >= '0' && *labelEnd <= 'Z'; labelEnd++) { }
label = Common::String(gotoLine, labelEnd);
if (label == "REM") {
break;
}
// To build goto list, no need to get back the real label position
const char *text = findLabel(label.c_str());
gotos.push_back(Goto(label, text));
if (*labelEnd == '.') {
if (!strncmp(labelEnd, ".WAV", 4)) {
labelEnd += 4;
} else {
debug("Problem with GOTO.WAV: '%s'", gotoLine);
}
}
for (; *labelEnd == ' ' || *labelEnd == ','; labelEnd++) { }
if (*labelEnd == '\r') {
break;
}
// Next goto tag
gotoLine = labelEnd;
}
}
bool DialogsManager::buildGotoIf(const char *ifLine, Common::Array<Goto> &gotos) {
ifLine += 3;
bool finishedConditions = false;
while (!finishedConditions) {
const char *endVar = ifLine;
const char *equalPos;
// Find next '='
for (; *endVar != '='; endVar++) { }
equalPos = endVar;
// Strip spaces at the end
endVar--;
for (; *endVar == ' '; endVar--) { }
endVar++;
Common::String variable(ifLine, endVar);
const char *testValue = equalPos + 1;
for (; *testValue == ' ' || *testValue == '\t'; testValue++) { }
byte value = (*this)[variable];
if (value != *testValue) {
// IF is not taken, go to next line
return false;
}
ifLine = testValue + 1;
for (; *ifLine == ' ' || *ifLine == '\t'; ifLine++) { }
if (!strncmp(ifLine, "AND IF ", 7)) {
ifLine += 7;
} else {
finishedConditions = true;
}
}
/* We are in the (implicit) THEN part of the IF
* ifLine points to the instruction */
if (!strncmp(ifLine, "GOTO", 4)) {
buildGotoGoto(ifLine, gotos);
} else if (!strncmp(ifLine, "LET", 3)) {
executeLet(ifLine);
} else if (!strncmp(ifLine, "SHOW", 4)) {
executeShow(ifLine);
} else {
debug("Invalid IF line: %s", ifLine);
return false;
}
return true;
}
void DialogsManager::executeLet(const char *letLine) {
letLine = letLine + 4;
const char *endVar = letLine;
const char *equalPos;
// Find next '='
for (; *endVar != '='; endVar++) { }
equalPos = endVar;
// Strip spaces at the end
endVar--;
for (; *endVar == ' '; endVar--) { }
endVar++;
Common::String variable(letLine, endVar);
(*this)[variable] = equalPos[1];
}
void DialogsManager::executeShow(const char *showLine) {
showLine = showLine + 5;
const char *endShow = showLine;
// Find next ')' and include it
for (; *endShow != ')'; endShow++) { }
endShow++;
Common::String show(showLine, endShow);
executeShow(show);
}
const char *DialogsManager::executePlayerQuestion(const char *text, bool dryRun,
const char **realLabel) {
// Go after the text
const char *actions = nextLine(text);
while (actions && *actions != ':') {
if (!strncmp(actions, "IF ", 3)) {
actions = parseIf(actions);
} else if (!strncmp(actions, "LET ", 4)) {
if (!dryRun) {
executeLet(actions);
}
actions = nextLine(actions);
} else if (!strncmp(actions, "GOTO ", 5)) {
return findLabel(actions + 5, realLabel);
} else {
actions = nextLine(actions);
}
}
// There were no GOTO, so it's the end of the conversation
return nullptr;
}
const char *DialogsManager::parseIf(const char *ifLine) {
ifLine += 3;
bool finishedConditions = false;
while (!finishedConditions) {
const char *endVar = ifLine;
const char *equalPos;
// Find next '='
for (; *endVar != '='; endVar++) { }
equalPos = endVar;
// Strip spaces at the end
endVar--;
for (; *endVar == ' '; endVar--) { }
endVar++;
Common::String variable(ifLine, endVar);
const char *testValue = equalPos + 1;
for (; *testValue == ' ' || *testValue == '\t'; testValue++) { }
byte value = (*this)[variable];
if (value != *testValue) {
// IF is not taken, go to next line
return nextLine(ifLine);
}
ifLine = testValue + 1;
for (; *ifLine == ' ' || *ifLine == '\t'; ifLine++) { }
if (!strncmp(ifLine, "AND IF ", 7)) {
ifLine += 7;
} else {
finishedConditions = true;
}
}
/* We are in the (implicit) THEN part of the IF
* ifLine points to the instruction */
return ifLine;
}
void DialogsManager::registerSubtitlesSettings(const Common::String &videoName,
const SubtitlesSettings &settings) {
_subtitlesSettings[videoName] = settings;
}
} // End of namespace CryOmni3D

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 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_DIALOGS_MANAGER_H
#define CRYOMNI3D_DIALOGS_MANAGER_H
#include "common/array.h"
#include "common/hash-str.h"
#include "common/hashmap.h"
#include "common/rect.h"
#include "common/str.h"
#include "common/str-array.h"
namespace CryOmni3D {
class DialogsManager {
public:
struct SubtitlesSettings {
SubtitlesSettings() { }
SubtitlesSettings(int16 textLeft, int16 textTop, int16 textRight, int16 textBottom,
int16 drawLeft, int16 drawTop, int16 drawRight, int16 drawBottom) :
textRect(textLeft, textTop, textRight, textBottom), drawRect(drawLeft, drawTop, drawRight,
drawBottom) { }
Common::Rect textRect;
Common::Rect drawRect;
};
DialogsManager() : _gtoBuffer(nullptr), _gtoEnd(nullptr),
_ignoreNoEndOfConversation(false) { }
virtual ~DialogsManager();
void init(unsigned int size, const Common::String &endOfConversationText) { _dialogsVariables.resize(size); _endOfConversationText = endOfConversationText; }
void loadGTO(const Common::String &gtoFile);
void setupVariable(unsigned int id, const Common::String &variable) { _dialogsVariables[id] = DialogVariable(variable, 'N'); }
void reinitVariables();
unsigned int size() const { return _dialogsVariables.size(); }
byte &operator[](unsigned int idx) { return _dialogsVariables[idx].value; }
const byte &operator[](unsigned int idx) const { return _dialogsVariables[idx].value; }
byte &operator[](const Common::String &name) { return find(name).value; }
const byte &operator[](const Common::String &name) const { return find(name).value; }
void registerSubtitlesSettings(const Common::String &videoName, const SubtitlesSettings &settings);
bool play(const Common::String &sequence, bool &slowStop);
protected:
virtual void executeShow(const Common::String &show) = 0;
virtual void playDialog(const Common::String &video, const Common::String &sound,
const Common::String &text, const SubtitlesSettings &settings) = 0;
virtual void displayMessage(const Common::String &text) = 0;
virtual unsigned int askPlayerQuestions(const Common::String &video,
const Common::StringArray &questions) = 0;
private:
struct Goto {
Goto() : label(), text(nullptr) {
}
Goto(const Common::String &label_, const char *text_) : label(label_), text(text_) {
}
Common::String label;
const char *text;
};
struct DialogVariable {
DialogVariable() : name(), value(0) {
}
DialogVariable(const Common::String &name_, byte value_) : name(name_), value(value_) {
}
Common::String name;
byte value;
};
const DialogVariable &find(const Common::String &name) const;
DialogVariable &find(const Common::String &name);
Common::Array<DialogVariable> _dialogsVariables;
void populateLabels();
const char *findLabel(const char *label, const char **realLabel = nullptr) const;
Common::String getLabelSound(const char *label) const;
const char *findSequence(const char *sequence) const;
Common::String findVideo(const char *data) const;
Common::String getText(const char *text) const;
Common::Array<Goto> executeAfterPlayAndBuildGotoList(const char *actions);
void buildGotoGoto(const char *gotoLine, Common::Array<Goto> &gotos);
bool buildGotoIf(const char *ifLine, Common::Array<Goto> &gotos);
void executeLet(const char *letLine);
void executeShow(const char *showLine);
const char *executePlayerQuestion(const char *text, bool dryRun, const char **realLabel = nullptr);
const char *parseIf(const char *ifLine);
const char *nextLine(const char *currentPtr) const;
const char *nextChar(const char *currentPtr) const;
const char *previousMatch(const char *currentPtr, const char *str) const;
char *_gtoBuffer;
const char *_gtoEnd;
Common::Array<const char *> _labels;
Common::String _endOfConversationText;
bool _ignoreNoEndOfConversation;
Common::HashMap<Common::String, SubtitlesSettings> _subtitlesSettings;
};
} // End of namespace CryOmni3D
#endif

View File

@ -0,0 +1,308 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "engines/cryomni3d/fixed_image.h"
#include "common/file.h"
#include "common/system.h"
#include "graphics/surface.h"
#include "image/image_decoder.h"
namespace CryOmni3D {
ZonFixedImage::ZonFixedImage(CryOmni3DEngine &engine,
Inventory &inventory,
const Sprites &sprites,
const FixedImageConfiguration *configuration) :
_engine(engine), _inventory(inventory), _sprites(sprites),
_configuration(configuration),
_callback(nullptr), _imageDecoder(nullptr), _imageSurface(nullptr) {
}
ZonFixedImage::~ZonFixedImage() {
delete _imageDecoder;
}
void ZonFixedImage::run(const Common::Functor1<ZonFixedImage *, void> *callback) {
_exit = false;
_zonesMode = kZonesMode_None;
_callback = callback;
g_system->showMouse(true);
while (!_exit) {
(*_callback)(this);
}
_engine.waitMouseRelease();
g_system->showMouse(false);
// Deselect object
_inventory.setSelectedObject(nullptr);
delete _callback;
_callback = nullptr;
}
void ZonFixedImage::load(const Common::String &image) {
_imageSurface = nullptr;
delete _imageDecoder;
_imageDecoder = nullptr;
_imageDecoder = _engine.loadHLZ(image);
if (!_imageDecoder) {
error("Can't display fixed image");
}
_imageSurface = _imageDecoder->getSurface();
loadZones(image);
#if 0
// This is not correct but to debug zones I think it's OK
Graphics::Surface *tmpSurf = (Graphics::Surface *) _imageSurface;
for (Common::Array<Zone>::const_iterator it = _zones.begin(); it != _zones.end(); it++) {
Common::Rect tmp = it->rect;
tmpSurf->frameRect(tmp, 244);
}
#endif
_zonesMode = kZonesMode_Standard;
_refreshCursor = true;
display();
// WORKAROUND: Wait for release after displaying the fixed image to avoid handling events due to mouse being pressed
// There is this bug in game
// Don't display cursor to prevent displaying an invalid cursor
g_system->showMouse(false);
g_system->updateScreen();
_engine.waitMouseRelease();
g_system->showMouse(true);
}
void ZonFixedImage::display() const {
_engine.setupPalette(_imageDecoder->getPalette(), _imageDecoder->getPaletteStartIndex(),
_imageDecoder->getPaletteColorCount());
g_system->copyRectToScreen(_imageSurface->getPixels(), _imageSurface->pitch, 0, 0,
_imageSurface->w, _imageSurface->h);
g_system->updateScreen();
}
void ZonFixedImage::loadZones(const Common::String &image) {
_zones.clear();
Common::String fname(image);
int lastDotPos = fname.size() - 1;
for (; lastDotPos >= 0; --lastDotPos) {
if (fname[lastDotPos] == '.') {
break;
}
}
if (lastDotPos > -1) {
fname.erase(lastDotPos);
fname += ".zon";
}
Common::File zonFile;
if (!zonFile.open(fname)) {
error("Can't open ZON file '%s'", fname.c_str());
}
int32 zonesNumber = zonFile.size() / 26;
_zones.reserve(zonesNumber);
_highLeftId = -1;
_highRightId = -1;
int leftSeen = 0x7fffffff; // MAX_INT
int rightSeen = 0;
Common::Array<Zone>::size_type index = 0;
while (zonesNumber > 0) {
Zone zone;
zone.rect.left = zonFile.readSint16BE();
zone.rect.top = zonFile.readSint16BE();
zone.rect.right = zonFile.readSint16BE();
zone.rect.bottom = zonFile.readSint16BE();
zone.spriteId = zonFile.readSint16BE();
zone.cursorId = _sprites.revMapSpriteId(zone.spriteId);
zone.valid = true;
zonFile.skip(16);
_zones.push_back(zone);
if (zone.cursorId == _configuration->spriteHigh) {
if (leftSeen > zone.rect.right) {
// The right side is at the leftest seen
leftSeen = zone.rect.right;
_highLeftId = index;
}
if (rightSeen < zone.rect.left) {
// The left side is at the rightest seen
rightSeen = zone.rect.left;
_highRightId = index;
}
}
zonesNumber--;
index++;
}
}
Common::Point ZonFixedImage::getZoneCenter(unsigned int zoneId) const {
if (zoneId >= _zones.size()) {
error("Invalid zoneId %u/%u", zoneId, _zones.size());
}
const Common::Rect &rect = _zones[zoneId].rect;
return Common::Point((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2);
}
void ZonFixedImage::manage() {
_currentZone = -1;
_zoneLow = false;
_zoneHigh = false;
_zoneHighLeft = false;
_zoneHighRight = false;
_zoneLeft = false;
_zoneRight = false;
_zoneQuestion = false;
_zoneListen = false;
_zoneSee = false;
_zoneUse = false;
_zoneSpeak = false;
_usedObject = nullptr;
// Force poll events even when we must refresh the cursor
if (!_engine.pollEvents() && !_refreshCursor) {
g_system->updateScreen();
// TODO: countdown even when no events
return;
}
_refreshCursor = false;
// Feed the key for the caller
_key = _engine.getNextKey();
Common::Point mousePos = _engine.getMousePos();
if (_key == Common::KEYCODE_ESCAPE) {
_exit = true;
return;
} else if (_engine.shouldAbort()) {
_exit = true;
return;
}
if (_key == Common::KEYCODE_SPACE ||
_engine.getCurrentMouseButton() == 2 ||
mousePos.y > _configuration->toolbarTriggerY) {
bool mustRedraw = _engine.displayToolbar(_imageSurface);
// We just came back from toolbar: check if an object is selected and go into object mode
if (_inventory.selectedObject()) {
_zonesMode = kZonesMode_Object;
}
if (mustRedraw) {
display();
}
// Return without any event to redo the loop and force refresh
_refreshCursor = true;
return;
}
Common::Array<Zone>::iterator zoneIt;
for (zoneIt = _zones.begin(); zoneIt != _zones.end(); zoneIt++) {
if (zoneIt->valid && zoneIt->rect.contains(mousePos)) {
break;
}
}
if (zoneIt != _zones.end()) {
_currentZone = zoneIt - _zones.begin();
} else {
_currentZone = -1;
}
if (_zonesMode == kZonesMode_Standard) {
if (zoneIt != _zones.end()) {
_engine.setCursor(zoneIt->cursorId);
if (_engine.getCurrentMouseButton() == 1) {
handleMouseZones(zoneIt);
}
} else {
_engine.setCursor(_configuration->spriteNothing);
}
} else if (_zonesMode == kZonesMode_Object) {
Object *selectedObj = _inventory.selectedObject();
if (!selectedObj) {
// Normally useless but we never know
_engine.setCursor(_configuration->spriteNothing);
} else if (zoneIt != _zones.end()) {
_engine.setCursor(selectedObj->idSA());
if (_engine.getDragStatus() == kDragStatus_Finished) {
// Just clicked, store the event and go back to standard mode
_usedObject = selectedObj;
_zonesMode = kZonesMode_Standard;
// We changed mode: need to refresh
_refreshCursor = true;
}
} else {
_engine.setCursor(selectedObj->idSl());
}
}
// TODO: handle countdown
g_system->updateScreen();
}
void ZonFixedImage::handleMouseZones(const Common::Array<Zone>::const_iterator &currentZone) {
if (currentZone->cursorId == _configuration->spriteLow) {
_zoneLow = true;
} else if (currentZone->cursorId == _configuration->spriteHigh) {
Common::Array<Zone>::size_type id = currentZone - _zones.begin();
if (id == _highLeftId) {
_zoneHighLeft = true;
} else if (id == _highRightId) {
_zoneHighRight = true;
} else {
_zoneHigh = true;
}
} else if (currentZone->cursorId == _configuration->spriteLeft) {
_zoneLeft = true;
} else if (currentZone->cursorId == _configuration->spriteRight) {
_zoneRight = true;
} else if (currentZone->cursorId == _configuration->spriteQuestion) {
_zoneQuestion = true;
} else if (currentZone->cursorId == _configuration->spriteListen) {
_zoneListen = true;
} else if (currentZone->cursorId == _configuration->spriteSee) {
_zoneSee = true;
} else if (currentZone->cursorId == _configuration->spriteUse) {
_zoneUse = true;
} else if (currentZone->cursorId == _configuration->spriteSpeak) {
_zoneSpeak = true;
} else {
error("Invalid cursor ID: %d in ImgFix", currentZone->cursorId);
}
}
} // End of namespace CryOmni3D

View File

@ -0,0 +1,128 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_FIXED_IMAGE_H
#define CRYOMNI3D_FIXED_IMAGE_H
#include "common/func.h"
#include "engines/cryomni3d/cryomni3d.h"
#include "engines/cryomni3d/objects.h"
namespace Graphics {
class Surface;
}
namespace CryOmni3D {
struct FixedImageConfiguration {
unsigned int spriteNothing;
unsigned int spriteLow;
unsigned int spriteHigh;
unsigned int spriteLeft;
unsigned int spriteRight;
unsigned int spriteQuestion;
unsigned int spriteListen;
unsigned int spriteSee;
unsigned int spriteUse;
unsigned int spriteSpeak;
int16 toolbarTriggerY;
};
class ZonFixedImage {
public:
typedef Common::Functor1<ZonFixedImage *, void> CallbackFunctor;
enum ZonesMode {
kZonesMode_None = 0,
kZonesMode_Standard,
kZonesMode_Object
};
/* These functions are used in main engine code */
ZonFixedImage(CryOmni3DEngine &engine, Inventory &inventory, const Sprites &sprites,
const FixedImageConfiguration *configuration);
~ZonFixedImage();
void run(const CallbackFunctor *callback);
/* THis function is used to refresh image after various events */
void display() const;
/* These functions and attributes are used in image handler */
void load(const Common::String &image);
void manage();
const Graphics::Surface *surface() const { return _imageSurface; }
void changeCallback(CallbackFunctor *callback) { delete _callback; _callback = callback; }
Common::Point getZoneCenter(unsigned int zoneId) const;
ZonesMode _zonesMode;
/* These attributes are read by the image handler to check what action player did */
unsigned int _currentZone;
bool _exit;
bool _zoneLow;
bool _zoneHigh;
bool _zoneHighLeft;
bool _zoneHighRight;
bool _zoneLeft;
bool _zoneRight;
bool _zoneQuestion;
bool _zoneListen;
bool _zoneSee;
bool _zoneUse;
bool _zoneSpeak;
Object *_usedObject;
Common::KeyState _key;
protected:
const Common::Functor1<ZonFixedImage *, void> *_callback;
CryOmni3DEngine &_engine;
Inventory &_inventory;
const Sprites &_sprites;
struct Zone {
Common::Rect rect;
/* ZON file stores the index in the sprite */
uint16 spriteId;
uint16 cursorId;
bool valid;
};
void loadZones(const Common::String &image);
void handleMouseZones(const Common::Array<Zone>::const_iterator &currentZone);
Image::ImageDecoder *_imageDecoder;
const Graphics::Surface *_imageSurface;
Common::Array<Zone> _zones;
Common::Array<Zone>::size_type _highLeftId;
Common::Array<Zone>::size_type _highRightId;
const FixedImageConfiguration *_configuration;
bool _refreshCursor;
};
} // End of namespace CryOmni3D
#endif

View File

@ -0,0 +1,340 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/debug.h"
#include "common/file.h"
#include "graphics/managed_surface.h"
#include "engines/cryomni3d/font_manager.h"
namespace CryOmni3D {
FontManager::FontManager() : _currentFont(nullptr), _transparentBackground(false),
_spaceWidth(0), _charSpacing(0), _lineHeight(30), _foreColor(0), _blockTextRemaining(nullptr) {
}
FontManager::~FontManager() {
for (Common::Array<Font *>::iterator it = _fonts.begin(); it != _fonts.end(); it++) {
delete *it;
}
}
void FontManager::loadFonts(const Common::Array<Common::String> &fontFiles) {
_fonts.reserve(_fonts.size() + fontFiles.size());
for (Common::Array<Common::String>::const_iterator it = fontFiles.begin(); it != fontFiles.end();
it++) {
Common::File font_fl;
//debug("Open font file %s", it->c_str());
if (!font_fl.open(*it)) {
error("Can't open file %s", it->c_str());
}
loadFont(font_fl);
}
}
void FontManager::loadFont(Common::ReadStream &font_fl) {
byte magic[8];
font_fl.read(magic, sizeof(magic));
if (memcmp(magic, "CRYOFONT", 8)) {
error("Invalid font magic");
}
// 3 unknown uint16
font_fl.readUint16BE();
font_fl.readUint16BE();
font_fl.readUint16BE();
Font *font = new Font();
font->maxHeight = font_fl.readSint16BE();
//debug("Max char height %d", font.maxHeight);
font_fl.read(font->comment, sizeof(font->comment));
//debug("Comment %s", font.comment);
for (unsigned int i = 0; i < Font::kCharactersCount; i++) {
uint16 h = font_fl.readUint16BE();
uint16 w = font_fl.readUint16BE();
unsigned int sz = font->chars[i].setup(w, h);
//debug("Char %d sz %dx%d %d", i, w, h, sz);
font->chars[i].offX = font_fl.readSint16BE();
font->chars[i].offY = font_fl.readSint16BE();
font->chars[i].printedWidth = font_fl.readUint16BE();
//debug("Char %d offX %d offY %d PW %d", i, font.chars[i].offX, font.chars[i].offY, font.chars[i].printedWidth);
font_fl.read(font->chars[i].data, sz);
//debug("Char %d read %d", i, v);
}
_fonts.push_back(font);
}
void FontManager::setCurrentFont(int currentFont) {
if (currentFont == -1) {
currentFont = 0;
}
_currentFontId = currentFont;
_currentFont = _fonts[currentFont];
setSpaceWidth(0);
}
void FontManager::setSpaceWidth(unsigned int additionalSpace) {
if (_currentFont) {
_spaceWidth = additionalSpace + _currentFont->chars[0].printedWidth;
} else {
_spaceWidth = 0;
}
}
unsigned int FontManager::displayStr_(unsigned int x, unsigned int y,
const Common::String &text) const {
unsigned int offset = 0;
for (Common::String::const_iterator it = text.begin(); it != text.end(); it++) {
offset += displayChar(x + offset, y, *it);
}
return offset;
}
unsigned int FontManager::displayChar(unsigned int x, unsigned int y, unsigned char c) const {
if (!_currentFont) {
error("There is no current font");
}
if (!_currentSurface) {
error("There is no current surface");
}
if (c < ' ' || c >= 255) {
c = '?';
}
c -= 32;
const Character &char_ = _currentFont->chars[c];
int realX = x + char_.offX;
int realY = y + char_.offY + _currentFont->maxHeight - 2;
if (!_transparentBackground) {
_currentSurface->fillRect(Common::Rect(realX, realY, realX + char_.w, realY + char_.h), 0xff);
}
Graphics::Surface src;
src.init(char_.w, char_.h, char_.w, char_.data, Graphics::PixelFormat::createFormatCLUT8());
_currentSurface->transBlitFrom(src, Common::Point(realX, realY), 0, false, _foreColor);
// WORKAROUND: in Versailles game the space width is calculated differently in this function and in the getStrWidth one, let's try to be consistent
#define KEEP_SPACE_BUG
#ifndef KEEP_SPACE_BUG
if (c == 0) {
return _spaceWidth;
} else {
return _charSpacing + char_.printedWidth;
}
#else
return _charSpacing + char_.printedWidth;
#endif
}
unsigned int FontManager::getStrWidth(const Common::String &text) const {
unsigned int width = 0;
for (Common::String::const_iterator it = text.begin(); it != text.end(); it++) {
unsigned char c = *it;
if (c == ' ') {
width += _spaceWidth;
} else {
if (c < ' ' || c >= 255) {
c = '?';
}
c -= 32;
width += _charSpacing;
width += _currentFont->chars[c].printedWidth;
}
}
return width;
}
bool FontManager::displayBlockText(const Common::String &text,
Common::String::const_iterator begin) {
bool notEnoughSpace = false;
Common::String::const_iterator ptr = begin;
Common::Array<Common::String> words;
if (begin != text.end()) {
_blockTextRemaining = nullptr;
while (ptr != text.end() && !notEnoughSpace) {
unsigned int finalPos;
bool has_cr;
calculateWordWrap(text, &ptr, &finalPos, &has_cr, words);
unsigned int spacesWidth = (words.size() - 1) * _spaceWidth;
unsigned int remainingSpace = (_blockRect.right - finalPos);
unsigned int spaceConsumed = 0;
double spaceWidthPerWord;
if (words.size() == 1) {
spaceWidthPerWord = _spaceWidth;
} else {
spaceWidthPerWord = (double)spacesWidth / (double)words.size();
}
Common::Array<Common::String>::const_iterator word;
unsigned int word_i;
for (word = words.begin(), word_i = 0; word != words.end(); word++, word_i++) {
_blockPos.x += displayStr_(_blockPos.x, _blockPos.y, *word);
if (!_justifyText || has_cr) {
_blockPos.x += _spaceWidth;
} else {
double sp = (word_i + 1) * spaceWidthPerWord - spaceConsumed;
_blockPos.x += sp;
spaceConsumed += sp;
remainingSpace -= sp;
}
}
if (_blockPos.y + _lineHeight + getFontMaxHeight() >= _blockRect.bottom) {
notEnoughSpace = true;
_blockTextRemaining = ptr;
} else {
_blockPos.x = _blockRect.left;
_blockPos.y += _lineHeight;
}
}
}
return notEnoughSpace;
}
unsigned int FontManager::getLinesCount(const Common::String &text, unsigned int width) {
if (text.size() == 0) {
// One line even if it's empty
return 1;
}
if (text.size() > 1024) {
// Too long text, be lazy
return getStrWidth(text) / width + 3;
}
unsigned int lineCount = 0;
Common::String::const_iterator textP = text.begin();
unsigned int len = text.size();
while (len > 0) {
Common::String buffer;
unsigned int lineWidth = 0;
lineCount++;
while (lineWidth < width && len > 0 && *textP != '\r') {
buffer += *(textP++);
len--;
lineWidth = getStrWidth(buffer);
}
if (lineWidth >= width) {
// We overrun the line, get backwards
while (buffer.size()) {
if (buffer[buffer.size() - 1] == ' ') {
break;
}
buffer.deleteLastChar();
textP--;
len++;
}
if (!buffer.size()) {
// Word was too long: fail
return 0;
}
if (*textP == ' ') {
textP++;
}
// Continue with next line
continue;
}
if (len == 0) {
// Job is finished
break;
}
if (*textP == '\r') {
// Next line
len--;
textP++;
}
}
return lineCount;
}
void FontManager::calculateWordWrap(const Common::String &text,
Common::String::const_iterator *position, unsigned int *finalPos, bool *hasCr,
Common::Array<Common::String> &words) const {
*hasCr = false;
unsigned int offset = 0;
bool wordWrap = false;
unsigned int lineWidth = _blockRect.right - _blockRect.left;
Common::String::const_iterator ptr = *position;
words.clear();
if (ptr == text.end() || *ptr == '\r') {
ptr++;
*hasCr = true;
*position = ptr;
*finalPos = offset;
return;
}
while (!wordWrap) {
Common::String::const_iterator begin = ptr;
for (; ptr != text.end() && *ptr != '\r' && *ptr != ' '; ptr++) { }
Common::String word(begin, ptr);
unsigned int width = getStrWidth(word);
if (width + offset >= lineWidth) {
wordWrap = true;
// word is too long: just put pointer back at begining
ptr = begin;
} else {
words.push_back(word);
offset += width + _spaceWidth;
for (; ptr != text.end() && *ptr == ' '; ptr++) { }
for (; ptr != text.end() && *ptr == '\r'; ptr++) {
wordWrap = true;
*hasCr = true;
}
}
}
if (words.size() > 0) {
offset -= _spaceWidth;
}
*finalPos = offset;
*position = ptr;
}
FontManager::Character::Character() : h(0), w(0), offX(0), offY(0), printedWidth(0), data(0) {
}
FontManager::Character::~Character() {
delete[] data;
}
unsigned int FontManager::Character::setup(uint16 width, uint16 height) {
w = width;
h = height;
unsigned int sz = w * h;
data = new byte[sz];
return sz;
}
} // End of namespace CryOmni3D

View File

@ -0,0 +1,120 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_FONT_MANAGER_H
#define CRYOMNI3D_FONT_MANAGER_H
#include "common/array.h"
#include "common/str.h"
#include "common/rect.h"
namespace Common {
class ReadStream;
}
namespace Graphics {
class ManagedSurface;
}
namespace CryOmni3D {
class FontManager {
public:
FontManager();
virtual ~FontManager();
void loadFonts(const Common::Array<Common::String> &fontFiles);
void setCurrentFont(int currentFont);
unsigned int getCurrentFont() { return _currentFontId; }
void setTransparentBackground(bool transparent) { _transparentBackground = transparent; }
void setSpaceWidth(unsigned int additionalSpace);
void setForeColor(byte color) { _foreColor = color; }
void setLineHeight(int h) { _lineHeight = h; }
int lineHeight() { return _lineHeight; }
void setCharSpacing(unsigned int w) { _charSpacing = w; }
void setSurface(Graphics::ManagedSurface *surface) { _currentSurface = surface; }
int getFontMaxHeight() { return _currentFont->maxHeight; }
void displayInt(unsigned int x, unsigned int y, int value) const { displayStr_(x, y, Common::String::format("%d", value)); }
void displayStr(unsigned int x, unsigned int y, const Common::String &text) const { displayStr_(x, y, text); }
unsigned int getStrWidth(const Common::String &text) const;
unsigned int getLinesCount(const Common::String &text, unsigned int width);
void setupBlock(const Common::Rect &block, bool justifyText = false) { _blockRect = block; _blockPos.x = block.left; _blockPos.y = block.top; _justifyText = justifyText; }
bool displayBlockText(const Common::String &text) { return displayBlockText(text, text.begin()); }
bool displayBlockText(const Common::String &text, Common::String::const_iterator begin);
Common::String::const_iterator blockTextRemaining() { return _blockTextRemaining; }
Common::Point blockTextLastPos() { return _blockPos; }
private:
void loadFont(Common::ReadStream &font_fl);
unsigned int displayStr_(unsigned int x, unsigned int y, const Common::String &text) const;
unsigned int displayChar(unsigned int x, unsigned int y, unsigned char c) const;
void calculateWordWrap(const Common::String &text, Common::String::const_iterator *position,
unsigned int *finalPos, bool *has_br, Common::Array<Common::String> &words) const;
struct Character {
uint16 h;
uint16 w;
int16 offX;
int16 offY;
uint16 printedWidth;
byte *data;
Character();
~Character();
unsigned int setup(uint16 width, uint16 height);
};
struct Font {
static const int kCharactersCount = 223;
uint16 maxHeight;
byte comment[32];
Character chars[kCharactersCount];
};
Common::Array<Font *> _fonts;
const Font *_currentFont;
unsigned int _currentFontId;
bool _transparentBackground;
unsigned int _spaceWidth;
unsigned int _charSpacing;
byte _foreColor;
Graphics::ManagedSurface *_currentSurface;
Common::Rect _blockRect;
Common::Point _blockPos;
int _lineHeight;
bool _justifyText;
Common::String::const_iterator _blockTextRemaining;
};
} // End of namespace CryOmni3D
#endif

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 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "cryomni3d/image/codecs/hlz.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "graphics/surface.h"
namespace Image {
HLZDecoder::HLZDecoder(int width, int height) : Codec(),
_width(width), _height(height), _surface(nullptr) {
}
HLZDecoder::~HLZDecoder() {
if (_surface) {
_surface->free();
delete _surface;
}
}
const Graphics::Surface *HLZDecoder::decodeFrame(Common::SeekableReadStream &stream) {
if (!_surface) {
_surface = new Graphics::Surface();
}
_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
byte *dst = (byte *)_surface->getPixels();
decodeFrameInPlace(stream, -1, dst);
return _surface;
}
Graphics::PixelFormat HLZDecoder::getPixelFormat() const {
return Graphics::PixelFormat::createFormatCLUT8();
}
static inline bool getReg(Common::SeekableReadStream &stream, uint32 *size, uint32 *reg,
int *regBits) {
if (*regBits == 0) {
if (*size < 4) {
error("Can't feed register: not enough data");
}
*reg = stream.readUint32LE();
*size -= 4;
*regBits = 32;
}
bool ret = (*reg >> 31) != 0;
*reg <<= 1;
(*regBits)--;
return ret;
}
void HLZDecoder::decodeFrameInPlace(Common::SeekableReadStream &stream, uint32 size, byte *dst) {
bool eof = false;
bool checkSize = (size != (uint32) - 1);
byte *orig = dst;
uint32 reg;
int regBits = 0;
#define GETREG() getReg(stream, &size, &reg, &regBits)
while (!eof) {
if (GETREG()) {
if (size < 1) {
error("Can't read pixel byte");
}
byte c = stream.readByte();
*(dst++) = c;
size--;
} else {
int offset, repeat_count;
if (GETREG()) {
// Long repeat
if (size < 2) {
error("Can't read repeat count/offset");
}
uint16 tmp = stream.readUint16LE();
size -= 2;
repeat_count = tmp & 0x7;
offset = (tmp >> 3) - 0x2000;
if (repeat_count == 0) {
if (size < 1) {
error("Can't read long repeat count");
}
repeat_count = stream.readByte();
size--;
if (repeat_count == 0) {
eof = true;
continue;
}
}
} else {
// Short repeat
repeat_count = GETREG() << 1;
repeat_count |= GETREG();
if (size < 1) {
error("Can't read offset byte");
}
offset = stream.readByte() - 0x100;
size--;
}
repeat_count += 2;
if (dst + offset < orig) {
error("Invalid offset %d, dst is %d", offset, (int)(dst - orig));
}
for (; repeat_count > 0; repeat_count--) {
// offset is always < 0
*dst = *(dst + offset);
dst++;
}
}
}
if (checkSize && size != 0) {
stream.skip(size);
}
#undef GETREG
}
} // End of namespace Image

View File

@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_IMAGE_CODECS_HLZ_H
#define CRYOMNI3D_IMAGE_CODECS_HLZ_H
#include "image/codecs/codec.h"
namespace Image {
/**
* HLZ image decoder.
*
* Used by HLZ image format and HNM video format.
*/
class HLZDecoder : public Codec {
public:
HLZDecoder(int width, int height);
~HLZDecoder();
const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
Graphics::PixelFormat getPixelFormat() const;
static void decodeFrameInPlace(Common::SeekableReadStream &stream, uint32 size, byte *dst);
private:
Graphics::Surface *_surface;
int _width, _height;
int _bitsPerPixel;
};
} // End of namespace Image
#endif

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 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "cryomni3d/image/hlz.h"
#include "common/stream.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "image/codecs/codec.h"
#include "cryomni3d/image/codecs/hlz.h"
namespace Image {
HLZFileDecoder::HLZFileDecoder() {
_surface = 0;
_codec = 0;
}
HLZFileDecoder::~HLZFileDecoder() {
destroy();
}
void HLZFileDecoder::destroy() {
delete _codec;
_codec = 0;
_surface = 0;
}
bool HLZFileDecoder::loadStream(Common::SeekableReadStream &stream) {
destroy();
stream.read(_palette, sizeof(_palette));
uint16 width = stream.readUint16LE();
uint16 height = stream.readUint16LE();
if (width == 0 || height == 0) {
return false;
}
_codec = new HLZDecoder(width, height);
_surface = _codec->decodeFrame(stream);
return true;
}
} // End of namespace Image

View File

@ -0,0 +1,69 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/**
* @file
* Image decoder used in engines:
* - hugo
* - mohawk
* - wintermute
*/
#ifndef CRYOMNI3D_IMAGE_HLZ_H
#define CRYOMNI3D_IMAGE_HLZ_H
#include "common/scummsys.h"
#include "common/str.h"
#include "image/image_decoder.h"
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
struct Surface;
}
namespace Image {
class HLZDecoder;
class HLZFileDecoder : public ImageDecoder {
public:
HLZFileDecoder();
virtual ~HLZFileDecoder();
// ImageDecoder API
void destroy();
virtual bool loadStream(Common::SeekableReadStream &stream);
virtual const Graphics::Surface *getSurface() const { return _surface; }
const byte *getPalette() const { return _palette; }
uint16 getPaletteColorCount() const { return 256; }
private:
HLZDecoder *_codec;
const Graphics::Surface *_surface;
byte _palette[256 * 3];
};
} // End of namespace Image
#endif

View File

@ -0,0 +1,38 @@
MODULE := engines/cryomni3d
MODULE_OBJS = \
cryomni3d.o \
omni3d.o \
detection.o \
mouse_boxes.o \
dialogs_manager.o \
fixed_image.o \
font_manager.o \
objects.o \
sprites.o \
wam_parser.o \
video/hnm_decoder.o \
image/hlz.o \
image/codecs/hlz.o
ifdef ENABLE_VERSAILLES
MODULE_OBJS += \
versailles/data.o \
versailles/dialogs_manager.o \
versailles/dialogs.o \
versailles/documentation.o \
versailles/engine.o \
versailles/logic.o \
versailles/menus.o \
versailles/music.o \
versailles/saveload.o \
versailles/toolbar.o
endif
# This module can be built as a plugin
ifeq ($(ENABLE_CRYOMNI3D), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk

View File

@ -0,0 +1,95 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "cryomni3d/mouse_boxes.h"
#include "common/rect.h"
#include "cryomni3d/font_manager.h"
namespace CryOmni3D {
MouseBoxes::MouseBoxes(unsigned int size) {
_boxes.resize(size);
}
MouseBoxes::~MouseBoxes() {
}
void MouseBoxes::reset() {
unsigned int sz = _boxes.size();
_boxes.clear();
_boxes.resize(sz);
}
void MouseBoxes::setupBox(int box_id, int left, int top, int right, int bottom,
const Common::String *text) {
MouseBox &box = _boxes[box_id];
box.left = left;
box.top = top;
box.right = right;
box.bottom = bottom;
box.isChar = false;
box.string = text;
}
void MouseBoxes::setupBox(int box_id, int left, int top, int right, int bottom, const char *text) {
MouseBox &box = _boxes[box_id];
box.left = left;
box.top = top;
box.right = right;
box.bottom = bottom;
box.isChar = true;
box.charp = text;
}
Common::Rect MouseBoxes::getBoxRect(int box_id) const {
const MouseBox &box = _boxes[box_id];
return Common::Rect(box.left, box.top, box.right, box.bottom);
}
Common::Point MouseBoxes::getBoxOrigin(int box_id) const {
const MouseBox &box = _boxes[box_id];
return Common::Point(box.left, box.top);
}
bool MouseBoxes::hitTest(int box_id, const Common::Point &pt) {
const MouseBox &box = _boxes[box_id];
return (box.left != -1) &&
(pt.x > box.left && pt.x < box.right &&
pt.y > box.top && pt.y < box.bottom);
}
void MouseBoxes::display(int box_id, const FontManager &font_manager) {
const MouseBox &box = _boxes[box_id];
if (box.string) {
if (box.isChar) {
font_manager.displayStr(box.left, box.top, box.charp);
} else {
font_manager.displayStr(box.left, box.top, *box.string);
}
}
}
}

View File

@ -0,0 +1,73 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_MOUSE_BOXES_H
#define CRYOMNI3D_MOUSE_BOXES_H
#include "common/array.h"
#include "common/str.h"
namespace Common {
class Point;
class Rect;
}
namespace CryOmni3D {
class FontManager;
class MouseBoxes {
public:
MouseBoxes(unsigned int size);
virtual ~MouseBoxes();
void reset();
void setupBox(int box_id, int left, int top, int right, int bottom,
const Common::String *text = nullptr);
void setupBox(int box_id, int left, int top, int right, int bottom, const char *text);
Common::Rect getBoxRect(int box_id) const;
Common::Point getBoxOrigin(int box_id) const;
bool hitTest(int box_id, const Common::Point &pt);
void display(int box_id, const FontManager &font_manager);
private:
struct MouseBox {
MouseBox() : left(-1), top(-1), right(-1), bottom(-1), string(nullptr), isChar(false) {}
int left;
int top;
int right;
int bottom;
// Can be nullptr
bool isChar;
union {
const Common::String *string;
const char *charp;
};
};
Common::Array<MouseBox> _boxes;
};
} // End of namespace CryOmni3D
#endif

View File

@ -0,0 +1,107 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "engines/cryomni3d/objects.h"
namespace CryOmni3D {
Object *Objects::findObjectByNameID(unsigned int nameID) {
for (iterator it = begin(); it != end(); it++) {
if (it->valid() && it->idOBJ() == nameID) {
return it;
}
}
// TODO: check if 111 and 112 are called and should be filtered out or not
error("nameID not found %u", nameID);
}
Object *Objects::findObjectByIconID(unsigned int iconID) {
for (iterator it = begin(); it != end(); it++) {
if (it->valid() && it->idCA() == iconID) {
return it;
}
}
error("iconID not found %u", iconID);
}
void Inventory::clear() {
for (iterator it = begin(); it != end(); it++) {
*it = nullptr;
}
}
void Inventory::add(Object *obj) {
for (iterator it = begin(); it != end(); it++) {
if (*it == nullptr) {
*it = obj;
(*_changeCallback)(it - begin());
return;
}
}
error("No more room in inventory");
}
void Inventory::remove(unsigned int position) {
(*this)[position] = nullptr;
(*_changeCallback)(-1u);
}
void Inventory::removeByCursorId(unsigned int cursorId) {
for (iterator it = begin(); it != end(); it++) {
if ((*it) && (*it)->idCA() == cursorId) {
remove(it - begin());
return;
}
}
// Don't bail out
}
void Inventory::removeByNameId(unsigned int nameId) {
for (iterator it = begin(); it != end(); it++) {
if ((*it) && (*it)->idOBJ() == nameId) {
remove(it - begin());
return;
}
}
// Don't bail out
}
bool Inventory::inInventoryByCursorId(unsigned int cursorId) const {
for (const_iterator it = begin(); it != end(); it++) {
if ((*it) && (*it)->idCA() == cursorId) {
return true;
}
}
return false;
}
bool Inventory::inInventoryByNameId(unsigned int nameId) const {
for (const_iterator it = begin(); it != end(); it++) {
if ((*it) && (*it)->idOBJ() == nameId) {
return true;
}
}
return false;
}
} // End of namespace CryOmni3D

102
engines/cryomni3d/objects.h Normal file
View File

@ -0,0 +1,102 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_OBJECTS_H
#define CRYOMNI3D_OBJECTS_H
#include "common/array.h"
#include "common/func.h"
#include "common/str.h"
#include "cryomni3d/sprites.h"
namespace CryOmni3D {
class Object {
public:
typedef Common::Functor0<void> *ViewCallback;
Object() : _valid(false), _idCA(-1), _idCl(-1), _idSA(-1), _idSl(-1), _idOBJ(-1),
_viewCallback(nullptr) {}
Object(const Sprites &sprites, unsigned int idCA, unsigned int idOBJ) : _idCA(idCA),
_idCl(sprites.calculateSpriteId(idCA, 1)), _idSA(sprites.calculateSpriteId(idCA, 2)),
_idSl(sprites.calculateSpriteId(idCA, 3)),
_valid(true), _idOBJ(idOBJ), _viewCallback(nullptr) {}
~Object() { delete _viewCallback; }
unsigned int valid() const { return _valid; }
unsigned int idCA() const { return _idCA; }
unsigned int idCl() const { return _idCl; }
unsigned int idSA() const { return _idSA; }
unsigned int idSl() const { return _idSl; }
unsigned int idOBJ() const { return _idOBJ; }
ViewCallback viewCallback() const { return _viewCallback; }
// Takes ownership of the pointer
void setViewCallback(ViewCallback callback) { _viewCallback = callback; }
void rename(unsigned int newIdOBJ) { _idOBJ = newIdOBJ; }
private:
unsigned int _idOBJ;
unsigned int _idCA;
unsigned int _idCl;
unsigned int _idSA;
unsigned int _idSl;
bool _valid;
ViewCallback _viewCallback;
};
class Objects : public Common::Array<Object> {
public:
Object *findObjectByNameID(unsigned int nameID);
Object *findObjectByIconID(unsigned int iconID);
private:
};
class Inventory : public Common::Array<Object *> {
public:
Inventory() : _selectedObject(nullptr), _changeCallback(nullptr) { }
~Inventory() { delete _changeCallback; }
void init(unsigned int count, Common::Functor1<unsigned int, void> *changeCallback) { _changeCallback = changeCallback; resize(count); }
void clear();
void add(Object *);
void remove(unsigned int position);
void removeByCursorId(unsigned int cursorId);
void removeByNameId(unsigned int nameId);
bool inInventoryByCursorId(unsigned int cursorId) const;
bool inInventoryByNameId(unsigned int nameId) const;
Object *selectedObject() const { return _selectedObject; }
void setSelectedObject(Object *obj) { _selectedObject = obj; }
private:
Object *_selectedObject;
Common::Functor1<unsigned int, void> *_changeCallback;
};
} // End of namespace CryOmni3D
#endif

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 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "engines/cryomni3d/omni3d.h"
#include "common/rect.h"
namespace CryOmni3D {
void Omni3DManager::init(double hfov) {
_alpha = 0.;
_beta = 0.;
_xSpeed = 0.;
_ySpeed = 0.;
double oppositeSide = tan(hfov / 2.) / (4. / 3.);
double vf = atan2(oppositeSide, 1.);
_vfov = (M_PI_2 - vf - (13. / 180.*M_PI)) * 10. / 9.;
double warpVfov = 155. / 180. * M_PI;
double hypV = 768. / 2. / sin(warpVfov / 2.);
double oppHTot = tan(hfov / 2.) * 16. / 320.;
_helperValue = 2048 * 65536 / (2. * M_PI);
for (int i = 0; i < 31; i++) {
double oppH = (i - 15) * oppHTot;
double angle = atan2(oppH, 1.);
_anglesH[i] = angle;
_hypothenusesH[i] = sqrt(oppH * oppH + 1);
double oppVTot = hypV * _hypothenusesH[i];
for (int j = 0; j < 21; j++) {
double oppV = (j - 20) * oppHTot;
_oppositeV[j] = oppV;
double coord = sqrt(oppV * oppV + _hypothenusesH[i] * _hypothenusesH[i]);
coord = oppVTot / coord;
coord = coord * 65536;
_squaresCoords[i][j] = coord;
}
}
_surface.create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
clearConstraints();
}
Omni3DManager::~Omni3DManager() {
_surface.free();
}
void Omni3DManager::updateCoords(int xDelta, int yDelta, bool useOldSpeed) {
double xDelta1 = xDelta * 0.00025;
double yDelta1 = yDelta * 0.0002;
if (useOldSpeed) {
_xSpeed += xDelta1;
_ySpeed += yDelta1;
} else {
_xSpeed = xDelta1;
_ySpeed = yDelta1;
}
_alpha += _xSpeed;
_beta += _ySpeed;
//debug("alpha = %lf beta = %lf xSpeed = %lf ySpeed = %lf", _alpha, _beta, _xSpeed, _ySpeed);
_xSpeed *= 0.4;
_ySpeed *= 0.6;
if (useOldSpeed) {
if (abs(_xSpeed) < 0.001) {
_xSpeed = 0.;
}
if (abs(_ySpeed) < 0.001) {
_ySpeed = 0.;
}
}
if (_alpha < _alphaMin) {
_alpha = _alphaMin;
_xSpeed = 0.;
} else if (_alpha > _alphaMax) {
_alpha = _alphaMax;
_xSpeed = 0.;
}
if (_beta < _betaMin) {
_beta = _betaMin;
_ySpeed = 0.;
} else if (_beta > _betaMax) {
_beta = _betaMax;
_ySpeed = 0.;
}
if (_alpha >= 2. * M_PI) {
_alpha -= 2. * M_PI;
} else if (_alpha < 0.) {
_alpha += 2. * M_PI;
}
_dirtyCoords = true;
updateImageCoords();
}
void Omni3DManager::updateImageCoords() {
if (!_dirtyCoords) {
return;
}
if (_alpha >= 2.*M_PI) {
_alpha -= 2.*M_PI;
} else if (_alpha < 0) {
_alpha += 2.*M_PI;
}
if (_beta > 0.9 * _vfov) {
_beta = 0.9 * _vfov;
} else if (_beta < -0.9 * _vfov) {
_beta = -0.9 * _vfov;
}
double tmp = (2048 * 65536) - 2048 * 65536 / (2. * M_PI) * _alpha;
unsigned int k = 0;
for (unsigned int i = 0; i < 31; i++) {
double v11 = _anglesH[i] + _beta;
double v26 = sin(v11);
double v25 = cos(v11) * _hypothenusesH[i];
unsigned int offset = 80;
unsigned int j;
for (j = 0; j < 20; j++) {
double v16 = atan2(_oppositeV[j], v25);
double v17 = v16 * _helperValue;
double v18 = (384 * 65536) - _squaresCoords[i][j] * v26;
k += 2;
_imageCoords[k + 0] = (int)(tmp + v17);
_imageCoords[k + offset + 0] = (int)(tmp - v17);
_imageCoords[k + 1] = (int) v18;
_imageCoords[k + offset + 1] = (int) v18;
offset -= 4;
}
double v19 = atan2(_oppositeV[j], v25);
k += 2;
_imageCoords[k + 0] = (int)((2048.*65536.) - (_alpha - v19) * _helperValue);
_imageCoords[k + 1] = (int)((384.*65536.) - _squaresCoords[i][j] * v26);
k += 40;
}
_dirtyCoords = false;
_dirty = true;
}
const Graphics::Surface *Omni3DManager::getSurface() {
if (!_sourceSurface) {
return nullptr;
}
if (_dirtyCoords) {
updateImageCoords();
}
if (_dirty) {
unsigned int off = 2;
byte *dst = (byte *)_surface.getBasePtr(0, 0);
const byte *src = (const byte *)_sourceSurface->getBasePtr(0, 0);
for (unsigned int i = 0; i < 30; i++) {
for (unsigned int j = 0; j < 40; j++) {
int x1 = (_imageCoords[off + 2] - _imageCoords[off + 0]) >> 4;
int y1 = (_imageCoords[off + 3] - _imageCoords[off + 1]) >> 4;
int x1_ = (_imageCoords[off + 82 + 2] - _imageCoords[off + 82 + 0]) >> 4;
int y1_ = (_imageCoords[off + 82 + 3] - _imageCoords[off + 82 + 1]) >> 4;
int dx1 = (x1_ - x1) >> 10;
int dy1 = (y1_ - y1) >> 15;
y1 >>= 5;
int dx2 = (_imageCoords[off + 82 + 0] - _imageCoords[off + 0]) >> 4;
int dy2 = (_imageCoords[off + 82 + 1] - _imageCoords[off + 1]) >> 9;
int x2 = (((_imageCoords[off + 0] >> 0) * 2) + dx2) >> 1;
int y2 = (((_imageCoords[off + 1] >> 5) * 2) + dy2) >> 1;
for (unsigned int y = 0; y < 16; y++) {
unsigned int px = (x2 * 2 + x1) * 16;
unsigned int py = (y2 * 2 + y1) / 2;
unsigned int deltaX = x1 * 32;
unsigned int deltaY = y1;
for (unsigned int x = 0; x < 16; x++) {
unsigned int srcOff = (py & 0x1ff800) | (px >> 21);
dst[x] = src[srcOff];
px += deltaX;
py += deltaY;
}
dst += 640;
x1 += dx1;
y1 += dy1;
x2 += dx2;
y2 += dy2;
}
dst -= 16 * 640 - 16;
off += 2;
}
dst += 15 * 640;
off += 2;
}
_dirty = false;
}
return &_surface;
}
void Omni3DManager::clearConstraints() {
_alphaMin = -HUGE_VAL;
_alphaMax = HUGE_VAL;
_betaMin = -HUGE_VAL;
_betaMax = HUGE_VAL;
}
Common::Point Omni3DManager::mapMouseCoords(const Common::Point &mouse) {
Common::Point pt;
if (_dirtyCoords) {
updateImageCoords();
}
int smallX = mouse.x & 0xf, squareX = mouse.x >> 4;
int smallY = mouse.y & 0xf, squareY = mouse.y >> 4;
unsigned int off = 82 * squareY + 2 * squareX;
pt.x = ((_imageCoords[off + 2] +
smallY * ((_imageCoords[off + 84] - _imageCoords[off + 2]) >> 4) +
(smallX * smallY) * ((_imageCoords[off + 86] - _imageCoords[off + 84]) >> 8) +
(smallX * (16 - smallY)) * ((_imageCoords[off + 4] - _imageCoords[off + 2]) >> 8))
& 0x07ff0000) >> 16;
pt.y = (_imageCoords[off + 3] +
smallY * ((_imageCoords[off + 85] - _imageCoords[off + 3]) >> 4) +
(smallX * smallY) * ((_imageCoords[off + 87] - _imageCoords[off + 85]) >> 8) +
(smallX * (16 - smallY)) * ((_imageCoords[off + 5] - _imageCoords[off + 3]) >> 8)) >> 16;
return pt;
}
} // End of namespace CryOmni3D

View File

@ -0,0 +1,83 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_OMNI3D_H
#define CRYOMNI3D_OMNI3D_H
#include "graphics/surface.h"
namespace CryOmni3D {
class Omni3DManager {
public:
Omni3DManager() {}
virtual ~Omni3DManager();
void init(double hfov);
void setSourceSurface(const Graphics::Surface *surface) { _sourceSurface = surface; _dirty = true; }
void clearConstraints();
void setAlphaConstraints(double alphaMin, double alphaMax) { _alphaMin = alphaMin; _alphaMax = alphaMax; }
void setBetaMinConstraint(double betaMin) { _betaMin = betaMin; }
void setBetaMaxConstraint(double betaMax) { _betaMax = betaMax; }
void setAlpha(double alpha) { _alpha = alpha; _dirtyCoords = true; }
void setBeta(double beta) { _beta = beta; _dirtyCoords = true; }
void updateCoords(int xDelta, int yDelta, bool useOldSpeed);
double getAlpha() const { return _alpha; }
double getBeta() const { return _beta; }
Common::Point mapMouseCoords(const Common::Point &mouse);
bool hasSpeed() { return _xSpeed != 0. || _ySpeed != 0.; }
bool needsUpdate() { return _dirty || _dirtyCoords; }
const Graphics::Surface *getSurface();
private:
void updateImageCoords();
double _vfov;
double _alpha, _beta;
double _xSpeed, _ySpeed;
double _alphaMin, _alphaMax;
double _betaMin, _betaMax;
int _imageCoords[2544];
double _squaresCoords[31][21];
double _hypothenusesH[31];
double _anglesH[31];
double _oppositeV[21];
double _helperValue;
bool _dirty;
bool _dirtyCoords;
const Graphics::Surface *_sourceSurface;
Graphics::Surface _surface;
};
} // End of namespace CryOmni3D
#endif

View File

@ -0,0 +1,218 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/debug.h"
#include "common/file.h"
#include "graphics/surface.h"
#include "engines/cryomni3d/sprites.h"
// #define SPRTIES_DEBUG
namespace CryOmni3D {
#define MAP_ID(id) \
do { \
if (_map) { \
id = (*_map)[id]; \
} \
} while(false)
Sprites::Sprites() : _map(nullptr) {
_surface = new Graphics::Surface();
}
Sprites::~Sprites() {
for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++) {
if ((*it)->refCnt > 1) {
(*it)->refCnt--;
} else {
delete *it;
}
}
delete _map;
delete _surface;
}
void Sprites::loadSprites(Common::ReadStream &spr_fl) {
byte magic[4];
while (true) {
if (spr_fl.read(magic, sizeof(magic)) == 0) {
break;
}
if (memcmp(magic, "SPRI", sizeof(magic))) {
error("Invalid sprite magic");
}
// 2 unknown uint32
spr_fl.readUint32BE();
spr_fl.readUint32BE();
CryoCursor *cursor = new CryoCursor();
uint16 w = spr_fl.readUint16BE();
uint16 h = spr_fl.readUint16BE();
unsigned int sz = cursor->setup(w, h);
cursor->_offX = spr_fl.readUint32BE();
cursor->_offY = spr_fl.readUint32BE();
spr_fl.read(cursor->_data, sz);
_cursors.push_back(cursor);
}
}
void Sprites::setupMapTable(const unsigned int *table, unsigned int size) {
delete _map;
_map = nullptr;
// Reset the reverse mapping
for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++) {
(*it)->_constantId = -1;
}
if (table) {
_map = new Common::Array<unsigned int>(table, size);
// Sweep all the mapping and set its reverse values
unsigned int i = 0;
for (Common::Array<unsigned int>::const_iterator it = _map->begin(); it != _map->end(); it++, i++) {
_cursors[*it]->_constantId = i;
}
#ifdef SPRITES_DEBUG
// Normally we don't have any unreachable sprties from constants,
// as it could be time consuming, this should be fixed in the static map
// Count unswept values
unsigned int unswept = 0;
for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++) {
if ((*it)->_constantId == -1u) {
unswept++;
}
}
if (unswept) {
warning("We got %d unreachable sprites from map table. This should not happen."
" Fixing it for now", unswept);
// Enlarge the map to hold new indexes
_map->reserve(_map->size() + unswept);
// Set new indexes to unswept sprites
i = 0;
for (Common::Array<CryoCursor *>::iterator it = _cursors.begin(); it != _cursors.end(); it++, i++) {
if ((*it)->_constantId == -1u) {
warning("Fixing sprite the %d sprite", i);
(*it)->_constantId = _map->size();
_map->push_back(i);
}
}
}
#endif
}
}
void Sprites::setSpriteHotspot(unsigned int spriteId, unsigned int x, unsigned int y) {
MAP_ID(spriteId);
_cursors[spriteId]->_offX = x;
_cursors[spriteId]->_offY = y;
}
void Sprites::replaceSprite(unsigned int oldSpriteId, unsigned int newSpriteId) {
MAP_ID(oldSpriteId);
MAP_ID(newSpriteId);
if (_cursors[oldSpriteId]->refCnt > 1) {
_cursors[oldSpriteId]->refCnt--;
} else {
delete _cursors[oldSpriteId];
}
_cursors[oldSpriteId] = _cursors[newSpriteId];
_cursors[oldSpriteId]->refCnt++;
}
unsigned int Sprites::getSpritesCount() const {
if (_map) {
return _map->size();
} else {
return _cursors.size();
}
}
unsigned int Sprites::revMapSpriteId(unsigned int id) const {
if (_map) {
if (id >= _cursors.size()) {
error("revMapSpriteId is out of bounds: %d/%d", id, _cursors.size());
}
id = _cursors[id]->_constantId;
}
return id;
}
unsigned int Sprites::calculateSpriteId(unsigned int baseId, unsigned int offset) const {
if (_map) {
MAP_ID(baseId);
baseId += offset;
if (baseId >= _cursors.size()) {
error("Calculate sprite is out of bounds: %d/%d", baseId, _cursors.size());
}
unsigned int spriteId = _cursors[baseId]->_constantId;
if (spriteId == -1u) {
error("Sprite %d is unreachable", baseId);
}
return spriteId;
} else {
return baseId + offset;
}
}
const Graphics::Surface &Sprites::getSurface(unsigned int spriteId) const {
MAP_ID(spriteId);
CryoCursor *cursor = _cursors[spriteId];
_surface->init(cursor->_width, cursor->_height, cursor->_width, cursor->_data,
Graphics::PixelFormat::createFormatCLUT8());
return *_surface;
}
const Graphics::Cursor &Sprites::getCursor(unsigned int spriteId) const {
MAP_ID(spriteId);
return *_cursors[spriteId];
}
Sprites::CryoCursor::CryoCursor() : _width(0), _height(0), _offX(0), _offY(0), _data(nullptr),
refCnt(1) {
}
Sprites::CryoCursor::~CryoCursor() {
assert(refCnt == 1);
delete[] _data;
}
unsigned int Sprites::CryoCursor::setup(uint16 width, uint16 height) {
_width = width;
_height = height;
unsigned int sz = _width * _height;
_data = new byte[sz];
return sz;
}
} // End of namespace CryOmni3D

102
engines/cryomni3d/sprites.h Normal file
View File

@ -0,0 +1,102 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_SPRITES_H
#define CRYOMNI3D_SPRITES_H
#include "common/array.h"
#include "common/str.h"
#include "graphics/cursor.h"
namespace Common {
class ReadStream;
}
namespace Graphics {
class Surface;
}
namespace CryOmni3D {
class Sprites {
public:
Sprites();
virtual ~Sprites();
void loadSprites(Common::ReadStream &spr_fl);
void setupMapTable(const unsigned int *table, unsigned int size);
void setSpriteHotspot(unsigned int spriteId, unsigned int x, unsigned int y);
void replaceSprite(unsigned int oldSpriteId, unsigned int newSpriteId);
unsigned int getSpritesCount() const;
const Graphics::Surface &getSurface(unsigned int spriteId) const;
const Graphics::Cursor &getCursor(unsigned int spriteId) const;
unsigned int revMapSpriteId(unsigned int id) const;
unsigned int calculateSpriteId(unsigned int baseId, unsigned int offset) const;
byte getKeyColor(unsigned int spriteId) const { return 0; }
private:
class CryoCursor : public Graphics::Cursor {
public:
virtual uint16 getWidth() const override { return _width; }
virtual uint16 getHeight() const override { return _height; }
virtual uint16 getHotspotX() const override { return _offX; }
virtual uint16 getHotspotY() const override { return _offY; }
virtual byte getKeyColor() const override { return 0; }
virtual const byte *getSurface() const override { return _data; }
virtual const byte *getPalette() const override { return nullptr; }
virtual byte getPaletteStartIndex() const override { return 0; }
virtual uint16 getPaletteCount() const override { return 0; }
unsigned int setup(uint16 width, uint16 height);
uint16 _width;
uint16 _height;
int16 _offX;
int16 _offY;
unsigned int _constantId;
byte *_data;
unsigned int refCnt;
CryoCursor();
virtual ~CryoCursor();
};
// Pointer to avoid to mutate Sprites when asking for a cursor
Graphics::Surface *_surface;
Common::Array<CryoCursor *> _cursors;
Common::Array<unsigned int> *_map;
};
} // End of namespace CryOmni3D
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,318 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "cryomni3d/versailles/engine.h"
namespace CryOmni3D {
namespace Versailles {
bool CryOmni3DEngine_Versailles::preprocessDialog(const Common::String &sequence) {
if (_inventory.inInventoryByNameId(96) && _inventory.inInventoryByNameId(98)) {
_dialogsMan["{JOUEUR-TROUVE-TITRE-ET-PAMPHLET}"] = 'Y';
}
if (_inventory.inInventoryByNameId(126)) {
_dialogsMan["{JOUEUR_POSSEDE_EPIGRAPHE}"] = 'Y';
}
if (_currentLevel == 1 && _currentPlaceId == 3) {
playInGameVideo("11D_LEB1");
}
_dialogsMan["{JOUEUR-VU-MEMORANDUM-DANS-LUSTRE-DU-SALON-DE-LA-GUERRE}"] = 'N';
if (_currentLevel == 5 && _gameVariables[GameVariables::kSeenMemorandum] &&
!_inventory.inInventoryByNameId(140)) {
_dialogsMan["{JOUEUR-VU-MEMORANDUM-DANS-LUSTRE-DU-SALON-DE-LA-GUERRE}"] = 'Y';
}
if (_currentLevel == 1 && _currentPlaceId == 1 && currentGameTime() == 3 &&
sequence.hasPrefix("13F_HUI") && _gameVariables[GameVariables::kWarnedIncomplete] == 0 &&
_dialogsMan["{JOUEUR-TROUVE-TITRE-ET-PAMPHLET}"] == 'Y' &&
(!_inventory.inInventoryByNameId(96) || !_inventory.inInventoryByNameId(98))) {
displayMessageBoxWarp(18);
_gameVariables[GameVariables::kWarnedIncomplete] = 1;
return 0;
}
if (_currentLevel == 2 && _currentPlaceId == 11 && currentGameTime() == 4 &&
sequence.hasPrefix("24Z_BON") && _gameVariables[GameVariables::kWarnedIncomplete] == 0 &&
_dialogsMan["{JOUEUR-MONTRE-TITRE-FABLE-APPARU-SUR-ESQUISSE}"] == 'Y' &&
(!_inventory.inInventoryByNameId(101) || !_inventory.inInventoryByNameId(103))) {
displayMessageBoxWarp(18);
_gameVariables[GameVariables::kWarnedIncomplete] = 1;
return 0;
}
if (_currentLevel == 3 && _currentPlaceId == 10 && currentGameTime() == 3 &&
sequence.hasPrefix("31O_SUIA") && _gameVariables[GameVariables::kWarnedIncomplete] == 0 &&
_dialogsMan["CROISSY-ACCEPTE-TEXTE"] == 'Y' &&
(!_inventory.inInventoryByNameId(121) || !_inventory.inInventoryByNameId(119) ||
!_inventory.inInventoryByNameId(115) || _gameVariables[GameVariables::kGotMedaillesSolution] == 0)) {
displayMessageBoxWarp(18);
_gameVariables[GameVariables::kWarnedIncomplete] = 1;
return 0;
}
if (_currentLevel == 4 && _currentPlaceId == 10 && currentGameTime() == 3 &&
sequence.hasPrefix("42C_BON") && _gameVariables[GameVariables::kWarnedIncomplete] == 0 &&
_dialogsMan["{JOUEUR-MONTRE-PAMPHLET-RELIGION}"] == 'Y' &&
(!_inventory.inInventoryByNameId(127) || _gameVariables[GameVariables::kUsedPlanVauban1] == 0 ||
_gameVariables[GameVariables::kUsedPlanVauban2] == 0)) {
displayMessageBoxWarp(18);
_gameVariables[GameVariables::kWarnedIncomplete] = 1;
return 0;
}
if (_currentLevel == 5 && _currentPlaceId == 10 && currentGameTime() == 3 &&
sequence.hasPrefix("42C_BON") && _gameVariables[GameVariables::kWarnedIncomplete] == 0 &&
_dialogsMan["{JOUEUR-MONTRE-PAMPHLET-RELIGION}"] == 'Y' &&
(!_inventory.inInventoryByNameId(127) || _gameVariables[GameVariables::kUsedPlanVauban1] == 0 ||
_gameVariables[GameVariables::kUsedPlanVauban2] == 0)) {
displayMessageBoxWarp(18);
_gameVariables[GameVariables::kWarnedIncomplete] = 1;
return 0;
}
if (_currentLevel == 6 && _currentPlaceId == 14 && currentGameTime() == 2 &&
sequence.hasPrefix("61_BON") && _gameVariables[GameVariables::kWarnedIncomplete] == 0) {
displayMessageBoxWarp(19);
_gameVariables[GameVariables::kWarnedIncomplete] = 1;
return 0;
}
return 1;
}
void CryOmni3DEngine_Versailles::postprocessDialog(const Common::String &sequence) {
if (_currentLevel == 1) {
if (_dialogsMan["{LEVEL1_FINI}"] == 'Y') {
playTransitionEndLevel(1);
}
} else if (_currentLevel == 2) {
_dialogsMan["{JOUEUR-PRESENTE-FAUX-CROQUIS}"] = 'N';
_dialogsMan["{JOUEUR-PRESENTE-FAUX-CROQUIS2}"] = 'N';
_dialogsMan["{JOUEUR-PRESENTE-FAUX-CROQUIS3}"] = 'N';
_dialogsMan["{LE JOUEUR-PRESENTE-AUTRES-ESQUISSES-OU-ESQUISSE-NON-TRIEES}"] = 'N';
_dialogsMan["{LE JOUEUR-PRESENTE-ESQUISSES-TRIEES}"] = 'N';
_dialogsMan["{JOUEUR-MONTRE-TITRE-FABLE-APPARU-SUR-ESQUISSE}"] = 'N';
_dialogsMan["{JOUEUR-MONTRE-ESQUISSE-DETRUITE}"] = 'N';
if (_dialogsMan["{LEVEL2_FINI}"] == 'Y') {
playTransitionEndLevel(2);
}
} else if (_currentLevel == 3) {
if (currentGameTime() == 1 && _dialogsMan["LULLY-DONNE-MISSION1-JOUEUR"] == 'Y') {
setGameTime(2, 3);
}
if (!_gameVariables[GameVariables::kGotMedaillesSolution] &&
_dialogsMan["MONSIEUR-DONNE-SOLUTION-MEDAILLES"] == 'Y') {
playInGameVideo("32M_MR2");
_gameVariables[GameVariables::kGotMedaillesSolution] = 1;
}
if (!_gameVariables[GameVariables::kCollectePartition] &&
_dialogsMan["LULLY-DIT-CHAT-PENDU-JOUEUR"] == 'Y') {
_gameVariables[GameVariables::kCollectePartition] = 1;
collectObject(118);
setGameTime(3, 3);
}
if (currentGameTime() == 1 && _dialogsMan["CROISSY-ACCEPTE-TEXTE"] == 'Y') {
setGameTime(4, 3);
}
if (_dialogsMan["{LEVEL3_FINI}"] == 'Y') {
playTransitionEndLevel(3);
}
if (sequence == "32M_MR" && _dialogsMan["MONSIEUR-DONNE-SOLUTION-MEDAILLES"] == 'Y') {
_dialogsMan["{JOUEUR-MONTRE-MEDAILLES-MONSIEUR}"] = 'Y';
}
_dialogsMan["{JOUEUR-MONTRE-PAMPHLET-ARCHITECTURE}"] = 'N';
_dialogsMan["{JOUEUR-MONTRE-EPIGRAPHE-MEDAILLES}"] = 'N';
_dialogsMan["{JOUEUR-MONTRE-TOUT-AUTRE-CHOSE}"] = 'N';
} else if (_currentLevel == 4) {
if (_dialogsMan["{LEVEL4_FINI}"] == 'Y') {
playTransitionEndLevel(4);
}
} else if (_currentLevel == 5) {
if (sequence == "54I_BON" && _dialogsMan["BONTEMPS-DIT-PROMENADE"] == 'Y') {
collectObject(141);
playTransitionEndLevel(5);
}
if (sequence == "52A4_LAC" && _gameVariables[GameVariables::kStatePamphletReligion] != 3 &&
_dialogsMan["LACHAIZE-DIT-REFORME"] == 'Y' && _dialogsMan["LACHAIZE-DIT-DRAGONNADES"] == 'Y' &&
_dialogsMan["LACHAIZE-TROUVE-ECROUELLES"] == 'Y') {
_inventory.removeByNameId(125);
_gameVariables[GameVariables::kStatePamphletReligion] = 3;
collectObject(125);
_inventory.setSelectedObject(nullptr);
}
}
}
void CryOmni3DEngine_Versailles::updateGameTimeDialVariables() {
_dialogsMan["{CURRENT_GAME_TIME1}"] = 'N';
_dialogsMan["{CURRENT_GAME_TIME2}"] = 'N';
_dialogsMan["{CURRENT_GAME_TIME3}"] = 'N';
_dialogsMan["{CURRENT_GAME_TIME4}"] = 'N';
_dialogsMan["{CURRENT_GAME_TIME5}"] = 'N';
switch (currentGameTime()) {
case 1:
_dialogsMan["{CURRENT_GAME_TIME1}"] = 'Y';
break;
case 2:
_dialogsMan["{CURRENT_GAME_TIME2}"] = 'Y';
break;
case 3:
_dialogsMan["{CURRENT_GAME_TIME3}"] = 'Y';
break;
case 4:
_dialogsMan["{CURRENT_GAME_TIME4}"] = 'Y';
break;
case 5:
_dialogsMan["{CURRENT_GAME_TIME5}"] = 'Y';
break;
default:
error("Invalid current game time %d", currentGameTime());
}
}
void CryOmni3DEngine_Versailles::setupDialogShows() {
_dialogsMan.registerShowCallback("(BONTEMPS-MONTRE-TROISIEME-TITRE-DE-FABLE)",
&CryOmni3DEngine_Versailles::dialogShowBontempsShowThird);
_dialogsMan.registerShowCallback("(HUISSIER DONNE PAMPHLET SUR LA FAMILLE ROYALE)",
&CryOmni3DEngine_Versailles::dialogShowHuissierShowPamphlet);
_dialogsMan.registerShowCallback("(MONSEIGNEUR TRIE LES ESQUISSES)",
&CryOmni3DEngine_Versailles::dialogShowMonseigneurSorts);
_dialogsMan.registerShowCallback("(ANIMATION LE BRUN REGARDE LES ESQUISSES)",
&CryOmni3DEngine_Versailles::dialogShowLeBrunWatches);
_dialogsMan.registerShowCallback("(OUVERTURE DES PORTES)",
&CryOmni3DEngine_Versailles::dialogShowDoorsOpen);
_dialogsMan.registerShowCallback("(GARDE SUISSE DONNE CLEF PETITE PORTE)",
&CryOmni3DEngine_Versailles::dialogShowSwissGuardGives);
_dialogsMan.registerShowCallback("(LULLY CORRIGE LA PARTITION.)",
&CryOmni3DEngine_Versailles::dialogShowLullyCorrects);
_dialogsMan.registerShowCallback("(BONTEMPS DONNE AUTORISATION)",
&CryOmni3DEngine_Versailles::dialogShowBontempsGivesAuth);
_dialogsMan.registerShowCallback("(CROISSY PART)",
&CryOmni3DEngine_Versailles::dialogShowCroissyLeave);
_dialogsMan.registerShowCallback("(MAINTENON-DONNE-PAMPHLET-RELIGION)",
&CryOmni3DEngine_Versailles::dialogShowMaintenonGives);
_dialogsMan.registerShowCallback("(LA CHAIZE REND TEXTE)",
&CryOmni3DEngine_Versailles::dialogShowLaChaizeGivesBack);
_dialogsMan.registerShowCallback("(LA CHAIZE " "\x83" "CRIT DRAGONNADES)",
&CryOmni3DEngine_Versailles::dialogShowLaChaizeWrites);
_dialogsMan.registerShowCallback("(LACHAIZE-DONNE-PAMPHLET-JOUEUR)",
&CryOmni3DEngine_Versailles::dialogShowLaChaizeGivesPamphlet);
_dialogsMan.registerShowCallback("(BONTEMPS-DONNE-CLEF-DES-COMBLES)",
&CryOmni3DEngine_Versailles::dialogShowBontempsGivesKey);
_dialogsMan.registerShowCallback("(LE DUC DU MAINE S'EN VA)",
&CryOmni3DEngine_Versailles::dialogShowDuMaineLeaves);
_dialogsMan.registerShowCallback("(SC" "\xe9" "NE DE TRANSITION)",
&CryOmni3DEngine_Versailles::dialogShowTransitionScene);
_dialogsMan.registerShowCallback("(FIN DU JEU)", &CryOmni3DEngine_Versailles::dialogShowEndOfGame);
_dialogsMan.registerShowCallback("(LEBRUN-DONNE-FAUSSES-ESQUISSES)",
&CryOmni3DEngine_Versailles::dialogShowLeBrunGives);
_dialogsMan.registerShowCallback("(LEBRUN_S_EN_VA)",
&CryOmni3DEngine_Versailles::dialogShowLeBrunLeave);
}
void CryOmni3DEngine_Versailles::dialogShowBontempsShowThird() {
// Nothing to do
}
void CryOmni3DEngine_Versailles::dialogShowHuissierShowPamphlet() {
collectObject(101);
_inventory.setSelectedObject(nullptr);
}
void CryOmni3DEngine_Versailles::dialogShowMonseigneurSorts() {
_inventory.removeByNameId(105);
collectObject(106);
_gameVariables[GameVariables::kEsquissePainted] = 2;
_inventory.setSelectedObject(nullptr);
setGameTime(3, 2);
_dialogsMan["MONSEIGNEUR-ATTEND-ESQUISSES"] = 'N';
}
void CryOmni3DEngine_Versailles::dialogShowLeBrunWatches() {
// Nothing to do
}
void CryOmni3DEngine_Versailles::dialogShowDoorsOpen() {
// Nothing to do
}
void CryOmni3DEngine_Versailles::dialogShowSwissGuardGives() {
collectObject(123);
_dialogsMan["{JOUEUR-POSSEDE-CLE}"] = 'Y';
}
void CryOmni3DEngine_Versailles::dialogShowLullyCorrects() {
// Nothing to do
}
void CryOmni3DEngine_Versailles::dialogShowBontempsGivesAuth() {
collectObject(120);
}
void CryOmni3DEngine_Versailles::dialogShowCroissyLeave() {
// Nothing to do
}
void CryOmni3DEngine_Versailles::dialogShowMaintenonGives() {
collectObject(125);
_inventory.setSelectedObject(nullptr);
}
void CryOmni3DEngine_Versailles::dialogShowLaChaizeGivesBack() {
// Nothing to do
}
void CryOmni3DEngine_Versailles::dialogShowLaChaizeWrites() {
// Nothing to do
}
void CryOmni3DEngine_Versailles::dialogShowLaChaizeGivesPamphlet() {
// Nothing to do
}
void CryOmni3DEngine_Versailles::dialogShowBontempsGivesKey() {
collectObject(140);
_inventory.setSelectedObject(nullptr);
}
void CryOmni3DEngine_Versailles::dialogShowDuMaineLeaves() {
playInGameVideo("62S_DUC1");
_inventory.removeByNameId(144);
_inventory.setSelectedObject(nullptr);
setPlaceState(19, 1);
}
void CryOmni3DEngine_Versailles::dialogShowTransitionScene() {
}
void CryOmni3DEngine_Versailles::dialogShowEndOfGame() {
playTransitionEndLevel(6);
}
void CryOmni3DEngine_Versailles::dialogShowLeBrunGives() {
collectObject(107);
_inventory.setSelectedObject(nullptr);
}
void CryOmni3DEngine_Versailles::dialogShowLeBrunLeave() {
playInGameVideo("11D_LEB3");
setGameTime(2, 1);
}
} // End of namespace Versailles
} // End of namespace CryOmni3D

View File

@ -0,0 +1,379 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "audio/decoders/wave.h"
#include "common/file.h"
#include "common/system.h"
#include "cryomni3d/video/hnm_decoder.h"
#include "cryomni3d/versailles/dialogs_manager.h"
#include "cryomni3d/versailles/engine.h"
namespace CryOmni3D {
namespace Versailles {
bool Versailles_DialogsManager::play(const Common::String &sequence) {
// Prepare with specific Versailles stuff
if (!_engine->preprocessDialog(sequence)) {
return false;
}
_engine->musicSetQuiet(true);
_engine->setCursor(181);
// No need to adjust hide cursor counter, there isn't any in ScummVM
bool cursorWasVisible = g_system->showMouse(true);
bool slowStop = false;
bool didSth = DialogsManager::play(sequence, slowStop);
g_system->showMouse(cursorWasVisible);
if (didSth && slowStop) {
if (_engine->showSubtitles()) {
bool skip = false;
unsigned int end = g_system->getMillis() + 2000;
while (!g_engine->shouldQuit() && g_system->getMillis() < end && !skip) {
g_system->updateScreen();
if (_engine->pollEvents() &&
(_engine->checkKeysPressed(1, Common::KEYCODE_SPACE) ||
_engine->getCurrentMouseButton() == 1)) {
skip = true;
}
}
}
}
_engine->postprocessDialog(sequence);
_engine->musicSetQuiet(false);
_lastImage.free();
_engine->waitMouseRelease();
return didSth;
}
void Versailles_DialogsManager::executeShow(const Common::String &show) {
Common::HashMap<Common::String, ShowCallback>::iterator showIt = _shows.find(show);
if (showIt == _shows.end()) {
error("Missing show %s", show.c_str());
}
_lastImage.free();
ShowCallback cb = showIt->_value;
(_engine->*cb)();
}
void Versailles_DialogsManager::playDialog(const Common::String &video, const Common::String &sound,
const Common::String &text, const SubtitlesSettings &settings) {
Common::String videoFName(video);
Common::String soundFName(sound);
videoFName += ".hnm";
// Don't look for HNS file here
while (soundFName.size() < 8) {
soundFName += '_';
}
soundFName += ".wav";
Video::HNMDecoder *videoDecoder = new Video::HNMDecoder(true);
if (!videoDecoder->loadFile(videoFName)) {
warning("Failed to open movie file %s/%s", video.c_str(), videoFName.c_str());
delete videoDecoder;
return;
}
Common::File *audioFile = new Common::File();
if (!audioFile->open(soundFName)) {
warning("Failed to open sound file %s/%s", sound.c_str(), soundFName.c_str());
delete videoDecoder;
delete audioFile;
return;
}
Audio::SeekableAudioStream *audioDecoder = Audio::makeWAVStream(audioFile, DisposeAfterUse::YES);
// We lost ownership of the audioFile just set it to nullptr and don't use it
audioFile = nullptr;
if (!audioDecoder) {
delete videoDecoder;
return;
}
g_system->showMouse(false);
uint16 width = videoDecoder->getWidth();
uint16 height = videoDecoder->getHeight();
// Preload first frame to draw subtitles from it
const Graphics::Surface *firstFrame = videoDecoder->decodeNextFrame();
assert(firstFrame != nullptr);
if (videoDecoder->hasDirtyPalette()) {
const byte *palette = videoDecoder->getPalette();
_engine->setupPalette(palette, 0, 256);
}
FontManager &fontManager = _engine->_fontManager;
_lastImage.create(firstFrame->w, firstFrame->h, firstFrame->format);
_lastImage.blitFrom(*firstFrame);
fontManager.setCurrentFont(7);
fontManager.setTransparentBackground(true);
fontManager.setForeColor(241);
fontManager.setLineHeight(22);
fontManager.setSpaceWidth(2);
fontManager.setCharSpacing(1);
if (_engine->showSubtitles()) {
Common::Rect block = settings.textRect;
unsigned int lines = fontManager.getLinesCount(text, block.width() - 8);
if (lines == 0) {
lines = 5;
}
unsigned int blockHeight = fontManager.lineHeight() * lines + 6;
block.setHeight(blockHeight);
if (block.bottom >= 480) {
block.bottom = 470;
warning("Dialog text is really too long");
}
// Make only the block area translucent inplace
Graphics::Surface blockSurface = _lastImage.getSubArea(block);
_engine->makeTranslucent(blockSurface, blockSurface);
fontManager.setSurface(&_lastImage);
block.grow(-4);
fontManager.setupBlock(block);
fontManager.displayBlockText(text);
}
g_system->copyRectToScreen(_lastImage.getPixels(), _lastImage.pitch, 0, 0, width, height);
g_system->updateScreen();
const Common::Rect &drawRect = settings.drawRect;
if (audioDecoder->getLength() == 0) {
// Empty wave file
delete audioDecoder;
unsigned int duration = 100 * text.size();
if (duration < 1000) {
duration = 1000;
}
bool skipWait = false;
unsigned int end = g_system->getMillis() + duration;
while (!g_engine->shouldQuit() && g_system->getMillis() < end && !skipWait) {
if (_engine->pollEvents() && _engine->checkKeysPressed(1, Common::KEYCODE_SPACE)) {
skipWait = true;
}
}
} else {
// Let start the show!
videoDecoder->start();
Audio::SoundHandle audioHandle;
_engine->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &audioHandle, audioDecoder);
// We lost ownership of the audioDecoder just set it to nullptr and don't use it
audioDecoder = nullptr;
bool skipVideo = false;
while (!g_engine->shouldQuit() && _engine->_mixer->isSoundHandleActive(audioHandle) && !skipVideo) {
if (_engine->pollEvents() && _engine->checkKeysPressed(1, Common::KEYCODE_SPACE)) {
skipVideo = true;
}
if (videoDecoder->needsUpdate()) {
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (frame) {
if (videoDecoder->hasDirtyPalette()) {
const byte *palette = videoDecoder->getPalette();
_engine->setupPalette(palette, 0, 256);
}
// Only refresh the moving part of the animation
const Graphics::Surface subFrame = frame->getSubArea(drawRect);
g_system->copyRectToScreen(subFrame.getPixels(), subFrame.pitch, drawRect.left, drawRect.top,
subFrame.w, subFrame.h);
}
}
g_system->updateScreen();
}
_engine->_mixer->stopHandle(audioHandle);
}
// It's intentional that _lastImage is set with the first video image
delete videoDecoder;
g_system->showMouse(true);
}
void Versailles_DialogsManager::displayMessage(const Common::String &text) {
_engine->displayMessageBoxWarp(text);
}
unsigned int Versailles_DialogsManager::askPlayerQuestions(const Common::String &video,
const Common::StringArray &questions) {
if (_lastImage.empty()) {
loadFrame(video);
}
if (questions.size() == 0 || questions.size() > 5) {
return -1;
}
FontManager &fontManager = _engine->_fontManager;
fontManager.setCurrentFont(7);
fontManager.setTransparentBackground(true);
fontManager.setLineHeight(18);
fontManager.setSpaceWidth(2);
fontManager.setSurface(&_lastImage);
int16 tops[5];
int16 bottoms[5];
int16 currentHeight = 0;
unsigned int questionId = 0;
for (Common::StringArray::const_iterator it = questions.begin(); it != questions.end();
it++, questionId++) {
tops[questionId] = currentHeight;
unsigned int lines = fontManager.getLinesCount(*it, 598);
if (lines == 0) {
lines = 1;
}
currentHeight += 18 * lines;
bottoms[questionId] = currentHeight;
}
int offsetY = 480 - (bottoms[questions.size() - 1] - tops[0]);
if (offsetY > 402) {
offsetY = 402;
} else if (offsetY < 2) {
offsetY = 2;
}
for (questionId = 0; questionId < questions.size(); questionId++) {
tops[questionId] += offsetY;
bottoms[questionId] += offsetY;
}
_engine->setCursor(181);
Graphics::Surface alphaSurface = _lastImage.getSubArea(Common::Rect(0, offsetY - 2, 640, 480));
_engine->makeTranslucent(alphaSurface, alphaSurface);
bool finished = false;
bool update = true;
unsigned int selectedQuestion = -1;
while (!finished) {
if (update) {
update = false;
questionId = 0;
for (Common::StringArray::const_iterator it = questions.begin(); it != questions.end();
it++, questionId++) {
fontManager.setForeColor(selectedQuestion == questionId ? 241 : 245);
fontManager.setupBlock(Common::Rect(10, tops[questionId], 608, bottoms[questionId]));
fontManager.displayBlockText(*it);
}
g_system->copyRectToScreen(_lastImage.getPixels(), _lastImage.pitch, 0, 0, _lastImage.w,
_lastImage.h);
}
g_system->updateScreen();
if (_engine->pollEvents()) {
_engine->clearKeys();
if (g_engine->shouldQuit()) {
finished = true;
selectedQuestion = -1;
break;
}
Common::Point mousePos = _engine->getMousePos();
if (_engine->getDragStatus() == kDragStatus_Finished && selectedQuestion != -1u) {
finished = true;
} else if (mousePos.x >= 608 || mousePos.y < offsetY) {
if (selectedQuestion != -1u) {
selectedQuestion = -1;
update = true;
}
} else {
for (questionId = 0; questionId < questions.size(); questionId++) {
if (mousePos.y > tops[questionId] && mousePos.y < bottoms[questionId]) {
break;
}
}
if (questionId < questions.size()) {
if (selectedQuestion != questionId) {
selectedQuestion = questionId;
update = true;
}
} else {
selectedQuestion = -1;
update = true;
}
}
}
}
return selectedQuestion;
}
void Versailles_DialogsManager::loadFrame(const Common::String &video) {
Common::String videoFName(video);
int lastDotPos = videoFName.size() - 1;
for (; lastDotPos >= 0; --lastDotPos) {
if (videoFName[lastDotPos] == '.') {
break;
}
}
if (lastDotPos > -1) {
videoFName.erase(lastDotPos);
videoFName += ".hnm";
}
Video::HNMDecoder *videoDecoder = new Video::HNMDecoder();
if (!videoDecoder->loadFile(videoFName)) {
warning("Failed to open movie file %s/%s", video.c_str(), videoFName.c_str());
delete videoDecoder;
return;
}
if (videoDecoder->hasDirtyPalette()) {
const byte *palette = videoDecoder->getPalette();
_engine->setupPalette(palette, 0, 256);
}
// Preload first frame to draw subtitles from it
const Graphics::Surface *firstFrame = videoDecoder->decodeNextFrame();
_lastImage.create(firstFrame->w, firstFrame->h, firstFrame->format);
_lastImage.blitFrom(*firstFrame);
}
} // End of namespace Versailles
} // End of namespace CryOmni3D

View File

@ -0,0 +1,68 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_VERSAILLES_DIALOGS_MANAGER_H
#define CRYOMNI3D_VERSAILLES_DIALOGS_MANAGER_H
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "graphics/managed_surface.h"
#include "cryomni3d/dialogs_manager.h"
#include "cryomni3d/font_manager.h"
namespace CryOmni3D {
namespace Versailles {
class CryOmni3DEngine_Versailles;
class Versailles_DialogsManager : public DialogsManager {
public:
Versailles_DialogsManager(CryOmni3DEngine_Versailles *engine) : _engine(engine) { }
// This overload will hide the base one and this is what we want
bool play(const Common::String &sequence);
typedef void (CryOmni3DEngine_Versailles::*ShowCallback)();
void registerShowCallback(const Common::String &showName, ShowCallback callback) { _shows[showName] = callback; }
protected:
void executeShow(const Common::String &show) override;
void playDialog(const Common::String &video, const Common::String &sound,
const Common::String &text, const SubtitlesSettings &settings) override;
void displayMessage(const Common::String &text) override;
unsigned int askPlayerQuestions(const Common::String &video,
const Common::StringArray &questions) override;
private:
CryOmni3DEngine_Versailles *_engine;
Common::HashMap<Common::String, ShowCallback> _shows;
void loadFrame(const Common::String &video);
Graphics::ManagedSurface _lastImage;
};
} // End of namespace Versailles
} // End of namespace CryOmni3D
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,143 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_VERSAILLES_DOCUMENTATION_H
#define CRYOMNI3D_VERSAILLES_DOCUMENTATION_H
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/rect.h"
#include "common/str-array.h"
#include "graphics/managed_surface.h"
namespace CryOmni3D {
class FontManager;
class MouseBoxes;
class Sprites;
class CryOmni3DEngine;
namespace Versailles {
class Versailles_Documentation {
public:
Versailles_Documentation() : _engine(nullptr), _fontManager(nullptr), _messages(nullptr),
_linksData(nullptr), _linksSize(0) { }
~Versailles_Documentation() { delete _linksData; }
void init(const Sprites *sprites, FontManager *fontManager, const Common::StringArray *messages,
CryOmni3DEngine *engine);
void handleDocArea();
void handleDocInGame(const Common::String &record);
private:
Common::String docAreaHandleSummary();
Common::String docAreaHandleTimeline();
Common::String docAreaHandleGeneralMap();
Common::String docAreaHandleCastleMap();
unsigned int docAreaHandleRecords(const Common::String &record);
void docAreaPrepareNavigation();
void docAreaPrepareRecord(Graphics::ManagedSurface &surface, MouseBoxes &boxes);
unsigned int docAreaHandleRecord(Graphics::ManagedSurface &surface, MouseBoxes &boxes,
Common::String &nextRecord);
void inGamePrepareRecord(Graphics::ManagedSurface &surface, MouseBoxes &boxes);
unsigned int inGameHandleRecord(Graphics::ManagedSurface &surface, MouseBoxes &boxes,
Common::String &nextRecord);
void setupRecordBoxes(bool inDocArea, MouseBoxes &boxes);
void setupTimelineBoxes(MouseBoxes &boxes);
void drawRecordData(Graphics::ManagedSurface &surface,
const Common::String &text, const Common::String &title,
const Common::String &subtitle, const Common::String &caption);
void drawRecordBoxes(Graphics::ManagedSurface &surface, bool inDocArea, MouseBoxes &boxes);
unsigned int handlePopupMenu(const Graphics::ManagedSurface &surface,
const Common::Point &anchor, bool rightAligned, unsigned int itemHeight,
const Common::StringArray &items);
struct RecordInfo {
unsigned int id;
unsigned int position;
unsigned int size;
};
struct LinkInfo {
Common::String record;
Common::String title;
};
struct TimelineEntry {
char year[8];
unsigned int x;
unsigned int y;
};
static const TimelineEntry kTimelineEntries[];
static char *getDocPartAddress(char *start, char *end, const char *patterns[]);
static const char *getDocTextAddress(char *start, char *end);
static const char *getRecordTitle(char *start, char *end);
static const char *getRecordSubtitle(char *start, char *end);
static const char *getRecordCaption(char *start, char *end);
static void getRecordHyperlinks(char *start, char *end, Common::StringArray &hyperlinks);
Common::String getRecordTitle(const Common::String &record);
Common::String getRecordData(const Common::String &record, Common::String &title,
Common::String &subtitle, Common::String &caption,
Common::StringArray &hyperlinks);
void convertHyperlinks(const Common::StringArray &hyperlinks, Common::Array<LinkInfo> &links);
void loadLinksFile();
void getLinks(const Common::String &record, Common::Array<LinkInfo> &links);
static const char *kAllDocsFile;
static const char *kLinksDocsFile;
static const unsigned int kPopupMenuMargin = 5;
CryOmni3DEngine *_engine;
FontManager *_fontManager;
const Sprites *_sprites;
const Common::StringArray *_messages;
Common::StringArray _recordsOrdered;
Common::HashMap<Common::String, RecordInfo> _records;
char *_linksData;
unsigned int _linksSize;
Common::Array<LinkInfo> _allLinks;
Common::StringArray _visitTrace;
Common::String _currentRecord;
Common::String _categoryStartRecord;
Common::String _categoryEndRecord;
Common::String _categoryTitle;
Common::Array<LinkInfo> _currentLinks;
bool _currentInTimeline;
bool _currentMapLayout;
bool _currentHasMap;
};
} // End of namespace Versailles
} // End of namespace CryOmni3D
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,453 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_VERSAILLES_ENGINE_H
#define CRYOMNI3D_VERSAILLES_ENGINE_H
#include "common/events.h"
#include "common/random.h"
#include "common/array.h"
#include "common/hashmap.h"
#include "common/str.h"
#include "cryomni3d/cryomni3d.h"
#include "cryomni3d/omni3d.h"
#include "cryomni3d/sprites.h"
#include "cryomni3d/wam_parser.h"
#include "cryomni3d/versailles/documentation.h"
#include "cryomni3d/versailles/toolbar.h"
#include "cryomni3d/versailles/dialogs_manager.h"
namespace Graphics {
class ManagedSurface;
class Surface;
}
namespace CryOmni3D {
struct FixedImageConfiguration;
class ZonFixedImage;
}
namespace CryOmni3D {
namespace Versailles {
struct PlaceStateActionKey {
unsigned int placeId;
unsigned int placeState;
unsigned int actionId;
PlaceStateActionKey(unsigned int placeId_, unsigned int placeState_, unsigned int actionId_) :
placeId(placeId_), placeState(placeState_), actionId(actionId_) {}
bool operator==(const PlaceStateActionKey &other) const {
return other.placeId == placeId && other.placeState == placeState && other.actionId == actionId;
}
};
struct PlaceActionKey {
unsigned int placeId;
unsigned int actionId;
PlaceActionKey(unsigned int placeId_, unsigned int actionId_) :
placeId(placeId_), actionId(actionId_) {}
bool operator==(const PlaceActionKey &other) const {
return other.placeId == placeId && other.actionId == actionId;
}
};
}
}
namespace Common {
template<>
struct Hash<CryOmni3D::Versailles::PlaceStateActionKey> {
uint operator()(const CryOmni3D::Versailles::PlaceStateActionKey &k) const {
// placeState shouldn't be greater than 8 and placeId shouldn't be greater than 100
// originalActionId shouldn't be greater than 65536
return (k.placeId << 24 | k.placeState << 16) ^ k.actionId;
}
};
template<>
struct Hash<CryOmni3D::Versailles::PlaceActionKey> {
uint operator()(const CryOmni3D::Versailles::PlaceActionKey &k) const {
// placeId shouldn't be greater than 100
// originalActionId shouldn't be greater than 65536
return (k.placeId << 16) ^ k.actionId;
}
};
}
namespace CryOmni3D {
namespace Versailles {
class CryOmni3DEngine_Versailles;
enum AbortCommand {
AbortNoAbort = 0,
AbortQuit = 1,
AbortLoadGame = 2,
AbortNewGame = 3,
AbortNextLevel = 5,
AbortFinished = 6,
AbortGameOver = 7
};
struct GameVariables {
enum Var {
// TODO: make these enum members more correct
kCollectPartition = 0, // 0
kUnlockPetitePorte,
kAlreadyCame31,
kDrawerStatus,
kCurrentTime,
kGotMedaillesSolution,
kDrawerFurnitureStatus,
kCollectePartition,
kCollectPamphletArchi,
kGotRevealedPaper, // OK
kCollectCle, // 10
kCollectCartonDessin,
kEsquissePainted,
kStateFauxCroquis,
kCollectNourriture,
kCollectPlume,
kStatePamphletReligion,
kCollectPetiteCle3,
kCollectGravure,
kCollectCordon,
kCollectPlanVauban, // 20
kCollectPlanVauban2,
kCollectEchelle,
kLostCordon,
kDescendreLustre,
kOrangerRatisse,
kDiscussedLabyrOrder,
kUsedBougieAllumee,
kStateBombe,
kInkSpilled, // OK
kCollectedPaperOnTable, // OK // 30
kCoffreUnlocked,
//kUselessVar,
kCollectedPaperInTrunk = 33, // OK
kUsingPinceauColor,
kUsedScissors, // OK
kUsedClefsCombles,
kHasPlayedLebrun, // OK
kWarnedIncomplete,
kUsedPlanVauban1,
kUsedPlanVauban2, // 40
kSeenMemorandum,
kCollectScissors, // OK
kSavedCountdown, // TODO: calculate it in real time
kMax
};
};
// For random sounds we set a constant ID and avoid to use it elsewhere
struct SoundIds {
enum {
kOrgue = 0,
kLeb001,
kMax
};
};
struct PlaceState {
typedef void (CryOmni3DEngine_Versailles::*InitFunc)();
typedef bool (CryOmni3DEngine_Versailles::*FilterEventFunc)(unsigned int *event);
PlaceState() : initPlace(nullptr), filterEvent(nullptr), docImage(nullptr), state(0) {}
PlaceState(InitFunc initPlace_, FilterEventFunc filterEvent_, const char *docImage_) :
initPlace(initPlace_), filterEvent(filterEvent_), docImage(docImage_), state(0) {}
InitFunc initPlace;
FilterEventFunc filterEvent;
const char *docImage;
unsigned int state;
};
struct LevelInitialState {
unsigned int placeId;
double alpha;
double beta;
};
struct FakeTransitionActionPlace {
unsigned int actionId;
unsigned int placeId;
};
typedef void (CryOmni3DEngine_Versailles::*FixedImgCallback)(ZonFixedImage *);
struct MsgBoxParameters {
int font;
byte foreColor;
unsigned int lineHeight;
unsigned int spaceWidth;
unsigned int charSpacing;
unsigned int initialWidth;
unsigned int incrementWidth;
unsigned int initialHeight;
unsigned int incrementHeight;
unsigned int timeoutChar;
};
class CryOmni3DEngine_Versailles : public CryOmni3DEngine {
friend class Versailles_DialogsManager;
protected:
Common::Error run() override;
public:
CryOmni3DEngine_Versailles(OSystem *syst, const CryOmni3DGameDescription *gamedesc);
virtual ~CryOmni3DEngine_Versailles();
void setupPalette(const byte *colors, uint start, uint num) override { setupPalette(colors, start, num, true); }
void makeTranslucent(Graphics::Surface &dst, const Graphics::Surface &src) const override;
virtual bool displayToolbar(const Graphics::Surface *original) override { return _toolbar.displayToolbar(original); };
virtual bool hasPlaceDocumentation() override;
virtual bool displayPlaceDocumentation() override;
virtual unsigned int displayOptions() override;
virtual bool shouldAbort() override { return g_engine->shouldQuit() || _abortCommand != AbortNoAbort; }
private:
void setupFonts();
void setupSprites();
void loadCursorsPalette();
void calculateTransparentMapping();
void setupMessages();
void setupObjects();
void setupDialogVariables();
void setupImgScripts();
void setupPaintingsTitles();
void syncOmni3DSettings();
void syncSoundSettings();
void playTransitionEndLevel(int level);
void changeLevel(int level);
void initNewLevel(int level);
void setupLevelWarps(int level);
void initPlacesStates();
void initWhoSpeaksWhere();
void initDocPeopleRecord();
void setupLevelActionsMask();
unsigned int currentGameTime() const { return _gameVariables[GameVariables::kCurrentTime]; }
void setGameTime(unsigned int newTime, unsigned int level);
void updateGameTimeDialVariables();
void gameStep();
void doGameOver();
void setPlaceState(unsigned int placeId, unsigned int newState);
void doPlaceChange();
void executeTransition(unsigned int nextPlaceId);
void fakeTransition(unsigned int dstPlaceId);
unsigned int determineTransitionAnimation(unsigned int srcId, unsigned int dstId,
const Transition **transition);
unsigned int getFakeTransition(unsigned int actionId) const;
int handleWarp();
bool handleWarpMouse(unsigned int *actionId, unsigned int movingCuror);
void animateWarpTransition(const Transition *transition);
void redrawWarp();
void handleFixedImg(const FixedImgCallback &callback);
void executeSeeAction(unsigned int actionId);
void executeSpeakAction(unsigned int actionId);
void setupDialogShows();
bool preprocessDialog(const Common::String &sequence);
void postprocessDialog(const Common::String &sequence);
void executeDocAction(unsigned int actionId);
void drawMenuTitle(Graphics::ManagedSurface *surface, byte color);
unsigned int displayFilePicker(const Graphics::Surface *bgFrame, bool saveMode,
Common::String &saveName);
unsigned int displayYesNoBox(Graphics::ManagedSurface &surface, const Common::Rect &position,
unsigned int msg_id);
void displayMessageBox(const MsgBoxParameters &params, const Graphics::Surface *surface,
unsigned int msg_id, const Common::Point &position,
const Common::Functor0<void> &callback) { displayMessageBox(params, surface, _messages[msg_id], position, callback); }
void displayMessageBox(const MsgBoxParameters &params, const Graphics::Surface *surface,
const Common::String &msg, const Common::Point &position,
const Common::Functor0<void> &callback);
void displayMessageBoxWarp(const Common::String &message);
void displayMessageBoxWarp(unsigned int msg_id) { displayMessageBoxWarp(_messages[msg_id]); }
void displayCredits();
void warpMsgBoxCB();
bool canVisit() const;
Common::String getSaveFileName(bool visit, unsigned int saveNum) const;
void getSavesList(bool visit, Common::Array<Common::String> &saveNames);
void saveGame(bool visit, unsigned int saveNum, const Common::String &saveName) const;
bool loadGame(bool visit, unsigned int saveNum);
void animateCursor(const Object *object);
void collectObject(unsigned int nameID, const ZonFixedImage *fimg = nullptr,
bool showObject = true);
typedef void (CryOmni3DEngine_Versailles::*DisplayObjectHook)(Graphics::ManagedSurface &surface);
void displayObject(const Common::String &imgName, DisplayObjectHook hook = nullptr);
void setupPalette(const byte *colors, uint start, uint num, bool commit);
bool showSubtitles() const;
void playInGameVideo(const Common::String &filename, bool restoreCursorPalette = true);
unsigned int getMusicId(unsigned int level, unsigned int placeId) const;
bool musicWouldChange(unsigned int level, unsigned int placeId) const;
void musicUpdate();
void musicPause();
void musicResume();
void musicStop();
void musicSetQuiet(bool quiet);
Common::StringArray _messages;
static const unsigned int kSpritesMapTable[];
static const unsigned int kSpritesMapTableSize;
static const LevelInitialState kLevelInitialStates[];
static const FakeTransitionActionPlace kFakeTransitions[];
Common::HashMap<unsigned int, FixedImgCallback> _imgScripts;
Common::Array<Common::String> _paintingsTitles;
Toolbar _toolbar;
byte *_mainPalette;
byte *_cursorPalette;
bool _fadedPalette;
bool _forcePaletteUpdate;
bool _forceRedrawWarp;
byte *_transparentPaletteMap;
unsigned int _transparentSrcStart;
unsigned int _transparentSrcStop;
unsigned int _transparentDstStart;
unsigned int _transparentDstStop;
unsigned int _transparentNewStart;
unsigned int _transparentNewStop;
bool _isPlaying;
bool _isVisiting;
AbortCommand _abortCommand;
unsigned int _loadedSave;
int _omni3dSpeed;
unsigned int _currentLevel;
Versailles_DialogsManager _dialogsMan;
Omni3DManager _omni3dMan;
ZonFixedImage *_fixedImage;
Common::Array<unsigned int> _gameVariables;
Common::Array<PlaceState> _placeStates;
Common::HashMap<PlaceStateActionKey, unsigned int> _actionMasks;
Common::HashMap<PlaceActionKey, Common::String> _whoSpeaksWhere;
Common::HashMap<unsigned int, const char *> _docPeopleRecord;
bool _transitionAnimateWarp;
unsigned int _nextPlaceId;
WAMParser _wam;
unsigned int _currentPlaceId;
const Place *_currentPlace;
const Image::ImageDecoder *_currentWarpImage;
const char *_musicCurrentFile;
Audio::SoundHandle _musicHandle;
float _musicVolumeFactor;
static const char *kMusicFiles[8][8];
Versailles_Documentation _docManager;
static const MsgBoxParameters kWarpMsgBoxParameters;
static const MsgBoxParameters kFixedimageMsgBoxParameters;
static const FixedImageConfiguration kFixedImageConfiguration;
//Objects
template<unsigned int ID>
void genericDisplayObject();
// Fixed image
template<unsigned int ID>
void genericDumbImage(ZonFixedImage *fimg);
template<unsigned int ID>
void genericPainting(ZonFixedImage *fimg);
#define IMG_CB(name) void img_ ## name(ZonFixedImage *fimg)
IMG_CB(31142);
IMG_CB(31142b);
IMG_CB(31142c);
IMG_CB(31142d);
IMG_CB(31143);
IMG_CB(31143b);
IMG_CB(31143c);
IMG_CB(31143d);
IMG_CB(41202);
IMG_CB(41202b);
IMG_CB(41801);
IMG_CB(41801b);
IMG_CB(41801c);
IMG_CB(41802);
IMG_CB(41802b);
IMG_CB(41802c);
IMG_CB(41802d);
#undef IMG_CB
#define FILTER_EVENT(level, place) bool filterEventLevel ## level ## Place ## place(unsigned int *event)
#define INIT_PLACE(level, place) void initPlaceLevel ## level ## Place ## place()
FILTER_EVENT(1, 1);
FILTER_EVENT(1, 2);
INIT_PLACE(1, 3);
FILTER_EVENT(1, 3);
//FILTER_EVENT(1, 7); // Not used
FILTER_EVENT(1, 14);
#undef FILTER_EVENT
#undef INIT_PLACE
// Dialogs shows
void dialogShowBontempsShowThird();
void dialogShowHuissierShowPamphlet();
void dialogShowMonseigneurSorts();
void dialogShowLeBrunWatches();
void dialogShowDoorsOpen();
void dialogShowSwissGuardGives();
void dialogShowLullyCorrects();
void dialogShowBontempsGivesAuth();
void dialogShowCroissyLeave();
void dialogShowMaintenonGives();
void dialogShowLaChaizeGivesBack();
void dialogShowLaChaizeWrites();
void dialogShowLaChaizeGivesPamphlet();
void dialogShowBontempsGivesKey();
void dialogShowDuMaineLeaves();
void dialogShowTransitionScene();
void dialogShowEndOfGame();
void dialogShowLeBrunGives();
void dialogShowLeBrunLeave();
};
} // End of namespace Versailles
} // End of namespace CryOmni3D
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,281 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "audio/audiostream.h"
#include "audio/decoders/wave.h"
#include "common/config-manager.h"
#include "common/error.h"
#include "common/file.h"
#include "cryomni3d/versailles/engine.h"
namespace CryOmni3D {
namespace Versailles {
const char *CryOmni3DEngine_Versailles::kMusicFiles[8][8] = {
{ "1amb", }, // Level 1
{ "2amb", "2amb2", "2amb1" }, // Level 2
{ "3amb", "3amb1", "3amb2" }, // Level 3
{ "4amb", "4amb1" }, // Level 4
{ "5amb1", "5amb2" }, // Level 5
{ "6amb1", "6amb2", "6amb3", "6amb4" }, // Level 6
{ "7amb", }, // Level 7
{ "3amb", "3amb1", "3amb2", "2amb", "2amb1", "2amb2", "4amb" }, // Level 8
};
void CryOmni3DEngine_Versailles::musicUpdate() {
if (!_isPlaying || _currentLevel <= 0 ||
_mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType) ||
_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) == 0) {
// No music in all of these cases
musicStop();
return;
}
unsigned int musicId = getMusicId(_currentLevel, _currentPlaceId);
const char *musicBName = kMusicFiles[_currentLevel - 1][musicId];
assert(musicBName != nullptr);
// Ensure sound is playing in all cases
musicResume();
if (musicBName == _musicCurrentFile) {
// Same file, nothing more to do
return;
}
// New file, stop the old one first
musicStop();
Common::String musicFName = musicBName;
musicFName += ".wav";
Common::File *musicFile = new Common::File();
if (!musicFile->open(musicFName)) {
warning("Failed to open music file %s/%s", musicBName, musicFName.c_str());
delete musicFile;
return;
}
Audio::SeekableAudioStream *musicDecoder = Audio::makeWAVStream(musicFile, DisposeAfterUse::YES);
// We lost ownership of the musicFile just set it to nullptr and don't use it
musicFile = nullptr;
if (!musicDecoder) {
warning("Failed to decode music file %s/%s", musicBName, musicFName.c_str());
return;
}
Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(musicDecoder, 0);
// We lost ownership of musicDecoder just set it to nullptr and don't use it
musicDecoder = nullptr;
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopStream);
_musicCurrentFile = musicBName;
}
void CryOmni3DEngine_Versailles::musicPause() {
_mixer->pauseHandle(_musicHandle, true);
}
void CryOmni3DEngine_Versailles::musicResume() {
_mixer->pauseHandle(_musicHandle, false);
}
void CryOmni3DEngine_Versailles::musicStop() {
// Fade the music first
if (_mixer->isSoundHandleActive(_musicHandle)) {
// We recreate the real channel volume to decrease this one 2 by 2
int musicVol = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
byte channelVol = _mixer->getChannelVolume(_musicHandle);
int realVolume = (musicVol * channelVol) / Audio::Mixer::kMaxChannelVolume;
bool skip = false;
while (realVolume > 0 && !skip) {
// pollEvents waits for 10ms
realVolume -= 2;
channelVol = CLIP((realVolume * Audio::Mixer::kMaxChannelVolume) / musicVol, 0, 255);
_mixer->setChannelVolume(_musicHandle, channelVol);
if (pollEvents() && checkKeysPressed(1, Common::KEYCODE_SPACE)) {
skip = true;
}
}
}
_mixer->stopHandle(_musicHandle);
_musicCurrentFile = nullptr;
}
void CryOmni3DEngine_Versailles::musicSetQuiet(bool quiet) {
float newFactor = quiet ? 3.5f : 1.f;
if (newFactor != _musicVolumeFactor) {
_musicVolumeFactor = newFactor;
syncSoundSettings();
}
}
bool CryOmni3DEngine_Versailles::musicWouldChange(unsigned int level, unsigned int placeId) const {
unsigned int musicId = getMusicId(level, placeId);
const char *musicFile = kMusicFiles[_currentLevel - 1][musicId];
return musicFile != _musicCurrentFile;
}
unsigned int CryOmni3DEngine_Versailles::getMusicId(unsigned int level,
unsigned int placeId) const {
// No need of place state
switch (level) {
case 1:
// Only one music
return 0;
case 2:
switch (placeId) {
case 4:
return 1;
case 10:
case 11:
case 13:
return 2;
default:
return 0;
}
case 3:
switch (placeId) {
case 1:
case 2:
case 3:
case 4:
return 2;
case 6:
case 7:
case 8:
case 12:
case 24:
return 1;
default:
return 0;
}
case 4:
switch (placeId) {
case 1:
case 2:
case 3:
case 4:
return 1;
default:
return 0;
}
case 5:
switch (placeId) {
case 6:
case 7:
case 8:
case 12:
case 26:
case 27:
case 30:
case 31:
return 1;
default:
return 0;
}
case 6:
switch (placeId) {
case 1:
return 3;
case 3:
case 4:
case 5:
case 6:
case 8:
case 9:
case 10:
case 11:
return 0;
case 14:
case 16:
case 17:
case 19:
case 20:
case 22:
case 24:
case 26:
case 27:
case 32:
case 34:
case 38:
case 44:
return 2;
default:
return 1;
}
case 7:
return 0;
case 8:
switch (placeId) {
case 1:
case 2:
case 3:
case 4:
return 2;
case 6:
case 7:
case 8:
return 1;
case 9:
case 10:
case 11:
return 0;
case 12:
return 1;
case 13:
case 14:
case 15:
case 16:
return 0;
case 24:
return 1;
case 33:
case 34:
case 35:
return 5;
case 36:
case 37:
case 38:
case 39:
return 3;
case 40:
return 4;
case 42:
case 43:
case 44:
return 6;
default:
return 0;
}
default:
error("Invalid level %d when choosing music", level);
}
}
} // End of namespace Versailles
} // End of namespace CryOmni3D

View File

@ -0,0 +1,315 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/savefile.h"
#include "common/system.h"
#include "cryomni3d/versailles/engine.h"
#define DEBUG_SAVE
namespace CryOmni3D {
namespace Versailles {
#define SAVE_DESCRIPTION_LEN 20
Common::String CryOmni3DEngine_Versailles::getSaveFileName(bool visit, unsigned int saveNum) const {
return Common::String::format("%s%s.%04u", _targetName.c_str(), visit ? "_visit" : "", saveNum);
}
bool CryOmni3DEngine_Versailles::canVisit() const {
// Build a custom SearchSet
const Common::FSNode gameDataDir(ConfMan.get("path"));
Common::SearchSet visitsSearchSet;
visitsSearchSet.addSubDirectoryMatching(gameDataDir, "datas_v/savegame/visite", 1);
return visitsSearchSet.hasFile("game0001.sav");
}
void CryOmni3DEngine_Versailles::getSavesList(bool visit, Common::StringArray &saveNames) {
Common::SaveFileManager *saveMan = g_system->getSavefileManager();
char saveName[SAVE_DESCRIPTION_LEN + 1];
saveName[SAVE_DESCRIPTION_LEN] = '\0';
Common::String pattern = Common::String::format("%s%s.????", _targetName.c_str(),
visit ? "_visit" : "");
Common::StringArray filenames = saveMan->listSavefiles(pattern);
sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
saveNames.clear();
saveNames.reserve(100);
int num = 1;
int slotNum;
if (visit) {
// Add bootstrap visit
const Common::FSNode gameDataDir(ConfMan.get("path"));
Common::SearchSet visitsSearchSet;
visitsSearchSet.addSubDirectoryMatching(gameDataDir, "datas_v/savegame/visite", 1);
if (visitsSearchSet.hasFile("game0001.sav")) {
Common::File visitFile;
if (!visitFile.open("game0001.sav", visitsSearchSet)) {
error("Can't load visit file");
}
visitFile.read(saveName, SAVE_DESCRIPTION_LEN);
saveNames.push_back(saveName);
} else {
warning("visiting mode but no bootstrap");
// No bootstrap visit, too bad
saveNames.push_back(_messages[55]); //Fill with free slot
}
num++;
}
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end();
++file) {
// Obtain the last 4 digits of the filename, since they correspond to the save slot
slotNum = atoi(file->c_str() + file->size() - 4);
if (slotNum >= 1 && slotNum <= 99) {
while (num < slotNum) {
saveNames.push_back(_messages[55]); //Fill with free slot
num++;
}
num++;
#ifdef DEBUG_SAVE
Common::InSaveFile *in = _saveFileMan->openRawFile(*file);
#else
Common::InSaveFile *in = _saveFileMan->openForLoading(*file);
#endif
if (in) {
in->read(saveName, SAVE_DESCRIPTION_LEN);
saveNames.push_back(saveName);
delete in;
}
}
}
for (unsigned int i = saveNames.size(); i < 100; i++) {
saveNames.push_back(_messages[55]);
}
}
void CryOmni3DEngine_Versailles::saveGame(bool visit, unsigned int saveNum,
const Common::String &saveName) const {
if (visit && saveNum == 1) {
error("Can't erase bootstrap visit");
}
Common::String saveFileName = getSaveFileName(visit, saveNum);
Common::OutSaveFile *out;
if (!(out = _saveFileMan->openForSaving(saveFileName,
#ifdef DEBUG_SAVE
false
#else
true
#endif
))) {
return;
}
// Write save name
char saveNameC[SAVE_DESCRIPTION_LEN];
memset(saveNameC, 0, sizeof(saveNameC));
strncpy(saveNameC, saveName.c_str(), sizeof(saveNameC));
out->write(saveNameC, sizeof(saveNameC));
// dummy values
out->writeUint32LE(0);
out->writeUint32BE(0);
out->writeUint32BE(0);
// Dialog variables
assert(_dialogsMan.size() < 200);
for (unsigned int i = 0; i < _dialogsMan.size(); i++) {
out->writeByte(_dialogsMan[i]);
}
for (unsigned int i = _dialogsMan.size(); i < 200; i++) {
out->writeByte(0);
}
// Inventory
assert(_inventory.size() == 50);
for (Inventory::const_iterator it = _inventory.begin(); it != _inventory.end(); it++) {
unsigned int objId = -1;
if (*it != nullptr) {
// Inventory contains pointers to objects stored in _objects
objId = *it - _objects.begin();
}
out->writeUint32BE(objId);
}
// Offset of inventory in toolbar
out->writeUint32BE(_toolbar.inventoryOffset());
// Level, place, warp position
out->writeUint32BE(_currentLevel);
out->writeUint32BE(_currentPlaceId);
out->writeDoubleBE(_omni3dMan.getAlpha());
out->writeDoubleBE(_omni3dMan.getBeta());
// Places states
assert(_placeStates.size() < 100);
Common::Array<PlaceState>::const_iterator placeIt = _placeStates.begin();
for (unsigned int i = 0; placeIt != _placeStates.end(); placeIt++, i++) {
out->writeUint32BE(placeIt->state);
}
for (unsigned int i = _placeStates.size(); i < 100; i++) {
out->writeUint32BE(0);
}
// Game variables
assert(_gameVariables.size() < 100);
for (Common::Array<unsigned int>::const_iterator it = _gameVariables.begin();
it != _gameVariables.end(); it++) {
out->writeUint32BE(*it);
}
for (unsigned int i = _gameVariables.size(); i < 100; i++) {
out->writeUint32BE(0);
}
out->finalize();
delete out;
}
bool CryOmni3DEngine_Versailles::loadGame(bool visit, unsigned int saveNum) {
Common::SeekableReadStream *in;
if (visit && saveNum == 1) {
// Load bootstrap visit
const Common::FSNode gameDataDir(ConfMan.get("path"));
Common::SearchSet visitsSearchSet;
visitsSearchSet.addSubDirectoryMatching(gameDataDir, "datas_v/savegame/visite", 1);
Common::File *visitFile = new Common::File();
if (!visitFile->open("game0001.sav", visitsSearchSet)) {
delete visitFile;
error("Can't load visit file");
}
in = visitFile;
} else {
Common::String saveFileName = getSaveFileName(visit, saveNum);
#ifdef DEBUG_SAVE
in = _saveFileMan->openRawFile(saveFileName);
#else
in = _saveFileMan->openForLoading(saveFileName);
#endif
}
if (!in || in->size() != 1260) {
return false;
}
musicStop();
// Load save name but don't use it
char saveNameC[SAVE_DESCRIPTION_LEN];
in->read(saveNameC, sizeof(saveNameC));
// dummy values
in->readUint32LE();
in->readUint32BE();
in->readUint32BE();
// Dialog variables
assert(_dialogsMan.size() < 200);
for (unsigned int i = 0; i < _dialogsMan.size(); i++) {
_dialogsMan[i] = in->readByte();
}
for (unsigned int i = _dialogsMan.size(); i < 200; i++) {
in->readByte();
}
// Inventory
assert(_inventory.size() == 50);
for (Inventory::iterator it = _inventory.begin(); it != _inventory.end(); it++) {
unsigned int objId = in->readUint32BE();
if (objId >= _objects.size()) {
objId = -1;
}
if (objId != -1u) {
*it = _objects.begin() + objId;
} else {
*it = nullptr;
}
}
// Offset of inventory in toolbar
_toolbar.setInventoryOffset(in->readUint32BE());
// Level, place, warp position
_currentLevel = in->readUint32BE();
// Use nextPlace to force place move
_nextPlaceId = in->readUint32BE();
// Store alpha and beta for later use
double alpha = in->readDoubleBE();
double beta = in->readDoubleBE();
// Places states
// Store them and use them once we called initNewLevel, we can't call it before because it needs _gameVariables (and especially kCurrentTime) to be correctly set
uint32 placesStates[100];
for (unsigned int i = 0; i < 100; i++) {
placesStates[i] = in->readUint32BE();
}
// Game variables
assert(_gameVariables.size() < 100);
for (Common::Array<unsigned int>::iterator it = _gameVariables.begin(); it != _gameVariables.end();
it++) {
*it = in->readUint32BE();
}
for (unsigned int i = _gameVariables.size(); i < 100; i++) {
in->readUint32BE();
}
delete in;
if (_gameVariables[GameVariables::kCurrentTime] == 0) {
_gameVariables[GameVariables::kCurrentTime] = 1;
}
// Everything has been loaded, setup new level
// We will set places states and warp coordinates just after that to avoid them from being reset
initNewLevel(_currentLevel);
_omni3dMan.setAlpha(alpha);
_omni3dMan.setBeta(beta);
// _placeStates has just been resized in initNewLevel
unsigned int i = 0;
for (Common::Array<PlaceState>::iterator placeIt = _placeStates.begin();
placeIt != _placeStates.end() && i < ARRAYSIZE(placesStates); placeIt++, i++) {
placeIt->state = placesStates[i];
}
// TODO: countdown
return true;
}
} // End of namespace Versailles
} // End of namespace CryOmni3D

View File

@ -0,0 +1,599 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/system.h"
#include "cryomni3d/cryomni3d.h"
#include "cryomni3d/versailles/toolbar.h"
namespace CryOmni3D {
namespace Versailles {
void Toolbar::init(const Sprites *sprites, FontManager *fontManager,
const Common::Array<Common::String> *messages, Inventory *inventory,
CryOmni3DEngine *engine) {
_sprites = sprites;
_fontManager = fontManager;
_messages = messages;
_inventory = inventory;
_engine = engine;
_bgSurface.create(640, 60, Graphics::PixelFormat::createFormatCLUT8());
_destSurface.create(640, 60, Graphics::PixelFormat::createFormatCLUT8());
// Inventory
addZone(51, 56, Common::Point(211, 8), &Toolbar::callbackInventory<0>);
addZone(51, 56, Common::Point(258, 8), &Toolbar::callbackInventory<1>);
addZone(51, 56, Common::Point(305, 8), &Toolbar::callbackInventory<2>);
addZone(51, 56, Common::Point(352, 8), &Toolbar::callbackInventory<3>);
addZone(51, 56, Common::Point(399, 8), &Toolbar::callbackInventory<4>);
addZone(51, 56, Common::Point(446, 8), &Toolbar::callbackInventory<5>);
addZone(51, 56, Common::Point(493, 8), &Toolbar::callbackInventory<6>);
addZone(51, 56, Common::Point(540, 8), &Toolbar::callbackInventory<7>);
// Documentation
const Graphics::Cursor &cursorDoc = _sprites->getCursor(133);
Common::Point docPos(627 - cursorDoc.getWidth(), 42 - cursorDoc.getHeight());
addZone(133, 137, docPos, &Toolbar::callbackDocumentation);
// Options
const Graphics::Cursor &cursorOpt = _sprites->getCursor(225);
Common::Point optPos(0, 60 - cursorOpt.getHeight());
addZone(225, 225, optPos, &Toolbar::callbackOptions);
// Previous or next
addZone(183, -1, Common::Point(190, 18), &Toolbar::callbackInventoryPrev);
addZone(240, -1, Common::Point(574, 18), &Toolbar::callbackInventoryNext);
// View
addZone(142, -1, Common::Point(158, 12), &Toolbar::callbackViewObject);
}
Toolbar::~Toolbar() {
_bgSurface.free();
_destSurface.free();
}
void Toolbar::inventoryChanged(unsigned int newPosition) {
if (newPosition != -1u && newPosition > _inventoryOffset) {
_inventoryOffset = newPosition - 7;
}
// Refresh
updateZones();
}
void Toolbar::addZone(uint16 cursorMainId, uint16 cursorSecondaryId, Common::Point position,
ZoneCallback callback) {
const Graphics::Cursor &cursorMain = _sprites->getCursor(cursorMainId);
Common::Rect rct(cursorMain.getWidth(), cursorMain.getHeight());
rct.moveTo(position);
// By default it's the secondary image
Zone zone = { rct, cursorMainId, cursorSecondaryId, callback, true, false };
_zones.push_back(zone);
}
Common::Array<Toolbar::Zone>::const_iterator Toolbar::hitTestZones(const Common::Point &mousePos)
const {
Common::Array<Zone>::const_iterator it;
for (it = _zones.begin(); it != _zones.end(); it++) {
if (!it->hidden && it->rect.contains(mousePos) && it->callback) {
break;
}
}
return it;
}
unsigned int Toolbar::captureEvent(const Common::Point &mousePos, unsigned int dragStatus) {
unsigned int result = 0;
Common::Array<Zone>::const_iterator it = hitTestZones(mousePos);
if (it != _zones.end()) {
result = (this->*(it->callback))(dragStatus);
}
return result;
}
void Toolbar::updateZones() {
_zones[8].secondary = !_engine->hasPlaceDocumentation();
Inventory::const_iterator inventoryIt, inventorySelectedIt;
if (!_inventoryEnabled) {
_inventoryMaxOffset = 0;
_inventoryOffset = 0;
_zones[10].secondary = true;
_zones[11].secondary = true;
} else {
_inventoryMaxOffset = 0;
// Find an object in inventory after the 8 first
for (inventoryIt = _inventory->begin() + 8; inventoryIt != _inventory->end(); inventoryIt++) {
if (*inventoryIt != nullptr) {
_inventoryMaxOffset = (inventoryIt - _inventory->begin()) - 7;
}
}
_zones[10].secondary = !_inventoryMaxOffset;
_zones[11].secondary = !_inventoryMaxOffset;
if (_inventoryOffset > _inventoryMaxOffset) {
// Clamp inventory offset to its max
_inventoryOffset = _inventoryMaxOffset;
}
inventoryIt = _inventory->begin() + _inventoryOffset;
inventorySelectedIt = _inventory->begin() + _inventorySelected;
}
// Inventory zones are from 0 to 7
for (Common::Array<Zone>::iterator zoneIt = _zones.begin(); zoneIt != _zones.begin() + 8;
zoneIt++, inventoryIt++) {
if (!_inventoryEnabled) {
zoneIt->hidden = true;
zoneIt->imageMain = 0;
zoneIt->imageSecondary = 0;
zoneIt->secondary = false;
} else if (inventoryIt >= _inventory->end() || *inventoryIt == nullptr) {
// Nothing in inventory at this position
zoneIt->hidden = false;
zoneIt->imageMain = 51;
zoneIt->imageSecondary = 56;
zoneIt->secondary = true;
} else {
// Setup inventory icon
zoneIt->hidden = false;
zoneIt->imageMain = (*inventoryIt)->idCA();
zoneIt->imageSecondary = (*inventoryIt)->idCl();
zoneIt->secondary = (inventorySelectedIt != inventoryIt);
}
}
}
unsigned int Toolbar::callbackInventory(unsigned int invId, unsigned int dragStatus) {
if (!_inventoryEnabled) {
return 0;
}
invId += _inventoryOffset;
Object *obj = nullptr;
if (invId < _inventory->size()) {
obj = (*_inventory)[invId];
}
if (obj == nullptr) {
return 0;
}
if (!obj->valid()) {
return 0;
}
switch (dragStatus) {
case kDragStatus_Pressed:
_inventorySelected = invId;
_engine->setCursor(181);
_zones[12].secondary = (obj->viewCallback() == nullptr);
_inventory_button_dragging = true;
return 1;
case kDragStatus_Dragging:
if (_inventorySelected == invId) {
return 0;
}
_inventorySelected = invId;
_zones[12].secondary = (obj->viewCallback() == nullptr);
_inventory_button_dragging = true;
return 1;
case kDragStatus_Finished:
_engine->setCursor(obj->idSl());
_inventory->setSelectedObject(obj);
_inventorySelected = invId;
return 1;
default:
return 0;
}
}
unsigned int Toolbar::callbackInventoryPrev(unsigned int dragStatus) {
if (!_inventoryEnabled) {
return 0;
}
if (dragStatus == kDragStatus_Pressed && _inventoryOffset > 0) {
// Restart auto repeat only if there could be something
_engine->setAutoRepeatClick(150);
_inventoryOffset--;
return 1;
}
// In any other case we didn't do anything
return 0;
}
unsigned int Toolbar::callbackInventoryNext(unsigned int dragStatus) {
if (!_inventoryEnabled) {
return 0;
}
if (dragStatus == kDragStatus_Pressed && _inventoryOffset < _inventoryMaxOffset) {
_engine->setAutoRepeatClick(150);
_inventoryOffset++;
return 1;
}
// In any other case we didn't do anything
return 0;
}
unsigned int Toolbar::callbackViewObject(unsigned int dragStatus) {
if (!_inventoryEnabled) {
return 0;
}
_mouse_in_view_object = true;
if (_inventorySelected == -1u) {
// Nothing selected in toolbar
return 0;
}
Inventory::const_iterator inventorySelectedIt = _inventory->begin() + _inventorySelected;
Object *selectedObject = *inventorySelectedIt;
if (selectedObject == nullptr || selectedObject->viewCallback() == nullptr) {
// Nothing to view, the sprite isn't even displayed
return 0;
}
switch (dragStatus) {
case kDragStatus_NoDrag:
_backup_selected_object = selectedObject;
_engine->setCursor(181);
return 0;
case kDragStatus_Pressed:
case kDragStatus_Dragging:
return 1;
case kDragStatus_Finished:
// Just clicked
g_system->showMouse(false);
(*selectedObject->viewCallback())();
g_system->showMouse(true);
_parentMustRedraw = true;
_shortExit = true;
return 1;
default:
return 0;
}
}
unsigned int Toolbar::callbackOptions(unsigned int dragStatus) {
_mouse_in_options = true;
switch (dragStatus) {
case kDragStatus_NoDrag:
_backup_selected_object = _inventory->selectedObject();
_engine->setCursor(181);
return 0;
case kDragStatus_Pressed:
case kDragStatus_Dragging:
// Nothing to do, we wait release
return 0;
case kDragStatus_Finished:
// Just clicked
_engine->displayOptions();
_parentMustRedraw = true;
_shortExit = true;
_engine->setMousePos(Common::Point(320, 240)); // Center of screen
// Displaying options hides the mouse
g_system->showMouse(true);
return 0;
default:
return 0;
}
}
unsigned int Toolbar::callbackDocumentation(unsigned int dragStatus) {
_mouse_in_options = true;
switch (dragStatus) {
case kDragStatus_NoDrag:
case kDragStatus_Pressed:
case kDragStatus_Dragging:
// Nothing to do, we wait release
return 0;
case kDragStatus_Finished:
// Just clicked
if (_engine->displayPlaceDocumentation()) {
_parentMustRedraw = true;
_shortExit = true;
_engine->setMousePos(Common::Point(320, 240)); // Center of screen
}
return 0;
default:
return 0;
}
}
void Toolbar::drawToolbar(const Graphics::Surface *original) {
if (_position > 60) {
_position = 60;
}
if (_position != 0) {
// Not entirely drawn, we must copy a part of the original image
Common::Rect rct(0, 420, 640, 420 + _position);
_destSurface.copyRectToSurface(*original, 0, 0, rct);
}
if (_position == 60) {
// Entirely hidden, just stop there, we have nothing to draw
return;
}
// Not entirely hidden, we must display the transparent background prepared for us
Common::Rect rct(0, _position, 640, 60);
_destSurface.copyRectToSurface(_bgSurface, 0, _position, rct);
// Now draw the various zones on the surface
for (Common::Array<Zone>::const_iterator it = _zones.begin(); it != _zones.end(); it++) {
if (it->hidden) {
continue;
}
uint16 spriteId = it->secondary ? it->imageSecondary : it->imageMain;
if (spriteId == uint16(-1)) {
continue;
}
Common::Rect dst = it->rect;
dst.translate(0, _position);
// Clip the rectangle to fit inside the surface
dst.clip(Common::Rect(_destSurface.w, _destSurface.h));
if (dst.isEmpty()) {
continue;
}
const Graphics::Surface &sprite = _sprites->getSurface(spriteId);
_destSurface.transBlitFrom(sprite, Common::Rect(dst.width(), dst.height()), dst,
_sprites->getKeyColor(spriteId));
}
// And now draw the object description if needed
if (_inventoryEnabled && _inventoryHovered != -1u) {
Object *obj = (*_inventory)[_inventoryHovered];
unsigned int zoneId = _inventoryHovered - _inventoryOffset;
if (zoneId >= 8) {
// The object is hidden: huh?
return;
}
_fontManager->setSurface(&_destSurface);
_fontManager->setForeColor(243);
_fontManager->setCurrentFont(5);
_fontManager->setTransparentBackground(true);
const Common::String &objName = (*_messages)[obj->idOBJ()];
unsigned int x = 195 - _fontManager->getStrWidth(objName);
unsigned int startX = _zones[zoneId].rect.left + kTextOffset;
_fontManager->displayStr(x, 38 + _position, objName);
_destSurface.hLine(x, 54 + _position, startX - 1, 243); // minus 1 because hLine draws inclusive
_destSurface.vLine(startX, 42 + _position, 54 + _position, 243);
}
}
bool Toolbar::displayToolbar(const Graphics::Surface *original) {
/**
* In game there are 2 functions to handle toolbar: one in warp and one in fixed images
* This one is the warp one and fixed images have a more asynchronous one during pop-up/down phases
* Let's make it simple for now
*/
// WORKAROUND: Set cursor here to be more consistent: it's thumb cursor just before showing until just after showed
_engine->setCursor(181);
_parentMustRedraw = false;
_shortExit = false;
// Prepare the background of the toolbar by making it translucent
// Get the lowest part of the image
const Graphics::Surface subset = original->getSubArea(Common::Rect(0, original->h - _bgSurface.h,
_bgSurface.w, original->h));
_engine->makeTranslucent(_bgSurface, subset);
// Draw the original surface on the surface to use for toolbar
g_system->copyRectToScreen(original->getPixels(), original->pitch, 0, 0, original->w, original->h);
// WORKAROUND: Reset the inventory status at init to let sprites highlighted until toolbar is hidden
_inventorySelected = -1;
_inventoryHovered = -1;
_zones[12].secondary = true;
updateZones();
for (_position = 60; _position > 0; _position--) {
// Make the toolbar go up
drawToolbar(original);
g_system->copyRectToScreen(_destSurface.getPixels(), _destSurface.pitch, 0,
original->h - _destSurface.h, _destSurface.w, _destSurface.h);
g_system->updateScreen();
// pollEvents will slow down the animation because it waits 10ms
_engine->pollEvents();
if (g_engine->shouldQuit()) {
return false;
}
}
// Flush events
_engine->clearKeys();
_engine->waitMouseRelease();
handleToolbarEvents(original);
if (g_engine->shouldQuit()) {
return false;
}
if (_shortExit) {
return _parentMustRedraw;
}
for (_position = 0; _position <= 60; _position++) {
// Make the toolbar go up
drawToolbar(original);
g_system->copyRectToScreen(_destSurface.getPixels(), _destSurface.pitch, 0,
original->h - _destSurface.h, _destSurface.w, _destSurface.h);
g_system->updateScreen();
// pollEvents will slow down the animation because it waits 10ms
_engine->pollEvents();
if (g_engine->shouldQuit()) {
return false;
}
}
return _parentMustRedraw;
}
void Toolbar::handleToolbarEvents(const Graphics::Surface *original) {
bool mouseInsideToolbar;
bool exitToolbar = false;
bool redrawToolbar;
// Don't have anything hovered for now
_inventoryHovered = -1;
_inventorySelected = -1;
_inventory->setSelectedObject(nullptr);
_backup_selected_object = nullptr;
// Refresh zones because we erased selected object
updateZones();
// No need of original surface because the toolbar is fully displayed
drawToolbar(original);
// TODO: countdown
g_system->copyRectToScreen(_destSurface.getPixels(), _destSurface.pitch, 0,
original->h - _destSurface.h, _destSurface.w, _destSurface.h);
g_system->updateScreen();
_engine->setCursor(181);
mouseInsideToolbar = (_engine->getMousePos().y > 388);
while (!exitToolbar) {
_mouse_in_options = false;
_mouse_in_view_object = false;
_engine->pollEvents();
if (g_engine->shouldQuit()) {
exitToolbar = true;
break;
}
redrawToolbar = false;
if (_engine->checkKeysPressed(2, Common::KEYCODE_ESCAPE, Common::KEYCODE_SPACE) ||
_engine->getCurrentMouseButton() == 2) {
_engine->waitMouseRelease();
exitToolbar = true;
break;
}
Common::Point mousePosInToolbar = _engine->getMousePos();
mousePosInToolbar -= Common::Point(0, 420);
if (captureEvent(mousePosInToolbar, _engine->getDragStatus())) {
// Something has changed with the zones handling, update zones
updateZones();
redrawToolbar = true;
} else if (_engine->getDragStatus() == kDragStatus_Pressed) {
// A click happened and wasn't handled, deselect object
_inventorySelected = -1;
_inventory->setSelectedObject(nullptr);
_engine->setCursor(181);
// Reset view object
_zones[12].secondary = true;
updateZones();
redrawToolbar = true;
}
if (!mouseInsideToolbar) {
mouseInsideToolbar = (_engine->getMousePos().y > 388);
} else if (_engine->getMousePos().y <= 388) {
// mouseInsideToolbar is true and the mouse is outside the toolbar
exitToolbar = true;
break;
}
if (_engine->getCurrentMouseButton() == 1) {
// When the mouse button is down, nothing is selected
// It's selected on release
_inventory->setSelectedObject(nullptr);
}
if (_backup_selected_object != nullptr && !(_mouse_in_options || _mouse_in_view_object) &&
!_engine->getCurrentMouseButton()) {
_inventory->setSelectedObject(_backup_selected_object);
_engine->setCursor(_backup_selected_object->idSl());
_backup_selected_object = nullptr;
}
// Hover the inventory objects
if (_inventory->selectedObject() == nullptr /* || _inventory_button_dragging */) {
// The 2nd above condition is maybe useless because when the mouse button is down the selected object is always null
bool shouldHover = false;
Common::Array<Zone>::const_iterator zoneIt = hitTestZones(mousePosInToolbar);
unsigned int zoneId = zoneIt - _zones.begin();
unsigned int inventoryId = zoneId + _inventoryOffset;
if (zoneId < 8 && inventoryId < _inventory->size() && (*_inventory)[inventoryId] != nullptr) {
// It's the inventory
shouldHover = true;
if (_inventoryHovered != inventoryId && (*_inventory)[inventoryId]->valid()) {
// It's not the one currently hovered and it's a valid object
_inventoryHovered = inventoryId;
redrawToolbar = true;
}
}
if (!shouldHover && _inventoryHovered != -1u && !_mouse_in_view_object) {
// Remove hovering
_inventoryHovered = -1;
_inventorySelected = -1;
updateZones();
if (!_inventory->selectedObject()) {
// Reset back the cursor if nothing is selected
_engine->setCursor(181);
}
// Remove view
_zones[12].secondary = true;
redrawToolbar = true;
}
_inventory_button_dragging = false;
}
if (_parentMustRedraw) {
break;
}
if (redrawToolbar) {
drawToolbar(original);
g_system->copyRectToScreen(_destSurface.getPixels(), _destSurface.pitch, 0,
original->h - _destSurface.h, _destSurface.w, _destSurface.h);
}
g_system->updateScreen();
}
// Hide description when finished and selected object
// WORKAROUND: moved to the start to keep the selected object hilighted until the toolbar disappearance
}
} // End of namespace Versailles
} // End of namespace CryOmni3D

View File

@ -0,0 +1,117 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_VERSAILLES_TOOLBAR_H
#define CRYOMNI3D_VERSAILLES_TOOLBAR_H
#include "common/array.h"
#include "common/rect.h"
#include "graphics/managed_surface.h"
#include "graphics/surface.h"
#include "cryomni3d/font_manager.h"
#include "cryomni3d/objects.h"
#include "cryomni3d/sprites.h"
namespace CryOmni3D {
class CryOmni3DEngine;
namespace Versailles {
class Toolbar {
public:
Toolbar() : _sprites(nullptr), _fontManager(nullptr), _inventory(nullptr),
_messages(nullptr), _inventoryOffset(0), _engine(nullptr),
_inventoryHovered(-1), _inventorySelected(-1), _inventoryEnabled(true),
_position(60) { }
~Toolbar();
void init(const Sprites *sprites, FontManager *fontManager,
const Common::Array<Common::String> *messages, Inventory *inventory, CryOmni3DEngine *engine);
Graphics::Surface &getBackgroundSurface() { return _bgSurface; }
bool displayToolbar(const Graphics::Surface *original);
void inventoryChanged(unsigned int newPosition);
unsigned int inventoryOffset() const { return _inventoryOffset; }
void setInventoryOffset(unsigned int offset) { _inventoryOffset = offset; }
void setInventoryEnabled(bool enabled) { _inventoryEnabled = enabled; }
private:
typedef unsigned int (Toolbar::*ZoneCallback)(unsigned int dragStatus);
struct Zone {
Common::Rect rect;
uint16 imageMain;
uint16 imageSecondary;
ZoneCallback callback;
bool secondary;
bool hidden;
};
Common::Array<Zone> _zones;
const Sprites *_sprites;
FontManager *_fontManager;
const Common::Array<Common::String> *_messages;
Inventory *_inventory;
CryOmni3DEngine *_engine;
static const unsigned int kTextOffset = 13;
void addZone(uint16 cursorMainId, uint16 cursorSecondaryId, Common::Point position,
ZoneCallback callback);
void updateZones();
Common::Array<Zone>::const_iterator hitTestZones(const Common::Point &mousePos) const;
unsigned int captureEvent(const Common::Point &mousePos, unsigned int dragStatus);
void drawToolbar(const Graphics::Surface *original);
void handleToolbarEvents(const Graphics::Surface *original);
bool _inventoryEnabled;
unsigned int _inventoryMaxOffset;
unsigned int _inventoryOffset;
unsigned int _inventoryHovered;
unsigned int _inventorySelected;
Object *_backup_selected_object;
bool _mouse_in_options;
bool _mouse_in_view_object;
bool _inventory_button_dragging;
bool _parentMustRedraw;
bool _shortExit;
unsigned int _position;
Graphics::Surface _bgSurface;
Graphics::ManagedSurface _destSurface;
template<unsigned int N>
unsigned int callbackInventory(unsigned int dragStatus) { return callbackInventory(N, dragStatus); }
unsigned int callbackInventory(unsigned int invId, unsigned int dragStatus);
unsigned int callbackInventoryPrev(unsigned int dragStatus);
unsigned int callbackInventoryNext(unsigned int dragStatus);
unsigned int callbackViewObject(unsigned int dragStatus);
unsigned int callbackOptions(unsigned int dragStatus);
unsigned int callbackDocumentation(unsigned int dragStatus);
};
} // End of namespace Versailles
} // End of namespace CryOmni3D
#endif

View File

@ -0,0 +1,392 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/debug.h"
#include "common/endian.h"
#include "common/system.h"
#include "common/stream.h"
#include "common/file.h"
#include "common/textconsole.h"
#include "audio/decoders/raw.h"
#include "cryomni3d/video/hnm_decoder.h"
#include "cryomni3d/image/codecs/hlz.h"
namespace Video {
// When no sound display a frame every 80ms
HNMDecoder::HNMDecoder(bool loop) : _regularFrameDelay(80), _videoTrack(nullptr),
_audioTrack(nullptr), _stream(nullptr), _loop(loop) {
}
HNMDecoder::~HNMDecoder() {
close();
// We don't deallocate _videoTrack and _audioTrack as they are owned by base class
}
bool HNMDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
uint32 tag = stream->readUint32BE();
/* For now, only HNM4, HNM6 in the future */
if (tag != MKTAG('H', 'N', 'M', '4')) {
close();
return false;
}
//uint32 ukn = stream->readUint32BE();
stream->skip(4);
uint16 width = stream->readUint16LE();
uint16 height = stream->readUint16LE();
//uint32 filesize = stream->readUint32LE();
stream->skip(4);
uint32 frameCount = stream->readUint32LE();
//uint32 tabOffset = stream->readUint32LE();
stream->skip(4);
uint16 soundBits = stream->readUint16LE();
uint16 soundChannels = stream->readUint16LE();
uint32 frameSize = stream->readUint32LE();
char unknownStr[16];
char copyright[16];
stream->read(unknownStr, sizeof(unknownStr));
stream->read(copyright, sizeof(copyright));
if (_loop) {
// This will force loop mode
frameCount = 0;
}
_videoTrack = new HNM4VideoTrack(width, height, frameSize, frameCount, _regularFrameDelay);
if (soundBits != 0 && soundChannels != 0) {
// HNM4 is 22050Hz
_audioTrack = new DPCMAudioTrack(soundChannels, soundBits, 22050, getSoundType());
} else {
_audioTrack = nullptr;
}
addTrack(_videoTrack);
addTrack(_audioTrack);
_stream = stream;
return true;
}
void HNMDecoder::close() {
VideoDecoder::close();
// Tracks are cleant by VideoDecoder::close
_videoTrack = nullptr;
_audioTrack = nullptr;
delete _stream;
_stream = nullptr;
}
void HNMDecoder::readNextPacket() {
// We are called to feed a frame
// Each chunk is packetized and a packet seems to contain only one frame
uint32 superchunkRemaining = _stream->readUint32LE();
if (!superchunkRemaining) {
if (!_loop) {
error("End of file but still requesting data");
} else {
// Looping: read back from start of file, skip header and read a new super chunk header
_videoTrack->restart();
_stream->seek(64, SEEK_SET);
superchunkRemaining = _stream->readUint32LE();
}
}
superchunkRemaining = (superchunkRemaining & 0x00ffffff) - 4;
while (superchunkRemaining) {
uint32 chunkSize = _stream->readUint32LE();
uint16 chunkType = _stream->readUint16BE();
//uint16 ukn = _stream->readUint16LE();
_stream->skip(2);
if (chunkType == MKTAG16('P', 'L')) {
_videoTrack->decodePalette(_stream, chunkSize - 8);
} else if (chunkType == MKTAG16('I', 'Z')) {
_stream->skip(4);
_videoTrack->decodeIntraframe(_stream, chunkSize - 8 - 4);
} else if (chunkType == MKTAG16('I', 'U')) {
_videoTrack->decodeInterframe(_stream, chunkSize - 8);
} else if (chunkType == MKTAG16('S', 'D')) {
if (_audioTrack) {
Audio::Timestamp duration = _audioTrack->decodeSound(_stream, chunkSize - 8);
_videoTrack->setFrameDelay(duration.msecs());
} else {
error("Shouldn't have audio data");
}
} else {
error("Got %d chunk: size %d", chunkType, chunkSize);
}
superchunkRemaining -= chunkSize;
}
}
HNMDecoder::HNM4VideoTrack::HNM4VideoTrack(uint32 width, uint32 height, uint32 frameSize,
uint32 frameCount, uint32 regularFrameDelay) :
_frameCount(frameCount), _regularFrameDelay(regularFrameDelay), _nextFrameStartTime(0) {
restart();
_curFrame = -1;
memset(_palette, 0, 256 * 3);
if (width * height != frameSize) {
error("Invalid frameSize");
}
// We will use _frameBufferC/P as the surface pixels, just init there with nullptr to avoid unintended usage of surface
const Graphics::PixelFormat &f = Graphics::PixelFormat::createFormatCLUT8();
_surface.init(width, height, width * f.bytesPerPixel, nullptr, f);
_frameBufferC = new byte[frameSize];
memset(_frameBufferC, 0, frameSize);
_frameBufferP = new byte[frameSize];
memset(_frameBufferP, 0, frameSize);
}
HNMDecoder::HNM4VideoTrack::~HNM4VideoTrack() {
// Don't free _surface as we didn't used create() but init()
delete[] _frameBufferC;
_frameBufferC = nullptr;
delete[] _frameBufferP;
_frameBufferP = nullptr;
}
void HNMDecoder::HNM4VideoTrack::setFrameDelay(uint32 frameDelay) {
if (_nextFrameDelay == -1u) {
_nextFrameDelay = frameDelay;
} else if (_nextNextFrameDelay == -1u) {
_nextNextFrameDelay = frameDelay;
} else {
_nextNextFrameDelay += frameDelay;
}
}
void HNMDecoder::HNM4VideoTrack::decodePalette(Common::SeekableReadStream *stream, uint32 size) {
while (true) {
if (size < 2) {
break;
}
unsigned int start = stream->readByte();
unsigned int count = stream->readByte();
size -= 2;
if (start == 255 && count == 255) {
break;
}
if (count == 0) {
count = 256;
}
if (size < count * 3) {
error("Invalid palette chunk data");
}
if (start + count > 256) {
error("Invalid palette start/count values");
}
size -= count * 3;
byte *palette_ptr = &_palette[start * 3];
for (; count > 0; count--) {
byte r = stream->readByte();
byte g = stream->readByte();
byte b = stream->readByte();
*(palette_ptr++) = r * 4;
*(palette_ptr++) = g * 4;
*(palette_ptr++) = b * 4;
}
}
_dirtyPalette = true;
if (size > 0) {
stream->skip(size);
}
}
void HNMDecoder::HNM4VideoTrack::decodeInterframe(Common::SeekableReadStream *stream, uint32 size) {
SWAP(_frameBufferC, _frameBufferP);
uint16 width = _surface.w;
bool eop = false;
unsigned int currentPos = 0;
while (!eop) {
if (size < 1) {
warning("Not enough data in chunk for interframe block");
break;
}
byte countFlgs = stream->readByte();
size -= 1;
byte count = countFlgs & 0x3f;
byte flgs = (countFlgs >> 6) & 0x3;
if (count == 0) {
byte c;
switch (flgs) {
case 0:
if (size < 1) {
error("Not enough data for case 0");
}
// Move in image
c = stream->readByte();
currentPos += c;
size -= 1;
break;
case 1:
if (size < 1) {
error("Not enough data for case 1");
}
// New pixels
c = stream->readByte();
_frameBufferC[currentPos] = c;
c = stream->readByte();
_frameBufferC[currentPos + width] = c;
currentPos++;
size -= 2;
break;
case 2:
// New line
currentPos += width;
break;
case 3:
// End of picture
eop = true;
break;
}
} else {
if (size < 2) {
error("Not enough data for count > 0");
}
bool negative = (flgs & 0x2) != 0;
bool previous = (flgs & 0x1) != 0;
int offset = stream->readUint16LE();
size -= 2;
if (negative) {
offset -= 0x10000;
}
offset += currentPos;
if (offset < 0) {
error("Invalid offset");
}
byte *ptr;
if (previous) {
ptr = _frameBufferP;
} else {
ptr = _frameBufferC;
}
for (; count > 0; count--) {
_frameBufferC[currentPos] = ptr[offset];
_frameBufferC[currentPos + width] = ptr[offset + width];
currentPos++;
offset++;
}
}
}
_surface.setPixels(_frameBufferC);
_curFrame++;
_nextFrameStartTime += _nextFrameDelay != -1u ? _nextFrameDelay : _regularFrameDelay;
_nextFrameDelay = _nextNextFrameDelay;
_nextNextFrameDelay = -1u;
if (size > 0) {
stream->skip(size);
}
}
void HNMDecoder::HNM4VideoTrack::decodeIntraframe(Common::SeekableReadStream *stream, uint32 size) {
Image::HLZDecoder::decodeFrameInPlace(*stream, size, _frameBufferC);
memcpy(_frameBufferP, _frameBufferC, _surface.w * _surface.h);
_surface.setPixels(_frameBufferC);
_curFrame++;
_nextFrameStartTime += _nextFrameDelay != -1u ? _nextFrameDelay : _regularFrameDelay;
_nextFrameDelay = _nextNextFrameDelay;
_nextNextFrameDelay = -1u;
}
HNMDecoder::DPCMAudioTrack::DPCMAudioTrack(uint16 channels, uint16 bits, unsigned int sampleRate,
Audio::Mixer::SoundType soundType) : AudioTrack(soundType), _channels(channels), _bits(bits),
_audioStream(nullptr), _gotLUT(false), _lastSample(0) {
if (bits != 16) {
error("Unsupported audio bits");
}
if (channels != 2) {
warning("Unsupported %d audio channels", channels);
}
_audioStream = Audio::makeQueuingAudioStream(sampleRate, false);
}
HNMDecoder::DPCMAudioTrack::~DPCMAudioTrack() {
delete _audioStream;
}
Audio::Timestamp HNMDecoder::DPCMAudioTrack::decodeSound(Common::SeekableReadStream *stream,
uint32 size) {
if (!_gotLUT) {
if (size < 256 * sizeof(*_lut)) {
error("Invalid first sound chunk");
}
stream->read(_lut, 256 * sizeof(*_lut));
size -= 256 * sizeof(*_lut);
#ifndef SCUMM_LITTLE_ENDIAN
for (unsigned int i = 0; i < 256; i++) {
_lut[i] = FROM_LE_16(_lut[i]);
}
#endif
_gotLUT = true;
}
if (size > 0) {
uint16 *out = (uint16 *)malloc(size * sizeof(*out));
uint16 *p = out;
uint16 sample = _lastSample;
for (uint32 i = 0; i < size; i++, p++) {
byte deltaId = stream->readByte();
sample += _lut[deltaId];
*p = sample;
}
_lastSample = sample;
byte flags = Audio::FLAG_16BITS;
#ifdef SCUMM_LITTLE_ENDIAN
flags |= Audio::FLAG_LITTLE_ENDIAN;
#endif
// channels is 2 but we are MONO!
_audioStream->queueBuffer((byte *)out, size * sizeof(*out), DisposeAfterUse::YES, flags);
}
return Audio::Timestamp(0, size, 22050);
}
} // End of namespace Video

View File

@ -0,0 +1,129 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_VIDEO_HNM_DECODER_H
#define CRYOMNI3D_VIDEO_HNM_DECODER_H
#include "common/rational.h"
#include "graphics/pixelformat.h"
#include "video/video_decoder.h"
#include "graphics/surface.h"
#include "audio/audiostream.h"
namespace Common {
class SeekableReadStream;
}
namespace Video {
/**
* Decoder for HNM videos.
*
* Video decoder used in engines:
* - cryomni3d
*/
class HNMDecoder : public VideoDecoder {
public:
HNMDecoder(bool loop = false);
virtual ~HNMDecoder();
bool loadStream(Common::SeekableReadStream *stream);
void readNextPacket();
void close();
void setRegularFrameDelay(uint32 regularFrameDelay) { _regularFrameDelay = regularFrameDelay; }
private:
class HNM4VideoTrack : public VideoTrack {
public:
HNM4VideoTrack(uint32 width, uint32 height, uint32 frameSize, uint32 frameCount,
uint32 regularFrameDelay);
~HNM4VideoTrack();
// When _frameCount is 0, it means we are looping
bool endOfTrack() const { return (_frameCount == 0) ? false : VideoTrack::endOfTrack(); }
uint16 getWidth() const { return _surface.w; }
uint16 getHeight() const { return _surface.h; }
Graphics::PixelFormat getPixelFormat() const { return _surface.format; }
int getCurFrame() const { return _curFrame; }
int getFrameCount() const { return _frameCount; }
uint32 getNextFrameStartTime() const { return _nextFrameStartTime; }
const Graphics::Surface *decodeNextFrame() { return &_surface; }
const byte *getPalette() const { _dirtyPalette = false; return _palette; }
bool hasDirtyPalette() const { return _dirtyPalette; }
/** Decode a video chunk. */
void decodePalette(Common::SeekableReadStream *stream, uint32 size);
void decodeInterframe(Common::SeekableReadStream *stream, uint32 size);
void decodeIntraframe(Common::SeekableReadStream *stream, uint32 size);
void restart() { _nextFrameDelay = -1; _nextNextFrameDelay = -1; }
void setFrameDelay(uint32 frameDelay);
private:
Graphics::Surface _surface;
uint32 _regularFrameDelay;
uint32 _nextFrameDelay;
uint32 _nextNextFrameDelay;
uint32 _nextFrameStartTime;
uint32 _frameCount;
int _curFrame;
byte _palette[256 * 3];
mutable bool _dirtyPalette;
byte *_frameBufferC;
byte *_frameBufferP;
};
class DPCMAudioTrack : public AudioTrack {
public:
DPCMAudioTrack(uint16 channels, uint16 bits, unsigned int sampleRate,
Audio::Mixer::SoundType soundType);
~DPCMAudioTrack();
Audio::Timestamp decodeSound(Common::SeekableReadStream *stream, uint32 size);
protected:
Audio::AudioStream *getAudioStream() const { return _audioStream; }
private:
uint16 _channels;
uint16 _bits;
Audio::QueuingAudioStream *_audioStream;
bool _gotLUT;
uint16 _lut[256];
uint16 _lastSample;
};
bool _loop;
uint32 _regularFrameDelay;
// These two pointer are owned by VideoDecoder
HNM4VideoTrack *_videoTrack;
DPCMAudioTrack *_audioTrack;
Common::SeekableReadStream *_stream;
};
} // End of namespace Video
#endif

View File

@ -0,0 +1,216 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/stream.h"
#include "engines/cryomni3d/wam_parser.h"
#include "engines/cryomni3d/omni3d.h"
namespace CryOmni3D {
void WAMParser::loadStream(Common::ReadStream &stream) {
char str[16];
_places.clear();
stream.readByte();
stream.readByte();
stream.read(str, 16);
stream.readUint32LE();
unsigned int nPlaces = stream.readByte();
//debug("nPlaces = %u", nPlaces);
for (unsigned int i = 0; i < nPlaces; i++) {
Place place;
unsigned int nWarps = stream.readByte();
//debug("nWarps = %u", nWarps);
for (unsigned int k = 0; k < 8; k++) {
stream.read(str, 16);
//debug("Warp: %.16s", str);
if (nWarps > 0) {
place.warps.push_back(str);
nWarps--;
}
}
place.placeId = stream.readUint32LE();
// Normally placeId should be unique but it's not always the case
// In original game the last place is considered but we try to be efficient and stop at the first place in findPlaceById
// Let's be a little less efficient at startup by removing duplicates
Place *oldPlace = findPlaceById_(place.placeId);
if (oldPlace) {
debug("Found duplicate place %u at %u, removing it", place.placeId,
(unsigned int)(oldPlace - _places.begin()));
_places.erase(oldPlace);
}
//debug("nPlaceId = %u", place.placeId);
stream.readUint32LE();
unsigned int nTransitions = stream.readByte();
//debug("nTransitions = %u", nTransitions);
unsigned int nZones = stream.readByte();
//debug("nZones = %u", nZones);
for (unsigned int j = 0; j < nTransitions; j++) {
Transition trans;
stream.readUint32LE();
unsigned int nAnimations = stream.readByte();
for (unsigned int k = 0; k < 8; k++) {
stream.read(str, 16);
if (nAnimations > 0) {
trans.animations.push_back(str);
nAnimations--;
}
}
stream.readUint32LE();
trans.dstId = stream.readUint32LE();
stream.readByte();
trans.srcAlpha = stream.readDoubleLE();
trans.srcBeta = stream.readDoubleLE();
trans.dstAlpha = stream.readDoubleLE();
trans.dstBeta = stream.readDoubleLE();
place.transitions.push_back(trans);
}
for (unsigned int j = 0; j < nZones; j++) {
Zone zone;
zone.zoneId = stream.readSint32LE();
zone.rct.left = stream.readSint32LE();
zone.rct.top = stream.readSint32LE();
zone.rct.setWidth(stream.readSint32LE());
zone.rct.setHeight(stream.readSint32LE());
zone.action = stream.readSint32LE();
place.zones.push_back(zone);
}
_places.push_back(place);
}
}
const Place *WAMParser::findPlaceById(unsigned int placeId) const {
for (Common::Array<Place>::const_iterator it = _places.begin(); it != _places.end(); it++) {
if (it->placeId == placeId) {
return it;
}
}
return nullptr;
}
Place *WAMParser::findPlaceById_(unsigned int placeId) {
for (Common::Array<Place>::iterator it = _places.begin(); it != _places.end(); it++) {
if (it->placeId == placeId) {
return it;
}
}
return nullptr;
}
void Place::setupWarpConstraints(Omni3DManager &omni3d) const {
int16 iAlphaMin, iAlphaMax;
bool alphaConstraint = false;
omni3d.clearConstraints();
for (Common::Array<Zone>::const_iterator it = zones.begin(); it != zones.end(); it++) {
if (it->action == 100000) {
int16 aMin = it->rct.left;
if (aMin < 0) {
aMin += 2048;
}
int16 aMax = aMin + it->rct.width();
if (aMax > 2048) {
aMax -= 2048;
}
// debug("x1=%d x2=%d", aMin, aMax);
if (aMax < aMin) {
int16 tmp = aMax;
aMax = aMin;
aMin = tmp;
}
if (alphaConstraint) {
if (aMin < iAlphaMax && aMax > iAlphaMax) {
iAlphaMax = aMax;
}
if (aMin < iAlphaMin && aMax > iAlphaMin) {
iAlphaMin = aMin;
}
} else {
iAlphaMin = aMin;
iAlphaMax = aMax;
alphaConstraint = true;
}
} else if (it->action == 200000) {
double betaMin = ((int)it->rct.bottom - (768 / 2)) / 768. * M_PI;
omni3d.setBetaMinConstraint(betaMin);
} else if (it->action == 300000) {
double betaMax = ((int)it->rct.top - (768 / 2)) / 768. * M_PI;
omni3d.setBetaMaxConstraint(betaMax);
}
}
if (alphaConstraint) {
double alphaMin = (1 - iAlphaMin / 2048.) * M_PI * 2.;
alphaMin += 75. / 180. * M_PI_2;
if (alphaMin < 0.) {
alphaMin += M_PI * 2.;
} else if (alphaMin > M_PI * 2.) {
alphaMin -= M_PI * 2.;
}
double alphaMax = (1 - iAlphaMax / 2048.) * M_PI * 2.;
alphaMax -= 75. / 180. * M_PI_2;
if (alphaMax < 0.) {
alphaMax += M_PI * 2.;
} else if (alphaMax > M_PI * 2.) {
alphaMax -= M_PI * 2.;
}
omni3d.setAlphaConstraints(alphaMin, alphaMax);
}
}
unsigned int Place::hitTest(const Common::Point &point) const {
for (Common::Array<Zone>::const_iterator it = zones.begin(); it != zones.end(); it++) {
if (it->action) {
if (it->rct.contains(point)) {
return it->action;
}
if (it->rct.left < 0) {
Common::Rect rct = it->rct;
rct.translate(2048, 0);
if (rct.contains(point)) {
return it->action;
}
} else if (it->rct.right > 2048) {
Common::Rect rct = it->rct;
rct.translate(-2048, 0);
if (rct.contains(point)) {
return it->action;
}
}
}
}
return 0;
}
const Transition *Place::findTransition(unsigned int nextPlaceId) const {
for (Common::Array<Transition>::const_iterator it = transitions.begin(); it != transitions.end();
it++) {
if (it->dstId == nextPlaceId) {
return it;
}
}
return nullptr;
}
} // End of namespace CryOmni3D

View File

@ -0,0 +1,81 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef CRYOMNI3D_WAM_PARSER_H
#define CRYOMNI3D_WAM_PARSER_H
#include "common/array.h"
#include "common/rect.h"
#include "common/str.h"
namespace Common {
class ReadStream;
}
namespace CryOmni3D {
class Omni3DManager;
struct Zone {
unsigned int zoneId;
unsigned int action;
Common::Rect rct;
};
struct Transition {
unsigned int dstId;
double srcAlpha;
double srcBeta;
double dstAlpha;
double dstBeta;
Common::Array<Common::String> animations;
unsigned int getNumAnimations() const { return animations.size(); }
};
struct Place {
unsigned int placeId;
Common::Array<Common::String> warps;
Common::Array<Transition> transitions;
Common::Array<Zone> zones;
unsigned int getNumStates() const { return warps.size(); }
unsigned int getNumTransitions() const { return transitions.size(); }
void setupWarpConstraints(Omni3DManager &omni3d) const;
unsigned int hitTest(const Common::Point &point) const;
const Transition *findTransition(unsigned int nextPlaceId) const;
};
class WAMParser {
public:
void loadStream(Common::ReadStream &stream);
const Place *findPlaceById(unsigned int placeId) const;
private:
// For duplicate finding
// We use a different name because else it gets chosen before the const one and fails because it's private
Place *findPlaceById_(unsigned int placeId);
Common::Array<Place> _places;
};
} // End of namespace CryOmni3D
#endif