Initial commit
This commit is contained in:
1
engines/sword25/POTFILES
Normal file
1
engines/sword25/POTFILES
Normal file
@@ -0,0 +1 @@
|
||||
engines/sword25/metaengine.cpp
|
||||
3
engines/sword25/configure.engine
Normal file
3
engines/sword25/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine sword25 "Broken Sword 2.5" yes "" "" "png 16bit highres theoradec lua" ""
|
||||
34
engines/sword25/console.cpp
Normal file
34
engines/sword25/console.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/console.h"
|
||||
#include "sword25/sword25.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Sword25Console::Sword25Console(Sword25Engine *vm) : GUI::Debugger(), _vm(vm) {
|
||||
assert(_vm);
|
||||
}
|
||||
|
||||
Sword25Console::~Sword25Console() {
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
42
engines/sword25/console.h
Normal file
42
engines/sword25/console.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_CONSOLE_H
|
||||
#define SWORD25_CONSOLE_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Sword25Engine;
|
||||
|
||||
class Sword25Console : public GUI::Debugger {
|
||||
public:
|
||||
Sword25Console(Sword25Engine *vm);
|
||||
~Sword25Console(void) override;
|
||||
|
||||
private:
|
||||
Sword25Engine *_vm;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
7
engines/sword25/credits.pl
Normal file
7
engines/sword25/credits.pl
Normal file
@@ -0,0 +1,7 @@
|
||||
begin_section("Sword2.5");
|
||||
add_person("Torbjörn Andersson", "eriktorbjorn", "");
|
||||
add_person("Paul Gilbert", "dreammaster", "");
|
||||
add_person("Max Horn", "Fingolfin", "(retired)");
|
||||
add_person("Filippos Karapetis", "bluegr", "");
|
||||
add_person("Eugene Sandulenko", "sev", "");
|
||||
end_section();
|
||||
71
engines/sword25/detection.cpp
Normal file
71
engines/sword25/detection.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "sword25/detection.h"
|
||||
#include "sword25/detection_tables.h"
|
||||
#include "sword25/sword25.h"
|
||||
|
||||
static const PlainGameDescriptor sword25Game[] = {
|
||||
{"sword25", "Broken Sword 2.5: The Return of the Templars"},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{Sword25::kDebugScript, "Script", "Script debug level"},
|
||||
{Sword25::kDebugSound, "Sound", "Sound debug level"},
|
||||
{Sword25::kDebugInternalDebugger, "Internaldebugger", "Internal debugger level"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
static const char *const directoryGlobs[] = {
|
||||
"system", // Used by extracted dats
|
||||
0
|
||||
};
|
||||
|
||||
class Sword25MetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
|
||||
public:
|
||||
Sword25MetaEngineDetection() : AdvancedMetaEngineDetection(Sword25::gameDescriptions, sword25Game) {
|
||||
_guiOptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_ENGLISH_SPEECH);
|
||||
_maxScanDepth = 2;
|
||||
_directoryGlobs = directoryGlobs;
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "sword25";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Broken Sword 2.5";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Broken Sword 2.5 (C) Malte Thiesen, Daniel Queteschiner and Michael Elsdorfer";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN_STATIC(SWORD25_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, Sword25MetaEngineDetection);
|
||||
35
engines/sword25/detection.h
Normal file
35
engines/sword25/detection.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_DETECTION_H
|
||||
#define SWORD25_DETECTION_H
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
enum GameFlags {
|
||||
GF_EXTRACTED = 1 << 0
|
||||
};
|
||||
|
||||
#define GAMEOPTION_ENGLISH_SPEECH GUIO_GAMEOPTIONS1
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif // SWORD25_DETECTION_H
|
||||
159
engines/sword25/detection_tables.h
Normal file
159
engines/sword25/detection_tables.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
static const ADGameDescription gameDescriptions[] = {
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("data.b25c", "f8b6e03ada2d2f6cf27fbc11ad1572e9", 654310588),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("lang_fr.b25c", "690caf157387e06d2c3d1ca53c43f428", 1006043),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("data.b25c", "f8b6e03ada2d2f6cf27fbc11ad1572e9", 654310588),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("lang_hr.b25c", "e881054d1f8ec1e527422fc521c25405", 1273217),
|
||||
Common::HR_HRV,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("lang_it.b25c", "f3325666da0515cc2b42062e953c0889", 996197),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("lang_pl.b25c", "49dc1a20f95391a808e475c49be2bac0", 1281799),
|
||||
Common::PL_POL,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("lang_pt.b25c", "1df701432f9e13dcefe1adeb890b9c69", 993812),
|
||||
Common::PT_BRA,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("lang_ru.b25c", "deb33dd2f90a71ff60181918a8ce5063", 1235378),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("lang_es.b25c", "384c19072d83725f351bb9ecb4d3f02b", 987965),
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
{
|
||||
"sword25",
|
||||
"",
|
||||
AD_ENTRY1s("lang_he.b25c","993604772b9c7d9d54219d6993ab9c6c", 1214843),
|
||||
Common::HE_ISR,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
// Hungarian "psylog" version.
|
||||
// Submitted by goodoldgeorg in bug report #5888.
|
||||
{
|
||||
"sword25",
|
||||
"psylog version",
|
||||
AD_ENTRY1s("lang_hu.b25c", "7de51a3b4926a192549e75b1a7d81667", 1864915),
|
||||
Common::HU_HUN,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
|
||||
// Extracted version
|
||||
{
|
||||
"sword25",
|
||||
"Extracted",
|
||||
{{"_includes.lua", 0, 0, AD_NO_SIZE},
|
||||
{"boot.lua", 0, 0, AD_NO_SIZE},
|
||||
{"kernel.lua", 0, 0, AD_NO_SIZE},
|
||||
AD_LISTEND},
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformUnknown,
|
||||
GF_EXTRACTED | ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
|
||||
// Distributed by ScummVM
|
||||
// Contains all language packs, English voice-overs and Hungarian version
|
||||
// Mark it as Unknown Language since it contains multiple languages. If we
|
||||
// mark it as English, then changing the language in-game causes the detection
|
||||
// to fail the next time we try to start the engine.
|
||||
{
|
||||
"sword25",
|
||||
"Latest version",
|
||||
AD_ENTRY1s("data.b25c", "880a8a67faf4a4e7ab62cf114b771428", 827397764),
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformUnknown,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NOASPECT)
|
||||
},
|
||||
|
||||
AD_TABLE_END_MARKER
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
229
engines/sword25/fmv/movieplayer.cpp
Normal file
229
engines/sword25/fmv/movieplayer.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "sword25/sword25.h" // for kDebugScript
|
||||
#include "sword25/fmv/movieplayer.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "sword25/gfx/panel.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
#ifndef FLT_EPSILON
|
||||
#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON != 1.0 */
|
||||
#endif
|
||||
|
||||
#ifdef USE_THEORADEC
|
||||
MoviePlayer::MoviePlayer(Kernel *pKernel) : Service(pKernel), _decoder() {
|
||||
if (!registerScriptBindings())
|
||||
error("Script bindings could not be registered.");
|
||||
else
|
||||
debugC(kDebugScript, "Script bindings registered.");
|
||||
}
|
||||
|
||||
MoviePlayer::~MoviePlayer() {
|
||||
_decoder.close();
|
||||
}
|
||||
|
||||
bool MoviePlayer::loadMovie(const Common::String &filename, uint z) {
|
||||
if (isMovieLoaded())
|
||||
unloadMovie();
|
||||
// Get the file and load it into the decoder
|
||||
Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(filename);
|
||||
_decoder.loadStream(in);
|
||||
_decoder.start();
|
||||
|
||||
GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
|
||||
|
||||
#ifdef THEORA_INDIRECT_RENDERING
|
||||
_outputBitmap = pGfx->getMainPanel()->addDynamicBitmap(_decoder.getWidth(), _decoder.getHeight());
|
||||
if (!_outputBitmap.isValid()) {
|
||||
error("Output bitmap for movie playback could not be created.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute the scaling of the output bitmap, so that it takes up the most space
|
||||
float screenToVideoWidth = (float)pGfx->getDisplayWidth() / (float)_outputBitmap->getWidth();
|
||||
float screenToVideoHeight = (float)pGfx->getDisplayHeight() / (float)_outputBitmap->getHeight();
|
||||
float scaleFactor = MIN(screenToVideoWidth, screenToVideoHeight);
|
||||
|
||||
if (abs((int)(scaleFactor - 1.0f)) < FLT_EPSILON)
|
||||
scaleFactor = 1.0f;
|
||||
|
||||
_outputBitmap->setScaleFactor(scaleFactor);
|
||||
_outputBitmap->setZ(z);
|
||||
|
||||
// Center bitmap on screen
|
||||
_outputBitmap->setX((pGfx->getDisplayWidth() - _outputBitmap->getWidth()) / 2);
|
||||
_outputBitmap->setY((pGfx->getDisplayHeight() - _outputBitmap->getHeight()) / 2);
|
||||
#else
|
||||
_backSurface = pGfx->getSurface();
|
||||
|
||||
_outX = (pGfx->getDisplayWidth() - _decoder.getWidth()) / 2;
|
||||
_outY = (pGfx->getDisplayHeight() - _decoder.getHeight()) / 2;
|
||||
|
||||
if (_outX < 0)
|
||||
_outX = 0;
|
||||
if (_outY < 0)
|
||||
_outY = 0;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoviePlayer::unloadMovie() {
|
||||
_decoder.close();
|
||||
_outputBitmap.erase();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoviePlayer::play() {
|
||||
_decoder.pauseVideo(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoviePlayer::pause() {
|
||||
_decoder.pauseVideo(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MoviePlayer::update() {
|
||||
if (_decoder.isVideoLoaded()) {
|
||||
if (_decoder.endOfVideo()) {
|
||||
// Movie complete, so unload the movie
|
||||
unloadMovie();
|
||||
} else if (_decoder.needsUpdate()) {
|
||||
const Graphics::Surface *s = _decoder.decodeNextFrame();
|
||||
if (s) {
|
||||
// Transfer the next frame
|
||||
assert(s->format.bytesPerPixel == 4);
|
||||
|
||||
#ifdef THEORA_INDIRECT_RENDERING
|
||||
const byte *frameData = (const byte *)s->getPixels();
|
||||
_outputBitmap->setContent(frameData, s->pitch * s->h, 0, s->pitch);
|
||||
#else
|
||||
g_system->copyRectToScreen(s->getPixels(), s->pitch, _outX, _outY, MIN(s->w, _backSurface->w), MIN(s->h, _backSurface->h));
|
||||
g_system->updateScreen();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MoviePlayer::isMovieLoaded() {
|
||||
return _decoder.isVideoLoaded();
|
||||
}
|
||||
|
||||
bool MoviePlayer::isPaused() {
|
||||
return _decoder.isPaused() || _decoder.endOfVideo();
|
||||
}
|
||||
|
||||
float MoviePlayer::getScaleFactor() {
|
||||
if (_decoder.isVideoLoaded())
|
||||
return _outputBitmap->getScaleFactorX();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MoviePlayer::setScaleFactor(float scaleFactor) {
|
||||
if (_decoder.isVideoLoaded()) {
|
||||
_outputBitmap->setScaleFactor(scaleFactor);
|
||||
|
||||
// Ausgabebitmap auf dem Bildschirm zentrieren
|
||||
GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
|
||||
_outputBitmap->setX((gfxPtr->getDisplayWidth() - _outputBitmap->getWidth()) / 2);
|
||||
_outputBitmap->setY((gfxPtr->getDisplayHeight() - _outputBitmap->getHeight()) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
double MoviePlayer::getTime() {
|
||||
return _decoder.getTime() / 1000.0;
|
||||
}
|
||||
|
||||
#else // USE_THEORADEC
|
||||
|
||||
MoviePlayer::MoviePlayer(Kernel *pKernel) : Service(pKernel) {
|
||||
if (!registerScriptBindings())
|
||||
error("Script bindings could not be registered.");
|
||||
else
|
||||
debugC(kDebugScript, "Script bindings registered.");
|
||||
}
|
||||
|
||||
MoviePlayer::~MoviePlayer() {
|
||||
}
|
||||
|
||||
bool MoviePlayer::loadMovie(const Common::String &Filename, unsigned int Z) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoviePlayer::unloadMovie() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoviePlayer::play() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoviePlayer::pause() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MoviePlayer::update() {
|
||||
}
|
||||
|
||||
bool MoviePlayer::isMovieLoaded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MoviePlayer::isPaused() {
|
||||
return true;
|
||||
}
|
||||
|
||||
float MoviePlayer::getScaleFactor() {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
void MoviePlayer::setScaleFactor(float ScaleFactor) {
|
||||
}
|
||||
|
||||
double MoviePlayer::getTime() {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
#endif // USE_THEORADEC
|
||||
|
||||
} // End of namespace Sword25
|
||||
154
engines/sword25/fmv/movieplayer.h
Normal file
154
engines/sword25/fmv/movieplayer.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_MOVIEPLAYER_H
|
||||
#define SWORD25_MOVIEPLAYER_H
|
||||
|
||||
#include "common/scummsys.h" // for USE_THEORADEC
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/service.h"
|
||||
#include "sword25/gfx/bitmap.h"
|
||||
|
||||
#ifdef USE_THEORADEC
|
||||
#include "video/theora_decoder.h"
|
||||
#endif
|
||||
|
||||
#define THEORA_INDIRECT_RENDERING
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class MoviePlayer : public Service {
|
||||
public:
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constructor / Destructor
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
MoviePlayer(Kernel *pKernel);
|
||||
~MoviePlayer() override;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Player interface must be implemented by a Movie Player
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads a movie file
|
||||
*
|
||||
* This method loads a movie file and prepares it for playback.
|
||||
* There can be oly one movie file loaded at a time. If you already have loaded a
|
||||
* movie file, it will be unloaded and, if necessary, stopped playing.
|
||||
* @param Filename The filename of the movie file to be loaded
|
||||
* @param Z Z indicates the position of the film on the main graphics layer
|
||||
* @return Returns false if an error occurred while loading, otherwise true.
|
||||
*/
|
||||
bool loadMovie(const Common::String &filename, uint z);
|
||||
|
||||
/**
|
||||
* Unloads the currently loaded movie file.
|
||||
* @return Returns false if an error occurred while unloading, otherwise true.
|
||||
* @remark This method can only be called when IsMovieLoaded() returns true.
|
||||
*/
|
||||
bool unloadMovie();
|
||||
|
||||
/**
|
||||
* Plays the loaded movie.
|
||||
*
|
||||
* The film will be keeping the aspect ratio of the screen.
|
||||
* If the film was previously paused with Pause(), then the film will resume playing.
|
||||
* @return Returns false if an error occurred while starting, otherwise true.
|
||||
* @remark This method can only be called when IsMovieLoaded() returns true.
|
||||
*/
|
||||
bool play();
|
||||
|
||||
/**
|
||||
* Pauses movie playback.
|
||||
*
|
||||
* A paused movie can later be resumed by calling the Play() method again.
|
||||
* @return Returns false if an error occurred while pausing, otherwise true.
|
||||
* @remark This method can only be called when IsMovieLoaded() returns true.
|
||||
*/
|
||||
bool pause();
|
||||
|
||||
/**
|
||||
* This function must be called once per frame.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Returns whether a film is loaded for playback.
|
||||
*/
|
||||
bool isMovieLoaded();
|
||||
|
||||
/**
|
||||
* Returns whether the movie playback is paused.
|
||||
* @remark This method can only be called when IsMovieLoaded() returns true.
|
||||
*/
|
||||
bool isPaused();
|
||||
|
||||
/**
|
||||
* Returns the scaling factor for the loaded film.
|
||||
*
|
||||
* When a movie is loaded, the scaling factor is automatically selected so that the film
|
||||
* takes the maximum screen space, without the film being distorted.
|
||||
* @return Returns the scaling factor of the film.
|
||||
* @remark This method can only be called when IsMovieLoaded() returns true.
|
||||
*/
|
||||
float getScaleFactor();
|
||||
|
||||
/**
|
||||
* Sets the factor by which the loaded film is to be scaled.
|
||||
* @param ScaleFactor The desired scale factor.
|
||||
* @remark This method can only be called when IsMovieLoaded() returns true.
|
||||
*/
|
||||
void setScaleFactor(float scaleFactor);
|
||||
|
||||
/**
|
||||
* Returns the current playing position in seconds.
|
||||
* @remark This method can only be called when IsMovieLoaded() returns true.
|
||||
*/
|
||||
double getTime();
|
||||
|
||||
private:
|
||||
bool registerScriptBindings();
|
||||
|
||||
|
||||
#ifdef USE_THEORADEC
|
||||
Video::TheoraDecoder _decoder;
|
||||
|
||||
Graphics::Surface *_backSurface;
|
||||
int _outX, _outY;
|
||||
|
||||
RenderObjectPtr<Bitmap> _outputBitmap;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
159
engines/sword25/fmv/movieplayer_script.cpp
Normal file
159
engines/sword25/fmv/movieplayer_script.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h" // for USE_THEORADEC
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/script/script.h"
|
||||
#include "sword25/script/luabindhelper.h"
|
||||
|
||||
#include "sword25/fmv/movieplayer.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
int loadMovie(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
lua_pushbooleancpp(L, FMVPtr->loadMovie(luaL_checkstring(L, 1), lua_gettop(L) == 2 ? static_cast<uint>(luaL_checknumber(L, 2)) : 10));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unloadMovie(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
lua_pushbooleancpp(L, FMVPtr->unloadMovie());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int play(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
lua_pushbooleancpp(L, FMVPtr->play());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pause(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
lua_pushbooleancpp(L, FMVPtr->pause());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int update(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
FMVPtr->update();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isMovieLoaded(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
lua_pushbooleancpp(L, FMVPtr->isMovieLoaded());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int isPaused(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
lua_pushbooleancpp(L, FMVPtr->isPaused());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int getScaleFactor(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
lua_pushnumber(L, FMVPtr->getScaleFactor());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int setScaleFactor(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
FMVPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 1)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getTime(lua_State *L) {
|
||||
MoviePlayer *FMVPtr = Kernel::getInstance()->getFMV();
|
||||
assert(FMVPtr);
|
||||
|
||||
lua_pushnumber(L, FMVPtr->getTime());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *LIBRARY_NAME = "Movieplayer";
|
||||
|
||||
const luaL_reg LIBRARY_FUNCTIONS[] = {
|
||||
{ "LoadMovie", loadMovie },
|
||||
{ "UnloadMovie", unloadMovie },
|
||||
{ "Play", play },
|
||||
{ "Pause", pause },
|
||||
{ "Update", update },
|
||||
{ "IsMovieLoaded", isMovieLoaded },
|
||||
{ "IsPaused", isPaused },
|
||||
{ "GetScaleFactor", getScaleFactor },
|
||||
{ "SetScaleFactor", setScaleFactor },
|
||||
{ "GetTime", getTime },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
bool MoviePlayer::registerScriptBindings() {
|
||||
ScriptEngine *pScript = Kernel::getInstance()->getScript();
|
||||
assert(pScript);
|
||||
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
|
||||
assert(L);
|
||||
|
||||
if (!LuaBindhelper::addFunctionsToLib(L, LIBRARY_NAME, LIBRARY_FUNCTIONS)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
680
engines/sword25/gfx/animation.cpp
Normal file
680
engines/sword25/gfx/animation.cpp
Normal file
@@ -0,0 +1,680 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/animation.h"
|
||||
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/resmanager.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "sword25/gfx/animationtemplate.h"
|
||||
#include "sword25/gfx/animationtemplateregistry.h"
|
||||
#include "sword25/gfx/animationresource.h"
|
||||
#include "sword25/gfx/bitmapresource.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Animation::Animation(RenderObjectPtr<RenderObject> parentPtr, const Common::String &fileName) :
|
||||
TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION) {
|
||||
// Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
|
||||
if (!_initSuccess)
|
||||
return;
|
||||
|
||||
initMembers();
|
||||
|
||||
// Vom negativen Fall ausgehen.
|
||||
_initSuccess = false;
|
||||
|
||||
initializeAnimationResource(fileName);
|
||||
|
||||
// Erfolg signalisieren.
|
||||
_initSuccess = true;
|
||||
}
|
||||
|
||||
Animation::Animation(RenderObjectPtr<RenderObject> parentPtr, const AnimationTemplate &templ) :
|
||||
TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION) {
|
||||
// Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
|
||||
if (!_initSuccess)
|
||||
return;
|
||||
|
||||
initMembers();
|
||||
|
||||
// Vom negativen Fall ausgehen.
|
||||
_initSuccess = false;
|
||||
|
||||
_animationTemplateHandle = AnimationTemplate::create(templ);
|
||||
|
||||
// Erfolg signalisieren.
|
||||
_initSuccess = true;
|
||||
}
|
||||
|
||||
Animation::Animation(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
|
||||
TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION, handle) {
|
||||
// Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
|
||||
if (!_initSuccess)
|
||||
return;
|
||||
|
||||
initMembers();
|
||||
|
||||
// Objekt vom Stream laden.
|
||||
_initSuccess = unpersist(reader);
|
||||
}
|
||||
|
||||
void Animation::initializeAnimationResource(const Common::String &fileName) {
|
||||
// Die Resource wird für die gesamte Lebensdauer des Animations-Objektes gelockt.
|
||||
Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(fileName);
|
||||
if (resourcePtr && resourcePtr->getType() == Resource::TYPE_ANIMATION)
|
||||
_animationResourcePtr = static_cast<AnimationResource *>(resourcePtr);
|
||||
else {
|
||||
error("The resource \"%s\" could not be requested. The Animation can't be created.", fileName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Größe und Position der Animation anhand des aktuellen Frames bestimmen.
|
||||
computeCurrentCharacteristics();
|
||||
}
|
||||
|
||||
void Animation::initMembers() {
|
||||
_currentFrame = 0;
|
||||
_currentFrameTime = 0;
|
||||
_direction = FORWARD;
|
||||
_running = false;
|
||||
_finished = false;
|
||||
_relX = 0;
|
||||
_relY = 0;
|
||||
_scaleFactorX = 1.0f;
|
||||
_scaleFactorY = 1.0f;
|
||||
_modulationColor = BS_ARGBMASK;
|
||||
_animationResourcePtr = 0;
|
||||
_animationTemplateHandle = 0;
|
||||
_framesLocked = false;
|
||||
|
||||
_loopPointCallback = 0;
|
||||
_actionCallback = 0;
|
||||
_deleteCallback = 0;
|
||||
}
|
||||
|
||||
Animation::~Animation() {
|
||||
if (getAnimationDescription()) {
|
||||
stop();
|
||||
getAnimationDescription()->unlock();
|
||||
}
|
||||
|
||||
// Invoke the "delete" callback
|
||||
if (_deleteCallback)
|
||||
(_deleteCallback)(getHandle());
|
||||
|
||||
}
|
||||
|
||||
void Animation::play() {
|
||||
// If the animation was completed, then play it again from the start.
|
||||
if (_finished)
|
||||
stop();
|
||||
|
||||
_running = true;
|
||||
lockAllFrames();
|
||||
}
|
||||
|
||||
void Animation::pause() {
|
||||
_running = false;
|
||||
unlockAllFrames();
|
||||
}
|
||||
|
||||
void Animation::stop() {
|
||||
_currentFrame = 0;
|
||||
_currentFrameTime = 0;
|
||||
_direction = FORWARD;
|
||||
pause();
|
||||
}
|
||||
|
||||
void Animation::setFrame(uint nr) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
|
||||
if (nr >= animationDescriptionPtr->getFrameCount()) {
|
||||
error("Tried to set animation to illegal frame (%d). Value must be between 0 and %d.",
|
||||
nr, animationDescriptionPtr->getFrameCount());
|
||||
return;
|
||||
}
|
||||
|
||||
_currentFrame = nr;
|
||||
_currentFrameTime = 0;
|
||||
computeCurrentCharacteristics();
|
||||
forceRefresh();
|
||||
}
|
||||
|
||||
bool Animation::doRender(RectangleList *updateRects) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
assert(_currentFrame < animationDescriptionPtr->getFrameCount());
|
||||
|
||||
// Bitmap des aktuellen Frames holen
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(_currentFrame).fileName);
|
||||
assert(pResource);
|
||||
assert(pResource->getType() == Resource::TYPE_BITMAP);
|
||||
BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
|
||||
|
||||
// Framebufferobjekt holen
|
||||
GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
|
||||
assert(pGfx);
|
||||
|
||||
// Bitmap zeichnen
|
||||
bool result;
|
||||
if (isScalingAllowed() && (_width != pBitmapResource->getWidth() || _height != pBitmapResource->getHeight())) {
|
||||
result = pBitmapResource->blit(_absoluteX, _absoluteY,
|
||||
(animationDescriptionPtr->getFrame(_currentFrame).flipV ? Graphics::FLIP_V : 0) |
|
||||
(animationDescriptionPtr->getFrame(_currentFrame).flipH ? Graphics::FLIP_H : 0),
|
||||
0, _modulationColor, _width, _height,
|
||||
updateRects);
|
||||
} else {
|
||||
result = pBitmapResource->blit(_absoluteX, _absoluteY,
|
||||
(animationDescriptionPtr->getFrame(_currentFrame).flipV ? Graphics::FLIP_V : 0) |
|
||||
(animationDescriptionPtr->getFrame(_currentFrame).flipH ? Graphics::FLIP_H : 0),
|
||||
0, _modulationColor, -1, -1,
|
||||
updateRects);
|
||||
}
|
||||
|
||||
// Resource freigeben
|
||||
pBitmapResource->release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Animation::frameNotification(int timeElapsed) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
assert(timeElapsed >= 0);
|
||||
|
||||
// Nur wenn die Animation läuft wird sie auch weiterbewegt
|
||||
if (_running) {
|
||||
// Gesamte vergangene Zeit bestimmen (inkl. Restzeit des aktuellen Frames)
|
||||
_currentFrameTime += timeElapsed;
|
||||
|
||||
// Anzahl an zu überpringenden Frames bestimmen
|
||||
int skipFrames = animationDescriptionPtr->getMillisPerFrame() == 0 ? 0 : _currentFrameTime / animationDescriptionPtr->getMillisPerFrame();
|
||||
|
||||
// Neue Frame-Restzeit bestimmen
|
||||
_currentFrameTime -= animationDescriptionPtr->getMillisPerFrame() * skipFrames;
|
||||
|
||||
// Neuen Frame bestimmen (je nach aktuellener Abspielrichtung wird addiert oder subtrahiert)
|
||||
int tmpCurFrame = _currentFrame;
|
||||
switch (_direction) {
|
||||
case FORWARD:
|
||||
tmpCurFrame += skipFrames;
|
||||
break;
|
||||
|
||||
case BACKWARD:
|
||||
tmpCurFrame -= skipFrames;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
// Deal with overflows
|
||||
if (tmpCurFrame < 0) {
|
||||
// Loop-Point callback
|
||||
if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
|
||||
_loopPointCallback = 0;
|
||||
|
||||
// An underflow may only occur if the animation type is JOJO.
|
||||
assert(animationDescriptionPtr->getAnimationType() == AT_JOJO);
|
||||
tmpCurFrame = - tmpCurFrame;
|
||||
_direction = FORWARD;
|
||||
} else if (static_cast<uint>(tmpCurFrame) >= animationDescriptionPtr->getFrameCount()) {
|
||||
// Loop-Point callback
|
||||
if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
|
||||
_loopPointCallback = 0;
|
||||
|
||||
switch (animationDescriptionPtr->getAnimationType()) {
|
||||
case AT_ONESHOT:
|
||||
tmpCurFrame = animationDescriptionPtr->getFrameCount() - 1;
|
||||
_finished = true;
|
||||
pause();
|
||||
break;
|
||||
|
||||
case AT_LOOP:
|
||||
tmpCurFrame = tmpCurFrame % animationDescriptionPtr->getFrameCount();
|
||||
break;
|
||||
|
||||
case AT_JOJO:
|
||||
tmpCurFrame = animationDescriptionPtr->getFrameCount() - (tmpCurFrame % animationDescriptionPtr->getFrameCount()) - 1;
|
||||
_direction = BACKWARD;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)_currentFrame != tmpCurFrame) {
|
||||
forceRefresh();
|
||||
|
||||
if (animationDescriptionPtr->getFrame(_currentFrame).action != "") {
|
||||
// action callback
|
||||
if (_actionCallback && !(_actionCallback)(getHandle()))
|
||||
_actionCallback = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_currentFrame = static_cast<uint>(tmpCurFrame);
|
||||
}
|
||||
|
||||
// Größe und Position der Animation anhand des aktuellen Frames bestimmen
|
||||
computeCurrentCharacteristics();
|
||||
|
||||
assert(_currentFrame < animationDescriptionPtr->getFrameCount());
|
||||
assert(_currentFrameTime >= 0);
|
||||
}
|
||||
|
||||
void Animation::computeCurrentCharacteristics() {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
|
||||
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
|
||||
assert(pResource);
|
||||
assert(pResource->getType() == Resource::TYPE_BITMAP);
|
||||
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
|
||||
|
||||
// Größe des Bitmaps auf die Animation übertragen
|
||||
_width = static_cast<int>(pBitmap->getWidth() * _scaleFactorX);
|
||||
_height = static_cast<int>(pBitmap->getHeight() * _scaleFactorY);
|
||||
|
||||
// Position anhand des Hotspots berechnen und setzen
|
||||
int posX = _relX + computeXModifier();
|
||||
int posY = _relY + computeYModifier();
|
||||
|
||||
RenderObject::setPos(posX, posY);
|
||||
|
||||
pBitmap->release();
|
||||
}
|
||||
|
||||
bool Animation::lockAllFrames() {
|
||||
if (!_framesLocked) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
|
||||
if (!Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(i).fileName)) {
|
||||
error("Could not lock all animation frames.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_framesLocked = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Animation::unlockAllFrames() {
|
||||
if (_framesLocked) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
|
||||
Resource *pResource;
|
||||
if (!(pResource = Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(i).fileName))) {
|
||||
error("Could not unlock all animation frames.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Zwei mal freigeben um den Request von LockAllFrames() und den jetzigen Request aufzuheben
|
||||
pResource->release();
|
||||
if (pResource->getLockCount())
|
||||
pResource->release();
|
||||
}
|
||||
|
||||
_framesLocked = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Animation::ANIMATION_TYPES Animation::getAnimationType() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
return animationDescriptionPtr->getAnimationType();
|
||||
}
|
||||
|
||||
int Animation::getFPS() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
return animationDescriptionPtr->getFPS();
|
||||
}
|
||||
|
||||
int Animation::getFrameCount() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
return animationDescriptionPtr->getFrameCount();
|
||||
}
|
||||
|
||||
bool Animation::isScalingAllowed() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
return animationDescriptionPtr->isScalingAllowed();
|
||||
}
|
||||
|
||||
bool Animation::isAlphaAllowed() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
return animationDescriptionPtr->isAlphaAllowed();
|
||||
}
|
||||
|
||||
bool Animation::isColorModulationAllowed() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
return animationDescriptionPtr->isColorModulationAllowed();
|
||||
}
|
||||
|
||||
void Animation::setPos(int relX, int relY) {
|
||||
_relX = relX;
|
||||
_relY = relY;
|
||||
|
||||
computeCurrentCharacteristics();
|
||||
}
|
||||
|
||||
void Animation::setX(int relX) {
|
||||
_relX = relX;
|
||||
|
||||
computeCurrentCharacteristics();
|
||||
}
|
||||
|
||||
void Animation::setY(int relY) {
|
||||
_relY = relY;
|
||||
|
||||
computeCurrentCharacteristics();
|
||||
}
|
||||
|
||||
void Animation::setAlpha(int alpha) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
if (!animationDescriptionPtr->isAlphaAllowed()) {
|
||||
warning("Tried to set alpha value on an animation that does not support alpha. Call was ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint newModulationColor = (_modulationColor & BS_RGBMASK) | alpha << BS_ASHIFT;
|
||||
if (newModulationColor != _modulationColor) {
|
||||
_modulationColor = newModulationColor;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::setModulationColor(uint modulationColor) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
if (!animationDescriptionPtr->isColorModulationAllowed()) {
|
||||
warning("Tried to set modulation color on an animation that does not support color modulation. Call was ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
uint newModulationColor = (modulationColor & BS_RGBMASK) | (_modulationColor & BS_AMASK);
|
||||
if (newModulationColor != _modulationColor) {
|
||||
_modulationColor = newModulationColor;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::setScaleFactor(float scaleFactor) {
|
||||
setScaleFactorX(scaleFactor);
|
||||
setScaleFactorY(scaleFactor);
|
||||
}
|
||||
|
||||
void Animation::setScaleFactorX(float scaleFactorX) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
if (!animationDescriptionPtr->isScalingAllowed()) {
|
||||
warning("Tried to set x scale factor on an animation that does not support scaling. Call was ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
if (scaleFactorX != _scaleFactorX) {
|
||||
_scaleFactorX = scaleFactorX;
|
||||
if (_scaleFactorX <= 0.0f)
|
||||
_scaleFactorX = 0.001f;
|
||||
forceRefresh();
|
||||
computeCurrentCharacteristics();
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::setScaleFactorY(float scaleFactorY) {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
if (!animationDescriptionPtr->isScalingAllowed()) {
|
||||
warning("Tried to set y scale factor on an animation that does not support scaling. Call was ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
if (scaleFactorY != _scaleFactorY) {
|
||||
_scaleFactorY = scaleFactorY;
|
||||
if (_scaleFactorY <= 0.0f)
|
||||
_scaleFactorY = 0.001f;
|
||||
forceRefresh();
|
||||
computeCurrentCharacteristics();
|
||||
}
|
||||
}
|
||||
|
||||
const Common::String &Animation::getCurrentAction() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
return animationDescriptionPtr->getFrame(_currentFrame).action;
|
||||
}
|
||||
|
||||
int Animation::getX() const {
|
||||
return _relX;
|
||||
}
|
||||
|
||||
int Animation::getY() const {
|
||||
return _relY;
|
||||
}
|
||||
|
||||
int Animation::getAbsoluteX() const {
|
||||
return _absoluteX + (_relX - _x);
|
||||
}
|
||||
|
||||
int Animation::getAbsoluteY() const {
|
||||
return _absoluteY + (_relY - _y);
|
||||
}
|
||||
|
||||
int Animation::computeXModifier() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
|
||||
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
|
||||
assert(pResource);
|
||||
assert(pResource->getType() == Resource::TYPE_BITMAP);
|
||||
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
|
||||
|
||||
int result = curFrame.flipV ? - static_cast<int>((pBitmap->getWidth() - 1 - curFrame.hotspotX) * _scaleFactorX) :
|
||||
- static_cast<int>(curFrame.hotspotX * _scaleFactorX);
|
||||
|
||||
pBitmap->release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int Animation::computeYModifier() const {
|
||||
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
|
||||
assert(animationDescriptionPtr);
|
||||
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
|
||||
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
|
||||
assert(pResource);
|
||||
assert(pResource->getType() == Resource::TYPE_BITMAP);
|
||||
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
|
||||
|
||||
int result = curFrame.flipH ? - static_cast<int>((pBitmap->getHeight() - 1 - curFrame.hotspotY) * _scaleFactorY) :
|
||||
- static_cast<int>(curFrame.hotspotY * _scaleFactorY);
|
||||
|
||||
pBitmap->release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Animation::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
result &= RenderObject::persist(writer);
|
||||
|
||||
writer.write(_relX);
|
||||
writer.write(_relY);
|
||||
writer.write(_scaleFactorX);
|
||||
writer.write(_scaleFactorY);
|
||||
writer.write(_modulationColor);
|
||||
writer.write(_currentFrame);
|
||||
writer.write(_currentFrameTime);
|
||||
writer.write(_running);
|
||||
writer.write(_finished);
|
||||
writer.write(static_cast<uint32>(_direction));
|
||||
|
||||
// Je nach Animationstyp entweder das Template oder die Ressource speichern.
|
||||
if (_animationResourcePtr) {
|
||||
uint32 marker = 0;
|
||||
writer.write(marker);
|
||||
writer.writeString(_animationResourcePtr->getFileName());
|
||||
} else if (_animationTemplateHandle) {
|
||||
uint32 marker = 1;
|
||||
writer.write(marker);
|
||||
writer.write(_animationTemplateHandle);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
//writer.write(_AnimationDescriptionPtr);
|
||||
|
||||
writer.write(_framesLocked);
|
||||
|
||||
// The following is only there to for compatibility with older saves
|
||||
// resp. the original engine.
|
||||
writer.write((uint32)1);
|
||||
writer.writeString("LuaLoopPointCB");
|
||||
writer.write(getHandle());
|
||||
writer.write((uint32)1);
|
||||
writer.writeString("LuaActionCB");
|
||||
writer.write(getHandle());
|
||||
writer.write((uint32)1);
|
||||
writer.writeString("LuaDeleteCB");
|
||||
writer.write(getHandle());
|
||||
|
||||
result &= RenderObject::persistChildren(writer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool Animation::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
result &= RenderObject::unpersist(reader);
|
||||
|
||||
reader.read(_relX);
|
||||
reader.read(_relY);
|
||||
reader.read(_scaleFactorX);
|
||||
reader.read(_scaleFactorY);
|
||||
reader.read(_modulationColor);
|
||||
reader.read(_currentFrame);
|
||||
reader.read(_currentFrameTime);
|
||||
reader.read(_running);
|
||||
reader.read(_finished);
|
||||
uint32 direction;
|
||||
reader.read(direction);
|
||||
_direction = static_cast<Direction>(direction);
|
||||
|
||||
// Animationstyp einlesen.
|
||||
uint32 marker;
|
||||
reader.read(marker);
|
||||
if (marker == 0) {
|
||||
Common::String resourceFilename;
|
||||
reader.readString(resourceFilename);
|
||||
initializeAnimationResource(resourceFilename);
|
||||
} else if (marker == 1) {
|
||||
reader.read(_animationTemplateHandle);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
reader.read(_framesLocked);
|
||||
if (_framesLocked)
|
||||
lockAllFrames();
|
||||
|
||||
|
||||
// The following is only there to for compatibility with older saves
|
||||
// resp. the original engine.
|
||||
uint32 callbackCount;
|
||||
Common::String callbackFunctionName;
|
||||
uint32 callbackData;
|
||||
|
||||
// loop point callback
|
||||
reader.read(callbackCount);
|
||||
assert(callbackCount == 1);
|
||||
reader.readString(callbackFunctionName);
|
||||
assert(callbackFunctionName == "LuaLoopPointCB");
|
||||
reader.read(callbackData);
|
||||
assert(callbackData == getHandle());
|
||||
|
||||
// loop point callback
|
||||
reader.read(callbackCount);
|
||||
assert(callbackCount == 1);
|
||||
reader.readString(callbackFunctionName);
|
||||
assert(callbackFunctionName == "LuaActionCB");
|
||||
reader.read(callbackData);
|
||||
assert(callbackData == getHandle());
|
||||
|
||||
// loop point callback
|
||||
reader.read(callbackCount);
|
||||
assert(callbackCount == 1);
|
||||
reader.readString(callbackFunctionName);
|
||||
assert(callbackFunctionName == "LuaDeleteCB");
|
||||
reader.read(callbackData);
|
||||
assert(callbackData == getHandle());
|
||||
|
||||
// Set the callbacks
|
||||
setCallbacks();
|
||||
|
||||
result &= RenderObject::unpersistChildren(reader);
|
||||
|
||||
return reader.isGood() && result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
AnimationDescription *Animation::getAnimationDescription() const {
|
||||
if (_animationResourcePtr)
|
||||
return _animationResourcePtr;
|
||||
else
|
||||
return AnimationTemplateRegistry::instance().resolveHandle(_animationTemplateHandle);
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
219
engines/sword25/gfx/animation.h
Normal file
219
engines/sword25/gfx/animation.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_ANIMATION_H
|
||||
#define SWORD25_ANIMATION_H
|
||||
|
||||
// Includes
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/timedrenderobject.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// Forward declarations
|
||||
class Kernel;
|
||||
class AnimationResource;
|
||||
class AnimationTemplate;
|
||||
class AnimationDescription;
|
||||
class InputPersistenceBlock;
|
||||
|
||||
class Animation : public TimedRenderObject {
|
||||
friend class RenderObject;
|
||||
|
||||
private:
|
||||
Animation(RenderObjectPtr<RenderObject> parentPtr, const Common::String &fileName);
|
||||
Animation(RenderObjectPtr<RenderObject> parentPtr, const AnimationTemplate &template_);
|
||||
Animation(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
|
||||
|
||||
public:
|
||||
enum ANIMATION_TYPES {
|
||||
AT_ONESHOT,
|
||||
AT_LOOP,
|
||||
AT_JOJO
|
||||
};
|
||||
|
||||
~Animation() override;
|
||||
|
||||
void play();
|
||||
void pause();
|
||||
void stop();
|
||||
void setFrame(uint nr);
|
||||
|
||||
void setPos(int x, int y) override;
|
||||
void setX(int x) override;
|
||||
void setY(int y) override;
|
||||
|
||||
int getX() const override;
|
||||
int getY() const override;
|
||||
int getAbsoluteX() const override;
|
||||
int getAbsoluteY() const override;
|
||||
|
||||
/**
|
||||
@brief Setzt den Alphawert der Animation.
|
||||
@param Alpha der neue Alphawert der Animation (0 = keine Deckung, 255 = volle Deckung).
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
|
||||
*/
|
||||
void setAlpha(int alpha);
|
||||
|
||||
/**
|
||||
@brief Setzt die Modulationfarbe der Animation.
|
||||
@param Color eine 24-Bit Farbe, die die Modulationsfarbe der Animation festlegt.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
|
||||
*/
|
||||
void setModulationColor(uint modulationColor);
|
||||
|
||||
/**
|
||||
@brief Setzt den Skalierungsfaktor der Animation.
|
||||
@param ScaleFactor der Faktor um den die Animation in beide Richtungen gestreckt werden soll.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
void setScaleFactor(float scaleFactor);
|
||||
|
||||
/**
|
||||
@brief Setzt den Skalierungsfaktor der Animation auf der X-Achse.
|
||||
@param ScaleFactor der Faktor um den die Animation in Richtungen der X-Achse gestreckt werden soll.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
void setScaleFactorX(float scaleFactorX);
|
||||
|
||||
/**
|
||||
@brief Setzt den Skalierungsfaktor der Animation auf der Y-Achse.
|
||||
@param ScaleFactor der Faktor um den die Animation in Richtungen der Y-Achse gestreckt werden soll.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
void setScaleFactorY(float scaleFactorY);
|
||||
|
||||
/**
|
||||
@brief Gibt den Skalierungsfakter der Animation auf der X-Achse zurück.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
float getScaleFactorX() const {
|
||||
return _scaleFactorX;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt den Skalierungsfakter der Animation auf der Y-Achse zurück.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
float getScaleFactorY() const {
|
||||
return _scaleFactorY;
|
||||
}
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
void frameNotification(int timeElapsed) override;
|
||||
|
||||
ANIMATION_TYPES getAnimationType() const;
|
||||
int getFPS() const;
|
||||
int getFrameCount() const;
|
||||
bool isScalingAllowed() const;
|
||||
bool isAlphaAllowed() const;
|
||||
bool isColorModulationAllowed() const;
|
||||
uint getCurrentFrame() const {
|
||||
return _currentFrame;
|
||||
}
|
||||
const Common::String &getCurrentAction() const;
|
||||
bool isRunning() const {
|
||||
return _running;
|
||||
}
|
||||
|
||||
typedef bool (*ANIMATION_CALLBACK)(uint);
|
||||
|
||||
void setCallbacks();
|
||||
|
||||
protected:
|
||||
bool doRender(RectangleList *updateRects) override;
|
||||
|
||||
private:
|
||||
enum Direction {
|
||||
FORWARD,
|
||||
BACKWARD
|
||||
};
|
||||
|
||||
int32 _relX;
|
||||
int32 _relY;
|
||||
float _scaleFactorX;
|
||||
float _scaleFactorY;
|
||||
uint32 _modulationColor;
|
||||
uint32 _currentFrame;
|
||||
int32 _currentFrameTime;
|
||||
bool _running;
|
||||
bool _finished;
|
||||
Direction _direction;
|
||||
AnimationResource *_animationResourcePtr;
|
||||
uint32 _animationTemplateHandle;
|
||||
bool _framesLocked;
|
||||
|
||||
ANIMATION_CALLBACK _loopPointCallback;
|
||||
ANIMATION_CALLBACK _actionCallback;
|
||||
ANIMATION_CALLBACK _deleteCallback;
|
||||
|
||||
/**
|
||||
@brief Lockt alle Frames.
|
||||
@return Gibt false zurück, falls nicht alle Frames gelockt werden konnten.
|
||||
*/
|
||||
bool lockAllFrames();
|
||||
|
||||
/**
|
||||
@brief Unlockt alle Frames.
|
||||
@return Gibt false zurück, falls nicht alles Frames freigegeben werden konnten.
|
||||
*/
|
||||
bool unlockAllFrames();
|
||||
|
||||
/**
|
||||
@brief Diese Methode aktualisiert die Parameter (Größe, Position) der Animation anhand des aktuellen Frames.
|
||||
|
||||
Diese Methode muss bei jedem Framewechsel aufgerufen werden damit der RenderObject-Manager immer aktuelle Daten hat.
|
||||
*/
|
||||
void computeCurrentCharacteristics();
|
||||
|
||||
/**
|
||||
@brief Berechnet den Abstand zwischen dem linken Rand und dem Hotspot auf X-Achse in der aktuellen Darstellung.
|
||||
*/
|
||||
int computeXModifier() const;
|
||||
|
||||
/**
|
||||
@brief Berechnet den Abstand zwischen dem linken Rand und dem Hotspot auf X-Achse in der aktuellen Darstellung.
|
||||
*/
|
||||
int computeYModifier() const;
|
||||
|
||||
void initMembers();
|
||||
AnimationDescription *getAnimationDescription() const;
|
||||
|
||||
/**
|
||||
* Initializes a new animation resource from an XML file.
|
||||
*/
|
||||
void initializeAnimationResource(const Common::String &fileName);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
61
engines/sword25/gfx/animationdescription.cpp
Normal file
61
engines/sword25/gfx/animationdescription.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/gfx/animationdescription.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
bool AnimationDescription::persist(OutputPersistenceBlock &writer) {
|
||||
writer.write(static_cast<uint32>(_animationType));
|
||||
writer.write(_FPS);
|
||||
writer.write(_millisPerFrame);
|
||||
writer.write(_scalingAllowed);
|
||||
writer.write(_alphaAllowed);
|
||||
writer.write(_colorModulationAllowed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimationDescription::unpersist(InputPersistenceBlock &reader) {
|
||||
uint32 animationType;
|
||||
reader.read(animationType);
|
||||
_animationType = static_cast<Animation::ANIMATION_TYPES>(animationType);
|
||||
reader.read(_FPS);
|
||||
reader.read(_millisPerFrame);
|
||||
reader.read(_scalingAllowed);
|
||||
reader.read(_alphaAllowed);
|
||||
reader.read(_colorModulationAllowed);
|
||||
|
||||
return reader.isGood();
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
99
engines/sword25/gfx/animationdescription.h
Normal file
99
engines/sword25/gfx/animationdescription.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_ANIMATIONDESCRIPTION_H
|
||||
#define SWORD25_ANIMATIONDESCRIPTION_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
#include "sword25/gfx/animation.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class AnimationDescription : public Persistable {
|
||||
protected:
|
||||
AnimationDescription() :
|
||||
_animationType(Animation::AT_LOOP),
|
||||
_FPS(10),
|
||||
_millisPerFrame(0),
|
||||
_scalingAllowed(true),
|
||||
_alphaAllowed(true),
|
||||
_colorModulationAllowed(true)
|
||||
{}
|
||||
|
||||
public:
|
||||
struct Frame {
|
||||
// Die Hotspot-Angabe bezieht sich auf das ungeflippte Bild!!
|
||||
int32 hotspotX;
|
||||
int32 hotspotY;
|
||||
bool flipV;
|
||||
bool flipH;
|
||||
Common::String fileName;
|
||||
Common::String action;
|
||||
};
|
||||
|
||||
virtual const Frame &getFrame(uint index) const = 0;
|
||||
virtual uint getFrameCount() const = 0;
|
||||
virtual void unlock() = 0;
|
||||
|
||||
Animation::ANIMATION_TYPES getAnimationType() const {
|
||||
return _animationType;
|
||||
}
|
||||
int getFPS() const {
|
||||
return _FPS;
|
||||
}
|
||||
int getMillisPerFrame() const {
|
||||
return _millisPerFrame;
|
||||
}
|
||||
bool isScalingAllowed() const {
|
||||
return _scalingAllowed;
|
||||
}
|
||||
bool isAlphaAllowed() const {
|
||||
return _alphaAllowed;
|
||||
}
|
||||
bool isColorModulationAllowed() const {
|
||||
return _colorModulationAllowed;
|
||||
}
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
protected:
|
||||
Animation::ANIMATION_TYPES _animationType;
|
||||
int32 _FPS;
|
||||
int32 _millisPerFrame;
|
||||
bool _scalingAllowed;
|
||||
bool _alphaAllowed;
|
||||
bool _colorModulationAllowed;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
252
engines/sword25/gfx/animationresource.cpp
Normal file
252
engines/sword25/gfx/animationresource.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/animationresource.h"
|
||||
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/resmanager.h" // for PRECACHE_RESOURCES
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/gfx/bitmapresource.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
enum {
|
||||
DEFAULT_FPS = 10,
|
||||
MIN_FPS = 1,
|
||||
MAX_FPS = 200
|
||||
};
|
||||
|
||||
AnimationResource::AnimationResource(const Common::String &filename) :
|
||||
Resource(filename, Resource::TYPE_ANIMATION),
|
||||
Common::XMLParser(),
|
||||
_valid(false) {
|
||||
// Get a pointer to the package manager
|
||||
_pPackage = Kernel::getInstance()->getPackage();
|
||||
assert(_pPackage);
|
||||
|
||||
// Switch to the folder the specified Xml fiile is in
|
||||
Common::String oldDirectory = _pPackage->getCurrentDirectory();
|
||||
if (getFileName().contains('/')) {
|
||||
Common::String dir = Common::String(getFileName().c_str(), strrchr(getFileName().c_str(), '/'));
|
||||
_pPackage->changeDirectory(dir);
|
||||
}
|
||||
|
||||
// Load the contents of the file
|
||||
uint fileSize;
|
||||
char *xmlData = _pPackage->getXmlFile(getFileName(), &fileSize);
|
||||
if (!xmlData) {
|
||||
error("Could not read \"%s\".", getFileName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the contents
|
||||
if (!loadBuffer((const byte *)xmlData, fileSize))
|
||||
return;
|
||||
|
||||
_valid = parse();
|
||||
close();
|
||||
free(xmlData);
|
||||
|
||||
// Switch back to the previous folder
|
||||
_pPackage->changeDirectory(oldDirectory);
|
||||
|
||||
// Give an error message if there weren't any frames specified
|
||||
if (_frames.empty()) {
|
||||
error("\"%s\" does not have any frames.", getFileName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre-cache all the frames
|
||||
if (!precacheAllFrames()) {
|
||||
error("Could not precache all frames of \"%s\".", getFileName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Post processing to compute animation features
|
||||
if (!computeFeatures()) {
|
||||
error("Could not determine the features of \"%s\".", getFileName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
_valid = true;
|
||||
}
|
||||
|
||||
bool AnimationResource::parseBooleanKey(Common::String s, bool &result) {
|
||||
s.toLowercase();
|
||||
if (!strcmp(s.c_str(), "true"))
|
||||
result = true;
|
||||
else if (!strcmp(s.c_str(), "false"))
|
||||
result = false;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimationResource::parserCallback_animation(ParserNode *node) {
|
||||
if (!parseIntegerKey(node->values["fps"], 1, &_FPS) || (_FPS < MIN_FPS) || (_FPS > MAX_FPS)) {
|
||||
return parserError(Common::String::format("Illegal or missing fps attribute in <animation> tag in \"%s\". Assuming default (\"%d\").",
|
||||
getFileName().c_str(), DEFAULT_FPS));
|
||||
}
|
||||
|
||||
// Loop type value
|
||||
const char *loopTypeString = node->values["type"].c_str();
|
||||
|
||||
if (strcmp(loopTypeString, "oneshot") == 0) {
|
||||
_animationType = Animation::AT_ONESHOT;
|
||||
} else if (strcmp(loopTypeString, "loop") == 0) {
|
||||
_animationType = Animation::AT_LOOP;
|
||||
} else if (strcmp(loopTypeString, "jojo") == 0) {
|
||||
_animationType = Animation::AT_JOJO;
|
||||
} else {
|
||||
warning("Illegal type value (\"%s\") in <animation> tag in \"%s\". Assuming default (\"loop\").",
|
||||
loopTypeString, getFileName().c_str());
|
||||
_animationType = Animation::AT_LOOP;
|
||||
}
|
||||
|
||||
// Calculate the milliseconds required per frame
|
||||
// FIXME: Double check variable naming. Based on the constant, it may be microseconds
|
||||
_millisPerFrame = 1000000 / _FPS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimationResource::parserCallback_frame(ParserNode *node) {
|
||||
Frame frame;
|
||||
|
||||
const char *fileString = node->values["file"].c_str();
|
||||
if (!fileString) {
|
||||
error("<frame> tag without file attribute occurred in \"%s\".", getFileName().c_str());
|
||||
return false;
|
||||
}
|
||||
frame.fileName = _pPackage->getAbsolutePath(fileString);
|
||||
if (frame.fileName.empty()) {
|
||||
error("Could not create absolute path for file specified in <frame> tag in \"%s\": \"%s\".",
|
||||
getFileName().c_str(), fileString);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *actionString = node->values["action"].c_str();
|
||||
if (actionString)
|
||||
frame.action = actionString;
|
||||
|
||||
const char *hotspotxString = node->values["hotspotx"].c_str();
|
||||
const char *hotspotyString = node->values["hotspoty"].c_str();
|
||||
if ((!hotspotxString && hotspotyString) ||
|
||||
(hotspotxString && !hotspotyString))
|
||||
warning("%s attribute occurred without %s attribute in <frame> tag in \"%s\". Assuming default (\"0\").",
|
||||
hotspotxString ? "hotspotx" : "hotspoty",
|
||||
!hotspotyString ? "hotspoty" : "hotspotx",
|
||||
getFileName().c_str());
|
||||
|
||||
frame.hotspotX = 0;
|
||||
if (hotspotxString && !parseIntegerKey(hotspotxString, 1, &frame.hotspotX))
|
||||
warning("Illegal hotspotx value (\"%s\") in frame tag in \"%s\". Assuming default (\"%d\").",
|
||||
hotspotxString, getFileName().c_str(), frame.hotspotX);
|
||||
|
||||
frame.hotspotY = 0;
|
||||
if (hotspotyString && !parseIntegerKey(hotspotyString, 1, &frame.hotspotY))
|
||||
warning("Illegal hotspoty value (\"%s\") in frame tag in \"%s\". Assuming default (\"%d\").",
|
||||
hotspotyString, getFileName().c_str(), frame.hotspotY);
|
||||
|
||||
Common::String flipVString = node->values["flipv"];
|
||||
if (!flipVString.empty()) {
|
||||
if (!parseBooleanKey(flipVString, frame.flipV)) {
|
||||
warning("Illegal flipv value (\"%s\") in <frame> tag in \"%s\". Assuming default (\"false\").",
|
||||
flipVString.c_str(), getFileName().c_str());
|
||||
frame.flipV = false;
|
||||
}
|
||||
} else
|
||||
frame.flipV = false;
|
||||
|
||||
Common::String flipHString = node->values["fliph"];
|
||||
if (!flipHString.empty()) {
|
||||
if (!parseBooleanKey(flipHString, frame.flipH)) {
|
||||
warning("Illegal fliph value (\"%s\") in <frame> tag in \"%s\". Assuming default (\"false\").",
|
||||
flipHString.c_str(), getFileName().c_str());
|
||||
frame.flipH = false;
|
||||
}
|
||||
} else
|
||||
frame.flipH = false;
|
||||
|
||||
_frames.push_back(frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
AnimationResource::~AnimationResource() {
|
||||
}
|
||||
|
||||
bool AnimationResource::precacheAllFrames() const {
|
||||
Common::Array<Frame>::const_iterator iter = _frames.begin();
|
||||
for (; iter != _frames.end(); ++iter) {
|
||||
#ifdef PRECACHE_RESOURCES
|
||||
if (!Kernel::getInstance()->getResourceManager()->precacheResource((*iter).fileName)) {
|
||||
error("Could not precache \"%s\".", (*iter).fileName.c_str());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName);
|
||||
pResource->release(); //unlock precached resource
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimationResource::computeFeatures() {
|
||||
assert(_frames.size());
|
||||
|
||||
// Alle Features werden als vorhanden angenommen
|
||||
_scalingAllowed = true;
|
||||
_alphaAllowed = true;
|
||||
_colorModulationAllowed = true;
|
||||
|
||||
// Alle Frame durchgehen und alle Features deaktivieren, die auch nur von einem Frame nicht unterstützt werden.
|
||||
Common::Array<Frame>::const_iterator iter = _frames.begin();
|
||||
for (; iter != _frames.end(); ++iter) {
|
||||
BitmapResource *pBitmap;
|
||||
if (!(pBitmap = static_cast<BitmapResource *>(Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName)))) {
|
||||
error("Could not request \"%s\".", (*iter).fileName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pBitmap->isScalingAllowed())
|
||||
_scalingAllowed = false;
|
||||
if (!pBitmap->isAlphaAllowed())
|
||||
_alphaAllowed = false;
|
||||
if (!pBitmap->isColorModulationAllowed())
|
||||
_colorModulationAllowed = false;
|
||||
|
||||
pBitmap->release();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
118
engines/sword25/gfx/animationresource.h
Normal file
118
engines/sword25/gfx/animationresource.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_ANIMATIONRESOURCE_H
|
||||
#define SWORD25_ANIMATIONRESOURCE_H
|
||||
|
||||
#include "common/formats/xmlparser.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/resource.h"
|
||||
#include "sword25/gfx/animationdescription.h"
|
||||
#include "sword25/gfx/animation.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
class PackageManager;
|
||||
|
||||
class AnimationResource : public Resource, public AnimationDescription, public Common::XMLParser {
|
||||
public:
|
||||
AnimationResource(const Common::String &filename);
|
||||
~AnimationResource() override;
|
||||
|
||||
const Frame &getFrame(uint index) const override {
|
||||
return _frames[index];
|
||||
}
|
||||
uint getFrameCount() const override {
|
||||
return _frames.size();
|
||||
}
|
||||
void unlock() override {
|
||||
release();
|
||||
}
|
||||
|
||||
Animation::ANIMATION_TYPES getAnimationType() const {
|
||||
return _animationType;
|
||||
}
|
||||
int getFPS() const {
|
||||
return _FPS;
|
||||
}
|
||||
int getMillisPerFrame() const {
|
||||
return _millisPerFrame;
|
||||
}
|
||||
bool isScalingAllowed() const {
|
||||
return _scalingAllowed;
|
||||
}
|
||||
bool isAlphaAllowed() const {
|
||||
return _alphaAllowed;
|
||||
}
|
||||
bool isColorModulationAllowed() const {
|
||||
return _colorModulationAllowed;
|
||||
}
|
||||
bool isValid() const {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _valid;
|
||||
|
||||
Common::Array<Frame> _frames;
|
||||
|
||||
PackageManager *_pPackage;
|
||||
|
||||
|
||||
bool computeFeatures();
|
||||
bool precacheAllFrames() const;
|
||||
|
||||
// Parser
|
||||
CUSTOM_XML_PARSER(AnimationResource) {
|
||||
XML_KEY(animation)
|
||||
XML_PROP(fps, true)
|
||||
XML_PROP(type, true)
|
||||
|
||||
XML_KEY(frame)
|
||||
XML_PROP(file, true)
|
||||
XML_PROP(hotspotx, true)
|
||||
XML_PROP(hotspoty, true)
|
||||
XML_PROP(fliph, false)
|
||||
XML_PROP(flipv, false)
|
||||
KEY_END()
|
||||
KEY_END()
|
||||
} PARSER_END()
|
||||
|
||||
bool parseBooleanKey(Common::String s, bool &result);
|
||||
|
||||
// Parser callback methods
|
||||
bool parserCallback_animation(ParserNode *node);
|
||||
bool parserCallback_frame(ParserNode *node);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
237
engines/sword25/gfx/animationtemplate.cpp
Normal file
237
engines/sword25/gfx/animationtemplate.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/resource.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
|
||||
#include "sword25/gfx/animationresource.h"
|
||||
#include "sword25/gfx/animationtemplate.h"
|
||||
#include "sword25/gfx/animationtemplateregistry.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
uint AnimationTemplate::create(const Common::String &sourceAnimation) {
|
||||
AnimationTemplate *animationTemplatePtr = new AnimationTemplate(sourceAnimation);
|
||||
|
||||
if (animationTemplatePtr->isValid()) {
|
||||
return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
|
||||
} else {
|
||||
delete animationTemplatePtr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint AnimationTemplate::create(const AnimationTemplate &other) {
|
||||
AnimationTemplate *animationTemplatePtr = new AnimationTemplate(other);
|
||||
|
||||
if (animationTemplatePtr->isValid()) {
|
||||
return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
|
||||
} else {
|
||||
delete animationTemplatePtr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint AnimationTemplate::create(InputPersistenceBlock &reader, uint handle) {
|
||||
AnimationTemplate *animationTemplatePtr = new AnimationTemplate(reader, handle);
|
||||
|
||||
if (animationTemplatePtr->isValid()) {
|
||||
return AnimationTemplateRegistry::instance().resolvePtr(animationTemplatePtr);
|
||||
} else {
|
||||
delete animationTemplatePtr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationTemplate::AnimationTemplate(const Common::String &sourceAnimation) {
|
||||
// Objekt registrieren.
|
||||
AnimationTemplateRegistry::instance().registerObject(this);
|
||||
|
||||
_valid = false;
|
||||
|
||||
// Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
|
||||
_sourceAnimationPtr = requestSourceAnimation(sourceAnimation);
|
||||
|
||||
// Erfolg signalisieren
|
||||
_valid = (_sourceAnimationPtr != 0);
|
||||
}
|
||||
|
||||
AnimationTemplate::AnimationTemplate(const AnimationTemplate &other) : AnimationDescription() {
|
||||
// Objekt registrieren.
|
||||
AnimationTemplateRegistry::instance().registerObject(this);
|
||||
|
||||
_valid = false;
|
||||
|
||||
// Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt.
|
||||
if (!other._sourceAnimationPtr)
|
||||
return;
|
||||
_sourceAnimationPtr = requestSourceAnimation(other._sourceAnimationPtr->getFileName());
|
||||
|
||||
// Alle Member kopieren.
|
||||
_animationType = other._animationType;
|
||||
_FPS = other._FPS;
|
||||
_millisPerFrame = other._millisPerFrame;
|
||||
_scalingAllowed = other._scalingAllowed;
|
||||
_alphaAllowed = other._alphaAllowed;
|
||||
_colorModulationAllowed = other._colorModulationAllowed;
|
||||
_frames = other._frames;
|
||||
_sourceAnimationPtr = other._sourceAnimationPtr;
|
||||
_valid = other._valid;
|
||||
|
||||
_valid &= (_sourceAnimationPtr != 0);
|
||||
}
|
||||
|
||||
AnimationTemplate::AnimationTemplate(InputPersistenceBlock &reader, uint handle) {
|
||||
// Objekt registrieren.
|
||||
AnimationTemplateRegistry::instance().registerObject(this, handle);
|
||||
|
||||
// Objekt laden.
|
||||
_valid = unpersist(reader);
|
||||
}
|
||||
|
||||
AnimationResource *AnimationTemplate::requestSourceAnimation(const Common::String &sourceAnimation) const {
|
||||
ResourceManager *RMPtr = Kernel::getInstance()->getResourceManager();
|
||||
Resource *resourcePtr;
|
||||
if (NULL == (resourcePtr = RMPtr->requestResource(sourceAnimation)) || resourcePtr->getType() != Resource::TYPE_ANIMATION) {
|
||||
error("The resource \"%s\" could not be requested or is has an invalid type. The animation template can't be created.", sourceAnimation.c_str());
|
||||
return 0;
|
||||
}
|
||||
return static_cast<AnimationResource *>(resourcePtr);
|
||||
}
|
||||
|
||||
AnimationTemplate::~AnimationTemplate() {
|
||||
// Animations-Resource freigeben
|
||||
if (_sourceAnimationPtr) {
|
||||
_sourceAnimationPtr->release();
|
||||
}
|
||||
|
||||
// Objekt deregistrieren
|
||||
AnimationTemplateRegistry::instance().deregisterObject(this);
|
||||
}
|
||||
|
||||
void AnimationTemplate::addFrame(int index) {
|
||||
if (validateSourceIndex(index)) {
|
||||
_frames.push_back(_sourceAnimationPtr->getFrame(index));
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationTemplate::setFrame(int destIndex, int srcIndex) {
|
||||
if (validateDestIndex(destIndex) && validateSourceIndex(srcIndex)) {
|
||||
_frames[destIndex] = _sourceAnimationPtr->getFrame(srcIndex);
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimationTemplate::validateSourceIndex(uint index) const {
|
||||
if (index > _sourceAnimationPtr->getFrameCount()) {
|
||||
warning("Tried to insert a frame (\"%d\") that does not exist in the source animation (\"%s\"). Ignoring call.",
|
||||
index, _sourceAnimationPtr->getFileName().c_str());
|
||||
return false;
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimationTemplate::validateDestIndex(uint index) const {
|
||||
if (index > _frames.size()) {
|
||||
warning("Tried to change a nonexistent frame (\"%d\") in a template animation. Ignoring call.",
|
||||
index);
|
||||
return false;
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationTemplate::setFPS(int FPS) {
|
||||
_FPS = FPS;
|
||||
_millisPerFrame = 1000000 / _FPS;
|
||||
}
|
||||
|
||||
bool AnimationTemplate::persist(OutputPersistenceBlock &writer) {
|
||||
bool Result = true;
|
||||
|
||||
// Parent persistieren.
|
||||
Result &= AnimationDescription::persist(writer);
|
||||
|
||||
// Frameanzahl schreiben.
|
||||
writer.write((uint32)_frames.size());
|
||||
|
||||
// Frames einzeln persistieren.
|
||||
Common::Array<const Frame>::const_iterator Iter = _frames.begin();
|
||||
while (Iter != _frames.end()) {
|
||||
writer.write(Iter->hotspotX);
|
||||
writer.write(Iter->hotspotY);
|
||||
writer.write(Iter->flipV);
|
||||
writer.write(Iter->flipH);
|
||||
writer.writeString(Iter->fileName);
|
||||
writer.writeString(Iter->action);
|
||||
++Iter;
|
||||
}
|
||||
|
||||
// Restliche Member persistieren.
|
||||
writer.writeString(_sourceAnimationPtr->getFileName());
|
||||
writer.write(_valid);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool AnimationTemplate::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
// Parent wieder herstellen.
|
||||
result &= AnimationDescription::unpersist(reader);
|
||||
|
||||
// Frameanzahl lesen.
|
||||
uint32 frameCount;
|
||||
reader.read(frameCount);
|
||||
|
||||
// Frames einzeln wieder herstellen.
|
||||
for (uint i = 0; i < frameCount; ++i) {
|
||||
Frame frame;
|
||||
reader.read(frame.hotspotX);
|
||||
reader.read(frame.hotspotY);
|
||||
reader.read(frame.flipV);
|
||||
reader.read(frame.flipH);
|
||||
reader.readString(frame.fileName);
|
||||
reader.readString(frame.action);
|
||||
|
||||
_frames.push_back(frame);
|
||||
}
|
||||
|
||||
// Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
|
||||
Common::String sourceAnimation;
|
||||
reader.readString(sourceAnimation);
|
||||
_sourceAnimationPtr = requestSourceAnimation(sourceAnimation);
|
||||
|
||||
reader.read(_valid);
|
||||
|
||||
return _sourceAnimationPtr && reader.isGood() && result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
121
engines/sword25/gfx/animationtemplate.h
Normal file
121
engines/sword25/gfx/animationtemplate.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_ANIMATION_TEMPLATE_H
|
||||
#define SWORD25_ANIMATION_TEMPLATE_H
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Includes
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
#include "sword25/gfx/animationdescription.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class AnimationResource;
|
||||
|
||||
class AnimationTemplate : public AnimationDescription {
|
||||
public:
|
||||
static uint create(const Common::String &sourceAnimation);
|
||||
static uint create(const AnimationTemplate &other);
|
||||
static uint create(InputPersistenceBlock &reader, uint handle);
|
||||
AnimationTemplate *resolveHandle(uint handle) const;
|
||||
|
||||
private:
|
||||
AnimationTemplate(const Common::String &sourceAnimation);
|
||||
AnimationTemplate(const AnimationTemplate &other);
|
||||
AnimationTemplate(InputPersistenceBlock &reader, uint handle);
|
||||
|
||||
public:
|
||||
~AnimationTemplate() override;
|
||||
|
||||
const Frame &getFrame(uint index) const override {
|
||||
assert(index < _frames.size());
|
||||
return _frames[index];
|
||||
}
|
||||
uint getFrameCount() const override {
|
||||
return _frames.size();
|
||||
}
|
||||
void unlock() override {
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Fügt einen neuen Frame zur Animation hinzu.
|
||||
|
||||
Der Frame wird an das Ende der Animation angehängt.
|
||||
|
||||
@param Index der Index des Frames in der Quellanimation
|
||||
*/
|
||||
void addFrame(int index);
|
||||
|
||||
/**
|
||||
@brief Ändert einen bereits in der Animation vorhandenen Frame.
|
||||
@param DestIndex der Index des Frames der überschrieben werden soll
|
||||
@param SrcIndex der Index des einzufügenden Frames in der Quellanimation
|
||||
*/
|
||||
void setFrame(int destIndex, int srcIndex);
|
||||
|
||||
/**
|
||||
@brief Setzt den Animationstyp.
|
||||
@param Type der Typ der Animation. Muss aus den enum BS_Animation::ANIMATION_TYPES sein.
|
||||
*/
|
||||
void setAnimationType(Animation::ANIMATION_TYPES type) {
|
||||
_animationType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Setzt die Abspielgeschwindigkeit.
|
||||
@param FPS die Abspielgeschwindigkeit in Frames pro Sekunde.
|
||||
*/
|
||||
void setFPS(int FPS);
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
private:
|
||||
Common::Array<Frame> _frames;
|
||||
AnimationResource *_sourceAnimationPtr;
|
||||
bool _valid;
|
||||
|
||||
AnimationResource *requestSourceAnimation(const Common::String &sourceAnimation) const;
|
||||
bool validateSourceIndex(uint index) const;
|
||||
bool validateDestIndex(uint index) const;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
95
engines/sword25/gfx/animationtemplateregistry.cpp
Normal file
95
engines/sword25/gfx/animationtemplateregistry.cpp
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/gfx/animationtemplateregistry.h"
|
||||
#include "sword25/gfx/animationtemplate.h"
|
||||
|
||||
namespace Common {
|
||||
DECLARE_SINGLETON(Sword25::AnimationTemplateRegistry);
|
||||
}
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
bool AnimationTemplateRegistry::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
// Das nächste zu vergebene Handle schreiben.
|
||||
writer.write(_nextHandle);
|
||||
|
||||
// Anzahl an BS_AnimationTemplates schreiben.
|
||||
writer.write((uint32)_handle2PtrMap.size());
|
||||
|
||||
// Alle BS_AnimationTemplates persistieren.
|
||||
HANDLE2PTR_MAP::const_iterator iter = _handle2PtrMap.begin();
|
||||
while (iter != _handle2PtrMap.end()) {
|
||||
// Handle persistieren.
|
||||
writer.write(iter->_key);
|
||||
|
||||
// Objekt persistieren.
|
||||
result &= iter->_value->persist(writer);
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool AnimationTemplateRegistry::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
// Das nächste zu vergebene Handle wieder herstellen.
|
||||
reader.read(_nextHandle);
|
||||
|
||||
// Alle vorhandenen BS_AnimationTemplates zerstören.
|
||||
while (!_handle2PtrMap.empty())
|
||||
delete _handle2PtrMap.begin()->_value;
|
||||
|
||||
// Anzahl an BS_AnimationTemplates einlesen.
|
||||
uint32 animationTemplateCount;
|
||||
reader.read(animationTemplateCount);
|
||||
|
||||
// Alle gespeicherten BS_AnimationTemplates wieder herstellen.
|
||||
for (uint i = 0; i < animationTemplateCount; ++i) {
|
||||
// Handle lesen.
|
||||
uint32 handle;
|
||||
reader.read(handle);
|
||||
|
||||
// BS_AnimationTemplate wieder herstellen.
|
||||
result &= (AnimationTemplate::create(reader, handle) != 0);
|
||||
}
|
||||
|
||||
return reader.isGood() && result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
56
engines/sword25/gfx/animationtemplateregistry.h
Normal file
56
engines/sword25/gfx/animationtemplateregistry.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_ANIMATIONTEMPLATEREGISTRY_H
|
||||
#define SWORD25_ANIMATIONTEMPLATEREGISTRY_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
#include "sword25/kernel/objectregistry.h"
|
||||
|
||||
#include "common/singleton.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class AnimationTemplate;
|
||||
|
||||
class AnimationTemplateRegistry :
|
||||
public ObjectRegistry<AnimationTemplate>,
|
||||
public Persistable,
|
||||
public Common::Singleton<AnimationTemplateRegistry> {
|
||||
public:
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
177
engines/sword25/gfx/bitmap.cpp
Normal file
177
engines/sword25/gfx/bitmap.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/bitmap.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Bitmap::Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
|
||||
RenderObject(parentPtr, type, handle),
|
||||
_modulationColor(BS_ARGBMASK),
|
||||
_scaleFactorX(1.0f),
|
||||
_scaleFactorY(1.0f),
|
||||
_flipH(false),
|
||||
_flipV(false) {
|
||||
}
|
||||
|
||||
Bitmap::~Bitmap() {
|
||||
}
|
||||
|
||||
void Bitmap::setAlpha(int alpha) {
|
||||
if (!isAlphaAllowed()) {
|
||||
warning("Tried to set alpha value on a bitmap that does not support alpha blending. Call was ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (alpha < 0 || alpha > 255) {
|
||||
int oldAlpha = alpha;
|
||||
if (alpha < 0)
|
||||
alpha = 0;
|
||||
if (alpha > 255)
|
||||
alpha = 255;
|
||||
warning("Tried to set an invalid alpha value (%d) on a bitmap. Value was changed to %d.", oldAlpha, alpha);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint newModulationColor = (_modulationColor & BS_RGBMASK) | alpha << BS_ASHIFT;
|
||||
if (newModulationColor != _modulationColor) {
|
||||
_modulationColor = newModulationColor;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::setModulationColor(uint modulationColor) {
|
||||
if (!isColorModulationAllowed()) {
|
||||
warning("Tried to set modulation color of a bitmap that does not support color modulation. Call was ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint newModulationColor = (modulationColor & BS_RGBMASK) | (_modulationColor & BS_AMASK);
|
||||
if (newModulationColor != _modulationColor) {
|
||||
_modulationColor = newModulationColor;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::setScaleFactor(float scaleFactor) {
|
||||
setScaleFactorX(scaleFactor);
|
||||
setScaleFactorY(scaleFactor);
|
||||
}
|
||||
|
||||
void Bitmap::setScaleFactorX(float scaleFactorX) {
|
||||
if (!isScalingAllowed()) {
|
||||
warning("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (scaleFactorX < 0) {
|
||||
warning("Tried to set scale factor of a bitmap to a negative value. Call was ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (scaleFactorX != _scaleFactorX) {
|
||||
_scaleFactorX = scaleFactorX;
|
||||
_width = static_cast<int>(_originalWidth * _scaleFactorX);
|
||||
if (_scaleFactorX <= 0.0f)
|
||||
_scaleFactorX = 0.001f;
|
||||
if (_width <= 0)
|
||||
_width = 1;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::setScaleFactorY(float scaleFactorY) {
|
||||
if (!isScalingAllowed()) {
|
||||
warning("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (scaleFactorY < 0) {
|
||||
warning("Tried to set scale factor of a bitmap to a negative value. Call was ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (scaleFactorY != _scaleFactorY) {
|
||||
_scaleFactorY = scaleFactorY;
|
||||
_height = static_cast<int>(_originalHeight * scaleFactorY);
|
||||
if (_scaleFactorY <= 0.0f)
|
||||
_scaleFactorY = 0.001f;
|
||||
if (_height <= 0)
|
||||
_height = 1;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::setFlipH(bool flipH) {
|
||||
_flipH = flipH;
|
||||
forceRefresh();
|
||||
}
|
||||
|
||||
void Bitmap::setFlipV(bool flipV) {
|
||||
_flipV = flipV;
|
||||
forceRefresh();
|
||||
}
|
||||
|
||||
bool Bitmap::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
result &= RenderObject::persist(writer);
|
||||
writer.write(_flipH);
|
||||
writer.write(_flipV);
|
||||
writer.write(_scaleFactorX);
|
||||
writer.write(_scaleFactorY);
|
||||
writer.write(_modulationColor);
|
||||
writer.write(_originalWidth);
|
||||
writer.write(_originalHeight);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bitmap::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
result &= RenderObject::unpersist(reader);
|
||||
reader.read(_flipH);
|
||||
reader.read(_flipV);
|
||||
reader.read(_scaleFactorX);
|
||||
reader.read(_scaleFactorY);
|
||||
reader.read(_modulationColor);
|
||||
reader.read(_originalWidth);
|
||||
reader.read(_originalHeight);
|
||||
|
||||
forceRefresh();
|
||||
|
||||
return reader.isGood() && result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
186
engines/sword25/gfx/bitmap.h
Normal file
186
engines/sword25/gfx/bitmap.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_BITMAP_H
|
||||
#define SWORD25_BITMAP_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "sword25/gfx/renderobject.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Bitmap : public RenderObject {
|
||||
protected:
|
||||
Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle = 0);
|
||||
|
||||
public:
|
||||
|
||||
~Bitmap() override;
|
||||
|
||||
/**
|
||||
@brief Setzt den Alphawert des Bitmaps.
|
||||
@param Alpha der neue Alphawert der Bitmaps (0 = keine Deckung, 255 = volle Deckung).
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
|
||||
*/
|
||||
void setAlpha(int alpha);
|
||||
|
||||
/**
|
||||
@brief Setzt die Modulationfarbe der Bitmaps.
|
||||
@param Color eine 24-Bit Farbe, die die Modulationsfarbe des Bitmaps festlegt.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
|
||||
*/
|
||||
void setModulationColor(uint modulationColor);
|
||||
|
||||
/**
|
||||
@brief Setzt den Skalierungsfaktor des Bitmaps.
|
||||
@param ScaleFactor der Faktor um den das Bitmap in beide Richtungen gestreckt werden soll.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
void setScaleFactor(float scaleFactor);
|
||||
|
||||
/**
|
||||
@brief Setzt den Skalierungsfaktor der Bitmap auf der X-Achse.
|
||||
@param ScaleFactor der Faktor um den die Bitmap in Richtungen der X-Achse gestreckt werden soll. Dieser Wert muss positiv sein.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
void setScaleFactorX(float scaleFactorX);
|
||||
|
||||
/**
|
||||
@brief Setzt den Skalierungsfaktor der Bitmap auf der Y-Achse.
|
||||
@param ScaleFactor der Faktor um den die Bitmap in Richtungen der Y-Achse gestreckt werden soll. Dieser Wert muss positiv sein.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
void setScaleFactorY(float scaleFactorY);
|
||||
|
||||
/**
|
||||
@brief Legt fest, ob das Bild an der X-Achse gespiegelt werden soll.
|
||||
*/
|
||||
void setFlipH(bool flipH);
|
||||
|
||||
/**
|
||||
@brief Legt fest, ob das Bild an der Y-Achse gespiegelt werden soll.
|
||||
*/
|
||||
void setFlipV(bool flipV);
|
||||
|
||||
/**
|
||||
@brief Gibt den aktuellen Alphawert des Bildes zurück.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
|
||||
*/
|
||||
int getAlpha() {
|
||||
return _modulationColor >> BS_ASHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt die aktuelle 24bit RGB Modulationsfarde des Bildes zurück.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
|
||||
*/
|
||||
int getModulationColor() {
|
||||
return _modulationColor & BS_RGBMASK;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt den Skalierungsfakter des Bitmaps auf der X-Achse zurück.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
float getScaleFactorX() const {
|
||||
return _scaleFactorX;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt den Skalierungsfakter des Bitmaps auf der Y-Achse zurück.
|
||||
@remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
|
||||
*/
|
||||
float getScaleFactorY() const {
|
||||
return _scaleFactorY;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt zurück, ob das Bild an der X-Achse gespiegelt angezeigt wird.
|
||||
*/
|
||||
bool isFlipH() {
|
||||
return _flipH;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt zurück, ob das Bild an der Y-Achse gespiegelt angezeigt wird.
|
||||
*/
|
||||
bool isFlipV() {
|
||||
return _flipV;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Die folgenden Methoden müssen alle BS_Bitmap-Klassen implementieren
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@brief Liest einen Pixel des Bildes.
|
||||
@param X die X-Koordinate des Pixels.
|
||||
@param Y die Y-Koordinate des Pixels
|
||||
@return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
|
||||
@remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
|
||||
eher dafür gedacht einzelne Pixel des Bildes auszulesen.
|
||||
*/
|
||||
virtual uint getPixel(int x, int y) const = 0;
|
||||
|
||||
/**
|
||||
@brief Füllt den Inhalt des Bildes mit Pixeldaten.
|
||||
@param Pixeldata ein Vector der die Pixeldaten enthält. Sie müssen in dem Farbformat des Bildes vorliegen und es müssen genügend Daten
|
||||
vorhanden sein, um das ganze Bild zu füllen.
|
||||
@param Offset der Offset in Byte im Pixeldata-Vector an dem sich der erste zu schreibende Pixel befindet.<br>
|
||||
Der Standardwert ist 0.
|
||||
@param Stride der Abstand in Byte zwischen dem Zeilenende und dem Beginn einer neuen Zeile im Pixeldata-Vector.<br>
|
||||
Der Standardwert ist 0.
|
||||
@return Gibt false zurück, falls der Aufruf fehlgeschlagen ist.
|
||||
@remark Ein Aufruf dieser Methode ist nur erlaubt, wenn IsSetContentAllowed() true zurückgibt.
|
||||
*/
|
||||
virtual bool setContent(const byte *pixeldata, uint size, uint offset = 0, uint stride = 0) = 0;
|
||||
|
||||
virtual bool isScalingAllowed() const = 0;
|
||||
virtual bool isAlphaAllowed() const = 0;
|
||||
virtual bool isColorModulationAllowed() const = 0;
|
||||
virtual bool isSetContentAllowed() const = 0;
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
protected:
|
||||
bool _flipH;
|
||||
bool _flipV;
|
||||
float _scaleFactorX;
|
||||
float _scaleFactorY;
|
||||
uint32 _modulationColor;
|
||||
int32 _originalWidth;
|
||||
int32 _originalHeight;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
200
engines/sword25/gfx/bitmapresource.h
Normal file
200
engines/sword25/gfx/bitmapresource.h
Normal file
@@ -0,0 +1,200 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_BITMAP_RESOURCE_H
|
||||
#define SWORD25_BITMAP_RESOURCE_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/resource.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class BitmapResource : public Resource {
|
||||
public:
|
||||
BitmapResource(const Common::String &filename, Image *pImage) :
|
||||
_pImage(pImage), Resource(filename, Resource::TYPE_BITMAP) {}
|
||||
~BitmapResource() override { delete _pImage; }
|
||||
|
||||
/**
|
||||
@brief Gibt zurück, ob das Objekt einen gültigen Zustand hat.
|
||||
*/
|
||||
bool isValid() const {
|
||||
return (_pImage != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt die Breite des Bitmaps zurück.
|
||||
*/
|
||||
int getWidth() const {
|
||||
assert(_pImage);
|
||||
return _pImage->getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt die Höhe des Bitmaps zurück.
|
||||
*/
|
||||
int getHeight() const {
|
||||
assert(_pImage);
|
||||
return _pImage->getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Rendert das Bild in den Framebuffer.
|
||||
@param PosX die Position auf der X-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
|
||||
Der Standardwert ist 0.
|
||||
@param PosY die Position auf der Y-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
|
||||
Der Standardwert ist 0.
|
||||
@param Flipping gibt an, wie das Bild gespiegelt werden soll.<br>
|
||||
Der Standardwert ist BS_Image::FLIP_NONE (keine Spiegelung)
|
||||
@param pSrcPartRect Pointer auf ein Common::Rect, welches den Ausschnitt des Quellbildes spezifiziert, der gerendert
|
||||
werden soll oder NULL, falls das gesamte Bild gerendert werden soll.<br>
|
||||
Dieser Ausschnitt bezieht sich auf das ungespiegelte und unskalierte Bild.<br>
|
||||
Der Standardwert ist NULL.
|
||||
@param Color ein ARGB Farbwert, der die Parameter für die Farbmodulation und fürs Alphablending festlegt.<br>
|
||||
Die Alpha-Komponente der Farbe bestimmt den Alphablending Parameter (0 = keine Deckung, 255 = volle Deckung).<br>
|
||||
Die Farbkomponenten geben die Farbe für die Farbmodulation an.<br>
|
||||
Der Standardwert is BS_ARGB(255, 255, 255, 255) (volle Deckung, keine Farbmodulation).
|
||||
Zum Erzeugen des Farbwertes können die Makros BS_RGB und BS_ARGB benutzt werden.
|
||||
@param Width gibt die Ausgabebreite des Bildausschnittes an.
|
||||
Falls diese von der Breite des Bildausschnittes abweicht wird
|
||||
das Bild entsprechend Skaliert.<br>
|
||||
Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
|
||||
Der Standardwert ist -1.
|
||||
@param Width gibt die Ausgabehöhe des Bildausschnittes an.
|
||||
Falls diese von der Höhe des Bildauschnittes abweicht, wird
|
||||
das Bild entsprechend Skaliert.<br>
|
||||
Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
|
||||
Der Standardwert ist -1.
|
||||
@return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
|
||||
@remark Er werden nicht alle Blitting-Operationen von allen BS_Image-Klassen unterstützt.<br>
|
||||
Mehr Informationen gibt es in der Klassenbeschreibung von BS_Image und durch folgende Methoden:
|
||||
- IsBlitTarget()
|
||||
- IsScalingAllowed()
|
||||
- IsFillingAllowed()
|
||||
- IsAlphaAllowed()
|
||||
- IsColorModulationAllowed()
|
||||
*/
|
||||
bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pSrcPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) {
|
||||
assert(_pImage);
|
||||
return _pImage->blit(posX, posY, flipping, pSrcPartRect, color, width, height, updateRects);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Füllt einen Rechteckigen Bereich des Bildes mit einer Farbe.
|
||||
@param pFillRect Pointer auf ein Common::Rect, welches den Ausschnitt des Bildes spezifiziert, der gefüllt
|
||||
werden soll oder NULL, falls das gesamte Bild gefüllt werden soll.<br>
|
||||
Der Standardwert ist NULL.
|
||||
@param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll.
|
||||
@remark Ein Aufruf dieser Methode ist nur gestattet, wenn IsFillingAllowed() true zurückgibt.
|
||||
@remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem Alphawert ungleich
|
||||
255 angibt.
|
||||
@remark Unabhängig vom Farbformat des Bildes muss ein 32 Bit Farbwert angegeben werden. Zur Erzeugung, können die Makros
|
||||
BS_RGB und BS_ARGB benutzt werden.
|
||||
@remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
|
||||
*/
|
||||
bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) {
|
||||
assert(_pImage);
|
||||
return _pImage->fill(pFillRect, color);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Liest einen Pixel des Bildes.
|
||||
@param X die X-Koordinate des Pixels.
|
||||
@param Y die Y-Koordinate des Pixels
|
||||
@return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
|
||||
@remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
|
||||
eher dafür gedacht einzelne Pixel des Bildes auszulesen.
|
||||
*/
|
||||
uint getPixel(int x, int y) const {
|
||||
return _pImage->getPixel(x, y);
|
||||
}
|
||||
|
||||
//@{
|
||||
/** @name Auskunfts-Methoden */
|
||||
|
||||
/**
|
||||
@brief Überprüft, ob das BS_Image ein Zielbild für einen Blit-Aufruf sein kann.
|
||||
@return Gibt false zurück, falls ein Blit-Aufruf mit diesem Objekt als Ziel nicht gestattet ist.
|
||||
*/
|
||||
bool isBlitTarget() {
|
||||
assert(_pImage);
|
||||
return _pImage->isBlitTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt true zurück, falls das BS_Image bei einem Aufruf von Blit() skaliert dargestellt werden kann.
|
||||
*/
|
||||
bool isScalingAllowed() {
|
||||
assert(_pImage);
|
||||
return _pImage->isScalingAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt true zurück, wenn das BS_Image mit einem Aufruf von Fill() gefüllt werden kann.
|
||||
*/
|
||||
bool isFillingAllowed() {
|
||||
assert(_pImage);
|
||||
return _pImage->isFillingAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit einem Alphawert dargestellt werden kann.
|
||||
*/
|
||||
bool isAlphaAllowed() {
|
||||
assert(_pImage);
|
||||
return _pImage->isAlphaAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit Farbmodulation dargestellt werden kann.
|
||||
*/
|
||||
bool isColorModulationAllowed() {
|
||||
assert(_pImage);
|
||||
return _pImage->isColorModulationAllowed();
|
||||
}
|
||||
|
||||
bool isSolid() {
|
||||
assert(_pImage);
|
||||
return _pImage->isSolid();
|
||||
}
|
||||
|
||||
private:
|
||||
Image *_pImage;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
179
engines/sword25/gfx/dynamicbitmap.cpp
Normal file
179
engines/sword25/gfx/dynamicbitmap.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/dynamicbitmap.h"
|
||||
#include "sword25/gfx/bitmapresource.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
DynamicBitmap::DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height) :
|
||||
Bitmap(parentPtr, TYPE_DYNAMICBITMAP) {
|
||||
// The BS_BITMAP could not be created, so stop.
|
||||
if (!_initSuccess)
|
||||
return;
|
||||
|
||||
_initSuccess = createRenderedImage(width, height);
|
||||
}
|
||||
|
||||
DynamicBitmap::DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
|
||||
Bitmap(parentPtr, TYPE_DYNAMICBITMAP, handle) {
|
||||
_initSuccess = unpersist(reader);
|
||||
}
|
||||
|
||||
bool DynamicBitmap::createRenderedImage(uint width, uint height) {
|
||||
bool result = false;
|
||||
_image.reset(new RenderedImage(width, height, result));
|
||||
|
||||
_originalWidth = _width = width;
|
||||
_originalHeight = _height = height;
|
||||
|
||||
_image->setAlphaType(Graphics::ALPHA_OPAQUE);
|
||||
_isSolid = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DynamicBitmap::~DynamicBitmap() {
|
||||
}
|
||||
|
||||
uint DynamicBitmap::getPixel(int x, int y) const {
|
||||
assert(x >= 0 && x < _width);
|
||||
assert(y >= 0 && y < _height);
|
||||
|
||||
return _image->getPixel(x, y);
|
||||
}
|
||||
|
||||
bool DynamicBitmap::doRender(RectangleList *updateRects) {
|
||||
// Get the frame buffer object
|
||||
GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
|
||||
assert(pGfx);
|
||||
|
||||
// Draw the bitmap
|
||||
bool result;
|
||||
if (_scaleFactorX == 1.0f && _scaleFactorY == 1.0f) {
|
||||
#if 1
|
||||
// This is what the game does originally, which can be
|
||||
// a bit slow when drawing videos, but it's not the main
|
||||
// bottleneck.
|
||||
result = _image->blit(_absoluteX, _absoluteY,
|
||||
(_flipV ? Graphics::FLIP_V : 0) |
|
||||
(_flipH ? Graphics::FLIP_H : 0),
|
||||
0, _modulationColor, -1, -1,
|
||||
updateRects);
|
||||
#else
|
||||
// WIP: A bit faster code
|
||||
|
||||
// We don't need to check for transparency when drawing
|
||||
// videos, thus just copy the buffer contents directly.
|
||||
// This messes up the fire animation in the menu, for
|
||||
// some odd reason. It also makes the video colors a
|
||||
// bit lighter, resulting in a visible black border
|
||||
// around the typed letters in the intro (which are
|
||||
// drawn over a black border)
|
||||
_image->copyDirectly(_absoluteX, _absoluteY);
|
||||
#endif
|
||||
return true;
|
||||
} else {
|
||||
result = _image->blit(_absoluteX, _absoluteY,
|
||||
(_flipV ? Graphics::FLIP_V : 0) |
|
||||
(_flipH ? Graphics::FLIP_H : 0),
|
||||
0, _modulationColor, _width, _height,
|
||||
updateRects);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DynamicBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
|
||||
++_version; // Update version to display the new video image
|
||||
return _image->setContent(pixeldata, size, offset, stride);
|
||||
}
|
||||
|
||||
bool DynamicBitmap::isScalingAllowed() const {
|
||||
return _image->isScalingAllowed();
|
||||
}
|
||||
|
||||
bool DynamicBitmap::isAlphaAllowed() const {
|
||||
return _image->isAlphaAllowed();
|
||||
}
|
||||
|
||||
bool DynamicBitmap::isColorModulationAllowed() const {
|
||||
return _image->isColorModulationAllowed();
|
||||
}
|
||||
|
||||
bool DynamicBitmap::isSetContentAllowed() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DynamicBitmap::persist(OutputPersistenceBlock &writer) {
|
||||
#if 0
|
||||
bool result = true;
|
||||
|
||||
result &= Bitmap::persist(writer);
|
||||
|
||||
// Image data is not saved. This is not important, since BS_DynamicBitmap is only used by the video player.
|
||||
// A video cannot be saved while it's being played. Only the state of a BS_DynamicBitmap can be persisted.
|
||||
warning("Persisting a BS_DynamicBitmap. Bitmap content is not persisted.");
|
||||
|
||||
result &= RenderObject::persistChildren(writer);
|
||||
|
||||
return result;
|
||||
#endif
|
||||
|
||||
error("Request to persist a dynamic bitmap (video) - probably a bug");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DynamicBitmap::unpersist(InputPersistenceBlock &reader) {
|
||||
#if 0
|
||||
bool result = true;
|
||||
|
||||
result &= Bitmap::unpersist(reader);
|
||||
result &= createRenderedImage(_width, _height);
|
||||
|
||||
warning("Unpersisting a BS_DynamicBitmap. Bitmap contents are missing.");
|
||||
|
||||
// Initialize a transparent image.
|
||||
byte *transparentImageData = (byte *)calloc(_width * _height * 4, 1);
|
||||
_image->setContent(transparentImageData, _width * _height);
|
||||
free(transparentImageData);
|
||||
|
||||
result &= RenderObject::unpersistChildren(reader);
|
||||
|
||||
return reader.isGood() && result;
|
||||
#endif
|
||||
|
||||
error("Request to unpersist a dynamic bitmap (video) - probably a corrupted saved game or a bug");
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
74
engines/sword25/gfx/dynamicbitmap.h
Normal file
74
engines/sword25/gfx/dynamicbitmap.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_DYNAMIC_BITMAP_H
|
||||
#define SWORD25_DYNAMIC_BITMAP_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/bitmap.h"
|
||||
#include "sword25/gfx/image/renderedimage.h"
|
||||
|
||||
#include "common/ptr.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class DynamicBitmap : public Bitmap {
|
||||
friend class RenderObject;
|
||||
|
||||
public:
|
||||
~DynamicBitmap() override;
|
||||
|
||||
uint getPixel(int x, int y) const override;
|
||||
|
||||
bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) override;
|
||||
|
||||
bool isScalingAllowed() const override;
|
||||
bool isAlphaAllowed() const override;
|
||||
bool isColorModulationAllowed() const override;
|
||||
bool isSetContentAllowed() const override;
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
protected:
|
||||
bool doRender(RectangleList *updateRects) override;
|
||||
|
||||
private:
|
||||
DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height);
|
||||
DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
|
||||
|
||||
bool createRenderedImage(uint width, uint height);
|
||||
|
||||
Common::ScopedPtr<RenderedImage> _image;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
138
engines/sword25/gfx/fontresource.cpp
Normal file
138
engines/sword25/gfx/fontresource.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/resmanager.h" // for PRECACHE_RESOURCES
|
||||
#include "sword25/package/packagemanager.h"
|
||||
|
||||
#include "sword25/gfx/fontresource.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
enum {
|
||||
DEFAULT_LINEHEIGHT = 20,
|
||||
DEFAULT_GAPWIDTH = 1
|
||||
};
|
||||
|
||||
FontResource::FontResource(Kernel *pKernel, const Common::String &fileName) :
|
||||
_pKernel(pKernel),
|
||||
_valid(false),
|
||||
Resource(fileName, Resource::TYPE_FONT),
|
||||
Common::XMLParser() {
|
||||
|
||||
// Get a pointer to the package manager
|
||||
assert(_pKernel);
|
||||
PackageManager *pPackage = _pKernel->getPackage();
|
||||
assert(pPackage);
|
||||
|
||||
// Load the contents of the file
|
||||
uint fileSize;
|
||||
char *xmlData = pPackage->getXmlFile(getFileName(), &fileSize);
|
||||
if (!xmlData) {
|
||||
error("Could not read \"%s\".", getFileName().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the contents
|
||||
if (!loadBuffer((const byte *)xmlData, fileSize))
|
||||
return;
|
||||
|
||||
_valid = parse();
|
||||
close();
|
||||
free(xmlData);
|
||||
}
|
||||
|
||||
bool FontResource::parserCallback_font(ParserNode *node) {
|
||||
// Get the attributes of the font
|
||||
Common::String bitmapFilename = node->values["bitmap"];
|
||||
|
||||
if (!parseIntegerKey(node->values["lineheight"], 1, &_lineHeight)) {
|
||||
warning("Illegal or missing lineheight attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
|
||||
getFileName().c_str(), DEFAULT_LINEHEIGHT);
|
||||
_lineHeight = DEFAULT_LINEHEIGHT;
|
||||
}
|
||||
|
||||
if (!parseIntegerKey(node->values["gap"], 1, &_gapWidth)) {
|
||||
warning("Illegal or missing gap attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
|
||||
getFileName().c_str(), DEFAULT_GAPWIDTH);
|
||||
_gapWidth = DEFAULT_GAPWIDTH;
|
||||
}
|
||||
|
||||
// Get a reference to the package manager
|
||||
assert(_pKernel);
|
||||
PackageManager *pPackage = _pKernel->getPackage();
|
||||
assert(pPackage);
|
||||
|
||||
// Get the full path and filename for the bitmap resource
|
||||
_bitmapFileName = pPackage->getAbsolutePath(bitmapFilename);
|
||||
if (_bitmapFileName == "") {
|
||||
error("Image file \"%s\" was specified in <font> tag of \"%s\" but could not be found.",
|
||||
_bitmapFileName.c_str(), getFileName().c_str());
|
||||
}
|
||||
|
||||
#ifdef PRECACHE_RESOURCES
|
||||
// Pre-cache the resource
|
||||
if (!_pKernel->getResourceManager()->precacheResource(_bitmapFileName)) {
|
||||
error("Could not precache \"%s\".", _bitmapFileName.c_str());
|
||||
}
|
||||
#else
|
||||
Resource *pResource = _pKernel->getResourceManager()->requestResource(_bitmapFileName);
|
||||
pResource->release(); //unlock precached resource
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FontResource::parserCallback_character(ParserNode *node) {
|
||||
// Get the attributes of the character
|
||||
int charCode, top, left, right, bottom;
|
||||
|
||||
if (!parseIntegerKey(node->values["code"], 1, &charCode) || (charCode < 0) || (charCode >= 256)) {
|
||||
return parserError("Illegal or missing code attribute in <character> tag in '" + getFileName() + "'.");
|
||||
}
|
||||
|
||||
if (!parseIntegerKey(node->values["top"], 1, &top) || (top < 0)) {
|
||||
return parserError("Illegal or missing top attribute in <character> tag in '" + getFileName() + "'.");
|
||||
}
|
||||
if (!parseIntegerKey(node->values["left"], 1, &left) || (left < 0)) {
|
||||
return parserError("Illegal or missing left attribute in <character> tag in '" + getFileName() + "'.");
|
||||
}
|
||||
if (!parseIntegerKey(node->values["right"], 1, &right) || (right < 0)) {
|
||||
return parserError("Illegal or missing right attribute in <character> tag in '" + getFileName() + "'.");
|
||||
}
|
||||
if (!parseIntegerKey(node->values["bottom"], 1, &bottom) || (bottom < 0)) {
|
||||
return parserError("Illegal or missing bottom attribute in <character> tag in '" + getFileName() + "'.");
|
||||
}
|
||||
|
||||
_characterRects[charCode] = Common::Rect(left, top, right, bottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
130
engines/sword25/gfx/fontresource.h
Normal file
130
engines/sword25/gfx/fontresource.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_FONTRESOURCE_H
|
||||
#define SWORD25_FONTRESOURCE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/formats/xmlparser.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/resource.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
|
||||
class FontResource : public Resource, Common::XMLParser {
|
||||
public:
|
||||
/**
|
||||
@brief Erzeugt eine neues Exemplar von BS_FontResource
|
||||
@param pKernel ein Pointer auf den Kernel
|
||||
@param FileName der Dateiname der zu ladenen Resource
|
||||
@remark Wenn der Konstruktor erfolgreich ausgeführt werden konnte gibt die Methode IsValid true zurück.
|
||||
*/
|
||||
FontResource(Kernel *pKernel, const Common::String &fileName);
|
||||
|
||||
/**
|
||||
@brief Gibt true zurück, wenn das Objekt korrekt initialisiert wurde.
|
||||
|
||||
Diese Methode kann dazu benutzt werden um festzustellen, ob der Konstruktor erfolgreich ausgeführt wurde.
|
||||
*/
|
||||
bool isValid() const {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt die Zeilenhöhe des Fonts in Pixeln zurück.
|
||||
|
||||
Die Zeilenhöhe ist der Wert, der zur Y-Koordinate addiert wird, wenn ein Zeilenumbruch auftritt.
|
||||
*/
|
||||
int getLineHeight() const {
|
||||
return _lineHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt den Buchstabenabstand der Fonts in Pixeln zurück.
|
||||
|
||||
Der Buchstabenabstand ist der Wert, der zwischen zwei Buchstaben freigelassen wird.
|
||||
*/
|
||||
int getGapWidth() const {
|
||||
return _gapWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt das Bounding-Rect eines Zeichens auf der Charactermap zurück.
|
||||
@param Character der ASCII-Code des Zeichens
|
||||
@return Das Bounding-Rect des übergebenen Zeichens auf der Charactermap.
|
||||
*/
|
||||
const Common::Rect &getCharacterRect(int character) const {
|
||||
assert(character >= 0 && character < 256);
|
||||
return _characterRects[character];
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt den Dateinamen der Charactermap zurück.
|
||||
*/
|
||||
const Common::String &getCharactermapFileName() const {
|
||||
return _bitmapFileName;
|
||||
}
|
||||
|
||||
private:
|
||||
Kernel *_pKernel;
|
||||
bool _valid;
|
||||
Common::String _bitmapFileName;
|
||||
int _lineHeight;
|
||||
int _gapWidth;
|
||||
Common::Rect _characterRects[256];
|
||||
|
||||
// Parser
|
||||
CUSTOM_XML_PARSER(FontResource) {
|
||||
XML_KEY(font)
|
||||
XML_PROP(bitmap, true)
|
||||
XML_PROP(lineheight, false)
|
||||
XML_PROP(gap, false)
|
||||
|
||||
XML_KEY(character)
|
||||
XML_PROP(code, true)
|
||||
XML_PROP(left, true)
|
||||
XML_PROP(top, true)
|
||||
XML_PROP(right, true)
|
||||
XML_PROP(bottom, true)
|
||||
KEY_END()
|
||||
KEY_END()
|
||||
} PARSER_END()
|
||||
|
||||
// Parser callback methods
|
||||
bool parserCallback_font(ParserNode *node);
|
||||
bool parserCallback_character(ParserNode *node);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
498
engines/sword25/gfx/graphicengine.cpp
Normal file
498
engines/sword25/gfx/graphicengine.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "sword25/sword25.h" // for kDebugScript
|
||||
#include "sword25/gfx/bitmapresource.h"
|
||||
#include "sword25/gfx/animationresource.h"
|
||||
#include "sword25/gfx/fontresource.h"
|
||||
#include "sword25/gfx/panel.h"
|
||||
#include "sword25/gfx/renderobjectmanager.h"
|
||||
#include "sword25/gfx/screenshot.h"
|
||||
#include "sword25/gfx/image/renderedimage.h"
|
||||
#include "sword25/gfx/image/swimage.h"
|
||||
#include "sword25/gfx/image/vectorimage.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
|
||||
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
#include "sword25/fmv/movieplayer.h"
|
||||
|
||||
#include "common/lua/lua.h"
|
||||
#include "common/lua/lauxlib.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
enum {
|
||||
BIT_DEPTH = 32,
|
||||
BACKBUFFER_COUNT = 1
|
||||
};
|
||||
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
static const uint FRAMETIME_SAMPLE_COUNT = 5; // Frame duration is averaged over FRAMETIME_SAMPLE_COUNT frames
|
||||
|
||||
GraphicEngine::GraphicEngine(Kernel *pKernel) :
|
||||
_width(0),
|
||||
_height(0),
|
||||
_bitDepth(0),
|
||||
_lastTimeStamp((uint) -1), // force reset of _UpdateLastFrameDuration() on first call
|
||||
_lastFrameDuration(0),
|
||||
_timerActive(true),
|
||||
_frameTimeSampleSlot(0),
|
||||
_thumbnail(NULL),
|
||||
ResourceService(pKernel) {
|
||||
_frameTimeSamples.resize(FRAMETIME_SAMPLE_COUNT);
|
||||
|
||||
if (!registerScriptBindings())
|
||||
error("Script bindings could not be registered.");
|
||||
else
|
||||
debugC(kDebugScript, "Script bindings registered.");
|
||||
}
|
||||
|
||||
GraphicEngine::~GraphicEngine() {
|
||||
unregisterScriptBindings();
|
||||
_backSurface.free();
|
||||
delete _thumbnail;
|
||||
}
|
||||
|
||||
bool GraphicEngine::init(int width, int height, int bitDepth, int backbufferCount) {
|
||||
// Warn when an unsupported bit depth has been selected.
|
||||
if (bitDepth != BIT_DEPTH) {
|
||||
warning("Can't use a bit depth of %d (not supported). Falling back to %d.", bitDepth, BIT_DEPTH);
|
||||
_bitDepth = BIT_DEPTH;
|
||||
}
|
||||
|
||||
// Warn when wrong BackBuffer is specified.
|
||||
if (backbufferCount != BACKBUFFER_COUNT) {
|
||||
warning("Can't use %d backbuffers (not supported). Falling back to %d.", backbufferCount, BACKBUFFER_COUNT);
|
||||
backbufferCount = BACKBUFFER_COUNT;
|
||||
}
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
_bitDepth = bitDepth;
|
||||
_screenRect.left = 0;
|
||||
_screenRect.top = 0;
|
||||
_screenRect.right = _width;
|
||||
_screenRect.bottom = _height;
|
||||
_isRTL = Common::parseLanguage(ConfMan.get("language")) == Common::HE_ISR;
|
||||
|
||||
const Graphics::PixelFormat format = g_system->getScreenFormat();
|
||||
|
||||
_backSurface.create(width, height, format);
|
||||
|
||||
// By default Vsync is on.
|
||||
setVsync(true);
|
||||
|
||||
// Layer-Manager initialization
|
||||
_renderObjectManagerPtr.reset(new RenderObjectManager(width, height, backbufferCount + 1));
|
||||
|
||||
// Create the main panel
|
||||
_mainPanelPtr = _renderObjectManagerPtr->getTreeRoot()->addPanel(width, height, BS_ARGB(0, 0, 0, 0));
|
||||
if (!_mainPanelPtr.isValid())
|
||||
return false;
|
||||
_mainPanelPtr->setVisible(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphicEngine::startFrame(bool updateAll) {
|
||||
// Calculate how much time has elapsed since the last frame.
|
||||
updateLastFrameDuration();
|
||||
|
||||
// Prepare the Layer Manager for the next frame
|
||||
_renderObjectManagerPtr->startFrame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphicEngine::endFrame() {
|
||||
#ifndef THEORA_INDIRECT_RENDERING
|
||||
if (Kernel::getInstance()->getFMV()->isMovieLoaded())
|
||||
return true;
|
||||
#endif
|
||||
|
||||
_renderObjectManagerPtr->render();
|
||||
|
||||
g_system->updateScreen();
|
||||
|
||||
// Debug-Lines zeichnen
|
||||
if (!_debugLines.empty()) {
|
||||
#if 0
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glBegin(GL_LINES);
|
||||
|
||||
Common::Array<DebugLine>::const_iterator iter = m_DebugLines.begin();
|
||||
for (; iter != m_DebugLines.end(); ++iter) {
|
||||
const uint &Color = (*iter).Color;
|
||||
const BS_Vertex &Start = (*iter).Start;
|
||||
const BS_Vertex &End = (*iter).End;
|
||||
|
||||
glColor4ub((Color >> 16) & 0xff, (Color >> 8) & 0xff, Color & 0xff, Color >> 24);
|
||||
glVertex2d(Start.X, Start.Y);
|
||||
glVertex2d(End.X, End.Y);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
#endif
|
||||
|
||||
warning("STUB: Drawing debug lines");
|
||||
|
||||
_debugLines.clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderObjectPtr<Panel> GraphicEngine::getMainPanel() {
|
||||
return _mainPanelPtr;
|
||||
}
|
||||
|
||||
void GraphicEngine::setVsync(bool vsync) {
|
||||
// ScummVM has no concept of VSync
|
||||
}
|
||||
|
||||
bool GraphicEngine::getVsync() const {
|
||||
warning("STUB: getVsync()");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphicEngine::fill(const Common::Rect *fillRectPtr, uint color) {
|
||||
Common::Rect rect(_width - 1, _height - 1);
|
||||
|
||||
int ca = (color >> BS_ASHIFT) & 0xff;
|
||||
|
||||
if (ca == 0)
|
||||
return true;
|
||||
|
||||
int cr = (color >> BS_RSHIFT) & 0xff;
|
||||
int cg = (color >> BS_GSHIFT) & 0xff;
|
||||
int cb = (color >> BS_BSHIFT) & 0xff;
|
||||
|
||||
if (fillRectPtr) {
|
||||
rect = *fillRectPtr;
|
||||
}
|
||||
|
||||
if (rect.width() > 0 && rect.height() > 0) {
|
||||
if (ca == 0xff) {
|
||||
_backSurface.fillRect(rect, _backSurface.format.ARGBToColor(ca, cr, cg, cb));
|
||||
} else {
|
||||
byte *outo = (byte *)_backSurface.getBasePtr(rect.left, rect.top);
|
||||
byte *out;
|
||||
|
||||
for (int i = rect.top; i < rect.bottom; i++) {
|
||||
out = outo;
|
||||
for (int j = rect.left; j < rect.right; j++) {
|
||||
#if defined(SCUMM_LITTLE_ENDIAN)
|
||||
*out = 255;
|
||||
out++;
|
||||
*out += (byte)(((cb - *out) * ca) >> 8);
|
||||
out++;
|
||||
*out += (byte)(((cg - *out) * ca) >> 8);
|
||||
out++;
|
||||
*out += (byte)(((cr - *out) * ca) >> 8);
|
||||
out++;
|
||||
#else
|
||||
*out += (byte)(((cr - *out) * ca) >> 8);
|
||||
out++;
|
||||
*out += (byte)(((cg - *out) * ca) >> 8);
|
||||
out++;
|
||||
*out += (byte)(((cb - *out) * ca) >> 8);
|
||||
out++;
|
||||
*out = 255;
|
||||
out++;
|
||||
#endif
|
||||
}
|
||||
|
||||
outo += _backSurface.pitch;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RESOURCE MANAGING
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Resource *GraphicEngine::loadResource(const Common::String &filename) {
|
||||
assert(canLoadResource(filename));
|
||||
|
||||
// Load image for "software buffer". These are images used to dynamically
|
||||
// add shadows to actors (e.g. when George walks under the shadow of a
|
||||
// a building)
|
||||
if (filename.hasSuffix("_s.png")) {
|
||||
bool result = false;
|
||||
SWImage *pImage = new SWImage(filename, result);
|
||||
if (!result) {
|
||||
delete pImage;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BitmapResource *pResource = new BitmapResource(filename, pImage);
|
||||
if (!pResource->isValid()) {
|
||||
delete pResource;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pResource;
|
||||
}
|
||||
|
||||
// Load sprite image. Savegame thumbnails are also loaded here.
|
||||
if (filename.hasSuffix(".png") || filename.hasSuffix(".b25s") ||
|
||||
filename.hasPrefix("/saves")) {
|
||||
bool result = false;
|
||||
RenderedImage *pImage = new RenderedImage(filename, result);
|
||||
if (!result) {
|
||||
delete pImage;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BitmapResource *pResource = new BitmapResource(filename, pImage);
|
||||
if (!pResource->isValid()) {
|
||||
delete pResource;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pResource;
|
||||
}
|
||||
|
||||
|
||||
// Load vector graphics
|
||||
if (filename.hasSuffix(".swf")) {
|
||||
debug(2, "VectorImage: %s", filename.c_str());
|
||||
|
||||
// Pointer auf Package-Manager holen
|
||||
PackageManager *pPackage = Kernel::getInstance()->getPackage();
|
||||
assert(pPackage);
|
||||
|
||||
// Loading data
|
||||
byte *pFileData;
|
||||
uint fileSize;
|
||||
pFileData = pPackage->getFile(filename, &fileSize);
|
||||
if (!pFileData) {
|
||||
error("File \"%s\" could not be loaded.", filename.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
VectorImage *pImage = new VectorImage(pFileData, fileSize, result, filename);
|
||||
if (!result) {
|
||||
delete pImage;
|
||||
delete[] pFileData;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BitmapResource *pResource = new BitmapResource(filename, pImage);
|
||||
if (!pResource->isValid()) {
|
||||
delete pResource;
|
||||
delete[] pFileData;
|
||||
return 0;
|
||||
}
|
||||
|
||||
delete[] pFileData;
|
||||
return pResource;
|
||||
}
|
||||
|
||||
// Load animation
|
||||
if (filename.hasSuffix("_ani.xml")) {
|
||||
AnimationResource *pResource = new AnimationResource(filename);
|
||||
if (pResource->isValid())
|
||||
return pResource;
|
||||
else {
|
||||
delete pResource;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Load font
|
||||
if (filename.hasSuffix("_fnt.xml")) {
|
||||
FontResource *pResource = new FontResource(Kernel::getInstance(), filename);
|
||||
if (pResource->isValid())
|
||||
return pResource;
|
||||
else {
|
||||
delete pResource;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
error("Service cannot load \"%s\".", filename.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool GraphicEngine::canLoadResource(const Common::String &filename) {
|
||||
return filename.hasSuffix(".png") ||
|
||||
filename.hasSuffix("_ani.xml") ||
|
||||
filename.hasSuffix("_fnt.xml") ||
|
||||
filename.hasSuffix(".swf") ||
|
||||
filename.hasSuffix(".b25s") ||
|
||||
filename.hasPrefix("/saves");
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// DEBUGGING
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void GraphicEngine::drawDebugLine(const Vertex &start, const Vertex &end, uint color) {
|
||||
_debugLines.push_back(DebugLine(start, end, color));
|
||||
}
|
||||
|
||||
void GraphicEngine::updateLastFrameDuration() {
|
||||
// Record current time
|
||||
const uint currentTime = Kernel::getInstance()->getMilliTicks();
|
||||
|
||||
// Compute the elapsed time since the last frame and prevent too big ( > 250 msecs) time jumps.
|
||||
// These can occur when loading save states, during debugging or due to hardware inaccuracies.
|
||||
_frameTimeSamples[_frameTimeSampleSlot] = static_cast<uint>(currentTime - _lastTimeStamp);
|
||||
if (_frameTimeSamples[_frameTimeSampleSlot] > 250000)
|
||||
_frameTimeSamples[_frameTimeSampleSlot] = 250000;
|
||||
_frameTimeSampleSlot = (_frameTimeSampleSlot + 1) % FRAMETIME_SAMPLE_COUNT;
|
||||
|
||||
// Compute the average frame duration over multiple frames to eliminate outliers.
|
||||
Common::Array<uint>::const_iterator it = _frameTimeSamples.begin();
|
||||
uint sum = *it;
|
||||
for (it++; it != _frameTimeSamples.end(); it++)
|
||||
sum += *it;
|
||||
_lastFrameDuration = sum * 1000 / FRAMETIME_SAMPLE_COUNT;
|
||||
|
||||
// Update m_LastTimeStamp with the current frame's timestamp
|
||||
_lastTimeStamp = currentTime;
|
||||
}
|
||||
|
||||
bool GraphicEngine::saveThumbnailScreenshot(const Common::String &filename) {
|
||||
// Note: In ScummVM, rather than saving the thumbnail to a file, we store it in memory
|
||||
// until needed when creating savegame files
|
||||
delete _thumbnail;
|
||||
|
||||
_thumbnail = Screenshot::createThumbnail(_backSurface.surfacePtr());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicEngine::ARGBColorToLuaColor(lua_State *L, uint color) {
|
||||
lua_Number components[4] = {
|
||||
(lua_Number)((color >> BS_RSHIFT) & 0xff), // Red
|
||||
(lua_Number)((color >> BS_GSHIFT) & 0xff), // Green
|
||||
(lua_Number)((color >> BS_BSHIFT) & 0xff), // Blue
|
||||
(lua_Number)( color >> BS_ASHIFT), // Alpha
|
||||
};
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
for (uint i = 1; i <= 4; i++) {
|
||||
lua_pushnumber(L, i);
|
||||
lua_pushnumber(L, components[i - 1]);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
}
|
||||
|
||||
uint GraphicEngine::luaColorToARGBColor(lua_State *L, int stackIndex) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// Make sure that we really look at a table
|
||||
luaL_checktype(L, stackIndex, LUA_TTABLE);
|
||||
// getting table size
|
||||
uint n = luaL_getn(L, stackIndex);
|
||||
// only RGB or RGBA colors are supported
|
||||
if (n != 3 && n != 4)
|
||||
luaL_argcheck(L, 0, stackIndex, "at least 3 of the 4 color components have to be specified");
|
||||
|
||||
// Red color component reading
|
||||
lua_rawgeti(L, stackIndex, 1);
|
||||
uint red = static_cast<uint>(lua_tonumber(L, -1));
|
||||
if (!lua_isnumber(L, -1) || red >= 256)
|
||||
luaL_argcheck(L, 0, stackIndex, "red color component must be an integer between 0 and 255");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Green color component reading
|
||||
lua_rawgeti(L, stackIndex, 2);
|
||||
uint green = static_cast<uint>(lua_tonumber(L, -1));
|
||||
if (!lua_isnumber(L, -1) || green >= 256)
|
||||
luaL_argcheck(L, 0, stackIndex, "green color component must be an integer between 0 and 255");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Blue color component reading
|
||||
lua_rawgeti(L, stackIndex, 3);
|
||||
uint blue = static_cast<uint>(lua_tonumber(L, -1));
|
||||
if (!lua_isnumber(L, -1) || blue >= 256)
|
||||
luaL_argcheck(L, 0, stackIndex, "blue color component must be an integer between 0 and 255");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Alpha color component reading
|
||||
uint alpha = 0xff;
|
||||
if (n == 4) {
|
||||
lua_rawgeti(L, stackIndex, 4);
|
||||
alpha = static_cast<uint>(lua_tonumber(L, -1));
|
||||
if (!lua_isnumber(L, -1) || alpha >= 256)
|
||||
luaL_argcheck(L, 0, stackIndex, "alpha color component must be an integer between 0 and 255");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return BS_ARGB(alpha, red, green, blue);
|
||||
}
|
||||
|
||||
bool GraphicEngine::persist(OutputPersistenceBlock &writer) {
|
||||
writer.write(_timerActive);
|
||||
|
||||
bool result = _renderObjectManagerPtr->persist(writer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GraphicEngine::unpersist(InputPersistenceBlock &reader) {
|
||||
reader.read(_timerActive);
|
||||
_renderObjectManagerPtr->unpersist(reader);
|
||||
|
||||
return reader.isGood();
|
||||
}
|
||||
|
||||
bool GraphicEngine::isRTL() {
|
||||
return _isRTL;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
309
engines/sword25/gfx/graphicengine.h
Normal file
309
engines/sword25/gfx/graphicengine.h
Normal file
@@ -0,0 +1,309 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* GraphicEngine
|
||||
* ----------------
|
||||
* This the graphics engine interface.
|
||||
*
|
||||
* Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_GRAPHICENGINE_H
|
||||
#define SWORD25_GRAPHICENGINE_H
|
||||
|
||||
// Includes
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/str.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/resservice.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
#include "sword25/gfx/renderobjectptr.h"
|
||||
#include "sword25/math/vertex.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
class Image;
|
||||
class Panel;
|
||||
class Screenshot;
|
||||
class RenderObjectManager;
|
||||
|
||||
#define BS_ASHIFT 24
|
||||
#define BS_RSHIFT 16
|
||||
#define BS_GSHIFT 8
|
||||
#define BS_BSHIFT 0
|
||||
|
||||
#define BS_AMASK 0xFF000000
|
||||
#define BS_RMASK 0x00FF0000
|
||||
#define BS_GMASK 0x0000FF00
|
||||
#define BS_BMASK 0x000000FF
|
||||
|
||||
#define BS_RGBMASK (BS_RMASK | BS_GMASK | BS_BMASK)
|
||||
#define BS_ARGBMASK (BS_AMASK | BS_RMASK | BS_GMASK | BS_BMASK)
|
||||
|
||||
#define BS_RGB(R,G,B) (BS_AMASK | ((R) << BS_RSHIFT) | ((G) << BS_GSHIFT) | ((B) << BS_BSHIFT))
|
||||
#define BS_ARGB(A,R,G,B) (((A) << BS_ASHIFT) | ((R) << BS_RSHIFT) | ((G) << BS_GSHIFT) | ((B) << BS_BSHIFT))
|
||||
|
||||
/**
|
||||
* This is the graphics engine. Unlike the original code, this is not
|
||||
* an interface that needs to be subclassed, but rather already contains
|
||||
* all required functionality.
|
||||
*/
|
||||
class GraphicEngine : public ResourceService, public Persistable {
|
||||
public:
|
||||
// Constructor
|
||||
// -----------
|
||||
GraphicEngine(Kernel *pKernel);
|
||||
~GraphicEngine() override;
|
||||
|
||||
// Interface
|
||||
// ---------
|
||||
|
||||
/**
|
||||
* Initializes the graphics engine and sets the screen mode. Returns
|
||||
* true if initialisation failed.
|
||||
* @note This method should be called immediately after the
|
||||
* initialisation of all services.
|
||||
*
|
||||
* @param Height The height of the output buffer in pixels. The default value is 600
|
||||
* @param BitDepth The bit depth of the desired output buffer in bits. The default value is 16
|
||||
* @param BackbufferCount The number of back buffers to be created. The default value is 2
|
||||
*/
|
||||
bool init(int width = 800, int height = 600, int bitDepth = 16, int backbufferCount = 2);
|
||||
|
||||
/**
|
||||
* Begins rendering a new frame.
|
||||
* Notes: This method must be called at the beginning of the main loop, before any rendering methods are used.
|
||||
* Notes: Implementations of this method must call _UpdateLastFrameDuration()
|
||||
* @param UpdateAll Specifies whether the renderer should redraw everything on the next frame.
|
||||
* This feature can be useful if the renderer with Dirty Rectangles works, but sometimes the client may
|
||||
*/
|
||||
bool startFrame(bool updateAll = false);
|
||||
|
||||
/**
|
||||
* Ends the rendering of a frame and draws it on the screen.
|
||||
*
|
||||
* This method must be at the end of the main loop. After this call, no further Render method may be called.
|
||||
* This should only be called once for a given previous call to #StartFrame.
|
||||
*/
|
||||
bool endFrame();
|
||||
|
||||
// Debug methods
|
||||
|
||||
/**
|
||||
* Draws a line in the frame buffer
|
||||
*
|
||||
* This method must be called between calls to StartFrame() and EndFrame(), and is intended only for debugging
|
||||
* purposes. The line will only appear for a single frame. If the line is to be shown permanently, it must be
|
||||
* called for every frame.
|
||||
* @param Start The starting point of the line
|
||||
* @param End The ending point of the line
|
||||
* @param Color The color of the line. The default is BS_RGB (255,255,255) (White)
|
||||
*/
|
||||
void drawDebugLine(const Vertex &start, const Vertex &end, uint color = BS_RGB(255, 255, 255));
|
||||
|
||||
/**
|
||||
* Creates a thumbnail with the dimensions of 200x125. This will not include the top and bottom of the screen..
|
||||
* the interface boards the image as a 16th of it's original size.
|
||||
* Notes: This method should only be called after a call to EndFrame(), and before the next call to StartFrame().
|
||||
* The frame buffer must have a resolution of 800x600.
|
||||
* @param Filename The filename for the screenshot
|
||||
*/
|
||||
bool saveThumbnailScreenshot(const Common::String &filename);
|
||||
|
||||
RenderObjectPtr<Panel> getMainPanel();
|
||||
|
||||
/**
|
||||
* Specifies the time (in microseconds) since the last frame has passed
|
||||
*/
|
||||
int getLastFrameDurationMicro() const {
|
||||
if (!_timerActive)
|
||||
return 0;
|
||||
return _lastFrameDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the time (in microseconds) the previous frame took
|
||||
*/
|
||||
float getLastFrameDuration() const {
|
||||
if (!_timerActive)
|
||||
return 0;
|
||||
return static_cast<float>(_lastFrameDuration) / 1000000.0f;
|
||||
}
|
||||
|
||||
void stopMainTimer() {
|
||||
_timerActive = false;
|
||||
}
|
||||
|
||||
void resumeMainTimer() {
|
||||
_timerActive = true;
|
||||
}
|
||||
|
||||
float getSecondaryFrameDuration() const {
|
||||
return static_cast<float>(_lastFrameDuration) / 1000000.0f;
|
||||
}
|
||||
|
||||
// Accessor methods
|
||||
|
||||
/**
|
||||
* Returns the width of the output buffer in pixels
|
||||
*/
|
||||
int getDisplayWidth() const {
|
||||
return _width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the output buffer in pixels
|
||||
*/
|
||||
int getDisplayHeight() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounding box of the output buffer: (0, 0, Width, Height)
|
||||
*/
|
||||
Common::Rect &getDisplayRect() {
|
||||
return _screenRect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bit depth of the output buffer
|
||||
*/
|
||||
int getBitDepth() {
|
||||
return _bitDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the frame buffer change is to be synchronised with Vsync. This is turned on by default.
|
||||
* Notes: In windowed mode, this setting has no effect.
|
||||
* @param Vsync Indicates whether the frame buffer changes are to be synchronised with Vsync.
|
||||
*/
|
||||
void setVsync(bool vsync);
|
||||
|
||||
/**
|
||||
* Returns true if V-Sync is on.
|
||||
* Notes: In windowed mode, this setting has no effect.
|
||||
*/
|
||||
bool getVsync() const;
|
||||
|
||||
/**
|
||||
* Returns true if the engine is running in Windowed mode.
|
||||
*/
|
||||
bool isWindowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills a rectangular area of the frame buffer with a color.
|
||||
* Notes: It is possible to create transparent rectangles by passing a color with an Alpha value of 255.
|
||||
* @param FillRectPtr Pointer to a Common::Rect, which specifies the section of the frame buffer to be filled.
|
||||
* If the rectangle falls partly off-screen, then it is automatically trimmed.
|
||||
* If a NULL value is passed, then the entire image is to be filled.
|
||||
* @param Color The 32-bit color with which the area is to be filled. The default is BS_RGB(0, 0, 0) (black)
|
||||
* @note FIf the rectangle is not completely inside the screen, it is automatically clipped.
|
||||
*/
|
||||
bool fill(const Common::Rect *fillRectPtr = 0, uint color = BS_RGB(0, 0, 0));
|
||||
|
||||
Graphics::ManagedSurface _backSurface;
|
||||
Graphics::ManagedSurface *getSurface() { return &_backSurface; }
|
||||
|
||||
Common::SeekableReadStream *_thumbnail;
|
||||
Common::SeekableReadStream *getThumbnail() { return _thumbnail; }
|
||||
|
||||
// Access methods
|
||||
|
||||
// Resource-Managing Methods
|
||||
// --------------------------
|
||||
Resource *loadResource(const Common::String &fileName) override;
|
||||
bool canLoadResource(const Common::String &fileName) override;
|
||||
|
||||
// Persistence Methods
|
||||
// -------------------
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
bool isRTL();
|
||||
static void ARGBColorToLuaColor(lua_State *L, uint color);
|
||||
static uint luaColorToARGBColor(lua_State *L, int stackIndex);
|
||||
|
||||
protected:
|
||||
|
||||
// Display Variables
|
||||
// -----------------
|
||||
int _width;
|
||||
int _height;
|
||||
Common::Rect _screenRect;
|
||||
int _bitDepth;
|
||||
|
||||
/**
|
||||
* Calculates the time since the last frame beginning has passed.
|
||||
*/
|
||||
void updateLastFrameDuration();
|
||||
|
||||
private:
|
||||
bool registerScriptBindings();
|
||||
void unregisterScriptBindings();
|
||||
|
||||
// LastFrameDuration Variables
|
||||
// ---------------------------
|
||||
uint _lastTimeStamp;
|
||||
uint _lastFrameDuration;
|
||||
bool _timerActive;
|
||||
Common::Array<uint> _frameTimeSamples;
|
||||
uint _frameTimeSampleSlot;
|
||||
|
||||
private:
|
||||
RenderObjectPtr<Panel> _mainPanelPtr;
|
||||
|
||||
Common::ScopedPtr<RenderObjectManager> _renderObjectManagerPtr;
|
||||
|
||||
bool _isRTL;
|
||||
|
||||
struct DebugLine {
|
||||
DebugLine(const Vertex &start, const Vertex &end, uint color) :
|
||||
_start(start),
|
||||
_end(end),
|
||||
_color(color) {}
|
||||
DebugLine() {}
|
||||
|
||||
Vertex _start;
|
||||
Vertex _end;
|
||||
uint _color;
|
||||
};
|
||||
|
||||
Common::Array<DebugLine> _debugLines;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
1273
engines/sword25/gfx/graphicengine_script.cpp
Normal file
1273
engines/sword25/gfx/graphicengine_script.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2649
engines/sword25/gfx/image/art.cpp
Normal file
2649
engines/sword25/gfx/image/art.cpp
Normal file
File diff suppressed because it is too large
Load Diff
215
engines/sword25/gfx/image/art.h
Normal file
215
engines/sword25/gfx/image/art.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Libart_LGPL - library of basic graphic primitives
|
||||
*
|
||||
* Copyright (c) 1998 Raph Levien
|
||||
*
|
||||
* Licensed under GNU LGPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/* Simple macros to set up storage allocation and basic types for libart
|
||||
functions. */
|
||||
|
||||
#ifndef __ART_H__
|
||||
#define __ART_H__
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/* These aren't, strictly speaking, configuration macros, but they're
|
||||
damn handy to have around, and may be worth playing with for
|
||||
debugging. */
|
||||
#define art_new(type, n) ((type *)malloc ((n) * sizeof(type)))
|
||||
|
||||
#define art_renew(p, type, n) ((type *)realloc (p, (n) * sizeof(type)))
|
||||
|
||||
/* This one must be used carefully - in particular, p and max should
|
||||
be variables. They can also be pstruct->el lvalues. */
|
||||
#define art_expand(p, type, max) \
|
||||
do { \
|
||||
if (max) {\
|
||||
type *tmp = art_renew(p, type, max <<= 1); \
|
||||
if (!tmp) error("Cannot reallocate memory for art data"); \
|
||||
p = tmp; \
|
||||
} else { \
|
||||
max = 1; \
|
||||
p = art_new(type, 1); \
|
||||
if (!p) error("Cannot allocate memory for art data"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct ArtDRect {
|
||||
/*< public >*/
|
||||
double x0, y0, x1, y1;
|
||||
};
|
||||
|
||||
struct ArtPoint {
|
||||
/*< public >*/
|
||||
double x, y;
|
||||
};
|
||||
|
||||
/* Basic data structures and constructors for sorted vector paths */
|
||||
|
||||
struct ArtSVPSeg {
|
||||
int n_points;
|
||||
int dir; /* == 0 for "up", 1 for "down" */
|
||||
ArtDRect bbox;
|
||||
ArtPoint *points;
|
||||
};
|
||||
|
||||
struct ArtSVP {
|
||||
int n_segs;
|
||||
ArtSVPSeg segs[1];
|
||||
};
|
||||
|
||||
void art_svp_free(ArtSVP *svp);
|
||||
|
||||
/* Basic data structures and constructors for bezier paths */
|
||||
|
||||
enum ArtPathcode {
|
||||
ART_MOVETO,
|
||||
ART_MOVETO_OPEN,
|
||||
ART_CURVETO,
|
||||
ART_LINETO,
|
||||
ART_END
|
||||
};
|
||||
|
||||
struct ArtBpath {
|
||||
/*< public >*/
|
||||
ArtPathcode code;
|
||||
double x1;
|
||||
double y1;
|
||||
double x2;
|
||||
double y2;
|
||||
double x3;
|
||||
double y3;
|
||||
};
|
||||
|
||||
/* Basic data structures and constructors for simple vector paths */
|
||||
|
||||
/* CURVETO is not allowed! */
|
||||
struct ArtVpath {
|
||||
ArtPathcode code;
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
/* Some of the functions need to go into their own modules */
|
||||
|
||||
void art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
|
||||
ArtPathcode code, double x, double y);
|
||||
|
||||
ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness);
|
||||
|
||||
/* The funky new SVP intersector. */
|
||||
|
||||
#ifndef ART_WIND_RULE_DEFINED
|
||||
#define ART_WIND_RULE_DEFINED
|
||||
enum ArtWindRule {
|
||||
ART_WIND_RULE_NONZERO,
|
||||
ART_WIND_RULE_INTERSECT,
|
||||
ART_WIND_RULE_ODDEVEN,
|
||||
ART_WIND_RULE_POSITIVE
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ArtSvpWriter {
|
||||
int (*add_segment)(ArtSvpWriter *self, int wind_left, int delta_wind,
|
||||
double x, double y);
|
||||
void (*add_point)(ArtSvpWriter *self, int seg_id, double x, double y);
|
||||
void (*close_segment)(ArtSvpWriter *self, int seg_id);
|
||||
};
|
||||
|
||||
ArtSvpWriter *art_svp_writer_rewind_new(ArtWindRule rule);
|
||||
|
||||
ArtSVP *art_svp_writer_rewind_reap(ArtSvpWriter *self);
|
||||
|
||||
int art_svp_seg_compare(const void *s1, const void *s2);
|
||||
|
||||
void art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out);
|
||||
|
||||
|
||||
/* Sort vector paths into sorted vector paths. */
|
||||
|
||||
ArtSVP *art_svp_from_vpath(ArtVpath *vpath);
|
||||
|
||||
/* Sort vector paths into sorted vector paths. */
|
||||
|
||||
enum ArtPathStrokeJoinType {
|
||||
ART_PATH_STROKE_JOIN_MITER,
|
||||
ART_PATH_STROKE_JOIN_ROUND,
|
||||
ART_PATH_STROKE_JOIN_BEVEL
|
||||
};
|
||||
|
||||
enum ArtPathStrokeCapType {
|
||||
ART_PATH_STROKE_CAP_BUTT,
|
||||
ART_PATH_STROKE_CAP_ROUND,
|
||||
ART_PATH_STROKE_CAP_SQUARE
|
||||
};
|
||||
|
||||
ArtSVP *art_svp_vpath_stroke(ArtVpath *vpath,
|
||||
ArtPathStrokeJoinType join,
|
||||
ArtPathStrokeCapType cap,
|
||||
double line_width,
|
||||
double miter_limit,
|
||||
double flatness);
|
||||
|
||||
/* This version may have winding numbers exceeding 1. */
|
||||
ArtVpath *art_svp_vpath_stroke_raw(ArtVpath *vpath,
|
||||
ArtPathStrokeJoinType join,
|
||||
ArtPathStrokeCapType cap,
|
||||
double line_width,
|
||||
double miter_limit,
|
||||
double flatness);
|
||||
|
||||
|
||||
/* The spiffy antialiased renderer for sorted vector paths. */
|
||||
|
||||
struct ArtSVPRenderAAStep {
|
||||
int x;
|
||||
int delta; /* stored with 16 fractional bits */
|
||||
};
|
||||
|
||||
struct ArtSVPRenderAAIter;
|
||||
|
||||
ArtSVPRenderAAIter *art_svp_render_aa_iter(const ArtSVP *svp,
|
||||
int x0, int y0, int x1, int y1);
|
||||
|
||||
void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
|
||||
ArtSVPRenderAAStep **p_steps, int *p_n_steps);
|
||||
|
||||
void art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter);
|
||||
|
||||
void art_svp_render_aa(const ArtSVP *svp,
|
||||
int x0, int y0, int x1, int y1,
|
||||
void (*callback)(void *callback_data,
|
||||
int y,
|
||||
int start,
|
||||
ArtSVPRenderAAStep *steps, int n_steps),
|
||||
void *callback_data);
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif /* __ART_H__ */
|
||||
193
engines/sword25/gfx/image/image.h
Normal file
193
engines/sword25/gfx/image/image.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
BS_Image
|
||||
--------
|
||||
|
||||
Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_IMAGE_H
|
||||
#define SWORD25_IMAGE_H
|
||||
|
||||
// Includes
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "common/rect.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class RectangleList;
|
||||
|
||||
class Image {
|
||||
public:
|
||||
virtual ~Image() {}
|
||||
|
||||
//@{
|
||||
/** @name Accessor methods */
|
||||
|
||||
/**
|
||||
@brief Returns the width of the image in pixels
|
||||
*/
|
||||
virtual int getWidth() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns the height of the image in pixels
|
||||
*/
|
||||
virtual int getHeight() const = 0;
|
||||
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/** @name Render methodes */
|
||||
|
||||
/**
|
||||
@brief renders the image in the framebuffer
|
||||
@param pDest a pointer to the target image. In most cases this is the framebuffer.
|
||||
@param PosX the position on the X-axis in the target image in pixels where the image is supposed to be rendered.<br>
|
||||
The default value is 0.
|
||||
@param PosY the position on the Y-axis in the target image in pixels where the image is supposed to be rendered.<br>
|
||||
The default value is 0.
|
||||
@param Flipping how the image should be flipped.<br>
|
||||
The default value is Graphics::FLIP_NONE (no flipping)
|
||||
@param pSrcPartRect Pointer on Common::Rect which specifies the section to be rendered. If the whole image has to be rendered the Pointer is NULL.<br>
|
||||
This referes to the unflipped and unscaled image.<br>
|
||||
The default value is NULL.
|
||||
@param Color an ARGB color value, which determines the parameters for the color modulation und alpha blending.<br>
|
||||
The alpha component of the color determines the alpha blending parameter (0 = no covering, 255 = full covering).<br>
|
||||
The color components determines the color for color modulation.<br>
|
||||
The default value is BS_ARGB(255, 255, 255, 255) (full covering, no color modulation).
|
||||
The macros BS_RGB and BS_ARGB can be used for the creation of the color value.
|
||||
@param Width the output width of the screen section.
|
||||
The images will be scaled if the output width of the screen section differs from the image section.<br>
|
||||
The value -1 determines that the image should not be scaled.<br>
|
||||
The default value is -1.
|
||||
@param Width the output height of the screen section.
|
||||
The images will be scaled if the output width of the screen section differs from the image section.<br>
|
||||
The value -1 determines that the image should not be scaled.<br>
|
||||
The default value is -1.
|
||||
@return returns false if the rendering failed.
|
||||
@remark Not all blitting operations of all BS_Image classes are supported.<br>
|
||||
More information can be find in the class description of BS_Image and the following methodes:
|
||||
- IsBlitTarget()
|
||||
- IsScalingAllowed()
|
||||
- IsFillingAllowed()
|
||||
- IsAlphaAllowed()
|
||||
- IsColorModulationAllowed()
|
||||
- IsSetContentAllowed()
|
||||
*/
|
||||
virtual bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) = 0;
|
||||
|
||||
/**
|
||||
@brief fills a rectangular section of the image with a color.
|
||||
@param pFillRect Pointer on Common::Rect which specifies the section of the image which is supposed to be filled. If the whole image has to be filled this value is NULL.<br>
|
||||
The default value is NULL.
|
||||
@param Color the 32 Bit color value for filling the image section.
|
||||
@remark It is possible to create transparent rectangulars by using a color with alpha value not equal to 255.
|
||||
@remark Independent from the color format of the image, it must be given a 32 bit color value. The macros BS_RGB and BS_ARGB can be used for the creation of the color value.
|
||||
@remark If the rectangular is not completely inside the screen area, it will be automatically trimmed.
|
||||
*/
|
||||
virtual bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) = 0;
|
||||
|
||||
/**
|
||||
@brief Fills the content of the image with pixel data.
|
||||
@param Pixeldata a vector which cotains the pixel data. They must be present in the color format of the image and there must be enough data available for filling the whole image.
|
||||
@param Offset the offset in Byte in Pixeldata-Vector on which the first pixel to write is located.<br>
|
||||
The default value is 0.
|
||||
@param Stride the distance in Byte between the end of line and the beginning of a new line in Pixeldata-Vector.<br>
|
||||
The default value is 0.
|
||||
@return returns false, if the call failed.
|
||||
@remark A call of this methode is only allowd if IsSetContentAllowed() returns true.
|
||||
*/
|
||||
virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) = 0;
|
||||
|
||||
/**
|
||||
@brief Reads out a pixel of the image.
|
||||
@param X the X-coordinate of the pixel.
|
||||
@param Y the y-coordinate of the pixel.
|
||||
@return Returns the 32-bit color value of the pixel at the given position.
|
||||
@remark This methode should not be used in no way to read out bigger parts of the image because the method is very slow. The method is rather intended for reading out single pixels of the image..
|
||||
*/
|
||||
virtual uint getPixel(int x, int y) = 0;
|
||||
|
||||
//@{
|
||||
/** @name Information methodes */
|
||||
|
||||
/**
|
||||
@brief Checks, if it is allowed to call BS_Image Blit().
|
||||
@return Returns false, if a Blit() call is not allowed at this object.
|
||||
*/
|
||||
virtual bool isBlitSource() const = 0;
|
||||
|
||||
/**
|
||||
@brief Checks, if the BS_Image can be a target image for a Blit call.
|
||||
@return Returns false, if a Blit() call with this object as a target is not allowed.
|
||||
*/
|
||||
virtual bool isBlitTarget() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns true, if the BS_Image is allowed to be scaled by a Blit() call.
|
||||
*/
|
||||
virtual bool isScalingAllowed() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns true, if the BS_Image is allowed to be filled by a Fill() call.
|
||||
*/
|
||||
virtual bool isFillingAllowed() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns true, if the BS_Image is allowed to be displayed with an alpha value.
|
||||
*/
|
||||
virtual bool isAlphaAllowed() const = 0;
|
||||
|
||||
/**
|
||||
@brief Return true, if the BS_Image is allowed to be displayed with color modulation by a Blit() call
|
||||
*/
|
||||
virtual bool isColorModulationAllowed() const = 0;
|
||||
|
||||
/**
|
||||
@brief Returns true, if the content of the BS_Image is allowed to be replaced by call of SetContent().
|
||||
*/
|
||||
virtual bool isSetContentAllowed() const = 0;
|
||||
|
||||
virtual bool isSolid() const { return false; }
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
82
engines/sword25/gfx/image/imgloader.cpp
Normal file
82
engines/sword25/gfx/image/imgloader.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "sword25/gfx/image/imgloader.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "image/png.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
bool ImgLoader::decodePNGImage(const byte *fileDataPtr, uint fileSize, Graphics::Surface *dest) {
|
||||
assert(dest);
|
||||
Common::MemoryReadStream *fileStr = new Common::MemoryReadStream(fileDataPtr, fileSize, DisposeAfterUse::NO);
|
||||
|
||||
::Image::PNGDecoder png;
|
||||
if (!png.loadStream(*fileStr)) // the fileStr pointer, and thus pFileData will be deleted after this is done
|
||||
error("Error while reading PNG image");
|
||||
|
||||
const Graphics::Surface *sourceSurface = png.getSurface();
|
||||
Graphics::Surface *pngSurface = sourceSurface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0), png.getPalette().data(), png.getPalette().size());
|
||||
|
||||
dest->copyFrom(*pngSurface);
|
||||
|
||||
pngSurface->free();
|
||||
delete pngSurface;
|
||||
delete fileStr;
|
||||
|
||||
// Signal success
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImgLoader::decodeThumbnailImage(const byte *pFileData, uint fileSize, Graphics::Surface *dest) {
|
||||
assert(dest);
|
||||
const byte *src = pFileData + 4; // skip header
|
||||
uint width = READ_LE_UINT16(src); src += 2;
|
||||
uint height = READ_LE_UINT16(src); src += 2;
|
||||
src++; // version, ignored for now
|
||||
|
||||
dest->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
|
||||
uint32 *dst = (uint32 *)dest->getBasePtr(0, 0); // treat as uint32, for pixelformat output
|
||||
byte r, g, b;
|
||||
|
||||
for (uint32 i = 0; i < width * height; i++) {
|
||||
r = *src++;
|
||||
g = *src++;
|
||||
b = *src++;
|
||||
*dst++ = dest->format.RGBToColor(r, g, b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
74
engines/sword25/gfx/image/imgloader.h
Normal file
74
engines/sword25/gfx/image/imgloader.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_IMGLOADER_H
|
||||
#define SWORD25_IMGLOADER_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
} // End of namespace Graphics
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/**
|
||||
* Class for loading PNG files, and PNG data embedded into savegames.
|
||||
*
|
||||
* Originally written by Malte Thiesen.
|
||||
*/
|
||||
class ImgLoader {
|
||||
protected:
|
||||
ImgLoader() {} // Protected constructor to prevent instances
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Decode an image.
|
||||
* @param[in] fileDatePtr pointer to the image data
|
||||
* @param[in] fileSize size of the image data in bytes
|
||||
* @param[out] dest if successful, surface will contain the image
|
||||
* data (storage is allocated via create).
|
||||
* @return false in case of an error
|
||||
*
|
||||
* @remark This function does not free the image buffer passed to it,
|
||||
* it is the callers responsibility to do so.
|
||||
*/
|
||||
static bool decodePNGImage(const byte *pFileData, uint fileSize,
|
||||
Graphics::Surface *dest);
|
||||
|
||||
static bool decodeThumbnailImage(const byte *pFileData, uint fileSize,
|
||||
Graphics::Surface *dest);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
266
engines/sword25/gfx/image/renderedimage.cpp
Normal file
266
engines/sword25/gfx/image/renderedimage.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// INCLUDES
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "common/savefile.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/gfx/image/imgloader.h"
|
||||
#include "sword25/gfx/image/renderedimage.h"
|
||||
|
||||
#include "sword25/gfx/renderobjectmanager.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "graphics/thumbnail.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CONSTRUCTION / DESTRUCTION
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Load a NULL-terminated string from the given stream.
|
||||
*/
|
||||
static Common::String loadString(Common::SeekableReadStream &in, uint maxSize = 999) {
|
||||
Common::String result;
|
||||
|
||||
while (!in.eos() && (result.size() < maxSize)) {
|
||||
char ch = (char)in.readByte();
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
result += ch;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static byte *readSavegameThumbnail(const Common::String &filename, uint &fileSize, bool &isPNG) {
|
||||
byte *pFileData;
|
||||
Common::SaveFileManager *sfm = g_system->getSavefileManager();
|
||||
Common::InSaveFile *file = sfm->openForLoading(lastPathComponent(filename, '/'));
|
||||
if (!file)
|
||||
error("Save file \"%s\" could not be loaded.", filename.c_str());
|
||||
|
||||
// Seek to the actual PNG image
|
||||
loadString(*file); // Marker (BS25SAVEGAME)
|
||||
Common::String storedVersionID = loadString(*file); // Version
|
||||
if (storedVersionID != "SCUMMVM1")
|
||||
loadString(*file);
|
||||
|
||||
loadString(*file); // Description
|
||||
uint32 compressedGamedataSize = atoi(loadString(*file).c_str());
|
||||
loadString(*file); // Uncompressed game data size
|
||||
file->skip(compressedGamedataSize); // Skip the game data and move to the thumbnail itself
|
||||
uint32 thumbnailStart = file->pos();
|
||||
|
||||
fileSize = file->size() - thumbnailStart;
|
||||
|
||||
// Check if the thumbnail is in our own format, or a PNG file.
|
||||
uint32 header = file->readUint32BE();
|
||||
isPNG = (header != MKTAG('S','C','R','N'));
|
||||
file->seek(-4, SEEK_CUR);
|
||||
|
||||
pFileData = new byte[fileSize];
|
||||
file->read(pFileData, fileSize);
|
||||
delete file;
|
||||
|
||||
return pFileData;
|
||||
}
|
||||
|
||||
RenderedImage::RenderedImage(const Common::String &filename, bool &result) :
|
||||
_alphaType(Graphics::ALPHA_FULL) {
|
||||
result = false;
|
||||
|
||||
PackageManager *pPackage = Kernel::getInstance()->getPackage();
|
||||
assert(pPackage);
|
||||
|
||||
_backSurface = Kernel::getInstance()->getGfx()->getSurface();
|
||||
|
||||
// Load file
|
||||
byte *pFileData;
|
||||
uint fileSize;
|
||||
|
||||
bool isPNG = true;
|
||||
|
||||
if (filename.hasPrefix("/saves")) {
|
||||
pFileData = readSavegameThumbnail(filename, fileSize, isPNG);
|
||||
} else {
|
||||
pFileData = pPackage->getFile(filename, &fileSize);
|
||||
}
|
||||
|
||||
if (!pFileData) {
|
||||
error("File \"%s\" could not be loaded.", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Uncompress the image
|
||||
if (isPNG)
|
||||
result = ImgLoader::decodePNGImage(pFileData, fileSize, _surface.surfacePtr());
|
||||
else
|
||||
result = ImgLoader::decodeThumbnailImage(pFileData, fileSize, _surface.surfacePtr());
|
||||
|
||||
if (!result) {
|
||||
error("Could not decode image.");
|
||||
delete[] pFileData;
|
||||
return;
|
||||
}
|
||||
|
||||
// Cleanup FileData
|
||||
delete[] pFileData;
|
||||
|
||||
_doCleanup = true;
|
||||
_alphaType = _surface.detectAlpha();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
RenderedImage::RenderedImage(uint width, uint height, bool &result) :
|
||||
_alphaType(Graphics::ALPHA_FULL) {
|
||||
|
||||
_surface.create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
|
||||
|
||||
_backSurface = Kernel::getInstance()->getGfx()->getSurface();
|
||||
|
||||
_doCleanup = true;
|
||||
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
RenderedImage::RenderedImage() : _alphaType(Graphics::ALPHA_FULL) {
|
||||
_backSurface = Kernel::getInstance()->getGfx()->getSurface();
|
||||
|
||||
_surface.format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
|
||||
|
||||
_doCleanup = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
RenderedImage::~RenderedImage() {
|
||||
if (_doCleanup) {
|
||||
_surface.free();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool RenderedImage::fill(const Common::Rect *pFillRect, uint color) {
|
||||
error("Fill() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool RenderedImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
|
||||
// Check if PixelData contains enough pixel to create an image with image size equals width * height
|
||||
if (size < static_cast<uint>(_surface.w * _surface.h * 4)) {
|
||||
error("PixelData vector is too small to define a 32 bit %dx%d image.", _surface.w, _surface.h);
|
||||
return false;
|
||||
}
|
||||
|
||||
const byte *in = &pixeldata[offset];
|
||||
byte *out = (byte *)_surface.getPixels();
|
||||
|
||||
for (int i = 0; i < _surface.h; i++) {
|
||||
memcpy(out, in, _surface.w * 4);
|
||||
out += _surface.w * 4;
|
||||
in += stride;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderedImage::replaceContent(byte *pixeldata, int width, int height) {
|
||||
_surface.w = width;
|
||||
_surface.h = height;
|
||||
_surface.pitch = width * 4;
|
||||
_surface.setPixels(pixeldata);
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
uint RenderedImage::getPixel(int x, int y) {
|
||||
error("GetPixel() is not supported. Returning black.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool RenderedImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, RectangleList *updateRects) {
|
||||
int newFlipping = (((flipping & 1) ? Graphics::FLIP_V : 0) | ((flipping & 2) ? Graphics::FLIP_H : 0));
|
||||
|
||||
int ca = (color >> BS_ASHIFT) & 0xff;
|
||||
int cr = (color >> BS_RSHIFT) & 0xff;
|
||||
int cg = (color >> BS_GSHIFT) & 0xff;
|
||||
int cb = (color >> BS_BSHIFT) & 0xff;
|
||||
|
||||
Common::Rect srcRect = pPartRect ? *pPartRect : Common::Rect(_surface.w, _surface.h);
|
||||
if (width == -1) width = srcRect.width();
|
||||
if (height == -1) height = srcRect.height();
|
||||
|
||||
_backSurface->blendBlitFrom(_surface, srcRect, Common::Rect(posX, posY, posX + width, posY + height),
|
||||
newFlipping, _surface.format.ARGBToColor(ca, cr, cg, cb), Graphics::BLEND_NORMAL, _alphaType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderedImage::copyDirectly(int posX, int posY) {
|
||||
byte *data = (byte *)_surface.getPixels();
|
||||
int w = _surface.w;
|
||||
int h = _surface.h;
|
||||
|
||||
// Handle off-screen clipping
|
||||
if (posY < 0) {
|
||||
h = MAX(0, (int)_surface.h - -posY);
|
||||
data = (byte *)_surface.getPixels() + _surface.w * -posY;
|
||||
posY = 0;
|
||||
}
|
||||
|
||||
if (posX < 0) {
|
||||
w = MAX(0, (int)_surface.h - -posX);
|
||||
data = (byte *)_surface.getPixels() + (-posX * 4);
|
||||
posX = 0;
|
||||
}
|
||||
|
||||
w = CLIP((int)w, 0, (int)MAX((int)_backSurface->w - posX, 0));
|
||||
h = CLIP((int)h, 0, (int)MAX((int)_backSurface->h - posY, 0));
|
||||
|
||||
g_system->copyRectToScreen(data, _backSurface->pitch, posX, posY, w, h);
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
120
engines/sword25/gfx/image/renderedimage.h
Normal file
120
engines/sword25/gfx/image/renderedimage.h
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RENDERED_IMAGE_H
|
||||
#define SWORD25_RENDERED_IMAGE_H
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// INCLUDES
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class RenderedImage : public Image {
|
||||
private:
|
||||
RenderedImage(const RenderedImage &) : Image(), _doCleanup(false) {}
|
||||
RenderedImage &operator=(const RenderedImage &) { return *this; }
|
||||
public:
|
||||
RenderedImage(const Common::String &filename, bool &result);
|
||||
|
||||
/**
|
||||
@brief Creates an empty BS_RenderedImage
|
||||
|
||||
@param Width The width of the image to be created.
|
||||
@param Height The height of the image to be created
|
||||
@param Result Informs the caller, whether the constructor is executed successfully. If it contains false
|
||||
after the call, do not call methods on the object and destroy the object immediately.
|
||||
*/
|
||||
RenderedImage(uint width, uint height, bool &result);
|
||||
RenderedImage();
|
||||
|
||||
~RenderedImage() override;
|
||||
|
||||
int getWidth() const override {
|
||||
return _surface.w;
|
||||
}
|
||||
int getHeight() const override {
|
||||
return _surface.h;
|
||||
}
|
||||
|
||||
void copyDirectly(int posX, int posY);
|
||||
|
||||
bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) override;
|
||||
bool fill(const Common::Rect *pFillRect, uint color) override;
|
||||
bool setContent(const byte *pixeldata, uint size, uint offset = 0, uint stride = 0) override;
|
||||
void replaceContent(byte *pixeldata, int width, int height);
|
||||
uint getPixel(int x, int y) override;
|
||||
|
||||
bool isBlitSource() const override {
|
||||
return true;
|
||||
}
|
||||
bool isBlitTarget() const override {
|
||||
return false;
|
||||
}
|
||||
bool isScalingAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isFillingAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isAlphaAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isColorModulationAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isSetContentAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void setAlphaType(Graphics::AlphaType alphaType) { _alphaType = alphaType; }
|
||||
bool isSolid() const override { return _alphaType == Graphics::ALPHA_OPAQUE; }
|
||||
|
||||
private:
|
||||
Graphics::ManagedSurface _surface;
|
||||
Graphics::AlphaType _alphaType;
|
||||
bool _doCleanup;
|
||||
|
||||
Graphics::ManagedSurface *_backSurface;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
100
engines/sword25/gfx/image/swimage.cpp
Normal file
100
engines/sword25/gfx/image/swimage.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/gfx/image/imgloader.h"
|
||||
#include "sword25/gfx/image/swimage.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
SWImage::SWImage(const Common::String &filename, bool &result) : _image() {
|
||||
result = false;
|
||||
|
||||
PackageManager *pPackage = Kernel::getInstance()->getPackage();
|
||||
assert(pPackage);
|
||||
|
||||
// Load file
|
||||
byte *pFileData;
|
||||
uint fileSize;
|
||||
pFileData = pPackage->getFile(filename, &fileSize);
|
||||
if (!pFileData) {
|
||||
error("File \"%s\" could not be loaded.", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Uncompress the image
|
||||
if (!ImgLoader::decodePNGImage(pFileData, fileSize, &_image)) {
|
||||
error("Could not decode image.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cleanup FileData
|
||||
delete[] pFileData;
|
||||
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
SWImage::~SWImage() {
|
||||
_image.free();
|
||||
}
|
||||
|
||||
|
||||
bool SWImage::blit(int posX, int posY,
|
||||
int flipping,
|
||||
Common::Rect *pPartRect,
|
||||
uint color,
|
||||
int width, int height,
|
||||
RectangleList *updateRects) {
|
||||
error("Blit() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SWImage::fill(const Common::Rect *pFillRect, uint color) {
|
||||
error("Fill() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SWImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
|
||||
error("SetContent() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint SWImage::getPixel(int x, int y) {
|
||||
assert(x >= 0 && x < _image.w);
|
||||
assert(y >= 0 && y < _image.h);
|
||||
|
||||
byte a, r, g, b;
|
||||
_image.format.colorToARGB(_image.getPixel(x, y), a, r, g, b);
|
||||
|
||||
return BS_ARGB(a, r, g, b);
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
90
engines/sword25/gfx/image/swimage.h
Normal file
90
engines/sword25/gfx/image/swimage.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_SWIMAGE_H
|
||||
#define SWORD25_SWIMAGE_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class SWImage : public Image {
|
||||
public:
|
||||
SWImage(const Common::String &filename, bool &result);
|
||||
~SWImage() override;
|
||||
|
||||
int getWidth() const override {
|
||||
return _image.w;
|
||||
}
|
||||
int getHeight() const override {
|
||||
return _image.h;
|
||||
}
|
||||
|
||||
bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) override;
|
||||
bool fill(const Common::Rect *fillRectPtr, uint color) override;
|
||||
bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) override;
|
||||
uint getPixel(int x, int y) override;
|
||||
|
||||
bool isBlitSource() const override {
|
||||
return false;
|
||||
}
|
||||
bool isBlitTarget() const override {
|
||||
return false;
|
||||
}
|
||||
bool isScalingAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isFillingAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isAlphaAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isColorModulationAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isSetContentAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
Graphics::Surface _image;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
643
engines/sword25/gfx/image/vectorimage.cpp
Normal file
643
engines/sword25/gfx/image/vectorimage.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Includes
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "sword25/gfx/image/art.h"
|
||||
#include "sword25/gfx/image/vectorimage.h"
|
||||
#include "sword25/gfx/image/renderedimage.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
#define BEZSMOOTHNESS 0.5
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// SWF datatype
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Bitstream helper class
|
||||
// -----------------------------------------------------------------------------
|
||||
// The parsing of SWF files requires both bitwise readout and on Byte boundaries
|
||||
// oriented reading.
|
||||
// This class is specially equipped for this.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class VectorImage::SWFBitStream {
|
||||
public:
|
||||
SWFBitStream(const byte *pData, uint dataSize) :
|
||||
m_Pos(pData), m_End(pData + dataSize), m_WordMask(0)
|
||||
{}
|
||||
|
||||
inline uint32 getBits(uint bitCount) {
|
||||
if (bitCount == 0 || bitCount > 32) {
|
||||
error("SWFBitStream::GetBits() must read at least 1 and at most 32 bits at a time");
|
||||
}
|
||||
|
||||
uint32 value = 0;
|
||||
while (bitCount) {
|
||||
if (m_WordMask == 0)
|
||||
flushByte();
|
||||
|
||||
value <<= 1;
|
||||
value |= ((m_Word & m_WordMask) != 0) ? 1 : 0;
|
||||
m_WordMask >>= 1;
|
||||
|
||||
--bitCount;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline int32 getSignedBits(uint bitCount) {
|
||||
// readout bits
|
||||
uint32 temp = getBits(bitCount);
|
||||
|
||||
// If the sign-bit is set, fill the rest of the return value with 1-bit (sign extension)
|
||||
if (temp & 1 << (bitCount - 1))
|
||||
return (0xffffffff << bitCount) | temp;
|
||||
else
|
||||
return temp;
|
||||
}
|
||||
|
||||
inline uint32 getUInt32() {
|
||||
uint32 byte1 = getByte();
|
||||
uint32 byte2 = getByte();
|
||||
uint32 byte3 = getByte();
|
||||
uint32 byte4 = getByte();
|
||||
|
||||
return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
|
||||
}
|
||||
|
||||
inline uint16 getUInt16() {
|
||||
uint32 byte1 = getByte();
|
||||
uint32 byte2 = getByte();
|
||||
|
||||
return byte1 | (byte2 << 8);
|
||||
}
|
||||
|
||||
inline byte getByte() {
|
||||
flushByte();
|
||||
byte value = m_Word;
|
||||
m_WordMask = 0;
|
||||
flushByte();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline void flushByte() {
|
||||
if (m_WordMask != 128) {
|
||||
if (m_Pos >= m_End) {
|
||||
error("Attempted to read past end of file");
|
||||
} else {
|
||||
m_Word = *m_Pos++;
|
||||
m_WordMask = 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void skipBytes(uint skipLength) {
|
||||
flushByte();
|
||||
if (m_Pos + skipLength >= m_End) {
|
||||
error("Attempted to read past end of file");
|
||||
} else {
|
||||
m_Pos += skipLength;
|
||||
m_Word = *(m_Pos - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const byte *m_Pos;
|
||||
const byte *m_End;
|
||||
|
||||
byte m_Word;
|
||||
uint m_WordMask;
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constants and utility functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constants
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const uint32 MAX_ACCEPTED_FLASH_VERSION = 3; // The maximum flash file version that is accepted by the loader
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Converts SWF rectangle data in a bit stream in Common::Rect objects
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Common::Rect flashRectToBSRect(VectorImage::SWFBitStream &bs) {
|
||||
bs.flushByte();
|
||||
|
||||
// Determines how many bits of the single components are encoded
|
||||
uint32 bitsPerValue = bs.getBits(5);
|
||||
|
||||
// Readout the single components
|
||||
int32 xMin = bs.getSignedBits(bitsPerValue);
|
||||
int32 xMax = bs.getSignedBits(bitsPerValue);
|
||||
int32 yMin = bs.getSignedBits(bitsPerValue);
|
||||
int32 yMax = bs.getSignedBits(bitsPerValue);
|
||||
|
||||
return Common::Rect(xMin, yMin, xMax + 1, yMax + 1);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Calculate the bounding box of a BS_VectorImageElement
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Common::Rect CalculateBoundingBox(const VectorImageElement &vectorImageElement) {
|
||||
double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0;
|
||||
|
||||
for (int j = vectorImageElement.getPathCount() - 1; j >= 0; j--) {
|
||||
ArtBpath *bez = vectorImageElement.getPathInfo(j).getVec();
|
||||
ArtVpath *vec = art_bez_path_to_vec(bez, 0.5);
|
||||
|
||||
if (vec[0].code == ART_END) {
|
||||
free(vec);
|
||||
continue;
|
||||
} else {
|
||||
x0 = x1 = vec[0].x;
|
||||
y0 = y1 = vec[0].y;
|
||||
for (int i = 1; vec[i].code != ART_END; i++) {
|
||||
if (vec[i].x < x0) x0 = vec[i].x;
|
||||
if (vec[i].x > x1) x1 = vec[i].x;
|
||||
if (vec[i].y < y0) y0 = vec[i].y;
|
||||
if (vec[i].y > y1) y1 = vec[i].y;
|
||||
}
|
||||
}
|
||||
free(vec);
|
||||
}
|
||||
|
||||
return Common::Rect(static_cast<int>(x0), static_cast<int>(y0), static_cast<int>(x1) + 1, static_cast<int>(y1) + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Construction
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname) : _pixelData(0), _fname(fname) {
|
||||
success = false;
|
||||
_bgColor = 0;
|
||||
|
||||
// Create bitstream object
|
||||
// In the following the file data will be readout of the bitstream object.
|
||||
SWFBitStream bs(pFileData, fileSize);
|
||||
|
||||
// Check SWF signature
|
||||
uint32 signature[3];
|
||||
signature[0] = bs.getByte();
|
||||
signature[1] = bs.getByte();
|
||||
signature[2] = bs.getByte();
|
||||
if (signature[0] != 'F' ||
|
||||
signature[1] != 'W' ||
|
||||
signature[2] != 'S') {
|
||||
error("File is not a valid SWF-file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the version
|
||||
uint32 version = bs.getByte();
|
||||
if (version > MAX_ACCEPTED_FLASH_VERSION) {
|
||||
error("File is of version %d. Highest accepted version is %d.", version, MAX_ACCEPTED_FLASH_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Readout filesize and compare with the actual size
|
||||
uint32 storedFileSize = bs.getUInt32();
|
||||
if (storedFileSize != fileSize) {
|
||||
error("File is not a valid SWF-file");
|
||||
return;
|
||||
}
|
||||
|
||||
// readout SWF size
|
||||
flashRectToBSRect(bs);
|
||||
|
||||
// Get frame rate and frame count
|
||||
/* uint32 frameRate = */
|
||||
bs.getUInt16();
|
||||
/* uint32 frameCount = */
|
||||
bs.getUInt16();
|
||||
|
||||
// Parse tags
|
||||
// Because we are only interested in the first DifneShape-Tag...
|
||||
bool keepParsing = true;
|
||||
while (keepParsing) {
|
||||
// Tags always begin on byte boundaries
|
||||
bs.flushByte();
|
||||
|
||||
// Readout tag type and length
|
||||
uint16 tagTypeAndLength = bs.getUInt16();
|
||||
uint32 tagType = tagTypeAndLength >> 6;
|
||||
uint32 tagLength = tagTypeAndLength & 0x3f;
|
||||
if (tagLength == 0x3f)
|
||||
tagLength = bs.getUInt32();
|
||||
|
||||
switch (tagType) {
|
||||
case 2:
|
||||
// DefineShape
|
||||
success = parseDefineShape(2, bs);
|
||||
return;
|
||||
case 22:
|
||||
// DefineShape2
|
||||
success = parseDefineShape(2, bs);
|
||||
return;
|
||||
case 32:
|
||||
success = parseDefineShape(3, bs);
|
||||
return;
|
||||
case 9:
|
||||
// SetBackgroundColor
|
||||
{
|
||||
byte r, g, b;
|
||||
r = bs.getByte();
|
||||
g = bs.getByte();
|
||||
b = bs.getByte();
|
||||
_bgColor = BS_RGB(r, g, b);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
warning("Ignoring tag: %d, %d bytes", tagType, tagLength);
|
||||
// Ignore unknown tags
|
||||
bs.skipBytes(tagLength);
|
||||
}
|
||||
}
|
||||
|
||||
// The execution must not arrive at this point: Either a shape is found, then the function will be leaved before, or it is found none, then
|
||||
// an exception occurs as soon as it is read beyond of the end of file.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
VectorImage::~VectorImage() {
|
||||
for (int j = _elements.size() - 1; j >= 0; j--)
|
||||
for (int i = _elements[j].getPathCount() - 1; i >= 0; i--)
|
||||
if (_elements[j].getPathInfo(i).getVec())
|
||||
free(_elements[j].getPathInfo(i).getVec());
|
||||
|
||||
free(_pixelData);
|
||||
}
|
||||
|
||||
|
||||
ArtBpath *ensureBezStorage(ArtBpath *bez, int nodes, int *allocated) {
|
||||
if (*allocated <= nodes) {
|
||||
(*allocated) += 20;
|
||||
|
||||
return art_renew(bez, ArtBpath, *allocated);
|
||||
}
|
||||
|
||||
return bez;
|
||||
}
|
||||
|
||||
ArtBpath *VectorImage::storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated) {
|
||||
(*bezNodes)++;
|
||||
|
||||
bez = ensureBezStorage(bez, *bezNodes, bezAllocated);
|
||||
bez[*bezNodes].code = ART_END;
|
||||
|
||||
ArtBpath *bez1 = art_new(ArtBpath, *bezNodes + 1);
|
||||
if (!bez1)
|
||||
error("[VectorImage::storeBez] Cannot allocate memory");
|
||||
|
||||
for (int i = 0; i <= *bezNodes; i++)
|
||||
bez1[i] = bez[i];
|
||||
|
||||
_elements.back()._pathInfos.push_back(VectorPathInfo(bez1, *bezNodes, lineStyle, fillStyle0, fillStyle1));
|
||||
|
||||
return bez;
|
||||
}
|
||||
|
||||
bool VectorImage::parseDefineShape(uint shapeType, SWFBitStream &bs) {
|
||||
/*uint32 shapeID = */bs.getUInt16();
|
||||
|
||||
// readout bounding box
|
||||
_boundingBox = flashRectToBSRect(bs);
|
||||
|
||||
// create first image element
|
||||
_elements.resize(1);
|
||||
|
||||
// read styles
|
||||
uint numFillBits;
|
||||
uint numLineBits;
|
||||
if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
|
||||
return false;
|
||||
|
||||
uint lineStyle = 0;
|
||||
uint fillStyle0 = 0;
|
||||
uint fillStyle1 = 0;
|
||||
|
||||
// parse shaperecord
|
||||
// ------------------
|
||||
|
||||
double curX = 0;
|
||||
double curY = 0;
|
||||
int bezNodes = 0;
|
||||
int bezAllocated = 10;
|
||||
ArtBpath *bez = art_new(ArtBpath, bezAllocated);
|
||||
|
||||
bool endOfShapeDiscovered = false;
|
||||
while (!endOfShapeDiscovered) {
|
||||
uint32 typeFlag = bs.getBits(1);
|
||||
|
||||
// Non-Edge Record
|
||||
if (typeFlag == 0) {
|
||||
// Determines which parameters are set
|
||||
uint32 stateNewStyles = bs.getBits(1);
|
||||
uint32 stateLineStyle = bs.getBits(1);
|
||||
uint32 stateFillStyle1 = bs.getBits(1);
|
||||
uint32 stateFillStyle0 = bs.getBits(1);
|
||||
uint32 stateMoveTo = bs.getBits(1);
|
||||
|
||||
uint prevLineStyle = lineStyle;
|
||||
uint prevFillStyle0 = fillStyle0;
|
||||
uint prevFillStyle1 = fillStyle1;
|
||||
|
||||
// End of the shape definition is reached?
|
||||
if (!stateNewStyles && !stateLineStyle && !stateFillStyle0 && !stateFillStyle1 && !stateMoveTo) {
|
||||
endOfShapeDiscovered = true;
|
||||
// Decode parameters
|
||||
} else {
|
||||
if (stateMoveTo) {
|
||||
uint32 moveToBits = bs.getBits(5);
|
||||
curX = bs.getSignedBits(moveToBits);
|
||||
curY = bs.getSignedBits(moveToBits);
|
||||
}
|
||||
|
||||
if (stateFillStyle0) {
|
||||
if (numFillBits > 0)
|
||||
fillStyle0 = bs.getBits(numFillBits);
|
||||
else
|
||||
fillStyle0 = 0;
|
||||
}
|
||||
|
||||
if (stateFillStyle1) {
|
||||
if (numFillBits > 0)
|
||||
fillStyle1 = bs.getBits(numFillBits);
|
||||
else
|
||||
fillStyle1 = 0;
|
||||
}
|
||||
|
||||
if (stateLineStyle) {
|
||||
if (numLineBits)
|
||||
lineStyle = bs.getBits(numLineBits);
|
||||
else
|
||||
numLineBits = 0;
|
||||
}
|
||||
|
||||
// Create a new path, unless there were only defined new styles
|
||||
if (stateLineStyle || stateFillStyle0 || stateFillStyle1 || stateMoveTo) {
|
||||
// Store previous curve if any
|
||||
if (bezNodes) {
|
||||
bez = storeBez(bez, prevLineStyle, prevFillStyle0, prevFillStyle1, &bezNodes, &bezAllocated);
|
||||
}
|
||||
|
||||
// Start new curve
|
||||
bez = ensureBezStorage(bez, 1, &bezAllocated);
|
||||
bez[0].code = ART_MOVETO_OPEN;
|
||||
bez[0].x3 = curX;
|
||||
bez[0].y3 = curY;
|
||||
bezNodes = 0;
|
||||
}
|
||||
|
||||
if (stateNewStyles) {
|
||||
// The old style definitions will be discarded and overwritten with the new at this point in Flash.
|
||||
// A new element will be started with a new element.
|
||||
_elements.resize(_elements.size() + 1);
|
||||
if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Edge record
|
||||
uint32 edgeFlag = bs.getBits(1);
|
||||
uint32 numBits = bs.getBits(4) + 2;
|
||||
|
||||
// Curved edge
|
||||
if (edgeFlag == 0) {
|
||||
double controlDeltaX = bs.getSignedBits(numBits);
|
||||
double controlDeltaY = bs.getSignedBits(numBits);
|
||||
double anchorDeltaX = bs.getSignedBits(numBits);
|
||||
double anchorDeltaY = bs.getSignedBits(numBits);
|
||||
|
||||
double controlX = curX + controlDeltaX;
|
||||
double controlY = curY + controlDeltaY;
|
||||
double newX = controlX + anchorDeltaX;
|
||||
double newY = controlY + anchorDeltaY;
|
||||
|
||||
#define WEIGHT (2.0/3.0)
|
||||
|
||||
bezNodes++;
|
||||
bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
|
||||
bez[bezNodes].code = ART_CURVETO;
|
||||
bez[bezNodes].x1 = WEIGHT * controlX + (1 - WEIGHT) * curX;
|
||||
bez[bezNodes].y1 = WEIGHT * controlY + (1 - WEIGHT) * curY;
|
||||
bez[bezNodes].x2 = WEIGHT * controlX + (1 - WEIGHT) * newX;
|
||||
bez[bezNodes].y2 = WEIGHT * controlY + (1 - WEIGHT) * newY;
|
||||
bez[bezNodes].x3 = newX;
|
||||
bez[bezNodes].y3 = newY;
|
||||
|
||||
curX = newX;
|
||||
curY = newY;
|
||||
} else {
|
||||
// Staight edge
|
||||
int32 deltaX = 0;
|
||||
int32 deltaY = 0;
|
||||
|
||||
uint32 generalLineFlag = bs.getBits(1);
|
||||
if (generalLineFlag) {
|
||||
deltaX = bs.getSignedBits(numBits);
|
||||
deltaY = bs.getSignedBits(numBits);
|
||||
} else {
|
||||
uint32 vertLineFlag = bs.getBits(1);
|
||||
if (vertLineFlag)
|
||||
deltaY = bs.getSignedBits(numBits);
|
||||
else
|
||||
deltaX = bs.getSignedBits(numBits);
|
||||
}
|
||||
|
||||
curX += deltaX;
|
||||
curY += deltaY;
|
||||
|
||||
bezNodes++;
|
||||
bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
|
||||
bez[bezNodes].code = ART_LINETO;
|
||||
bez[bezNodes].x3 = curX;
|
||||
bez[bezNodes].y3 = curY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store last curve
|
||||
if (bezNodes)
|
||||
bez = storeBez(bez, lineStyle, fillStyle0, fillStyle1, &bezNodes, &bezAllocated);
|
||||
|
||||
free(bez);
|
||||
|
||||
// Calculate the bounding boxes of each element
|
||||
Common::Array<VectorImageElement>::iterator it = _elements.begin();
|
||||
for (; it != _elements.end(); ++it)
|
||||
it->_boundingBox = CalculateBoundingBox(*it);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool VectorImage::parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits) {
|
||||
bs.flushByte();
|
||||
|
||||
// Parse fill styles
|
||||
// -----------------
|
||||
|
||||
// Determine number of fill styles
|
||||
uint fillStyleCount = bs.getByte();
|
||||
if (fillStyleCount == 0xff)
|
||||
fillStyleCount = bs.getUInt16();
|
||||
|
||||
// Readout all fill styles. If a fill style with Typ != 0 is found, the parsing is aborted.
|
||||
// Only "solid fill" (Typ 0) is supported.
|
||||
_elements.back()._fillStyles.reserve(fillStyleCount);
|
||||
for (uint i = 0; i < fillStyleCount; ++i) {
|
||||
byte type = bs.getByte();
|
||||
uint32 color;
|
||||
byte r = bs.getByte();
|
||||
byte g = bs.getByte();
|
||||
byte b = bs.getByte();
|
||||
byte a = 0xff;
|
||||
|
||||
if (shapeType == 3)
|
||||
a = bs.getByte();
|
||||
|
||||
color = BS_ARGB(a, r, g, b);
|
||||
|
||||
if (type != 0)
|
||||
return false;
|
||||
|
||||
_elements.back()._fillStyles.push_back(color);
|
||||
}
|
||||
|
||||
// Line styles parsen
|
||||
// -----------------
|
||||
|
||||
// Determine number of line styles
|
||||
uint lineStyleCount = bs.getByte();
|
||||
if (lineStyleCount == 0xff)
|
||||
lineStyleCount = bs.getUInt16();
|
||||
|
||||
// Readout all line styles
|
||||
_elements.back()._lineStyles.reserve(lineStyleCount);
|
||||
for (uint i = 0; i < lineStyleCount; ++i) {
|
||||
double width = bs.getUInt16();
|
||||
uint32 color;
|
||||
byte r = bs.getByte();
|
||||
byte g = bs.getByte();
|
||||
byte b = bs.getByte();
|
||||
byte a = 0xff;
|
||||
|
||||
if (shapeType == 3)
|
||||
a = bs.getByte();
|
||||
|
||||
color = BS_ARGB(a, r, g, b);
|
||||
|
||||
_elements.back()._lineStyles.push_back(VectorImageElement::LineStyleType(width, color));
|
||||
}
|
||||
|
||||
// Readout the bit width for the following style indices
|
||||
numFillBits = bs.getBits(4);
|
||||
numLineBits = bs.getBits(4);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool VectorImage::fill(const Common::Rect *pFillRect, uint color) {
|
||||
error("Fill() is not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
uint VectorImage::getPixel(int x, int y) {
|
||||
error("GetPixel() is not supported. Returning black.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool VectorImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
|
||||
error("SetContent() is not supported.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VectorImage::blit(int posX, int posY,
|
||||
int flipping,
|
||||
Common::Rect *pPartRect,
|
||||
uint color,
|
||||
int width, int height,
|
||||
RectangleList *updateRects) {
|
||||
static VectorImage *oldThis = 0;
|
||||
static int oldWidth = -2;
|
||||
static int oldHeight = -2;
|
||||
|
||||
// If width or height to 0, nothing needs to be shown.
|
||||
if (width == 0 || height == 0)
|
||||
return true;
|
||||
|
||||
// Determine if the old image in the cache can not be reused and must be recalculated
|
||||
if (!(oldThis == this && oldWidth == width && oldHeight == height)) {
|
||||
render(width, height);
|
||||
|
||||
oldThis = this;
|
||||
oldHeight = height;
|
||||
oldWidth = width;
|
||||
}
|
||||
|
||||
RenderedImage *rend = new RenderedImage();
|
||||
|
||||
rend->replaceContent(_pixelData, width, height);
|
||||
rend->blit(posX, posY, flipping, pPartRect, color, width, height, updateRects);
|
||||
|
||||
delete rend;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
232
engines/sword25/gfx/image/vectorimage.h
Normal file
232
engines/sword25/gfx/image/vectorimage.h
Normal file
@@ -0,0 +1,232 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_VECTORIMAGE_H
|
||||
#define SWORD25_VECTORIMAGE_H
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Includes
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "art.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class VectorImage;
|
||||
|
||||
/**
|
||||
@brief Pfadinformationen zu BS_VectorImageElement Objekten
|
||||
|
||||
Jedes BS_VectorImageElement besteht aus Kantenzügen, oder auch Pfaden. Jeder dieser Pfad hat Eigenschaften, die in Objekten diesen Typs
|
||||
gespeichert werden.
|
||||
*/
|
||||
|
||||
class VectorPathInfo {
|
||||
public:
|
||||
VectorPathInfo(ArtBpath *vec, int len, uint lineStyle, uint fillStyle0, uint fillStyle1) :
|
||||
_vec(vec), _lineStyle(lineStyle), _fillStyle0(fillStyle0), _fillStyle1(fillStyle1), _len(len) {}
|
||||
|
||||
VectorPathInfo() {
|
||||
_lineStyle = _fillStyle0 = _fillStyle1 = _len = 0;
|
||||
_vec = 0;
|
||||
}
|
||||
|
||||
ArtBpath *getVec() const {
|
||||
return _vec;
|
||||
}
|
||||
int getVecLen() const {
|
||||
return _len;
|
||||
}
|
||||
uint getLineStyle() const {
|
||||
return _lineStyle;
|
||||
}
|
||||
uint getFillStyle0() const {
|
||||
return _fillStyle0;
|
||||
}
|
||||
uint getFillStyle1() const {
|
||||
return _fillStyle1;
|
||||
}
|
||||
|
||||
private:
|
||||
ArtBpath *_vec;
|
||||
uint _lineStyle;
|
||||
uint _fillStyle0;
|
||||
uint _fillStyle1;
|
||||
uint _len;
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Ein Element eines Vektorbild. Ein BS_VectorImage besteht aus diesen Elementen, die jeweils einen Teil der Graphik definieren.
|
||||
Werden alle Elemente eines Vektorbildes übereinandergelegt, ergibt sich das komplette Bild.
|
||||
*/
|
||||
class VectorImageElement {
|
||||
friend class VectorImage;
|
||||
public:
|
||||
uint getPathCount() const {
|
||||
return _pathInfos.size();
|
||||
}
|
||||
const VectorPathInfo &getPathInfo(uint pathNr) const {
|
||||
assert(pathNr < getPathCount());
|
||||
return _pathInfos[pathNr];
|
||||
}
|
||||
|
||||
double getLineStyleWidth(uint lineStyle) const {
|
||||
assert(lineStyle < _lineStyles.size());
|
||||
return _lineStyles[lineStyle].width;
|
||||
}
|
||||
|
||||
uint getLineStyleCount() const {
|
||||
return _lineStyles.size();
|
||||
}
|
||||
|
||||
uint32 getLineStyleColor(uint lineStyle) const {
|
||||
assert(lineStyle < _lineStyles.size());
|
||||
return _lineStyles[lineStyle].color;
|
||||
}
|
||||
|
||||
uint getFillStyleCount() const {
|
||||
return _fillStyles.size();
|
||||
}
|
||||
|
||||
uint32 getFillStyleColor(uint fillStyle) const {
|
||||
assert(fillStyle < _fillStyles.size());
|
||||
return _fillStyles[fillStyle];
|
||||
}
|
||||
|
||||
const Common::Rect &getBoundingBox() const {
|
||||
return _boundingBox;
|
||||
}
|
||||
|
||||
private:
|
||||
struct LineStyleType {
|
||||
LineStyleType(double width_, uint32 color_) : width(width_), color(color_) {}
|
||||
LineStyleType() {
|
||||
width = 0;
|
||||
color = 0;
|
||||
}
|
||||
double width;
|
||||
uint32 color;
|
||||
};
|
||||
|
||||
Common::Array<VectorPathInfo> _pathInfos;
|
||||
Common::Array<LineStyleType> _lineStyles;
|
||||
Common::Array<uint32> _fillStyles;
|
||||
Common::Rect _boundingBox;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Eine Vektorgraphik
|
||||
|
||||
Objekte dieser Klasse enthalten die Informationen eines SWF-Shapes.
|
||||
*/
|
||||
|
||||
class VectorImage : public Image {
|
||||
public:
|
||||
VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname);
|
||||
~VectorImage() override;
|
||||
|
||||
uint getElementCount() const {
|
||||
return _elements.size();
|
||||
}
|
||||
const VectorImageElement &getElement(uint elementNr) const {
|
||||
assert(elementNr < _elements.size());
|
||||
return _elements[elementNr];
|
||||
}
|
||||
const Common::Rect &getBoundingBox() const {
|
||||
return _boundingBox;
|
||||
}
|
||||
|
||||
//
|
||||
// Die abstrakten Methoden von BS_Image
|
||||
//
|
||||
int getWidth() const override {
|
||||
return _boundingBox.width();
|
||||
}
|
||||
int getHeight() const override {
|
||||
return _boundingBox.height();
|
||||
}
|
||||
bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) override;
|
||||
|
||||
void render(int width, int height);
|
||||
|
||||
uint getPixel(int x, int y) override;
|
||||
bool isBlitSource() const override {
|
||||
return true;
|
||||
}
|
||||
bool isBlitTarget() const override {
|
||||
return false;
|
||||
}
|
||||
bool isScalingAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isFillingAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool isAlphaAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isColorModulationAllowed() const override {
|
||||
return true;
|
||||
}
|
||||
bool isSetContentAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) override;
|
||||
bool blit(int posX = 0, int posY = 0,
|
||||
int flipping = Graphics::FLIP_NONE,
|
||||
Common::Rect *pPartRect = NULL,
|
||||
uint color = BS_ARGB(255, 255, 255, 255),
|
||||
int width = -1, int height = -1,
|
||||
RectangleList *updateRects = 0) override;
|
||||
|
||||
class SWFBitStream;
|
||||
|
||||
private:
|
||||
bool parseDefineShape(uint shapeType, SWFBitStream &bs);
|
||||
bool parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits);
|
||||
|
||||
ArtBpath *storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated);
|
||||
Common::Array<VectorImageElement> _elements;
|
||||
Common::Rect _boundingBox;
|
||||
|
||||
byte *_pixelData;
|
||||
|
||||
Common::String _fname;
|
||||
uint _bgColor;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
470
engines/sword25/gfx/image/vectorimagerenderer.cpp
Normal file
470
engines/sword25/gfx/image/vectorimagerenderer.cpp
Normal file
@@ -0,0 +1,470 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code contains portions of Libart_LGPL - library of basic graphic primitives
|
||||
*
|
||||
* Copyright (c) 1998 Raph Levien
|
||||
*
|
||||
* Licensed under GNU LGPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code contains portions of Swfdec
|
||||
*
|
||||
* Copyright (c) 2004-2006 David Schleef <ds@schleef.org>
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/image/art.h"
|
||||
#include "sword25/gfx/image/vectorimage.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
void art_rgb_fill_run1(byte *buf, byte r, byte g, byte b, int n) {
|
||||
int i;
|
||||
|
||||
if (r == g && g == b && r == 255) {
|
||||
memset(buf, g, n + n + n + n);
|
||||
} else {
|
||||
uint32 *alt = (uint32 *)buf;
|
||||
uint32 color = MS_RGB(r, g, b);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
*alt++ = color;
|
||||
}
|
||||
}
|
||||
|
||||
void art_rgb_run_alpha1(byte *buf, byte r, byte g, byte b, int alpha, int n) {
|
||||
int i;
|
||||
int v;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
#if defined(SCUMM_LITTLE_ENDIAN)
|
||||
v = *buf;
|
||||
*buf++ = MIN(v + alpha, 0xff);
|
||||
v = *buf;
|
||||
*buf++ = v + (((b - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = v + (((g - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = v + (((r - v) * alpha + 0x80) >> 8);
|
||||
#else
|
||||
v = *buf;
|
||||
*buf++ = v + (((r - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = v + (((g - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = v + (((b - v) * alpha + 0x80) >> 8);
|
||||
v = *buf;
|
||||
*buf++ = MIN(v + alpha, 0xff);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
|
||||
|
||||
struct _ArtRgbSVPAlphaData {
|
||||
int alphatab[256];
|
||||
byte r, g, b, alpha;
|
||||
byte *buf;
|
||||
int rowstride;
|
||||
int x0, x1;
|
||||
};
|
||||
|
||||
static void art_rgb_svp_alpha_callback1(void *callback_data, int y,
|
||||
int start, ArtSVPRenderAAStep *steps, int n_steps) {
|
||||
ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
|
||||
byte *linebuf;
|
||||
int run_x0, run_x1;
|
||||
uint32 running_sum = start;
|
||||
int x0, x1;
|
||||
int k;
|
||||
byte r, g, b;
|
||||
int *alphatab;
|
||||
int alpha;
|
||||
|
||||
linebuf = data->buf;
|
||||
x0 = data->x0;
|
||||
x1 = data->x1;
|
||||
|
||||
r = data->r;
|
||||
g = data->g;
|
||||
b = data->b;
|
||||
alphatab = data->alphatab;
|
||||
|
||||
if (n_steps > 0) {
|
||||
run_x1 = steps[0].x;
|
||||
if (run_x1 > x0) {
|
||||
alpha = (running_sum >> 16) & 0xff;
|
||||
if (alpha)
|
||||
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
|
||||
}
|
||||
|
||||
for (k = 0; k < n_steps - 1; k++) {
|
||||
running_sum += steps[k].delta;
|
||||
run_x0 = run_x1;
|
||||
run_x1 = steps[k + 1].x;
|
||||
if (run_x1 > run_x0) {
|
||||
alpha = (running_sum >> 16) & 0xff;
|
||||
if (alpha)
|
||||
art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
|
||||
}
|
||||
}
|
||||
running_sum += steps[k].delta;
|
||||
if (x1 > run_x1) {
|
||||
alpha = (running_sum >> 16) & 0xff;
|
||||
if (alpha)
|
||||
art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
|
||||
}
|
||||
} else {
|
||||
alpha = (running_sum >> 16) & 0xff;
|
||||
if (alpha)
|
||||
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
|
||||
}
|
||||
|
||||
data->buf += data->rowstride;
|
||||
}
|
||||
|
||||
static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,
|
||||
int start,
|
||||
ArtSVPRenderAAStep *steps, int n_steps) {
|
||||
ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
|
||||
byte *linebuf;
|
||||
int run_x0, run_x1;
|
||||
uint32 running_sum = start;
|
||||
int x0, x1;
|
||||
int k;
|
||||
byte r, g, b;
|
||||
int *alphatab;
|
||||
int alpha;
|
||||
|
||||
linebuf = data->buf;
|
||||
x0 = data->x0;
|
||||
x1 = data->x1;
|
||||
|
||||
r = data->r;
|
||||
g = data->g;
|
||||
b = data->b;
|
||||
alphatab = data->alphatab;
|
||||
|
||||
if (n_steps > 0) {
|
||||
run_x1 = steps[0].x;
|
||||
if (run_x1 > x0) {
|
||||
alpha = running_sum >> 16;
|
||||
if (alpha) {
|
||||
if (alpha >= 255)
|
||||
art_rgb_fill_run1(linebuf, r, g, b, run_x1 - x0);
|
||||
else
|
||||
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
|
||||
}
|
||||
}
|
||||
|
||||
for (k = 0; k < n_steps - 1; k++) {
|
||||
running_sum += steps[k].delta;
|
||||
run_x0 = run_x1;
|
||||
run_x1 = steps[k + 1].x;
|
||||
if (run_x1 > run_x0) {
|
||||
alpha = running_sum >> 16;
|
||||
if (alpha) {
|
||||
if (alpha >= 255)
|
||||
art_rgb_fill_run1(linebuf + (run_x0 - x0) * 4, r, g, b, run_x1 - run_x0);
|
||||
else
|
||||
art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
running_sum += steps[k].delta;
|
||||
if (x1 > run_x1) {
|
||||
alpha = running_sum >> 16;
|
||||
if (alpha) {
|
||||
if (alpha >= 255)
|
||||
art_rgb_fill_run1(linebuf + (run_x1 - x0) * 4, r, g, b, x1 - run_x1);
|
||||
else
|
||||
art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alpha = running_sum >> 16;
|
||||
if (alpha) {
|
||||
if (alpha >= 255)
|
||||
art_rgb_fill_run1(linebuf, r, g, b, x1 - x0);
|
||||
else
|
||||
art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
|
||||
}
|
||||
}
|
||||
|
||||
data->buf += data->rowstride;
|
||||
}
|
||||
|
||||
void art_rgb_svp_alpha1(const ArtSVP *svp,
|
||||
int x0, int y0, int x1, int y1,
|
||||
uint32 color,
|
||||
byte *buf, int rowstride) {
|
||||
ArtRgbSVPAlphaData data;
|
||||
int i;
|
||||
int a, da;
|
||||
|
||||
data.r = (color >> 16) & 0xFF;
|
||||
data.g = (color >> 8) & 0xFF;
|
||||
data.b = (color >> 0) & 0xFF;
|
||||
data.alpha = (color >> 24) & 0xFF;
|
||||
|
||||
a = 0x8000;
|
||||
da = (data.alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
data.alphatab[i] = a >> 16;
|
||||
a += da;
|
||||
}
|
||||
|
||||
data.buf = buf;
|
||||
data.rowstride = rowstride;
|
||||
data.x0 = x0;
|
||||
data.x1 = x1;
|
||||
if (data.alpha == 255)
|
||||
art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1, &data);
|
||||
else
|
||||
art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
|
||||
}
|
||||
|
||||
static int art_vpath_len(ArtVpath *a) {
|
||||
int i = 0;
|
||||
while (a[i].code != ART_END)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
ArtVpath *art_vpath_cat(ArtVpath *a, ArtVpath *b) {
|
||||
ArtVpath *dest;
|
||||
ArtVpath *p;
|
||||
int len_a, len_b;
|
||||
|
||||
len_a = art_vpath_len(a);
|
||||
len_b = art_vpath_len(b);
|
||||
dest = art_new(ArtVpath, len_a + len_b + 1);
|
||||
if (!dest)
|
||||
error("[art_vpath_cat] Cannot allocate memory");
|
||||
|
||||
p = dest;
|
||||
|
||||
for (int i = 0; i < len_a; i++)
|
||||
*p++ = *a++;
|
||||
for (int i = 0; i <= len_b; i++)
|
||||
*p++ = *b++;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void art_svp_make_convex(ArtSVP *svp) {
|
||||
int i;
|
||||
|
||||
if (svp->n_segs > 0 && svp->segs[0].dir == 0) {
|
||||
for (i = 0; i < svp->n_segs; i++) {
|
||||
svp->segs[i].dir = !svp->segs[i].dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArtVpath *art_vpath_reverse(ArtVpath *a) {
|
||||
ArtVpath *dest;
|
||||
ArtVpath it;
|
||||
int len;
|
||||
int state = 0;
|
||||
int i;
|
||||
|
||||
len = art_vpath_len(a);
|
||||
dest = art_new(ArtVpath, len + 1);
|
||||
if (!dest)
|
||||
error("[art_vpath_reverse] Cannot allocate memory");
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
it = a[len - i - 1];
|
||||
if (state) {
|
||||
it.code = ART_LINETO;
|
||||
} else {
|
||||
it.code = ART_MOVETO_OPEN;
|
||||
state = 1;
|
||||
}
|
||||
if (a[len - i - 1].code == ART_MOVETO ||
|
||||
a[len - i - 1].code == ART_MOVETO_OPEN) {
|
||||
state = 0;
|
||||
}
|
||||
dest[i] = it;
|
||||
}
|
||||
dest[len] = a[len];
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
ArtVpath *art_vpath_reverse_free(ArtVpath *a) {
|
||||
ArtVpath *dest;
|
||||
|
||||
dest = art_vpath_reverse(a);
|
||||
free(a);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void drawBez(ArtBpath *bez1, ArtBpath *bez2, byte *buffer, int width, int height, int deltaX, int deltaY, double scaleX, double scaleY, double penWidth, unsigned int color) {
|
||||
ArtVpath *vec = NULL;
|
||||
ArtVpath *vec1 = NULL;
|
||||
ArtVpath *vec2 = NULL;
|
||||
ArtSVP *svp = NULL;
|
||||
|
||||
#if 0
|
||||
const char *codes[] = {"ART_MOVETO", "ART_MOVETO_OPEN", "ART_CURVETO", "ART_LINETO", "ART_END"};
|
||||
for (int i = 0;; i++) {
|
||||
debugN(" bez[%d].code = %s;\n", i, codes[bez[i].code]);
|
||||
if (bez[i].code == ART_END)
|
||||
break;
|
||||
if (bez[i].code == ART_CURVETO) {
|
||||
debugN(" bez[%d].x1 = %f; bez[%d].y1 = %f;\n", i, bez[i].x1, i, bez[i].y1);
|
||||
debugN(" bez[%d].x2 = %f; bez[%d].y2 = %f;\n", i, bez[i].x2, i, bez[i].y2);
|
||||
}
|
||||
debugN(" bez[%d].x3 = %f; bez[%d].y3 = %f;\n", i, bez[i].x3, i, bez[i].y3);
|
||||
}
|
||||
|
||||
debugN(" drawBez(bez, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
|
||||
#endif
|
||||
|
||||
// HACK: Some frames have green bounding boxes drawn.
|
||||
// Perhaps they were used by original game artist Umriss
|
||||
// We skip them just like the original
|
||||
if (bez2 == 0 && color == BS_RGB(0x00, 0xff, 0x00)) {
|
||||
return;
|
||||
}
|
||||
|
||||
vec1 = art_bez_path_to_vec(bez1, 0.5);
|
||||
if (bez2 != 0) {
|
||||
vec2 = art_bez_path_to_vec(bez2, 0.5);
|
||||
vec2 = art_vpath_reverse_free(vec2);
|
||||
vec = art_vpath_cat(vec1, vec2);
|
||||
|
||||
free(vec1);
|
||||
free(vec2);
|
||||
} else {
|
||||
vec = vec1;
|
||||
}
|
||||
|
||||
int size = art_vpath_len(vec);
|
||||
ArtVpath *vect = art_new(ArtVpath, size + 1);
|
||||
if (!vect)
|
||||
error("[drawBez] Cannot allocate memory");
|
||||
|
||||
int k;
|
||||
for (k = 0; k < size; k++) {
|
||||
vect[k].code = vec[k].code;
|
||||
vect[k].x = (vec[k].x - deltaX) * scaleX;
|
||||
vect[k].y = (vec[k].y - deltaY) * scaleY;
|
||||
}
|
||||
vect[k].code = ART_END;
|
||||
|
||||
if (bez2 == 0) { // Line drawing
|
||||
svp = art_svp_vpath_stroke(vect, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
|
||||
} else {
|
||||
svp = art_svp_from_vpath(vect);
|
||||
art_svp_make_convex(svp);
|
||||
}
|
||||
|
||||
art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4);
|
||||
|
||||
free(vect);
|
||||
art_svp_free(svp);
|
||||
free(vec);
|
||||
}
|
||||
|
||||
void VectorImage::render(int width, int height) {
|
||||
double scaleX = (width == - 1) ? 1 : static_cast<double>(width) / static_cast<double>(getWidth());
|
||||
double scaleY = (height == - 1) ? 1 : static_cast<double>(height) / static_cast<double>(getHeight());
|
||||
|
||||
debug(3, "VectorImage::render(%d, %d) %s", width, height, _fname.c_str());
|
||||
|
||||
if (_pixelData)
|
||||
free(_pixelData);
|
||||
|
||||
_pixelData = (byte *)malloc(width * height * 4);
|
||||
memset(_pixelData, 0, width * height * 4);
|
||||
|
||||
for (uint e = 0; e < _elements.size(); e++) {
|
||||
|
||||
//// Draw shapes
|
||||
for (uint s = 0; s < _elements[e].getFillStyleCount(); s++) {
|
||||
int fill0len = 0;
|
||||
int fill1len = 0;
|
||||
|
||||
// Count vector sizes in order to minimize memory
|
||||
// fragmentation
|
||||
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
||||
if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1)
|
||||
fill0len += _elements[e].getPathInfo(p).getVecLen();
|
||||
|
||||
if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1)
|
||||
fill1len += _elements[e].getPathInfo(p).getVecLen();
|
||||
}
|
||||
|
||||
// Now lump together vectors
|
||||
ArtBpath *fill1 = art_new(ArtBpath, fill1len + 1);
|
||||
ArtBpath *fill0 = art_new(ArtBpath, fill0len + 1);
|
||||
ArtBpath *fill1pos = fill1;
|
||||
ArtBpath *fill0pos = fill0;
|
||||
|
||||
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
||||
if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1) {
|
||||
for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
|
||||
*fill0pos++ = _elements[e].getPathInfo(p).getVec()[i];
|
||||
}
|
||||
|
||||
if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1) {
|
||||
for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
|
||||
*fill1pos++ = _elements[e].getPathInfo(p).getVec()[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Close vectors
|
||||
(*fill0pos).code = ART_END;
|
||||
(*fill1pos).code = ART_END;
|
||||
|
||||
drawBez(fill1, fill0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, -1, _elements[e].getFillStyleColor(s));
|
||||
|
||||
free(fill0);
|
||||
free(fill1);
|
||||
}
|
||||
|
||||
//// Draw strokes
|
||||
for (uint s = 0; s < _elements[e].getLineStyleCount(); s++) {
|
||||
double penWidth = _elements[e].getLineStyleWidth(s);
|
||||
penWidth *= sqrt(fabs(scaleX * scaleY));
|
||||
|
||||
for (uint p = 0; p < _elements[e].getPathCount(); p++) {
|
||||
if (_elements[e].getPathInfo(p).getLineStyle() == s + 1) {
|
||||
drawBez(_elements[e].getPathInfo(p).getVec(), 0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, penWidth, _elements[e].getLineStyleColor(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Sword25
|
||||
159
engines/sword25/gfx/microtiles.cpp
Normal file
159
engines/sword25/gfx/microtiles.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/microtiles.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
MicroTileArray::MicroTileArray(int16 width, int16 height) {
|
||||
_tilesW = (width / TileSize) + ((width % TileSize) > 0 ? 1 : 0);
|
||||
_tilesH = (height / TileSize) + ((height % TileSize) > 0 ? 1 : 0);
|
||||
_tiles = new BoundingBox[_tilesW * _tilesH];
|
||||
clear();
|
||||
}
|
||||
|
||||
MicroTileArray::~MicroTileArray() {
|
||||
delete[] _tiles;
|
||||
}
|
||||
|
||||
void MicroTileArray::addRect(Common::Rect r) {
|
||||
|
||||
int ux0, uy0, ux1, uy1;
|
||||
int tx0, ty0, tx1, ty1;
|
||||
int ix0, iy0, ix1, iy1;
|
||||
|
||||
r.clip(Common::Rect(0, 0, 799, 599));
|
||||
|
||||
ux0 = r.left / TileSize;
|
||||
uy0 = r.top / TileSize;
|
||||
ux1 = r.right / TileSize;
|
||||
uy1 = r.bottom / TileSize;
|
||||
|
||||
tx0 = r.left % TileSize;
|
||||
ty0 = r.top % TileSize;
|
||||
tx1 = r.right % TileSize;
|
||||
ty1 = r.bottom % TileSize;
|
||||
|
||||
for (int yc = uy0; yc <= uy1; yc++) {
|
||||
for (int xc = ux0; xc <= ux1; xc++) {
|
||||
ix0 = (xc == ux0) ? tx0 : 0;
|
||||
ix1 = (xc == ux1) ? tx1 : TileSize - 1;
|
||||
iy0 = (yc == uy0) ? ty0 : 0;
|
||||
iy1 = (yc == uy1) ? ty1 : TileSize - 1;
|
||||
updateBoundingBox(_tiles[xc + yc * _tilesW], ix0, iy0, ix1, iy1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MicroTileArray::clear() {
|
||||
memset(_tiles, 0, _tilesW * _tilesH * sizeof(BoundingBox));
|
||||
}
|
||||
|
||||
byte MicroTileArray::TileX0(const BoundingBox &boundingBox) {
|
||||
return (boundingBox >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
byte MicroTileArray::TileY0(const BoundingBox &boundingBox) {
|
||||
return (boundingBox >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
byte MicroTileArray::TileX1(const BoundingBox &boundingBox) {
|
||||
return (boundingBox >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
byte MicroTileArray::TileY1(const BoundingBox &boundingBox) {
|
||||
return boundingBox & 0xFF;
|
||||
}
|
||||
|
||||
bool MicroTileArray::isBoundingBoxEmpty(const BoundingBox &boundingBox) {
|
||||
return boundingBox == EmptyBoundingBox;
|
||||
}
|
||||
|
||||
bool MicroTileArray::isBoundingBoxFull(const BoundingBox &boundingBox) {
|
||||
return boundingBox == FullBoundingBox;
|
||||
}
|
||||
|
||||
void MicroTileArray::setBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1) {
|
||||
boundingBox = (x0 << 24) | (y0 << 16) | (x1 << 8) | y1;
|
||||
}
|
||||
|
||||
void MicroTileArray::updateBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1) {
|
||||
if (!isBoundingBoxEmpty(boundingBox)) {
|
||||
x0 = MIN(TileX0(boundingBox), x0);
|
||||
y0 = MIN(TileY0(boundingBox), y0);
|
||||
x1 = MAX(TileX1(boundingBox), x1);
|
||||
y1 = MAX(TileY1(boundingBox), y1);
|
||||
}
|
||||
setBoundingBox(boundingBox, x0, y0, x1, y1);
|
||||
}
|
||||
|
||||
RectangleList *MicroTileArray::getRectangles() {
|
||||
|
||||
RectangleList *rects = new RectangleList();
|
||||
|
||||
int x, y;
|
||||
int x0, y0, x1, y1;
|
||||
int i = 0;
|
||||
|
||||
for (y = 0; y < _tilesH; ++y) {
|
||||
for (x = 0; x < _tilesW; ++x) {
|
||||
|
||||
int finish = 0;
|
||||
BoundingBox boundingBox = _tiles[i];
|
||||
|
||||
if (isBoundingBoxEmpty(boundingBox)) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
x0 = (x * TileSize) + TileX0(boundingBox);
|
||||
y0 = (y * TileSize) + TileY0(boundingBox);
|
||||
y1 = (y * TileSize) + TileY1(boundingBox);
|
||||
|
||||
if (TileX1(boundingBox) == TileSize - 1 && x != _tilesW - 1) { // check if the tile continues
|
||||
while (!finish) {
|
||||
++x;
|
||||
++i;
|
||||
if (x == _tilesW || i >= _tilesW * _tilesH ||
|
||||
TileY0(_tiles[i]) != TileY0(boundingBox) ||
|
||||
TileY1(_tiles[i]) != TileY1(boundingBox) ||
|
||||
TileX0(_tiles[i]) != 0)
|
||||
{
|
||||
--x;
|
||||
--i;
|
||||
finish = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x1 = (x * TileSize) + TileX1(_tiles[i]);
|
||||
|
||||
rects->push_back(Common::Rect(x0, y0, x1 + 1, y1 + 1));
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return rects;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
63
engines/sword25/gfx/microtiles.h
Normal file
63
engines/sword25/gfx/microtiles.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_MICROTILES_H
|
||||
#define SWORD25_MICROTILES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/list.h"
|
||||
#include "common/util.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
typedef uint32 BoundingBox;
|
||||
|
||||
const BoundingBox FullBoundingBox = 0x00001F1F;
|
||||
const BoundingBox EmptyBoundingBox = 0x00000000;
|
||||
const int TileSize = 32;
|
||||
|
||||
class RectangleList : public Common::List<Common::Rect> {
|
||||
};
|
||||
|
||||
class MicroTileArray {
|
||||
public:
|
||||
MicroTileArray(int16 width, int16 height);
|
||||
~MicroTileArray();
|
||||
void addRect(Common::Rect r);
|
||||
void clear();
|
||||
RectangleList *getRectangles();
|
||||
protected:
|
||||
BoundingBox *_tiles;
|
||||
int16 _tilesW, _tilesH;
|
||||
byte TileX0(const BoundingBox &boundingBox);
|
||||
byte TileY0(const BoundingBox &boundingBox);
|
||||
byte TileX1(const BoundingBox &boundingBox);
|
||||
byte TileY1(const BoundingBox &boundingBox);
|
||||
bool isBoundingBoxEmpty(const BoundingBox &boundingBox);
|
||||
bool isBoundingBoxFull(const BoundingBox &boundingBox);
|
||||
void setBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1);
|
||||
void updateBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1);
|
||||
};
|
||||
|
||||
} // namespace Sword25
|
||||
|
||||
#endif // SWORD25_MICROTILES_H
|
||||
115
engines/sword25/gfx/panel.cpp
Normal file
115
engines/sword25/gfx/panel.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/panel.h"
|
||||
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "sword25/gfx/image/image.h"
|
||||
|
||||
#include "sword25/gfx/renderobjectmanager.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Panel::Panel(RenderObjectPtr<RenderObject> parentPtr, int width, int height, uint color) :
|
||||
RenderObject(parentPtr, RenderObject::TYPE_PANEL),
|
||||
_color(color) {
|
||||
_initSuccess = false;
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
if (_width < 0) {
|
||||
error("Tried to initialize a panel with an invalid width (%d).", _width);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_height < 0) {
|
||||
error("Tried to initialize a panel with an invalid height (%d).", _height);
|
||||
return;
|
||||
}
|
||||
|
||||
_initSuccess = true;
|
||||
}
|
||||
|
||||
Panel::Panel(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
|
||||
RenderObject(parentPtr, RenderObject::TYPE_PANEL, handle), _color(0) {
|
||||
_initSuccess = unpersist(reader);
|
||||
}
|
||||
|
||||
Panel::~Panel() {
|
||||
}
|
||||
|
||||
bool Panel::doRender(RectangleList *updateRects) {
|
||||
// Falls der Alphawert 0 ist, ist das Panel komplett durchsichtig und es muss nichts gezeichnet werden.
|
||||
if (_color >> BS_ASHIFT == 0)
|
||||
return true;
|
||||
|
||||
GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
|
||||
assert(gfxPtr);
|
||||
|
||||
for (RectangleList::iterator it = updateRects->begin(); it != updateRects->end(); ++it) {
|
||||
const Common::Rect &clipRect = *it;
|
||||
if (_bbox.intersects(clipRect)) {
|
||||
Common::Rect intersectionRect = _bbox.findIntersectingRect(clipRect);
|
||||
gfxPtr->fill(&intersectionRect, _color);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Panel::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
result &= RenderObject::persist(writer);
|
||||
writer.write(_color);
|
||||
|
||||
result &= RenderObject::persistChildren(writer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Panel::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
result &= RenderObject::unpersist(reader);
|
||||
|
||||
uint32 color;
|
||||
reader.read(color);
|
||||
setColor(color);
|
||||
|
||||
result &= RenderObject::unpersistChildren(reader);
|
||||
|
||||
return reader.isGood() && result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
71
engines/sword25/gfx/panel.h
Normal file
71
engines/sword25/gfx/panel.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_PANEL_H
|
||||
#define SWORD25_PANEL_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/renderobject.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Panel : public RenderObject {
|
||||
friend class RenderObject;
|
||||
|
||||
private:
|
||||
Panel(RenderObjectPtr<RenderObject> parentPtr, int width, int height, uint color);
|
||||
Panel(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
|
||||
|
||||
public:
|
||||
~Panel() override;
|
||||
|
||||
uint getColor() const {
|
||||
return _color;
|
||||
}
|
||||
void setColor(uint color) {
|
||||
if (_color != color) {
|
||||
_color = color;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
protected:
|
||||
bool doRender(RectangleList *updateRects) override;
|
||||
|
||||
private:
|
||||
uint32 _color;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
542
engines/sword25/gfx/renderobject.cpp
Normal file
542
engines/sword25/gfx/renderobject.cpp
Normal file
@@ -0,0 +1,542 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/renderobject.h"
|
||||
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
|
||||
#include "sword25/gfx/renderobjectregistry.h"
|
||||
#include "sword25/gfx/renderobjectmanager.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
|
||||
#include "sword25/gfx/bitmap.h"
|
||||
#include "sword25/gfx/staticbitmap.h"
|
||||
#include "sword25/gfx/dynamicbitmap.h"
|
||||
#include "sword25/gfx/animation.h"
|
||||
#include "sword25/gfx/panel.h"
|
||||
#include "sword25/gfx/text.h"
|
||||
#include "sword25/gfx/animationtemplate.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
int RenderObject::_nextGlobalVersion = 0;
|
||||
|
||||
RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
|
||||
_managerPtr(0),
|
||||
_parentPtr(parentPtr),
|
||||
_x(0),
|
||||
_y(0),
|
||||
_z(0),
|
||||
_oldX(-1),
|
||||
_oldY(-1),
|
||||
_oldZ(-1),
|
||||
_width(0),
|
||||
_height(0),
|
||||
_visible(true),
|
||||
_oldVisible(false),
|
||||
_childChanged(true),
|
||||
_type(type),
|
||||
_initSuccess(false),
|
||||
_refreshForced(true),
|
||||
_handle(0),
|
||||
_version(++_nextGlobalVersion),
|
||||
_isSolid(false) {
|
||||
|
||||
if (handle == 0)
|
||||
_handle = RenderObjectRegistry::instance().registerObject(this);
|
||||
else
|
||||
_handle = RenderObjectRegistry::instance().registerObject(this, handle);
|
||||
|
||||
if (_handle == 0)
|
||||
error("Failed to initialize RenderObject()");
|
||||
|
||||
updateAbsolutePos();
|
||||
|
||||
// Add this item to the children of the parent object, if not root (ParentPtr is invalid),
|
||||
// assign to the same RenderObjectManager.
|
||||
if (_parentPtr.isValid()) {
|
||||
_managerPtr = _parentPtr->getManager();
|
||||
_parentPtr->addObject(this->getHandle());
|
||||
} else {
|
||||
if (getType() != TYPE_ROOT) {
|
||||
error("Tried to create a non-root render object and has no parent. All non-root render objects have to have a parent.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updateObjectState();
|
||||
|
||||
_initSuccess = true;
|
||||
}
|
||||
|
||||
RenderObject::~RenderObject() {
|
||||
// Remove object from its parent.
|
||||
if (_parentPtr.isValid())
|
||||
_parentPtr->detatchChildren(this->getHandle());
|
||||
|
||||
deleteAllChildren();
|
||||
|
||||
RenderObjectRegistry::instance().deregisterObject(this);
|
||||
}
|
||||
|
||||
void RenderObject::preRender(RenderObjectQueue *renderQueue) {
|
||||
validateObject();
|
||||
|
||||
if (!_visible)
|
||||
return;
|
||||
|
||||
// If necessary, update the children rendering order of the updated objects.
|
||||
if (_childChanged) {
|
||||
sortRenderObjects();
|
||||
_childChanged = false;
|
||||
}
|
||||
|
||||
renderQueue->add(this);
|
||||
|
||||
RENDEROBJECT_ITER it = _children.begin();
|
||||
for (; it != _children.end(); ++it)
|
||||
(*it)->preRender(renderQueue);
|
||||
|
||||
}
|
||||
|
||||
bool RenderObject::render(RectangleList *updateRects, const Common::Array<int> &updateRectsMinZ) {
|
||||
|
||||
// Falls das Objekt nicht sichtbar ist, muss gar nichts gezeichnet werden
|
||||
if (!_visible)
|
||||
return true;
|
||||
|
||||
// Objekt zeichnen.
|
||||
bool needRender = false;
|
||||
int index = 0;
|
||||
|
||||
// Only draw if the bounding box intersects any update rectangle and
|
||||
// the object is in front of the minimum Z value.
|
||||
for (RectangleList::iterator rectIt = updateRects->begin(); !needRender && rectIt != updateRects->end(); ++rectIt, ++index)
|
||||
needRender = (_bbox.contains(*rectIt) || _bbox.intersects(*rectIt)) && getAbsoluteZ() >= updateRectsMinZ[index];
|
||||
|
||||
if (needRender)
|
||||
doRender(updateRects);
|
||||
|
||||
// Draw all children
|
||||
RENDEROBJECT_ITER it = _children.begin();
|
||||
for (; it != _children.end(); ++it)
|
||||
if (!(*it)->render(updateRects, updateRectsMinZ))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderObject::validateObject() {
|
||||
_oldBbox = _bbox;
|
||||
_oldVisible = _visible;
|
||||
_oldX = _x;
|
||||
_oldY = _y;
|
||||
_oldZ = _z;
|
||||
_refreshForced = false;
|
||||
}
|
||||
|
||||
bool RenderObject::updateObjectState() {
|
||||
// If the object has changed, the internal state must be recalculated and possibly
|
||||
// update Regions be registered for the next frame.
|
||||
if ((calcBoundingBox() != _oldBbox) ||
|
||||
(_visible != _oldVisible) ||
|
||||
(_x != _oldX) ||
|
||||
(_y != _oldY) ||
|
||||
(_z != _oldZ) ||
|
||||
_refreshForced) {
|
||||
if (_parentPtr.isValid())
|
||||
_parentPtr->signalChildChange();
|
||||
|
||||
// Die Bounding-Box neu berechnen und Update-Regions registrieren.
|
||||
updateBoxes();
|
||||
|
||||
++_version;
|
||||
|
||||
// Änderungen Validieren
|
||||
validateObject();
|
||||
}
|
||||
|
||||
// Dann muss der Objektstatus der Kinder aktualisiert werden.
|
||||
RENDEROBJECT_ITER it = _children.begin();
|
||||
for (; it != _children.end(); ++it)
|
||||
if (!(*it)->updateObjectState())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderObject::updateBoxes() {
|
||||
_bbox = calcBoundingBox();
|
||||
}
|
||||
|
||||
Common::Rect RenderObject::calcBoundingBox() const {
|
||||
Common::Rect bbox(0, 0, _width, _height);
|
||||
|
||||
// Die absolute Position der Bounding-Box berechnen.
|
||||
bbox.translate(_absoluteX, _absoluteY);
|
||||
|
||||
// Die Bounding-Box am Elternobjekt clippen.
|
||||
if (_parentPtr.isValid()) {
|
||||
bbox.clip(_parentPtr->getBbox());
|
||||
}
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
||||
void RenderObject::calcAbsolutePos(int32 &x, int32 &y, int32 &z) const {
|
||||
x = calcAbsoluteX();
|
||||
y = calcAbsoluteY();
|
||||
z = calcAbsoluteZ();
|
||||
}
|
||||
|
||||
int32 RenderObject::calcAbsoluteX() const {
|
||||
if (_parentPtr.isValid())
|
||||
return _parentPtr->getAbsoluteX() + _x;
|
||||
else
|
||||
return _x;
|
||||
}
|
||||
|
||||
int32 RenderObject::calcAbsoluteY() const {
|
||||
if (_parentPtr.isValid())
|
||||
return _parentPtr->getAbsoluteY() + _y;
|
||||
else
|
||||
return _y;
|
||||
}
|
||||
|
||||
int32 RenderObject::calcAbsoluteZ() const {
|
||||
if (_parentPtr.isValid())
|
||||
return _parentPtr->getAbsoluteZ() + _z;
|
||||
else
|
||||
return _z;
|
||||
}
|
||||
|
||||
void RenderObject::deleteAllChildren() {
|
||||
while (!_children.empty()) {
|
||||
RenderObjectPtr<RenderObject> curPtr = _children.back();
|
||||
curPtr.erase();
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderObject::addObject(RenderObjectPtr<RenderObject> pObject) {
|
||||
if (!pObject.isValid()) {
|
||||
error("Tried to add a null object to a renderobject.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert Object in the children list.
|
||||
_children.push_back(pObject);
|
||||
|
||||
// Make sure that before the next render the channel order is updated.
|
||||
if (_parentPtr.isValid())
|
||||
_parentPtr->signalChildChange();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderObject::detatchChildren(RenderObjectPtr<RenderObject> pObject) {
|
||||
// Kinderliste durchgehen und Objekt entfernen falls vorhanden
|
||||
RENDEROBJECT_ITER it = _children.begin();
|
||||
for (; it != _children.end(); ++it)
|
||||
if (*it == pObject) {
|
||||
_children.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
error("Tried to detach children from a render object that isn't its parent.");
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderObject::sortRenderObjects() {
|
||||
Common::sort(_children.begin(), _children.end(), greater);
|
||||
}
|
||||
|
||||
void RenderObject::updateAbsolutePos() {
|
||||
calcAbsolutePos(_absoluteX, _absoluteY, _absoluteZ);
|
||||
|
||||
RENDEROBJECT_ITER it = _children.begin();
|
||||
for (; it != _children.end(); ++it)
|
||||
(*it)->updateAbsolutePos();
|
||||
}
|
||||
|
||||
bool RenderObject::getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result) {
|
||||
result = pObject->getBbox();
|
||||
result.clip(_bbox);
|
||||
return result.isValidRect();
|
||||
}
|
||||
|
||||
void RenderObject::setPos(int x, int y) {
|
||||
_x = x;
|
||||
_y = y;
|
||||
updateAbsolutePos();
|
||||
}
|
||||
|
||||
void RenderObject::setX(int x) {
|
||||
_x = x;
|
||||
updateAbsolutePos();
|
||||
}
|
||||
|
||||
void RenderObject::setY(int y) {
|
||||
_y = y;
|
||||
updateAbsolutePos();
|
||||
}
|
||||
|
||||
void RenderObject::setZ(int z) {
|
||||
if (z < 0)
|
||||
error("Tried to set a negative Z value (%d).", z);
|
||||
else {
|
||||
_z = z;
|
||||
updateAbsolutePos();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderObject::setVisible(bool visible) {
|
||||
_visible = visible;
|
||||
}
|
||||
|
||||
RenderObjectPtr<Animation> RenderObject::addAnimation(const Common::String &filename) {
|
||||
RenderObjectPtr<Animation> aniPtr((new Animation(this->getHandle(), filename))->getHandle());
|
||||
if (aniPtr.isValid() && aniPtr->getInitSuccess())
|
||||
return aniPtr;
|
||||
else {
|
||||
if (aniPtr.isValid())
|
||||
aniPtr.erase();
|
||||
return RenderObjectPtr<Animation>();
|
||||
}
|
||||
}
|
||||
|
||||
RenderObjectPtr<Animation> RenderObject::addAnimation(const AnimationTemplate &animationTemplate) {
|
||||
Animation *aniPtr = new Animation(this->getHandle(), animationTemplate);
|
||||
if (aniPtr && aniPtr->getInitSuccess())
|
||||
return aniPtr->getHandle();
|
||||
else {
|
||||
delete aniPtr;
|
||||
return RenderObjectPtr<Animation>();
|
||||
}
|
||||
}
|
||||
|
||||
RenderObjectPtr<Bitmap> RenderObject::addBitmap(const Common::String &filename) {
|
||||
RenderObjectPtr<Bitmap> bitmapPtr((new StaticBitmap(this->getHandle(), filename))->getHandle());
|
||||
if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
|
||||
return RenderObjectPtr<Bitmap>(bitmapPtr);
|
||||
else {
|
||||
if (bitmapPtr.isValid())
|
||||
bitmapPtr.erase();
|
||||
return RenderObjectPtr<Bitmap>();
|
||||
}
|
||||
}
|
||||
|
||||
RenderObjectPtr<Bitmap> RenderObject::addDynamicBitmap(uint width, uint height) {
|
||||
RenderObjectPtr<Bitmap> bitmapPtr((new DynamicBitmap(this->getHandle(), width, height))->getHandle());
|
||||
if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
|
||||
return bitmapPtr;
|
||||
else {
|
||||
if (bitmapPtr.isValid())
|
||||
bitmapPtr.erase();
|
||||
return RenderObjectPtr<Bitmap>();
|
||||
}
|
||||
}
|
||||
|
||||
RenderObjectPtr<Panel> RenderObject::addPanel(int width, int height, uint color) {
|
||||
RenderObjectPtr<Panel> panelPtr((new Panel(this->getHandle(), width, height, color))->getHandle());
|
||||
if (panelPtr.isValid() && panelPtr->getInitSuccess())
|
||||
return panelPtr;
|
||||
else {
|
||||
if (panelPtr.isValid())
|
||||
panelPtr.erase();
|
||||
return RenderObjectPtr<Panel>();
|
||||
}
|
||||
}
|
||||
|
||||
RenderObjectPtr<Text> RenderObject::addText(const Common::String &font, const Common::String &text) {
|
||||
RenderObjectPtr<Text> textPtr((new Text(this->getHandle()))->getHandle());
|
||||
if (textPtr.isValid() && textPtr->getInitSuccess() && textPtr->setFont(font)) {
|
||||
textPtr->setText(text);
|
||||
return textPtr;
|
||||
} else {
|
||||
if (textPtr.isValid())
|
||||
textPtr.erase();
|
||||
return RenderObjectPtr<Text>();
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderObject::persist(OutputPersistenceBlock &writer) {
|
||||
// Typ und Handle werden als erstes gespeichert, damit beim Laden ein Objekt vom richtigen Typ mit dem richtigen Handle erzeugt werden kann.
|
||||
writer.write(static_cast<uint32>(_type));
|
||||
writer.write(_handle);
|
||||
|
||||
// Restliche Objekteigenschaften speichern.
|
||||
writer.write(_x);
|
||||
writer.write(_y);
|
||||
writer.write(_absoluteX);
|
||||
writer.write(_absoluteY);
|
||||
writer.write(_z);
|
||||
writer.write(_width);
|
||||
writer.write(_height);
|
||||
writer.write(_visible);
|
||||
writer.write(_childChanged);
|
||||
writer.write(_initSuccess);
|
||||
writer.write((int32)_bbox.left);
|
||||
writer.write((int32)_bbox.top);
|
||||
writer.write((int32)_bbox.right);
|
||||
writer.write((int32)_bbox.bottom);
|
||||
writer.write((int32)_oldBbox.left);
|
||||
writer.write((int32)_oldBbox.top);
|
||||
writer.write((int32)_oldBbox.right);
|
||||
writer.write((int32)_oldBbox.bottom);
|
||||
writer.write(_oldX);
|
||||
writer.write(_oldY);
|
||||
writer.write(_oldZ);
|
||||
writer.write(_oldVisible);
|
||||
writer.write(_parentPtr.isValid() ? _parentPtr->getHandle() : 0);
|
||||
writer.write(_refreshForced);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderObject::unpersist(InputPersistenceBlock &reader) {
|
||||
// Typ und Handle wurden schon von RecreatePersistedRenderObject() ausgelesen. Jetzt werden die restlichen Objekteigenschaften ausgelesen.
|
||||
reader.read(_x);
|
||||
reader.read(_y);
|
||||
reader.read(_absoluteX);
|
||||
reader.read(_absoluteY);
|
||||
reader.read(_z);
|
||||
reader.read(_width);
|
||||
reader.read(_height);
|
||||
reader.read(_visible);
|
||||
reader.read(_childChanged);
|
||||
reader.read(_initSuccess);
|
||||
reader.read(_bbox.left);
|
||||
reader.read(_bbox.top);
|
||||
reader.read(_bbox.right);
|
||||
reader.read(_bbox.bottom);
|
||||
reader.read(_oldBbox.left);
|
||||
reader.read(_oldBbox.top);
|
||||
reader.read(_oldBbox.right);
|
||||
reader.read(_oldBbox.bottom);
|
||||
reader.read(_oldX);
|
||||
reader.read(_oldY);
|
||||
reader.read(_oldZ);
|
||||
reader.read(_oldVisible);
|
||||
uint32 parentHandle;
|
||||
reader.read(parentHandle);
|
||||
_parentPtr = RenderObjectPtr<RenderObject>(parentHandle);
|
||||
reader.read(_refreshForced);
|
||||
|
||||
updateAbsolutePos();
|
||||
updateObjectState();
|
||||
|
||||
return reader.isGood();
|
||||
}
|
||||
|
||||
bool RenderObject::persistChildren(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
// Kinderanzahl speichern.
|
||||
writer.write((uint32)_children.size());
|
||||
|
||||
// Rekursiv alle Kinder speichern.
|
||||
RENDEROBJECT_LIST::iterator it = _children.begin();
|
||||
while (it != _children.end()) {
|
||||
result &= (*it)->persist(writer);
|
||||
++it;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RenderObject::unpersistChildren(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
// Kinderanzahl einlesen.
|
||||
uint32 childrenCount;
|
||||
reader.read(childrenCount);
|
||||
if (!reader.isGood())
|
||||
return false;
|
||||
|
||||
// Alle Kinder rekursiv wieder herstellen.
|
||||
for (uint32 i = 0; i < childrenCount; ++i) {
|
||||
if (!recreatePersistedRenderObject(reader).isValid())
|
||||
return false;
|
||||
}
|
||||
|
||||
return result && reader.isGood();
|
||||
}
|
||||
|
||||
RenderObjectPtr<RenderObject> RenderObject::recreatePersistedRenderObject(InputPersistenceBlock &reader) {
|
||||
RenderObjectPtr<RenderObject> result;
|
||||
|
||||
// Typ und Handle auslesen.
|
||||
uint32 type;
|
||||
uint32 handle;
|
||||
reader.read(type);
|
||||
reader.read(handle);
|
||||
if (!reader.isGood())
|
||||
return result;
|
||||
|
||||
switch (type) {
|
||||
case TYPE_PANEL:
|
||||
result = (new Panel(reader, this->getHandle(), handle))->getHandle();
|
||||
break;
|
||||
|
||||
case TYPE_STATICBITMAP:
|
||||
result = (new StaticBitmap(reader, this->getHandle(), handle))->getHandle();
|
||||
break;
|
||||
|
||||
case TYPE_DYNAMICBITMAP:
|
||||
// Videos are not normally saved: this probably indicates a bug, thus die here.
|
||||
//result = (new DynamicBitmap(reader, this->getHandle(), handle))->getHandle();
|
||||
error("Request to recreate a video. This is either a corrupted saved game, or a bug");
|
||||
break;
|
||||
|
||||
case TYPE_TEXT:
|
||||
result = (new Text(reader, this->getHandle(), handle))->getHandle();
|
||||
break;
|
||||
|
||||
case TYPE_ANIMATION:
|
||||
result = (new Animation(reader, this->getHandle(), handle))->getHandle();
|
||||
break;
|
||||
|
||||
default:
|
||||
error("Cannot recreate render object of unknown type %d.", type);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RenderObject::greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs) {
|
||||
// Das Objekt mit dem kleinem Z-Wert müssen zuerst gerendert werden.
|
||||
if (lhs->_z != rhs->_z)
|
||||
return lhs->_z < rhs->_z;
|
||||
// Falls der Z-Wert gleich ist, wird das weiter oben gelegenen Objekte zuerst gezeichnet.
|
||||
return lhs->_y < rhs->_y;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
553
engines/sword25/gfx/renderobject.h
Normal file
553
engines/sword25/gfx/renderobject.h
Normal file
@@ -0,0 +1,553 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
BS_RenderObject
|
||||
---------------
|
||||
Dieses ist die Klasse die sämtliche sichtbaren Objekte beschreibt. Alle anderen sichtbaren Objekte müssen von ihr abgeleitet werden.
|
||||
Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
|
||||
Alle BS_RenderObject Instanzen werden von einem BS_RenderObjectManager in einem Baum verwaltet.
|
||||
|
||||
Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RENDEROBJECT_H
|
||||
#define SWORD25_RENDEROBJECT_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
#include "common/rect.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "sword25/gfx/renderobjectptr.h"
|
||||
|
||||
#include "common/list.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
class RenderObjectManager;
|
||||
class RenderObjectQueue;
|
||||
class RectangleList;
|
||||
class Bitmap;
|
||||
class Animation;
|
||||
class AnimationTemplate;
|
||||
class Panel;
|
||||
class Text;
|
||||
|
||||
// Klassendefinition
|
||||
/**
|
||||
@brief Dieses ist die Klasse die sämtliche sichtbaren Objekte beschreibt.
|
||||
|
||||
Alle anderen sichtbaren Objekte müssen von ihr abgeleitet werden.
|
||||
Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
|
||||
Alle BS_RenderObject Instanzen werden von einem BS_RenderObjektManager in einem Baum verwaltet.
|
||||
*/
|
||||
class RenderObject {
|
||||
public:
|
||||
// Konstanten
|
||||
// ----------
|
||||
enum TYPES {
|
||||
/// Das Wurzelobjekt. Siehe BS_RenderObjectManager
|
||||
TYPE_ROOT,
|
||||
/// Ein Image. Siehe BS_Bitmap.
|
||||
TYPE_STATICBITMAP,
|
||||
TYPE_DYNAMICBITMAP,
|
||||
/// Eine Animation. Siehe BS_Animation.
|
||||
TYPE_ANIMATION,
|
||||
/// Eine farbige Fläche. Siehe BS_Panel.
|
||||
TYPE_PANEL,
|
||||
/// Ein Text. Siehe BS_Text.
|
||||
TYPE_TEXT,
|
||||
/// Ein unbekannter Objekttyp. Diesen Typ sollte kein Renderobjekt annehmen.
|
||||
TYPE_UNKNOWN
|
||||
};
|
||||
|
||||
// Add-Methoden
|
||||
// ------------
|
||||
|
||||
/**
|
||||
@brief Erzeugt ein Bitmap als Kinderobjekt des Renderobjektes.
|
||||
@param FileName der Dateiname der Quellbilddatei
|
||||
@return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
|
||||
Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
RenderObjectPtr<Bitmap> addBitmap(const Common::String &fileName);
|
||||
/**
|
||||
@brief Erzeugt ein veränderbares Bitmap als Kinderobjekt des Renderobjektes.
|
||||
@param Width die Breite des Bitmaps
|
||||
@param Height die Höhe des Bitmaps
|
||||
@return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
|
||||
Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
RenderObjectPtr<Bitmap> addDynamicBitmap(uint width, uint height);
|
||||
/**
|
||||
@brief Erzeugt eine Animation auf Basis einer Animationsdatei als Kinderobjekt des Renderobjektes.
|
||||
@param FileName der Dateiname der Quelldatei
|
||||
@return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
|
||||
Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
RenderObjectPtr<Animation> addAnimation(const Common::String &fileName);
|
||||
/**
|
||||
@brief Erzeugt eine Animation auf Basis eines Animationstemplate als Kinderobjekt des Renderobjektes.
|
||||
@param pAnimationTemplate ein Pointer auf das Animationstemplate
|
||||
@return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
|
||||
Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
@remark Das Renderobjekt übernimmt die Verwaltung des Animationstemplate.
|
||||
*/
|
||||
RenderObjectPtr<Animation> addAnimation(const AnimationTemplate &animationTemplate);
|
||||
/**
|
||||
@brief Erzeugt ein neues Farbpanel als Kinderobjekt des Renderobjektes.
|
||||
@param Width die Breite des Panels
|
||||
@param Height die Höhe des Panels
|
||||
@param Color die Farbe des Panels.<br>
|
||||
Der Standardwert ist Schwarz (BS_RGB(0, 0, 0)).
|
||||
@return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
|
||||
Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
|
||||
RenderObjectPtr<Panel> addPanel(int width, int height, uint color = BS_RGB(0, 0, 0));
|
||||
/**
|
||||
@brief Erzeugt ein Textobjekt als Kinderobjekt des Renderobjektes.
|
||||
@param Font der Dateiname des zu verwendenen Fonts
|
||||
@param Text der anzuzeigende Text.<br>
|
||||
Der Standardwert ist "".
|
||||
@return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
|
||||
Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
RenderObjectPtr<Text> addText(const Common::String &font, const Common::String &text = "");
|
||||
|
||||
// Cast-Methoden
|
||||
// -------------
|
||||
/**
|
||||
@brief Castet das Objekt zu einem BS_Bitmap-Objekt wenn zulässig.
|
||||
@return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
|
||||
Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
RenderObjectPtr<Bitmap> toBitmap() {
|
||||
if (_type == TYPE_STATICBITMAP || _type == TYPE_DYNAMICBITMAP)
|
||||
return RenderObjectPtr<Bitmap>(this->getHandle());
|
||||
else
|
||||
return RenderObjectPtr<Bitmap>();
|
||||
}
|
||||
/**
|
||||
@brief Castet das Objekt zu einem BS_Animation-Objekt wenn zulässig.
|
||||
@return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
|
||||
Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
RenderObjectPtr<Animation> toAnimation() {
|
||||
if (_type == TYPE_ANIMATION)
|
||||
return RenderObjectPtr<Animation>(this->getHandle());
|
||||
else
|
||||
return RenderObjectPtr<Animation>();
|
||||
}
|
||||
/**
|
||||
@brief Castet das Objekt zu einem BS_Panel-Objekt wenn zulässig.
|
||||
@return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
|
||||
Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
RenderObjectPtr<Panel> toPanel() {
|
||||
if (_type == TYPE_PANEL)
|
||||
return RenderObjectPtr<Panel>(this->getHandle());
|
||||
else
|
||||
return RenderObjectPtr<Panel>();
|
||||
}
|
||||
/**
|
||||
@brief Castet das Object zu einem BS_Text-Objekt wenn zulässig.
|
||||
@return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
|
||||
Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
|
||||
*/
|
||||
RenderObjectPtr<Text> toText() {
|
||||
if (_type == TYPE_TEXT)
|
||||
return RenderObjectPtr<Text>(this->getHandle());
|
||||
else
|
||||
return RenderObjectPtr<Text>();
|
||||
}
|
||||
|
||||
// Konstruktor / Desktruktor
|
||||
// -------------------------
|
||||
/**
|
||||
@brief Erzeugt ein neues BS_RenderObject.
|
||||
@param pKernel ein Pointer auf den Kernel
|
||||
@param pParent ein Pointer auf das Elternobjekt des neuen Objektes im Objektbaum.<br>
|
||||
Der Pointer darf nicht NULL sein.
|
||||
@param Type der Objekttyp<br>
|
||||
Der Typ BS_RenderObject::TYPE_ROOT ist nicht zulässig. Wurzelknoten müssen mit dem alternativen Konstruktor erzeugt
|
||||
werden.
|
||||
@param Handle das Handle, welches dem Objekt zugewiesen werden soll.<br>
|
||||
Dieser Parameter erzwingt ein bestimmtes Handle für das neue Objekt, oder wählt automatisch ein Handle, wenn der Parameter 0 ist.
|
||||
Ist das gewünschte Handle bereits vergeben, gibt GetInitSuccess() false zurück.<br>
|
||||
Der Standardwert ist 0.
|
||||
@remark Nach dem Aufruf des Konstruktors kann über die Methode GetInitSuccess() abgefragt werden, ob die Konstruktion erfolgreich war.<br>
|
||||
Es ist nicht notwendig alle BS_RenderObject Instanzen einzeln zu löschen. Dieses geschiet automatisch beim Löschen eines
|
||||
Vorfahren oder beim Löschen des zuständigen BS_RenderObjectManager.
|
||||
*/
|
||||
RenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle = 0);
|
||||
virtual ~RenderObject();
|
||||
|
||||
// Interface
|
||||
// ---------
|
||||
|
||||
void preRender(RenderObjectQueue *renderQueue);
|
||||
|
||||
/**
|
||||
@brief Rendert des Objekt und alle seine Unterobjekte.
|
||||
@return Gibt false zurück, falls beim Rendern ein Fehler aufgetreten ist.
|
||||
@remark Vor jedem Aufruf dieser Methode muss ein Aufruf von UpdateObjectState() erfolgt sein.
|
||||
Dieses kann entweder direkt geschehen oder durch den Aufruf von UpdateObjectState() an einem Vorfahren-Objekt.<br>
|
||||
Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
|
||||
*/
|
||||
bool render(RectangleList *updateRects, const Common::Array<int> &updateRectsMinZ);
|
||||
|
||||
/**
|
||||
@brief Bereitet das Objekt und alle seine Unterobjekte auf einen Rendervorgang vor.
|
||||
Hierbei werden alle Dirty-Rectangles berechnet und die Renderreihenfolge aktualisiert.
|
||||
@return Gibt false zurück, falls ein Fehler aufgetreten ist.
|
||||
@remark Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
|
||||
*/
|
||||
bool updateObjectState();
|
||||
/**
|
||||
@brief Löscht alle Kinderobjekte.
|
||||
*/
|
||||
void deleteAllChildren();
|
||||
|
||||
// Accessor-Methoden
|
||||
// -----------------
|
||||
/**
|
||||
@brief Setzt die Position des Objektes.
|
||||
@param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
|
||||
@param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
|
||||
*/
|
||||
virtual void setPos(int x, int y);
|
||||
/**
|
||||
@brief Setzt die Position des Objektes auf der X-Achse.
|
||||
@param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
|
||||
*/
|
||||
virtual void setX(int x);
|
||||
/**
|
||||
@brief Setzt die Position des Objektes auf der Y-Achse.
|
||||
@param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
|
||||
*/
|
||||
virtual void setY(int y);
|
||||
/**
|
||||
@brief Setzt den Z-Wert des Objektes.
|
||||
@param Z der neue Z-Wert des Objektes relativ zum Elternobjekt<br>
|
||||
Negative Z-Werte sind nicht zulässig.
|
||||
@remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit höherem
|
||||
Z-Wert gezeichnet. Je höher der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
|
||||
Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
|
||||
Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
|
||||
*/
|
||||
virtual void setZ(int z);
|
||||
/**
|
||||
@brief Setzt die Sichtbarkeit eine Objektes.
|
||||
@param Visible der neue Sichtbarkeits-Zustand des Objektes<br>
|
||||
true entspricht sichtbar, false entspricht unsichtbar.
|
||||
*/
|
||||
virtual void setVisible(bool visible);
|
||||
/**
|
||||
@brief Gibt die Position des Objektes auf der X-Achse relativ zum Elternobjekt zurück.
|
||||
*/
|
||||
virtual int getX() const {
|
||||
return _x;
|
||||
}
|
||||
/**
|
||||
@brief Gibt die Position des Objektes auf der Y-Achse relativ zum Elternobjekt zurück.
|
||||
*/
|
||||
virtual int getY() const {
|
||||
return _y;
|
||||
}
|
||||
/**
|
||||
@brief Gibt die absolute Position des Objektes auf der X-Achse zurück.
|
||||
*/
|
||||
virtual int getAbsoluteX() const {
|
||||
return _absoluteX;
|
||||
}
|
||||
/**
|
||||
@brief Gibt die absolute Position des Objektes auf der Y-Achse zurück.
|
||||
*/
|
||||
virtual int getAbsoluteY() const {
|
||||
return _absoluteY;
|
||||
}
|
||||
/**
|
||||
@brief Gibt den Z-Wert des Objektes relativ zum Elternobjekt zurück.
|
||||
@remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit höherem
|
||||
Z-Wert gezeichnet. Je höher der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
|
||||
Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
|
||||
Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
|
||||
*/
|
||||
int getZ() const {
|
||||
return _z;
|
||||
}
|
||||
|
||||
int getAbsoluteZ() const {
|
||||
return _absoluteZ;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt die Breite des Objektes zurück.
|
||||
*/
|
||||
int getWidth() const {
|
||||
return _width;
|
||||
}
|
||||
/**
|
||||
@brief Gibt die Höhe des Objektes zurück.
|
||||
*/
|
||||
int getHeight() const {
|
||||
return _height;
|
||||
}
|
||||
/**
|
||||
@brief Gibt den Sichtbarkeitszustand des Objektes zurück.
|
||||
@return Gibt den Sichtbarkeitszustand des Objektes zurück.<br>
|
||||
true entspricht sichtbar, false entspricht unsichtbar.
|
||||
*/
|
||||
bool isVisible() const {
|
||||
return _visible;
|
||||
}
|
||||
/**
|
||||
@brief Gibt den Typ des Objektes zurück.
|
||||
*/
|
||||
TYPES getType() const {
|
||||
return _type;
|
||||
}
|
||||
/**
|
||||
@brief Gibt zurück, ob das Objekt erfolgreich initialisiert wurde.
|
||||
@remark Hässlicher Workaround um das Problem, dass Konstruktoren keine Rückgabewerte haben.
|
||||
*/
|
||||
bool getInitSuccess() const {
|
||||
return _initSuccess;
|
||||
}
|
||||
/**
|
||||
@brief Gibt die Bounding-Box des Objektes zurück.
|
||||
@remark Diese Angabe erfolgt ausnahmsweise in Bildschirmkoordianten und nicht relativ zum Elternobjekt.
|
||||
*/
|
||||
const Common::Rect &getBbox() const {
|
||||
return _bbox;
|
||||
}
|
||||
/**
|
||||
@brief Stellt sicher, dass das Objekt im nächsten Frame neu gezeichnet wird.
|
||||
*/
|
||||
void forceRefresh() {
|
||||
_refreshForced = true;
|
||||
}
|
||||
/**
|
||||
@brief Gibt das Handle des Objekte zurück.
|
||||
*/
|
||||
uint32 getHandle() const {
|
||||
return _handle;
|
||||
}
|
||||
|
||||
// Get the RenderObjects current version
|
||||
int getVersion() const {
|
||||
return _version;
|
||||
}
|
||||
|
||||
bool isSolid() const {
|
||||
return _isSolid;
|
||||
}
|
||||
|
||||
// Persistenz-Methoden
|
||||
// -------------------
|
||||
virtual bool persist(OutputPersistenceBlock &writer);
|
||||
virtual bool unpersist(InputPersistenceBlock &reader);
|
||||
// TODO: Evtl. protected
|
||||
bool persistChildren(OutputPersistenceBlock &writer);
|
||||
bool unpersistChildren(InputPersistenceBlock &reader);
|
||||
// TODO: Evtl. private
|
||||
RenderObjectPtr<RenderObject> recreatePersistedRenderObject(InputPersistenceBlock &reader);
|
||||
|
||||
protected:
|
||||
// Typen
|
||||
// -----
|
||||
typedef Common::List<RenderObjectPtr<RenderObject> > RENDEROBJECT_LIST;
|
||||
typedef Common::List<RenderObjectPtr<RenderObject> >::iterator RENDEROBJECT_ITER;
|
||||
|
||||
int32 _x; ///< Die X-Position des Objektes relativ zum Eltern-Objekt
|
||||
int32 _y; ///< Die Y-Position des Objektes relativ zum Eltern-Objekt
|
||||
int32 _z; ///< Der Z-Wert des Objektes relativ zum Eltern-Objekt
|
||||
int32 _absoluteX; ///< Die absolute X-Position des Objektes
|
||||
int32 _absoluteY; ///< Die absolute Y-Position des Objektes
|
||||
int32 _absoluteZ;
|
||||
int32 _width; ///< Die Breite des Objektes
|
||||
int32 _height; ///< Die Höhe des Objektes
|
||||
bool _visible; ///< Ist true, wenn das Objekt sichtbar ist
|
||||
bool _childChanged; ///< Ist true, wenn sich ein Kinderobjekt verändert hat
|
||||
TYPES _type; ///< Der Objekttyp
|
||||
bool _initSuccess; ///< Ist true, wenn Objekt erfolgreich intialisiert werden konnte
|
||||
Common::Rect _bbox; ///< Die Bounding-Box des Objektes in Bildschirmkoordinaten
|
||||
|
||||
// Kopien der Variablen, die für die Errechnung des Dirty-Rects und zur Bestimmung der Objektveränderung notwendig sind
|
||||
Common::Rect _oldBbox;
|
||||
int32 _oldX;
|
||||
int32 _oldY;
|
||||
int32 _oldZ;
|
||||
bool _oldVisible;
|
||||
|
||||
static int _nextGlobalVersion;
|
||||
|
||||
int32 _version;
|
||||
|
||||
// This should be set to true if the RenderObject is NOT alpha-blended to optimize drawing
|
||||
bool _isSolid;
|
||||
|
||||
/// Ein Pointer auf den BS_RenderObjektManager, der das Objekt verwaltet.
|
||||
RenderObjectManager *_managerPtr;
|
||||
|
||||
// Render-Methode
|
||||
// --------------
|
||||
/**
|
||||
@brief Einschubmethode, die den tatsächlichen Redervorgang durchführt.
|
||||
|
||||
Diese Methode wird von Render() aufgerufen um das Objekt darzustellen.
|
||||
Diese Methode sollte von allen Klassen implementiert werden, die von BS_RederObject erben, um das Zeichnen umzusetzen.
|
||||
|
||||
@return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
|
||||
@remark
|
||||
*/
|
||||
virtual bool doRender(RectangleList *updateRects) = 0; // { return true; }
|
||||
|
||||
// RenderObject-Baum Variablen
|
||||
// ---------------------------
|
||||
// Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
|
||||
// Alle Eigenschaften wie X, Y, Z und Visible eines BS_RenderObjects sind relativ zu denen seines Vaters.
|
||||
// Außerdem sind die Objekte von links nach rechts in ihrer Renderreihenfolge sortiert.
|
||||
// Das primäre Sortierkriterium ist hierbei der Z-Wert und das sekundäre der Y-Wert (von oben nach unten).
|
||||
// Beispiel:
|
||||
// Screen
|
||||
// / | \.
|
||||
// / | \.
|
||||
// / | \.
|
||||
// / | \.
|
||||
// Background Interface Maus
|
||||
// / \ / | \.
|
||||
// / \ / | \.
|
||||
// George Tür Icn1 Icn2 Icn3
|
||||
//
|
||||
// Wenn jetzt das Interface mit SetVisible() ausgeblendet würde, verschwinden auch die Icons, die sich im Interface
|
||||
// befinden.
|
||||
// Wenn der Hintergrund bewegt wird (Scrolling), bewegen sich auch die darauf befindlichen Gegenstände und Personen.
|
||||
|
||||
/// Ein Pointer auf das Elternobjekt.
|
||||
RenderObjectPtr<RenderObject> _parentPtr;
|
||||
/// Die Liste der Kinderobjekte nach der Renderreihenfolge geordnet
|
||||
RENDEROBJECT_LIST _children;
|
||||
|
||||
/**
|
||||
@brief Gibt einen Pointer auf den BS_RenderObjektManager zurück, der das Objekt verwaltet.
|
||||
*/
|
||||
RenderObjectManager *getManager() const {
|
||||
return _managerPtr;
|
||||
}
|
||||
/**
|
||||
@brief Fügt dem Objekt ein neues Kinderobjekt hinzu.
|
||||
@param pObject ein Pointer auf das einzufügende Objekt
|
||||
@return Gibt false zurück, falls das Objekt nicht eingefügt werden konnte.
|
||||
*/
|
||||
bool addObject(RenderObjectPtr<RenderObject> pObject);
|
||||
|
||||
private:
|
||||
/// Ist true, wenn das Objekt in nächsten Frame neu gezeichnet werden soll
|
||||
bool _refreshForced;
|
||||
|
||||
uint32 _handle;
|
||||
|
||||
/**
|
||||
@brief Entfernt ein Objekt aus der Kinderliste.
|
||||
@param pObject ein Pointer auf das zu entfernende Objekt
|
||||
@return Gibt false zurück, falls das zu entfernende Objekt nicht in der Liste gefunden werden konnte.
|
||||
*/
|
||||
bool detatchChildren(RenderObjectPtr<RenderObject> pObject);
|
||||
/**
|
||||
@brief Berechnet die Bounding-Box und registriert das Dirty-Rect beim BS_RenderObjectManager.
|
||||
*/
|
||||
void updateBoxes();
|
||||
/**
|
||||
@brief Berechnet die Bounding-Box des Objektes.
|
||||
@return Gibt die Bounding-Box des Objektes in Bildschirmkoordinaten zurück.
|
||||
*/
|
||||
Common::Rect calcBoundingBox() const;
|
||||
/**
|
||||
@brief Berechnet das Dirty-Rectangle des Objektes.
|
||||
@return Gibt das Dirty-Rectangle des Objektes in Bildschirmkoordinaten zurück.
|
||||
*/
|
||||
Common::Rect calcDirtyRect() const;
|
||||
/**
|
||||
@brief Berechnet die absolute Position des Objektes.
|
||||
*/
|
||||
void calcAbsolutePos(int32 &x, int32 &y, int32 &z) const;
|
||||
/**
|
||||
@brief Berechnet die absolute Position des Objektes auf der X-Achse.
|
||||
*/
|
||||
int32 calcAbsoluteX() const;
|
||||
/**
|
||||
@brief Berechnet die absolute Position des Objektes.
|
||||
*/
|
||||
int32 calcAbsoluteY() const;
|
||||
|
||||
int32 calcAbsoluteZ() const;
|
||||
|
||||
/**
|
||||
@brief Sortiert alle Kinderobjekte nach ihrem Renderang.
|
||||
*/
|
||||
void sortRenderObjects();
|
||||
/**
|
||||
@brief Validiert den Zustand eines Objektes nachdem die durch die Veränderung verursachten Folgen abgearbeitet wurden.
|
||||
*/
|
||||
void validateObject();
|
||||
/**
|
||||
@brief Berechnet die absolute Position des Objektes und aller seiner Kinderobjekte neu.
|
||||
|
||||
Diese Methode muss aufgerufen werden, wann immer sich die Position des Objektes verändert. Damit die Kinderobjekte immer die
|
||||
richtige absolute Position haben.
|
||||
*/
|
||||
void updateAbsolutePos();
|
||||
/**
|
||||
@brief Teilt dem Objekt mit, dass sich eines seiner Kinderobjekte dahingehend verändert hat, die eine erneute Bestimmung der
|
||||
Rendereihenfolge verlangt.
|
||||
*/
|
||||
void signalChildChange() {
|
||||
_childChanged = true;
|
||||
}
|
||||
/**
|
||||
@brief Berechnet des Schnittrechteck der Bounding-Box des Objektes mit einem anderen Objekt.
|
||||
@param pObjekt ein Pointer auf das Objekt mit dem geschnitten werden soll
|
||||
@param Result das Ergebnisrechteck
|
||||
@return Gibt false zurück, falls sich die Objekte gar nicht schneiden.
|
||||
*/
|
||||
bool getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result);
|
||||
/**
|
||||
@brief Vergleichsoperator der auf Objektpointern basiert statt auf Objekten.
|
||||
@remark Diese Methode wird fürs Sortieren der Kinderliste nach der Rendereihenfolge benutzt.
|
||||
*/
|
||||
static bool greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
220
engines/sword25/gfx/renderobjectmanager.cpp
Normal file
220
engines/sword25/gfx/renderobjectmanager.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/renderobjectmanager.h"
|
||||
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "sword25/gfx/animationtemplateregistry.h"
|
||||
#include "common/rect.h"
|
||||
#include "sword25/gfx/renderobject.h"
|
||||
#include "sword25/gfx/timedrenderobject.h"
|
||||
#include "sword25/gfx/rootrenderobject.h"
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
void RenderObjectQueue::add(RenderObject *renderObject) {
|
||||
push_back(RenderObjectQueueItem(renderObject, renderObject->getBbox(), renderObject->getVersion()));
|
||||
}
|
||||
|
||||
bool RenderObjectQueue::exists(const RenderObjectQueueItem &renderObjectQueueItem) {
|
||||
for (RenderObjectQueue::iterator it = begin(); it != end(); ++it)
|
||||
if ((*it)._renderObject == renderObjectQueueItem._renderObject &&
|
||||
(*it)._version == renderObjectQueueItem._version &&
|
||||
(*it)._bbox == renderObjectQueueItem._bbox)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
RenderObjectManager::RenderObjectManager(int width, int height, int framebufferCount) :
|
||||
_frameStarted(false) {
|
||||
// Wurzel des BS_RenderObject-Baumes erzeugen.
|
||||
_rootPtr = (new RootRenderObject(this, width, height))->getHandle();
|
||||
_uta = new MicroTileArray(width, height);
|
||||
_currQueue = new RenderObjectQueue();
|
||||
_prevQueue = new RenderObjectQueue();
|
||||
}
|
||||
|
||||
RenderObjectManager::~RenderObjectManager() {
|
||||
// Die Wurzel des Baumes löschen, damit werden alle BS_RenderObjects mitgelöscht.
|
||||
_rootPtr.erase();
|
||||
delete _uta;
|
||||
delete _currQueue;
|
||||
delete _prevQueue;
|
||||
}
|
||||
|
||||
void RenderObjectManager::startFrame() {
|
||||
_frameStarted = true;
|
||||
|
||||
// Verstrichene Zeit bestimmen
|
||||
int timeElapsed = Kernel::getInstance()->getGfx()->getLastFrameDurationMicro();
|
||||
|
||||
// Alle BS_TimedRenderObject Objekte über den Framestart und die verstrichene Zeit in Kenntnis setzen
|
||||
RenderObjectList::iterator iter = _timedRenderObjects.begin();
|
||||
for (; iter != _timedRenderObjects.end(); ++iter)
|
||||
(*iter)->frameNotification(timeElapsed);
|
||||
}
|
||||
|
||||
bool RenderObjectManager::render() {
|
||||
// Den Objekt-Status des Wurzelobjektes aktualisieren. Dadurch werden rekursiv alle Baumelemente aktualisiert.
|
||||
// Beim aktualisieren des Objekt-Status werden auch die Update-Rects gefunden, so dass feststeht, was neu gezeichnet
|
||||
// werden muss.
|
||||
if (!_rootPtr.isValid() || !_rootPtr->updateObjectState())
|
||||
return false;
|
||||
|
||||
_frameStarted = false;
|
||||
|
||||
// Die Render-Methode der Wurzel aufrufen. Dadurch wird das rekursive Rendern der Baumelemente angestoßen.
|
||||
|
||||
_currQueue->clear();
|
||||
_rootPtr->preRender(_currQueue);
|
||||
|
||||
_uta->clear();
|
||||
|
||||
// Add rectangles of objects which don't exist in this frame any more
|
||||
for (RenderObjectQueue::iterator it = _prevQueue->begin(); it != _prevQueue->end(); ++it) {
|
||||
if (!_currQueue->exists(*it))
|
||||
_uta->addRect((*it)._bbox);
|
||||
}
|
||||
|
||||
// Add rectangles of objects which are different from the previous frame
|
||||
for (RenderObjectQueue::iterator it = _currQueue->begin(); it != _currQueue->end(); ++it) {
|
||||
if (!_prevQueue->exists(*it))
|
||||
_uta->addRect((*it)._bbox);
|
||||
}
|
||||
|
||||
RectangleList *updateRects = _uta->getRectangles();
|
||||
Common::Array<int> updateRectsMinZ;
|
||||
|
||||
updateRectsMinZ.reserve(updateRects->size());
|
||||
|
||||
// Calculate the minimum drawing Z value of each update rectangle
|
||||
// Solid bitmaps with a Z order less than the value calculated here would be overdrawn again and
|
||||
// so don't need to be drawn in the first place which speeds things up a bit.
|
||||
for (RectangleList::iterator rectIt = updateRects->begin(); rectIt != updateRects->end(); ++rectIt) {
|
||||
int minZ = 0;
|
||||
for (RenderObjectQueue::iterator it = _currQueue->reverse_begin(); it != _currQueue->end(); --it) {
|
||||
if ((*it)._renderObject->isVisible() && (*it)._renderObject->isSolid() &&
|
||||
(*it)._renderObject->getBbox().contains(*rectIt)) {
|
||||
minZ = (*it)._renderObject->getAbsoluteZ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateRectsMinZ.push_back(minZ);
|
||||
}
|
||||
|
||||
if (_rootPtr->render(updateRects, updateRectsMinZ)) {
|
||||
// Copy updated rectangles to the video screen
|
||||
Graphics::ManagedSurface *backSurface = Kernel::getInstance()->getGfx()->getSurface();
|
||||
for (RectangleList::iterator rectIt = updateRects->begin(); rectIt != updateRects->end(); ++rectIt) {
|
||||
const int x = (*rectIt).left;
|
||||
const int y = (*rectIt).top;
|
||||
const int width = (*rectIt).width();
|
||||
const int height = (*rectIt).height();
|
||||
g_system->copyRectToScreen(backSurface->getBasePtr(x, y), backSurface->pitch, x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
delete updateRects;
|
||||
|
||||
SWAP(_currQueue, _prevQueue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderObjectManager::attatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> renderObjectPtr) {
|
||||
_timedRenderObjects.push_back(renderObjectPtr);
|
||||
}
|
||||
|
||||
void RenderObjectManager::detatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> renderObjectPtr) {
|
||||
for (uint i = 0; i < _timedRenderObjects.size(); i++)
|
||||
if (_timedRenderObjects[i] == renderObjectPtr) {
|
||||
_timedRenderObjects.remove_at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderObjectManager::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
// Alle Kinder des Wurzelknotens speichern. Dadurch werden alle BS_RenderObjects gespeichert rekursiv gespeichert.
|
||||
result &= _rootPtr->persistChildren(writer);
|
||||
|
||||
writer.write(_frameStarted);
|
||||
|
||||
// Referenzen auf die TimedRenderObjects persistieren.
|
||||
writer.write((uint32)_timedRenderObjects.size());
|
||||
RenderObjectList::const_iterator iter = _timedRenderObjects.begin();
|
||||
while (iter != _timedRenderObjects.end()) {
|
||||
writer.write((*iter)->getHandle());
|
||||
++iter;
|
||||
}
|
||||
|
||||
// Alle BS_AnimationTemplates persistieren.
|
||||
result &= AnimationTemplateRegistry::instance().persist(writer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RenderObjectManager::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
// Alle Kinder des Wurzelknotens löschen. Damit werden alle BS_RenderObjects gelöscht.
|
||||
_rootPtr->deleteAllChildren();
|
||||
|
||||
// Alle BS_RenderObjects wieder hestellen.
|
||||
if (!_rootPtr->unpersistChildren(reader))
|
||||
return false;
|
||||
|
||||
reader.read(_frameStarted);
|
||||
|
||||
// Momentan gespeicherte Referenzen auf TimedRenderObjects löschen.
|
||||
_timedRenderObjects.resize(0);
|
||||
|
||||
// Referenzen auf die TimedRenderObjects wieder herstellen.
|
||||
uint32 timedObjectCount;
|
||||
reader.read(timedObjectCount);
|
||||
for (uint32 i = 0; i < timedObjectCount; ++i) {
|
||||
uint32 handle;
|
||||
reader.read(handle);
|
||||
_timedRenderObjects.push_back(handle);
|
||||
}
|
||||
|
||||
// Alle BS_AnimationTemplates wieder herstellen.
|
||||
result &= AnimationTemplateRegistry::instance().unpersist(reader);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
145
engines/sword25/gfx/renderobjectmanager.h
Normal file
145
engines/sword25/gfx/renderobjectmanager.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
BS_RenderObjectManager
|
||||
----------------------
|
||||
Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig.
|
||||
|
||||
Sie sorgt z.B. dafür, dass die BS_RenderObjects in der richtigen Reihenfolge gerendert werden.
|
||||
|
||||
Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RENDEROBJECTMANAGER_H
|
||||
#define SWORD25_RENDEROBJECTMANAGER_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/renderobjectptr.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
|
||||
#include "sword25/gfx/microtiles.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
class RenderObject;
|
||||
class TimedRenderObject;
|
||||
class RenderObjectManager;
|
||||
|
||||
struct RenderObjectQueueItem {
|
||||
RenderObject *_renderObject;
|
||||
Common::Rect _bbox;
|
||||
int _version;
|
||||
RenderObjectQueueItem(RenderObject *renderObject, const Common::Rect &bbox, int version)
|
||||
: _renderObject(renderObject), _bbox(bbox), _version(version) {}
|
||||
};
|
||||
|
||||
class RenderObjectQueue : public Common::List<RenderObjectQueueItem> {
|
||||
public:
|
||||
void add(RenderObject *renderObject);
|
||||
bool exists(const RenderObjectQueueItem &renderObjectQueueItem);
|
||||
};
|
||||
|
||||
/**
|
||||
@brief Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig.
|
||||
|
||||
Sie sorgt dafür, dass die BS_RenderObjects in der richtigen Reihenfolge gerendert werden und ermöglicht den Zugriff auf die
|
||||
BS_RenderObjects über einen String.
|
||||
*/
|
||||
class RenderObjectManager : public Persistable {
|
||||
public:
|
||||
/**
|
||||
@brief Erzeugt ein neues BS_RenderObjectManager-Objekt.
|
||||
@param Width die horizontale Bildschirmauflösung in Pixeln
|
||||
@param Height die vertikale Bildschirmauflösung in Pixeln
|
||||
@param Die Anzahl an Framebuffern, die eingesetzt wird (Backbuffer + Primary).
|
||||
*/
|
||||
RenderObjectManager(int width, int height, int framebufferCount);
|
||||
~RenderObjectManager() override;
|
||||
|
||||
// Interface
|
||||
// ---------
|
||||
/**
|
||||
@brief Initialisiert den Manager für einen neuen Frame.
|
||||
@remark Alle Veränderungen an Objekten müssen nach einem Aufruf dieser Methode geschehen, damit sichergestellt ist, dass diese
|
||||
visuell umgesetzt werden.<br>
|
||||
Mit dem Aufruf dieser Methode werden die Rückgabewerte von GetUpdateRects() und GetUpdateRectCount() auf ihre Startwerte
|
||||
zurückgesetzt. Wenn man also mit diesen Werten arbeiten möchten, muss man dies nach einem Aufruf von Render() und vor
|
||||
einem Aufruf von StartFrame() tun.
|
||||
*/
|
||||
void startFrame();
|
||||
/**
|
||||
@brief Rendert alle Objekte die sich während des letzten Aufrufes von Render() verändert haben.
|
||||
@return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
|
||||
*/
|
||||
bool render();
|
||||
/**
|
||||
@brief Gibt einen Pointer auf die Wurzel des Objektbaumes zurück.
|
||||
*/
|
||||
RenderObjectPtr<RenderObject> getTreeRoot() {
|
||||
return _rootPtr;
|
||||
}
|
||||
/**
|
||||
@brief Fügt ein BS_TimedRenderObject in die Liste der zeitabhängigen Render-Objekte.
|
||||
|
||||
Alle Objekte die sich in dieser Liste befinden werden vor jedem Frame über die seit dem letzten Frame
|
||||
vergangene Zeit informiert, so dass sich ihren Zustand zeitabhängig verändern können.
|
||||
|
||||
@param RenderObject das einzufügende BS_TimedRenderObject
|
||||
*/
|
||||
void attatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> pRenderObject);
|
||||
/**
|
||||
@brief Entfernt ein BS_TimedRenderObject aus der Liste für zeitabhängige Render-Objekte.
|
||||
*/
|
||||
void detatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> pRenderObject);
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
private:
|
||||
bool _frameStarted;
|
||||
typedef Common::Array<RenderObjectPtr<TimedRenderObject> > RenderObjectList;
|
||||
RenderObjectList _timedRenderObjects;
|
||||
|
||||
MicroTileArray *_uta;
|
||||
RenderObjectQueue *_currQueue, *_prevQueue;
|
||||
|
||||
// RenderObject-Tree Variablen
|
||||
// ---------------------------
|
||||
// Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
|
||||
// Zu weiteren Informationen siehe: "renderobject.h"
|
||||
RenderObjectPtr<RenderObject> _rootPtr; // Die Wurzel der Baumes
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
75
engines/sword25/gfx/renderobjectptr.h
Normal file
75
engines/sword25/gfx/renderobjectptr.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RENDER_OBJECT_PTR_H
|
||||
#define SWORD25_RENDER_OBJECT_PTR_H
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Includes
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/renderobjectregistry.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class RenderObject;
|
||||
|
||||
template<class T>
|
||||
class RenderObjectPtr {
|
||||
public:
|
||||
RenderObjectPtr() : _handle(0) {}
|
||||
|
||||
RenderObjectPtr(uint handle) : _handle(handle) {}
|
||||
|
||||
T *operator->() const {
|
||||
return static_cast<T *>(RenderObjectRegistry::instance().resolveHandle(_handle));
|
||||
}
|
||||
|
||||
bool operator==(const RenderObjectPtr<T> & other) {
|
||||
return _handle == other._handle;
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return RenderObjectRegistry::instance().resolveHandle(_handle) != 0;
|
||||
}
|
||||
|
||||
void erase() {
|
||||
delete static_cast<T *>(RenderObjectRegistry::instance().resolveHandle(_handle));
|
||||
_handle = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
uint _handle;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
50
engines/sword25/gfx/renderobjectregistry.h
Normal file
50
engines/sword25/gfx/renderobjectregistry.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RENDEROBJECTREGISTRY_H
|
||||
#define SWORD25_RENDEROBJECTREGISTRY_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/objectregistry.h"
|
||||
|
||||
#include "common/singleton.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class RenderObject;
|
||||
|
||||
class RenderObjectRegistry :
|
||||
public ObjectRegistry<RenderObject>,
|
||||
public Common::Singleton<RenderObjectRegistry> {
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
68
engines/sword25/gfx/rootrenderobject.h
Normal file
68
engines/sword25/gfx/rootrenderobject.h
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_ROOTRENDEROBJECT_H
|
||||
#define SWORD25_ROOTRENDEROBJECT_H
|
||||
|
||||
// Includes
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/renderobject.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Forward Declarations
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class Kernel;
|
||||
|
||||
// Klassendefinition
|
||||
class RenderObjectManager;
|
||||
|
||||
class RootRenderObject : public RenderObject {
|
||||
friend class RenderObjectManager;
|
||||
|
||||
private:
|
||||
RootRenderObject(RenderObjectManager *managerPtr, int width, int height) :
|
||||
RenderObject(RenderObjectPtr<RenderObject>(), TYPE_ROOT) {
|
||||
_managerPtr = managerPtr;
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool doRender(RectangleList *updateRects) override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
125
engines/sword25/gfx/screenshot.cpp
Normal file
125
engines/sword25/gfx/screenshot.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "sword25/gfx/screenshot.h"
|
||||
#include "sword25/kernel/filesystemutil.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
#define THUMBNAIL_VERSION 1
|
||||
|
||||
bool Screenshot::saveToFile(Graphics::Surface *data, Common::WriteStream *stream) {
|
||||
// Convert the RGBA data to RGB
|
||||
const uint32 *pSrc = (const uint32 *)data->getPixels();
|
||||
|
||||
// Write our own custom header
|
||||
stream->writeUint32BE(MKTAG('S','C','R','N')); // SCRN, short for "Screenshot"
|
||||
stream->writeUint16LE(data->w);
|
||||
stream->writeUint16LE(data->h);
|
||||
stream->writeByte(THUMBNAIL_VERSION);
|
||||
|
||||
for (int y = 0; y < data->h; y++) {
|
||||
for (int x = 0; x < data->w; x++) {
|
||||
// This is only called by createThumbnail below, which
|
||||
// provides a fake 'surface' with LE data in it.
|
||||
byte a, r, g, b;
|
||||
|
||||
data->format.colorToARGB(*pSrc++, a, r, g, b);
|
||||
stream->writeByte(r);
|
||||
stream->writeByte(g);
|
||||
stream->writeByte(b);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Common::SeekableReadStream *Screenshot::createThumbnail(Graphics::Surface *data) {
|
||||
// This method takes a screen image with a dimension of 800x600, and creates a screenshot with a dimension of 200x125.
|
||||
// First 50 pixels are cut off the top and bottom (the interface boards in the game). The remaining image of 800x500
|
||||
// will be on a 16th of its size, reduced by being handed out in 4x4 pixel blocks and the average of each block
|
||||
// generates a pixel of the target image. Finally, the result as a PNG file is stored as a file.
|
||||
|
||||
// The source image must be 800x600.
|
||||
if (data->w != 800 || data->h != 600 || data->format.bytesPerPixel != 4) {
|
||||
error("The sreenshot dimensions have to be 800x600 in order to be saved as a thumbnail.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Buffer for the output thumbnail
|
||||
Graphics::Surface thumbnail;
|
||||
thumbnail.create(200, 125, g_system->getScreenFormat());
|
||||
|
||||
// Uber das Zielbild iterieren und einen Pixel zur Zeit berechnen.
|
||||
uint x, y;
|
||||
x = y = 0;
|
||||
|
||||
for (uint32 *pDest = (uint32 *)thumbnail.getPixels(); pDest < thumbnail.getBasePtr(0, thumbnail.h); ) {
|
||||
// Get an average over a 4x4 pixel block in the source image
|
||||
int alpha, red, green, blue;
|
||||
alpha = red = green = blue = 0;
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
const uint32 *srcP = (const uint32 *)data->getBasePtr(x * 4, y * 4 + j + 50);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
byte a, r, g, b;
|
||||
data->format.colorToARGB(*(srcP + i), a, r, g, b);
|
||||
alpha += a;
|
||||
red += r;
|
||||
green += g;
|
||||
blue += b;
|
||||
}
|
||||
}
|
||||
|
||||
*pDest++ = thumbnail.format.ARGBToColor(alpha / 16, red / 16, green / 16, blue / 16);
|
||||
|
||||
// Move to next block
|
||||
++x;
|
||||
if (x == (uint)thumbnail.w) {
|
||||
x = 0;
|
||||
++y;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a PNG representation of the thumbnail data
|
||||
Common::MemoryWriteStreamDynamic stream(DisposeAfterUse::NO);
|
||||
saveToFile(&thumbnail, &stream);
|
||||
thumbnail.free();
|
||||
|
||||
// Output a MemoryReadStream that encompasses the written data
|
||||
Common::SeekableReadStream *result = new Common::MemoryReadStream(stream.getData(), stream.size(),
|
||||
DisposeAfterUse::YES);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
47
engines/sword25/gfx/screenshot.h
Normal file
47
engines/sword25/gfx/screenshot.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_SCREENSHOT_H
|
||||
#define SWORD25_SCREENSHOT_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Screenshot {
|
||||
public:
|
||||
static bool saveToFile(Graphics::Surface *data, Common::WriteStream *stream);
|
||||
static Common::SeekableReadStream *createThumbnail(Graphics::Surface *data);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
186
engines/sword25/gfx/staticbitmap.cpp
Normal file
186
engines/sword25/gfx/staticbitmap.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/staticbitmap.h"
|
||||
#include "sword25/gfx/bitmapresource.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
StaticBitmap::StaticBitmap(RenderObjectPtr<RenderObject> parentPtr, const Common::String &filename) :
|
||||
Bitmap(parentPtr, TYPE_STATICBITMAP) {
|
||||
// Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
|
||||
if (!_initSuccess)
|
||||
return;
|
||||
|
||||
_initSuccess = initBitmapResource(filename);
|
||||
}
|
||||
|
||||
StaticBitmap::StaticBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
|
||||
Bitmap(parentPtr, TYPE_STATICBITMAP, handle) {
|
||||
_initSuccess = unpersist(reader);
|
||||
}
|
||||
|
||||
bool StaticBitmap::initBitmapResource(const Common::String &filename) {
|
||||
// Bild-Resource laden
|
||||
Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(filename);
|
||||
if (!resourcePtr) {
|
||||
warning("Could not request resource \"%s\".", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
if (resourcePtr->getType() != Resource::TYPE_BITMAP) {
|
||||
error("Requested resource \"%s\" is not a bitmap.", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
BitmapResource *bitmapPtr = static_cast<BitmapResource *>(resourcePtr);
|
||||
|
||||
// Den eindeutigen Dateinamen zum späteren Referenzieren speichern
|
||||
_resourceFilename = bitmapPtr->getFileName();
|
||||
|
||||
// RenderObject Eigenschaften aktualisieren
|
||||
_originalWidth = _width = bitmapPtr->getWidth();
|
||||
_originalHeight = _height = bitmapPtr->getHeight();
|
||||
|
||||
_isSolid = bitmapPtr->isSolid();
|
||||
|
||||
// Bild-Resource freigeben
|
||||
bitmapPtr->release();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StaticBitmap::~StaticBitmap() {
|
||||
}
|
||||
|
||||
bool StaticBitmap::doRender(RectangleList *updateRects) {
|
||||
// Bitmap holen
|
||||
Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
|
||||
assert(resourcePtr);
|
||||
assert(resourcePtr->getType() == Resource::TYPE_BITMAP);
|
||||
BitmapResource *bitmapResourcePtr = static_cast<BitmapResource *>(resourcePtr);
|
||||
|
||||
// Framebufferobjekt holen
|
||||
GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
|
||||
assert(gfxPtr);
|
||||
|
||||
// Bitmap zeichnen
|
||||
bool result;
|
||||
if (_scaleFactorX == 1.0f && _scaleFactorY == 1.0f) {
|
||||
result = bitmapResourcePtr->blit(_absoluteX, _absoluteY,
|
||||
(_flipV ? Graphics::FLIP_V : 0) |
|
||||
(_flipH ? Graphics::FLIP_H : 0),
|
||||
0, _modulationColor, -1, -1,
|
||||
updateRects);
|
||||
} else {
|
||||
result = bitmapResourcePtr->blit(_absoluteX, _absoluteY,
|
||||
(_flipV ? Graphics::FLIP_V : 0) |
|
||||
(_flipH ? Graphics::FLIP_H : 0),
|
||||
0, _modulationColor, _width, _height,
|
||||
updateRects);
|
||||
}
|
||||
|
||||
// Resource freigeben
|
||||
bitmapResourcePtr->release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint StaticBitmap::getPixel(int x, int y) const {
|
||||
assert(x >= 0 && x < _width);
|
||||
assert(y >= 0 && y < _height);
|
||||
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
|
||||
assert(pResource->getType() == Resource::TYPE_BITMAP);
|
||||
BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
|
||||
uint result = pBitmapResource->getPixel(x, y);
|
||||
pResource->release();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StaticBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
|
||||
error("SetContent() ist not supported with this object.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StaticBitmap::isAlphaAllowed() const {
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
|
||||
assert(pResource->getType() == Resource::TYPE_BITMAP);
|
||||
bool result = static_cast<BitmapResource *>(pResource)->isAlphaAllowed();
|
||||
pResource->release();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StaticBitmap::isColorModulationAllowed() const {
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
|
||||
assert(pResource->getType() == Resource::TYPE_BITMAP);
|
||||
bool result = static_cast<BitmapResource *>(pResource)->isColorModulationAllowed();
|
||||
pResource->release();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StaticBitmap::isScalingAllowed() const {
|
||||
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename);
|
||||
assert(pResource->getType() == Resource::TYPE_BITMAP);
|
||||
bool result = static_cast<BitmapResource *>(pResource)->isScalingAllowed();
|
||||
pResource->release();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StaticBitmap::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
result &= Bitmap::persist(writer);
|
||||
writer.writeString(_resourceFilename);
|
||||
|
||||
result &= RenderObject::persistChildren(writer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StaticBitmap::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
result &= Bitmap::unpersist(reader);
|
||||
Common::String resourceFilename;
|
||||
reader.readString(resourceFilename);
|
||||
// We may not have saves, and we actually do not need to
|
||||
// restore them. So do not even try to load them.
|
||||
if (!resourceFilename.hasPrefix("/saves"))
|
||||
result &= initBitmapResource(resourceFilename);
|
||||
|
||||
result &= RenderObject::unpersistChildren(reader);
|
||||
|
||||
return reader.isGood() && result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
77
engines/sword25/gfx/staticbitmap.h
Normal file
77
engines/sword25/gfx/staticbitmap.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_STATIC_BITMAP_H
|
||||
#define SWORD25_STATIC_BITMAP_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/bitmap.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class StaticBitmap : public Bitmap {
|
||||
friend class RenderObject;
|
||||
|
||||
private:
|
||||
/**
|
||||
@remark Filename muss absoluter Pfad sein
|
||||
*/
|
||||
StaticBitmap(RenderObjectPtr<RenderObject> parentPtr, const Common::String &filename);
|
||||
StaticBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
|
||||
|
||||
public:
|
||||
~StaticBitmap() override;
|
||||
|
||||
uint getPixel(int x, int y) const override;
|
||||
|
||||
bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) override;
|
||||
|
||||
bool isScalingAllowed() const override;
|
||||
bool isAlphaAllowed() const override;
|
||||
bool isColorModulationAllowed() const override;
|
||||
bool isSetContentAllowed() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
protected:
|
||||
bool doRender(RectangleList *updateRects) override;
|
||||
|
||||
private:
|
||||
Common::String _resourceFilename;
|
||||
|
||||
bool initBitmapResource(const Common::String &filename);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
369
engines/sword25/gfx/text.cpp
Normal file
369
engines/sword25/gfx/text.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
#include "common/unicode-bidi.h"
|
||||
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/resmanager.h" // for PRECACHE_RESOURCES
|
||||
#include "sword25/gfx/fontresource.h"
|
||||
#include "sword25/gfx/bitmapresource.h"
|
||||
|
||||
#include "sword25/gfx/text.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
namespace {
|
||||
const uint32 AUTO_WRAP_THRESHOLD_DEFAULT = 300;
|
||||
}
|
||||
|
||||
Text::Text(RenderObjectPtr<RenderObject> parentPtr) :
|
||||
RenderObject(parentPtr, RenderObject::TYPE_TEXT),
|
||||
_modulationColor(BS_ARGBMASK),
|
||||
_autoWrap(false),
|
||||
_autoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
|
||||
|
||||
}
|
||||
|
||||
Text::Text(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
|
||||
RenderObject(parentPtr, TYPE_TEXT, handle),
|
||||
// Temporarily set fields prior to unpersisting actual values
|
||||
_modulationColor(BS_ARGBMASK),
|
||||
_autoWrap(false),
|
||||
_autoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
|
||||
|
||||
// Unpersist the fields
|
||||
_initSuccess = unpersist(reader);
|
||||
}
|
||||
|
||||
bool Text::setFont(const Common::String &font) {
|
||||
// Load font
|
||||
|
||||
#ifdef PRECACHE_RESOURCES
|
||||
if (getResourceManager()->precacheResource(font)) {
|
||||
_font = font;
|
||||
updateFormat();
|
||||
forceRefresh();
|
||||
return true;
|
||||
} else {
|
||||
error("Could not precache font \"%s\". Font probably does not exist.", font.c_str());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
Resource *pResource = getResourceManager()->requestResource(font);
|
||||
pResource->release(); //unlock precached resource
|
||||
_font = font;
|
||||
updateFormat();
|
||||
forceRefresh();
|
||||
return true;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void Text::setText(const Common::String &text) {
|
||||
if (_text != text) {
|
||||
_text = text;
|
||||
updateFormat();
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::setColor(uint32 modulationColor) {
|
||||
uint32 newModulationColor = (modulationColor & BS_RGBMASK) | (_modulationColor & BS_AMASK);
|
||||
if (newModulationColor != _modulationColor) {
|
||||
_modulationColor = newModulationColor;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::setAlpha(int alpha) {
|
||||
assert(alpha >= 0 && alpha < 256);
|
||||
uint32 newModulationColor = (_modulationColor & BS_RGBMASK) | (alpha << BS_ASHIFT);
|
||||
if (newModulationColor != _modulationColor) {
|
||||
_modulationColor = newModulationColor;
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::setAutoWrap(bool autoWrap) {
|
||||
if (autoWrap != _autoWrap) {
|
||||
_autoWrap = autoWrap;
|
||||
updateFormat();
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Text::setAutoWrapThreshold(uint32 autoWrapThreshold) {
|
||||
if (autoWrapThreshold != _autoWrapThreshold) {
|
||||
_autoWrapThreshold = autoWrapThreshold;
|
||||
updateFormat();
|
||||
forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
bool Text::doRender(RectangleList *updateRects) {
|
||||
// lock Font Resource
|
||||
FontResource *fontPtr = lockFontResource();
|
||||
if (!fontPtr)
|
||||
return false;
|
||||
|
||||
// lock Character map resource
|
||||
ResourceManager *rmPtr = getResourceManager();
|
||||
BitmapResource *charMapPtr;
|
||||
{
|
||||
Resource *pResource = rmPtr->requestResource(fontPtr->getCharactermapFileName());
|
||||
if (!pResource) {
|
||||
warning("Could not request resource \"%s\".", fontPtr->getCharactermapFileName().c_str());
|
||||
return false;
|
||||
}
|
||||
if (pResource->getType() != Resource::TYPE_BITMAP) {
|
||||
error("Requested resource \"%s\" is not a bitmap.", fontPtr->getCharactermapFileName().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
charMapPtr = static_cast<BitmapResource *>(pResource);
|
||||
}
|
||||
|
||||
// Getting frame buffer object
|
||||
GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx();
|
||||
assert(gfxPtr);
|
||||
|
||||
bool result = true;
|
||||
Common::Array<Line>::iterator iter = _lines.begin();
|
||||
for (; iter != _lines.end(); ++iter) {
|
||||
// Determine whether any letters of the current line are affected by the update.
|
||||
Common::Rect checkRect = (*iter).bbox;
|
||||
checkRect.translate(_absoluteX, _absoluteY);
|
||||
|
||||
// Render each letter individually.
|
||||
int curX = _absoluteX + (*iter).bbox.left;
|
||||
int curY = _absoluteY + (*iter).bbox.top;
|
||||
for (uint i = 0; i < (*iter).text.size(); ++i) {
|
||||
Common::Rect curRect = fontPtr->getCharacterRect((byte)(*iter).text[i]);
|
||||
|
||||
Common::Rect renderRect(curX, curY, curX + curRect.width(), curY + curRect.height());
|
||||
renderRect.translate(curRect.left - curX, curRect.top - curY);
|
||||
result = charMapPtr->blit(curX, curY, Graphics::FLIP_NONE, &renderRect, _modulationColor, -1, -1, updateRects);
|
||||
if (!result)
|
||||
break;
|
||||
|
||||
curX += curRect.width() + fontPtr->getGapWidth();
|
||||
}
|
||||
}
|
||||
|
||||
// Free Character map resource
|
||||
charMapPtr->release();
|
||||
|
||||
// Free Font resource
|
||||
fontPtr->release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ResourceManager *Text::getResourceManager() {
|
||||
// Getting pointer to resource manager
|
||||
return Kernel::getInstance()->getResourceManager();
|
||||
}
|
||||
|
||||
FontResource *Text::lockFontResource() {
|
||||
ResourceManager *rmPtr = getResourceManager();
|
||||
|
||||
// Lock font resource
|
||||
FontResource *fontPtr;
|
||||
{
|
||||
Resource *resourcePtr = rmPtr->requestResource(_font);
|
||||
if (!resourcePtr) {
|
||||
warning("Could not request resource \"%s\".", _font.c_str());
|
||||
return NULL;
|
||||
}
|
||||
if (resourcePtr->getType() != Resource::TYPE_FONT) {
|
||||
error("Requested resource \"%s\" is not a font.", _font.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fontPtr = static_cast<FontResource *>(resourcePtr);
|
||||
}
|
||||
|
||||
return fontPtr;
|
||||
}
|
||||
|
||||
void Text::updateFormat() {
|
||||
FontResource *fontPtr = lockFontResource();
|
||||
assert(fontPtr);
|
||||
|
||||
bool isRTL = Kernel::getInstance()->getGfx()->isRTL();
|
||||
updateMetrics(*fontPtr);
|
||||
|
||||
_lines.resize(1);
|
||||
if (_autoWrap && (uint) _width >= _autoWrapThreshold && _text.size() >= 2) {
|
||||
_width = 0;
|
||||
uint curLineWidth = 0;
|
||||
uint curLineHeight = 0;
|
||||
uint curLine = 0;
|
||||
uint tempLineWidth = 0;
|
||||
uint lastSpace = 0; // we need at least 1 space character to start a new line...
|
||||
_lines[0].text = "";
|
||||
for (uint i = 0; i < _text.size(); ++i) {
|
||||
uint j;
|
||||
tempLineWidth = 0;
|
||||
lastSpace = 0;
|
||||
for (j = i; j < _text.size(); ++j) {
|
||||
if ((byte)_text[j] == ' ')
|
||||
lastSpace = j;
|
||||
|
||||
const Common::Rect &curCharRect = fontPtr->getCharacterRect((byte)_text[j]);
|
||||
tempLineWidth += curCharRect.width();
|
||||
tempLineWidth += fontPtr->getGapWidth();
|
||||
|
||||
if ((tempLineWidth >= _autoWrapThreshold) && (lastSpace > 0))
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == _text.size()) // everything in 1 line.
|
||||
lastSpace = _text.size();
|
||||
|
||||
curLineWidth = 0;
|
||||
curLineHeight = 0;
|
||||
for (j = i; j < lastSpace; ++j) {
|
||||
_lines[curLine].text += _text[j];
|
||||
|
||||
const Common::Rect &curCharRect = fontPtr->getCharacterRect((byte)_text[j]);
|
||||
curLineWidth += curCharRect.width();
|
||||
curLineWidth += fontPtr->getGapWidth();
|
||||
if ((uint)curCharRect.height() > curLineHeight)
|
||||
curLineHeight = curCharRect.height();
|
||||
}
|
||||
|
||||
if (isRTL) {
|
||||
_lines[curLine].text = Common::convertBiDiString(_lines[curLine].text, Common::kWindows1255);
|
||||
}
|
||||
|
||||
_lines[curLine].bbox.right = curLineWidth;
|
||||
_lines[curLine].bbox.bottom = curLineHeight;
|
||||
if ((uint)_width < curLineWidth)
|
||||
_width = curLineWidth;
|
||||
|
||||
if (lastSpace < _text.size()) {
|
||||
++curLine;
|
||||
assert(curLine == _lines.size());
|
||||
_lines.resize(curLine + 1);
|
||||
_lines[curLine].text = "";
|
||||
}
|
||||
|
||||
i = lastSpace;
|
||||
}
|
||||
|
||||
// Bounding box of each line relative to the first set (center aligned).
|
||||
_height = 0;
|
||||
Common::Array<Line>::iterator iter = _lines.begin();
|
||||
for (; iter != _lines.end(); ++iter) {
|
||||
Common::Rect &bbox = (*iter).bbox;
|
||||
bbox.left = (_width - bbox.right) / 2;
|
||||
bbox.right = bbox.left + bbox.right;
|
||||
bbox.top = (iter - _lines.begin()) * fontPtr->getLineHeight();
|
||||
bbox.bottom = bbox.top + bbox.bottom;
|
||||
_height += bbox.height();
|
||||
}
|
||||
} else {
|
||||
if (isRTL) {
|
||||
_lines[0].text = Common::convertBiDiString(_text, Common::kWindows1255);
|
||||
} else {
|
||||
// No auto format, so all the text is copied to a single line.
|
||||
_lines[0].text = _text;
|
||||
}
|
||||
_lines[0].bbox = Common::Rect(0, 0, _width, _height);
|
||||
}
|
||||
|
||||
fontPtr->release();
|
||||
}
|
||||
|
||||
void Text::updateMetrics(FontResource &fontResource) {
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
|
||||
for (uint i = 0; i < _text.size(); ++i) {
|
||||
const Common::Rect &curRect = fontResource.getCharacterRect((byte)_text[i]);
|
||||
_width += curRect.width();
|
||||
if (i != _text.size() - 1)
|
||||
_width += fontResource.getGapWidth();
|
||||
if (_height < curRect.height())
|
||||
_height = curRect.height();
|
||||
}
|
||||
}
|
||||
|
||||
bool Text::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
result &= RenderObject::persist(writer);
|
||||
|
||||
writer.write(_modulationColor);
|
||||
writer.writeString(_font);
|
||||
writer.writeString(_text);
|
||||
writer.write(_autoWrap);
|
||||
writer.write(_autoWrapThreshold);
|
||||
|
||||
result &= RenderObject::persistChildren(writer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Text::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
result &= RenderObject::unpersist(reader);
|
||||
|
||||
// Read color and alpha
|
||||
reader.read(_modulationColor);
|
||||
|
||||
// Run all methods on loading relevant members.
|
||||
// So, the layout is automatically updated and all necessary logic is executed.
|
||||
|
||||
Common::String font;
|
||||
reader.readString(font);
|
||||
setFont(font);
|
||||
|
||||
Common::String text;
|
||||
reader.readString(text);
|
||||
setText(text);
|
||||
|
||||
bool autoWrap;
|
||||
reader.read(autoWrap);
|
||||
setAutoWrap(autoWrap);
|
||||
|
||||
uint32 autoWrapThreshold;
|
||||
reader.read(autoWrapThreshold);
|
||||
setAutoWrapThreshold(autoWrapThreshold);
|
||||
|
||||
result &= RenderObject::unpersistChildren(reader);
|
||||
|
||||
return reader.isGood() && result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
165
engines/sword25/gfx/text.h
Normal file
165
engines/sword25/gfx/text.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_TEXT_H
|
||||
#define SWORD25_TEXT_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "common/rect.h"
|
||||
#include "sword25/gfx/renderobject.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
class FontResource;
|
||||
class ResourceManager;
|
||||
|
||||
class Text : public RenderObject {
|
||||
friend class RenderObject;
|
||||
|
||||
public:
|
||||
/**
|
||||
@brief Setzt den Font mit dem der Text dargestellt werden soll.
|
||||
@param Font der Dateiname der Fontdatei.
|
||||
@return Gibt false zurück, wenn der Font nicht gefunden wurde.
|
||||
*/
|
||||
bool setFont(const Common::String &font);
|
||||
|
||||
/**
|
||||
@brief Setzt den darzustellenden Text.
|
||||
@param Text der darzustellende Text
|
||||
*/
|
||||
void setText(const Common::String &text);
|
||||
|
||||
/**
|
||||
@brief Setzt den Alphawert des Textes.
|
||||
@param Alpha der neue Alphawert des Textes (0 = keine Deckung, 255 = volle Deckung).
|
||||
*/
|
||||
void setAlpha(int alpha);
|
||||
|
||||
/**
|
||||
@brief Legt fest, ob der Text automatisch umgebrochen werden soll.
|
||||
|
||||
Wenn dieses Attribut auf true gesetzt ist, wird der Text umgebrochen, sofern er länger als GetAutoWrapThreshold() ist.
|
||||
|
||||
@param AutoWrap gibt an, ob der automatische Umbruch aktiviert oder deaktiviert werden soll.
|
||||
@remark Dieses Attribut wird mit dem Wert false initialisiert.
|
||||
*/
|
||||
void setAutoWrap(bool autoWrap);
|
||||
|
||||
/**
|
||||
@brief Legt die Längengrenze des Textes in Pixeln fest, ab der ein automatischer Zeilenumbruch vorgenommen wird.
|
||||
@remark Dieses Attribut wird mit dem Wert 300 initialisiert.
|
||||
@remark Eine automatische Formatierung wird nur vorgenommen, wenn diese durch einen Aufruf von SetAutoWrap() aktiviert wurde.
|
||||
*/
|
||||
void setAutoWrapThreshold(uint32 autoWrapThreshold);
|
||||
|
||||
/**
|
||||
@brief Gibt den dargestellten Text zurück.
|
||||
*/
|
||||
const Common::String &getText() {
|
||||
return _text;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt den Namen das momentan benutzten Fonts zurück.
|
||||
*/
|
||||
const Common::String &getFont() {
|
||||
return _font;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Setzt die Farbe des Textes.
|
||||
@param Color eine 24-Bit RGB Farbe, die die Farbe des Textes festlegt.
|
||||
*/
|
||||
void setColor(uint32 modulationColor);
|
||||
|
||||
/**
|
||||
@brief Gibt den Alphawert des Textes zurück.
|
||||
@return Der Alphawert des Textes (0 = keine Deckung, 255 = volle Deckung).
|
||||
*/
|
||||
int getAlpha() const {
|
||||
return _modulationColor >> BS_ASHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt die Farbe des Textes zurück.
|
||||
@return Eine 24-Bit RGB Farbe, die die Farbe des Textes angibt.
|
||||
*/
|
||||
int getColor() const {
|
||||
return _modulationColor & BS_RGBMASK;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt zurück, ob die automatische Formatierung aktiviert ist.
|
||||
*/
|
||||
bool isAutoWrapActive() const {
|
||||
return _autoWrap;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Gibt die Längengrenze des Textes in Pixeln zurück, ab der eine automatische Formatierung vorgenommen wird.
|
||||
*/
|
||||
uint32 getAutoWrapThreshold() const {
|
||||
return _autoWrapThreshold;
|
||||
}
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
protected:
|
||||
bool doRender(RectangleList *updateRects) override;
|
||||
|
||||
private:
|
||||
Text(RenderObjectPtr<RenderObject> parentPtr);
|
||||
Text(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
|
||||
|
||||
uint32 _modulationColor;
|
||||
Common::String _font;
|
||||
Common::String _text;
|
||||
bool _autoWrap;
|
||||
uint32 _autoWrapThreshold;
|
||||
|
||||
struct Line {
|
||||
Common::Rect bbox;
|
||||
Common::String text;
|
||||
};
|
||||
|
||||
Common::Array<Line> _lines;
|
||||
|
||||
void updateFormat();
|
||||
void updateMetrics(FontResource &fontResource);
|
||||
ResourceManager *getResourceManager();
|
||||
FontResource *lockFontResource();
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
48
engines/sword25/gfx/timedrenderobject.cpp
Normal file
48
engines/sword25/gfx/timedrenderobject.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/gfx/timedrenderobject.h"
|
||||
|
||||
#include "sword25/gfx/renderobjectmanager.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
TimedRenderObject::TimedRenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle) :
|
||||
RenderObject(pParent, type, handle) {
|
||||
assert(getManager());
|
||||
getManager()->attatchTimedRenderObject(this->getHandle());
|
||||
}
|
||||
|
||||
TimedRenderObject::~TimedRenderObject() {
|
||||
assert(getManager());
|
||||
getManager()->detatchTimedRenderObject(this->getHandle());
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
55
engines/sword25/gfx/timedrenderobject.h
Normal file
55
engines/sword25/gfx/timedrenderobject.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/gfx/renderobject.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/**
|
||||
@brief
|
||||
*/
|
||||
|
||||
class TimedRenderObject : public RenderObject {
|
||||
public:
|
||||
TimedRenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle = 0);
|
||||
~TimedRenderObject() override;
|
||||
|
||||
/**
|
||||
@brief Teilt dem Objekt mit, dass ein neuer Frame begonnen wird.
|
||||
|
||||
Diese Methode wird jeden Frame an jedem BS_TimedRenderObject aufgerufen um diesen zu ermöglichen
|
||||
ihren Zustand Zeitabhängig zu verändern (z.B. Animationen).<br>
|
||||
@param int TimeElapsed gibt an wie viel Zeit (in Microsekunden) seit dem letzten Frame vergangen ist.
|
||||
*/
|
||||
virtual void frameNotification(int timeElapsed) = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
283
engines/sword25/input/inputengine.cpp
Normal file
283
engines/sword25/input/inputengine.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/events.h"
|
||||
#include "common/system.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "sword25/sword25.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/input/inputengine.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
#define DOUBLE_CLICK_TIME 500
|
||||
#define DOUBLE_CLICK_RECT_SIZE 4
|
||||
|
||||
InputEngine::InputEngine(Kernel *pKernel) :
|
||||
Service(pKernel),
|
||||
_currentState(0),
|
||||
_leftMouseDown(false),
|
||||
_rightMouseDown(false),
|
||||
_mouseX(0),
|
||||
_mouseY(0),
|
||||
_leftDoubleClick(false),
|
||||
_doubleClickTime(DOUBLE_CLICK_TIME),
|
||||
_doubleClickRectWidth(DOUBLE_CLICK_RECT_SIZE),
|
||||
_doubleClickRectHeight(DOUBLE_CLICK_RECT_SIZE),
|
||||
_lastLeftClickTime(0),
|
||||
_lastLeftClickMouseX(0),
|
||||
_lastLeftClickMouseY(0) {
|
||||
memset(_keyboardState[0], 0, sizeof(_keyboardState[0]));
|
||||
memset(_keyboardState[1], 0, sizeof(_keyboardState[1]));
|
||||
_leftMouseState[0] = false;
|
||||
_leftMouseState[1] = false;
|
||||
_rightMouseState[0] = false;
|
||||
_rightMouseState[1] = false;
|
||||
|
||||
if (!registerScriptBindings())
|
||||
error("Script bindings could not be registered.");
|
||||
else
|
||||
debugC(kDebugScript, "Script bindings registered.");
|
||||
}
|
||||
|
||||
InputEngine::~InputEngine() {
|
||||
unregisterScriptBindings();
|
||||
}
|
||||
|
||||
bool InputEngine::init() {
|
||||
// No initialisation needed
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputEngine::update() {
|
||||
Common::Event event;
|
||||
|
||||
// We keep two sets of keyboard states: The current one, and that of
|
||||
// the previous frame. This allows us to detect which keys changed
|
||||
// state. Also, by keeping a single central keystate array, we
|
||||
// ensure that all script queries for key state during a single
|
||||
// frame get the same consistent replies.
|
||||
_currentState ^= 1;
|
||||
memcpy(_keyboardState[_currentState], _keyboardState[_currentState ^ 1], sizeof(_keyboardState[0]));
|
||||
|
||||
// Loop through processing any pending events
|
||||
bool handleEvents = true;
|
||||
while (handleEvents && g_system->getEventManager()->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
_leftMouseDown = event.type == Common::EVENT_LBUTTONDOWN;
|
||||
_mouseX = event.mouse.x;
|
||||
_mouseY = event.mouse.y;
|
||||
handleEvents = false;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
_rightMouseDown = event.type == Common::EVENT_RBUTTONDOWN;
|
||||
_mouseX = event.mouse.x;
|
||||
_mouseY = event.mouse.y;
|
||||
handleEvents = false;
|
||||
break;
|
||||
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
_mouseX = event.mouse.x;
|
||||
_mouseY = event.mouse.y;
|
||||
break;
|
||||
|
||||
case Common::EVENT_KEYDOWN:
|
||||
if (event.kbd.ascii != 0) {
|
||||
reportCharacter(event.kbd.ascii);
|
||||
}
|
||||
// fall through
|
||||
case Common::EVENT_KEYUP:
|
||||
alterKeyboardState(event.kbd.keycode, (event.type == Common::EVENT_KEYDOWN) ? 0x80 : 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_leftMouseState[_currentState] = _leftMouseDown;
|
||||
_rightMouseState[_currentState] = _rightMouseDown;
|
||||
|
||||
testForLeftDoubleClick();
|
||||
}
|
||||
|
||||
bool InputEngine::isLeftMouseDown() {
|
||||
return _leftMouseDown;
|
||||
}
|
||||
|
||||
bool InputEngine::isRightMouseDown() {
|
||||
return _rightMouseDown;
|
||||
}
|
||||
|
||||
void InputEngine::testForLeftDoubleClick() {
|
||||
_leftDoubleClick = false;
|
||||
|
||||
// Only bother checking for a double click if the left mouse button was clicked
|
||||
if (wasLeftMouseDown()) {
|
||||
// Get the time now
|
||||
uint now = Kernel::getInstance()->getMilliTicks();
|
||||
|
||||
// A double click is signalled if
|
||||
// 1. The two clicks are close enough together
|
||||
// 2. The mouse cursor hasn't moved much
|
||||
if (now - _lastLeftClickTime <= _doubleClickTime &&
|
||||
ABS(_mouseX - _lastLeftClickMouseX) <= _doubleClickRectWidth / 2 &&
|
||||
ABS(_mouseY - _lastLeftClickMouseY) <= _doubleClickRectHeight / 2) {
|
||||
_leftDoubleClick = true;
|
||||
|
||||
// Reset the time and position of the last click, so that clicking is not
|
||||
// interpreted as the first click of a further double-click
|
||||
_lastLeftClickTime = 0;
|
||||
_lastLeftClickMouseX = 0;
|
||||
_lastLeftClickMouseY = 0;
|
||||
} else {
|
||||
// There is no double click. Remember the position and time of the click,
|
||||
// in case it's the first click of a double-click sequence
|
||||
_lastLeftClickTime = now;
|
||||
_lastLeftClickMouseX = _mouseX;
|
||||
_lastLeftClickMouseY = _mouseY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEngine::alterKeyboardState(int keycode, byte newState) {
|
||||
assert(keycode < ARRAYSIZE(_keyboardState[_currentState]));
|
||||
_keyboardState[_currentState][keycode] = newState;
|
||||
}
|
||||
|
||||
bool InputEngine::isLeftDoubleClick() {
|
||||
return _leftDoubleClick;
|
||||
}
|
||||
|
||||
bool InputEngine::wasLeftMouseDown() {
|
||||
return (_leftMouseState[_currentState] == false) && (_leftMouseState[_currentState ^ 1] == true);
|
||||
}
|
||||
|
||||
bool InputEngine::wasRightMouseDown() {
|
||||
return (_rightMouseState[_currentState] == false) && (_rightMouseState[_currentState ^ 1] == true);
|
||||
}
|
||||
|
||||
int InputEngine::getMouseX() {
|
||||
return _mouseX;
|
||||
}
|
||||
|
||||
int InputEngine::getMouseY() {
|
||||
return _mouseY;
|
||||
}
|
||||
|
||||
bool InputEngine::isKeyDown(uint keyCode) {
|
||||
assert(keyCode < ARRAYSIZE(_keyboardState[_currentState]));
|
||||
return (_keyboardState[_currentState][keyCode] & 0x80) != 0;
|
||||
}
|
||||
|
||||
bool InputEngine::wasKeyDown(uint keyCode) {
|
||||
assert(keyCode < ARRAYSIZE(_keyboardState[_currentState]));
|
||||
return ((_keyboardState[_currentState][keyCode] & 0x80) == 0) &&
|
||||
((_keyboardState[_currentState ^ 1][keyCode] & 0x80) != 0);
|
||||
}
|
||||
|
||||
void InputEngine::setMouseX(int posX) {
|
||||
_mouseX = posX;
|
||||
g_system->warpMouse(_mouseX, _mouseY);
|
||||
}
|
||||
|
||||
void InputEngine::setMouseY(int posY) {
|
||||
_mouseY = posY;
|
||||
g_system->warpMouse(_mouseX, _mouseY);
|
||||
}
|
||||
|
||||
void InputEngine::setCharacterCallback(CharacterCallback callback) {
|
||||
_characterCallback = callback;
|
||||
}
|
||||
|
||||
void InputEngine::setCommandCallback(CommandCallback callback) {
|
||||
_commandCallback = callback;
|
||||
}
|
||||
|
||||
void InputEngine::reportCharacter(byte character) {
|
||||
if (_characterCallback)
|
||||
(*_characterCallback)(character);
|
||||
}
|
||||
|
||||
void InputEngine::reportCommand(KEY_COMMANDS command) {
|
||||
if (_commandCallback)
|
||||
(*_commandCallback)(command);
|
||||
}
|
||||
|
||||
bool InputEngine::persist(OutputPersistenceBlock &writer) {
|
||||
// Write out the number of command callbacks and their names.
|
||||
// Note: We do this only for compatibility with older engines resp.
|
||||
// the original engine.
|
||||
writer.write((uint32)1);
|
||||
writer.writeString("LuaCommandCB");
|
||||
|
||||
// Write out the number of command callbacks and their names.
|
||||
// Note: We do this only for compatibility with older engines resp.
|
||||
// the original engine.
|
||||
writer.write((uint32)1);
|
||||
writer.writeString("LuaCharacterCB");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputEngine::unpersist(InputPersistenceBlock &reader) {
|
||||
Common::String callbackFunctionName;
|
||||
|
||||
// Read number of command callbacks and their names.
|
||||
// Note: We do this only for compatibility with older engines resp.
|
||||
// the original engine.
|
||||
uint32 commandCallbackCount;
|
||||
reader.read(commandCallbackCount);
|
||||
assert(commandCallbackCount == 1);
|
||||
|
||||
reader.readString(callbackFunctionName);
|
||||
assert(callbackFunctionName == "LuaCommandCB");
|
||||
|
||||
// Read number of character callbacks and their names.
|
||||
// Note: We do this only for compatibility with older engines resp.
|
||||
// the original engine.
|
||||
uint32 characterCallbackCount;
|
||||
reader.read(characterCallbackCount);
|
||||
assert(characterCallbackCount == 1);
|
||||
|
||||
reader.readString(callbackFunctionName);
|
||||
assert(callbackFunctionName == "LuaCharacterCB");
|
||||
|
||||
return reader.isGood();
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
318
engines/sword25/input/inputengine.h
Normal file
318
engines/sword25/input/inputengine.h
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* BS_InputEngine
|
||||
* -------------
|
||||
* This is the input interface engine that contains all the methods that an
|
||||
* input source must implement.
|
||||
* All input engines must be derived from this class.
|
||||
*
|
||||
* Autor: Alex Arnst
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_INPUTENGINE_H
|
||||
#define SWORD25_INPUTENGINE_H
|
||||
|
||||
#include "common/keyboard.h"
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/service.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/// Class definitions
|
||||
|
||||
class InputEngine : public Service, public Persistable {
|
||||
public:
|
||||
InputEngine(Kernel *pKernel);
|
||||
~InputEngine() override;
|
||||
|
||||
// NOTE: These codes are registered in inputengine_script.cpp
|
||||
// If you add or remove entries of this enum, you must also adjust
|
||||
// the above file.
|
||||
enum KEY_CODES {
|
||||
KEY_BACKSPACE = Common::KEYCODE_BACKSPACE,
|
||||
KEY_TAB = Common::KEYCODE_TAB,
|
||||
KEY_CLEAR = Common::KEYCODE_CLEAR,
|
||||
KEY_RETURN = Common::KEYCODE_RETURN,
|
||||
KEY_PAUSE = Common::KEYCODE_PAUSE,
|
||||
KEY_CAPSLOCK = Common::KEYCODE_CAPSLOCK,
|
||||
KEY_ESCAPE = Common::KEYCODE_ESCAPE,
|
||||
KEY_SPACE = Common::KEYCODE_SPACE,
|
||||
KEY_PAGEUP = Common::KEYCODE_PAGEUP,
|
||||
KEY_PAGEDOWN = Common::KEYCODE_PAGEDOWN,
|
||||
KEY_END = Common::KEYCODE_END,
|
||||
KEY_HOME = Common::KEYCODE_HOME,
|
||||
KEY_LEFT = Common::KEYCODE_LEFT,
|
||||
KEY_UP = Common::KEYCODE_UP,
|
||||
KEY_RIGHT = Common::KEYCODE_RIGHT,
|
||||
KEY_DOWN = Common::KEYCODE_DOWN,
|
||||
KEY_PRINTSCREEN = Common::KEYCODE_PRINT,
|
||||
KEY_INSERT = Common::KEYCODE_INSERT,
|
||||
KEY_DELETE = Common::KEYCODE_DELETE,
|
||||
KEY_0 = Common::KEYCODE_0,
|
||||
KEY_1 = Common::KEYCODE_1,
|
||||
KEY_2 = Common::KEYCODE_2,
|
||||
KEY_3 = Common::KEYCODE_3,
|
||||
KEY_4 = Common::KEYCODE_4,
|
||||
KEY_5 = Common::KEYCODE_5,
|
||||
KEY_6 = Common::KEYCODE_6,
|
||||
KEY_7 = Common::KEYCODE_7,
|
||||
KEY_8 = Common::KEYCODE_8,
|
||||
KEY_9 = Common::KEYCODE_9,
|
||||
KEY_A = Common::KEYCODE_a,
|
||||
KEY_B = Common::KEYCODE_b,
|
||||
KEY_C = Common::KEYCODE_c,
|
||||
KEY_D = Common::KEYCODE_d,
|
||||
KEY_E = Common::KEYCODE_e,
|
||||
KEY_F = Common::KEYCODE_f,
|
||||
KEY_G = Common::KEYCODE_g,
|
||||
KEY_H = Common::KEYCODE_h,
|
||||
KEY_I = Common::KEYCODE_i,
|
||||
KEY_J = Common::KEYCODE_j,
|
||||
KEY_K = Common::KEYCODE_k,
|
||||
KEY_L = Common::KEYCODE_l,
|
||||
KEY_M = Common::KEYCODE_m,
|
||||
KEY_N = Common::KEYCODE_n,
|
||||
KEY_O = Common::KEYCODE_o,
|
||||
KEY_P = Common::KEYCODE_p,
|
||||
KEY_Q = Common::KEYCODE_q,
|
||||
KEY_R = Common::KEYCODE_r,
|
||||
KEY_S = Common::KEYCODE_s,
|
||||
KEY_T = Common::KEYCODE_t,
|
||||
KEY_U = Common::KEYCODE_u,
|
||||
KEY_V = Common::KEYCODE_v,
|
||||
KEY_W = Common::KEYCODE_w,
|
||||
KEY_X = Common::KEYCODE_x,
|
||||
KEY_Y = Common::KEYCODE_y,
|
||||
KEY_Z = Common::KEYCODE_z,
|
||||
KEY_NUMPAD0 = Common::KEYCODE_KP0,
|
||||
KEY_NUMPAD1 = Common::KEYCODE_KP1,
|
||||
KEY_NUMPAD2 = Common::KEYCODE_KP2,
|
||||
KEY_NUMPAD3 = Common::KEYCODE_KP3,
|
||||
KEY_NUMPAD4 = Common::KEYCODE_KP4,
|
||||
KEY_NUMPAD5 = Common::KEYCODE_KP5,
|
||||
KEY_NUMPAD6 = Common::KEYCODE_KP6,
|
||||
KEY_NUMPAD7 = Common::KEYCODE_KP7,
|
||||
KEY_NUMPAD8 = Common::KEYCODE_KP8,
|
||||
KEY_NUMPAD9 = Common::KEYCODE_KP9,
|
||||
KEY_MULTIPLY = Common::KEYCODE_KP_MULTIPLY,
|
||||
KEY_ADD = Common::KEYCODE_KP_PLUS,
|
||||
KEY_SEPARATOR = Common::KEYCODE_EQUALS, // FIXME: This mapping is just a wild guess!!
|
||||
KEY_SUBTRACT = Common::KEYCODE_KP_MINUS,
|
||||
KEY_DECIMAL = Common::KEYCODE_KP_PERIOD,
|
||||
KEY_DIVIDE = Common::KEYCODE_KP_DIVIDE,
|
||||
KEY_F1 = Common::KEYCODE_F1,
|
||||
KEY_F2 = Common::KEYCODE_F2,
|
||||
KEY_F3 = Common::KEYCODE_F3,
|
||||
KEY_F4 = Common::KEYCODE_F4,
|
||||
KEY_F5 = Common::KEYCODE_F5,
|
||||
KEY_F6 = Common::KEYCODE_F6,
|
||||
KEY_F7 = Common::KEYCODE_F7,
|
||||
KEY_F8 = Common::KEYCODE_F8,
|
||||
KEY_F9 = Common::KEYCODE_F9,
|
||||
KEY_F10 = Common::KEYCODE_F10,
|
||||
KEY_F11 = Common::KEYCODE_F11,
|
||||
KEY_F12 = Common::KEYCODE_F12,
|
||||
KEY_NUMLOCK = Common::KEYCODE_NUMLOCK,
|
||||
KEY_SCROLL = Common::KEYCODE_SCROLLOCK,
|
||||
KEY_LSHIFT = Common::KEYCODE_LSHIFT,
|
||||
KEY_RSHIFT = Common::KEYCODE_RSHIFT,
|
||||
KEY_LCONTROL = Common::KEYCODE_LCTRL,
|
||||
KEY_RCONTROL = Common::KEYCODE_RCTRL
|
||||
};
|
||||
|
||||
// NOTE: These codes are registered in inputengine_script.cpp
|
||||
// If you add or remove entries of this enum, you must also adjust
|
||||
// the above file.
|
||||
enum KEY_COMMANDS {
|
||||
KEY_COMMAND_ENTER = 1,
|
||||
KEY_COMMAND_LEFT = 2,
|
||||
KEY_COMMAND_RIGHT = 3,
|
||||
KEY_COMMAND_HOME = 4,
|
||||
KEY_COMMAND_END = 5,
|
||||
KEY_COMMAND_BACKSPACE = 6,
|
||||
KEY_COMMAND_TAB = 7,
|
||||
KEY_COMMAND_INSERT = 8,
|
||||
KEY_COMMAND_DELETE = 9
|
||||
};
|
||||
|
||||
/// --------------------------------------------------------------
|
||||
/// THESE METHODS MUST BE IMPLEMENTED BY THE INPUT ENGINE
|
||||
/// --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Initializes the input engine
|
||||
* @return Returns a true on success, otherwise false.
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* Performs a "tick" of the input engine.
|
||||
*
|
||||
* This method should be called once per frame. It can be used by implementations
|
||||
* of the input engine that are not running in their own thread, or to perform
|
||||
* additional administrative tasks that are needed.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Returns true if the left mouse button is pressed
|
||||
*/
|
||||
bool isLeftMouseDown();
|
||||
|
||||
/**
|
||||
* Returns true if the right mouse button is pressed.
|
||||
*/
|
||||
bool isRightMouseDown();
|
||||
|
||||
/**
|
||||
* Returns true if the left mouse button was pressed and released.
|
||||
*
|
||||
* The difference between this and IsLeftMouseDown() is that this only returns
|
||||
* true when the left mouse button is released.
|
||||
*/
|
||||
bool wasLeftMouseDown();
|
||||
|
||||
/**
|
||||
* Returns true if the right mouse button was pressed and released.
|
||||
*
|
||||
* The difference between this and IsRightMouseDown() is that this only returns
|
||||
* true when the right mouse button is released.
|
||||
*/
|
||||
bool wasRightMouseDown();
|
||||
|
||||
/**
|
||||
* Returns true if the left mouse button double click was done
|
||||
*/
|
||||
bool isLeftDoubleClick();
|
||||
|
||||
/**
|
||||
* Returns the X position of the cursor in pixels
|
||||
*/
|
||||
int getMouseX();
|
||||
|
||||
/**
|
||||
* Returns the Y position of the cursor in pixels
|
||||
*/
|
||||
int getMouseY();
|
||||
|
||||
/**
|
||||
* Sets the X position of the cursor in pixels
|
||||
*/
|
||||
void setMouseX(int posX);
|
||||
|
||||
/**
|
||||
* Sets the Y position of the cursor in pixels
|
||||
*/
|
||||
void setMouseY(int posY);
|
||||
|
||||
/**
|
||||
* Returns true if a given key was pressed
|
||||
* @param KeyCode The key code to be checked
|
||||
* @return Returns true if the given key is done, otherwise false.
|
||||
*/
|
||||
bool isKeyDown(uint keyCode);
|
||||
|
||||
/**
|
||||
* Returns true if a certain key was pushed and released.
|
||||
*
|
||||
* The difference between IsKeyDown() is that this only returns true after the key
|
||||
* has been released. This method facilitates the retrieval of keys, and reading
|
||||
* strings that users type.
|
||||
* @param KeyCode The key code to be checked
|
||||
*/
|
||||
bool wasKeyDown(uint keyCode);
|
||||
|
||||
typedef void (*CharacterCallback)(int command);
|
||||
|
||||
/**
|
||||
* Registers a callback function for keyboard input.
|
||||
*
|
||||
* The callback that is registered with this function will be called whenever an
|
||||
* input key is pressed. A letter entry is different from the query using the
|
||||
* methods isKeyDown() and wasKeyDown() in the sense that are treated instead
|
||||
* of actual scan-coded letters. These were taken into account, among other things:
|
||||
* the keyboard layout, the condition the Shift and Caps Lock keys and the repetition
|
||||
* of longer holding the key.
|
||||
* The input of strings by the user through use of callbacks should be implemented.
|
||||
*/
|
||||
void setCharacterCallback(CharacterCallback callback);
|
||||
|
||||
typedef void (*CommandCallback)(int command);
|
||||
|
||||
/**
|
||||
* Registers a callback function for the input of commands that can have influence on the string input
|
||||
*
|
||||
* The callback that is registered with this function will be called whenever the input service
|
||||
* has a key that affects the character string input. This could be the following keys:
|
||||
* Enter, End, Left, Right, ...
|
||||
* The input of strings by the user through the use of callbacks should be implemented.
|
||||
*/
|
||||
void setCommandCallback(CommandCallback callback);
|
||||
|
||||
void reportCharacter(byte character);
|
||||
void reportCommand(KEY_COMMANDS command);
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
private:
|
||||
bool registerScriptBindings();
|
||||
void unregisterScriptBindings();
|
||||
|
||||
private:
|
||||
void testForLeftDoubleClick();
|
||||
void alterKeyboardState(int keycode, byte newState);
|
||||
|
||||
byte _keyboardState[2][512];
|
||||
bool _leftMouseState[2];
|
||||
bool _rightMouseState[2];
|
||||
uint _currentState;
|
||||
int _mouseX;
|
||||
int _mouseY;
|
||||
bool _leftMouseDown;
|
||||
bool _rightMouseDown;
|
||||
bool _leftDoubleClick;
|
||||
uint _doubleClickTime;
|
||||
int _doubleClickRectWidth;
|
||||
int _doubleClickRectHeight;
|
||||
uint _lastLeftClickTime;
|
||||
int _lastLeftClickMouseX;
|
||||
int _lastLeftClickMouseY;
|
||||
CommandCallback _commandCallback;
|
||||
CharacterCallback _characterCallback;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
291
engines/sword25/input/inputengine_script.cpp
Normal file
291
engines/sword25/input/inputengine_script.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/str.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/script/script.h"
|
||||
#include "sword25/script/luabindhelper.h"
|
||||
#include "sword25/script/luacallback.h"
|
||||
|
||||
#include "sword25/input/inputengine.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
static void theCharacterCallback(int character);
|
||||
static void theCommandCallback(int command);
|
||||
|
||||
namespace {
|
||||
class CharacterCallbackClass : public LuaCallback {
|
||||
public:
|
||||
CharacterCallbackClass(lua_State *L) : LuaCallback(L) {}
|
||||
|
||||
Common::String _character;
|
||||
|
||||
protected:
|
||||
int preFunctionInvocation(lua_State *L) override {
|
||||
lua_pushstring(L, _character.c_str());
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
static CharacterCallbackClass *characterCallbackPtr = 0; // FIXME: should be turned into InputEngine member var
|
||||
|
||||
class CommandCallbackClass : public LuaCallback {
|
||||
public:
|
||||
CommandCallbackClass(lua_State *L) : LuaCallback(L) {
|
||||
_command = InputEngine::KEY_COMMAND_BACKSPACE;
|
||||
}
|
||||
|
||||
InputEngine::KEY_COMMANDS _command;
|
||||
|
||||
protected:
|
||||
int preFunctionInvocation(lua_State *L) override {
|
||||
lua_pushnumber(L, _command);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
static CommandCallbackClass *commandCallbackPtr = 0; // FIXME: should be turned into InputEngine member var
|
||||
|
||||
}
|
||||
|
||||
static InputEngine *getIE() {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
InputEngine *pIE = pKernel->getInput();
|
||||
assert(pIE);
|
||||
return pIE;
|
||||
}
|
||||
|
||||
static int init(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushbooleancpp(L, pIE->init());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int update(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
pIE->update();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isLeftMouseDown(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushbooleancpp(L, pIE->isLeftMouseDown());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isRightMouseDown(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushbooleancpp(L, pIE->isRightMouseDown());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wasLeftMouseDown(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushbooleancpp(L, pIE->wasLeftMouseDown());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wasRightMouseDown(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushbooleancpp(L, pIE->wasRightMouseDown());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isLeftDoubleClick(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushbooleancpp(L, pIE->isLeftDoubleClick());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getMouseX(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushnumber(L, pIE->getMouseX());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getMouseY(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushnumber(L, pIE->getMouseY());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isKeyDown(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushbooleancpp(L, pIE->isKeyDown((uint)luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wasKeyDown(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
lua_pushbooleancpp(L, pIE->wasKeyDown((uint)luaL_checknumber(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setMouseX(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
pIE->setMouseX((int)luaL_checknumber(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setMouseY(lua_State *L) {
|
||||
InputEngine *pIE = getIE();
|
||||
|
||||
pIE->setMouseY((int)luaL_checknumber(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void theCharacterCallback(int character) {
|
||||
characterCallbackPtr->_character = static_cast<byte>(character);
|
||||
lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
|
||||
characterCallbackPtr->invokeCallbackFunctions(L, 1);
|
||||
}
|
||||
|
||||
static int registerCharacterCallback(lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
characterCallbackPtr->registerCallbackFunction(L, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unregisterCharacterCallback(lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
characterCallbackPtr->unregisterCallbackFunction(L, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void theCommandCallback(int command) {
|
||||
commandCallbackPtr->_command = static_cast<InputEngine::KEY_COMMANDS>(command);
|
||||
lua_State *L = static_cast<lua_State *>(Kernel::getInstance()->getScript()->getScriptObject());
|
||||
commandCallbackPtr->invokeCallbackFunctions(L, 1);
|
||||
}
|
||||
|
||||
static int registerCommandCallback(lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
commandCallbackPtr->registerCallbackFunction(L, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unregisterCommandCallback(lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
commandCallbackPtr->unregisterCallbackFunction(L, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *PACKAGE_LIBRARY_NAME = "Input";
|
||||
|
||||
static const luaL_reg PACKAGE_FUNCTIONS[] = {
|
||||
{"Init", init},
|
||||
{"Update", update},
|
||||
{"IsLeftMouseDown", isLeftMouseDown},
|
||||
{"IsRightMouseDown", isRightMouseDown},
|
||||
{"WasLeftMouseDown", wasLeftMouseDown},
|
||||
{"WasRightMouseDown", wasRightMouseDown},
|
||||
{"IsLeftDoubleClick", isLeftDoubleClick},
|
||||
{"GetMouseX", getMouseX},
|
||||
{"GetMouseY", getMouseY},
|
||||
{"SetMouseX", setMouseX},
|
||||
{"SetMouseY", setMouseY},
|
||||
{"IsKeyDown", isKeyDown},
|
||||
{"WasKeyDown", wasKeyDown},
|
||||
{"RegisterCharacterCallback", registerCharacterCallback},
|
||||
{"UnregisterCharacterCallback", unregisterCharacterCallback},
|
||||
{"RegisterCommandCallback", registerCommandCallback},
|
||||
{"UnregisterCommandCallback", unregisterCommandCallback},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
#define X(k) {"KEY_" #k, InputEngine::KEY_##k}
|
||||
#define Y(k) {"KEY_COMMAND_" #k, InputEngine::KEY_COMMAND_##k}
|
||||
static const lua_constant_reg PACKAGE_CONSTANTS[] = {
|
||||
X(BACKSPACE), X(TAB), X(CLEAR), X(RETURN), X(PAUSE), X(CAPSLOCK), X(ESCAPE), X(SPACE), X(PAGEUP), X(PAGEDOWN), X(END), X(HOME), X(LEFT),
|
||||
X(UP), X(RIGHT), X(DOWN), X(PRINTSCREEN), X(INSERT), X(DELETE), X(0), X(1), X(2), X(3), X(4), X(5), X(6), X(7), X(8), X(9), X(A), X(B),
|
||||
X(C), X(D), X(E), X(F), X(G), X(H), X(I), X(J), X(K), X(L), X(M), X(N), X(O), X(P), X(Q), X(R), X(S), X(T), X(U), X(V), X(W), X(X), X(Y),
|
||||
X(Z), X(NUMPAD0), X(NUMPAD1), X(NUMPAD2), X(NUMPAD3), X(NUMPAD4), X(NUMPAD5), X(NUMPAD6), X(NUMPAD7), X(NUMPAD8), X(NUMPAD9), X(MULTIPLY),
|
||||
X(ADD), X(SEPARATOR), X(SUBTRACT), X(DECIMAL), X(DIVIDE), X(F1), X(F2), X(F3), X(F4), X(F5), X(F6), X(F7), X(F8), X(F9), X(F10), X(F11),
|
||||
X(F12), X(NUMLOCK), X(SCROLL), X(LSHIFT), X(RSHIFT), X(LCONTROL), X(RCONTROL),
|
||||
Y(ENTER), Y(LEFT), Y(RIGHT), Y(HOME), Y(END), Y(BACKSPACE), Y(TAB), Y(INSERT), Y(DELETE),
|
||||
{0, 0}
|
||||
};
|
||||
#undef X
|
||||
#undef Y
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool InputEngine::registerScriptBindings() {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ScriptEngine *pScript = pKernel->getScript();
|
||||
assert(pScript);
|
||||
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
|
||||
assert(L);
|
||||
|
||||
if (!LuaBindhelper::addFunctionsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_FUNCTIONS)) return false;
|
||||
if (!LuaBindhelper::addConstantsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_CONSTANTS)) return false;
|
||||
|
||||
assert(characterCallbackPtr == 0);
|
||||
characterCallbackPtr = new CharacterCallbackClass(L);
|
||||
|
||||
assert(commandCallbackPtr == 0);
|
||||
commandCallbackPtr = new CommandCallbackClass(L);
|
||||
|
||||
setCharacterCallback(theCharacterCallback);
|
||||
setCommandCallback(theCommandCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputEngine::unregisterScriptBindings() {
|
||||
delete characterCallbackPtr;
|
||||
characterCallbackPtr = 0;
|
||||
|
||||
delete commandCallbackPtr;
|
||||
commandCallbackPtr = 0;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
53
engines/sword25/kernel/common.h
Normal file
53
engines/sword25/kernel/common.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* common.h
|
||||
* -----------
|
||||
* This file contains functions or macros that are used across the entire project.
|
||||
* It is therefore extremely important that this header file be referenced in all
|
||||
* the other header files in the project.
|
||||
*
|
||||
* Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_COMMON_H
|
||||
#define SWORD25_COMMON_H
|
||||
|
||||
// Global constants
|
||||
#define DEBUG
|
||||
|
||||
// Includes
|
||||
#include "common/array.h"
|
||||
#include "common/file.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#endif
|
||||
83
engines/sword25/kernel/filesystemutil.cpp
Normal file
83
engines/sword25/kernel/filesystemutil.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "sword25/kernel/filesystemutil.h"
|
||||
#include "sword25/kernel/persistenceservice.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Common::Path FileSystemUtil::getUserdataDirectoryPath() {
|
||||
// FIXME: This code is a hack which bypasses the savefile API,
|
||||
// and should eventually be removed.
|
||||
Common::Path path = ConfMan.getPath("savepath");
|
||||
|
||||
if (path.empty()) {
|
||||
error("No save path has been defined");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
Common::String FileSystemUtil::getUserdataDirectory() {
|
||||
// Return the path using / separator
|
||||
return getUserdataDirectoryPath().toString('/');
|
||||
}
|
||||
|
||||
Common::String FileSystemUtil::getPathSeparator() {
|
||||
// FIXME: This code is a hack which bypasses the savefile API,
|
||||
// and should eventually be removed.
|
||||
return Common::String("/");
|
||||
}
|
||||
|
||||
bool FileSystemUtil::fileExists(const Common::String &filename) {
|
||||
Common::File f;
|
||||
if (f.exists(Common::Path(filename)))
|
||||
return true;
|
||||
|
||||
// Check if the file exists in the save folder
|
||||
Common::FSNode folder(PersistenceService::getSavegameDirectory());
|
||||
Common::FSNode fileNode = folder.getChild(getPathFilename(filename));
|
||||
return fileNode.exists();
|
||||
}
|
||||
|
||||
Common::String FileSystemUtil::getPathFilename(const Common::String &path) {
|
||||
for (int i = path.size() - 1; i >= 0; --i) {
|
||||
if ((path[i] == '/') || (path[i] == '\\')) {
|
||||
return Common::String(&path.c_str()[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
98
engines/sword25/kernel/filesystemutil.h
Normal file
98
engines/sword25/kernel/filesystemutil.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* The class BS_FileSystemUtil represents a wrapper for file system specific
|
||||
* operations that do not have equivalents in the C/C++ libraries.
|
||||
*
|
||||
* Each supported platform must implement this interface, and the method
|
||||
* BS_FileSystemUtil Singleton::instance()
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_FILESYSTEMUTIL_H
|
||||
#define SWORD25_FILESYSTEMUTIL_H
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Includes
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/str.h"
|
||||
#include "common/str-array.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Class definitions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class FileSystemUtil {
|
||||
public:
|
||||
|
||||
/**
|
||||
* This function returns the path of the directory in which all user data is to be stored.
|
||||
*
|
||||
* These are for example Screenshots, game saves, configuration files, log files, ...
|
||||
* @return Returns the name of the directory for user data.
|
||||
*/
|
||||
static Common::Path getUserdataDirectoryPath();
|
||||
|
||||
/**
|
||||
* This function returns the name of the directory in which all user data is to be stored.
|
||||
*
|
||||
* These are for example Screenshots, game saves, configuration files, log files, ...
|
||||
* @return Returns the name of the directory for user data.
|
||||
*/
|
||||
static Common::String getUserdataDirectory();
|
||||
|
||||
/**
|
||||
* @return Returns the path separator
|
||||
*/
|
||||
static Common::String getPathSeparator();
|
||||
|
||||
/**
|
||||
* @param Filename The path to a file.
|
||||
* @return Returns true if the file exists.
|
||||
*/
|
||||
static bool fileExists(const Common::String &filename);
|
||||
|
||||
/**
|
||||
* Gets the filename from a path and filename
|
||||
* @param Filename The full path and filename
|
||||
* @return Returns just the filename
|
||||
*/
|
||||
static Common::String getPathFilename(const Common::String &path);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
143
engines/sword25/kernel/inputpersistenceblock.cpp
Normal file
143
engines/sword25/kernel/inputpersistenceblock.cpp
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
InputPersistenceBlock::InputPersistenceBlock(const void *data, uint dataLength, int version) :
|
||||
_data(static_cast<const byte *>(data), dataLength),
|
||||
_errorState(NONE),
|
||||
_version(version) {
|
||||
_iter = _data.begin();
|
||||
}
|
||||
|
||||
InputPersistenceBlock::~InputPersistenceBlock() {
|
||||
if (_iter != _data.end())
|
||||
warning("Persistence block was not read to the end.");
|
||||
}
|
||||
|
||||
void InputPersistenceBlock::read(int16 &value) {
|
||||
int32 v;
|
||||
read(v);
|
||||
value = static_cast<int16>(v);
|
||||
}
|
||||
|
||||
void InputPersistenceBlock::read(int32 &value) {
|
||||
if (checkMarker(SINT_MARKER)) {
|
||||
value = (int32)READ_LE_UINT32(_iter);
|
||||
_iter += 4;
|
||||
} else {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void InputPersistenceBlock::read(uint32 &value) {
|
||||
if (checkMarker(UINT_MARKER)) {
|
||||
value = READ_LE_UINT32(_iter);
|
||||
_iter += 4;
|
||||
} else {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void InputPersistenceBlock::read(float &value) {
|
||||
if (checkMarker(FLOAT_MARKER)) {
|
||||
uint32 tmp[1];
|
||||
tmp[0] = READ_LE_UINT32(_iter);
|
||||
value = ((float *)tmp)[0];
|
||||
_iter += 4;
|
||||
} else {
|
||||
value = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void InputPersistenceBlock::read(bool &value) {
|
||||
if (checkMarker(BOOL_MARKER)) {
|
||||
uint uintBool = READ_LE_UINT32(_iter);
|
||||
_iter += 4;
|
||||
value = uintBool != 0;
|
||||
} else {
|
||||
value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void InputPersistenceBlock::readString(Common::String &value) {
|
||||
value = "";
|
||||
|
||||
if (checkMarker(STRING_MARKER)) {
|
||||
uint32 size;
|
||||
read(size);
|
||||
|
||||
if (checkBlockSize(size)) {
|
||||
value = Common::String(reinterpret_cast<const char *>(&*_iter), size);
|
||||
_iter += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputPersistenceBlock::readByteArray(Common::Array<byte> &value) {
|
||||
if (checkMarker(BLOCK_MARKER)) {
|
||||
uint32 size;
|
||||
read(size);
|
||||
|
||||
if (checkBlockSize(size)) {
|
||||
value = Common::Array<byte>(_iter, size);
|
||||
_iter += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InputPersistenceBlock::checkBlockSize(int size) {
|
||||
if (_data.end() - _iter >= size) {
|
||||
return true;
|
||||
} else {
|
||||
_errorState = END_OF_DATA;
|
||||
error("Unexpected end of persistence block.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InputPersistenceBlock::checkMarker(byte marker) {
|
||||
if (!isGood() || !checkBlockSize(1))
|
||||
return false;
|
||||
|
||||
if (*_iter++ == marker) {
|
||||
return true;
|
||||
} else {
|
||||
_errorState = OUT_OF_SYNC;
|
||||
error("Wrong type marker found in persistence block.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
81
engines/sword25/kernel/inputpersistenceblock.h
Normal file
81
engines/sword25/kernel/inputpersistenceblock.h
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_INPUTPERSISTENCEBLOCK_H
|
||||
#define SWORD25_INPUTPERSISTENCEBLOCK_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistenceblock.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class InputPersistenceBlock : public PersistenceBlock {
|
||||
public:
|
||||
enum ErrorState {
|
||||
NONE,
|
||||
END_OF_DATA,
|
||||
OUT_OF_SYNC
|
||||
};
|
||||
|
||||
InputPersistenceBlock(const void *data, uint dataLength, int version);
|
||||
virtual ~InputPersistenceBlock();
|
||||
|
||||
void read(int16 &value);
|
||||
void read(int32 &value);
|
||||
void read(uint32 &value);
|
||||
void read(float &value);
|
||||
void read(bool &value);
|
||||
void readString(Common::String &value);
|
||||
void readByteArray(Common::Array<byte> &value);
|
||||
|
||||
bool isGood() const {
|
||||
return _errorState == NONE;
|
||||
}
|
||||
ErrorState getErrorState() const {
|
||||
return _errorState;
|
||||
}
|
||||
|
||||
int getVersion() const { return _version; }
|
||||
|
||||
private:
|
||||
bool checkMarker(byte marker);
|
||||
bool checkBlockSize(int size);
|
||||
|
||||
Common::Array<byte> _data;
|
||||
Common::Array<byte>::const_iterator _iter;
|
||||
ErrorState _errorState;
|
||||
|
||||
int _version;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
193
engines/sword25/kernel/kernel.cpp
Normal file
193
engines/sword25/kernel/kernel.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "sword25/sword25.h" // for kDebugScript
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "sword25/fmv/movieplayer.h"
|
||||
#include "sword25/input/inputengine.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/persistenceservice.h"
|
||||
#include "sword25/math/geometry.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
#include "sword25/script/luascript.h"
|
||||
#include "sword25/sfx/soundengine.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Kernel *Kernel::_instance = 0;
|
||||
|
||||
Kernel::Kernel() :
|
||||
_resourceManager(NULL),
|
||||
_initSuccess(false),
|
||||
_gfx(0),
|
||||
_sfx(0),
|
||||
_input(0),
|
||||
_package(0),
|
||||
_script(0),
|
||||
_fmv(0),
|
||||
_rnd("sword25")
|
||||
{
|
||||
|
||||
_instance = this;
|
||||
|
||||
// Create the resource manager
|
||||
_resourceManager = new ResourceManager(this);
|
||||
|
||||
// Initialize the script engine
|
||||
_script = new LuaScriptEngine(this);
|
||||
if (!_script || !_script->init()) {
|
||||
_initSuccess = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Register kernel script bindings
|
||||
if (!registerScriptBindings()) {
|
||||
error("Script bindings could not be registered.");
|
||||
_initSuccess = false;
|
||||
return;
|
||||
}
|
||||
debugC(kDebugScript, "Script bindings registered.");
|
||||
|
||||
_input = new InputEngine(this);
|
||||
assert(_input);
|
||||
|
||||
_gfx = new GraphicEngine(this);
|
||||
assert(_gfx);
|
||||
|
||||
_sfx = new SoundEngine(this);
|
||||
assert(_sfx);
|
||||
|
||||
_package = new PackageManager(this);
|
||||
assert(_package);
|
||||
|
||||
_geometry = new Geometry(this);
|
||||
assert(_geometry);
|
||||
|
||||
_fmv = new MoviePlayer(this);
|
||||
assert(_fmv);
|
||||
|
||||
_initSuccess = true;
|
||||
}
|
||||
|
||||
Kernel::~Kernel() {
|
||||
// Services are de-registered in reverse order of creation
|
||||
|
||||
delete _input;
|
||||
_input = 0;
|
||||
|
||||
delete _gfx;
|
||||
_gfx = 0;
|
||||
|
||||
delete _sfx;
|
||||
_sfx = 0;
|
||||
|
||||
delete _package;
|
||||
_package = 0;
|
||||
|
||||
delete _geometry;
|
||||
_geometry = 0;
|
||||
|
||||
delete _fmv;
|
||||
_fmv = 0;
|
||||
|
||||
delete _script;
|
||||
_script = 0;
|
||||
|
||||
// Resource-Manager freigeben
|
||||
delete _resourceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random number
|
||||
* @param Min The minimum allowed value
|
||||
* @param Max The maximum allowed value
|
||||
*/
|
||||
int Kernel::getRandomNumber(int min, int max) {
|
||||
assert(min <= max);
|
||||
|
||||
return min + _rnd.getRandomNumber(max - min + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elapsed time since startup in milliseconds
|
||||
*/
|
||||
uint Kernel::getMilliTicks() {
|
||||
return g_system->getMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the active Gfx Service, or NULL if no Gfx service is active.
|
||||
*/
|
||||
GraphicEngine *Kernel::getGfx() {
|
||||
return _gfx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the active Sfx Service, or NULL if no Sfx service is active.
|
||||
*/
|
||||
SoundEngine *Kernel::getSfx() {
|
||||
return _sfx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the active input service, or NULL if no input service is active.
|
||||
*/
|
||||
InputEngine *Kernel::getInput() {
|
||||
return _input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the active package manager, or NULL if no manager is active.
|
||||
*/
|
||||
PackageManager *Kernel::getPackage() {
|
||||
return _package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the script engine, or NULL if it is not active.
|
||||
*/
|
||||
ScriptEngine *Kernel::getScript() {
|
||||
return _script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the movie player, or NULL if it is not active.
|
||||
*/
|
||||
MoviePlayer *Kernel::getFMV() {
|
||||
return _fmv;
|
||||
}
|
||||
|
||||
void Kernel::sleep(uint msecs) const {
|
||||
g_system->delayMillis(msecs);
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
195
engines/sword25/kernel/kernel.h
Normal file
195
engines/sword25/kernel/kernel.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* BS_Kernel
|
||||
* ---------
|
||||
* This is the main class of the engine.
|
||||
* This class creates and manages all other Engine elements: the sound engine, graphics engine ...
|
||||
* It is not necessary to release all the items individually, this is performed by the Kernel class.
|
||||
*
|
||||
* Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_KERNEL_H
|
||||
#define SWORD25_KERNEL_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/random.h"
|
||||
#include "common/stack.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "engines/engine.h"
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/resmanager.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// Class definitions
|
||||
class Service;
|
||||
class Geometry;
|
||||
class GraphicEngine;
|
||||
class ScriptEngine;
|
||||
class SoundEngine;
|
||||
class InputEngine;
|
||||
class PackageManager;
|
||||
class MoviePlayer;
|
||||
|
||||
/**
|
||||
* This is the main engine class
|
||||
*
|
||||
* This class creates and manages all other engine components such as sound engine, graphics engine ...
|
||||
* It is not necessary to release all the items individually, this is performed by the Kernel class.
|
||||
*/
|
||||
class Kernel {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Returns the elapsed time since startup in milliseconds
|
||||
*/
|
||||
uint getMilliTicks();
|
||||
|
||||
/**
|
||||
* Specifies whether the kernel was successfully initialized
|
||||
*/
|
||||
bool getInitSuccess() const {
|
||||
return _initSuccess;
|
||||
}
|
||||
/**
|
||||
* Returns a pointer to the BS_ResourceManager
|
||||
*/
|
||||
ResourceManager *getResourceManager() {
|
||||
return _resourceManager;
|
||||
}
|
||||
/**
|
||||
* Returns a random number
|
||||
* @param Min The minimum allowed value
|
||||
* @param Max The maximum allowed value
|
||||
*/
|
||||
int getRandomNumber(int min, int max);
|
||||
/**
|
||||
* Returns a pointer to the active Gfx Service, or NULL if no Gfx service is active
|
||||
*/
|
||||
GraphicEngine *getGfx();
|
||||
/**
|
||||
* Returns a pointer to the active Sfx Service, or NULL if no Sfx service is active
|
||||
*/
|
||||
SoundEngine *getSfx();
|
||||
/**
|
||||
* Returns a pointer to the active input service, or NULL if no input service is active
|
||||
*/
|
||||
InputEngine *getInput();
|
||||
/**
|
||||
* Returns a pointer to the active package manager, or NULL if no manager is active
|
||||
*/
|
||||
PackageManager *getPackage();
|
||||
/**
|
||||
* Returns a pointer to the script engine, or NULL if it is not active
|
||||
*/
|
||||
ScriptEngine *getScript();
|
||||
|
||||
/**
|
||||
* Returns a pointer to the movie player, or NULL if it is not active
|
||||
*/
|
||||
MoviePlayer *getFMV();
|
||||
|
||||
/**
|
||||
* Pauses for the specified amount of time
|
||||
* @param Msecs The amount of time in milliseconds
|
||||
*/
|
||||
void sleep(uint msecs) const;
|
||||
|
||||
/**
|
||||
* Returns the singleton instance for the kernel
|
||||
*/
|
||||
static Kernel *getInstance() {
|
||||
if (!_instance)
|
||||
_instance = new Kernel();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the kernel instance
|
||||
* This method should only be called when the game is ended. No subsequent calls to any kernel
|
||||
* methods should be done after calling this method.
|
||||
*/
|
||||
static void deleteInstance() {
|
||||
if (_instance) {
|
||||
delete _instance;
|
||||
_instance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises an error. This method is used in crashing testing.
|
||||
*/
|
||||
void crash() const {
|
||||
error("Kernel::Crash");
|
||||
}
|
||||
|
||||
private:
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constructor / destructor
|
||||
// Private singleton methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Kernel();
|
||||
virtual ~Kernel();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Singleton instance
|
||||
// -----------------------------------------------------------------------------
|
||||
static Kernel *_instance;
|
||||
|
||||
bool _initSuccess; // Specifies whether the engine was set up correctly
|
||||
|
||||
// Random number generator
|
||||
// -----------------------
|
||||
Common::RandomSource _rnd;
|
||||
|
||||
// Resourcemanager
|
||||
// ---------------
|
||||
ResourceManager *_resourceManager;
|
||||
|
||||
GraphicEngine *_gfx;
|
||||
SoundEngine *_sfx;
|
||||
InputEngine *_input;
|
||||
PackageManager *_package;
|
||||
ScriptEngine *_script;
|
||||
Geometry *_geometry;
|
||||
MoviePlayer *_fmv;
|
||||
|
||||
bool registerScriptBindings();
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
474
engines/sword25/kernel/kernel_script.cpp
Normal file
474
engines/sword25/kernel/kernel_script.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/filesystemutil.h"
|
||||
#include "sword25/kernel/resmanager.h"
|
||||
#include "sword25/kernel/persistenceservice.h"
|
||||
#include "sword25/script/script.h"
|
||||
#include "sword25/script/luabindhelper.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// Marks a function that should never be used
|
||||
static int dummyFuncError(lua_State *L) {
|
||||
error("Dummy function invoked by LUA");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getMilliTicks(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
|
||||
lua_pushnumber(L, pKernel->getMilliTicks());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getTimer(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
|
||||
lua_pushnumber(L, static_cast<lua_Number>(pKernel->getMilliTicks()) / 1000.0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int startService(lua_State *L) {
|
||||
// This function is used by system/boot.lua to init all services.
|
||||
// However, we do nothing here, as we just hard code the init sequence.
|
||||
lua_pushbooleancpp(L, true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sleep(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
pKernel->sleep(static_cast<uint>(luaL_checknumber(L, 1) * 1000));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crash(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
pKernel->crash();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int executeFile(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ScriptEngine *pSE = pKernel->getScript();
|
||||
assert(pSE);
|
||||
|
||||
lua_pushbooleancpp(L, pSE->executeFile(luaL_checkstring(L, 1)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getUserdataDirectory(lua_State *L) {
|
||||
lua_pushstring(L, FileSystemUtil::getUserdataDirectory().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getPathSeparator(lua_State *L) {
|
||||
lua_pushstring(L, FileSystemUtil::getPathSeparator().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fileExists(lua_State *L) {
|
||||
lua_pushbooleancpp(L, FileSystemUtil::fileExists(luaL_checkstring(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int createDirectory(lua_State *L) {
|
||||
// ScummVM engines cannot create directories, so we do nothing here.
|
||||
lua_pushbooleancpp(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getWinCode(lua_State *L) {
|
||||
lua_pushstring(L, "ScummVM");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getSubversionRevision(lua_State *L) {
|
||||
// ScummVM is 1337
|
||||
lua_pushnumber(L, 1337);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getUsedMemory(lua_State *L) {
|
||||
// It doesn't really matter what this call returns,
|
||||
// as it's used in a debug function.
|
||||
lua_pushnumber(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *KERNEL_LIBRARY_NAME = "Kernel";
|
||||
|
||||
static const luaL_reg KERNEL_FUNCTIONS[] = {
|
||||
{"DisconnectService", dummyFuncError},
|
||||
{"GetActiveServiceIdentifier", dummyFuncError},
|
||||
{"GetSuperclassCount", dummyFuncError},
|
||||
{"GetSuperclassIdentifier", dummyFuncError},
|
||||
{"GetServiceCount", dummyFuncError},
|
||||
{"GetServiceIdentifier", dummyFuncError},
|
||||
{"GetMilliTicks", getMilliTicks},
|
||||
{"GetTimer", getTimer},
|
||||
{"StartService", startService},
|
||||
{"Sleep", sleep},
|
||||
{"Crash", crash},
|
||||
{"ExecuteFile", executeFile},
|
||||
{"GetUserdataDirectory", getUserdataDirectory},
|
||||
{"GetPathSeparator", getPathSeparator},
|
||||
{"FileExists", fileExists},
|
||||
{"CreateDirectory", createDirectory},
|
||||
{"GetWinCode", getWinCode},
|
||||
{"GetSubversionRevision", getSubversionRevision},
|
||||
{"GetUsedMemory", getUsedMemory},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static int isVisible(lua_State *L) {
|
||||
// This function apparently is not used by the game scripts
|
||||
lua_pushbooleancpp(L, true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setVisible(lua_State *L) {
|
||||
// This function apparently is not used by the game scripts
|
||||
// pWindow->setVisible(lua_tobooleancpp(L, 1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getX(lua_State *L) {
|
||||
// This function apparently is not used by the game scripts
|
||||
lua_pushnumber(L, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getY(lua_State *L) {
|
||||
// This function apparently is not used by the game scripts
|
||||
lua_pushnumber(L, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setX(lua_State *L) {
|
||||
// This is called by system/boot.lua with -1 as value.
|
||||
// pWindow->setX(static_cast<int>(luaL_checknumber(L, 1)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setY(lua_State *L) {
|
||||
// This is called by system/boot.lua with -1 as value.
|
||||
// pWindow->setY(static_cast<int>(luaL_checknumber(L, 1)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getWidth(lua_State *L) {
|
||||
// This function apparently is not used by the game scripts
|
||||
lua_pushnumber(L, 800);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getHeight(lua_State *L) {
|
||||
// This function apparently is not used by the game scripts
|
||||
lua_pushnumber(L, 600);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setWidth(lua_State *L) {
|
||||
// This is called by system/boot.lua with 800 as value.
|
||||
// pWindow->setWidth(static_cast<int>(luaL_checknumber(L, 1)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setHeight(lua_State *L) {
|
||||
// This is called by system/boot.lua with 600 as value.
|
||||
// pWindow->setHeight(static_cast<int>(luaL_checknumber(L, 1)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getTitle(lua_State *L) {
|
||||
// This function apparently is not used by the game scripts
|
||||
lua_pushstring(L, "");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setTitle(lua_State *L) {
|
||||
// This is called by system/boot.lua and system/menu.lua, to
|
||||
// set the window title to the (localized) game name.
|
||||
// FIXME: Should we call OSystem::setWindowCaption() here?
|
||||
// pWindow->setTitle(luaL_checkstring(L, 1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int processMessages(lua_State *L) {
|
||||
// This is called by the main loop in system/boot.lua,
|
||||
// and the game keeps running as true is returned here.
|
||||
// It terminates if we return false.
|
||||
|
||||
// TODO: We could do more stuff here if desired...
|
||||
|
||||
// TODO: We could always return true here, and leave quit handling
|
||||
// to the closeWanted() opcode; see also the TODO comment in there.
|
||||
|
||||
lua_pushbooleancpp(L, !Engine::shouldQuit());
|
||||
g_system->delayMillis(10);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int closeWanted(lua_State *L) {
|
||||
// This is called by system/interface.lua to determine whether the
|
||||
// user requested the game to close (e.g. by clicking the 'close' widget
|
||||
// of the game window). As a consequence (i.e. this function returns true),
|
||||
// a quit confirmation dialog is shown.
|
||||
|
||||
// TODO: ScummVM currently has a bug / misfeature where some engines provide
|
||||
// quit confirmation dialogs, some don't; in addition, we have a global confirmation
|
||||
// dialog (but the user has to explicitly activate that in the config).
|
||||
// Anyway, this can lead to *two* confirmation dialogs being shown.
|
||||
// If it wasn't for that, we could simply check for Engine::shouldQuit() here,
|
||||
// and then invoke EventMan::resetQuit. But currently this would result in
|
||||
// the user seeing two confirmation dialogs. Bad.
|
||||
lua_pushbooleancpp(L, false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *WINDOW_LIBRARY_NAME = "Window";
|
||||
|
||||
static const luaL_reg WINDOW_FUNCTIONS[] = {
|
||||
{"IsVisible", isVisible},
|
||||
{"SetVisible", setVisible},
|
||||
{"GetX", getX},
|
||||
{"SetX", setX},
|
||||
{"GetY", getY},
|
||||
{"SetY", setY},
|
||||
{"GetClientX", dummyFuncError},
|
||||
{"GetClientY", dummyFuncError},
|
||||
{"GetWidth", getWidth},
|
||||
{"GetHeight", getHeight},
|
||||
{"SetWidth", setWidth},
|
||||
{"SetHeight", setHeight},
|
||||
{"GetTitle", getTitle},
|
||||
{"SetTitle", setTitle},
|
||||
{"ProcessMessages", processMessages},
|
||||
{"CloseWanted", closeWanted},
|
||||
{"WaitForFocus", dummyFuncError},
|
||||
{"HasFocus", dummyFuncError},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static int precacheResource(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ResourceManager *pResource = pKernel->getResourceManager();
|
||||
assert(pResource);
|
||||
|
||||
#ifdef PRECACHE_RESOURCES
|
||||
lua_pushbooleancpp(L, pResource->precacheResource(luaL_checkstring(L, 1)));
|
||||
#else
|
||||
lua_pushbooleancpp(L, true);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int forcePrecacheResource(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ResourceManager *pResource = pKernel->getResourceManager();
|
||||
assert(pResource);
|
||||
|
||||
#ifdef PRECACHE_RESOURCES
|
||||
lua_pushbooleancpp(L, pResource->precacheResource(luaL_checkstring(L, 1), true));
|
||||
#else
|
||||
lua_pushbooleancpp(L, true);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getMaxMemoryUsage(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ResourceManager *pResource = pKernel->getResourceManager();
|
||||
assert(pResource);
|
||||
|
||||
// This is used for debugging, so it doesn't really matter.
|
||||
// The default value set by the scripts is 256000000 bytes
|
||||
lua_pushnumber(L, 256000000);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setMaxMemoryUsage(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ResourceManager *pResource = pKernel->getResourceManager();
|
||||
assert(pResource);
|
||||
|
||||
// This call is ignored, we set a limit on the number of
|
||||
// simultaneous resources loaded instead.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emptyCache(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ResourceManager *pResource = pKernel->getResourceManager();
|
||||
assert(pResource);
|
||||
|
||||
pResource->emptyCache();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dumpLockedResources(lua_State *L) {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ResourceManager *pResource = pKernel->getResourceManager();
|
||||
assert(pResource);
|
||||
|
||||
pResource->dumpLockedResources();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *RESOURCE_LIBRARY_NAME = "Resource";
|
||||
|
||||
static const luaL_reg RESOURCE_FUNCTIONS[] = {
|
||||
{"PrecacheResource", precacheResource},
|
||||
{"ForcePrecacheResource", forcePrecacheResource},
|
||||
{"GetMaxMemoryUsage", getMaxMemoryUsage},
|
||||
{"SetMaxMemoryUsage", setMaxMemoryUsage},
|
||||
{"EmptyCache", emptyCache},
|
||||
{"IsLogCacheMiss", dummyFuncError},
|
||||
{"SetLogCacheMiss", dummyFuncError},
|
||||
{"DumpLockedResources", dumpLockedResources},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static int reloadSlots(lua_State *L) {
|
||||
PersistenceService::getInstance().reloadSlots();
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getSlotCount(lua_State *L) {
|
||||
lua_pushnumber(L, PersistenceService::getInstance().getSlotCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isSlotOccupied(lua_State *L) {
|
||||
lua_pushbooleancpp(L, PersistenceService::getInstance().isSlotOccupied(static_cast<uint>(luaL_checknumber(L, 1)) - 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getSavegameDirectory(lua_State *L) {
|
||||
lua_pushstring(L, PersistenceService::getInstance().getSavegameDirectory().toString('/').c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isSavegameCompatible(lua_State *L) {
|
||||
lua_pushbooleancpp(L, PersistenceService::getInstance().isSavegameCompatible(
|
||||
static_cast<uint>(luaL_checknumber(L, 1)) - 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getSavegameDescription(lua_State *L) {
|
||||
lua_pushstring(L, PersistenceService::getInstance().getSavegameDescription(
|
||||
static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getSavegameFilename(lua_State *L) {
|
||||
lua_pushstring(L, PersistenceService::getInstance().getSavegameFilename(static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int loadGame(lua_State *L) {
|
||||
lua_pushbooleancpp(L, PersistenceService::getInstance().loadGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int saveGame(lua_State *L) {
|
||||
lua_pushbooleancpp(L, PersistenceService::getInstance().saveGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1, luaL_checkstring(L, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *PERSISTENCE_LIBRARY_NAME = "Persistence";
|
||||
|
||||
static const luaL_reg PERSISTENCE_FUNCTIONS[] = {
|
||||
{"ReloadSlots", reloadSlots},
|
||||
{"GetSlotCount", getSlotCount},
|
||||
{"IsSlotOccupied", isSlotOccupied},
|
||||
{"GetSavegameDirectory", getSavegameDirectory},
|
||||
{"IsSavegameCompatible", isSavegameCompatible},
|
||||
{"GetSavegameDescription", getSavegameDescription},
|
||||
{"GetSavegameFilename", getSavegameFilename},
|
||||
{"LoadGame", loadGame},
|
||||
{"SaveGame", saveGame},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
bool Kernel::registerScriptBindings() {
|
||||
ScriptEngine *pScript = getScript();
|
||||
assert(pScript);
|
||||
lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
|
||||
assert(L);
|
||||
|
||||
if (!LuaBindhelper::addFunctionsToLib(L, KERNEL_LIBRARY_NAME, KERNEL_FUNCTIONS)) return false;
|
||||
if (!LuaBindhelper::addFunctionsToLib(L, WINDOW_LIBRARY_NAME, WINDOW_FUNCTIONS)) return false;
|
||||
if (!LuaBindhelper::addFunctionsToLib(L, RESOURCE_LIBRARY_NAME, RESOURCE_FUNCTIONS)) return false;
|
||||
if (!LuaBindhelper::addFunctionsToLib(L, PERSISTENCE_LIBRARY_NAME, PERSISTENCE_FUNCTIONS)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
167
engines/sword25/kernel/objectregistry.h
Normal file
167
engines/sword25/kernel/objectregistry.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_OBJECTREGISTRY_H
|
||||
#define SWORD25_OBJECTREGISTRY_H
|
||||
|
||||
#include "common/func.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
template<typename T>
|
||||
class ObjectRegistry {
|
||||
public:
|
||||
ObjectRegistry() : _nextHandle(1) {}
|
||||
virtual ~ObjectRegistry() {}
|
||||
|
||||
uint registerObject(T *objectPtr) {
|
||||
// Null-Pointer können nicht registriert werden.
|
||||
if (objectPtr == 0) {
|
||||
error("Cannot register a null pointer.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Falls das Objekt bereits registriert wurde, wird eine Warnung ausgeben und das Handle zurückgeben.
|
||||
uint handle = findHandleByPtr(objectPtr);
|
||||
if (handle != 0) {
|
||||
warning("Tried to register a object that was already registered.");
|
||||
return handle;
|
||||
}
|
||||
// Ansonsten wird das Objekt in beide Maps eingetragen und das neue Handle zurückgeben.
|
||||
else {
|
||||
_handle2PtrMap[_nextHandle] = objectPtr;
|
||||
_ptr2HandleMap[objectPtr] = _nextHandle;
|
||||
|
||||
return _nextHandle++;
|
||||
}
|
||||
}
|
||||
|
||||
uint registerObject(T *objectPtr, uint handle) {
|
||||
// Null-Pointer und Null-Handle können nicht registriert werden.
|
||||
if (objectPtr == 0 || handle == 0) {
|
||||
error("Cannot register a null pointer or a null handle.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Falls das Objekt bereits registriert wurde, wird ein Fehler ausgegeben und 0 zurückgeben.
|
||||
uint handleTest = findHandleByPtr(objectPtr);
|
||||
if (handleTest != 0) {
|
||||
error("Tried to register a object that was already registered.");
|
||||
return 0;
|
||||
}
|
||||
// Falls das Handle bereits vergeben ist, wird ein Fehler ausgegeben und 0 zurückgegeben.
|
||||
else if (findPtrByHandle(handle) != 0) {
|
||||
error("Tried to register a handle that is already taken.");
|
||||
return 0;
|
||||
}
|
||||
// Ansonsten wird das Objekt in beide Maps eingetragen und das gewünschte Handle zurückgeben.
|
||||
else {
|
||||
_handle2PtrMap[handle] = objectPtr;
|
||||
_ptr2HandleMap[objectPtr] = handle;
|
||||
|
||||
// Falls das vergebene Handle größer oder gleich dem nächsten automatische vergebenen Handle ist, wird das nächste automatisch
|
||||
// vergebene Handle erhöht.
|
||||
if (handle >= _nextHandle)
|
||||
_nextHandle = handle + 1;
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
void deregisterObject(T *objectPtr) {
|
||||
uint handle = findHandleByPtr(objectPtr);
|
||||
|
||||
if (handle != 0) {
|
||||
// Registriertes Objekt aus beiden Maps entfernen.
|
||||
_handle2PtrMap.erase(findHandleByPtr(objectPtr));
|
||||
_ptr2HandleMap.erase(objectPtr);
|
||||
} else {
|
||||
warning("Tried to remove a object that was not registered.");
|
||||
}
|
||||
}
|
||||
|
||||
T *resolveHandle(uint handle) {
|
||||
// Zum Handle gehöriges Objekt in der Hash-Map finden.
|
||||
T *objectPtr = findPtrByHandle(handle);
|
||||
|
||||
// Pointer zurückgeben. Im Fehlerfall ist dieser 0.
|
||||
return objectPtr;
|
||||
}
|
||||
|
||||
uint resolvePtr(T *objectPtr) {
|
||||
// Zum Pointer gehöriges Handle in der Hash-Map finden.
|
||||
uint handle = findHandleByPtr(objectPtr);
|
||||
|
||||
// Handle zurückgeben. Im Fehlerfall ist dieses 0.
|
||||
return handle;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct ClassPointer_EqualTo {
|
||||
bool operator()(const T *x, const T *y) const {
|
||||
return x == y;
|
||||
}
|
||||
};
|
||||
struct ClassPointer_Hash {
|
||||
uint operator()(const T *x) const {
|
||||
return *(uint *)&x;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Common::HashMap<uint32, T *> HANDLE2PTR_MAP;
|
||||
typedef Common::HashMap<T *, uint32, ClassPointer_Hash, ClassPointer_EqualTo> PTR2HANDLE_MAP;
|
||||
|
||||
HANDLE2PTR_MAP _handle2PtrMap;
|
||||
PTR2HANDLE_MAP _ptr2HandleMap;
|
||||
uint32 _nextHandle;
|
||||
|
||||
T *findPtrByHandle(uint handle) {
|
||||
// Zum Handle gehörigen Pointer finden.
|
||||
typename HANDLE2PTR_MAP::const_iterator it = _handle2PtrMap.find(handle);
|
||||
|
||||
// Pointer zurückgeben, oder, falls keiner gefunden wurde, 0 zurückgeben.
|
||||
return (it != _handle2PtrMap.end()) ? it->_value : 0;
|
||||
}
|
||||
|
||||
uint findHandleByPtr(T *objectPtr) {
|
||||
// Zum Pointer gehöriges Handle finden.
|
||||
typename PTR2HANDLE_MAP::const_iterator it = _ptr2HandleMap.find(objectPtr);
|
||||
|
||||
// Handle zurückgeben, oder, falls keines gefunden wurde, 0 zurückgeben.
|
||||
return (it != _ptr2HandleMap.end()) ? it->_value : 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
106
engines/sword25/kernel/outputpersistenceblock.cpp
Normal file
106
engines/sword25/kernel/outputpersistenceblock.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
|
||||
namespace {
|
||||
const uint INITIAL_BUFFER_SIZE = 1024 * 64;
|
||||
}
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
OutputPersistenceBlock::OutputPersistenceBlock() {
|
||||
_data.reserve(INITIAL_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::write(const void *data, uint32 size) {
|
||||
writeMarker(BLOCK_MARKER);
|
||||
|
||||
write(size);
|
||||
rawWrite(data, size);
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::write(int32 value) {
|
||||
writeMarker(SINT_MARKER);
|
||||
value = TO_LE_32(value);
|
||||
rawWrite(&value, sizeof(value));
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::write(uint32 value) {
|
||||
writeMarker(UINT_MARKER);
|
||||
value = TO_LE_32(value);
|
||||
rawWrite(&value, sizeof(value));
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::write(float value) {
|
||||
writeMarker(FLOAT_MARKER);
|
||||
uint32 tmp[1];
|
||||
|
||||
((float *)tmp)[0] = value;
|
||||
tmp[0] = TO_LE_32(tmp[0]);
|
||||
|
||||
rawWrite(&value, sizeof(value));
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::write(bool value) {
|
||||
writeMarker(BOOL_MARKER);
|
||||
|
||||
uint uintBool = value ? 1 : 0;
|
||||
uintBool = TO_LE_32(uintBool);
|
||||
rawWrite(&uintBool, sizeof(uintBool));
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::writeString(const Common::String &string) {
|
||||
writeMarker(STRING_MARKER);
|
||||
|
||||
write((uint32)string.size());
|
||||
rawWrite(string.c_str(), string.size());
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::writeByteArray(Common::Array<byte> &value) {
|
||||
writeMarker(BLOCK_MARKER);
|
||||
|
||||
write((uint32)value.size());
|
||||
rawWrite(&value[0], value.size());
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::writeMarker(byte marker) {
|
||||
_data.push_back(marker);
|
||||
}
|
||||
|
||||
void OutputPersistenceBlock::rawWrite(const void *dataPtr, size_t size) {
|
||||
if (size > 0) {
|
||||
uint oldSize = _data.size();
|
||||
_data.resize(oldSize + size);
|
||||
memcpy(&_data[oldSize], dataPtr, size);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
67
engines/sword25/kernel/outputpersistenceblock.h
Normal file
67
engines/sword25/kernel/outputpersistenceblock.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_OUTPUTPERSISTENCEBLOCK_H
|
||||
#define SWORD25_OUTPUTPERSISTENCEBLOCK_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistenceblock.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class OutputPersistenceBlock : public PersistenceBlock {
|
||||
public:
|
||||
OutputPersistenceBlock();
|
||||
|
||||
void write(const void *data, uint32 size);
|
||||
void write(int32 value);
|
||||
void write(uint32 value);
|
||||
void write(float value);
|
||||
void write(bool value);
|
||||
void writeString(const Common::String &string);
|
||||
void writeByteArray(Common::Array<byte> &value);
|
||||
|
||||
const void *getData() const {
|
||||
return &_data[0];
|
||||
}
|
||||
uint getDataSize() const {
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
private:
|
||||
void writeMarker(byte marker);
|
||||
void rawWrite(const void *dataPtr, size_t size);
|
||||
|
||||
Common::Array<byte> _data;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
49
engines/sword25/kernel/persistable.h
Normal file
49
engines/sword25/kernel/persistable.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_PERSISTABLE_H
|
||||
#define SWORD25_PERSISTABLE_H
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class OutputPersistenceBlock;
|
||||
class InputPersistenceBlock;
|
||||
|
||||
class Persistable {
|
||||
public:
|
||||
virtual ~Persistable() {}
|
||||
|
||||
virtual bool persist(OutputPersistenceBlock &writer) = 0;
|
||||
virtual bool unpersist(InputPersistenceBlock &reader) = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
77
engines/sword25/kernel/persistenceblock.h
Normal file
77
engines/sword25/kernel/persistenceblock.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_PERSISTENCEBLOCK_H
|
||||
#define SWORD25_PERSISTENCEBLOCK_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class PersistenceBlock {
|
||||
public:
|
||||
static uint getSInt32Size() {
|
||||
return sizeof(signed int) + sizeof(byte);
|
||||
}
|
||||
static uint getUInt32Size() {
|
||||
return sizeof(uint) + sizeof(byte);
|
||||
}
|
||||
static uint getFloat32Size() {
|
||||
return sizeof(float) + sizeof(byte);
|
||||
}
|
||||
static uint getBoolSize() {
|
||||
return sizeof(byte) + sizeof(byte);
|
||||
}
|
||||
static uint getStringSize(const Common::String &string) {
|
||||
return static_cast<uint>(sizeof(uint) + string.size() + sizeof(byte));
|
||||
}
|
||||
|
||||
protected:
|
||||
enum {
|
||||
SINT_MARKER,
|
||||
UINT_MARKER,
|
||||
FLOAT_MARKER,
|
||||
STRING_MARKER,
|
||||
BOOL_MARKER,
|
||||
BLOCK_MARKER
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#define CTASSERT(ex) typedef char ctassert_type[(ex) ? 1 : -1]
|
||||
CTASSERT(sizeof(byte) == 1);
|
||||
CTASSERT(sizeof(signed int) == 4);
|
||||
CTASSERT(sizeof(uint) == 4);
|
||||
CTASSERT(sizeof(float) == 4);
|
||||
#undef CTASSERT
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
437
engines/sword25/kernel/persistenceservice.cpp
Normal file
437
engines/sword25/kernel/persistenceservice.cpp
Normal file
@@ -0,0 +1,437 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/fs.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/compression/deflate.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/persistenceservice.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/filesystemutil.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "sword25/sfx/soundengine.h"
|
||||
#include "sword25/input/inputengine.h"
|
||||
#include "sword25/math/regionregistry.h"
|
||||
#include "sword25/script/script.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
//static const char *SAVEGAME_EXTENSION = ".b25s";
|
||||
static const char *SAVEGAME_DIRECTORY = "saves";
|
||||
static const char *FILE_MARKER = "BS25SAVEGAME";
|
||||
static const uint SLOT_COUNT = 18;
|
||||
static const uint FILE_COPY_BUFFER_SIZE = 1024 * 10;
|
||||
static const char *VERSIONIDOLD = "SCUMMVM1";
|
||||
static const char *VERSIONID = "SCUMMVM2";
|
||||
static const int VERSIONNUM = 3;
|
||||
|
||||
#define MAX_SAVEGAME_SIZE 100
|
||||
|
||||
char gameTarget[MAX_SAVEGAME_SIZE];
|
||||
|
||||
void setGameTarget(const char *target) {
|
||||
strncpy(gameTarget, target, MAX_SAVEGAME_SIZE - 1);
|
||||
}
|
||||
|
||||
static Common::String generateSavegameFilename(uint slotID) {
|
||||
char buffer[MAX_SAVEGAME_SIZE+5];
|
||||
snprintf(buffer, MAX_SAVEGAME_SIZE+5, "%s.%.3d", gameTarget, slotID);
|
||||
return Common::String(buffer);
|
||||
}
|
||||
|
||||
static Common::String formatTimestamp(TimeDate time) {
|
||||
// In the original BS2.5 engine, this used a local object to show the date/time as as a string.
|
||||
// For now in ScummVM it's being hardcoded to 'dd-MON-yyyy hh:mm:ss'
|
||||
Common::String monthList[12] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
char buffer[100];
|
||||
snprintf(buffer, 100, "%.2d-%s-%.4d %.2d:%.2d:%.2d",
|
||||
time.tm_mday, monthList[time.tm_mon].c_str(), 1900 + time.tm_year,
|
||||
time.tm_hour, time.tm_min, time.tm_sec
|
||||
);
|
||||
|
||||
return Common::String(buffer);
|
||||
}
|
||||
|
||||
static Common::String loadString(Common::InSaveFile *in, uint maxSize = 999) {
|
||||
Common::String result;
|
||||
|
||||
char ch = (char)in->readByte();
|
||||
while (ch != '\0') {
|
||||
result += ch;
|
||||
if (result.size() >= maxSize)
|
||||
break;
|
||||
ch = (char)in->readByte();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct SavegameInformation {
|
||||
bool isOccupied;
|
||||
bool isCompatible;
|
||||
Common::String description;
|
||||
int version;
|
||||
uint gamedataLength;
|
||||
uint gamedataOffset;
|
||||
uint gamedataUncompressedLength;
|
||||
|
||||
SavegameInformation() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
isOccupied = false;
|
||||
isCompatible = false;
|
||||
description = "";
|
||||
gamedataLength = 0;
|
||||
gamedataOffset = 0;
|
||||
gamedataUncompressedLength = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct PersistenceService::Impl {
|
||||
SavegameInformation _savegameInformations[SLOT_COUNT];
|
||||
|
||||
Impl() {
|
||||
reloadSlots();
|
||||
}
|
||||
|
||||
void reloadSlots() {
|
||||
// Iterate through all the saved games, and read their thumbnails.
|
||||
for (uint i = 0; i < SLOT_COUNT; ++i) {
|
||||
readSlotSavegameInformation(i);
|
||||
}
|
||||
}
|
||||
|
||||
void readSlotSavegameInformation(uint slotID) {
|
||||
// Get the information corresponding to the requested save slot.
|
||||
SavegameInformation &curSavegameInfo = _savegameInformations[slotID];
|
||||
curSavegameInfo.clear();
|
||||
|
||||
// Generate the save slot file name.
|
||||
Common::String filename = generateSavegameFilename(slotID);
|
||||
|
||||
// Try to open the savegame for loading
|
||||
Common::SaveFileManager *sfm = g_system->getSavefileManager();
|
||||
Common::InSaveFile *file = sfm->openForLoading(filename);
|
||||
|
||||
if (file) {
|
||||
// Read in the header
|
||||
Common::String storedMarker = loadString(file);
|
||||
Common::String storedVersionID = loadString(file);
|
||||
if (storedVersionID == VERSIONIDOLD) {
|
||||
curSavegameInfo.version = 1;
|
||||
} else {
|
||||
Common::String versionNum = loadString(file);
|
||||
curSavegameInfo.version = atoi(versionNum.c_str());
|
||||
}
|
||||
Common::String gameDescription = loadString(file);
|
||||
Common::String gamedataLength = loadString(file);
|
||||
curSavegameInfo.gamedataLength = atoi(gamedataLength.c_str());
|
||||
Common::String gamedataUncompressedLength = loadString(file);
|
||||
curSavegameInfo.gamedataUncompressedLength = atoi(gamedataUncompressedLength.c_str());
|
||||
|
||||
// If the header can be read in and is detected to be valid, we will have a valid file
|
||||
if (storedMarker == FILE_MARKER) {
|
||||
// The slot is marked as occupied.
|
||||
curSavegameInfo.isOccupied = true;
|
||||
// Check if the saved game is compatible with the current engine version.
|
||||
curSavegameInfo.isCompatible = (curSavegameInfo.version <= VERSIONNUM);
|
||||
// Load the save game description.
|
||||
curSavegameInfo.description = gameDescription;
|
||||
// The offset to the stored save game data within the file.
|
||||
// This reflects the current position, as the header information
|
||||
// is still followed by a space as separator.
|
||||
curSavegameInfo.gamedataOffset = static_cast<uint>(file->pos());
|
||||
}
|
||||
|
||||
delete file;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PersistenceService *persInstance = nullptr;
|
||||
|
||||
PersistenceService &PersistenceService::getInstance() {
|
||||
if (!persInstance)
|
||||
persInstance = new PersistenceService;
|
||||
|
||||
return *persInstance;
|
||||
}
|
||||
|
||||
PersistenceService::PersistenceService() : _impl(new Impl) {
|
||||
}
|
||||
|
||||
PersistenceService::~PersistenceService() {
|
||||
delete _impl;
|
||||
|
||||
delete persInstance;
|
||||
persInstance = nullptr;
|
||||
}
|
||||
|
||||
void PersistenceService::reloadSlots() {
|
||||
_impl->reloadSlots();
|
||||
}
|
||||
|
||||
uint PersistenceService::getSlotCount() {
|
||||
return SLOT_COUNT;
|
||||
}
|
||||
|
||||
Common::Path PersistenceService::getSavegameDirectory() {
|
||||
Common::FSNode node(FileSystemUtil::getUserdataDirectoryPath());
|
||||
Common::FSNode childNode = node.getChild(SAVEGAME_DIRECTORY);
|
||||
|
||||
// Try and return the path using the savegame subfolder. But if doesn't exist, fall back on the data directory
|
||||
if (childNode.exists())
|
||||
return childNode.getPath();
|
||||
|
||||
return node.getPath();
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool checkslotID(uint slotID) {
|
||||
// Überprüfen, ob die Slot-ID zulässig ist.
|
||||
if (slotID >= SLOT_COUNT) {
|
||||
error("Tried to access an invalid slot (%d). Only slot ids from 0 to %d are allowed.", slotID, SLOT_COUNT - 1);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PersistenceService::isSlotOccupied(uint slotID) {
|
||||
if (!checkslotID(slotID))
|
||||
return false;
|
||||
return _impl->_savegameInformations[slotID].isOccupied;
|
||||
}
|
||||
|
||||
bool PersistenceService::isSavegameCompatible(uint slotID) {
|
||||
if (!checkslotID(slotID))
|
||||
return false;
|
||||
return _impl->_savegameInformations[slotID].isCompatible;
|
||||
}
|
||||
|
||||
Common::String &PersistenceService::getSavegameDescription(uint slotID) {
|
||||
static Common::String emptyString;
|
||||
if (!checkslotID(slotID))
|
||||
return emptyString;
|
||||
return _impl->_savegameInformations[slotID].description;
|
||||
}
|
||||
|
||||
Common::String &PersistenceService::getSavegameFilename(uint slotID) {
|
||||
static Common::String result;
|
||||
if (!checkslotID(slotID))
|
||||
return result;
|
||||
result = generateSavegameFilename(slotID);
|
||||
return result;
|
||||
}
|
||||
|
||||
int PersistenceService::getSavegameVersion(uint slotID) {
|
||||
if (!checkslotID(slotID))
|
||||
return -1;
|
||||
return _impl->_savegameInformations[slotID].version;
|
||||
}
|
||||
|
||||
bool PersistenceService::saveGame(uint slotID, const Common::String &screenshotFilename) {
|
||||
// FIXME: This code is a hack which bypasses the savefile API,
|
||||
// and should eventually be removed.
|
||||
|
||||
// Überprüfen, ob die Slot-ID zulässig ist.
|
||||
if (slotID >= SLOT_COUNT) {
|
||||
error("Tried to save to an invalid slot (%d). Only slot ids form 0 to %d are allowed.", slotID, SLOT_COUNT - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dateinamen erzeugen.
|
||||
Common::String filename = generateSavegameFilename(slotID);
|
||||
|
||||
// Spielstanddatei öffnen und die Headerdaten schreiben.
|
||||
Common::SaveFileManager *sfm = g_system->getSavefileManager();
|
||||
Common::OutSaveFile *file = sfm->openForSaving(filename);
|
||||
|
||||
file->writeString(FILE_MARKER);
|
||||
file->writeByte(0);
|
||||
file->writeString(VERSIONID);
|
||||
file->writeByte(0);
|
||||
|
||||
char buf[20];
|
||||
snprintf(buf, 20, "%d", VERSIONNUM);
|
||||
file->writeString(buf);
|
||||
file->writeByte(0);
|
||||
|
||||
TimeDate dt;
|
||||
g_system->getTimeAndDate(dt);
|
||||
file->writeString(formatTimestamp(dt));
|
||||
file->writeByte(0);
|
||||
|
||||
if (file->err()) {
|
||||
error("Unable to write header data to savegame file \"%s\".", filename.c_str());
|
||||
}
|
||||
|
||||
// Alle notwendigen Module persistieren.
|
||||
OutputPersistenceBlock writer;
|
||||
bool success = true;
|
||||
success &= Kernel::getInstance()->getScript()->persist(writer);
|
||||
success &= RegionRegistry::instance().persist(writer);
|
||||
success &= Kernel::getInstance()->getGfx()->persist(writer);
|
||||
success &= Kernel::getInstance()->getSfx()->persist(writer);
|
||||
success &= Kernel::getInstance()->getInput()->persist(writer);
|
||||
if (!success) {
|
||||
error("Unable to persist modules for savegame file \"%s\".", filename.c_str());
|
||||
}
|
||||
|
||||
// Write the save game data uncompressed, since the final saved game will be
|
||||
// compressed anyway.
|
||||
char sBuffer[10];
|
||||
snprintf(sBuffer, 10, "%u", writer.getDataSize());
|
||||
file->writeString(sBuffer);
|
||||
file->writeByte(0);
|
||||
snprintf(sBuffer, 10, "%u", writer.getDataSize());
|
||||
file->writeString(sBuffer);
|
||||
file->writeByte(0);
|
||||
file->write(writer.getData(), writer.getDataSize());
|
||||
|
||||
// Get the screenshot
|
||||
Common::SeekableReadStream *thumbnail = Kernel::getInstance()->getGfx()->getThumbnail();
|
||||
|
||||
if (thumbnail) {
|
||||
byte *buffer = new byte[FILE_COPY_BUFFER_SIZE];
|
||||
thumbnail->seek(0, SEEK_SET);
|
||||
while (!thumbnail->eos()) {
|
||||
int bytesRead = thumbnail->read(&buffer[0], FILE_COPY_BUFFER_SIZE);
|
||||
file->write(&buffer[0], bytesRead);
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
} else {
|
||||
warning("The screenshot file \"%s\" does not exist. Savegame is written without a screenshot.", filename.c_str());
|
||||
}
|
||||
|
||||
file->finalize();
|
||||
delete file;
|
||||
|
||||
// Savegameinformationen für diesen Slot aktualisieren.
|
||||
_impl->readSlotSavegameInformation(slotID);
|
||||
|
||||
// Empty the cache, to remove old thumbnails
|
||||
Kernel::getInstance()->getResourceManager()->emptyThumbnailCache();
|
||||
|
||||
// Erfolg signalisieren.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PersistenceService::loadGame(uint slotID) {
|
||||
Common::SaveFileManager *sfm = g_system->getSavefileManager();
|
||||
Common::InSaveFile *file;
|
||||
|
||||
// Überprüfen, ob die Slot-ID zulässig ist.
|
||||
if (slotID >= SLOT_COUNT) {
|
||||
error("Tried to load from an invalid slot (%d). Only slot ids form 0 to %d are allowed.", slotID, SLOT_COUNT - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
SavegameInformation &curSavegameInfo = _impl->_savegameInformations[slotID];
|
||||
|
||||
// Überprüfen, ob der Slot belegt ist.
|
||||
if (!curSavegameInfo.isOccupied) {
|
||||
error("Tried to load from an empty slot (%d).", slotID);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Überprüfen, ob der Spielstand im angegebenen Slot mit der aktuellen Engine-Version kompatibel ist.
|
||||
// Im Debug-Modus wird dieser Test übersprungen. Für das Testen ist es hinderlich auf die Einhaltung dieser strengen Bedingung zu bestehen,
|
||||
// da sich die Versions-ID bei jeder Codeänderung mitändert.
|
||||
#ifndef DEBUG
|
||||
if (!curSavegameInfo.isCompatible) {
|
||||
error("Tried to load a savegame (%d) that is not compatible with this engine version.", slotID);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
byte *compressedDataBuffer = new byte[curSavegameInfo.gamedataLength];
|
||||
byte *uncompressedDataBuffer = new byte[curSavegameInfo.gamedataUncompressedLength];
|
||||
Common::String filename = generateSavegameFilename(slotID);
|
||||
file = sfm->openForLoading(filename);
|
||||
|
||||
file->seek(curSavegameInfo.gamedataOffset);
|
||||
file->read(reinterpret_cast<char *>(&compressedDataBuffer[0]), curSavegameInfo.gamedataLength);
|
||||
if (file->err()) {
|
||||
error("Unable to load the gamedata from the savegame file \"%s\".", filename.c_str());
|
||||
delete[] compressedDataBuffer;
|
||||
delete[] uncompressedDataBuffer;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Uncompress game data, if needed.
|
||||
unsigned long uncompressedBufferSize = curSavegameInfo.gamedataUncompressedLength;
|
||||
|
||||
if (uncompressedBufferSize > curSavegameInfo.gamedataLength) {
|
||||
// Older saved game, where the game data was compressed again.
|
||||
if (!Common::inflateZlib(reinterpret_cast<byte *>(&uncompressedDataBuffer[0]), &uncompressedBufferSize,
|
||||
reinterpret_cast<byte *>(&compressedDataBuffer[0]), curSavegameInfo.gamedataLength)) {
|
||||
error("Unable to decompress the gamedata from savegame file \"%s\".", filename.c_str());
|
||||
delete[] uncompressedDataBuffer;
|
||||
delete[] compressedDataBuffer;
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Newer saved game with uncompressed game data, copy it as-is.
|
||||
memcpy(uncompressedDataBuffer, compressedDataBuffer, uncompressedBufferSize);
|
||||
}
|
||||
|
||||
InputPersistenceBlock reader(&uncompressedDataBuffer[0], curSavegameInfo.gamedataUncompressedLength, curSavegameInfo.version);
|
||||
|
||||
// Einzelne Engine-Module depersistieren.
|
||||
bool success = true;
|
||||
success &= Kernel::getInstance()->getScript()->unpersist(reader);
|
||||
// Muss unbedingt nach Script passieren. Da sonst die bereits wiederhergestellten Regions per Garbage-Collection gekillt werden.
|
||||
success &= RegionRegistry::instance().unpersist(reader);
|
||||
success &= Kernel::getInstance()->getGfx()->unpersist(reader);
|
||||
success &= Kernel::getInstance()->getSfx()->unpersist(reader);
|
||||
success &= Kernel::getInstance()->getInput()->unpersist(reader);
|
||||
|
||||
delete[] compressedDataBuffer;
|
||||
delete[] uncompressedDataBuffer;
|
||||
delete file;
|
||||
|
||||
if (!success) {
|
||||
error("Unable to unpersist the gamedata from savegame file \"%s\".", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
75
engines/sword25/kernel/persistenceservice.h
Normal file
75
engines/sword25/kernel/persistenceservice.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_PERSISTENCESERVICE_H
|
||||
#define SWORD25_PERSISTENCESERVICE_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class PersistenceService {
|
||||
public:
|
||||
PersistenceService();
|
||||
virtual ~PersistenceService();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Singleton Method
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static PersistenceService &getInstance();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Interface
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint getSlotCount();
|
||||
static Common::Path getSavegameDirectory();
|
||||
|
||||
void reloadSlots();
|
||||
bool isSlotOccupied(uint slotID);
|
||||
bool isSavegameCompatible(uint slotID);
|
||||
int getSavegameVersion(uint slotID);
|
||||
Common::String &getSavegameDescription(uint slotID);
|
||||
Common::String &getSavegameFilename(uint slotID);
|
||||
|
||||
bool saveGame(uint slotID, const Common::String &screenshotFilename);
|
||||
bool loadGame(uint slotID);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
Impl *_impl;
|
||||
};
|
||||
|
||||
void setGameTarget(const char *target);
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
333
engines/sword25/kernel/resmanager.cpp
Normal file
333
engines/sword25/kernel/resmanager.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/sword25.h" // for kDebugResource
|
||||
#include "sword25/kernel/resmanager.h"
|
||||
#include "sword25/kernel/resource.h"
|
||||
#include "sword25/kernel/resservice.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// Sets the amount of resources that are simultaneously loaded.
|
||||
// This needs to be a relatively high number, as all the animation
|
||||
// frames in each scene are loaded as separate resources.
|
||||
// Also, George's walk states are all loaded here (150 files)
|
||||
#define SWORD25_RESOURCECACHE_MIN 400
|
||||
// The maximum number of loaded resources. If more than these resources
|
||||
// are loaded, the resource manager will start purging resources till it
|
||||
// hits the minimum limit above
|
||||
#define SWORD25_RESOURCECACHE_MAX 500
|
||||
|
||||
ResourceManager::~ResourceManager() {
|
||||
// Clear all unlocked resources
|
||||
emptyCache();
|
||||
|
||||
// All remaining resources are not released, so print warnings and release
|
||||
Common::List<Resource *>::iterator iter = _resources.begin();
|
||||
for (; iter != _resources.end(); ++iter) {
|
||||
warning("Resource \"%s\" was not released.", (*iter)->getFileName().c_str());
|
||||
|
||||
// Set the lock count to zero
|
||||
while ((*iter)->getLockCount() > 0) {
|
||||
(*iter)->release();
|
||||
};
|
||||
|
||||
// Delete the resource
|
||||
delete(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a RegisterResourceService. This method is the constructor of
|
||||
* BS_ResourceService, and thus helps all resource services in the ResourceManager list
|
||||
* @param pService Which service
|
||||
*/
|
||||
bool ResourceManager::registerResourceService(ResourceService *pService) {
|
||||
if (!pService) {
|
||||
error("Can't register NULL resource service.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_resourceServices.push_back(pService);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes resources as necessary until the specified memory limit is not being exceeded.
|
||||
*/
|
||||
void ResourceManager::deleteResourcesIfNecessary() {
|
||||
// If enough memory is available, or no resources are loaded, then the function can immediately end
|
||||
if (_resources.size() < SWORD25_RESOURCECACHE_MAX)
|
||||
return;
|
||||
|
||||
// Keep deleting resources until the memory usage of the process falls below the set maximum limit.
|
||||
// The list is processed backwards in order to first release those resources that have been
|
||||
// not been accessed for the longest
|
||||
Common::List<Resource *>::iterator iter = _resources.end();
|
||||
do {
|
||||
--iter;
|
||||
|
||||
// The resource may be released only if it isn't locked
|
||||
if ((*iter)->getLockCount() == 0)
|
||||
iter = deleteResource(*iter);
|
||||
} while (iter != _resources.begin() && _resources.size() >= SWORD25_RESOURCECACHE_MIN);
|
||||
|
||||
// Are we still above the minimum? If yes, then start releasing locked resources
|
||||
// FIXME: This code shouldn't be needed at all, but it seems like there is a bug
|
||||
// in the resource lock code, and resources are not unlocked when changing rooms.
|
||||
// Only image/animation resources are unlocked forcibly, thus this shouldn't have
|
||||
// any impact on the game itself.
|
||||
if (_resources.size() <= SWORD25_RESOURCECACHE_MIN)
|
||||
return;
|
||||
|
||||
iter = _resources.end();
|
||||
do {
|
||||
--iter;
|
||||
|
||||
// Only unlock image/animation resources
|
||||
if ((*iter)->getFileName().hasSuffix(".swf") ||
|
||||
(*iter)->getFileName().hasSuffix(".png")) {
|
||||
|
||||
warning("Forcibly unlocking %s", (*iter)->getFileName().c_str());
|
||||
|
||||
// Forcibly unlock the resource
|
||||
while ((*iter)->getLockCount() > 0)
|
||||
(*iter)->release();
|
||||
|
||||
iter = deleteResource(*iter);
|
||||
}
|
||||
} while (iter != _resources.begin() && _resources.size() >= SWORD25_RESOURCECACHE_MIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all resources that are not locked.
|
||||
*/
|
||||
void ResourceManager::emptyCache() {
|
||||
// Scan through the resource list
|
||||
Common::List<Resource *>::iterator iter = _resources.begin();
|
||||
while (iter != _resources.end()) {
|
||||
if ((*iter)->getLockCount() == 0) {
|
||||
// Delete the resource
|
||||
iter = deleteResource(*iter);
|
||||
} else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::emptyThumbnailCache() {
|
||||
// Scan through the resource list
|
||||
Common::List<Resource *>::iterator iter = _resources.begin();
|
||||
while (iter != _resources.end()) {
|
||||
if ((*iter)->getFileName().hasPrefix("/saves")) {
|
||||
// Unlock the thumbnail
|
||||
while ((*iter)->getLockCount() > 0)
|
||||
(*iter)->release();
|
||||
// Delete the thumbnail
|
||||
iter = deleteResource(*iter);
|
||||
} else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a requested resource. If any error occurs, returns NULL
|
||||
* @param FileName Filename of resource
|
||||
*/
|
||||
Resource *ResourceManager::requestResource(const Common::String &fileName) {
|
||||
// Get the absolute path to the file
|
||||
Common::String uniqueFileName = getUniqueFileName(fileName);
|
||||
if (uniqueFileName.empty())
|
||||
return NULL;
|
||||
|
||||
// Determine whether the resource is already loaded
|
||||
// If the resource is found, it will be placed at the head of the resource list and returned
|
||||
Resource *pResource = getResource(uniqueFileName);
|
||||
if (!pResource)
|
||||
pResource = loadResource(uniqueFileName);
|
||||
if (pResource) {
|
||||
moveToFront(pResource);
|
||||
(pResource)->addReference();
|
||||
return pResource;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef PRECACHE_RESOURCES
|
||||
|
||||
/**
|
||||
* Loads a resource into the cache
|
||||
* @param FileName The filename of the resource to be cached
|
||||
* @param ForceReload Indicates whether the file should be reloaded if it's already in the cache.
|
||||
* This is useful for files that may have changed in the interim
|
||||
*/
|
||||
bool ResourceManager::precacheResource(const Common::String &fileName, bool forceReload) {
|
||||
// Get the absolute path to the file
|
||||
Common::String uniqueFileName = getUniqueFileName(fileName);
|
||||
if (uniqueFileName.empty())
|
||||
return false;
|
||||
|
||||
Resource *resourcePtr = getResource(uniqueFileName);
|
||||
|
||||
if (forceReload && resourcePtr) {
|
||||
if (resourcePtr->getLockCount()) {
|
||||
error("Could not force precaching of \"%s\". The resource is locked.", fileName.c_str());
|
||||
return false;
|
||||
} else {
|
||||
deleteResource(resourcePtr);
|
||||
resourcePtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!resourcePtr && loadResource(uniqueFileName) == NULL) {
|
||||
// This isn't fatal - e.g. it can happen when loading saved games
|
||||
debugC(kDebugResource, "Could not precache \"%s\",", fileName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Moves a resource to the top of the resource list
|
||||
* @param pResource The resource
|
||||
*/
|
||||
void ResourceManager::moveToFront(Resource *pResource) {
|
||||
// Erase the resource from it's current position
|
||||
_resources.erase(pResource->_iterator);
|
||||
// Re-add the resource at the front of the list
|
||||
_resources.push_front(pResource);
|
||||
// Reset the resource iterator to the repositioned item
|
||||
pResource->_iterator = _resources.begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a resource and updates the m_UsedMemory total
|
||||
*
|
||||
* The resource must not already be loaded
|
||||
* @param FileName The unique filename of the resource to be loaded
|
||||
*/
|
||||
Resource *ResourceManager::loadResource(const Common::String &fileName) {
|
||||
// ResourceService finden, der die Resource laden kann.
|
||||
for (uint i = 0; i < _resourceServices.size(); ++i) {
|
||||
if (_resourceServices[i]->canLoadResource(fileName)) {
|
||||
// If more memory is desired, memory must be released
|
||||
deleteResourcesIfNecessary();
|
||||
|
||||
// Load the resource
|
||||
Resource *pResource = _resourceServices[i]->loadResource(fileName);
|
||||
if (!pResource) {
|
||||
error("Responsible service could not load resource \"%s\".", fileName.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Add the resource to the front of the list
|
||||
_resources.push_front(pResource);
|
||||
pResource->_iterator = _resources.begin();
|
||||
|
||||
// Also store the resource in the hash table for quick lookup
|
||||
_resourceHashMap[pResource->getFileName()] = pResource;
|
||||
|
||||
return pResource;
|
||||
}
|
||||
}
|
||||
|
||||
// This isn't fatal - e.g. it can happen when loading saved games
|
||||
debugC(kDebugResource, "Could not find a service that can load \"%s\".", fileName.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full path of a given resource filename.
|
||||
* It will return an empty string if a path could not be created.
|
||||
*/
|
||||
Common::String ResourceManager::getUniqueFileName(const Common::String &fileName) const {
|
||||
// Get a pointer to the package manager
|
||||
PackageManager *pPackage = (PackageManager *)_kernelPtr->getPackage();
|
||||
if (!pPackage) {
|
||||
error("Could not get package manager.");
|
||||
return Common::String();
|
||||
}
|
||||
|
||||
// Absoluten Pfad der Datei bekommen und somit die Eindeutigkeit des Dateinamens sicherstellen
|
||||
Common::String uniquefileName = pPackage->getAbsolutePath(fileName);
|
||||
if (uniquefileName.empty())
|
||||
error("Could not create absolute file name for \"%s\".", fileName.c_str());
|
||||
|
||||
return uniquefileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a resource, removes it from the lists, and updates m_UsedMemory
|
||||
*/
|
||||
Common::List<Resource *>::iterator ResourceManager::deleteResource(Resource *pResource) {
|
||||
// Remove the resource from the hash table
|
||||
_resourceHashMap.erase(pResource->_fileName);
|
||||
|
||||
// Delete the resource from the resource list
|
||||
Common::List<Resource *>::iterator result = _resources.erase(pResource->_iterator);
|
||||
|
||||
// Delete the resource
|
||||
delete pResource;
|
||||
|
||||
// Return the iterator
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to a loaded resource. If any error occurs, NULL will be returned.
|
||||
* @param UniquefileName The absolute path and filename
|
||||
*/
|
||||
Resource *ResourceManager::getResource(const Common::String &uniquefileName) const {
|
||||
// Determine whether the resource is already loaded
|
||||
ResMap::iterator it = _resourceHashMap.find(uniquefileName);
|
||||
if (it != _resourceHashMap.end())
|
||||
return it->_value;
|
||||
|
||||
// Resource was not found, i.e. has not yet been loaded.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the names of all currently locked resources to the log file
|
||||
*/
|
||||
void ResourceManager::dumpLockedResources() {
|
||||
for (Common::List<Resource *>::iterator iter = _resources.begin(); iter != _resources.end(); ++iter) {
|
||||
if ((*iter)->getLockCount() > 0) {
|
||||
debugC(kDebugResource, "%s", (*iter)->getFileName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
145
engines/sword25/kernel/resmanager.h
Normal file
145
engines/sword25/kernel/resmanager.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RESOURCEMANAGER_H
|
||||
#define SWORD25_RESOURCEMANAGER_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
//#define PRECACHE_RESOURCES
|
||||
|
||||
class ResourceService;
|
||||
class Resource;
|
||||
class Kernel;
|
||||
|
||||
class ResourceManager {
|
||||
friend class Kernel;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns a requested resource. If any error occurs, returns NULL
|
||||
* @param FileName Filename of resource
|
||||
*/
|
||||
Resource *requestResource(const Common::String &fileName);
|
||||
|
||||
#ifdef PRECACHE_RESOURCES
|
||||
/**
|
||||
* Loads a resource into the cache
|
||||
* @param FileName The filename of the resource to be cached
|
||||
* @param ForceReload Indicates whether the file should be reloaded if it's already in the cache.
|
||||
* This is useful for files that may have changed in the interim
|
||||
*/
|
||||
bool precacheResource(const Common::String &fileName, bool forceReload = false);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Registers a RegisterResourceService. This method is the constructor of
|
||||
* BS_ResourceService, and thus helps all resource services in the ResourceManager list
|
||||
* @param pService Which service
|
||||
*/
|
||||
bool registerResourceService(ResourceService *pService);
|
||||
|
||||
/**
|
||||
* Releases all resources that are not locked.
|
||||
**/
|
||||
void emptyCache();
|
||||
|
||||
/**
|
||||
* Removes all the savegame thumbnails from the cache
|
||||
**/
|
||||
void emptyThumbnailCache();
|
||||
|
||||
/**
|
||||
* Writes the names of all currently locked resources to the log file
|
||||
*/
|
||||
void dumpLockedResources();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Creates a new resource manager
|
||||
* Only the BS_Kernel class can generate copies this class. Thus, the constructor is private
|
||||
*/
|
||||
ResourceManager(Kernel *pKernel) :
|
||||
_kernelPtr(pKernel)
|
||||
{}
|
||||
virtual ~ResourceManager();
|
||||
|
||||
/**
|
||||
* Moves a resource to the top of the resource list
|
||||
* @param pResource The resource
|
||||
*/
|
||||
void moveToFront(Resource *pResource);
|
||||
|
||||
/**
|
||||
* Loads a resource and updates the m_UsedMemory total
|
||||
*
|
||||
* The resource must not already be loaded
|
||||
* @param FileName The unique filename of the resource to be loaded
|
||||
*/
|
||||
Resource *loadResource(const Common::String &fileName);
|
||||
|
||||
/**
|
||||
* Returns the full path of a given resource filename.
|
||||
* It will return an empty string if a path could not be created.
|
||||
*/
|
||||
Common::String getUniqueFileName(const Common::String &fileName) const;
|
||||
|
||||
/**
|
||||
* Deletes a resource, removes it from the lists, and updates m_UsedMemory
|
||||
*/
|
||||
Common::List<Resource *>::iterator deleteResource(Resource *pResource);
|
||||
|
||||
/**
|
||||
* Returns a pointer to a loaded resource. If any error occurs, NULL will be returned.
|
||||
* @param UniqueFileName The absolute path and filename
|
||||
*/
|
||||
Resource *getResource(const Common::String &uniqueFileName) const;
|
||||
|
||||
/**
|
||||
* Deletes resources as necessary until the specified memory limit is not being exceeded.
|
||||
*/
|
||||
void deleteResourcesIfNecessary();
|
||||
|
||||
Kernel *_kernelPtr;
|
||||
Common::Array<ResourceService *> _resourceServices;
|
||||
Common::List<Resource *> _resources;
|
||||
typedef Common::HashMap<Common::String, Resource *> ResMap;
|
||||
ResMap _resourceHashMap;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
53
engines/sword25/kernel/resource.cpp
Normal file
53
engines/sword25/kernel/resource.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/resource.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/package/packagemanager.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Resource::Resource(const Common::String &fileName, RESOURCE_TYPES type) :
|
||||
_type(type),
|
||||
_refCount(0) {
|
||||
PackageManager *pPM = Kernel::getInstance()->getPackage();
|
||||
assert(pPM);
|
||||
|
||||
_fileName = pPM->getAbsolutePath(fileName);
|
||||
}
|
||||
|
||||
void Resource::release() {
|
||||
if (_refCount) {
|
||||
--_refCount;
|
||||
} else
|
||||
warning("Released unlocked resource \"%s\".", _fileName.c_str());
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
106
engines/sword25/kernel/resource.h
Normal file
106
engines/sword25/kernel/resource.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RESOURCE_H
|
||||
#define SWORD25_RESOURCE_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/str.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
class ResourceManager;
|
||||
|
||||
class Resource {
|
||||
friend class ResourceManager;
|
||||
|
||||
public:
|
||||
enum RESOURCE_TYPES {
|
||||
TYPE_UNKNOWN,
|
||||
TYPE_BITMAP,
|
||||
TYPE_ANIMATION,
|
||||
TYPE_SOUND,
|
||||
TYPE_FONT
|
||||
};
|
||||
|
||||
Resource(const Common::String &uniqueFileName, RESOURCE_TYPES type);
|
||||
|
||||
/**
|
||||
* Prevents the resource from being released.
|
||||
* @remarks This method allows a resource to be locked multiple times.
|
||||
**/
|
||||
void addReference() {
|
||||
++_refCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a previous lock
|
||||
* @remarks The resource can still be released more times than it was 'locked', although it is
|
||||
* not recommended.
|
||||
**/
|
||||
void release();
|
||||
|
||||
/**
|
||||
* Returns the current lock count for the resource
|
||||
* @return The current lock count
|
||||
**/
|
||||
int getLockCount() const {
|
||||
return _refCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute path of the given resource
|
||||
*/
|
||||
const Common::String &getFileName() const {
|
||||
return _fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a resource's type
|
||||
*/
|
||||
uint getType() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~Resource() {}
|
||||
|
||||
private:
|
||||
Common::String _fileName; ///< The absolute filename
|
||||
uint _refCount; ///< The number of locks
|
||||
uint _type; ///< The type of the resource
|
||||
Common::List<Resource *>::iterator _iterator; ///< Points to the resource position in the LRU list
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
69
engines/sword25/kernel/resservice.h
Normal file
69
engines/sword25/kernel/resservice.h
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_RESOURCESERVICE_H
|
||||
#define SWORD25_RESOURCESERVICE_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/service.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/resmanager.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Resource;
|
||||
|
||||
class ResourceService : public Service {
|
||||
public:
|
||||
ResourceService(Kernel *pKernel) : Service(pKernel) {
|
||||
ResourceManager *pResource = pKernel->getResourceManager();
|
||||
pResource->registerResourceService(this);
|
||||
}
|
||||
|
||||
~ResourceService() override {}
|
||||
|
||||
/**
|
||||
* Loads a resource
|
||||
* @return Returns the resource if successful, otherwise NULL
|
||||
*/
|
||||
virtual Resource *loadResource(const Common::String &fileName) = 0;
|
||||
|
||||
/**
|
||||
* Checks whether the given name can be loaded by the resource service
|
||||
* @param FileName Dateiname
|
||||
* @return Returns true if the resource can be loaded.
|
||||
*/
|
||||
virtual bool canLoadResource(const Common::String &fileName) = 0;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
71
engines/sword25/kernel/service.h
Normal file
71
engines/sword25/kernel/service.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* BS_Service
|
||||
* -------------
|
||||
* This is the base class for all engine services.
|
||||
* A serivce is an essential part of the engine, ex. the graphics system.
|
||||
* This was intended to allow, for example, different plug in modules for
|
||||
* different kinds of hardware and/or systems.
|
||||
* The services are created at runtime via the kernel method NewService and NEVER with new.
|
||||
*
|
||||
* Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_SERVICE_H
|
||||
#define SWORD25_SERVICE_H
|
||||
|
||||
// Includes
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// Klassendefinition
|
||||
class Kernel;
|
||||
|
||||
class Service {
|
||||
private:
|
||||
Kernel *_pKernel;
|
||||
|
||||
protected:
|
||||
Service(Kernel *pKernel) : _pKernel(pKernel) {}
|
||||
|
||||
Kernel *GetKernel() const {
|
||||
return _pKernel;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~Service() {}
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
56
engines/sword25/math/geometry.h
Normal file
56
engines/sword25/math/geometry.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_GEOMETRY_H
|
||||
#define SWORD25_GEOMETRY_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/service.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Kernel;
|
||||
|
||||
class Geometry : public Service {
|
||||
public:
|
||||
Geometry(Kernel *pKernel) : Service(pKernel) {
|
||||
if (!registerScriptBindings())
|
||||
error("Script bindings could not be registered.");
|
||||
}
|
||||
|
||||
~Geometry() override {}
|
||||
|
||||
private:
|
||||
bool registerScriptBindings();
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
467
engines/sword25/math/geometry_script.cpp
Normal file
467
engines/sword25/math/geometry_script.cpp
Normal file
@@ -0,0 +1,467 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/array.h"
|
||||
#include "sword25/gfx/graphicengine.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/script/script.h"
|
||||
#include "sword25/script/luabindhelper.h"
|
||||
|
||||
#include "sword25/math/geometry.h"
|
||||
#include "sword25/math/region.h"
|
||||
#include "sword25/math/regionregistry.h"
|
||||
#include "sword25/math/walkregion.h"
|
||||
#include "sword25/math/vertex.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
// These strings are defined as #defines to enable compile-time string composition
|
||||
#define REGION_CLASS_NAME "Geo.Region"
|
||||
#define WALKREGION_CLASS_NAME "Geo.WalkRegion"
|
||||
|
||||
static void newUintUserData(lua_State *L, uint value) {
|
||||
void *userData = lua_newuserdata(L, sizeof(value));
|
||||
memcpy(userData, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static bool isValidPolygonDefinition(lua_State *L) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// Ensure that we actually consider a table
|
||||
if (!lua_istable(L, -1)) {
|
||||
luaL_error(L, "Invalid polygon definition. Unexpected type, \"table\" needed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int tableSize = luaL_getn(L, -1);
|
||||
|
||||
// Make sure that there are at least three Vertecies
|
||||
if (tableSize < 6) {
|
||||
luaL_error(L, "Invalid polygon definition. At least three vertecies needed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure that the number of table elements is divisible by two.
|
||||
// Since any two elements is a vertex, an odd number of elements is not allowed
|
||||
if ((tableSize % 2) != 0) {
|
||||
luaL_error(L, "Invalid polygon definition. Even number of table elements needed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that all elements in the table are of type Number
|
||||
for (int i = 1; i <= tableSize; i++) {
|
||||
lua_rawgeti(L, -1, i);
|
||||
if (!lua_isnumber(L, -1)) {
|
||||
luaL_error(L, "Invalid polygon definition. All table elements have to be numbers.");
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void tablePolygonToPolygon(lua_State *L, Polygon &polygon) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// Ensure that a valid polygon definition is on the stack.
|
||||
// It is not necessary to catch the return value, since all errors are reported on luaL_error
|
||||
// End script.
|
||||
isValidPolygonDefinition(L);
|
||||
|
||||
int vertexCount = luaL_getn(L, -1) / 2;
|
||||
|
||||
// Memory is reserved for Vertecies
|
||||
Common::Array<Vertex> vertices;
|
||||
vertices.reserve(vertexCount);
|
||||
|
||||
// Create Vertecies
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
// X Value
|
||||
lua_rawgeti(L, -1, (i * 2) + 1);
|
||||
int X = static_cast<int>(lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Y Value
|
||||
lua_rawgeti(L, -1, (i * 2) + 2);
|
||||
int Y = static_cast<int>(lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Vertex
|
||||
vertices.push_back(Vertex(X, Y));
|
||||
}
|
||||
assert((int)vertices.size() == vertexCount);
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
// Create polygon
|
||||
polygon.init(vertexCount, &vertices[0]);
|
||||
}
|
||||
|
||||
static uint tableRegionToRegion(lua_State *L, const char *className) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// You can define a region in Lua in two ways:
|
||||
// 1. A table that defines a polygon (polgon = table with numbers, which define
|
||||
// two consecutive numbers per vertex)
|
||||
// 2. A table containing more polygon definitions
|
||||
// Then the first polygon is the contour of the region, and the following are holes
|
||||
// defined in the first polygon.
|
||||
|
||||
// It may be passed only one parameter, and this must be a table
|
||||
if (lua_gettop(L) != 1 || !lua_istable(L, -1)) {
|
||||
luaL_error(L, "First and only parameter has to be of type \"table\".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint regionHandle = 0;
|
||||
if (!strcmp(className, REGION_CLASS_NAME)) {
|
||||
regionHandle = Region::create(Region::RT_REGION);
|
||||
} else if (!strcmp(className, WALKREGION_CLASS_NAME)) {
|
||||
regionHandle = WalkRegion::create(Region::RT_WALKREGION);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
assert(regionHandle);
|
||||
|
||||
// If the first element of the parameter is a number, then case 1 is accepted
|
||||
// If the first element of the parameter is a table, then case 2 is accepted
|
||||
// If the first element of the parameter has a different type, there is an error
|
||||
lua_rawgeti(L, -1, 1);
|
||||
int firstElementType = lua_type(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
switch (firstElementType) {
|
||||
case LUA_TNUMBER: {
|
||||
Polygon polygon;
|
||||
tablePolygonToPolygon(L, polygon);
|
||||
RegionRegistry::instance().resolveHandle(regionHandle)->init(polygon);
|
||||
}
|
||||
break;
|
||||
|
||||
case LUA_TTABLE: {
|
||||
lua_rawgeti(L, -1, 1);
|
||||
Polygon polygon;
|
||||
tablePolygonToPolygon(L, polygon);
|
||||
lua_pop(L, 1);
|
||||
|
||||
int polygonCount = luaL_getn(L, -1);
|
||||
if (polygonCount == 1)
|
||||
RegionRegistry::instance().resolveHandle(regionHandle)->init(polygon);
|
||||
else {
|
||||
Common::Array<Polygon> holes;
|
||||
holes.reserve(polygonCount - 1);
|
||||
|
||||
for (int i = 2; i <= polygonCount; i++) {
|
||||
lua_rawgeti(L, -1, i);
|
||||
holes.resize(holes.size() + 1);
|
||||
tablePolygonToPolygon(L, holes.back());
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
assert((int)holes.size() == polygonCount - 1);
|
||||
|
||||
RegionRegistry::instance().resolveHandle(regionHandle)->init(polygon, &holes);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
luaL_error(L, "Illegal region definition.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return regionHandle;
|
||||
}
|
||||
|
||||
static void newUserdataRegion(lua_State *L, const char *className) {
|
||||
// Region due to the Lua code to create
|
||||
// Any errors that occur will be intercepted to the luaL_error
|
||||
uint regionHandle = tableRegionToRegion(L, className);
|
||||
assert(regionHandle);
|
||||
|
||||
newUintUserData(L, regionHandle);
|
||||
// luaL_getmetatable(L, className);
|
||||
LuaBindhelper::getMetatable(L, className);
|
||||
assert(!lua_isnil(L, -1));
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
static int newRegion(lua_State *L) {
|
||||
newUserdataRegion(L, REGION_CLASS_NAME);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int newWalkRegion(lua_State *L) {
|
||||
newUserdataRegion(L, WALKREGION_CLASS_NAME);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *GEO_LIBRARY_NAME = "Geo";
|
||||
|
||||
static const luaL_reg GEO_FUNCTIONS[] = {
|
||||
{"NewRegion", newRegion},
|
||||
{"NewWalkRegion", newWalkRegion},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static Region *checkRegion(lua_State *L) {
|
||||
// The first parameter must be of type 'userdata', and the Metatable class Geo.Region or Geo.WalkRegion
|
||||
uint *regionHandlePtr = reinterpret_cast<uint *>(LuaBindhelper::my_checkudata(L, 1, REGION_CLASS_NAME));
|
||||
if (regionHandlePtr != 0 ||
|
||||
(regionHandlePtr = reinterpret_cast<uint *>(LuaBindhelper::my_checkudata(L, 1, WALKREGION_CLASS_NAME))) != 0) {
|
||||
return RegionRegistry::instance().resolveHandle(*regionHandlePtr);
|
||||
} else {
|
||||
luaL_argcheck(L, 0, 1, "'" REGION_CLASS_NAME "' expected");
|
||||
}
|
||||
|
||||
// Compilation fix. Execution never reaches this point
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r_isValid(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
lua_pushbooleancpp(L, pR->isValid());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int r_getX(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
lua_pushnumber(L, pR->getPosX());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int r_getY(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
lua_pushnumber(L, pR->getPosY());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int r_getPos(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
Vertex::vertexToLuaVertex(L, pR->getPosition());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int r_isPointInRegion(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
Vertex vertex;
|
||||
Vertex::luaVertexToVertex(L, 2, vertex);
|
||||
lua_pushbooleancpp(L, pR->isPointInRegion(vertex));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int r_setPos(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
Vertex vertex;
|
||||
Vertex::luaVertexToVertex(L, 2, vertex);
|
||||
pR->setPos(vertex.x, vertex.y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r_setX(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
pR->setPosX(static_cast<int>(luaL_checknumber(L, 2)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r_setY(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
pR->setPosY(static_cast<int>(luaL_checknumber(L, 2)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drawPolygon(const Polygon &polygon, uint color, const Vertex &offset) {
|
||||
GraphicEngine *pGE = Kernel::getInstance()->getGfx();
|
||||
assert(pGE);
|
||||
|
||||
for (int i = 0; i < polygon.vertexCount - 1; i++)
|
||||
pGE->drawDebugLine(polygon.vertices[i] + offset, polygon.vertices[i + 1] + offset, color);
|
||||
|
||||
pGE->drawDebugLine(polygon.vertices[polygon.vertexCount - 1] + offset, polygon.vertices[0] + offset, color);
|
||||
}
|
||||
|
||||
static void drawRegion(const Region ®ion, uint color, const Vertex &offset) {
|
||||
drawPolygon(region.getContour(), color, offset);
|
||||
for (int i = 0; i < region.getHoleCount(); i++)
|
||||
drawPolygon(region.getHole(i), color, offset);
|
||||
}
|
||||
|
||||
static int r_draw(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
|
||||
switch (lua_gettop(L)) {
|
||||
case 3: {
|
||||
Vertex offset;
|
||||
Vertex::luaVertexToVertex(L, 3, offset);
|
||||
drawRegion(*pR, GraphicEngine::luaColorToARGBColor(L, 2), offset);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
drawRegion(*pR, GraphicEngine::luaColorToARGBColor(L, 2), Vertex(0, 0));
|
||||
break;
|
||||
|
||||
default:
|
||||
drawRegion(*pR, BS_RGB(255, 255, 255), Vertex(0, 0));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r_getCentroid(lua_State *L) {
|
||||
Region *RPtr = checkRegion(L);
|
||||
assert(RPtr);
|
||||
|
||||
Vertex::vertexToLuaVertex(L, RPtr->getCentroid());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int r_delete(lua_State *L) {
|
||||
Region *pR = checkRegion(L);
|
||||
assert(pR);
|
||||
delete pR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_reg REGION_METHODS[] = {
|
||||
{"SetPos", r_setPos},
|
||||
{"SetX", r_setX},
|
||||
{"SetY", r_setY},
|
||||
{"GetPos", r_getPos},
|
||||
{"IsPointInRegion", r_isPointInRegion},
|
||||
{"GetX", r_getX},
|
||||
{"GetY", r_getY},
|
||||
{"IsValid", r_isValid},
|
||||
{"Draw", r_draw},
|
||||
{"GetCentroid", r_getCentroid},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static WalkRegion *checkWalkRegion(lua_State *L) {
|
||||
// The first parameter must be of type 'userdate', and the Metatable class Geo.WalkRegion
|
||||
uint regionHandle;
|
||||
if ((regionHandle = *reinterpret_cast<uint *>(LuaBindhelper::my_checkudata(L, 1, WALKREGION_CLASS_NAME))) != 0) {
|
||||
return reinterpret_cast<WalkRegion *>(RegionRegistry::instance().resolveHandle(regionHandle));
|
||||
} else {
|
||||
luaL_argcheck(L, 0, 1, "'" WALKREGION_CLASS_NAME "' expected");
|
||||
}
|
||||
|
||||
// Compilation fix. Execution never reaches this point
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wr_getPath(lua_State *L) {
|
||||
WalkRegion *pWR = checkWalkRegion(L);
|
||||
assert(pWR);
|
||||
|
||||
Vertex start;
|
||||
Vertex::luaVertexToVertex(L, 2, start);
|
||||
Vertex end;
|
||||
Vertex::luaVertexToVertex(L, 3, end);
|
||||
BS_Path path;
|
||||
if (pWR->queryPath(start, end, path)) {
|
||||
lua_newtable(L);
|
||||
BS_Path::const_iterator it = path.begin();
|
||||
for (; it != path.end(); it++) {
|
||||
lua_pushnumber(L, (it - path.begin()) + 1);
|
||||
Vertex::vertexToLuaVertex(L, *it);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
} else
|
||||
lua_pushnil(L);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_reg WALKREGION_METHODS[] = {
|
||||
{"GetPath", wr_getPath},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
bool Geometry::registerScriptBindings() {
|
||||
Kernel *pKernel = Kernel::getInstance();
|
||||
assert(pKernel);
|
||||
ScriptEngine *pScript = pKernel->getScript();
|
||||
assert(pScript);
|
||||
lua_State *L = static_cast< lua_State *>(pScript->getScriptObject());
|
||||
assert(L);
|
||||
|
||||
if (!LuaBindhelper::addMethodsToClass(L, REGION_CLASS_NAME, REGION_METHODS)) return false;
|
||||
if (!LuaBindhelper::addMethodsToClass(L, WALKREGION_CLASS_NAME, REGION_METHODS)) return false;
|
||||
if (!LuaBindhelper::addMethodsToClass(L, WALKREGION_CLASS_NAME, WALKREGION_METHODS)) return false;
|
||||
|
||||
if (!LuaBindhelper::setClassGCHandler(L, REGION_CLASS_NAME, r_delete)) return false;
|
||||
if (!LuaBindhelper::setClassGCHandler(L, WALKREGION_CLASS_NAME, r_delete)) return false;
|
||||
|
||||
if (!LuaBindhelper::addFunctionsToLib(L, GEO_LIBRARY_NAME, GEO_FUNCTIONS)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
194
engines/sword25/math/line.h
Normal file
194
engines/sword25/math/line.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
BS_Line
|
||||
-------
|
||||
This class contains only static methods, which have to do with straight line
|
||||
segments. There is no real straight line segment class. Calculations will be
|
||||
used with polygons, and it is important the process of starting and selecting
|
||||
endpoints of lines is dynamic. This would prhobit a polygon from a set
|
||||
being formed by fixed line segments
|
||||
|
||||
Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_LINE_H
|
||||
#define SWORD25_LINE_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Line {
|
||||
public:
|
||||
/**
|
||||
* Determines whether a piont is left of a line
|
||||
* @param a The start point of a line
|
||||
* @param b The end point of a line
|
||||
* @param c The test point
|
||||
* @return Returns true if the point is to the left of the line.
|
||||
* If the point is to the right of the line or on the line, false is returned.
|
||||
*/
|
||||
static bool isVertexLeft(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
return triangleArea2(a, b, c) > 0;
|
||||
}
|
||||
|
||||
static bool isVertexLeftOn(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
return triangleArea2(a, b, c) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a piont is right of a line
|
||||
* @param a The start point of a line
|
||||
* @param b The end point of a line
|
||||
* @param c The test point
|
||||
* @return Returns true if the point is to the right of the line.
|
||||
* If the point is to the right of the line or on the line, false is returned.
|
||||
*/
|
||||
static bool isVertexRight(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
return triangleArea2(a, b, c) < 0;
|
||||
}
|
||||
|
||||
static bool isVertexRightOn(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
return triangleArea2(a, b, c) <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a piont is on a line
|
||||
* @param a The start point of a line
|
||||
* @param b The end point of a line
|
||||
* @param c The test point
|
||||
* @return Returns true if the point is on the line, false otherwise.
|
||||
*/
|
||||
static bool isVertexOn(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
return triangleArea2(a, b, c) == 0;
|
||||
}
|
||||
|
||||
enum VERTEX_CLASSIFICATION {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
ON
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines where a point is relative to a line.
|
||||
* @param a The start point of a line
|
||||
* @param b The end point of a line
|
||||
* @param c The test point
|
||||
* @return LEFT is returned if the point is to the left of the line.
|
||||
* RIGHT is returned if the point is to the right of the line.
|
||||
* ON is returned if the point is on the line.
|
||||
*/
|
||||
static VERTEX_CLASSIFICATION classifyVertexToLine(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
int area = triangleArea2(a, b, c);
|
||||
if (area > 0) return LEFT;
|
||||
if (area < 0) return RIGHT;
|
||||
return ON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether two lines intersect
|
||||
* @param a The start point of the first line
|
||||
* @param b The end point of the first line
|
||||
* @param c The start point of the second line
|
||||
* @param d The end point of the second line
|
||||
* @remark In cases where a line only touches the other, false is returned (improper intersection)
|
||||
*/
|
||||
static bool doesIntersectProperly(const Vertex &a, const Vertex &b, const Vertex &c, const Vertex &d) {
|
||||
VERTEX_CLASSIFICATION class1 = classifyVertexToLine(a, b, c);
|
||||
VERTEX_CLASSIFICATION class2 = classifyVertexToLine(a, b, d);
|
||||
VERTEX_CLASSIFICATION class3 = classifyVertexToLine(c, d, a);
|
||||
VERTEX_CLASSIFICATION class4 = classifyVertexToLine(c, d, b);
|
||||
|
||||
if (class1 == ON || class2 == ON || class3 == ON || class4 == ON) return false;
|
||||
|
||||
return ((class1 == LEFT) ^(class2 == LEFT)) && ((class3 == LEFT) ^(class4 == LEFT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a point is on a line segment
|
||||
* @param a The start point of a line
|
||||
* @param b The end point of a line
|
||||
* @param c The test point
|
||||
*/
|
||||
static bool isOnLine(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
// The items must all be Collinear, otherwise don't bothering testing the point
|
||||
if (triangleArea2(a, b, c) != 0) return false;
|
||||
|
||||
// If the line segment is not vertical, check on the x-axis, otherwise the y-axis
|
||||
if (a.x != b.x) {
|
||||
return ((a.x <= c.x) &&
|
||||
(c.x <= b.x)) ||
|
||||
((a.x >= c.x) &&
|
||||
(c.x >= b.x));
|
||||
} else {
|
||||
return ((a.y <= c.y) &&
|
||||
(c.y <= b.y)) ||
|
||||
((a.y >= c.y) &&
|
||||
(c.y >= b.y));
|
||||
}
|
||||
}
|
||||
|
||||
static bool isOnLineStrict(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
// The items must all be Collinear, otherwise don't bothering testing the point
|
||||
if (triangleArea2(a, b, c) != 0) return false;
|
||||
|
||||
// If the line segment is not vertical, check on the x-axis, otherwise the y-axis
|
||||
if (a.x != b.x) {
|
||||
return ((a.x < c.x) &&
|
||||
(c.x < b.x)) ||
|
||||
((a.x > c.x) &&
|
||||
(c.x > b.x));
|
||||
} else {
|
||||
return ((a.y < c.y) &&
|
||||
(c.y < b.y)) ||
|
||||
((a.y > c.y) &&
|
||||
(c.y > b.y));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Return double the size of the triangle defined by the three passed points.
|
||||
*
|
||||
* The result is positive if the points are arrange counterclockwise,
|
||||
* and negative if they are arranged counter-clockwise.
|
||||
*/
|
||||
static int triangleArea2(const Vertex &a, const Vertex &b, const Vertex &c) {
|
||||
return a.x * b.y - a.y * b.x +
|
||||
a.y * c.x - a.x * c.y +
|
||||
b.x * c.y - c.x * b.y;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
419
engines/sword25/math/polygon.cpp
Normal file
419
engines/sword25/math/polygon.cpp
Normal file
@@ -0,0 +1,419 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
|
||||
#include "sword25/math/polygon.h"
|
||||
#include "sword25/math/line.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Polygon::Polygon() : vertexCount(0), vertices(NULL) {
|
||||
}
|
||||
|
||||
Polygon::Polygon(int vertexCount_, const Vertex *vertices_) : vertexCount(0), vertices(NULL) {
|
||||
init(vertexCount_, vertices_);
|
||||
}
|
||||
|
||||
Polygon::Polygon(const Polygon &other) : Persistable(other), vertexCount(0), vertices(NULL) {
|
||||
init(other.vertexCount, other.vertices);
|
||||
}
|
||||
|
||||
Polygon::Polygon(InputPersistenceBlock &Reader) : vertexCount(0), vertices(NULL) {
|
||||
unpersist(Reader);
|
||||
}
|
||||
|
||||
Polygon::~Polygon() {
|
||||
delete[] vertices;
|
||||
}
|
||||
|
||||
bool Polygon::init(int vertexCount_, const Vertex *vertices_) {
|
||||
// Rember the old obstate to restore it if an error occurs whilst initializing it with the new data
|
||||
int oldvertexCount = this->vertexCount;
|
||||
Vertex *oldvertices = this->vertices;
|
||||
|
||||
this->vertexCount = vertexCount_;
|
||||
this->vertices = new Vertex[vertexCount_ + 1];
|
||||
memcpy(this->vertices, vertices_, sizeof(Vertex) * vertexCount_);
|
||||
// TODO:
|
||||
// Duplicate and remove redundant vertecies (Superflous = 3 co-linear verts)
|
||||
// _WeedRepeatedvertices();
|
||||
// The first vertex is repeated at the end of the vertex array; this simplifies
|
||||
// some algorithms, running through the edges and thus can save the overflow control.
|
||||
this->vertices[vertexCount_] = this->vertices[0];
|
||||
|
||||
// If the polygon is self-intersecting, the object state is restore, and an error signalled
|
||||
if (checkForSelfIntersection()) {
|
||||
delete[] this->vertices;
|
||||
this->vertices = oldvertices;
|
||||
this->vertexCount = oldvertexCount;
|
||||
|
||||
// BS_LOG_ERROR("POLYGON: Tried to create a self-intersecting polygon.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Release old vertex list
|
||||
delete[] oldvertices;
|
||||
|
||||
// Calculate properties of the polygon
|
||||
_isCW = computeIsCW();
|
||||
_centroid = computeCentroid();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Review the order of the vertices
|
||||
// ---------------------------------
|
||||
|
||||
bool Polygon::isCW() const {
|
||||
return _isCW;
|
||||
}
|
||||
bool Polygon::computeIsCW() const {
|
||||
if (vertexCount) {
|
||||
// Find the vertex on extreme bottom right
|
||||
int v2Index = findLRVertexIndex();
|
||||
|
||||
// Find the vertex before and after it
|
||||
int v1Index = (v2Index + (vertexCount - 1)) % vertexCount;
|
||||
int v3Index = (v2Index + 1) % vertexCount;
|
||||
|
||||
// Cross product form
|
||||
// If the cross product of the vertex lying fartherest bottom left is positive,
|
||||
// the vertecies arranged in a clockwise order. Otherwise counter-clockwise
|
||||
if (crossProduct(vertices[v1Index], vertices[v2Index], vertices[v3Index]) >= 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Polygon::findLRVertexIndex() const {
|
||||
if (vertexCount) {
|
||||
int curIndex = 0;
|
||||
int maxX = vertices[0].x;
|
||||
int maxY = vertices[0].y;
|
||||
|
||||
for (int i = 1; i < vertexCount; i++) {
|
||||
if (vertices[i].y > maxY ||
|
||||
(vertices[i].y == maxY && vertices[i].x > maxX)) {
|
||||
maxX = vertices[i].x;
|
||||
maxY = vertices[i].y;
|
||||
curIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return curIndex;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
// Make a determine vertex order
|
||||
// -----------------------------
|
||||
|
||||
void Polygon::ensureCWOrder() {
|
||||
if (!isCW())
|
||||
reverseVertexOrder();
|
||||
}
|
||||
// Reverse the order of vertecies
|
||||
// ------------------------------
|
||||
|
||||
void Polygon::reverseVertexOrder() {
|
||||
// vertices are exchanged in pairs, until the list has been completely reversed
|
||||
for (int i = 0; i < vertexCount / 2; i++) {
|
||||
Vertex tempVertex = vertices[i];
|
||||
vertices[i] = vertices[vertexCount - i - 1];
|
||||
vertices[vertexCount - i - 1] = tempVertex;
|
||||
}
|
||||
|
||||
// Vertexordnung neu berechnen.
|
||||
_isCW = computeIsCW();
|
||||
}
|
||||
|
||||
// Cross Product
|
||||
// -------------
|
||||
|
||||
int Polygon::crossProduct(const Vertex &v1, const Vertex &v2, const Vertex &v3) const {
|
||||
return (v2.x - v1.x) * (v3.y - v2.y) -
|
||||
(v2.y - v1.y) * (v3.x - v2.x);
|
||||
}
|
||||
// Check for self-intersections
|
||||
// ----------------------------
|
||||
|
||||
bool Polygon::checkForSelfIntersection() const {
|
||||
// TODO: Finish this
|
||||
/*
|
||||
float AngleSum = 0.0f;
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
int j = (i + 1) % vertexCount;
|
||||
int k = (i + 2) % vertexCount;
|
||||
|
||||
float Dot = DotProduct(vertices[i], vertices[j], vertices[k]);
|
||||
|
||||
// Skalarproduct normalisieren
|
||||
float Length1 = sqrt((vertices[i].x - vertices[j].x) * (vertices[i].x - vertices[j].x) +
|
||||
(vertices[i].y - vertices[j].y) * (vertices[i].y - vertices[j].y));
|
||||
float Length2 = sqrt((vertices[k].x - vertices[j].x) * (vertices[k].x - vertices[j].x) +
|
||||
(vertices[k].y - vertices[j].y) * (vertices[k].y - vertices[j].y));
|
||||
float Norm = Length1 * Length2;
|
||||
|
||||
if (Norm > 0.0f) {
|
||||
Dot /= Norm;
|
||||
AngleSum += acos(Dot);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move
|
||||
// ----
|
||||
|
||||
void Polygon::operator+=(const Vertex &delta) {
|
||||
// Move all vertecies
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
vertices[i] += delta;
|
||||
|
||||
// Shift the focus
|
||||
_centroid += delta;
|
||||
}
|
||||
|
||||
// Line of Sight
|
||||
// -------------
|
||||
|
||||
bool Polygon::isLineInterior(const Vertex &a, const Vertex &b) const {
|
||||
// Both points have to be in the polygon
|
||||
if (!isPointInPolygon(a, true) || !isPointInPolygon(b, true))
|
||||
return false;
|
||||
|
||||
// If the points are identical, the line is trivially within the polygon
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
// Test whether the line intersects a line segment strictly (proper intersection)
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
int j = (i + 1) % vertexCount;
|
||||
const Vertex &vs = vertices[i];
|
||||
const Vertex &ve = vertices[j];
|
||||
|
||||
// If the line intersects a line segment strictly (proper cross section) the line is not in the polygon
|
||||
if (Line::doesIntersectProperly(a, b, vs, ve))
|
||||
return false;
|
||||
|
||||
// If one of the two line items is on the edge and the other is to the right of the edge,
|
||||
// then the line is not completely within the polygon
|
||||
if (Line::isOnLineStrict(vs, ve, a) && Line::isVertexRight(vs, ve, b))
|
||||
return false;
|
||||
if (Line::isOnLineStrict(vs, ve, b) && Line::isVertexRight(vs, ve, a))
|
||||
return false;
|
||||
|
||||
// If one of the two line items is on a vertex, the line traces into the polygon
|
||||
if ((a == vs) && !isLineInCone(i, b, true))
|
||||
return false;
|
||||
if ((b == vs) && !isLineInCone(i, a, true))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polygon::isLineExterior(const Vertex &a, const Vertex &b) const {
|
||||
// Neither of the two points must be strictly in the polygon (on the edge is allowed)
|
||||
if (isPointInPolygon(a, false) || isPointInPolygon(b, false))
|
||||
return false;
|
||||
|
||||
// If the points are identical, the line is trivially outside of the polygon
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
// Test whether the line intersects a line segment strictly (proper intersection)
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
int j = (i + 1) % vertexCount;
|
||||
const Vertex &vs = vertices[i];
|
||||
const Vertex &ve = vertices[j];
|
||||
|
||||
// If the line intersects a line segment strictly (proper intersection), then
|
||||
// the line is partially inside the polygon
|
||||
if (Line::doesIntersectProperly(a, b, vs, ve))
|
||||
return false;
|
||||
|
||||
// If one of the two line items is on the edge and the other is to the right of the edge,
|
||||
// the line is not completely outside the polygon
|
||||
if (Line::isOnLineStrict(vs, ve, a) && Line::isVertexLeft(vs, ve, b))
|
||||
return false;
|
||||
if (Line::isOnLineStrict(vs, ve, b) && Line::isVertexLeft(vs, ve, a))
|
||||
return false;
|
||||
|
||||
// If one of the lwo line items is on a vertex, the line must not run into the polygon
|
||||
if ((a == vs) && isLineInCone(i, b, false))
|
||||
return false;
|
||||
if ((b == vs) && isLineInCone(i, a, false))
|
||||
return false;
|
||||
|
||||
// If the vertex with start and end point is collinear, (a vs) and (b, vs) is not in the polygon
|
||||
if (Line::isOnLine(a, b, vs)) {
|
||||
if (isLineInCone(i, a, false))
|
||||
return false;
|
||||
if (isLineInCone(i, b, false))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polygon::isLineInCone(int startVertexIndex, const Vertex &endVertex, bool includeEdges) const {
|
||||
const Vertex &startVertex = vertices[startVertexIndex];
|
||||
const Vertex &nextVertex = vertices[(startVertexIndex + 1) % vertexCount];
|
||||
const Vertex &prevVertex = vertices[(startVertexIndex + vertexCount - 1) % vertexCount];
|
||||
|
||||
if (Line::isVertexLeftOn(prevVertex, startVertex, nextVertex)) {
|
||||
if (includeEdges)
|
||||
return Line::isVertexLeftOn(endVertex, startVertex, nextVertex) &&
|
||||
Line::isVertexLeftOn(startVertex, endVertex, prevVertex);
|
||||
else
|
||||
return Line::isVertexLeft(endVertex, startVertex, nextVertex) &&
|
||||
Line::isVertexLeft(startVertex, endVertex, prevVertex);
|
||||
} else {
|
||||
if (includeEdges)
|
||||
return !(Line::isVertexLeft(endVertex, startVertex, prevVertex) &&
|
||||
Line::isVertexLeft(startVertex, endVertex, nextVertex));
|
||||
else
|
||||
return !(Line::isVertexLeftOn(endVertex, startVertex, prevVertex) &&
|
||||
Line::isVertexLeftOn(startVertex, endVertex, nextVertex));
|
||||
}
|
||||
}
|
||||
|
||||
// Point-Polygon Tests
|
||||
// -------------------
|
||||
|
||||
bool Polygon::isPointInPolygon(int x, int y, bool borderBelongsToPolygon) const {
|
||||
return isPointInPolygon(Vertex(x, y), borderBelongsToPolygon);
|
||||
}
|
||||
|
||||
bool Polygon::isPointInPolygon(const Vertex &point, bool edgesBelongToPolygon) const {
|
||||
int rcross = 0; // Number of right-side overlaps
|
||||
int lcross = 0; // Number of left-side overlaps
|
||||
|
||||
// Each edge is checked whether it cuts the outgoing stream from the point
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
const Vertex &edgeStart = vertices[i];
|
||||
const Vertex &edgeEnd = vertices[(i + 1) % vertexCount];
|
||||
|
||||
// A vertex is a point? Then it lies on one edge of the polygon
|
||||
if (point == edgeStart)
|
||||
return edgesBelongToPolygon;
|
||||
|
||||
if ((edgeStart.y > point.y) != (edgeEnd.y > point.y)) {
|
||||
int term1 = (edgeStart.x - point.x) * (edgeEnd.y - point.y) - (edgeEnd.x - point.x) * (edgeStart.y - point.y);
|
||||
int term2 = (edgeEnd.y - point.y) - (edgeStart.y - edgeEnd.y);
|
||||
if ((term1 > 0) == (term2 >= 0))
|
||||
rcross++;
|
||||
}
|
||||
|
||||
if ((edgeStart.y < point.y) != (edgeEnd.y < point.y)) {
|
||||
int term1 = (edgeStart.x - point.x) * (edgeEnd.y - point.y) - (edgeEnd.x - point.x) * (edgeStart.y - point.y);
|
||||
int term2 = (edgeEnd.y - point.y) - (edgeStart.y - edgeEnd.y);
|
||||
if ((term1 < 0) == (term2 <= 0))
|
||||
lcross++;
|
||||
}
|
||||
}
|
||||
|
||||
// The point is on an adge, if the number of left and right intersections have the same even numbers
|
||||
if ((rcross % 2) != (lcross % 2))
|
||||
return edgesBelongToPolygon;
|
||||
|
||||
// The point is strictly inside the polygon if and only if the number of overlaps is odd
|
||||
if ((rcross % 2) == 1)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polygon::persist(OutputPersistenceBlock &writer) {
|
||||
writer.write(vertexCount);
|
||||
for (int i = 0; i < vertexCount; ++i) {
|
||||
writer.write((int32)vertices[i].x);
|
||||
writer.write((int32)vertices[i].y);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polygon::unpersist(InputPersistenceBlock &reader) {
|
||||
int32 storedvertexCount;
|
||||
reader.read(storedvertexCount);
|
||||
|
||||
Common::Array<Vertex> storedvertices;
|
||||
for (int i = 0; i < storedvertexCount; ++i) {
|
||||
int32 x, y;
|
||||
reader.read(x);
|
||||
reader.read(y);
|
||||
storedvertices.push_back(Vertex(x, y));
|
||||
}
|
||||
|
||||
init(storedvertexCount, &storedvertices[0]);
|
||||
|
||||
return reader.isGood();
|
||||
}
|
||||
|
||||
// Main Focus
|
||||
// ----------
|
||||
|
||||
Vertex Polygon::getCentroid() const {
|
||||
return _centroid;
|
||||
}
|
||||
|
||||
Vertex Polygon::computeCentroid() const {
|
||||
// Area of the polygon is calculated
|
||||
int doubleArea = 0;
|
||||
for (int i = 0; i < vertexCount; ++i) {
|
||||
doubleArea += vertices[i].x * vertices[i + 1].y - vertices[i + 1].x * vertices[i].y;
|
||||
}
|
||||
|
||||
// Avoid division by zero in the next step
|
||||
if (doubleArea == 0)
|
||||
return Vertex();
|
||||
|
||||
// Calculate centroid
|
||||
Vertex centroid;
|
||||
for (int i = 0; i < vertexCount; ++i) {
|
||||
int area = vertices[i].x * vertices[i + 1].y - vertices[i + 1].x * vertices[i].y;
|
||||
centroid.x += (vertices[i].x + vertices[i + 1].x) * area;
|
||||
centroid.y += (vertices[i].y + vertices[i + 1].y) * area;
|
||||
}
|
||||
centroid.x /= 3 * doubleArea;
|
||||
centroid.y /= 3 * doubleArea;
|
||||
|
||||
return centroid;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
229
engines/sword25/math/polygon.h
Normal file
229
engines/sword25/math/polygon.h
Normal file
@@ -0,0 +1,229 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_POLYGON_H
|
||||
#define SWORD25_POLYGON_H
|
||||
|
||||
// Includes
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
#include "sword25/math/vertex.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Vertex;
|
||||
|
||||
/**
|
||||
@brief Eine Polygonklasse.
|
||||
*/
|
||||
class Polygon : public Persistable {
|
||||
public:
|
||||
/**
|
||||
* Creates an object of type #BS_Polygon, containing 0 Vertecies.
|
||||
*
|
||||
* With the method Init(), Vertices can be added in later
|
||||
*/
|
||||
Polygon();
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*/
|
||||
Polygon(const Polygon &other);
|
||||
|
||||
/**
|
||||
* Creates a polygon using persisted data
|
||||
*/
|
||||
Polygon(InputPersistenceBlock &reader);
|
||||
|
||||
/**
|
||||
* Creaes an object of type #BS_Polygon, and assigns Vertices to it
|
||||
* @param VertexCount The number of vertices being passed
|
||||
* @param Vertecies An array of BS_Vertex objects representing the vertices in the polygon.
|
||||
* @remark The Vertecies that define a polygon must not have any self-intersections.
|
||||
* If the polygon does have self-intersections, then an empty polygon object is created.
|
||||
*/
|
||||
Polygon(int vertexCount_, const Vertex *vertices_);
|
||||
|
||||
/**
|
||||
* Deletes the BS_Polygon object
|
||||
*/
|
||||
~Polygon() override;
|
||||
|
||||
/**
|
||||
* Initializes the BS_Polygon with a list of Vertecies.
|
||||
*
|
||||
* The Vertices need to define a polygon must not have self-intersections.
|
||||
* If a polygon already has verticies, this will re-initialize it with the new list.
|
||||
*
|
||||
* @param VertexCount The number of vertices being passed
|
||||
* @param Vertecies An array of BS_Vertex objects representing the vertices in the polygon.
|
||||
* @return Returns false if the Vertecies have self-intersections. In this case,
|
||||
* the object is not initialized.
|
||||
*/
|
||||
bool init(int vertexCount_, const Vertex *vertices_);
|
||||
|
||||
//
|
||||
// ** Exploratory methods **
|
||||
//
|
||||
|
||||
/**
|
||||
* Checks whether the Vertecies of the polygon are arranged in a clockwise direction.
|
||||
* @return Returns true if the Vertecies of the polygon are arranged clockwise or co-planar.
|
||||
* Returns false if the Vertecies of the polygon are arrange counter-clockwise.
|
||||
* @remark This method only returns a meaningful result if the polygon has at least three Vertecies.
|
||||
*/
|
||||
bool isCW() const;
|
||||
|
||||
/**
|
||||
* Checks whether the Vertices of the polygon are arranged in a counter-clockwise direction.
|
||||
* @return Returns true if the Vertecies of the polygon are arranged counter-clockwise.
|
||||
* Returns false if the Vertecies of the polygon are arranged clockwise or co-planar.
|
||||
* @remark This method only returns a meaningful result if the polygon has at least three Vertecies.
|
||||
*/
|
||||
bool isCCW() const;
|
||||
|
||||
/**
|
||||
* Checks whether a point is inside the polygon
|
||||
* @param Vertex A Vertex with the co-ordinates of the point to be tested.
|
||||
* @param BorderBelongsToPolygon Specifies whether the edge of the polygon should be considered
|
||||
* @return Returns true if the point is inside the polygon, false if it is outside.
|
||||
*/
|
||||
bool isPointInPolygon(const Vertex &vertex, bool borderBelongsToPolygon = true) const;
|
||||
|
||||
/**
|
||||
* Checks whether a point is inside the polygon
|
||||
* @param X The X position of the point
|
||||
* @param Y The Y position of the point
|
||||
* @param BorderBelongsToPolygon Specifies whether the edge of the polygon should be considered
|
||||
* @return Returns true if the point is inside the polygon, false if it is outside.
|
||||
*/
|
||||
bool isPointInPolygon(int x, int y, bool borderBelongsToPolygon = true) const;
|
||||
|
||||
/**
|
||||
* Returns the focus/centroid of the polygon
|
||||
*/
|
||||
Vertex getCentroid() const;
|
||||
|
||||
// Edge belongs to the polygon
|
||||
// Polygon must be CW
|
||||
bool isLineInterior(const Vertex &a, const Vertex &b) const;
|
||||
// Edge does not belong to the polygon
|
||||
// Polygon must be CW
|
||||
bool isLineExterior(const Vertex &a, const Vertex &b) const;
|
||||
|
||||
//
|
||||
// Manipulation methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Ensures that the Vertecies of the polygon are arranged in a clockwise direction
|
||||
*/
|
||||
void ensureCWOrder();
|
||||
|
||||
/**
|
||||
* Ensures that the Vertecies of the polygon are arranged in a counter-clockwise direction
|
||||
*/
|
||||
void ensureCCWOrder();
|
||||
|
||||
/**
|
||||
* Reverses the Vertecies order.
|
||||
*/
|
||||
void reverseVertexOrder();
|
||||
|
||||
/**
|
||||
* Moves the polygon.
|
||||
* @param Delta The vertex around the polygon to be moved.
|
||||
*/
|
||||
void operator+=(const Vertex &delta);
|
||||
|
||||
//
|
||||
//------------------
|
||||
//
|
||||
|
||||
/// Specifies the number of Vertecies in the Vertecies array.
|
||||
int32 vertexCount;
|
||||
/// COntains the Vertecies of the polygon
|
||||
Vertex *vertices;
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
Polygon &operator=(const Polygon &p) {
|
||||
init(p.vertexCount, p.vertices);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _isCW;
|
||||
Vertex _centroid;
|
||||
|
||||
/**
|
||||
* Computes the centroid of the polygon.
|
||||
*/
|
||||
Vertex computeCentroid() const;
|
||||
|
||||
/**
|
||||
* Determines how the Vertecies of the polygon are arranged.
|
||||
* @return Returns true if the Vertecies are arranged in a clockwise
|
||||
* direction, otherwise false.
|
||||
*/
|
||||
bool computeIsCW() const;
|
||||
|
||||
/**
|
||||
* Calculates the cross product of three Vertecies
|
||||
* @param V1 The first Vertex
|
||||
* @param V2 The second Vertex
|
||||
* @param V3 The third Vertex
|
||||
* @return Returns the cross-product of the three vertecies
|
||||
* @todo This method would be better as a method of the BS_Vertex class
|
||||
*/
|
||||
int crossProduct(const Vertex &v1, const Vertex &v2, const Vertex &v3) const;
|
||||
|
||||
/**
|
||||
* Checks whether the polygon is self-intersecting
|
||||
* @return Returns true if the polygon is self-intersecting.
|
||||
* Returns false if the polygon is not self-intersecting.
|
||||
*/
|
||||
bool checkForSelfIntersection() const;
|
||||
|
||||
/**
|
||||
* Find the vertex of the polygon that is located below the right-most point,
|
||||
* and returns it's index in the vertex array.
|
||||
* @return Returns the index of the vertex at the bottom-right of the polygon.
|
||||
* Returns -1 if the vertex list is empty.
|
||||
*/
|
||||
int findLRVertexIndex() const;
|
||||
|
||||
bool isLineInCone(int startVertexIndex, const Vertex &endVertex, bool includeEdges) const;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
348
engines/sword25/math/region.cpp
Normal file
348
engines/sword25/math/region.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
|
||||
#include "sword25/math/region.h"
|
||||
#include "sword25/math/walkregion.h"
|
||||
#include "sword25/math/regionregistry.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Region::Region() : _valid(false), _type(RT_REGION) {
|
||||
RegionRegistry::instance().registerObject(this);
|
||||
}
|
||||
|
||||
Region::Region(InputPersistenceBlock &reader, uint handle) : _valid(false), _type(RT_REGION) {
|
||||
RegionRegistry::instance().registerObject(this, handle);
|
||||
unpersist(reader);
|
||||
}
|
||||
|
||||
uint Region::create(REGION_TYPE type) {
|
||||
Region *regionPtr = NULL;
|
||||
switch (type) {
|
||||
case RT_REGION:
|
||||
regionPtr = new Region();
|
||||
break;
|
||||
|
||||
case RT_WALKREGION:
|
||||
regionPtr = new WalkRegion();
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(true);
|
||||
}
|
||||
|
||||
return RegionRegistry::instance().resolvePtr(regionPtr);
|
||||
}
|
||||
|
||||
uint Region::create(InputPersistenceBlock &reader, uint handle) {
|
||||
// Read type
|
||||
uint32 type;
|
||||
reader.read(type);
|
||||
|
||||
// Depending on the type, create a new BS_Region or BS_WalkRegion object
|
||||
Region *regionPtr = NULL;
|
||||
if (type == RT_REGION) {
|
||||
regionPtr = new Region(reader, handle);
|
||||
} else if (type == RT_WALKREGION) {
|
||||
regionPtr = new WalkRegion(reader, handle);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return RegionRegistry::instance().resolvePtr(regionPtr);
|
||||
}
|
||||
|
||||
Region::~Region() {
|
||||
RegionRegistry::instance().deregisterObject(this);
|
||||
}
|
||||
|
||||
bool Region::init(const Polygon &contour, const Common::Array<Polygon> *pHoles) {
|
||||
// Reset object state
|
||||
_valid = false;
|
||||
_position = Vertex(0, 0);
|
||||
_polygons.clear();
|
||||
|
||||
// Reserve sufficient space for countour and holes in the polygon list
|
||||
if (pHoles)
|
||||
_polygons.reserve(1 + pHoles->size());
|
||||
else
|
||||
_polygons.reserve(1);
|
||||
|
||||
// The first polygon will be the contour
|
||||
_polygons.push_back(Polygon());
|
||||
_polygons[0].init(contour.vertexCount, contour.vertices);
|
||||
// Make sure that the Vertecies in the Contour are arranged in a clockwise direction
|
||||
_polygons[0].ensureCWOrder();
|
||||
|
||||
// Place the hole polygons in the following positions
|
||||
if (pHoles) {
|
||||
for (uint i = 0; i < pHoles->size(); ++i) {
|
||||
_polygons.push_back(Polygon());
|
||||
_polygons[i + 1].init((*pHoles)[i].vertexCount, (*pHoles)[i].vertices);
|
||||
_polygons[i + 1].ensureCWOrder();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize bounding box
|
||||
updateBoundingBox();
|
||||
|
||||
_valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Region::updateBoundingBox() {
|
||||
if (_polygons[0].vertexCount) {
|
||||
int minX = _polygons[0].vertices[0].x;
|
||||
int maxX = _polygons[0].vertices[0].x;
|
||||
int minY = _polygons[0].vertices[0].y;
|
||||
int maxY = _polygons[0].vertices[0].y;
|
||||
|
||||
for (int i = 1; i < _polygons[0].vertexCount; i++) {
|
||||
if (_polygons[0].vertices[i].x < minX) minX = _polygons[0].vertices[i].x;
|
||||
else if (_polygons[0].vertices[i].x > maxX) maxX = _polygons[0].vertices[i].x;
|
||||
if (_polygons[0].vertices[i].y < minY) minY = _polygons[0].vertices[i].y;
|
||||
else if (_polygons[0].vertices[i].y > maxY) maxY = _polygons[0].vertices[i].y;
|
||||
}
|
||||
|
||||
_boundingBox = Common::Rect(minX, minY, maxX + 1, maxY + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Position Changes
|
||||
void Region::setPos(int x, int y) {
|
||||
// Calculate the difference between the old and new position
|
||||
Vertex delta(x - _position.x, y - _position.y);
|
||||
|
||||
// Save the new position
|
||||
_position = Vertex(x, y);
|
||||
|
||||
// Move all the vertecies
|
||||
for (uint i = 0; i < _polygons.size(); ++i) {
|
||||
_polygons[i] += delta;
|
||||
}
|
||||
|
||||
// Update the bounding box
|
||||
updateBoundingBox();
|
||||
}
|
||||
|
||||
void Region::setPosX(int x) {
|
||||
setPos(x, _position.y);
|
||||
}
|
||||
|
||||
void Region::setPosY(int y) {
|
||||
setPos(_position.x, y);
|
||||
}
|
||||
|
||||
// Point-Region Tests
|
||||
bool Region::isPointInRegion(int x, int y) const {
|
||||
// Test whether the point is in the bounding box
|
||||
if (_boundingBox.contains(x, y)) {
|
||||
// Test whether the point is in the contour
|
||||
if (_polygons[0].isPointInPolygon(x, y, true)) {
|
||||
// Test whether the point is in a hole
|
||||
for (uint i = 1; i < _polygons.size(); i++) {
|
||||
if (_polygons[i].isPointInPolygon(x, y, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Region::isPointInRegion(const Vertex &vertex) const {
|
||||
return isPointInRegion(vertex.x, vertex.y);
|
||||
}
|
||||
|
||||
Vertex Region::findClosestRegionPoint(const Vertex &point) const {
|
||||
// Determine whether the point is inside a hole. If that is the case, the closest
|
||||
// point on the edge of the hole is determined
|
||||
int polygonIdx = 0;
|
||||
{
|
||||
for (uint i = 1; i < _polygons.size(); ++i) {
|
||||
if (_polygons[i].isPointInPolygon(point)) {
|
||||
polygonIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Polygon &polygon = _polygons[polygonIdx];
|
||||
|
||||
assert(polygon.vertexCount > 1);
|
||||
|
||||
// For each line of the polygon, calculate the point that is cloest to the given point
|
||||
// The point of this set with the smallest distance to the given point is the result.
|
||||
Vertex closestVertex = findClosestPointOnLine(polygon.vertices[0], polygon.vertices[1], point);
|
||||
int closestVertexDistance2 = closestVertex.distance(point);
|
||||
for (int i = 1; i < polygon.vertexCount; ++i) {
|
||||
int j = (i + 1) % polygon.vertexCount;
|
||||
|
||||
Vertex curVertex = findClosestPointOnLine(polygon.vertices[i], polygon.vertices[j], point);
|
||||
if (curVertex.distance(point) < closestVertexDistance2) {
|
||||
closestVertex = curVertex;
|
||||
closestVertexDistance2 = curVertex.distance(point);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine whether the point is really within the region. This must not be so, as a result of rounding
|
||||
// errors can occur at the edge of polygons
|
||||
if (isPointInRegion(closestVertex))
|
||||
return closestVertex;
|
||||
else {
|
||||
// Try to construct a point within the region - 8 points are tested in the immediate vicinity
|
||||
// of the point
|
||||
if (isPointInRegion(closestVertex + Vertex(-2, -2)))
|
||||
return closestVertex + Vertex(-2, -2);
|
||||
else if (isPointInRegion(closestVertex + Vertex(0, -2)))
|
||||
return closestVertex + Vertex(0, -2);
|
||||
else if (isPointInRegion(closestVertex + Vertex(2, -2)))
|
||||
return closestVertex + Vertex(2, -2);
|
||||
else if (isPointInRegion(closestVertex + Vertex(-2, 0)))
|
||||
return closestVertex + Vertex(-2, 0);
|
||||
else if (isPointInRegion(closestVertex + Vertex(0, 2)))
|
||||
return closestVertex + Vertex(0, 2);
|
||||
else if (isPointInRegion(closestVertex + Vertex(-2, 2)))
|
||||
return closestVertex + Vertex(-2, 2);
|
||||
else if (isPointInRegion(closestVertex + Vertex(-2, 0)))
|
||||
return closestVertex + Vertex(2, 2);
|
||||
else if (isPointInRegion(closestVertex + Vertex(2, 2)))
|
||||
return closestVertex + Vertex(2, 2);
|
||||
|
||||
// If no point could be found that way that lies within the region, find the next point
|
||||
closestVertex = polygon.vertices[0];
|
||||
int shortestVertexDistance2 = polygon.vertices[0].sqrDist(point);
|
||||
{
|
||||
for (int i = 1; i < polygon.vertexCount; i++) {
|
||||
int curDistance2 = polygon.vertices[i].sqrDist(point);
|
||||
if (curDistance2 < shortestVertexDistance2) {
|
||||
closestVertex = polygon.vertices[i];
|
||||
shortestVertexDistance2 = curDistance2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
warning("Clostest vertex forced because edgepoint was outside region.");
|
||||
return closestVertex;
|
||||
}
|
||||
}
|
||||
|
||||
Vertex Region::findClosestPointOnLine(const Vertex &lineStart, const Vertex &lineEnd, const Vertex point) const {
|
||||
float vector1X = static_cast<float>(point.x - lineStart.x);
|
||||
float vector1Y = static_cast<float>(point.y - lineStart.y);
|
||||
float vector2X = static_cast<float>(lineEnd.x - lineStart.x);
|
||||
float vector2Y = static_cast<float>(lineEnd.y - lineStart.y);
|
||||
float vector2Length = sqrt(vector2X * vector2X + vector2Y * vector2Y);
|
||||
vector2X /= vector2Length;
|
||||
vector2Y /= vector2Length;
|
||||
float distance = sqrt(static_cast<float>((lineStart.x - lineEnd.x) * (lineStart.x - lineEnd.x) +
|
||||
(lineStart.y - lineEnd.y) * (lineStart.y - lineEnd.y)));
|
||||
float dot = vector1X * vector2X + vector1Y * vector2Y;
|
||||
|
||||
if (dot <= 0)
|
||||
return lineStart;
|
||||
if (dot >= distance)
|
||||
return lineEnd;
|
||||
|
||||
Vertex vector3(static_cast<int>(vector2X * dot + 0.5f), static_cast<int>(vector2Y * dot + 0.5f));
|
||||
return lineStart + vector3;
|
||||
}
|
||||
|
||||
// Line of Sight
|
||||
bool Region::isLineOfSight(const Vertex &a, const Vertex &b) const {
|
||||
assert(_polygons.size());
|
||||
|
||||
// The line must be within the contour polygon, and outside of any hole polygons
|
||||
Common::Array<Polygon>::const_iterator iter = _polygons.begin();
|
||||
if (!(*iter).isLineInterior(a, b)) return false;
|
||||
for (iter++; iter != _polygons.end(); iter++)
|
||||
if (!(*iter).isLineExterior(a, b)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Persistence
|
||||
bool Region::persist(OutputPersistenceBlock &writer) {
|
||||
bool Result = true;
|
||||
|
||||
writer.write(static_cast<uint32>(_type));
|
||||
writer.write(_valid);
|
||||
writer.write((int32)_position.x);
|
||||
writer.write((int32)_position.y);
|
||||
|
||||
writer.write((uint32)_polygons.size());
|
||||
Common::Array<Polygon>::iterator It = _polygons.begin();
|
||||
while (It != _polygons.end()) {
|
||||
Result &= It->persist(writer);
|
||||
++It;
|
||||
}
|
||||
|
||||
writer.write((int32)_boundingBox.left);
|
||||
writer.write((int32)_boundingBox.top);
|
||||
writer.write((int32)_boundingBox.right);
|
||||
writer.write((int32)_boundingBox.bottom);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool Region::unpersist(InputPersistenceBlock &reader) {
|
||||
reader.read(_valid);
|
||||
reader.read(_position.x);
|
||||
reader.read(_position.y);
|
||||
|
||||
_polygons.clear();
|
||||
uint32 PolygonCount;
|
||||
reader.read(PolygonCount);
|
||||
for (uint i = 0; i < PolygonCount; ++i) {
|
||||
_polygons.push_back(Polygon(reader));
|
||||
}
|
||||
|
||||
reader.read(_boundingBox.left);
|
||||
reader.read(_boundingBox.top);
|
||||
reader.read(_boundingBox.right);
|
||||
reader.read(_boundingBox.bottom);
|
||||
|
||||
return reader.isGood();
|
||||
}
|
||||
|
||||
Vertex Region::getCentroid() const {
|
||||
if (_polygons.size() > 0)
|
||||
return _polygons[0].getCentroid();
|
||||
return
|
||||
Vertex();
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
237
engines/sword25/math/region.h
Normal file
237
engines/sword25/math/region.h
Normal file
@@ -0,0 +1,237 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_REGION_H
|
||||
#define SWORD25_REGION_H
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
#include "sword25/math/vertex.h"
|
||||
#include "sword25/math/polygon.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/**
|
||||
* This class is the base class of all regions.
|
||||
*
|
||||
* The IsValid() method can be queried to see whether the object is in a valid state.
|
||||
* If this is not the case, the method Init() is the only method that may be invoked.
|
||||
* This class guarantees that the Vertecies outline of the hole, and the polygons are
|
||||
* arranged in a clockwise direction, so that the polygon working algorithms will
|
||||
* work properly.
|
||||
*/
|
||||
class Region : public Persistable {
|
||||
protected:
|
||||
/**
|
||||
* Creates a new BS_Region object
|
||||
*
|
||||
* After creation the object is invaild (IsValid() return false), but a call can
|
||||
* be made later on to Init() to set up the region into a valid state.
|
||||
*/
|
||||
Region();
|
||||
|
||||
Region(InputPersistenceBlock &reader, uint handle);
|
||||
|
||||
public:
|
||||
enum REGION_TYPE {
|
||||
RT_REGION,
|
||||
RT_WALKREGION
|
||||
};
|
||||
|
||||
static uint create(REGION_TYPE type);
|
||||
static uint create(InputPersistenceBlock &reader, uint handle = 0);
|
||||
|
||||
~Region() override;
|
||||
|
||||
/**
|
||||
* Initializes a BS_Region object
|
||||
* @param Contour A polygon indicating the outline of the region
|
||||
* @param pHoles A pointer to an array of polygons representing the hole state in the region.
|
||||
* If the region has no holes, it must be passed as NULL. The default value is NULL.
|
||||
* @return Returns true if the initialisation was successful, otherwise false.
|
||||
* @remark If the region was already initialized, the old state will be deleted.
|
||||
*/
|
||||
virtual bool init(const Polygon &contour, const Common::Array<Polygon> *pHoles = NULL);
|
||||
|
||||
//
|
||||
// Exploratory Methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Specifies whether the object is in a valid state
|
||||
* @return Returns true if the object is in a valid state, otherwise false.
|
||||
* @remark Invalid objects can be made valid by calling Init with a valid state.
|
||||
*/
|
||||
bool isValid() const {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the region
|
||||
*/
|
||||
const Vertex &getPosition() const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the X position of the region
|
||||
*/
|
||||
int getPosX() const {
|
||||
return _position.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y position of the region
|
||||
*/
|
||||
int getPosY() const {
|
||||
return _position.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a point is inside the region
|
||||
* @param Vertex A verex with the co-ordinates of the test point
|
||||
* @return Returns true if the point is within the region, otherwise false.
|
||||
*/
|
||||
bool isPointInRegion(const Vertex &vertex) const;
|
||||
|
||||
/**
|
||||
* Indicates whether a point is inside the region
|
||||
* @param X The X position
|
||||
* @param Y The Y position
|
||||
* @return Returns true if the point is within the region, otherwise false.
|
||||
*/
|
||||
bool isPointInRegion(int x, int y) const;
|
||||
|
||||
/**
|
||||
* Returns the countour of the region
|
||||
*/
|
||||
const Polygon &getContour() const {
|
||||
return _polygons[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of polygons in the hole region
|
||||
*/
|
||||
int getHoleCount() const {
|
||||
return static_cast<int>(_polygons.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific hole polygon in the region
|
||||
* @param i The number of the hole to return.
|
||||
* The index must be between 0 and GetHoleCount() - 1.
|
||||
* @return Returns the desired hole polygon
|
||||
*/
|
||||
inline const Polygon &getHole(uint i) const;
|
||||
|
||||
/**
|
||||
* For a point outside the region, finds the closest point inside the region
|
||||
* @param Point The point that is outside the region
|
||||
* @return Returns the point within the region which is closest
|
||||
* @remark This method does not always work with pixel accuracy.
|
||||
* One should not therefore rely on the fact that there is really no point in
|
||||
* the region which is closer to the given point.
|
||||
*/
|
||||
Vertex findClosestRegionPoint(const Vertex &point) const;
|
||||
|
||||
/**
|
||||
* Returns the centroid for the region
|
||||
*/
|
||||
Vertex getCentroid() const;
|
||||
|
||||
bool isLineOfSight(const Vertex &a, const Vertex &b) const;
|
||||
|
||||
//
|
||||
// Manipulation Methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Sets the position of the region
|
||||
* @param X The new X psoition of the region
|
||||
* @param Y The new Y psoition of the region
|
||||
*/
|
||||
virtual void setPos(int x, int y);
|
||||
|
||||
/**
|
||||
* Sets the X position of the region
|
||||
* @param X The new X position of the region
|
||||
*/
|
||||
void setPosX(int x);
|
||||
|
||||
/**
|
||||
* Sets the Y position of the region
|
||||
* @param Y The new Y position of the region
|
||||
*/
|
||||
void setPosY(int y);
|
||||
|
||||
//
|
||||
// Manipulation Methods
|
||||
//
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
protected:
|
||||
/// This specifies the type of object
|
||||
REGION_TYPE _type;
|
||||
/// This variable indicates whether the current object state is valid
|
||||
bool _valid;
|
||||
/// This vertex is the position of the region
|
||||
Vertex _position;
|
||||
/// This array contains all the polygons that define the region. The first element of
|
||||
// the array is the contour, all others are the holes
|
||||
Common::Array<Polygon> _polygons;
|
||||
/// The bounding box for the region
|
||||
Common::Rect _boundingBox;
|
||||
|
||||
/**
|
||||
* Updates the bounding box of the region.
|
||||
*/
|
||||
void updateBoundingBox();
|
||||
|
||||
/**
|
||||
* Find the point on a line which is closest to another point
|
||||
* @param LineStart The start of the line
|
||||
* @param LineEnd The end of the line
|
||||
* @param Point The point to be compared against
|
||||
* @return Returns the point on the line which is cloest to the passed point.
|
||||
*/
|
||||
Vertex findClosestPointOnLine(const Vertex &lineStart, const Vertex &lineEnd, const Vertex point) const;
|
||||
};
|
||||
|
||||
inline const Polygon &Region::getHole(uint i) const {
|
||||
assert(i < _polygons.size() - 1);
|
||||
return _polygons[i + 1];
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
94
engines/sword25/math/regionregistry.cpp
Normal file
94
engines/sword25/math/regionregistry.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/math/regionregistry.h"
|
||||
#include "sword25/math/region.h"
|
||||
|
||||
namespace Common {
|
||||
DECLARE_SINGLETON(Sword25::RegionRegistry);
|
||||
}
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
bool RegionRegistry::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
// write out the next handle
|
||||
writer.write(_nextHandle);
|
||||
|
||||
// Number of regions to write
|
||||
writer.write((uint32)_handle2PtrMap.size());
|
||||
|
||||
// Persist all the BS_Regions
|
||||
HANDLE2PTR_MAP::const_iterator iter = _handle2PtrMap.begin();
|
||||
while (iter != _handle2PtrMap.end()) {
|
||||
// Handle persistence
|
||||
writer.write(iter->_key);
|
||||
|
||||
// Persist object
|
||||
result &= iter->_value->persist(writer);
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RegionRegistry::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
// read in the next handle
|
||||
reader.read(_nextHandle);
|
||||
|
||||
// Destroy all existing BS_Regions
|
||||
//FIXME: This doesn't seem right - the value is being deleted but not the actual hash node itself?
|
||||
while (!_handle2PtrMap.empty())
|
||||
delete _handle2PtrMap.begin()->_value;
|
||||
|
||||
// read in the number of BS_Regions
|
||||
uint32 regionCount;
|
||||
reader.read(regionCount);
|
||||
|
||||
// Restore all the BS_Regions objects
|
||||
for (uint i = 0; i < regionCount; ++i) {
|
||||
// Handle read
|
||||
uint32 handle;
|
||||
reader.read(handle);
|
||||
|
||||
// BS_Region restore
|
||||
result &= Region::create(reader, handle) != 0;
|
||||
}
|
||||
|
||||
return reader.isGood() && result;
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
55
engines/sword25/math/regionregistry.h
Normal file
55
engines/sword25/math/regionregistry.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_REGIONREGISTRY_H
|
||||
#define SWORD25_REGIONREGISTRY_H
|
||||
|
||||
#include "common/singleton.h"
|
||||
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/kernel/persistable.h"
|
||||
#include "sword25/kernel/objectregistry.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
class Region;
|
||||
|
||||
class RegionRegistry :
|
||||
public ObjectRegistry<Region>,
|
||||
public Persistable,
|
||||
public Common::Singleton<RegionRegistry> {
|
||||
public:
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
82
engines/sword25/math/vertex.cpp
Normal file
82
engines/sword25/math/vertex.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/math/vertex.h"
|
||||
|
||||
#include "common/lua/lua.h"
|
||||
#include "common/lua/lauxlib.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
Vertex &Vertex::luaVertexToVertex(lua_State *L, int stackIndex, Vertex &vertex) {
|
||||
#ifdef DEBUG
|
||||
int __startStackDepth = lua_gettop(L);
|
||||
#endif
|
||||
|
||||
// Ensure that we actually consider a table
|
||||
luaL_checktype(L, stackIndex, LUA_TTABLE);
|
||||
|
||||
// Read X Component
|
||||
lua_pushstring(L, "X");
|
||||
lua_gettable(L, stackIndex);
|
||||
if (!lua_isnumber(L, -1)) luaL_argcheck(L, 0, stackIndex, "the X component has to be a number");
|
||||
vertex.x = static_cast<int>(lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Read Y Component
|
||||
lua_pushstring(L, "Y");
|
||||
lua_gettable(L, stackIndex);
|
||||
if (!lua_isnumber(L, -1)) luaL_argcheck(L, 0, stackIndex, "the Y component has to be a number");
|
||||
vertex.y = static_cast<int>(lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
|
||||
#ifdef DEBUG
|
||||
assert(__startStackDepth == lua_gettop(L));
|
||||
#endif
|
||||
|
||||
return vertex;
|
||||
}
|
||||
|
||||
void Vertex::vertexToLuaVertex(lua_State *L, const Vertex &vertex) {
|
||||
// Create New Table
|
||||
lua_newtable(L);
|
||||
|
||||
// X value is written to table
|
||||
lua_pushstring(L, "X");
|
||||
lua_pushnumber(L, vertex.x);
|
||||
lua_settable(L, -3);
|
||||
|
||||
// Y value is written to table
|
||||
lua_pushstring(L, "Y");
|
||||
lua_pushnumber(L, vertex.y);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
74
engines/sword25/math/vertex.h
Normal file
74
engines/sword25/math/vertex.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
BS_Vertex
|
||||
---------
|
||||
|
||||
Autor: Malte Thiesen
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_VERTEX_H
|
||||
#define SWORD25_VERTEX_H
|
||||
|
||||
// Includes
|
||||
#include "common/rect.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
|
||||
struct lua_State;
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
/**
|
||||
* Defines a 2-D Vertex
|
||||
*/
|
||||
class Vertex : public Common::Point {
|
||||
public:
|
||||
Vertex() : Point() {}
|
||||
Vertex(int x_, int y_) : Point(x_, y_) {}
|
||||
Vertex(Point p) : Point(p) {}
|
||||
|
||||
/**
|
||||
* Calculates the square of the distance between two Vertecies.
|
||||
* @param Vertex The vertex for which the distance is to be calculated
|
||||
* @return Returns the square of the distance between itself and the passed vertex
|
||||
* @remark If only distances should be compared, sqrDist() should be used, since it is faster.
|
||||
*/
|
||||
inline int distance(const Vertex &vertex) const {
|
||||
return (int)(sqrt(static_cast<float>(sqrDist(vertex))) + 0.5);
|
||||
}
|
||||
|
||||
static Vertex &luaVertexToVertex(lua_State *L, int StackIndex, Vertex &vertex);
|
||||
static void vertexToLuaVertex(lua_State *L, const Vertex &vertex);
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
397
engines/sword25/math/walkregion.cpp
Normal file
397
engines/sword25/math/walkregion.cpp
Normal file
@@ -0,0 +1,397 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sword25/kernel/kernel.h"
|
||||
#include "sword25/kernel/inputpersistenceblock.h"
|
||||
#include "sword25/kernel/outputpersistenceblock.h"
|
||||
#include "sword25/math/walkregion.h"
|
||||
#include "sword25/math/line.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
static const int Infinity = 0x7fffffff;
|
||||
|
||||
WalkRegion::WalkRegion() {
|
||||
_type = RT_WALKREGION;
|
||||
}
|
||||
|
||||
WalkRegion::WalkRegion(InputPersistenceBlock &reader, uint handle) :
|
||||
Region(reader, handle) {
|
||||
_type = RT_WALKREGION;
|
||||
unpersist(reader);
|
||||
}
|
||||
|
||||
WalkRegion::~WalkRegion() {
|
||||
}
|
||||
|
||||
bool WalkRegion::init(const Polygon &contour, const Common::Array<Polygon> *pHoles) {
|
||||
// Default initialisation of the region
|
||||
if (!Region::init(contour, pHoles)) return false;
|
||||
|
||||
// Prepare structures for pathfinding
|
||||
initNodeVector();
|
||||
computeVisibilityMatrix();
|
||||
|
||||
// Signal success
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalkRegion::queryPath(Vertex startPoint, Vertex endPoint, BS_Path &path) {
|
||||
assert(path.empty());
|
||||
|
||||
// If the start and finish are identical, no path can be found trivially
|
||||
if (startPoint == endPoint)
|
||||
return true;
|
||||
|
||||
// Ensure that the start and finish are valid and find new start points if either
|
||||
// are outside the polygon
|
||||
if (!checkAndPrepareStartAndEnd(startPoint, endPoint)) return false;
|
||||
|
||||
// If between the start and point a line of sight exists, then it can be returned.
|
||||
if (isLineOfSight(startPoint, endPoint)) {
|
||||
path.push_back(startPoint);
|
||||
path.push_back(endPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
return findPath(startPoint, endPoint, path);
|
||||
}
|
||||
|
||||
struct DijkstraNode {
|
||||
typedef Common::Array<DijkstraNode> Container;
|
||||
typedef Container::iterator Iter;
|
||||
typedef Container::const_iterator ConstIter;
|
||||
|
||||
DijkstraNode() : parentIter(), cost(Infinity), chosen(false) {}
|
||||
ConstIter parentIter;
|
||||
int cost;
|
||||
bool chosen;
|
||||
};
|
||||
|
||||
static void initDijkstraNodes(DijkstraNode::Container &dijkstraNodes, const Region ®ion,
|
||||
const Vertex &start, const Common::Array<Vertex> &nodes) {
|
||||
// Allocate sufficient space in the array
|
||||
dijkstraNodes.resize(nodes.size());
|
||||
|
||||
// Initialize all the nodes which are visible from the starting node
|
||||
DijkstraNode::Iter dijkstraIter = dijkstraNodes.begin();
|
||||
for (Common::Array<Vertex>::const_iterator nodesIter = nodes.begin();
|
||||
nodesIter != nodes.end(); nodesIter++, dijkstraIter++) {
|
||||
(*dijkstraIter).parentIter = dijkstraNodes.end();
|
||||
if (region.isLineOfSight(*nodesIter, start))(*dijkstraIter).cost = (*nodesIter).distance(start);
|
||||
}
|
||||
assert(dijkstraIter == dijkstraNodes.end());
|
||||
}
|
||||
|
||||
static DijkstraNode::Iter chooseClosestNode(DijkstraNode::Container &nodes) {
|
||||
DijkstraNode::Iter closestNodeInter = nodes.end();
|
||||
int minCost = Infinity;
|
||||
|
||||
for (DijkstraNode::Iter iter = nodes.begin(); iter != nodes.end(); iter++) {
|
||||
if (!(*iter).chosen && (*iter).cost < minCost) {
|
||||
minCost = (*iter).cost;
|
||||
closestNodeInter = iter;
|
||||
}
|
||||
}
|
||||
|
||||
return closestNodeInter;
|
||||
}
|
||||
|
||||
static void relaxNodes(DijkstraNode::Container &nodes,
|
||||
const Common::Array< Common::Array<int> > &visibilityMatrix,
|
||||
const DijkstraNode::ConstIter &curNodeIter) {
|
||||
// All the successors of the current node that have not been chosen will be
|
||||
// inserted into the boundary node list, and the cost will be updated if
|
||||
// a shorter path has been found to them.
|
||||
|
||||
int curNodeIndex = curNodeIter - nodes.begin();
|
||||
for (uint i = 0; i < nodes.size(); i++) {
|
||||
int cost = visibilityMatrix[curNodeIndex][i];
|
||||
if (!nodes[i].chosen && cost != Infinity) {
|
||||
int totalCost = (*curNodeIter).cost + cost;
|
||||
if (totalCost < nodes[i].cost) {
|
||||
nodes[i].parentIter = curNodeIter;
|
||||
nodes[i].cost = totalCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void relaxEndPoint(const Vertex &curNodePos,
|
||||
const DijkstraNode::ConstIter &curNodeIter,
|
||||
const Vertex &endPointPos,
|
||||
DijkstraNode &endPoint,
|
||||
const Region ®ion) {
|
||||
if (region.isLineOfSight(curNodePos, endPointPos)) {
|
||||
int totalCost = (*curNodeIter).cost + curNodePos.distance(endPointPos);
|
||||
if (totalCost < endPoint.cost) {
|
||||
endPoint.parentIter = curNodeIter;
|
||||
endPoint.cost = totalCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void reverseArray(Common::Array<T> &arr) {
|
||||
const uint size = arr.size();
|
||||
if (size < 2)
|
||||
return;
|
||||
|
||||
for (uint i = 0; i <= (size / 2 - 1); ++i) {
|
||||
SWAP(arr[i], arr[size - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
bool WalkRegion::findPath(const Vertex &start, const Vertex &end, BS_Path &path) const {
|
||||
// This is an implementation of Dijkstra's algorithm
|
||||
|
||||
// Initialize edge node list
|
||||
DijkstraNode::Container dijkstraNodes;
|
||||
initDijkstraNodes(dijkstraNodes, *this, start, _nodes);
|
||||
|
||||
// The end point is treated separately, since it does not exist in the visibility graph
|
||||
DijkstraNode endPoint;
|
||||
|
||||
// Since a node is selected each round from the node list, and can never be selected again
|
||||
// after that, the maximum number of loop iterations is limited by the number of nodes
|
||||
for (uint i = 0; i < _nodes.size(); i++) {
|
||||
// Determine the nearest edge node in the node list
|
||||
DijkstraNode::Iter nodeInter = chooseClosestNode(dijkstraNodes);
|
||||
|
||||
// If no free nodes are absent from the edge node list, there is no path from start
|
||||
// to end node. This case should never occur, since the number of loop passes is
|
||||
// limited, but etter safe than sorry
|
||||
if (nodeInter == dijkstraNodes.end())
|
||||
return false;
|
||||
|
||||
// If the destination point is closer than the point cost, scan can stop
|
||||
(*nodeInter).chosen = true;
|
||||
if (endPoint.cost <= (*nodeInter).cost) {
|
||||
// Insert the end point in the list
|
||||
path.push_back(end);
|
||||
|
||||
// The list is done in reverse order and inserted into the path
|
||||
DijkstraNode::ConstIter curNode = endPoint.parentIter;
|
||||
while (curNode != dijkstraNodes.end()) {
|
||||
assert((*curNode).chosen);
|
||||
path.push_back(_nodes[curNode - dijkstraNodes.begin()]);
|
||||
curNode = (*curNode).parentIter;
|
||||
}
|
||||
|
||||
// The starting point is inserted into the path
|
||||
path.push_back(start);
|
||||
|
||||
// The nodes of the path must be untwisted, as they were extracted in reverse order.
|
||||
// This step could be saved if the path from end to the beginning was desired
|
||||
reverseArray<Vertex>(path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Relaxation step for nodes of the graph, and perform the end nodes
|
||||
relaxNodes(dijkstraNodes, _visibilityMatrix, nodeInter);
|
||||
relaxEndPoint(_nodes[nodeInter - dijkstraNodes.begin()], nodeInter, end, endPoint, *this);
|
||||
}
|
||||
|
||||
// If the loop has been completely run through, all the nodes have been chosen, and still
|
||||
// no path was found. There is therefore no path available
|
||||
return false;
|
||||
}
|
||||
|
||||
void WalkRegion::initNodeVector() {
|
||||
// Empty the Node list
|
||||
_nodes.clear();
|
||||
|
||||
// Determine the number of nodes
|
||||
int nodeCount = 0;
|
||||
{
|
||||
for (uint i = 0; i < _polygons.size(); i++)
|
||||
nodeCount += _polygons[i].vertexCount;
|
||||
}
|
||||
|
||||
// Knoten-Vector füllen
|
||||
_nodes.reserve(nodeCount);
|
||||
{
|
||||
for (uint j = 0; j < _polygons.size(); j++)
|
||||
for (int i = 0; i < _polygons[j].vertexCount; i++)
|
||||
_nodes.push_back(_polygons[j].vertices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void WalkRegion::computeVisibilityMatrix() {
|
||||
// Initialize visibility matrix
|
||||
_visibilityMatrix = Common::Array< Common::Array <int> >();
|
||||
for (uint idx = 0; idx < _nodes.size(); ++idx) {
|
||||
Common::Array<int> arr;
|
||||
for (uint idx2 = 0; idx2 < _nodes.size(); ++idx2)
|
||||
arr.push_back(Infinity);
|
||||
|
||||
_visibilityMatrix.push_back(arr);
|
||||
}
|
||||
|
||||
// Calculate visibility been vertecies
|
||||
for (uint j = 0; j < _nodes.size(); ++j) {
|
||||
for (uint i = j; i < _nodes.size(); ++i) {
|
||||
if (isLineOfSight(_nodes[i], _nodes[j])) {
|
||||
// There is a line of sight, so save the distance between the two
|
||||
int distance = _nodes[i].distance(_nodes[j]);
|
||||
_visibilityMatrix[i][j] = distance;
|
||||
_visibilityMatrix[j][i] = distance;
|
||||
} else {
|
||||
// There is no line of sight, so save Infinity as the distance
|
||||
_visibilityMatrix[i][j] = Infinity;
|
||||
_visibilityMatrix[j][i] = Infinity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WalkRegion::checkAndPrepareStartAndEnd(Vertex &start, Vertex &end) const {
|
||||
if (!isPointInRegion(start)) {
|
||||
Vertex newStart = findClosestRegionPoint(start);
|
||||
|
||||
// Check to make sure the point is really in the region. If not, stop with an error
|
||||
if (!isPointInRegion(newStart)) {
|
||||
error("Constructed startpoint ((%d,%d) from (%d,%d)) is not inside the region.",
|
||||
newStart.x, newStart.y,
|
||||
start.x, start.y);
|
||||
return false;
|
||||
}
|
||||
|
||||
start = newStart;
|
||||
}
|
||||
|
||||
// If the destination is outside the region, a point is determined that is within the region,
|
||||
// and that is used as an endpoint instead
|
||||
if (!isPointInRegion(end)) {
|
||||
Vertex newEnd = findClosestRegionPoint(end);
|
||||
|
||||
// Make sure that the determined point is really within the region
|
||||
if (!isPointInRegion(newEnd)) {
|
||||
error("Constructed endpoint ((%d,%d) from (%d,%d)) is not inside the region.",
|
||||
newEnd.x, newEnd.y,
|
||||
end.x, end.y);
|
||||
return false;
|
||||
}
|
||||
|
||||
end = newEnd;
|
||||
}
|
||||
|
||||
// Signal success
|
||||
return true;
|
||||
}
|
||||
|
||||
void WalkRegion::setPos(int x, int y) {
|
||||
// Calculate the difference between old and new position
|
||||
Vertex Delta(x - _position.x, y - _position.y);
|
||||
|
||||
// Move all the nodes
|
||||
for (uint i = 0; i < _nodes.size(); i++)
|
||||
_nodes[i] += Delta;
|
||||
|
||||
// Move regions
|
||||
Region::setPos(x, y);
|
||||
}
|
||||
|
||||
bool WalkRegion::persist(OutputPersistenceBlock &writer) {
|
||||
bool result = true;
|
||||
|
||||
// Persist the parent region
|
||||
result &= Region::persist(writer);
|
||||
|
||||
// Persist the nodes
|
||||
writer.write((uint32)_nodes.size());
|
||||
Common::Array<Vertex>::const_iterator it = _nodes.begin();
|
||||
while (it != _nodes.end()) {
|
||||
writer.write((int32)it->x);
|
||||
writer.write((int32)it->y);
|
||||
++it;
|
||||
}
|
||||
|
||||
// Persist the visibility matrix
|
||||
writer.write((uint32)_visibilityMatrix.size());
|
||||
Common::Array< Common::Array<int> >::const_iterator rowIter = _visibilityMatrix.begin();
|
||||
while (rowIter != _visibilityMatrix.end()) {
|
||||
writer.write((uint32)rowIter->size());
|
||||
Common::Array<int>::const_iterator colIter = rowIter->begin();
|
||||
while (colIter != rowIter->end()) {
|
||||
writer.write((int32)*colIter);
|
||||
++colIter;
|
||||
}
|
||||
|
||||
++rowIter;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WalkRegion::unpersist(InputPersistenceBlock &reader) {
|
||||
bool result = true;
|
||||
|
||||
// The parent object was already loaded in the constructor of BS_Region, so at
|
||||
// this point only the additional data from BS_WalkRegion needs to be loaded
|
||||
|
||||
// Node load
|
||||
uint32 nodeCount;
|
||||
reader.read(nodeCount);
|
||||
_nodes.clear();
|
||||
_nodes.resize(nodeCount);
|
||||
Common::Array<Vertex>::iterator it = _nodes.begin();
|
||||
while (it != _nodes.end()) {
|
||||
reader.read(it->x);
|
||||
reader.read(it->y);
|
||||
++it;
|
||||
}
|
||||
|
||||
// Visibility matrix load
|
||||
uint32 rowCount;
|
||||
reader.read(rowCount);
|
||||
_visibilityMatrix.clear();
|
||||
_visibilityMatrix.resize(rowCount);
|
||||
Common::Array< Common::Array<int> >::iterator rowIter = _visibilityMatrix.begin();
|
||||
while (rowIter != _visibilityMatrix.end()) {
|
||||
uint32 colCount;
|
||||
reader.read(colCount);
|
||||
rowIter->resize(colCount);
|
||||
Common::Array<int>::iterator colIter = rowIter->begin();
|
||||
while (colIter != rowIter->end()) {
|
||||
int32 t;
|
||||
reader.read(t);
|
||||
*colIter = t;
|
||||
++colIter;
|
||||
}
|
||||
|
||||
++rowIter;
|
||||
}
|
||||
|
||||
return result && reader.isGood();
|
||||
}
|
||||
|
||||
} // End of namespace Sword25
|
||||
109
engines/sword25/math/walkregion.h
Normal file
109
engines/sword25/math/walkregion.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on Broken Sword 2.5 engine
|
||||
*
|
||||
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
|
||||
*
|
||||
* Licensed under GNU GPL v2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SWORD25_WALKREGION_H
|
||||
#define SWORD25_WALKREGION_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "sword25/kernel/common.h"
|
||||
#include "sword25/math/region.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
typedef Common::Array<Vertex> BS_Path;
|
||||
|
||||
/**
|
||||
* This class represents the region in which the main character can move
|
||||
*/
|
||||
class WalkRegion : public Region {
|
||||
friend class Region;
|
||||
|
||||
protected:
|
||||
WalkRegion();
|
||||
WalkRegion(InputPersistenceBlock &Reader, uint handle);
|
||||
|
||||
public:
|
||||
~WalkRegion() override;
|
||||
|
||||
bool init(const Polygon &contour, const Common::Array<Polygon> *pHoles = 0) override;
|
||||
|
||||
/**
|
||||
* Get the shortest path between two points in the region
|
||||
*
|
||||
* This method requires that the starting point lies within the region. The end point
|
||||
* may lie outside the region. Int his case, the end is chosen as the cloest point to it
|
||||
* that lies within the region.
|
||||
*
|
||||
* @param X1 X Co-ordinate of the start point
|
||||
* @param Y1 Y Co-ordinate of the start point
|
||||
* @param X2 X Co-ordinate of the end point
|
||||
* @param Y2 Y Co-ordinate of the end point
|
||||
* @param Path An empty BS_Path that will be set to the resulting path
|
||||
* @return Returns false if the result is invalid, otherwise returns true.
|
||||
*/
|
||||
bool queryPath(int x1, int y1, int x2, int y2, BS_Path &path) {
|
||||
return queryPath(Vertex(x1, y1), Vertex(x2, y2), path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shortest path between two points in the region.
|
||||
*
|
||||
* @param StartPoint The start point
|
||||
* @param EndPoint The end point
|
||||
* @param Path An empty BS_Path that will be set to the resulting path
|
||||
* @return Returns false if the result is invalid, otherwise returns true.
|
||||
*/
|
||||
bool queryPath(Vertex startPoint, Vertex endPoint, BS_Path &path);
|
||||
|
||||
void setPos(int x, int y) override;
|
||||
|
||||
const Common::Array<Vertex> &getNodes() const {
|
||||
return _nodes;
|
||||
}
|
||||
const Common::Array< Common::Array<int> > &getVisibilityMatrix() const {
|
||||
return _visibilityMatrix;
|
||||
}
|
||||
|
||||
bool persist(OutputPersistenceBlock &writer) override;
|
||||
bool unpersist(InputPersistenceBlock &reader) override;
|
||||
|
||||
private:
|
||||
Common::Array<Vertex> _nodes;
|
||||
Common::Array< Common::Array<int> > _visibilityMatrix;
|
||||
|
||||
void initNodeVector();
|
||||
void computeVisibilityMatrix();
|
||||
bool checkAndPrepareStartAndEnd(Vertex &start, Vertex &end) const;
|
||||
bool findPath(const Vertex &start, const Vertex &end, BS_Path &path) const;
|
||||
};
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
#endif
|
||||
143
engines/sword25/metaengine.cpp
Normal file
143
engines/sword25/metaengine.cpp
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "sword25/sword25.h"
|
||||
#include "sword25/kernel/persistenceservice.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
namespace Sword25 {
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_ENGLISH_SPEECH,
|
||||
{
|
||||
_s("Use English speech"),
|
||||
_s("Use English speech instead of German for every language other than German"),
|
||||
"english_speech",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
uint32 Sword25Engine::getGameFlags() const { return _gameDescription->flags; }
|
||||
|
||||
} // End of namespace Sword25
|
||||
|
||||
class Sword25MetaEngine : public AdvancedMetaEngine<ADGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "sword25";
|
||||
}
|
||||
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return Sword25::optionsList;
|
||||
}
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
|
||||
int getMaximumSaveSlot() const override { return Sword25::PersistenceService::getSlotCount(); }
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
};
|
||||
|
||||
Common::Error Sword25MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||
*engine = new Sword25::Sword25Engine(syst, desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool Sword25MetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSimpleSavesNames);
|
||||
}
|
||||
|
||||
SaveStateList Sword25MetaEngine::listSaves(const char *target) const {
|
||||
Common::String pattern = target;
|
||||
pattern = pattern + ".###";
|
||||
SaveStateList saveList;
|
||||
|
||||
Sword25::PersistenceService ps;
|
||||
Sword25::setGameTarget(target);
|
||||
|
||||
ps.reloadSlots();
|
||||
|
||||
for (uint i = 0; i < ps.getSlotCount(); ++i) {
|
||||
if (ps.isSlotOccupied(i)) {
|
||||
Common::String desc = ps.getSavegameDescription(i);
|
||||
saveList.push_back(SaveStateDescriptor(this, i, desc));
|
||||
}
|
||||
}
|
||||
|
||||
return saveList;
|
||||
}
|
||||
|
||||
Common::KeymapArray Sword25MetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Sword25;
|
||||
|
||||
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "sword25-default", _("Default keymappings"));
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Move / Interact / Skip dialog"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action("SKIPDLG", _("Examine / Skip dialog"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionPause, _("Pause"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_p, 'p'));
|
||||
act->addDefaultInputMapping("p");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Action("REVEALHOTSPOTS", _("Reveal all interactive hotspots (hold the key)"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_SPACE, ASCII_SPACE));
|
||||
act->addDefaultInputMapping("SPACE");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
return Keymap::arrayOf(engineKeyMap);
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(SWORD25)
|
||||
REGISTER_PLUGIN_DYNAMIC(SWORD25, PLUGIN_TYPE_ENGINE, Sword25MetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(SWORD25, PLUGIN_TYPE_ENGINE, Sword25MetaEngine);
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user