mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-08 10:51:11 +00:00
TOLTECS: Merge toltecs engine
This is an engine for the game "3 Skulls of the Toltecs". It is a renamed, tweaked and manual merge of the pull request at https://github.com/scummvm/scummvm/pull/119
This commit is contained in:
commit
2613e7f587
@ -172,6 +172,9 @@ public:
|
||||
#if PLUGIN_ENABLED_STATIC(TINSEL)
|
||||
LINK_PLUGIN(TINSEL)
|
||||
#endif
|
||||
#if PLUGIN_ENABLED_STATIC(TOLTECS)
|
||||
LINK_PLUGIN(TOLTECS)
|
||||
#endif
|
||||
#if PLUGIN_ENABLED_STATIC(TOON)
|
||||
LINK_PLUGIN(TOON)
|
||||
#endif
|
||||
|
1
configure
vendored
1
configure
vendored
@ -117,6 +117,7 @@ add_engine sword25 "Broken Sword 2.5" no
|
||||
add_engine teenagent "Teen Agent" yes
|
||||
add_engine testbed "TestBed: the Testing framework" no
|
||||
add_engine tinsel "Tinsel" yes
|
||||
add_engine toltecs "3 Skulls of the Toltecs" no
|
||||
add_engine toon "Toonstruck" yes
|
||||
add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes
|
||||
add_engine tsage "TsAGE" yes
|
||||
|
@ -188,6 +188,11 @@ DEFINES += -DENABLE_TINSEL=$(ENABLE_TINSEL)
|
||||
MODULES += engines/tinsel
|
||||
endif
|
||||
|
||||
ifdef ENABLE_TOLTECS
|
||||
DEFINES += -DENABLE_TOLTECS=$(ENABLE_TOLTECS)
|
||||
MODULES += engines/toltecs
|
||||
endif
|
||||
|
||||
ifdef ENABLE_TOON
|
||||
DEFINES += -DENABLE_TOON=$(ENABLE_TOON)
|
||||
MODULES += engines/toon
|
||||
|
54
engines/toltecs/TODO.txt
Normal file
54
engines/toltecs/TODO.txt
Normal file
@ -0,0 +1,54 @@
|
||||
NOTES
|
||||
-------
|
||||
- The game is completable with revision 20 with some minor glitches (see BUGS).
|
||||
|
||||
|
||||
TODO
|
||||
------
|
||||
- Finish music support (volume, looping perhaps?)
|
||||
- Check if background resources are used before purging them when changing scenes - check
|
||||
the comments inside sfLoadScene.
|
||||
- Sometimes, some stray lines are drawn below fonts - probably because the text doesn't
|
||||
fit inside the screen and the surface is cut off incorrectly.
|
||||
- Fix some occasional artifacts on screen (e.g. when text is shown over an animation)
|
||||
sometimes.
|
||||
- Pathfinding bugs in Fort Apache and the Indian village - e.g. when moving to the door
|
||||
on the right, the hero walks around a big path. Same when moving to the tent on the top
|
||||
in the indian village.
|
||||
- When getting the piano player off the well, he walks up facing to the left (his side
|
||||
animation is shown).
|
||||
- Load/start sound and music of the saved scene when loading.
|
||||
- Some sounds are cut off prematurely (e.g. when an animation finishes before the sound
|
||||
sample, when changing scenes and a dialog is immediately started afterwards).
|
||||
- When saving a game, save the whole screen when an animation is playing
|
||||
e.g. when playing animations (the script controlled ones, not the movies) it should save
|
||||
the whole screen when the game is saved when such an animation is running since it uses
|
||||
delta-frames so currently, once restored, the screen is wrong. This is only observed in
|
||||
a few places.
|
||||
|
||||
|
||||
BUGS
|
||||
------
|
||||
None known (see TODO).
|
||||
|
||||
DONE
|
||||
------
|
||||
- Crashes sometimes on scene changes
|
||||
(I guess because some talktext is still running although the slot used by it has changed)
|
||||
Crashes sometimes (e.g. when inside the barn and going up the ladder)
|
||||
These crashes are caused by non-stopping background sounds, see TODO above.
|
||||
- The game music is in XMIDI. I'm assuming that the rest of the formats were because of badly
|
||||
dumped resources - finalize() Calls were missing and the resulting music could contain anything.
|
||||
- Implement dirty rectangles (low priority)
|
||||
- Optimize segment mask redrawing (only redraw what's neccessary)
|
||||
- Add movie playback functionality (movie format is known, needs implementing)
|
||||
- Add sound support (after rewrite of the resource system)
|
||||
- Still some clipping issues (walking upstairs in the saloon, when on top of the tower in the hideout)
|
||||
- Add game menu
|
||||
- Rewrite from scratch with only minor stuff from the disasm since the original menu is one huge
|
||||
messy function
|
||||
- Rewrite the resource system to something similar as used by M4
|
||||
- each resource type gets its own class, the resource cache manages the resource instances
|
||||
- generic resource class which has a method that creates a MemoryReadStream
|
||||
- rewrite parts of the engine to use the new resource system
|
||||
- Extend savegame format (savegame version, description, more data)
|
164
engines/toltecs/animation.cpp
Normal file
164
engines/toltecs/animation.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/animation.h"
|
||||
#include "toltecs/palette.h"
|
||||
#include "toltecs/screen.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
AnimationPlayer::AnimationPlayer(ToltecsEngine *vm) : _vm(vm) {
|
||||
_animBuffer = new byte[262144];
|
||||
}
|
||||
|
||||
AnimationPlayer::~AnimationPlayer() {
|
||||
delete[] _animBuffer;
|
||||
}
|
||||
|
||||
void AnimationPlayer::start(uint resIndex) {
|
||||
debug(1, "AnimationPlayer::start(%d)", resIndex);
|
||||
|
||||
_resIndex = resIndex;
|
||||
|
||||
_vm->_arc->openResource(_resIndex);
|
||||
_height = _vm->_arc->readUint16LE();
|
||||
_width = _vm->_arc->readUint16LE();
|
||||
_frameCount = _vm->_arc->readUint16LE();
|
||||
_vm->_arc->read(_vm->_palette->getAnimPalette(), 768);
|
||||
_curFrameSize = _vm->_arc->readUint32LE();
|
||||
_nextFrameOffset = _curFrameSize + 782;
|
||||
_vm->_arc->read(_animBuffer, _curFrameSize);
|
||||
_nextFrameSize = _vm->_arc->readUint32LE();
|
||||
_vm->_arc->closeResource();
|
||||
|
||||
debug(1, "AnimationPlayer::start() width = %d; height = %d; frameCount = %d", _width, _height, _frameCount);
|
||||
|
||||
_vm->_sceneWidth = _width;
|
||||
_vm->_sceneHeight = _height;
|
||||
|
||||
unpackFrame();
|
||||
|
||||
_keepFrameCounter = 0;
|
||||
_frameNumber = 0;
|
||||
// TODO mov screenFlag01, 0FFFFh
|
||||
// TODO mov animDrawFrameFlag, 0FFFFh
|
||||
|
||||
_firstNextFrameOffset = _nextFrameOffset;
|
||||
_firstCurFrameSize = _curFrameSize;
|
||||
_firstNextFrameSize = _nextFrameSize;
|
||||
|
||||
}
|
||||
|
||||
void AnimationPlayer::nextFrame() {
|
||||
debug(1, "AnimationPlayer::nextFrame()");
|
||||
|
||||
if (_frameNumber == _frameCount) {
|
||||
_nextFrameOffset = _firstNextFrameOffset;
|
||||
_curFrameSize = _firstCurFrameSize;
|
||||
_nextFrameSize = _firstNextFrameSize;
|
||||
_frameNumber = 1;
|
||||
} else {
|
||||
_frameNumber++;
|
||||
}
|
||||
|
||||
debug(1, "AnimationPlayer::nextFrame() frameNumber = %d", _frameNumber);
|
||||
|
||||
if (_keepFrameCounter > 0) {
|
||||
_keepFrameCounter--;
|
||||
return;
|
||||
}
|
||||
|
||||
_vm->_arc->openResource(_resIndex);
|
||||
_vm->_arc->seek(_nextFrameOffset, SEEK_CUR);
|
||||
_curFrameSize = _nextFrameSize;
|
||||
|
||||
if (_curFrameSize == 0)
|
||||
_curFrameSize = 1;
|
||||
|
||||
_vm->_arc->read(_animBuffer, _curFrameSize);
|
||||
_nextFrameSize = _vm->_arc->readUint32LE();
|
||||
_nextFrameOffset += _curFrameSize + 4;
|
||||
|
||||
if (_curFrameSize > 1) {
|
||||
unpackFrame();
|
||||
// TODO mov animDrawFrameFlag, 0FFFFh
|
||||
} else {
|
||||
_keepFrameCounter = _animBuffer[0] - 1;
|
||||
// TODO mov animDrawFrameFlag, 0
|
||||
}
|
||||
|
||||
_vm->_arc->closeResource();
|
||||
|
||||
|
||||
}
|
||||
|
||||
int16 AnimationPlayer::getStatus() {
|
||||
debug(1, "AnimationPlayer::getStatus()");
|
||||
int16 status = -1;
|
||||
if (_frameNumber == _frameCount)
|
||||
status = 0;
|
||||
else if (_frameNumber == _frameCount - 1)
|
||||
status = 1;
|
||||
debug(1, "AnimationPlayer::getStatus() status = %d", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
void AnimationPlayer::unpackFrame() {
|
||||
_vm->_screen->unpackRle(_animBuffer, _vm->_screen->_frontScreen, _width, _height);
|
||||
_vm->_screen->unpackRle(_animBuffer, _vm->_screen->_backScreen, _width, _height);
|
||||
_vm->_screen->_fullRefresh = true;
|
||||
}
|
||||
|
||||
void AnimationPlayer::saveState(Common::WriteStream *out) {
|
||||
out->writeUint16LE(_resIndex);
|
||||
// NOTE: The original engine doesn't save width/height, but we do
|
||||
out->writeUint16LE(_width);
|
||||
out->writeUint16LE(_height);
|
||||
out->writeUint16LE(_frameCount);
|
||||
out->writeUint16LE(_frameNumber);
|
||||
out->writeUint32LE(_keepFrameCounter);
|
||||
out->writeUint32LE(_curFrameSize);
|
||||
out->writeUint32LE(_nextFrameSize);
|
||||
out->writeUint32LE(_nextFrameOffset);
|
||||
out->writeUint32LE(_firstCurFrameSize);
|
||||
out->writeUint32LE(_firstNextFrameSize);
|
||||
out->writeUint32LE(_firstNextFrameOffset);
|
||||
}
|
||||
|
||||
void AnimationPlayer::loadState(Common::ReadStream *in) {
|
||||
_resIndex = in->readUint16LE();
|
||||
_width = in->readUint16LE();
|
||||
_height = in->readUint16LE();
|
||||
_frameCount = in->readUint16LE();
|
||||
_frameNumber = in->readUint16LE();
|
||||
_keepFrameCounter = in->readUint32LE();
|
||||
_curFrameSize = in->readUint32LE();
|
||||
_nextFrameSize = in->readUint32LE();
|
||||
_nextFrameOffset = in->readUint32LE();
|
||||
_firstCurFrameSize = in->readUint32LE();
|
||||
_firstNextFrameSize = in->readUint32LE();
|
||||
_firstNextFrameOffset = in->readUint32LE();
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
69
engines/toltecs/animation.h
Normal file
69
engines/toltecs/animation.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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_ANIMATION_H
|
||||
#define TOLTECS_ANIMATION_H
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/resource.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
class AnimationPlayer {
|
||||
public:
|
||||
AnimationPlayer(ToltecsEngine *vm);
|
||||
~AnimationPlayer();
|
||||
|
||||
void start(uint resIndex);
|
||||
void nextFrame();
|
||||
int16 getStatus();
|
||||
uint16 getFrameNumber() const { return _frameNumber; }
|
||||
|
||||
void saveState(Common::WriteStream *out);
|
||||
void loadState(Common::ReadStream *in);
|
||||
|
||||
//protected:
|
||||
public:
|
||||
ToltecsEngine *_vm;
|
||||
|
||||
// 262144
|
||||
byte *_animBuffer;
|
||||
|
||||
uint16 _resIndex;
|
||||
|
||||
uint16 _width, _height;
|
||||
uint16 _frameNumber, _frameCount;
|
||||
uint32 _keepFrameCounter;
|
||||
|
||||
uint32 _curFrameSize;
|
||||
uint32 _nextFrameSize, _nextFrameOffset;
|
||||
|
||||
uint32 _firstNextFrameOffset, _firstCurFrameSize, _firstNextFrameSize;
|
||||
|
||||
void unpackFrame();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_ANIMATION_H */
|
287
engines/toltecs/detection.cpp
Normal file
287
engines/toltecs/detection.cpp
Normal file
@ -0,0 +1,287 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/str-array.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
struct ToltecsGameDescription {
|
||||
ADGameDescription desc;
|
||||
};
|
||||
|
||||
uint32 ToltecsEngine::getFeatures() const {
|
||||
return _gameDescription->desc.flags;
|
||||
}
|
||||
|
||||
Common::Language ToltecsEngine::getLanguage() const {
|
||||
return _gameDescription->desc.language;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const PlainGameDescriptor toltecsGames[] = {
|
||||
{"toltecs", "3 Skulls of the Toltecs"},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
static const ToltecsGameDescription gameDescriptions[] = {
|
||||
|
||||
{
|
||||
// 3 Skulls of the Toltecs English version
|
||||
{
|
||||
"toltecs",
|
||||
0,
|
||||
AD_ENTRY1s("WESTERN", "05472037e9cfde146e953c434e74f0f4", 337643527),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformPC,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NONE)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// 3 Skulls of the Toltecs Russian version
|
||||
{
|
||||
"toltecs",
|
||||
0,
|
||||
AD_ENTRY1s("WESTERN", "ba1742d3193b68ceb9434e2ab7a09a9b", 391462783),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformPC,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NONE)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// 3 Skulls of the Toltecs German version
|
||||
{
|
||||
"toltecs",
|
||||
0,
|
||||
AD_ENTRY1s("WESTERN", "1a3292bad8e0bb5701800c73531dd75e", 345176617),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformPC,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NONE)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// 3 Skulls of the Toltecs German Demo version
|
||||
{
|
||||
"toltecs",
|
||||
0,
|
||||
AD_ENTRY1s("WESTERN", "1c85e82712d24f1d5c1ea2a66ddd75c2", 47730038),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformPC,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NONE)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// 3 Skulls of the Toltecs French version
|
||||
{
|
||||
"toltecs",
|
||||
0,
|
||||
AD_ENTRY1s("WESTERN", "4fb845635cbdac732453fe23be350df9", 327269545),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformPC,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NONE)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// 3 Skulls of the Toltecs Spanish version
|
||||
{
|
||||
"toltecs",
|
||||
0,
|
||||
AD_ENTRY1s("WESTERN", "479f468beccc1b0ce5873ec523d1380e", 308391018),
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformPC,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GUIO_NONE)
|
||||
},
|
||||
},
|
||||
|
||||
{ AD_TABLE_END_MARKER }
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
class ToltecsMetaEngine : public AdvancedMetaEngine {
|
||||
public:
|
||||
ToltecsMetaEngine() : AdvancedMetaEngine(Toltecs::gameDescriptions, sizeof(Toltecs::ToltecsGameDescription), toltecsGames) {
|
||||
_singleid = "toltecs";
|
||||
}
|
||||
|
||||
virtual const char *getName() const {
|
||||
return "Toltecs Engine";
|
||||
}
|
||||
|
||||
virtual const char *getOriginalCopyright() const {
|
||||
return "Toltecs Engine Revistronic (C) 1996";
|
||||
}
|
||||
|
||||
virtual bool hasFeature(MetaEngineFeature f) const;
|
||||
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
|
||||
SaveStateList listSaves(const char *target) const;
|
||||
virtual int getMaximumSaveSlot() const;
|
||||
void removeSaveState(const char *target, int slot) const;
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
|
||||
};
|
||||
|
||||
bool ToltecsMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
// (f == kSupportsDeleteSave) ||
|
||||
(f == kSavesSupportMetaInfo) ||
|
||||
(f == kSavesSupportThumbnail);
|
||||
}
|
||||
|
||||
bool Toltecs::ToltecsEngine::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsRTL) ||
|
||||
(f == kSupportsLoadingDuringRuntime) ||
|
||||
(f == kSupportsSavingDuringRuntime);
|
||||
}
|
||||
|
||||
bool ToltecsMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||
const Toltecs::ToltecsGameDescription *gd = (const Toltecs::ToltecsGameDescription *)desc;
|
||||
if (gd) {
|
||||
*engine = new Toltecs::ToltecsEngine(syst, gd);
|
||||
}
|
||||
return gd != 0;
|
||||
}
|
||||
|
||||
SaveStateList ToltecsMetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Toltecs::ToltecsEngine::SaveHeader header;
|
||||
Common::String pattern = target;
|
||||
pattern += ".???";
|
||||
|
||||
Common::StringArray filenames;
|
||||
filenames = saveFileMan->listSavefiles(pattern.c_str());
|
||||
Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
|
||||
|
||||
SaveStateList saveList;
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 3);
|
||||
|
||||
if (slotNum >= 0 && slotNum <= 999) {
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
|
||||
if (in) {
|
||||
if (Toltecs::ToltecsEngine::readSaveHeader(in, false, header) == Toltecs::ToltecsEngine::kRSHENoError) {
|
||||
saveList.push_back(SaveStateDescriptor(slotNum, header.description));
|
||||
}
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return saveList;
|
||||
}
|
||||
|
||||
int ToltecsMetaEngine::getMaximumSaveSlot() const {
|
||||
return 999;
|
||||
}
|
||||
|
||||
void ToltecsMetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
// Slot 0 can't be deleted, it's for restarting the game(s)
|
||||
if (slot == 0)
|
||||
return;
|
||||
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Common::String filename = Toltecs::ToltecsEngine::getSavegameFilename(target, slot);
|
||||
|
||||
saveFileMan->removeSavefile(filename.c_str());
|
||||
|
||||
Common::StringArray filenames;
|
||||
Common::String pattern = target;
|
||||
pattern += ".???";
|
||||
filenames = saveFileMan->listSavefiles(pattern.c_str());
|
||||
Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
|
||||
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 3);
|
||||
|
||||
// Rename every slot greater than the deleted slot,
|
||||
// Also do not rename quicksaves.
|
||||
if (slotNum > slot && slotNum < 990) {
|
||||
// FIXME: Our savefile renaming done here is inconsitent with what we do in
|
||||
// GUI_v2::deleteMenu. While here we rename every slot with a greater equal
|
||||
// number of the deleted slot to deleted slot, deleted slot + 1 etc.,
|
||||
// we only rename the following slots in GUI_v2::deleteMenu until a slot
|
||||
// is missing.
|
||||
saveFileMan->renameSavefile(file->c_str(), filename.c_str());
|
||||
|
||||
filename = Toltecs::ToltecsEngine::getSavegameFilename(target, ++slot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SaveStateDescriptor ToltecsMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
||||
Common::String filename = Toltecs::ToltecsEngine::getSavegameFilename(target, slot);
|
||||
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
|
||||
|
||||
if (in) {
|
||||
Toltecs::ToltecsEngine::SaveHeader header;
|
||||
Toltecs::ToltecsEngine::kReadSaveHeaderError error;
|
||||
|
||||
error = Toltecs::ToltecsEngine::readSaveHeader(in, true, header);
|
||||
delete in;
|
||||
|
||||
if (error == Toltecs::ToltecsEngine::kRSHENoError) {
|
||||
SaveStateDescriptor desc(slot, header.description);
|
||||
|
||||
desc.setDeletableFlag(false);
|
||||
desc.setWriteProtectedFlag(false);
|
||||
desc.setThumbnail(header.thumbnail);
|
||||
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(TOLTECS)
|
||||
REGISTER_PLUGIN_DYNAMIC(TOLTECS, PLUGIN_TYPE_ENGINE, ToltecsMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(TOLTECS, PLUGIN_TYPE_ENGINE, ToltecsMetaEngine);
|
||||
#endif
|
638
engines/toltecs/menu.cpp
Normal file
638
engines/toltecs/menu.cpp
Normal file
@ -0,0 +1,638 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/savefile.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/menu.h"
|
||||
#include "toltecs/palette.h"
|
||||
#include "toltecs/render.h"
|
||||
#include "toltecs/resource.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
MenuSystem::MenuSystem(ToltecsEngine *vm) : _vm(vm) {
|
||||
}
|
||||
|
||||
MenuSystem::~MenuSystem() {
|
||||
}
|
||||
|
||||
int MenuSystem::run() {
|
||||
|
||||
//debug("MenuSystem::run()");
|
||||
|
||||
_background = new Graphics::Surface();
|
||||
_background->create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
// Save original background
|
||||
Graphics::Surface backgroundOrig;
|
||||
backgroundOrig.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
|
||||
memcpy(backgroundOrig.getBasePtr(0,0), _vm->_screen->_frontScreen, 640 * 400);
|
||||
|
||||
_currMenuID = kMenuIdNone;
|
||||
_newMenuID = kMenuIdMain;
|
||||
_currItemID = kItemIdNone;
|
||||
_editingDescription = false;
|
||||
_cfgText = true;
|
||||
_cfgVoices = true;
|
||||
_cfgMasterVolume = 10;
|
||||
_cfgVoicesVolume = 10;
|
||||
_cfgMusicVolume = 10;
|
||||
_cfgSoundFXVolume = 10;
|
||||
_cfgBackgroundVolume = 10;
|
||||
_running = true;
|
||||
_top = 30 - _vm->_guiHeight / 2;
|
||||
_needRedraw = false;
|
||||
|
||||
// TODO: buildColorTransTable2
|
||||
_vm->_palette->buildColorTransTable(0, 16, 7);
|
||||
|
||||
_vm->_screen->_renderQueue->clear();
|
||||
// Draw the menu background and frame
|
||||
_vm->_screen->blastSprite(0x140 + _vm->_cameraX, 0x175 + _vm->_cameraY, 0, 1, 0x4000);
|
||||
shadeRect(60, 39, 520, 246, 30, 94);
|
||||
|
||||
memcpy(_background->pixels, _vm->_screen->_frontScreen, 640 * 400);
|
||||
|
||||
while (_running) {
|
||||
update();
|
||||
_vm->_system->updateScreen();
|
||||
}
|
||||
|
||||
// Restore original background
|
||||
memcpy(_vm->_screen->_frontScreen, backgroundOrig.getBasePtr(0,0), 640 * 400);
|
||||
_vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen, 640, 0, 0, 640, 400);
|
||||
_vm->_system->updateScreen();
|
||||
|
||||
// Cleanup
|
||||
backgroundOrig.free();
|
||||
_background->free();
|
||||
delete _background;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MenuSystem::update() {
|
||||
|
||||
if (_currMenuID != _newMenuID) {
|
||||
_currMenuID = _newMenuID;
|
||||
//debug("_currMenuID = %d", _currMenuID);
|
||||
initMenu(_currMenuID);
|
||||
}
|
||||
|
||||
handleEvents();
|
||||
|
||||
if (_needRedraw) {
|
||||
//_vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen + 39 * 640 + 60, 640, 60, 39, 520, 247);
|
||||
_vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen, 640, 0, 0, 640, 400);
|
||||
//debug("redraw");
|
||||
_needRedraw = false;
|
||||
}
|
||||
|
||||
_vm->_system->delayMillis(5);
|
||||
|
||||
}
|
||||
|
||||
void MenuSystem::handleEvents() {
|
||||
|
||||
Common::Event event;
|
||||
Common::EventManager *eventMan = _vm->_system->getEventManager();
|
||||
while (eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
handleKeyDown(event.kbd);
|
||||
break;
|
||||
case Common::EVENT_QUIT:
|
||||
_running = false;
|
||||
break;
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
handleMouseMove(event.mouse.x, event.mouse.y);
|
||||
break;
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
handleMouseClick(event.mouse.x, event.mouse.y);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MenuSystem::addClickTextItem(ItemID id, int x, int y, int w, uint fontNum, const char *caption, byte defaultColor, byte activeColor) {
|
||||
Item item;
|
||||
item.id = id;
|
||||
item.defaultColor = defaultColor;
|
||||
item.activeColor = activeColor;
|
||||
item.x = x;
|
||||
item.y = y;
|
||||
item.w = w;
|
||||
item.fontNum = fontNum;
|
||||
setItemCaption(&item, caption);
|
||||
_items.push_back(item);
|
||||
}
|
||||
|
||||
void MenuSystem::drawItem(ItemID itemID, bool active) {
|
||||
Item *item = getItem(itemID);
|
||||
if (item) {
|
||||
byte color = active ? item->activeColor : item->defaultColor;
|
||||
drawString(item->rect.left, item->y, 0, item->fontNum, color, item->caption.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::handleMouseMove(int x, int y) {
|
||||
if (!_editingDescription) {
|
||||
ItemID newItemID = findItemAt(x, y);
|
||||
if (_currItemID != newItemID) {
|
||||
leaveItem(_currItemID);
|
||||
_currItemID = newItemID;
|
||||
enterItem(newItemID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::handleMouseClick(int x, int y) {
|
||||
if (!_editingDescription) {
|
||||
ItemID id = findItemAt(x, y);
|
||||
clickItem(id);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::handleKeyDown(const Common::KeyState& kbd) {
|
||||
if (_editingDescription) {
|
||||
if (kbd.keycode >= Common::KEYCODE_SPACE && kbd.keycode <= Common::KEYCODE_z) {
|
||||
_editingDescriptionItem->caption += kbd.ascii;
|
||||
restoreRect(_editingDescriptionItem->rect.left, _editingDescriptionItem->rect.top,
|
||||
_editingDescriptionItem->rect.width() + 1, _editingDescriptionItem->rect.height() - 2);
|
||||
setItemCaption(_editingDescriptionItem, _editingDescriptionItem->caption.c_str());
|
||||
drawItem(_editingDescriptionID, true);
|
||||
} else if (kbd.keycode == Common::KEYCODE_BACKSPACE) {
|
||||
_editingDescriptionItem->caption.deleteLastChar();
|
||||
restoreRect(_editingDescriptionItem->rect.left, _editingDescriptionItem->rect.top,
|
||||
_editingDescriptionItem->rect.width() + 1, _editingDescriptionItem->rect.height() - 2);
|
||||
setItemCaption(_editingDescriptionItem, _editingDescriptionItem->caption.c_str());
|
||||
drawItem(_editingDescriptionID, true);
|
||||
} else if (kbd.keycode == Common::KEYCODE_RETURN) {
|
||||
SavegameItem *savegameItem = getSavegameItemByID(_editingDescriptionID);
|
||||
_editingDescription = false;
|
||||
_vm->requestSavegame(savegameItem->_slotNum, _editingDescriptionItem->caption);
|
||||
_running = false;
|
||||
} else if (kbd.keycode == Common::KEYCODE_ESCAPE) {
|
||||
_editingDescription = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemID MenuSystem::findItemAt(int x, int y) {
|
||||
for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) {
|
||||
if ((*iter).rect.contains(x, y))
|
||||
return (*iter).id;
|
||||
}
|
||||
return kItemIdNone;
|
||||
}
|
||||
|
||||
MenuSystem::Item *MenuSystem::getItem(ItemID id) {
|
||||
for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) {
|
||||
if ((*iter).id == id)
|
||||
return &(*iter);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void MenuSystem::setItemCaption(Item *item, const char *caption) {
|
||||
Font font(_vm->_res->load(_vm->_screen->getFontResIndex(item->fontNum))->data);
|
||||
int width = font.getTextWidth((const byte*)caption);
|
||||
int height = font.getHeight();
|
||||
item->rect = Common::Rect(item->x, item->y - height, item->x + width, item->y);
|
||||
if (item->w) {
|
||||
item->rect.translate(item->w - width / 2, 0);
|
||||
}
|
||||
item->caption = caption;
|
||||
}
|
||||
|
||||
void MenuSystem::initMenu(MenuID menuID) {
|
||||
|
||||
int newSlotNum;
|
||||
|
||||
_items.clear();
|
||||
|
||||
memcpy(_vm->_screen->_frontScreen, _background->pixels, 640 * 400);
|
||||
|
||||
switch (menuID) {
|
||||
case kMenuIdMain:
|
||||
drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrWhatCanIDoForYou));
|
||||
addClickTextItem(kItemIdLoad, 0, 115, 320, 0, _vm->getSysString(kStrLoad), 229, 255);
|
||||
addClickTextItem(kItemIdSave, 0, 135, 320, 0, _vm->getSysString(kStrSave), 229, 255);
|
||||
addClickTextItem(kItemIdToggleText, 0, 165, 320, 0, _vm->getSysString(kStrTextOn), 229, 255);
|
||||
addClickTextItem(kItemIdToggleVoices, 0, 185, 320, 0, _vm->getSysString(kStrVoicesOn), 229, 255);
|
||||
addClickTextItem(kItemIdVolumesMenu, 0, 215, 320, 0, _vm->getSysString(kStrVolume), 229, 255);
|
||||
addClickTextItem(kItemIdPlay, 0, 245, 320, 0, _vm->getSysString(kStrPlay), 229, 255);
|
||||
addClickTextItem(kItemIdQuit, 0, 275, 320, 0, _vm->getSysString(kStrQuit), 229, 255);
|
||||
break;
|
||||
case kMenuIdLoad:
|
||||
drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrLoadGame));
|
||||
addClickTextItem(kItemIdSavegameUp, 0, 155, 545, 1, "^", 255, 253);
|
||||
addClickTextItem(kItemIdSavegameDown, 0, 195, 545, 1, "\\", 255, 253);
|
||||
addClickTextItem(kItemIdCancel, 0, 275, 320, 0, _vm->getSysString(kStrCancel), 255, 253);
|
||||
addClickTextItem(kItemIdSavegame1, 0, 115 + 20 * 0, 300, 0, "SAVEGAME 1", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame2, 0, 115 + 20 * 1, 300, 0, "SAVEGAME 2", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame3, 0, 115 + 20 * 2, 300, 0, "SAVEGAME 3", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame4, 0, 115 + 20 * 3, 300, 0, "SAVEGAME 4", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame5, 0, 115 + 20 * 4, 300, 0, "SAVEGAME 5", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame6, 0, 115 + 20 * 5, 300, 0, "SAVEGAME 6", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame7, 0, 115 + 20 * 6, 300, 0, "SAVEGAME 7", 231, 234);
|
||||
loadSavegamesList();
|
||||
setSavegameCaptions();
|
||||
break;
|
||||
case kMenuIdSave:
|
||||
drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrSaveGame));
|
||||
addClickTextItem(kItemIdSavegameUp, 0, 155, 545, 1, "^", 255, 253);
|
||||
addClickTextItem(kItemIdSavegameDown, 0, 195, 545, 1, "\\", 255, 253);
|
||||
addClickTextItem(kItemIdCancel, 0, 275, 320, 0, _vm->getSysString(kStrCancel), 255, 253);
|
||||
addClickTextItem(kItemIdSavegame1, 0, 115 + 20 * 0, 300, 0, "SAVEGAME 1", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame2, 0, 115 + 20 * 1, 300, 0, "SAVEGAME 2", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame3, 0, 115 + 20 * 2, 300, 0, "SAVEGAME 3", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame4, 0, 115 + 20 * 3, 300, 0, "SAVEGAME 4", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame5, 0, 115 + 20 * 4, 300, 0, "SAVEGAME 5", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame6, 0, 115 + 20 * 5, 300, 0, "SAVEGAME 6", 231, 234);
|
||||
addClickTextItem(kItemIdSavegame7, 0, 115 + 20 * 6, 300, 0, "SAVEGAME 7", 231, 234);
|
||||
newSlotNum = loadSavegamesList() + 1;
|
||||
_savegames.push_back(SavegameItem(newSlotNum, Common::String::format("GAME %03d", _savegames.size() + 1)));
|
||||
setSavegameCaptions();
|
||||
break;
|
||||
case kMenuIdVolumes:
|
||||
drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrAdjustVolume));
|
||||
drawString(0, 130, 200, 0, 246, _vm->getSysString(kStrMaster));
|
||||
drawString(0, 155, 200, 0, 244, _vm->getSysString(kStrVoices));
|
||||
drawString(0, 180, 200, 0, 244, _vm->getSysString(kStrMusic));
|
||||
drawString(0, 205, 200, 0, 244, _vm->getSysString(kStrSoundFx));
|
||||
drawString(0, 230, 200, 0, 244, _vm->getSysString(kStrBackground));
|
||||
addClickTextItem(kItemIdDone, 0, 275, 200, 0, _vm->getSysString(kStrDone), 229, 253);
|
||||
addClickTextItem(kItemIdCancel, 0, 275, 440, 0, _vm->getSysString(kStrCancel), 229, 253);
|
||||
addClickTextItem(kItemIdMasterDown, 0, 130 + 25 * 0, 348, 1, "[", 229, 253);
|
||||
addClickTextItem(kItemIdVoicesDown, 0, 130 + 25 * 1, 348, 1, "[", 229, 253);
|
||||
addClickTextItem(kItemIdMusicDown, 0, 130 + 25 * 2, 348, 1, "[", 229, 253);
|
||||
addClickTextItem(kItemIdSoundFXDown, 0, 130 + 25 * 3, 348, 1, "[", 229, 253);
|
||||
addClickTextItem(kItemIdBackgroundDown, 0, 130 + 25 * 4, 348, 1, "[", 229, 253);
|
||||
addClickTextItem(kItemIdMasterUp, 0, 130 + 25 * 0, 372, 1, "]", 229, 253);
|
||||
addClickTextItem(kItemIdVoicesUp, 0, 130 + 25 * 1, 372, 1, "]", 229, 253);
|
||||
addClickTextItem(kItemIdMusicUp, 0, 130 + 25 * 2, 372, 1, "]", 229, 253);
|
||||
addClickTextItem(kItemIdSoundFXUp, 0, 130 + 25 * 3, 372, 1, "]", 229, 253);
|
||||
addClickTextItem(kItemIdBackgroundUp, 0, 130 + 25 * 4, 372, 1, "]", 229, 253);
|
||||
drawVolumeBar(kItemIdMaster);
|
||||
drawVolumeBar(kItemIdVoices);
|
||||
drawVolumeBar(kItemIdMusic);
|
||||
drawVolumeBar(kItemIdSoundFX);
|
||||
drawVolumeBar(kItemIdBackground);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) {
|
||||
drawItem((*iter).id, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MenuSystem::enterItem(ItemID id) {
|
||||
drawItem(id, true);
|
||||
}
|
||||
|
||||
void MenuSystem::leaveItem(ItemID id) {
|
||||
drawItem(id, false);
|
||||
}
|
||||
|
||||
void MenuSystem::clickItem(ItemID id) {
|
||||
//Item *item = getItem(id);
|
||||
switch (id) {
|
||||
// Main menu
|
||||
case kItemIdSave:
|
||||
_newMenuID = kMenuIdSave;
|
||||
break;
|
||||
case kItemIdLoad:
|
||||
_newMenuID = kMenuIdLoad;
|
||||
break;
|
||||
case kItemIdToggleText:
|
||||
setCfgText(!_cfgText, true);
|
||||
if (!_cfgVoices && !_cfgText)
|
||||
setCfgVoices(true, false);
|
||||
break;
|
||||
case kItemIdToggleVoices:
|
||||
setCfgVoices(!_cfgVoices, true);
|
||||
if (!_cfgVoices && !_cfgText)
|
||||
setCfgText(true, false);
|
||||
break;
|
||||
case kItemIdVolumesMenu:
|
||||
//debug("kItemIdVolumesMenu");
|
||||
_newMenuID = kMenuIdVolumes;
|
||||
break;
|
||||
case kItemIdPlay:
|
||||
//debug("kItemIdPlay");
|
||||
_running = false;
|
||||
break;
|
||||
case kItemIdQuit:
|
||||
_running = false;
|
||||
_vm->quitGame();
|
||||
break;
|
||||
// Volumes menu
|
||||
case kItemIdMasterUp:
|
||||
changeVolumeBar(kItemIdMaster, +1);
|
||||
break;
|
||||
case kItemIdVoicesUp:
|
||||
changeVolumeBar(kItemIdVoices, +1);
|
||||
break;
|
||||
case kItemIdMusicUp:
|
||||
changeVolumeBar(kItemIdMusic, +1);
|
||||
break;
|
||||
case kItemIdSoundFXUp:
|
||||
changeVolumeBar(kItemIdSoundFX, +1);
|
||||
break;
|
||||
case kItemIdBackgroundUp:
|
||||
changeVolumeBar(kItemIdBackground, +1);
|
||||
break;
|
||||
case kItemIdMasterDown:
|
||||
changeVolumeBar(kItemIdMaster, -1);
|
||||
break;
|
||||
case kItemIdVoicesDown:
|
||||
changeVolumeBar(kItemIdVoices, -1);
|
||||
break;
|
||||
case kItemIdMusicDown:
|
||||
changeVolumeBar(kItemIdMusic, -1);
|
||||
break;
|
||||
case kItemIdSoundFXDown:
|
||||
changeVolumeBar(kItemIdSoundFX, -1);
|
||||
break;
|
||||
case kItemIdBackgroundDown:
|
||||
changeVolumeBar(kItemIdBackground, -1);
|
||||
break;
|
||||
case kItemIdCancel:
|
||||
_newMenuID = kMenuIdMain;
|
||||
break;
|
||||
// Save/Load menu
|
||||
case kItemIdSavegame1:
|
||||
case kItemIdSavegame2:
|
||||
case kItemIdSavegame3:
|
||||
case kItemIdSavegame4:
|
||||
case kItemIdSavegame5:
|
||||
case kItemIdSavegame6:
|
||||
case kItemIdSavegame7:
|
||||
clickSavegameItem(id);
|
||||
break;
|
||||
case kItemIdDone:
|
||||
_newMenuID = kMenuIdMain;
|
||||
break;
|
||||
case kItemIdSavegameUp:
|
||||
scrollSavegames(-6);
|
||||
break;
|
||||
case kItemIdSavegameDown:
|
||||
scrollSavegames(+6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::restoreRect(int x, int y, int w, int h) {
|
||||
byte *src = (byte*)_background->getBasePtr(x, y);
|
||||
byte *dst = _vm->_screen->_frontScreen + x + y * 640;
|
||||
while (h--) {
|
||||
memcpy(dst, src, w);
|
||||
src += 640;
|
||||
dst += 640;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::shadeRect(int x, int y, int w, int h, byte color1, byte color2) {
|
||||
byte *src = (byte*)_background->getBasePtr(x, y);
|
||||
for (int xc = 0; xc < w; xc++) {
|
||||
src[xc] = color2;
|
||||
src[xc + h * 640] = color1;
|
||||
}
|
||||
src += 640;
|
||||
w -= 1;
|
||||
h -= 1;
|
||||
while (h--) {
|
||||
src[0] = color2;
|
||||
src[w] = color1;
|
||||
src += 640;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::drawString(int16 x, int16 y, int w, uint fontNum, byte color, const char *text) {
|
||||
fontNum = _vm->_screen->getFontResIndex(fontNum);
|
||||
Font font(_vm->_res->load(fontNum)->data);
|
||||
if (w) {
|
||||
x = x + w - font.getTextWidth((const byte*)text) / 2;
|
||||
}
|
||||
_vm->_screen->drawString(x, y - font.getHeight(), color, fontNum, (const byte*)text, -1, NULL, true);
|
||||
_needRedraw = true;
|
||||
}
|
||||
|
||||
int MenuSystem::loadSavegamesList() {
|
||||
|
||||
int maxSlotNum = -1;
|
||||
|
||||
_savegameListTopIndex = 0;
|
||||
_savegames.clear();
|
||||
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Toltecs::ToltecsEngine::SaveHeader header;
|
||||
Common::String pattern = _vm->getTargetName();
|
||||
pattern += ".???";
|
||||
|
||||
Common::StringArray filenames;
|
||||
filenames = saveFileMan->listSavefiles(pattern.c_str());
|
||||
Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
|
||||
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); file++) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 3);
|
||||
if (slotNum > maxSlotNum)
|
||||
maxSlotNum = slotNum;
|
||||
|
||||
if (slotNum >= 0 && slotNum <= 999) {
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
|
||||
if (in) {
|
||||
if (Toltecs::ToltecsEngine::readSaveHeader(in, false, header) == Toltecs::ToltecsEngine::kRSHENoError) {
|
||||
_savegames.push_back(SavegameItem(slotNum, header.description));
|
||||
//debug("%s -> %s", file->c_str(), header.description.c_str());
|
||||
}
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxSlotNum;
|
||||
}
|
||||
|
||||
MenuSystem::SavegameItem *MenuSystem::getSavegameItemByID(ItemID id) {
|
||||
switch (id) {
|
||||
case kItemIdSavegame1:
|
||||
case kItemIdSavegame2:
|
||||
case kItemIdSavegame3:
|
||||
case kItemIdSavegame4:
|
||||
case kItemIdSavegame5:
|
||||
case kItemIdSavegame6:
|
||||
case kItemIdSavegame7:
|
||||
return &_savegames[_savegameListTopIndex + id - kItemIdSavegame1];
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::setSavegameCaptions() {
|
||||
uint index = _savegameListTopIndex;
|
||||
setItemCaption(getItem(kItemIdSavegame1), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
|
||||
setItemCaption(getItem(kItemIdSavegame2), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
|
||||
setItemCaption(getItem(kItemIdSavegame3), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
|
||||
setItemCaption(getItem(kItemIdSavegame4), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
|
||||
setItemCaption(getItem(kItemIdSavegame5), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
|
||||
setItemCaption(getItem(kItemIdSavegame6), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
|
||||
setItemCaption(getItem(kItemIdSavegame7), index < _savegames.size() ? _savegames[index++]._description.c_str() : "");
|
||||
}
|
||||
|
||||
void MenuSystem::scrollSavegames(int delta) {
|
||||
int newPos = CLIP<int>(_savegameListTopIndex + delta, 0, _savegames.size() - 1);
|
||||
_savegameListTopIndex = newPos;
|
||||
restoreRect(80, 92, 440, 140);
|
||||
setSavegameCaptions();
|
||||
drawItem(kItemIdSavegame1, false);
|
||||
drawItem(kItemIdSavegame2, false);
|
||||
drawItem(kItemIdSavegame3, false);
|
||||
drawItem(kItemIdSavegame4, false);
|
||||
drawItem(kItemIdSavegame5, false);
|
||||
drawItem(kItemIdSavegame6, false);
|
||||
drawItem(kItemIdSavegame7, false);
|
||||
}
|
||||
|
||||
void MenuSystem::clickSavegameItem(ItemID id) {
|
||||
if (_currMenuID == kMenuIdLoad) {
|
||||
SavegameItem *savegameItem = getSavegameItemByID(id);
|
||||
//debug("slotNum = [%d]; description = [%s]", savegameItem->_slotNum, savegameItem->_description.c_str());
|
||||
//_vm->loadgame(savegameItem->_filename.c_str());
|
||||
_vm->requestLoadgame(savegameItem->_slotNum);
|
||||
_running = false;
|
||||
} else {
|
||||
_editingDescription = true;
|
||||
_editingDescriptionItem = getItem(id);
|
||||
_editingDescriptionID = id;
|
||||
_editingDescriptionItem->activeColor = 249;
|
||||
_editingDescriptionItem->defaultColor = 249;
|
||||
drawItem(_editingDescriptionID, true);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::setCfgText(bool value, bool active) {
|
||||
if (_cfgText != value) {
|
||||
Item *item = getItem(kItemIdToggleText);
|
||||
_cfgText = value;
|
||||
restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2);
|
||||
setItemCaption(item, _vm->getSysString(_cfgText ? kStrTextOn : kStrTextOff));
|
||||
drawItem(kItemIdToggleText, true);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::setCfgVoices(bool value, bool active) {
|
||||
if (_cfgVoices != value) {
|
||||
Item *item = getItem(kItemIdToggleVoices);
|
||||
_cfgVoices = value;
|
||||
restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2);
|
||||
setItemCaption(item, _vm->getSysString(_cfgVoices ? kStrVoicesOn : kStrVoicesOff));
|
||||
drawItem(kItemIdToggleVoices, true);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuSystem::drawVolumeBar(ItemID itemID) {
|
||||
int w = 440, y, volume;
|
||||
char text[21];
|
||||
|
||||
switch (itemID) {
|
||||
case kItemIdMaster:
|
||||
y = 130 + 25 * 0;
|
||||
volume = _cfgMasterVolume;
|
||||
break;
|
||||
case kItemIdVoices:
|
||||
y = 130 + 25 * 1;
|
||||
volume = _cfgVoicesVolume;
|
||||
break;
|
||||
case kItemIdMusic:
|
||||
y = 130 + 25 * 2;
|
||||
volume = _cfgMusicVolume;
|
||||
break;
|
||||
case kItemIdSoundFX:
|
||||
y = 130 + 25 * 3;
|
||||
volume = _cfgSoundFXVolume;
|
||||
break;
|
||||
case kItemIdBackground:
|
||||
y = 130 + 25 * 4;
|
||||
volume = _cfgBackgroundVolume;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Font font(_vm->_res->load(_vm->_screen->getFontResIndex(1))->data);
|
||||
restoreRect(390, y - font.getHeight(), 100, 25);
|
||||
|
||||
for (int i = 0; i < volume; i++)
|
||||
text[i] = '|';
|
||||
text[volume] = 0;
|
||||
|
||||
drawString(0, y, w, 0, 246, text);
|
||||
|
||||
}
|
||||
|
||||
void MenuSystem::changeVolumeBar(ItemID itemID, int delta) {
|
||||
|
||||
int *volume, newVolume;
|
||||
|
||||
switch (itemID) {
|
||||
case kItemIdMaster:
|
||||
volume = &_cfgMasterVolume;
|
||||
break;
|
||||
case kItemIdVoices:
|
||||
volume = &_cfgVoicesVolume;
|
||||
break;
|
||||
case kItemIdMusic:
|
||||
volume = &_cfgMusicVolume;
|
||||
break;
|
||||
case kItemIdSoundFX:
|
||||
volume = &_cfgSoundFXVolume;
|
||||
break;
|
||||
case kItemIdBackground:
|
||||
volume = &_cfgBackgroundVolume;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
newVolume = CLIP(*volume + delta, 0, 20);
|
||||
|
||||
if (newVolume != *volume) {
|
||||
*volume = newVolume;
|
||||
drawVolumeBar(itemID);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
167
engines/toltecs/menu.h
Normal file
167
engines/toltecs/menu.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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_MENU_H
|
||||
#define TOLTECS_MENU_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/str-array.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
enum MenuID {
|
||||
kMenuIdNone,
|
||||
kMenuIdMain,
|
||||
kMenuIdSave,
|
||||
kMenuIdLoad,
|
||||
kMenuIdVolumes
|
||||
};
|
||||
|
||||
enum ItemID {
|
||||
kItemIdNone,
|
||||
// Main menu
|
||||
kItemIdSave,
|
||||
kItemIdLoad,
|
||||
kItemIdToggleText,
|
||||
kItemIdToggleVoices,
|
||||
kItemIdVolumesMenu,
|
||||
kItemIdPlay,
|
||||
kItemIdQuit,
|
||||
// Volumes menu
|
||||
kItemIdMasterUp,
|
||||
kItemIdVoicesUp,
|
||||
kItemIdMusicUp,
|
||||
kItemIdSoundFXUp,
|
||||
kItemIdBackgroundUp,
|
||||
kItemIdMasterDown,
|
||||
kItemIdVoicesDown,
|
||||
kItemIdMusicDown,
|
||||
kItemIdSoundFXDown,
|
||||
kItemIdBackgroundDown,
|
||||
kItemIdMaster,
|
||||
kItemIdVoices,
|
||||
kItemIdMusic,
|
||||
kItemIdSoundFX,
|
||||
kItemIdBackground,
|
||||
kItemIdDone,
|
||||
kItemIdCancel,
|
||||
// Save/load menu
|
||||
kItemIdSavegameUp,
|
||||
kItemIdSavegameDown,
|
||||
kItemIdSavegame1,
|
||||
kItemIdSavegame2,
|
||||
kItemIdSavegame3,
|
||||
kItemIdSavegame4,
|
||||
kItemIdSavegame5,
|
||||
kItemIdSavegame6,
|
||||
kItemIdSavegame7,
|
||||
// TODO
|
||||
kMenuIdDummy
|
||||
};
|
||||
|
||||
class MenuSystem {
|
||||
|
||||
public:
|
||||
MenuSystem(ToltecsEngine *vm);
|
||||
~MenuSystem();
|
||||
|
||||
int run();
|
||||
void update();
|
||||
void handleEvents();
|
||||
|
||||
protected:
|
||||
|
||||
struct Item {
|
||||
Common::Rect rect;
|
||||
ItemID id;
|
||||
Common::String caption;
|
||||
byte defaultColor, activeColor;
|
||||
int x, y, w;
|
||||
uint fontNum;
|
||||
};
|
||||
|
||||
struct SavegameItem {
|
||||
int _slotNum;
|
||||
Common::String _description;
|
||||
SavegameItem()
|
||||
: _slotNum(-1), _description("") {}
|
||||
SavegameItem(int slotNum, Common::String description)
|
||||
: _slotNum(slotNum), _description(description) {}
|
||||
};
|
||||
|
||||
ToltecsEngine *_vm;
|
||||
Graphics::Surface *_background;
|
||||
|
||||
bool _running;
|
||||
MenuID _currMenuID, _newMenuID;
|
||||
ItemID _currItemID;
|
||||
int _top;
|
||||
int _savegameListTopIndex;
|
||||
bool _editingDescription;
|
||||
ItemID _editingDescriptionID;
|
||||
Item *_editingDescriptionItem;
|
||||
bool _needRedraw;
|
||||
|
||||
Common::Array<Item> _items;
|
||||
Common::Array<SavegameItem> _savegames;
|
||||
|
||||
bool _cfgText, _cfgVoices;
|
||||
int _cfgMasterVolume, _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume, _cfgBackgroundVolume;
|
||||
|
||||
void addClickTextItem(ItemID id, int x, int y, int w, uint fontNum, const char *caption, byte defaultColor, byte activeColor);
|
||||
|
||||
void drawItem(ItemID itemID, bool active);
|
||||
void handleMouseMove(int x, int y);
|
||||
void handleMouseClick(int x, int y);
|
||||
void handleKeyDown(const Common::KeyState& kbd);
|
||||
|
||||
ItemID findItemAt(int x, int y);
|
||||
Item *getItem(ItemID id);
|
||||
void setItemCaption(Item *item, const char *caption);
|
||||
|
||||
void initMenu(MenuID menuID);
|
||||
|
||||
void enterItem(ItemID id);
|
||||
void leaveItem(ItemID id);
|
||||
void clickItem(ItemID id);
|
||||
|
||||
void restoreRect(int x, int y, int w, int h);
|
||||
void shadeRect(int x, int y, int w, int h, byte color1, byte color2);
|
||||
void drawString(int16 x, int16 y, int w, uint fontNum, byte color, const char *text);
|
||||
|
||||
SavegameItem *getSavegameItemByID(ItemID id);
|
||||
|
||||
int loadSavegamesList();
|
||||
void setSavegameCaptions();
|
||||
void scrollSavegames(int delta);
|
||||
void clickSavegameItem(ItemID id);
|
||||
void setCfgText(bool value, bool active);
|
||||
void setCfgVoices(bool value, bool active);
|
||||
void drawVolumeBar(ItemID itemID);
|
||||
void changeVolumeBar(ItemID itemID, int delta);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_MENU_H */
|
220
engines/toltecs/microtiles.cpp
Normal file
220
engines/toltecs/microtiles.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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "toltecs/microtiles.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
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, 639, 399));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Common::Rect * MicroTileArray::getRectangles(int *num_rects, int min_x, int min_y, int max_x, int max_y) {
|
||||
|
||||
Common::Rect *rects = new Common::Rect[_tilesW * _tilesH];
|
||||
|
||||
int n_rects = 0;
|
||||
int x, y;
|
||||
int x0, y0, x1, y1;
|
||||
int i = 0;
|
||||
|
||||
for (y = 0; y < _tilesH; ++y) {
|
||||
for (x = 0; x < _tilesW; ++x) {
|
||||
|
||||
int start;
|
||||
int finish = 0;
|
||||
BoundingBox boundingBox;
|
||||
|
||||
boundingBox = _tiles[i];
|
||||
|
||||
if (isBoundingBoxEmpty(boundingBox))
|
||||
goto next;
|
||||
|
||||
x0 = (x * TileSize) + TileX0(boundingBox);
|
||||
y0 = (y * TileSize) + TileY0(boundingBox);
|
||||
y1 = (y * TileSize) + TileY1(boundingBox);
|
||||
|
||||
x0 = CLIP (x0, min_x, max_x);
|
||||
y0 = CLIP (y0, min_y, max_y);
|
||||
y1 = CLIP (y1, min_y, max_y);
|
||||
|
||||
// FIXME: Why is the following code in an #if block?
|
||||
#if 1
|
||||
start = i;
|
||||
|
||||
if (TileX1(boundingBox) != TileSize - 1 || x == _tilesW - 1) {
|
||||
/* the tile does not continue */
|
||||
goto done;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
#endif
|
||||
x1 = (x * TileSize) + TileX1(_tiles[i]);
|
||||
|
||||
x1 = CLIP (x1, min_x, max_x);
|
||||
|
||||
// FIXME: Why is the following code in an #if block?
|
||||
|
||||
#if 1
|
||||
|
||||
rects[n_rects].left = x0;
|
||||
rects[n_rects].top = y0;
|
||||
rects[n_rects].right = x1 + 1;
|
||||
rects[n_rects].bottom = y1 + 1;
|
||||
n_rects++;
|
||||
|
||||
#else
|
||||
|
||||
// FIXME: Why is this code disabled?
|
||||
|
||||
if (glom [start] != -1 && /* try to glom */
|
||||
rects [glom [start]].left == x0 &&
|
||||
rects [glom [start]].right == x1 &&
|
||||
rects [glom [start]].bottom == y0 - 1)
|
||||
{
|
||||
rects [glom [start]].bottom = y1;
|
||||
if (y != tilesH - 1) {
|
||||
glom [start + tilesW] = glom [start];
|
||||
}
|
||||
} else {
|
||||
rects[n_rects].left = x0;
|
||||
rects[n_rects].top = y0;
|
||||
rects[n_rects].right = x1;
|
||||
rects[n_rects].bottom = y1;
|
||||
if (y != tilesH - 1) {
|
||||
glom [start + tilesW] = n_rects;
|
||||
}
|
||||
n_rects ++;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
next:
|
||||
++i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
*num_rects = n_rects;
|
||||
|
||||
//delete glom;
|
||||
|
||||
return rects;
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
61
engines/toltecs/microtiles.h
Normal file
61
engines/toltecs/microtiles.h
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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_MICROTILES_H
|
||||
#define TOLTECS_MICROTILES_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/util.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
typedef uint32 BoundingBox;
|
||||
|
||||
const BoundingBox FullBoundingBox = 0x00001F1F;
|
||||
const BoundingBox EmptyBoundingBox = 0x00000000;
|
||||
const int TileSize = 32;
|
||||
|
||||
class MicroTileArray {
|
||||
public:
|
||||
MicroTileArray(int16 width, int16 height);
|
||||
~MicroTileArray();
|
||||
void addRect(Common::Rect r);
|
||||
void clear();
|
||||
Common::Rect *getRectangles(int *num_rects, int min_x, int min_y, int max_x, int max_y);
|
||||
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 Toltecs
|
||||
|
||||
#endif // TOLTECS_MICROTILES_H
|
28
engines/toltecs/module.mk
Normal file
28
engines/toltecs/module.mk
Normal file
@ -0,0 +1,28 @@
|
||||
MODULE := engines/toltecs
|
||||
|
||||
MODULE_OBJS = \
|
||||
animation.o \
|
||||
detection.o \
|
||||
menu.o \
|
||||
microtiles.o \
|
||||
movie.o \
|
||||
music.o \
|
||||
palette.o \
|
||||
toltecs.o \
|
||||
render.o \
|
||||
resource.o \
|
||||
saveload.o \
|
||||
screen.o \
|
||||
script.o \
|
||||
segmap.o \
|
||||
sound.o \
|
||||
sprite.o
|
||||
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_TOLTECS), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
290
engines/toltecs/movie.cpp
Normal file
290
engines/toltecs/movie.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/movie.h"
|
||||
#include "toltecs/palette.h"
|
||||
#include "toltecs/resource.h"
|
||||
#include "toltecs/screen.h"
|
||||
#include "toltecs/script.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
enum ChunkTypes {
|
||||
kChunkFirstImage = 0,
|
||||
kChunkSubsequentImages = 1,
|
||||
kChunkPalette = 2,
|
||||
kChunkUnused = 3,
|
||||
kChunkAudio = 4,
|
||||
kChunkShowSubtitle = 5,
|
||||
kChunkShakeScreen = 6,
|
||||
kChunkSetupSubtitles = 7,
|
||||
kChunkStopSubtitles = 8
|
||||
};
|
||||
|
||||
MoviePlayer::MoviePlayer(ToltecsEngine *vm) : _vm(vm) {
|
||||
}
|
||||
|
||||
MoviePlayer::~MoviePlayer() {
|
||||
}
|
||||
|
||||
void MoviePlayer::playMovie(uint resIndex) {
|
||||
|
||||
const uint32 subtitleSlot = kMaxScriptSlots - 1;
|
||||
int16 savedSceneWidth = _vm->_sceneWidth;
|
||||
int16 savedSceneHeight = _vm->_sceneHeight;
|
||||
int16 savedCameraHeight = _vm->_cameraHeight;
|
||||
int16 savedCameraX = _vm->_cameraX;
|
||||
int16 savedCameraY = _vm->_cameraY;
|
||||
int16 savedGuiHeight = _vm->_guiHeight;
|
||||
byte moviePalette[768];
|
||||
|
||||
_vm->_isSaveAllowed = false;
|
||||
|
||||
memset(moviePalette, 0, sizeof(moviePalette));
|
||||
|
||||
_vm->_screen->finishTalkTextItems();
|
||||
_vm->_screen->clearSprites();
|
||||
|
||||
_vm->_arc->openResource(resIndex);
|
||||
|
||||
_frameCount = _vm->_arc->readUint32LE();
|
||||
_chunkCount = _vm->_arc->readUint32LE();
|
||||
|
||||
// TODO: Figure out rest of the header
|
||||
_vm->_arc->readUint32LE();
|
||||
_vm->_arc->readUint32LE();
|
||||
_framesPerSoundChunk = _vm->_arc->readUint32LE();
|
||||
_vm->_arc->readUint32LE();
|
||||
|
||||
_vm->_sceneWidth = 640;
|
||||
_vm->_sceneHeight = 400;
|
||||
_vm->_cameraHeight = 400;
|
||||
_vm->_cameraX = 0;
|
||||
_vm->_cameraY = 0;
|
||||
_vm->_guiHeight = 0;
|
||||
|
||||
_audioStream = Audio::makeQueuingAudioStream(22050, false);
|
||||
|
||||
_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle, _audioStream);
|
||||
|
||||
_soundChunkFramesLeft = 0;
|
||||
_lastPrefetchOfs = 0;
|
||||
|
||||
fetchAudioChunks();
|
||||
|
||||
uint32 lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle);
|
||||
|
||||
while (_chunkCount--) {
|
||||
|
||||
byte chunkType = _vm->_arc->readByte();
|
||||
uint32 chunkSize = _vm->_arc->readUint32LE();
|
||||
byte *chunkBuffer = NULL;
|
||||
uint32 movieOffset;
|
||||
|
||||
debug(0, "chunkType = %d; chunkSize = %d", chunkType, chunkSize);
|
||||
|
||||
// Skip audio chunks - we've already queued them in
|
||||
// fetchAudioChunks() above
|
||||
if (chunkType == kChunkAudio) {
|
||||
_vm->_arc->skip(chunkSize);
|
||||
} else {
|
||||
chunkBuffer = new byte[chunkSize];
|
||||
_vm->_arc->read(chunkBuffer, chunkSize);
|
||||
}
|
||||
|
||||
movieOffset = _vm->_arc->pos();
|
||||
|
||||
switch (chunkType) {
|
||||
case kChunkFirstImage:
|
||||
case kChunkSubsequentImages:
|
||||
unpackRle(chunkBuffer, _vm->_screen->_backScreen);
|
||||
// TODO: Rework this
|
||||
_vm->_screen->updateShakeScreen();
|
||||
_vm->_screen->_fullRefresh = true;
|
||||
_vm->updateInput();
|
||||
_vm->drawScreen();
|
||||
|
||||
_soundChunkFramesLeft--;
|
||||
if (_soundChunkFramesLeft <= _framesPerSoundChunk) {
|
||||
fetchAudioChunks();
|
||||
}
|
||||
|
||||
while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < lastTime + 111) {
|
||||
g_system->delayMillis(10);
|
||||
}
|
||||
|
||||
lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle);
|
||||
|
||||
break;
|
||||
case kChunkPalette:
|
||||
unpackPalette(chunkBuffer, moviePalette, 256, 3);
|
||||
_vm->_palette->setFullPalette(moviePalette);
|
||||
break;
|
||||
case kChunkUnused:
|
||||
error("Chunk considered to be unused has been encountered");
|
||||
case kChunkAudio:
|
||||
// Already processed
|
||||
break;
|
||||
case kChunkShowSubtitle:
|
||||
// TODO: Check if the text is a subtitle (last character == 0xFE).
|
||||
// If so, don't show it if text display is disabled.
|
||||
memcpy(_vm->_script->getSlotData(subtitleSlot), chunkBuffer, chunkSize);
|
||||
_vm->_screen->updateTalkText(subtitleSlot, 0);
|
||||
break;
|
||||
case kChunkShakeScreen: // start/stop shakescreen effect
|
||||
if (chunkBuffer[0] == 0xFF)
|
||||
_vm->_screen->stopShakeScreen();
|
||||
else
|
||||
_vm->_screen->startShakeScreen(chunkBuffer[0]);
|
||||
break;
|
||||
case kChunkSetupSubtitles: // setup subtitle parameters
|
||||
_vm->_screen->_talkTextY = READ_LE_UINT16(chunkBuffer + 0);
|
||||
_vm->_screen->_talkTextX = READ_LE_UINT16(chunkBuffer + 2);
|
||||
_vm->_screen->_talkTextFontColor = ((chunkBuffer[4] << 4) & 0xF0) | ((chunkBuffer[4] >> 4) & 0x0F);
|
||||
debug(0, "_talkTextX = %d; _talkTextY = %d; _talkTextFontColor = %d",
|
||||
_vm->_screen->_talkTextX, _vm->_screen->_talkTextY, _vm->_screen->_talkTextFontColor);
|
||||
break;
|
||||
case kChunkStopSubtitles:
|
||||
_vm->_script->getSlotData(subtitleSlot)[0] = 0xFF;
|
||||
_vm->_screen->finishTalkTextItems();
|
||||
break;
|
||||
default:
|
||||
error("MoviePlayer::playMovie(%04X) Unknown chunk type %d at %08X", resIndex, chunkType, _vm->_arc->pos() - 5 - chunkSize);
|
||||
}
|
||||
|
||||
delete[] chunkBuffer;
|
||||
|
||||
_vm->_arc->seek(movieOffset, SEEK_SET);
|
||||
|
||||
if (!handleInput())
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
_audioStream->finish();
|
||||
_vm->_mixer->stopHandle(_audioStreamHandle);
|
||||
|
||||
_vm->_arc->closeResource();
|
||||
|
||||
debug(0, "playMovie() done");
|
||||
|
||||
_vm->_sceneWidth = savedSceneWidth;
|
||||
_vm->_sceneHeight = savedSceneHeight;
|
||||
_vm->_cameraHeight = savedCameraHeight;
|
||||
_vm->_cameraX = savedCameraX;
|
||||
_vm->_cameraY = savedCameraY;
|
||||
_vm->_guiHeight = savedGuiHeight;
|
||||
|
||||
_vm->_isSaveAllowed = true;
|
||||
}
|
||||
|
||||
void MoviePlayer::fetchAudioChunks() {
|
||||
|
||||
uint32 startOfs = _vm->_arc->pos();
|
||||
uint32 chunkCount = _chunkCount;
|
||||
uint prefetchChunkCount = 0;
|
||||
|
||||
if (_lastPrefetchOfs != 0)
|
||||
_vm->_arc->seek(_lastPrefetchOfs, SEEK_SET);
|
||||
|
||||
while (chunkCount-- && prefetchChunkCount < _framesPerSoundChunk / 2) {
|
||||
byte chunkType = _vm->_arc->readByte();
|
||||
uint32 chunkSize = _vm->_arc->readUint32LE();
|
||||
if (chunkType == 4) {
|
||||
byte *chunkBuffer = (byte*)malloc(chunkSize);
|
||||
_vm->_arc->read(chunkBuffer, chunkSize);
|
||||
_audioStream->queueBuffer(chunkBuffer, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
|
||||
chunkBuffer = NULL;
|
||||
prefetchChunkCount++;
|
||||
_soundChunkFramesLeft += _framesPerSoundChunk;
|
||||
} else {
|
||||
_vm->_arc->seek(chunkSize, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
_lastPrefetchOfs = _vm->_arc->pos();
|
||||
|
||||
_vm->_arc->seek(startOfs, SEEK_SET);
|
||||
|
||||
}
|
||||
|
||||
void MoviePlayer::unpackPalette(byte *source, byte *dest, int elemCount, int elemSize) {
|
||||
int ofs = 0, size = elemCount * elemSize;
|
||||
while (ofs < size) {
|
||||
byte len;
|
||||
len = *source++;
|
||||
if (len == 0) {
|
||||
len = *source++;
|
||||
} else {
|
||||
byte value = *source++;
|
||||
memset(dest, value, len);
|
||||
}
|
||||
ofs += len;
|
||||
dest += len;
|
||||
}
|
||||
}
|
||||
|
||||
void MoviePlayer::unpackRle(byte *source, byte *dest) {
|
||||
int size = 256000;
|
||||
while (size > 0) {
|
||||
byte a = *source++;
|
||||
byte b = *source++;
|
||||
if (a == 0) {
|
||||
dest += b;
|
||||
size -= b;
|
||||
} else {
|
||||
memset(dest, b, a);
|
||||
dest += a;
|
||||
size -= a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MoviePlayer::handleInput() {
|
||||
Common::Event event;
|
||||
Common::EventManager *eventMan = g_system->getEventManager();
|
||||
while (eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
|
||||
return false;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
return false;
|
||||
case Common::EVENT_QUIT:
|
||||
_vm->quitGame();
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return !_vm->shouldQuit();
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
59
engines/toltecs/movie.h
Normal file
59
engines/toltecs/movie.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_MOVIE_H
|
||||
#define TOLTECS_MOVIE_H
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h" // for Audio::SoundHandle
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
class MoviePlayer {
|
||||
|
||||
public:
|
||||
MoviePlayer(ToltecsEngine *vm);
|
||||
~MoviePlayer();
|
||||
|
||||
void playMovie(uint resIndex);
|
||||
|
||||
protected:
|
||||
ToltecsEngine *_vm;
|
||||
Audio::QueuingAudioStream *_audioStream;
|
||||
Audio::SoundHandle _audioStreamHandle;
|
||||
|
||||
uint32 _chunkCount, _frameCount, _lastPrefetchOfs;
|
||||
uint32 _soundChunkFramesLeft, _framesPerSoundChunk;
|
||||
|
||||
void unpackPalette(byte *source, byte *dest, int elemCount, int elemSize);
|
||||
void unpackRle(byte *source, byte *dest);
|
||||
|
||||
void fetchAudioChunks();
|
||||
|
||||
bool handleInput();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_MOVIE_H */
|
104
engines/toltecs/music.cpp
Normal file
104
engines/toltecs/music.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
// FIXME: This code is taken from SAGA and needs more work (e.g. setVolume).
|
||||
|
||||
#include "toltecs/music.h"
|
||||
|
||||
#include "audio/midiparser.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
MusicPlayer::MusicPlayer(bool isGM) : _isGM(isGM), _buffer(NULL) {
|
||||
MidiPlayer::createDriver();
|
||||
|
||||
int ret = _driver->open();
|
||||
if (ret == 0) {
|
||||
if (_nativeMT32)
|
||||
_driver->sendMT32Reset();
|
||||
else
|
||||
_driver->sendGMReset();
|
||||
|
||||
_driver->setTimerCallback(this, &timerCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void MusicPlayer::send(uint32 b) {
|
||||
if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
|
||||
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
|
||||
}
|
||||
|
||||
Audio::MidiPlayer::send(b);
|
||||
}
|
||||
|
||||
void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
stopAndClear();
|
||||
|
||||
_buffer = new byte[size];
|
||||
memcpy(_buffer, data, size);
|
||||
|
||||
MidiParser *parser;
|
||||
|
||||
if (!memcmp(data, "FORM", 4))
|
||||
parser = MidiParser::createParser_XMIDI(NULL);
|
||||
else
|
||||
parser = MidiParser::createParser_SMF();
|
||||
|
||||
if (parser->loadMusic(_buffer, size)) {
|
||||
parser->setTrack(0);
|
||||
parser->setMidiDriver(this);
|
||||
parser->setTimerRate(_driver->getBaseTempo());
|
||||
parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
|
||||
parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
|
||||
|
||||
_parser = parser;
|
||||
|
||||
setVolume(127);
|
||||
|
||||
_isLooping = loop;
|
||||
_isPlaying = true;
|
||||
} else {
|
||||
delete parser;
|
||||
}
|
||||
}
|
||||
|
||||
void MusicPlayer::pause() {
|
||||
setVolume(-1);
|
||||
_isPlaying = false;
|
||||
}
|
||||
|
||||
void MusicPlayer::resume() {
|
||||
setVolume(127);
|
||||
_isPlaying = true;
|
||||
}
|
||||
|
||||
void MusicPlayer::stopAndClear() {
|
||||
Common::StackLock lock(_mutex);
|
||||
stop();
|
||||
|
||||
delete[] _buffer;
|
||||
_buffer = NULL;
|
||||
}
|
||||
|
||||
} // End of namespace Made
|
53
engines/toltecs/music.h
Normal file
53
engines/toltecs/music.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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
// Music class
|
||||
|
||||
#ifndef MADE_MUSIC_H
|
||||
#define MADE_MUSIC_H
|
||||
|
||||
#include "audio/midiplayer.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
class MusicPlayer : public Audio::MidiPlayer {
|
||||
public:
|
||||
MusicPlayer(bool isGM = true);
|
||||
|
||||
void playMIDI(const byte *data, uint32 size, bool loop = false);
|
||||
void pause();
|
||||
void resume();
|
||||
void stopAndClear();
|
||||
|
||||
// MidiDriver_BASE interface implementation
|
||||
virtual void send(uint32 b);
|
||||
|
||||
protected:
|
||||
bool _isGM;
|
||||
|
||||
private:
|
||||
byte *_buffer;
|
||||
};
|
||||
|
||||
} // End of namespace Made
|
||||
|
||||
#endif
|
244
engines/toltecs/palette.cpp
Normal file
244
engines/toltecs/palette.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graphics/palette.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/palette.h"
|
||||
#include "toltecs/resource.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
Palette::Palette(ToltecsEngine *vm) : _vm(vm) {
|
||||
|
||||
clearFragments();
|
||||
|
||||
memset(_colorTransTable, 0, sizeof(_colorTransTable));
|
||||
|
||||
}
|
||||
|
||||
Palette::~Palette() {
|
||||
}
|
||||
|
||||
void Palette::setFullPalette(byte *palette) {
|
||||
byte colors[768];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
colors[i * 3 + 0] = palette[i * 3 + 0] << 2;
|
||||
colors[i * 3 + 1] = palette[i * 3 + 1] << 2;
|
||||
colors[i * 3 + 2] = palette[i * 3 + 2] << 2;
|
||||
}
|
||||
_vm->_system->getPaletteManager()->setPalette((const byte *)colors, 0, 256);
|
||||
_vm->_system->updateScreen();
|
||||
}
|
||||
|
||||
void Palette::getFullPalette(byte *palette) {
|
||||
byte colors[768];
|
||||
_vm->_system->getPaletteManager()->grabPalette(colors, 0, 256);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
palette[i * 3 + 0] = colors[i * 3 + 0] >> 2;
|
||||
palette[i * 3 + 1] = colors[i * 3 + 1] >> 2;
|
||||
palette[i * 3 + 2] = colors[i * 3 + 2] >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
void Palette::setDeltaPalette(byte *palette, byte mask, char deltaValue, int16 count, int16 startIndex) {
|
||||
byte colors[768];
|
||||
|
||||
byte *palPtr = palette + startIndex * 3;
|
||||
int16 index = startIndex, colorCount = count;
|
||||
byte rgb;
|
||||
|
||||
count++;
|
||||
|
||||
_vm->_system->getPaletteManager()->grabPalette(colors, 0, 256);
|
||||
|
||||
deltaValue *= -1;
|
||||
|
||||
while (count--) {
|
||||
rgb = *palPtr++;
|
||||
if (mask & 1) colors[index * 3 + 0] = CLIP<int>(rgb + deltaValue, 0, 63) << 2;
|
||||
rgb = *palPtr++;
|
||||
if (mask & 2) colors[index * 3 + 1] = CLIP<int>(rgb + deltaValue, 0, 63) << 2;
|
||||
rgb = *palPtr++;
|
||||
if (mask & 4) colors[index * 3 + 2] = CLIP<int>(rgb + deltaValue, 0, 63) << 2;
|
||||
index++;
|
||||
}
|
||||
|
||||
debug(0, "startIndex = %d; colorCount = %d", startIndex, colorCount);
|
||||
|
||||
_vm->_system->getPaletteManager()->setPalette((const byte *)colors, 0, 256);
|
||||
}
|
||||
|
||||
void Palette::loadAddPalette(uint resIndex, byte startIndex) {
|
||||
Resource *paletteResource = _vm->_res->load(resIndex);
|
||||
memcpy(&_mainPalette[startIndex * 3], paletteResource->data, paletteResource->size);
|
||||
}
|
||||
|
||||
void Palette::loadAddPaletteFrom(byte *source, byte startIndex, byte count) {
|
||||
memcpy(&_mainPalette[startIndex * 3], source, count * 3);
|
||||
}
|
||||
|
||||
void Palette::addFragment(uint resIndex, int16 id) {
|
||||
debug(0, "Palette::addFragment(%d, %d)", resIndex, id);
|
||||
|
||||
Resource *fragmentResource = _vm->_res->load(resIndex);
|
||||
byte count = fragmentResource->size / 3;
|
||||
|
||||
memcpy(&_mainPalette[_fragmentIndex * 3], fragmentResource->data, count * 3);
|
||||
|
||||
PaletteFragment fragment;
|
||||
fragment.id = id;
|
||||
fragment.index = _fragmentIndex;
|
||||
fragment.count = count;
|
||||
_fragments.push_back(fragment);
|
||||
|
||||
debug(0, "Palette::addFragment() index = %02X; count = %02X", fragment.index, fragment.count);
|
||||
|
||||
_fragmentIndex += count;
|
||||
|
||||
}
|
||||
|
||||
uint16 Palette::findFragment(int16 id) {
|
||||
debug(0, "Palette::findFragment(%d)", id);
|
||||
|
||||
uint16 result = 0;
|
||||
for (PaletteFragmentArray::iterator iter = _fragments.begin(); iter != _fragments.end(); iter++) {
|
||||
PaletteFragment fragment = *iter;
|
||||
if (fragment.id == id) {
|
||||
result = (fragment.count << 8) | fragment.index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug(0, "Palette::findFragment() result = %04X", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Palette::clearFragments() {
|
||||
debug(0, "Palette::clearFragments()");
|
||||
_fragmentIndex = 128;
|
||||
_fragments.clear();
|
||||
}
|
||||
|
||||
void Palette::buildColorTransTable(byte limit, char deltaValue, byte mask) {
|
||||
|
||||
// TODO
|
||||
|
||||
byte r = 0, g = 0, b = 0;
|
||||
|
||||
mask &= 7;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
||||
if (deltaValue < 0) {
|
||||
// TODO
|
||||
warning("Palette::buildColorTransTable(%d, %d, %02X) not yet implemented!", limit, deltaValue, mask);
|
||||
} else {
|
||||
r = _mainPalette[i * 3 + 0];
|
||||
g = _mainPalette[i * 3 + 1];
|
||||
b = _mainPalette[i * 3 + 2];
|
||||
if (MAX(r, MAX(b, g)) >= limit) {
|
||||
if ((mask & 1) && r >= deltaValue)
|
||||
r -= deltaValue;
|
||||
if ((mask & 2) && g >= deltaValue)
|
||||
g -= deltaValue;
|
||||
if ((mask & 4) && b >= deltaValue)
|
||||
b -= deltaValue;
|
||||
}
|
||||
}
|
||||
|
||||
int bestIndex = 0;
|
||||
uint16 bestMatch = 0xFFFF;
|
||||
|
||||
for (int j = 0; j < 256; j++) {
|
||||
byte distance = ABS(_mainPalette[j * 3 + 0] - r) + ABS(_mainPalette[j * 3 + 1] - g) + ABS(_mainPalette[j * 3 + 2] - b);
|
||||
byte maxColor = MAX(_mainPalette[j * 3 + 0], MAX(_mainPalette[j * 3 + 1], _mainPalette[j * 3 + 2]));
|
||||
uint16 match = (distance << 8) | maxColor;
|
||||
if (match < bestMatch) {
|
||||
bestMatch = match;
|
||||
bestIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
_colorTransTable[i] = bestIndex;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Palette::buildColorTransTable2(byte limit, char deltaValue, byte mask) {
|
||||
|
||||
// TODO
|
||||
|
||||
}
|
||||
|
||||
void Palette::saveState(Common::WriteStream *out) {
|
||||
|
||||
// Save currently active palette
|
||||
byte palette[768];
|
||||
getFullPalette(palette);
|
||||
out->write(palette, 768);
|
||||
|
||||
out->write(_mainPalette, 768);
|
||||
out->write(_animPalette, 768);
|
||||
out->write(_colorTransTable, 256);
|
||||
|
||||
uint16 fragmentCount = _fragments.size();
|
||||
out->writeUint16LE(fragmentCount);
|
||||
for (PaletteFragmentArray::iterator iter = _fragments.begin(); iter != _fragments.end(); iter++) {
|
||||
PaletteFragment fragment = *iter;
|
||||
out->writeUint16LE(fragment.id);
|
||||
out->writeByte(fragment.index);
|
||||
out->writeByte(fragment.count);
|
||||
}
|
||||
out->writeByte(_fragmentIndex);
|
||||
|
||||
}
|
||||
|
||||
void Palette::loadState(Common::ReadStream *in) {
|
||||
|
||||
// Save currently active palette
|
||||
byte palette[768];
|
||||
in->read(palette, 768);
|
||||
setFullPalette(palette);
|
||||
|
||||
in->read(_mainPalette, 768);
|
||||
in->read(_animPalette, 768);
|
||||
in->read(_colorTransTable, 256);
|
||||
|
||||
uint16 fragmentCount = in->readUint16LE();
|
||||
_fragments.clear();
|
||||
for (uint16 i = 0; i < fragmentCount; i++) {
|
||||
PaletteFragment fragment;
|
||||
fragment.id = in->readUint16LE();
|
||||
fragment.index = in->readByte();
|
||||
fragment.count = in->readByte();
|
||||
_fragments.push_back(fragment);
|
||||
}
|
||||
_fragmentIndex = in->readByte();
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Toltecs
|
85
engines/toltecs/palette.h
Normal file
85
engines/toltecs/palette.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_PALETTE_H
|
||||
#define TOLTECS_PALETTE_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
//#define ROT(index) (((index << 4) & 0xF0) | ((index >> 4) & 0x0F))
|
||||
//#define ROT(index) (index)
|
||||
|
||||
class Palette {
|
||||
public:
|
||||
Palette(ToltecsEngine *vm);
|
||||
~Palette();
|
||||
|
||||
void setFullPalette(byte *palette);
|
||||
void getFullPalette(byte *palette);
|
||||
void setDeltaPalette(byte *palette, byte mask, char deltaValue, int16 count, int16 startIndex);
|
||||
|
||||
void loadAddPalette(uint resIndex, byte startIndex);
|
||||
void loadAddPaletteFrom(byte *source, byte startIndex, byte count);
|
||||
|
||||
void addFragment(uint resIndex, int16 id);
|
||||
uint16 findFragment(int16 id);
|
||||
void clearFragments();
|
||||
|
||||
void buildColorTransTable(byte limit, char deltaValue, byte mask);
|
||||
void buildColorTransTable2(byte limit, char deltaValue, byte mask);
|
||||
byte getColorTransPixel(byte pixel) const { return _colorTransTable[pixel]; }
|
||||
|
||||
byte *getMainPalette() { return _mainPalette; }
|
||||
byte *getAnimPalette() { return _animPalette; }
|
||||
|
||||
void saveState(Common::WriteStream *out);
|
||||
void loadState(Common::ReadStream *in);
|
||||
|
||||
protected:
|
||||
|
||||
struct PaletteFragment {
|
||||
int16 id;
|
||||
byte index, count;
|
||||
};
|
||||
|
||||
typedef Common::Array<PaletteFragment> PaletteFragmentArray;
|
||||
|
||||
ToltecsEngine *_vm;
|
||||
|
||||
byte _mainPalette[768];
|
||||
byte _animPalette[768];
|
||||
byte _colorTransTable[256];
|
||||
|
||||
PaletteFragmentArray _fragments;
|
||||
byte _fragmentIndex;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_PALETTE_H */
|
311
engines/toltecs/render.cpp
Normal file
311
engines/toltecs/render.cpp
Normal file
@ -0,0 +1,311 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/render.h"
|
||||
#include "toltecs/resource.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
Common::Rect makeRect(int16 x, int16 y, int16 width, int16 height) {
|
||||
Common::Rect rect;
|
||||
rect.left = x;
|
||||
rect.top = y;
|
||||
rect.setWidth(width);
|
||||
rect.setHeight(height);
|
||||
return rect;
|
||||
}
|
||||
|
||||
RenderQueue::RenderQueue(ToltecsEngine *vm) : _vm(vm) {
|
||||
_currQueue = new RenderQueueArray();
|
||||
_prevQueue = new RenderQueueArray();
|
||||
_updateUta = new MicroTileArray(640, 400);
|
||||
}
|
||||
|
||||
RenderQueue::~RenderQueue() {
|
||||
delete _currQueue;
|
||||
delete _prevQueue;
|
||||
delete _updateUta;
|
||||
}
|
||||
|
||||
void RenderQueue::addSprite(SpriteDrawItem &sprite) {
|
||||
|
||||
RenderQueueItem item;
|
||||
item.type = kSprite;
|
||||
item.flags = kRefresh;
|
||||
item.rect = makeRect(sprite.x - _vm->_cameraX, sprite.y - _vm->_cameraY, sprite.width, sprite.height);
|
||||
item.priority = sprite.priority;
|
||||
|
||||
item.sprite = sprite;
|
||||
item.sprite.x -= _vm->_cameraX;
|
||||
item.sprite.y -= _vm->_cameraY;
|
||||
|
||||
// Add sprite sorted by priority
|
||||
RenderQueueArray::iterator iter = _currQueue->begin();
|
||||
while (iter != _currQueue->end() && (*iter).priority <= item.priority) {
|
||||
iter++;
|
||||
}
|
||||
_currQueue->insert(iter, item);
|
||||
|
||||
}
|
||||
|
||||
void RenderQueue::addText(int16 x, int16 y, byte color, uint fontResIndex, byte *text, int len) {
|
||||
|
||||
Font font(_vm->_res->load(fontResIndex)->data);
|
||||
|
||||
RenderQueueItem item;
|
||||
item.type = kText;
|
||||
item.flags = kRefresh;
|
||||
item.rect = makeRect(x, y, font.getTextWidth(text), font.getHeight());
|
||||
item.priority = 1000;
|
||||
|
||||
item.text.color = color;
|
||||
item.text.fontResIndex = fontResIndex;
|
||||
item.text.text = text;
|
||||
item.text.len = len;
|
||||
|
||||
_currQueue->push_back(item);
|
||||
|
||||
}
|
||||
|
||||
void RenderQueue::addMask(SegmapMaskRect &mask) {
|
||||
|
||||
RenderQueueItem item;
|
||||
item.type = kMask;
|
||||
item.flags = kRefresh;
|
||||
item.rect = makeRect(mask.x - _vm->_cameraX, mask.y - _vm->_cameraY, mask.width, mask.height);
|
||||
item.priority = mask.priority;
|
||||
|
||||
item.mask = mask;
|
||||
|
||||
// Only add the mask if a sprite intersects its rect
|
||||
if (rectIntersectsItem(item.rect)) {
|
||||
RenderQueueArray::iterator iter = _currQueue->begin();
|
||||
while (iter != _currQueue->end() && (*iter).priority <= item.priority) {
|
||||
iter++;
|
||||
}
|
||||
_currQueue->insert(iter, item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RenderQueue::update() {
|
||||
|
||||
bool doFullRefresh = _vm->_screen->_fullRefresh;
|
||||
|
||||
_updateUta->clear();
|
||||
|
||||
if (!doFullRefresh) {
|
||||
|
||||
for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
|
||||
RenderQueueItem *item = &(*iter);
|
||||
RenderQueueItem *prevItem = findItemInQueue(_prevQueue, *item);
|
||||
if (prevItem) {
|
||||
if (hasItemChanged(*prevItem, *item)) {
|
||||
item->flags = kRefresh;
|
||||
addDirtyRect(prevItem->rect);
|
||||
} else {
|
||||
item->flags = kUnchanged;
|
||||
}
|
||||
} else {
|
||||
item->flags = kRefresh;
|
||||
}
|
||||
}
|
||||
|
||||
for (RenderQueueArray::iterator iter = _prevQueue->begin(); iter != _prevQueue->end(); iter++) {
|
||||
RenderQueueItem *prevItem = &(*iter);
|
||||
RenderQueueItem *item = findItemInQueue(_currQueue, *prevItem);
|
||||
if (!item) {
|
||||
prevItem->flags = kRemoved;
|
||||
addDirtyRect(prevItem->rect);
|
||||
}
|
||||
}
|
||||
|
||||
restoreDirtyBackground();
|
||||
|
||||
for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
|
||||
RenderQueueItem *item = &(*iter);
|
||||
if (item->flags != kUnchanged)
|
||||
invalidateItemsByRect(item->rect, item);
|
||||
}
|
||||
|
||||
} else {
|
||||
byte *destp = _vm->_screen->_frontScreen;
|
||||
byte *srcp = _vm->_screen->_backScreen + _vm->_cameraX + _vm->_cameraY * _vm->_sceneWidth;
|
||||
int16 w = MIN<int16>(640, _vm->_sceneWidth);
|
||||
int16 h = MIN<int16>(400, _vm->_cameraHeight);
|
||||
while (h--) {
|
||||
memcpy(destp, srcp, w);
|
||||
destp += 640;
|
||||
srcp += _vm->_sceneWidth;
|
||||
}
|
||||
_vm->_screen->_fullRefresh = false;
|
||||
}
|
||||
|
||||
for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
|
||||
const RenderQueueItem *item = &(*iter);
|
||||
|
||||
if (item->flags == kRefresh || doFullRefresh) {
|
||||
|
||||
switch (item->type) {
|
||||
case kSprite:
|
||||
_vm->_screen->drawSprite(item->sprite);
|
||||
break;
|
||||
case kText:
|
||||
_vm->_screen->drawString(item->rect.left, item->rect.top, item->text.color, item->text.fontResIndex,
|
||||
item->text.text, item->text.len, NULL, true);
|
||||
break;
|
||||
case kMask:
|
||||
_vm->_screen->drawSurface(item->rect.left, item->rect.top, item->mask.surface);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!doFullRefresh)
|
||||
addDirtyRect(item->rect);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (doFullRefresh) {
|
||||
clear();
|
||||
_vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen, 640, 0, 0, 640, _vm->_cameraHeight);
|
||||
} else {
|
||||
updateDirtyRects();
|
||||
}
|
||||
|
||||
SWAP(_currQueue, _prevQueue);
|
||||
_currQueue->clear();
|
||||
|
||||
}
|
||||
|
||||
void RenderQueue::clear() {
|
||||
_prevQueue->clear();
|
||||
_currQueue->clear();
|
||||
}
|
||||
|
||||
bool RenderQueue::rectIntersectsItem(const Common::Rect &rect) {
|
||||
for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
|
||||
const RenderQueueItem *item = &(*iter);
|
||||
if (rect.intersects(item->rect))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RenderQueueItem *RenderQueue::findItemInQueue(RenderQueueArray *queue, const RenderQueueItem &item) {
|
||||
/* This checks if the given item also exists in the previously drawn frame.
|
||||
The state of the item (position, color etc) is handled elsewhere.
|
||||
*/
|
||||
for (RenderQueueArray::iterator iter = queue->begin(); iter != queue->end(); iter++) {
|
||||
RenderQueueItem *prevItem = &(*iter);
|
||||
if (prevItem->type == item.type) {
|
||||
switch (item.type) {
|
||||
case kSprite:
|
||||
if (prevItem->sprite.resIndex == item.sprite.resIndex &&
|
||||
prevItem->sprite.frameNum == item.sprite.frameNum)
|
||||
return prevItem;
|
||||
break;
|
||||
case kText:
|
||||
if (prevItem->text.text == item.text.text &&
|
||||
prevItem->text.len == item.text.len)
|
||||
return prevItem;
|
||||
break;
|
||||
case kMask:
|
||||
if (prevItem->mask.surface == item.mask.surface)
|
||||
return prevItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL; // Not found
|
||||
}
|
||||
|
||||
bool RenderQueue::hasItemChanged(const RenderQueueItem &item1, const RenderQueueItem &item2) {
|
||||
|
||||
if (item1.type != item2.type)
|
||||
return true;
|
||||
|
||||
if (item1.rect.left != item2.rect.left ||
|
||||
item1.rect.top != item2.rect.top ||
|
||||
item1.rect.right != item2.rect.right ||
|
||||
item1.rect.bottom != item2.rect.bottom)
|
||||
return true;
|
||||
|
||||
if (item1.type == kText && item1.text.color != item2.text.color)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderQueue::invalidateItemsByRect(const Common::Rect &rect, const RenderQueueItem *item) {
|
||||
for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) {
|
||||
RenderQueueItem *subItem = &(*iter);
|
||||
if (item != subItem &&
|
||||
subItem->flags == kUnchanged &&
|
||||
rect.intersects(subItem->rect)) {
|
||||
|
||||
subItem->flags = kRefresh;
|
||||
invalidateItemsByRect(subItem->rect, subItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderQueue::addDirtyRect(const Common::Rect &rect) {
|
||||
_updateUta->addRect(rect);
|
||||
}
|
||||
|
||||
void RenderQueue::restoreDirtyBackground() {
|
||||
int n_rects = 0;
|
||||
Common::Rect *rects = _updateUta->getRectangles(&n_rects, 0, 0, 639, _vm->_cameraHeight - 1);
|
||||
for (int i = 0; i < n_rects; i++) {
|
||||
byte *destp = _vm->_screen->_frontScreen + rects[i].left + rects[i].top * 640;
|
||||
byte *srcp = _vm->_screen->_backScreen + (_vm->_cameraX + rects[i].left) + (_vm->_cameraY + rects[i].top) * _vm->_sceneWidth;
|
||||
int16 w = rects[i].width();
|
||||
int16 h = rects[i].height();
|
||||
while (h--) {
|
||||
memcpy(destp, srcp, w);
|
||||
destp += 640;
|
||||
srcp += _vm->_sceneWidth;
|
||||
}
|
||||
invalidateItemsByRect(rects[i], NULL);
|
||||
}
|
||||
delete[] rects;
|
||||
}
|
||||
|
||||
void RenderQueue::updateDirtyRects() {
|
||||
int n_rects = 0;
|
||||
Common::Rect *rects = _updateUta->getRectangles(&n_rects, 0, 0, 639, _vm->_cameraHeight - 1);
|
||||
for (int i = 0; i < n_rects; i++) {
|
||||
_vm->_system->copyRectToScreen((const byte *)_vm->_screen->_frontScreen + rects[i].left + rects[i].top * 640,
|
||||
640, rects[i].left, rects[i].top, rects[i].width(), rects[i].height());
|
||||
}
|
||||
delete[] rects;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Toltecs
|
99
engines/toltecs/render.h
Normal file
99
engines/toltecs/render.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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_RENDER_H
|
||||
#define TOLTECS_RENDER_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "toltecs/segmap.h"
|
||||
#include "toltecs/screen.h"
|
||||
#include "toltecs/microtiles.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
enum RenderType {
|
||||
kSprite,
|
||||
kText,
|
||||
kMask
|
||||
};
|
||||
|
||||
enum RenderFlags {
|
||||
kNone = 1 << 0,
|
||||
kRefresh = 1 << 1,
|
||||
kRemoved = 1 << 2,
|
||||
kMoved = 1 << 3,
|
||||
kUnchanged = 1 << 4
|
||||
};
|
||||
|
||||
struct RenderTextItem {
|
||||
byte color;
|
||||
uint fontResIndex;
|
||||
byte *text;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct RenderQueueItem {
|
||||
RenderType type;
|
||||
uint flags;
|
||||
Common::Rect rect;
|
||||
int16 priority;
|
||||
union {
|
||||
SpriteDrawItem sprite;
|
||||
RenderTextItem text;
|
||||
SegmapMaskRect mask;
|
||||
};
|
||||
};
|
||||
|
||||
class RenderQueue {
|
||||
public:
|
||||
RenderQueue(ToltecsEngine *vm);
|
||||
~RenderQueue();
|
||||
|
||||
void addSprite(SpriteDrawItem &sprite);
|
||||
void addText(int16 x, int16 y, byte color, uint fontResIndex, byte *text, int len);
|
||||
void addMask(SegmapMaskRect &mask);
|
||||
void update();
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
typedef Common::List<RenderQueueItem> RenderQueueArray;
|
||||
|
||||
ToltecsEngine *_vm;
|
||||
RenderQueueArray *_currQueue, *_prevQueue;
|
||||
MicroTileArray *_updateUta;
|
||||
|
||||
bool rectIntersectsItem(const Common::Rect &rect);
|
||||
RenderQueueItem *findItemInQueue(RenderQueueArray *queue, const RenderQueueItem &item);
|
||||
bool hasItemChanged(const RenderQueueItem &item1, const RenderQueueItem &item2);
|
||||
void invalidateItemsByRect(const Common::Rect &rect, const RenderQueueItem *item);
|
||||
|
||||
void addDirtyRect(const Common::Rect &rect);
|
||||
void restoreDirtyBackground();
|
||||
void updateDirtyRects();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_RENDER_H */
|
128
engines/toltecs/resource.cpp
Normal file
128
engines/toltecs/resource.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/resource.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
|
||||
/* ArchiveReader */
|
||||
|
||||
ArchiveReader::ArchiveReader() {
|
||||
}
|
||||
|
||||
ArchiveReader::~ArchiveReader() {
|
||||
delete[] _offsets;
|
||||
}
|
||||
|
||||
void ArchiveReader::openArchive(const char *filename) {
|
||||
open(filename);
|
||||
uint32 firstOffs = readUint32LE();
|
||||
uint count = firstOffs / 4;
|
||||
_offsets = new uint32[count];
|
||||
_offsets[0] = firstOffs;
|
||||
for (uint i = 1; i < count; i++)
|
||||
_offsets[i] = readUint32LE();
|
||||
}
|
||||
|
||||
uint32 ArchiveReader::openResource(uint resIndex) {
|
||||
uint32 resourceSize = getResourceSize(resIndex);
|
||||
seek(_offsets[resIndex]);
|
||||
return resourceSize;
|
||||
}
|
||||
|
||||
void ArchiveReader::closeResource() {
|
||||
}
|
||||
|
||||
uint32 ArchiveReader::getResourceSize(uint resIndex) {
|
||||
return _offsets[resIndex + 1] - _offsets[resIndex];
|
||||
}
|
||||
|
||||
void ArchiveReader::dump(uint resIndex, const char *prefix) {
|
||||
int32 resourceSize = getResourceSize(resIndex);
|
||||
byte *data = new byte[resourceSize];
|
||||
|
||||
Common::String fn;
|
||||
|
||||
if (prefix)
|
||||
fn = Common::String::format("%s_%04X.0", prefix, resIndex);
|
||||
else
|
||||
fn = Common::String::format("%04X.0", resIndex);
|
||||
|
||||
openResource(resIndex);
|
||||
read(data, resourceSize);
|
||||
closeResource();
|
||||
|
||||
Common::DumpFile o;
|
||||
o.open(fn);
|
||||
o.write(data, resourceSize);
|
||||
o.finalize();
|
||||
o.close();
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
/* ResourceCache */
|
||||
|
||||
ResourceCache::ResourceCache(ToltecsEngine *vm) : _vm(vm) {
|
||||
}
|
||||
|
||||
ResourceCache::~ResourceCache() {
|
||||
purgeCache();
|
||||
}
|
||||
|
||||
void ResourceCache::purgeCache() {
|
||||
for (ResourceMap::iterator iter = _cache.begin(); iter != _cache.end(); ++iter) {
|
||||
delete[] iter->_value->data;
|
||||
delete iter->_value;
|
||||
iter->_value = 0;
|
||||
}
|
||||
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
Resource *ResourceCache::load(uint resIndex) {
|
||||
ResourceMap::iterator item = _cache.find(resIndex);
|
||||
if (item != _cache.end()) {
|
||||
debug(1, "ResourceCache::load(%d) From cache", resIndex);
|
||||
return (*item)._value;
|
||||
} else {
|
||||
debug(1, "ResourceCache::load(%d) From disk", resIndex);
|
||||
|
||||
Resource *resItem = new Resource();
|
||||
resItem->size = _vm->_arc->openResource(resIndex);
|
||||
resItem->data = new byte[resItem->size];
|
||||
_vm->_arc->read(resItem->data, resItem->size);
|
||||
_vm->_arc->closeResource();
|
||||
|
||||
_cache[resIndex] = resItem;
|
||||
|
||||
return resItem;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
85
engines/toltecs/resource.h
Normal file
85
engines/toltecs/resource.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_RESOURCE_H
|
||||
#define TOLTECS_RESOURCE_H
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
const uint kMaxCacheItems = 1024;
|
||||
const uint kMaxCacheSize = 8 * 1024 * 1024; // 8 MB
|
||||
|
||||
|
||||
class ArchiveReader : public Common::File {
|
||||
public:
|
||||
ArchiveReader();
|
||||
~ArchiveReader();
|
||||
|
||||
void openArchive(const char *filename);
|
||||
|
||||
// Returns the size of the opened resource
|
||||
uint32 openResource(uint resIndex);
|
||||
// Closes the resource
|
||||
void closeResource();
|
||||
// Returns the size of the resource
|
||||
uint32 getResourceSize(uint resIndex);
|
||||
|
||||
void dump(uint resIndex, const char *prefix = NULL);
|
||||
|
||||
protected:
|
||||
uint32 *_offsets;
|
||||
|
||||
};
|
||||
|
||||
struct Resource {
|
||||
uint32 size;
|
||||
byte *data;
|
||||
};
|
||||
|
||||
class ResourceCache {
|
||||
public:
|
||||
ResourceCache(ToltecsEngine *vm);
|
||||
~ResourceCache();
|
||||
|
||||
Resource *load(uint resIndex);
|
||||
void purgeCache();
|
||||
|
||||
protected:
|
||||
typedef Common::HashMap<uint, Resource*> ResourceMap;
|
||||
|
||||
ToltecsEngine *_vm;
|
||||
|
||||
ResourceMap _cache;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_H */
|
204
engines/toltecs/saveload.cpp
Normal file
204
engines/toltecs/saveload.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/savefile.h"
|
||||
|
||||
#include "graphics/thumbnail.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/animation.h"
|
||||
#include "toltecs/palette.h"
|
||||
#include "toltecs/script.h"
|
||||
#include "toltecs/screen.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
/* TODO:
|
||||
- Save with F7; Load with F9
|
||||
- Saving during an animation (AnimationPlayer) is not working correctly yet
|
||||
- Maybe switch to SCUMM/Tinsel serialization approach?
|
||||
*/
|
||||
|
||||
#define TOLTECS_SAVEGAME_VERSION 0 // 0 is dev version until in official SVN
|
||||
|
||||
ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) {
|
||||
|
||||
header.version = in->readUint32LE();
|
||||
if (header.version != TOLTECS_SAVEGAME_VERSION)
|
||||
return kRSHEInvalidVersion;
|
||||
|
||||
byte descriptionLen = in->readByte();
|
||||
header.description = "";
|
||||
while (descriptionLen--)
|
||||
header.description += (char)in->readByte();
|
||||
|
||||
if (loadThumbnail) {
|
||||
header.thumbnail = Graphics::loadThumbnail(*in);
|
||||
} else {
|
||||
Graphics::skipThumbnail(*in);
|
||||
}
|
||||
|
||||
// Not used yet, reserved for future usage
|
||||
header.gameID = in->readByte();
|
||||
header.flags = in->readUint32LE();
|
||||
|
||||
return ((in->eos() || in->err()) ? kRSHEIoError : kRSHENoError);
|
||||
}
|
||||
|
||||
void ToltecsEngine::savegame(const char *filename, const char *description) {
|
||||
|
||||
Common::OutSaveFile *out;
|
||||
if (!(out = g_system->getSavefileManager()->openForSaving(filename))) {
|
||||
warning("Can't create file '%s', game not saved", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
out->writeUint32LE(TOLTECS_SAVEGAME_VERSION);
|
||||
|
||||
byte descriptionLen = strlen(description);
|
||||
out->writeByte(descriptionLen);
|
||||
out->write(description, descriptionLen);
|
||||
|
||||
Graphics::saveThumbnail(*out);
|
||||
|
||||
// Not used yet, reserved for future usage
|
||||
out->writeByte(0);
|
||||
out->writeUint32LE(0);
|
||||
|
||||
out->writeUint16LE(_cameraX);
|
||||
out->writeUint16LE(_cameraY);
|
||||
out->writeUint16LE(_cameraHeight);
|
||||
|
||||
out->writeUint16LE(_guiHeight);
|
||||
|
||||
out->writeUint16LE(_sceneWidth);
|
||||
out->writeUint16LE(_sceneHeight);
|
||||
out->writeUint32LE(_sceneResIndex);
|
||||
|
||||
out->writeUint16LE(_walkSpeedX);
|
||||
out->writeUint16LE(_walkSpeedY);
|
||||
|
||||
out->writeUint32LE(_counter01);
|
||||
out->writeUint32LE(_counter02);
|
||||
out->writeByte(_movieSceneFlag ? 1 : 0);
|
||||
out->writeByte(_flag01);
|
||||
|
||||
out->writeUint16LE(_mouseX);
|
||||
out->writeUint16LE(_mouseY);
|
||||
out->writeUint16LE(_mouseDisabled);
|
||||
|
||||
_palette->saveState(out);
|
||||
_script->saveState(out);
|
||||
_anim->saveState(out);
|
||||
_screen->saveState(out);
|
||||
|
||||
out->finalize();
|
||||
delete out;
|
||||
|
||||
}
|
||||
|
||||
void ToltecsEngine::loadgame(const char *filename) {
|
||||
|
||||
Common::InSaveFile *in;
|
||||
if (!(in = g_system->getSavefileManager()->openForLoading(filename))) {
|
||||
warning("Can't open file '%s', game not loaded", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
SaveHeader header;
|
||||
|
||||
kReadSaveHeaderError errorCode = readSaveHeader(in, false, header);
|
||||
|
||||
if (errorCode != kRSHENoError) {
|
||||
warning("Error loading savegame '%s'", filename);
|
||||
delete in;
|
||||
return;
|
||||
}
|
||||
|
||||
_cameraX = in->readUint16LE();
|
||||
_cameraY = in->readUint16LE();
|
||||
_cameraHeight = in->readUint16LE();
|
||||
|
||||
_guiHeight = in->readUint16LE();
|
||||
|
||||
_sceneWidth = in->readUint16LE();
|
||||
_sceneHeight = in->readUint16LE();
|
||||
_sceneResIndex = in->readUint32LE();
|
||||
|
||||
_walkSpeedX = in->readUint16LE();
|
||||
_walkSpeedY = in->readUint16LE();
|
||||
|
||||
_counter01 = in->readUint32LE();
|
||||
_counter02 = in->readUint32LE();
|
||||
_movieSceneFlag = in->readByte() != 0;
|
||||
_flag01 = in->readByte();
|
||||
|
||||
_mouseX = in->readUint16LE();
|
||||
_mouseY = in->readUint16LE();
|
||||
_mouseDisabled = in->readUint16LE();
|
||||
|
||||
_system->warpMouse(_mouseX, _mouseY);
|
||||
_system->showMouse(_mouseDisabled == 0);
|
||||
|
||||
_palette->loadState(in);
|
||||
_script->loadState(in);
|
||||
_anim->loadState(in);
|
||||
_screen->loadState(in);
|
||||
|
||||
delete in;
|
||||
|
||||
loadScene(_sceneResIndex);
|
||||
|
||||
_newCameraX = _cameraX;
|
||||
_newCameraY = _cameraY;
|
||||
|
||||
}
|
||||
|
||||
Common::Error ToltecsEngine::loadGameState(int slot) {
|
||||
const char *fileName = getSavegameFilename(slot);
|
||||
loadgame(fileName);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::Error ToltecsEngine::saveGameState(int slot, const Common::String &description) {
|
||||
const char *fileName = getSavegameFilename(slot);
|
||||
savegame(fileName, description.c_str());
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
const char *ToltecsEngine::getSavegameFilename(int num) {
|
||||
static Common::String filename;
|
||||
filename = getSavegameFilename(_targetName, num);
|
||||
return filename.c_str();
|
||||
}
|
||||
|
||||
Common::String ToltecsEngine::getSavegameFilename(const Common::String &target, int num) {
|
||||
assert(num >= 0 && num <= 999);
|
||||
|
||||
char extension[5];
|
||||
sprintf(extension, "%03d", num);
|
||||
|
||||
return target + "." + extension;
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
808
engines/toltecs/screen.cpp
Normal file
808
engines/toltecs/screen.cpp
Normal file
@ -0,0 +1,808 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graphics/cursorman.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/palette.h"
|
||||
#include "toltecs/render.h"
|
||||
#include "toltecs/resource.h"
|
||||
#include "toltecs/screen.h"
|
||||
#include "toltecs/script.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
Screen::Screen(ToltecsEngine *vm) : _vm(vm) {
|
||||
|
||||
_frontScreen = new byte[268800];
|
||||
_backScreen = new byte[870400];
|
||||
|
||||
memset(_fontResIndexArray, 0, sizeof(_fontResIndexArray));
|
||||
_fontColor1 = 0;
|
||||
_fontColor2 = 0;
|
||||
|
||||
// Screen shaking
|
||||
_shakeActive = false;
|
||||
_shakeCounterInit = 0;
|
||||
_shakeCounter = 0;
|
||||
_shakePos = 0;
|
||||
|
||||
// Verb line
|
||||
_verbLineNum = 0;
|
||||
memset(_verbLineItems, 0, sizeof(_verbLineItems));
|
||||
_verbLineX = 160;
|
||||
_verbLineY = 2;
|
||||
_verbLineWidth = 20;
|
||||
_verbLineCount = 0;
|
||||
|
||||
// Talk text
|
||||
_talkTextItemNum = 0;
|
||||
memset(_talkTextItems, 0, sizeof(_talkTextItems));
|
||||
_talkTextX = 0;
|
||||
_talkTextY = 0;
|
||||
_talkTextFontColor = 0;
|
||||
_talkTextMaxWidth = 520;
|
||||
|
||||
_renderQueue = new RenderQueue(_vm);
|
||||
_fullRefresh = false;
|
||||
_guiRefresh = false;
|
||||
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
|
||||
delete[] _frontScreen;
|
||||
delete[] _backScreen;
|
||||
|
||||
delete _renderQueue;
|
||||
|
||||
}
|
||||
|
||||
void Screen::unpackRle(byte *source, byte *dest, uint16 width, uint16 height) {
|
||||
int32 size = width * height;
|
||||
while (size > 0) {
|
||||
byte a = *source++;
|
||||
byte b = *source++;
|
||||
if (a == 0) {
|
||||
dest += b;
|
||||
size -= b;
|
||||
} else {
|
||||
b = ((b << 4) & 0xF0) | ((b >> 4) & 0x0F);
|
||||
memset(dest, b, a);
|
||||
dest += a;
|
||||
size -= a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::loadMouseCursor(uint resIndex) {
|
||||
byte mouseCursor[16 * 16], *mouseCursorP = mouseCursor;
|
||||
byte *cursorData = _vm->_res->load(resIndex)->data;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
byte pixel;
|
||||
byte mask1 = *cursorData++;
|
||||
byte mask2 = *cursorData++;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
pixel = 0xE5;
|
||||
if ((mask2 & 0x80) == 0)
|
||||
pixel = 0xE0;
|
||||
mask2 <<= 1;
|
||||
if ((mask1 & 0x80) == 0)
|
||||
pixel = 0;
|
||||
mask1 <<= 1;
|
||||
*mouseCursorP++ = pixel;
|
||||
}
|
||||
}
|
||||
// FIXME: Where's the cursor hotspot? Using 8, 8 seems good enough for now.
|
||||
CursorMan.replaceCursor((const byte*)mouseCursor, 16, 16, 8, 8, 0);
|
||||
}
|
||||
|
||||
void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) {
|
||||
|
||||
byte *imageData = _vm->_res->load(resIndex)->data;
|
||||
int16 headerSize = READ_LE_UINT16(imageData);
|
||||
int16 width = imageData[2];
|
||||
int16 height = imageData[3];
|
||||
int16 workWidth = width, workHeight = height;
|
||||
imageData += headerSize;
|
||||
|
||||
byte *dest = _frontScreen + x + (y + _vm->_cameraHeight) * 640;
|
||||
|
||||
//debug(0, "Screen::drawGuiImage() x = %d; y = %d; w = %d; h = %d; resIndex = %d", x, y, width, height, resIndex);
|
||||
|
||||
while (workHeight > 0) {
|
||||
int count = 1;
|
||||
byte pixel = *imageData++;
|
||||
if (pixel & 0x80) {
|
||||
pixel &= 0x7F;
|
||||
count = *imageData++;
|
||||
count += 2;
|
||||
}
|
||||
pixel = pixel + 0xE0;
|
||||
while (count-- && workHeight > 0) {
|
||||
*dest++ = pixel;
|
||||
workWidth--;
|
||||
if (workWidth == 0) {
|
||||
workHeight--;
|
||||
dest += 640 - width;
|
||||
workWidth = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_guiRefresh = true;
|
||||
|
||||
}
|
||||
|
||||
void Screen::startShakeScreen(int16 shakeCounter) {
|
||||
_shakeActive = true;
|
||||
_shakeCounterInit = shakeCounter;
|
||||
_shakeCounter = shakeCounter;
|
||||
_shakePos = 0;
|
||||
}
|
||||
|
||||
void Screen::stopShakeScreen() {
|
||||
_shakeActive = false;
|
||||
_vm->_system->setShakePos(0);
|
||||
}
|
||||
|
||||
void Screen::updateShakeScreen() {
|
||||
if (_shakeActive) {
|
||||
_shakeCounter--;
|
||||
if (_shakeCounter == 0) {
|
||||
_shakeCounter = _shakeCounterInit;
|
||||
_shakePos ^= 8;
|
||||
_vm->_system->setShakePos(_shakePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::addStaticSprite(byte *spriteItem) {
|
||||
|
||||
DrawRequest drawRequest;
|
||||
memset(&drawRequest, 0, sizeof(drawRequest));
|
||||
|
||||
drawRequest.y = READ_LE_UINT16(spriteItem + 0);
|
||||
drawRequest.x = READ_LE_UINT16(spriteItem + 2);
|
||||
int16 fragmentId = READ_LE_UINT16(spriteItem + 4);
|
||||
drawRequest.baseColor = _vm->_palette->findFragment(fragmentId) & 0xFF;
|
||||
drawRequest.resIndex = READ_LE_UINT16(spriteItem + 6);
|
||||
drawRequest.flags = READ_LE_UINT16(spriteItem + 8);
|
||||
drawRequest.scaling = 0;
|
||||
|
||||
debug(0, "Screen::addStaticSprite() x = %d; y = %d; baseColor = %d; resIndex = %d; flags = %04X", drawRequest.x, drawRequest.y, drawRequest.baseColor, drawRequest.resIndex, drawRequest.flags);
|
||||
|
||||
addDrawRequest(drawRequest);
|
||||
|
||||
}
|
||||
|
||||
void Screen::addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode) {
|
||||
|
||||
//debug(0, "Screen::addAnimatedSprite(%d, %d, %d)", x, y, fragmentId);
|
||||
|
||||
DrawRequest drawRequest;
|
||||
memset(&drawRequest, 0, sizeof(drawRequest));
|
||||
|
||||
drawRequest.x = x;
|
||||
drawRequest.y = y;
|
||||
drawRequest.baseColor = _vm->_palette->findFragment(fragmentId) & 0xFF;
|
||||
|
||||
if (mode == 1) {
|
||||
drawRequest.scaling = _vm->_segmap->getScalingAtPoint(drawRequest.x, drawRequest.y);
|
||||
} else if (mode == 2) {
|
||||
drawRequest.scaling = 0;
|
||||
}
|
||||
|
||||
int16 count = spriteArray[0];
|
||||
|
||||
//debug(0, "count = %d", count);
|
||||
|
||||
for (int16 index = 1; index <= count; index++) {
|
||||
|
||||
byte *spriteItem = data + spriteArray[index];
|
||||
|
||||
uint16 loopNum = READ_LE_UINT16(spriteItem + 0) & 0x7FFF;
|
||||
uint16 loopCount = READ_LE_UINT16(spriteItem + 2);
|
||||
uint16 frameNum = READ_LE_UINT16(spriteItem + 4);
|
||||
uint16 frameCount = READ_LE_UINT16(spriteItem + 6);
|
||||
drawRequest.resIndex = READ_LE_UINT16(spriteItem + 8);
|
||||
drawRequest.flags = READ_LE_UINT16(spriteItem + 10 + loopNum * 2);
|
||||
|
||||
debug(0, "Screen::addAnimatedSprite(%d of %d) loopNum = %d; loopCount = %d; frameNum = %d; frameCount = %d; resIndex = %d; flags = %04X, mode = %d",
|
||||
index, count, loopNum, loopCount, frameNum, frameCount, drawRequest.resIndex, drawRequest.flags, mode);
|
||||
|
||||
addDrawRequest(drawRequest);
|
||||
|
||||
frameNum++;
|
||||
if (frameNum == frameCount) {
|
||||
frameNum = 0;
|
||||
loopNum++;
|
||||
if (loopNum == loopCount) {
|
||||
if (loop) {
|
||||
loopNum = 0;
|
||||
} else {
|
||||
loopNum--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loopNum |= 0x8000;
|
||||
}
|
||||
|
||||
WRITE_LE_UINT16(spriteItem + 0, loopNum);
|
||||
WRITE_LE_UINT16(spriteItem + 4, frameNum);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screen::clearSprites() {
|
||||
|
||||
}
|
||||
|
||||
void Screen::blastSprite(int16 x, int16 y, int16 fragmentId, int16 resIndex, uint16 flags) {
|
||||
|
||||
DrawRequest drawRequest;
|
||||
SpriteDrawItem sprite;
|
||||
|
||||
drawRequest.x = x;
|
||||
drawRequest.y = y;
|
||||
drawRequest.baseColor = _vm->_palette->findFragment(fragmentId) & 0xFF;
|
||||
drawRequest.resIndex = resIndex;
|
||||
drawRequest.flags = flags;
|
||||
drawRequest.scaling = 0;
|
||||
|
||||
if (createSpriteDrawItem(drawRequest, sprite)) {
|
||||
sprite.x -= _vm->_cameraX;
|
||||
sprite.y -= _vm->_cameraY;
|
||||
drawSprite(sprite);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) {
|
||||
|
||||
debug(0, "Screen::updateVerbLine() _verbLineNum = %d; _verbLineX = %d; _verbLineY = %d; _verbLineWidth = %d; _verbLineCount = %d",
|
||||
_verbLineNum, _verbLineX, _verbLineY, _verbLineWidth, _verbLineCount);
|
||||
|
||||
Font font(_vm->_res->load(_fontResIndexArray[0])->data);
|
||||
|
||||
_verbLineItems[_verbLineNum].slotIndex = slotIndex;
|
||||
_verbLineItems[_verbLineNum].slotOffset = slotOffset;
|
||||
|
||||
// First clear the line
|
||||
int16 y = _verbLineY;
|
||||
for (int16 i = 0; i < _verbLineCount; i++) {
|
||||
byte *dest = _frontScreen + _verbLineX - _verbLineWidth / 2 + (y - 1 + _vm->_cameraHeight) * 640;
|
||||
for (int16 j = 0; j < 20; j++) {
|
||||
memset(dest, 0xE0, _verbLineWidth);
|
||||
dest += 640;
|
||||
}
|
||||
y += 18;
|
||||
}
|
||||
|
||||
GuiTextWrapState wrapState;
|
||||
int16 len = 0;
|
||||
wrapState.width = 0;
|
||||
wrapState.destString = wrapState.textBuffer;
|
||||
wrapState.len1 = 0;
|
||||
wrapState.len2 = 0;
|
||||
|
||||
y = _verbLineY;
|
||||
|
||||
memset(wrapState.textBuffer, 0, sizeof(wrapState.textBuffer));
|
||||
|
||||
for (int16 i = 0; i <= _verbLineNum; i++) {
|
||||
wrapState.sourceString = _vm->_script->getSlotData(_verbLineItems[i].slotIndex) + _verbLineItems[i].slotOffset;
|
||||
len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState);
|
||||
wrapState.len1 += len;
|
||||
}
|
||||
|
||||
if (_verbLineCount != 1) {
|
||||
int16 charWidth = 0;
|
||||
if (*wrapState.sourceString < 0xF0) {
|
||||
while (*wrapState.sourceString > 0x20 && *wrapState.sourceString < 0xF0 && len > 0) {
|
||||
byte ch = *wrapState.sourceString--;
|
||||
wrapState.len1--;
|
||||
len--;
|
||||
charWidth = font.getCharWidth(ch) + font.getSpacing() - 1;
|
||||
wrapState.width -= charWidth;
|
||||
}
|
||||
wrapState.width += charWidth;
|
||||
wrapState.sourceString++;
|
||||
wrapState.len1 -= len;
|
||||
wrapState.len2 = len + 1;
|
||||
|
||||
drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0], wrapState);
|
||||
|
||||
wrapState.destString = wrapState.textBuffer;
|
||||
wrapState.width = 0;
|
||||
len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState);
|
||||
wrapState.len1 += len;
|
||||
|
||||
y += 9;
|
||||
}
|
||||
y += 9;
|
||||
}
|
||||
|
||||
wrapState.len1 -= len;
|
||||
wrapState.len2 = len;
|
||||
|
||||
drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0], wrapState);
|
||||
|
||||
_guiRefresh = true;
|
||||
|
||||
}
|
||||
|
||||
void Screen::updateTalkText(int16 slotIndex, int16 slotOffset) {
|
||||
|
||||
int16 x, y, maxWidth, width, length;
|
||||
byte durationModifier = 1;
|
||||
byte *textData = _vm->_script->getSlotData(slotIndex) + slotOffset;
|
||||
|
||||
TalkTextItem *item = &_talkTextItems[_talkTextItemNum];
|
||||
|
||||
item->fontNum = 0;
|
||||
item->color = _talkTextFontColor;
|
||||
|
||||
x = CLIP<int16>(_talkTextX - _vm->_cameraX, 120, _talkTextMaxWidth);
|
||||
y = CLIP<int16>(_talkTextY - _vm->_cameraY, 4, _vm->_cameraHeight - 16);
|
||||
|
||||
maxWidth = 624 - ABS(x - 320) * 2;
|
||||
|
||||
while (1) {
|
||||
if (*textData == 0x0A) {
|
||||
x = CLIP<int16>(READ_LE_UINT16(&textData[3]), 120, _talkTextMaxWidth);
|
||||
y = CLIP<int16>(READ_LE_UINT16(&textData[1]), 4, _vm->_cameraHeight - 16);
|
||||
maxWidth = 624 - ABS(x - 320) * 2;
|
||||
textData += 4;
|
||||
} else if (*textData == 0x14) {
|
||||
item->color = ((textData[1] << 4) & 0xF0) | ((textData[1] >> 4) & 0x0F);
|
||||
textData += 2;
|
||||
} else if (*textData == 0x19) {
|
||||
durationModifier = textData[1];
|
||||
textData += 2;
|
||||
} else if (*textData < 0x0A) {
|
||||
item->fontNum = textData[0];
|
||||
// FIXME: Some texts request a font which isn't registered so we change it to a font that is
|
||||
if (_fontResIndexArray[item->fontNum] == 0)
|
||||
item->fontNum = 0;
|
||||
textData += 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
item->slotIndex = slotIndex;
|
||||
item->slotOffset = textData - _vm->_script->getSlotData(slotIndex);
|
||||
|
||||
width = 0;
|
||||
length = 0;
|
||||
|
||||
item->duration = 0;
|
||||
item->lineCount = 0;
|
||||
|
||||
Font font(_vm->_res->load(_fontResIndexArray[item->fontNum])->data);
|
||||
int16 wordLength, wordWidth;
|
||||
|
||||
while (*textData < 0xF0) {
|
||||
if (*textData == 0x1E) {
|
||||
textData++;
|
||||
addTalkTextRect(font, x, y, length, width, item);
|
||||
width = 0;
|
||||
length = 0;
|
||||
} else {
|
||||
wordLength = 0;
|
||||
wordWidth = 0;
|
||||
while (*textData >= 0x20 && *textData < 0xF0) {
|
||||
byte ch = *textData++;
|
||||
wordLength++;
|
||||
if (ch == 0x20) {
|
||||
wordWidth += font.getWidth();
|
||||
break;
|
||||
} else {
|
||||
wordWidth += font.getCharWidth(ch) + font.getSpacing() - 1;
|
||||
}
|
||||
}
|
||||
if (width + wordWidth > maxWidth + font.getWidth()) {
|
||||
addTalkTextRect(font, x, y, length, width, item);
|
||||
width = wordWidth;
|
||||
length = wordLength;
|
||||
} else {
|
||||
width += wordWidth;
|
||||
length += wordLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addTalkTextRect(font, x, y, length, width, item);
|
||||
|
||||
if (item->lineCount > 0) {
|
||||
int16 ysub = (font.getHeight() - 1) * item->lineCount;
|
||||
if (item->lines[0].y - 4 < ysub)
|
||||
ysub = item->lines[0].y - 4;
|
||||
for (int16 l = 0; l < item->lineCount; l++)
|
||||
item->lines[l].y -= ysub;
|
||||
}
|
||||
|
||||
int16 textDurationMultiplier = item->duration + 8;
|
||||
if (_vm->_doSpeech && *textData == 0xFE) {
|
||||
textDurationMultiplier += 100;
|
||||
}
|
||||
item->duration = 4 * textDurationMultiplier * durationModifier;
|
||||
|
||||
}
|
||||
|
||||
void Screen::addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item) {
|
||||
|
||||
if (width > 0) {
|
||||
TextRect *textRect = &item->lines[item->lineCount];
|
||||
width = width + 1 - font.getSpacing();
|
||||
textRect->width = width;
|
||||
item->duration += length;
|
||||
textRect->length = length;
|
||||
textRect->y = y;
|
||||
textRect->x = CLIP<int16>(x - width / 2, 0, 640);
|
||||
item->lineCount++;
|
||||
}
|
||||
|
||||
y += font.getHeight() - 1;
|
||||
|
||||
}
|
||||
|
||||
void Screen::addTalkTextItemsToRenderQueue() {
|
||||
|
||||
for (int16 i = 0; i <= _talkTextItemNum; i++) {
|
||||
TalkTextItem *item = &_talkTextItems[i];
|
||||
byte *text = _vm->_script->getSlotData(item->slotIndex) + item->slotOffset;
|
||||
|
||||
if (item->fontNum == -1 || item->duration == 0)
|
||||
continue;
|
||||
|
||||
//item->duration -= _vm->_counter01;
|
||||
item->duration--;
|
||||
if (item->duration < 0)
|
||||
item->duration = 0;
|
||||
|
||||
for (byte j = 0; j < item->lineCount; j++) {
|
||||
_renderQueue->addText(item->lines[j].x, item->lines[j].y, item->color, _fontResIndexArray[item->fontNum],
|
||||
text, item->lines[j].length);
|
||||
text += item->lines[j].length;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int16 Screen::getTalkTextDuration() {
|
||||
return _talkTextItems[_talkTextItemNum].duration;
|
||||
}
|
||||
|
||||
void Screen::finishTalkTextItems() {
|
||||
for (int16 i = 0; i <= _talkTextItemNum; i++) {
|
||||
_talkTextItems[i].duration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::keepTalkTextItemsAlive() {
|
||||
for (int16 i = 0; i <= _talkTextItemNum; i++) {
|
||||
TalkTextItem *item = &_talkTextItems[i];
|
||||
if (item->fontNum == -1)
|
||||
item->duration = 0;
|
||||
else if (item->duration > 0)
|
||||
item->duration = 2;
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::registerFont(uint fontIndex, uint resIndex) {
|
||||
_fontResIndexArray[fontIndex] = resIndex;
|
||||
}
|
||||
|
||||
void Screen::drawGuiTextMulti(byte *textData) {
|
||||
|
||||
int16 x = 0, y = 0;
|
||||
|
||||
// Really strange stuff.
|
||||
for (int i = 30; i >= 0; i--) {
|
||||
if (textData[i] >= 0xF0)
|
||||
break;
|
||||
if (i == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
GuiTextWrapState wrapState;
|
||||
wrapState.sourceString = textData;
|
||||
|
||||
do {
|
||||
if (*wrapState.sourceString == 0x0A) {
|
||||
// Set text position
|
||||
y = wrapState.sourceString[1];
|
||||
x = READ_LE_UINT32(wrapState.sourceString + 2);
|
||||
wrapState.sourceString += 4;
|
||||
} else if (*wrapState.sourceString == 0x0B) {
|
||||
// Inc text position
|
||||
y += wrapState.sourceString[1];
|
||||
x += wrapState.sourceString[2];
|
||||
wrapState.sourceString += 3;
|
||||
} else {
|
||||
wrapState.destString = wrapState.textBuffer;
|
||||
wrapState.width = 0;
|
||||
wrapState.len1 = 0;
|
||||
wrapState.len2 = wrapGuiText(_fontResIndexArray[1], 640, wrapState);
|
||||
drawGuiText(x - wrapState.width / 2, y, _fontColor1, _fontColor2, _fontResIndexArray[1], wrapState);
|
||||
}
|
||||
} while (*wrapState.sourceString != 0xFF);
|
||||
|
||||
_guiRefresh = true;
|
||||
|
||||
}
|
||||
|
||||
int16 Screen::wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wrapState) {
|
||||
|
||||
Font font(_vm->_res->load(fontResIndex)->data);
|
||||
int16 len = 0;
|
||||
|
||||
while (*wrapState.sourceString >= 0x20 && *wrapState.sourceString < 0xF0) {
|
||||
byte ch = *wrapState.sourceString;
|
||||
byte charWidth;
|
||||
if (ch <= 0x20)
|
||||
charWidth = font.getWidth();
|
||||
else
|
||||
charWidth = font.getCharWidth(ch) + font.getSpacing() - 1;
|
||||
if (wrapState.width + charWidth >= maxWidth)
|
||||
break;
|
||||
len++;
|
||||
wrapState.width += charWidth;
|
||||
*wrapState.destString++ = *wrapState.sourceString++;
|
||||
}
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
void Screen::drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex, GuiTextWrapState &wrapState) {
|
||||
|
||||
debug(0, "Screen::drawGuiText(%d, %d, %d, %d, %d) wrapState.len1 = %d; wrapState.len2 = %d", x, y, fontColor1, fontColor2, fontResIndex, wrapState.len1, wrapState.len2);
|
||||
|
||||
int16 ywobble = 1;
|
||||
|
||||
x = drawString(x + 1, y + _vm->_cameraHeight, fontColor1, fontResIndex, wrapState.textBuffer, wrapState.len1, &ywobble, false);
|
||||
x = drawString(x, y + _vm->_cameraHeight, fontColor2, fontResIndex, wrapState.textBuffer + wrapState.len1, wrapState.len2, &ywobble, false);
|
||||
|
||||
}
|
||||
|
||||
int16 Screen::drawString(int16 x, int16 y, byte color, uint fontResIndex, const byte *text, int len, int16 *ywobble, bool outline) {
|
||||
|
||||
//debug(0, "Screen::drawString(%d, %d, %d, %d)", x, y, color, fontResIndex);
|
||||
|
||||
Font font(_vm->_res->load(fontResIndex)->data);
|
||||
|
||||
if (len == -1)
|
||||
len = strlen((const char*)text);
|
||||
|
||||
int16 yadd = 0;
|
||||
if (ywobble)
|
||||
yadd = *ywobble;
|
||||
|
||||
while (len--) {
|
||||
byte ch = *text++;
|
||||
if (ch <= 0x20) {
|
||||
x += font.getWidth();
|
||||
} else {
|
||||
drawChar(font, _frontScreen, x, y - yadd, ch, color, outline);
|
||||
x += font.getCharWidth(ch) + font.getSpacing() - 1;
|
||||
yadd = -yadd;
|
||||
}
|
||||
}
|
||||
|
||||
if (ywobble)
|
||||
*ywobble = yadd;
|
||||
|
||||
return x;
|
||||
|
||||
}
|
||||
|
||||
void Screen::drawChar(const Font &font, byte *dest, int16 x, int16 y, byte ch, byte color, bool outline) {
|
||||
|
||||
int16 charWidth, charHeight;
|
||||
byte *charData;
|
||||
|
||||
dest += x + y * 640;
|
||||
|
||||
charWidth = font.getCharWidth(ch);
|
||||
//charHeight = font.getHeight() - 2;//Why was this here?!
|
||||
charHeight = font.getHeight();
|
||||
charData = font.getCharData(ch);
|
||||
|
||||
while (charHeight--) {
|
||||
byte lineWidth = charWidth;
|
||||
while (lineWidth > 0) {
|
||||
byte count = charData[0] & 0x0F;
|
||||
byte flags = charData[0] & 0xF0;
|
||||
charData++;
|
||||
if ((flags & 0x80) == 0) {
|
||||
if (flags & 0x10) {
|
||||
memset(dest, color, count);
|
||||
} else if (outline) {
|
||||
memset(dest, 0, count);
|
||||
}
|
||||
}
|
||||
dest += count;
|
||||
lineWidth -= count;
|
||||
}
|
||||
dest += 640 - charWidth;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screen::drawSurface(int16 x, int16 y, Graphics::Surface *surface) {
|
||||
|
||||
int16 skipX = 0;
|
||||
int16 width = surface->w;
|
||||
int16 height = surface->h;
|
||||
byte *surfacePixels = (byte*)surface->getBasePtr(0, 0);
|
||||
byte *frontScreen;
|
||||
|
||||
// Not on screen, skip
|
||||
if (x + width < 0 || y + height < 0 || x >= 640 || y >= _vm->_cameraHeight)
|
||||
return;
|
||||
|
||||
if (x < 0) {
|
||||
skipX = -x;
|
||||
x = 0;
|
||||
width -= skipX;
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
int16 skipY = -y;
|
||||
surfacePixels += surface->w * skipY;
|
||||
y = 0;
|
||||
height -= skipY;
|
||||
}
|
||||
|
||||
if (x + width >= 640) {
|
||||
width -= x + width - 640;
|
||||
}
|
||||
|
||||
if (y + height >= _vm->_cameraHeight) {
|
||||
height -= y + height - _vm->_cameraHeight;
|
||||
}
|
||||
|
||||
frontScreen = _vm->_screen->_frontScreen + x + (y * 640);
|
||||
|
||||
for (int16 h = 0; h < height; h++) {
|
||||
surfacePixels += skipX;
|
||||
for (int16 w = 0; w < width; w++) {
|
||||
if (*surfacePixels != 0xFF)
|
||||
*frontScreen = *surfacePixels;
|
||||
frontScreen++;
|
||||
surfacePixels++;
|
||||
}
|
||||
frontScreen += 640 - width;
|
||||
surfacePixels += surface->w - width - skipX;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Screen::saveState(Common::WriteStream *out) {
|
||||
|
||||
// Save verb line
|
||||
out->writeUint16LE(_verbLineNum);
|
||||
out->writeUint16LE(_verbLineX);
|
||||
out->writeUint16LE(_verbLineY);
|
||||
out->writeUint16LE(_verbLineWidth);
|
||||
out->writeUint16LE(_verbLineCount);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
out->writeUint16LE(_verbLineItems[i].slotIndex);
|
||||
out->writeUint16LE(_verbLineItems[i].slotOffset);
|
||||
}
|
||||
|
||||
// Save talk text items
|
||||
out->writeUint16LE(_talkTextX);
|
||||
out->writeUint16LE(_talkTextY);
|
||||
out->writeUint16LE(_talkTextMaxWidth);
|
||||
out->writeByte(_talkTextFontColor);
|
||||
out->writeUint16LE(_talkTextItemNum);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
out->writeUint16LE(_talkTextItems[i].duration);
|
||||
out->writeUint16LE(_talkTextItems[i].slotIndex);
|
||||
out->writeUint16LE(_talkTextItems[i].slotOffset);
|
||||
out->writeUint16LE(_talkTextItems[i].fontNum);
|
||||
out->writeByte(_talkTextItems[i].color);
|
||||
out->writeByte(_talkTextItems[i].lineCount);
|
||||
for (int j = 0; j < _talkTextItems[i].lineCount; j++) {
|
||||
out->writeUint16LE(_talkTextItems[i].lines[j].x);
|
||||
out->writeUint16LE(_talkTextItems[i].lines[j].y);
|
||||
out->writeUint16LE(_talkTextItems[i].lines[j].width);
|
||||
out->writeUint16LE(_talkTextItems[i].lines[j].length);
|
||||
}
|
||||
}
|
||||
|
||||
// Save GUI bitmap
|
||||
{
|
||||
byte *gui = _frontScreen + _vm->_cameraHeight * 640;
|
||||
for (int i = 0; i < _vm->_guiHeight; i++) {
|
||||
out->write(gui, 640);
|
||||
gui += 640;
|
||||
}
|
||||
}
|
||||
|
||||
// Save fonts
|
||||
for (int i = 0; i < 10; i++)
|
||||
out->writeUint32LE(_fontResIndexArray[i]);
|
||||
out->writeByte(_fontColor1);
|
||||
out->writeByte(_fontColor2);
|
||||
|
||||
}
|
||||
|
||||
void Screen::loadState(Common::ReadStream *in) {
|
||||
|
||||
// Load verb line
|
||||
_verbLineNum = in->readUint16LE();
|
||||
_verbLineX = in->readUint16LE();
|
||||
_verbLineY = in->readUint16LE();
|
||||
_verbLineWidth = in->readUint16LE();
|
||||
_verbLineCount = in->readUint16LE();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
_verbLineItems[i].slotIndex = in->readUint16LE();
|
||||
_verbLineItems[i].slotOffset = in->readUint16LE();
|
||||
}
|
||||
|
||||
// Load talk text items
|
||||
_talkTextX = in->readUint16LE();
|
||||
_talkTextY = in->readUint16LE();
|
||||
_talkTextMaxWidth = in->readUint16LE();
|
||||
_talkTextFontColor = in->readByte();
|
||||
_talkTextItemNum = in->readUint16LE();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
_talkTextItems[i].duration = in->readUint16LE();
|
||||
_talkTextItems[i].slotIndex = in->readUint16LE();
|
||||
_talkTextItems[i].slotOffset = in->readUint16LE();
|
||||
_talkTextItems[i].fontNum = in->readUint16LE();
|
||||
_talkTextItems[i].color = in->readByte();
|
||||
_talkTextItems[i].lineCount = in->readByte();
|
||||
for (int j = 0; j < _talkTextItems[i].lineCount; j++) {
|
||||
_talkTextItems[i].lines[j].x = in->readUint16LE();
|
||||
_talkTextItems[i].lines[j].y = in->readUint16LE();
|
||||
_talkTextItems[i].lines[j].width = in->readUint16LE();
|
||||
_talkTextItems[i].lines[j].length = in->readUint16LE();
|
||||
}
|
||||
}
|
||||
|
||||
// Load GUI bitmap
|
||||
{
|
||||
byte *gui = _frontScreen + _vm->_cameraHeight * 640;
|
||||
for (int i = 0; i < _vm->_guiHeight; i++) {
|
||||
in->read(gui, 640);
|
||||
gui += 640;
|
||||
}
|
||||
_guiRefresh = true;
|
||||
}
|
||||
|
||||
// Load fonts
|
||||
for (int i = 0; i < 10; i++)
|
||||
_fontResIndexArray[i] = in->readUint32LE();
|
||||
_fontColor1 = in->readByte();
|
||||
_fontColor2 = in->readByte();
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
251
engines/toltecs/screen.h
Normal file
251
engines/toltecs/screen.h
Normal file
@ -0,0 +1,251 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_SCREEN_H
|
||||
#define TOLTECS_SCREEN_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "toltecs/toltecs.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
struct DrawRequest {
|
||||
int16 x, y;
|
||||
int16 resIndex;
|
||||
uint16 flags;
|
||||
int16 baseColor;
|
||||
int8 scaling;
|
||||
};
|
||||
|
||||
struct SpriteDrawItem {
|
||||
int16 x, y;
|
||||
int16 width, height;
|
||||
int16 origWidth, origHeight;
|
||||
int16 resIndex, frameNum;
|
||||
uint32 offset;
|
||||
int16 xdelta, ydelta;
|
||||
uint16 flags;
|
||||
int16 skipX, yerror;
|
||||
int16 priority;
|
||||
int16 baseColor;
|
||||
};
|
||||
|
||||
struct SpriteFrameEntry {
|
||||
int16 y, x, h, w;
|
||||
uint32 offset;
|
||||
SpriteFrameEntry() {
|
||||
}
|
||||
SpriteFrameEntry(byte *data) {
|
||||
y = READ_LE_UINT16(data + 0);
|
||||
x = READ_LE_UINT16(data + 2);
|
||||
h = READ_LE_UINT16(data + 4);
|
||||
w = READ_LE_UINT16(data + 6);
|
||||
offset = READ_LE_UINT32(data + 8);
|
||||
}
|
||||
};
|
||||
|
||||
class Font {
|
||||
public:
|
||||
Font(byte *fontData) : _fontData(fontData) {
|
||||
}
|
||||
~Font() {
|
||||
}
|
||||
int16 getSpacing() const {
|
||||
return _fontData[1];
|
||||
}
|
||||
int16 getHeight() const {
|
||||
return _fontData[2];
|
||||
}
|
||||
int16 getWidth() const {
|
||||
return _fontData[3];
|
||||
}
|
||||
int16 getCharWidth(byte ch) const {
|
||||
return _fontData[4 + (ch - 0x21)];
|
||||
}
|
||||
byte *getCharData(byte ch) const {
|
||||
return _fontData + 0x298 + READ_LE_UINT16(&_fontData[0xE0 + (ch - 0x21) * 2]);
|
||||
}
|
||||
int16 getTextWidth(const byte *text) {
|
||||
int16 width = 0;
|
||||
while (*text && *text < 0xF0) {
|
||||
byte ch = *text++;
|
||||
if (ch <= 0x20) {
|
||||
width += getWidth();
|
||||
} else {
|
||||
width += getCharWidth(ch) + getSpacing() - 1;
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
protected:
|
||||
byte *_fontData;
|
||||
};
|
||||
|
||||
struct PixelPacket {
|
||||
byte count;
|
||||
byte pixel;
|
||||
};
|
||||
|
||||
enum SpriteReaderStatus {
|
||||
kSrsPixelsLeft,
|
||||
kSrsEndOfLine,
|
||||
kSrsEndOfSprite
|
||||
};
|
||||
|
||||
class SpriteFilter {
|
||||
public:
|
||||
SpriteFilter(const SpriteDrawItem &sprite) : _sprite(&sprite) {
|
||||
}
|
||||
virtual ~SpriteFilter() {}
|
||||
virtual SpriteReaderStatus readPacket(PixelPacket &packet) = 0;
|
||||
protected:
|
||||
const SpriteDrawItem *_sprite;
|
||||
};
|
||||
|
||||
struct TextRect {
|
||||
int16 x, y;
|
||||
int16 width, length;
|
||||
};
|
||||
|
||||
struct TalkTextItem {
|
||||
int16 duration;
|
||||
int16 slotIndex;
|
||||
int16 slotOffset;
|
||||
int16 fontNum;
|
||||
byte color;
|
||||
byte lineCount;
|
||||
TextRect lines[15];
|
||||
};
|
||||
|
||||
struct GuiTextWrapState {
|
||||
int16 len1, len2;
|
||||
byte *sourceString;
|
||||
byte *destString;
|
||||
int16 width;
|
||||
byte textBuffer[100];
|
||||
};
|
||||
|
||||
class RenderQueue;
|
||||
|
||||
class Screen {
|
||||
public:
|
||||
Screen(ToltecsEngine *vm);
|
||||
~Screen();
|
||||
|
||||
void unpackRle(byte *source, byte *dest, uint16 width, uint16 height);
|
||||
|
||||
void loadMouseCursor(uint resIndex);
|
||||
|
||||
void drawGuiImage(int16 x, int16 y, uint resIndex);
|
||||
|
||||
void startShakeScreen(int16 shakeCounter);
|
||||
void stopShakeScreen();
|
||||
void updateShakeScreen();
|
||||
|
||||
// Sprite list
|
||||
void addStaticSprite(byte *spriteItem);
|
||||
void addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode);
|
||||
void clearSprites();
|
||||
|
||||
// Sprite drawing
|
||||
void drawSprite(const SpriteDrawItem &sprite);
|
||||
void drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawItem &sprite);
|
||||
void blastSprite(int16 x, int16 y, int16 fragmentId, int16 resIndex, uint16 flags);
|
||||
|
||||
// Verb line
|
||||
void updateVerbLine(int16 slotIndex, int16 slotOffset);
|
||||
|
||||
// Talk text
|
||||
void updateTalkText(int16 slotIndex, int16 slotOffset);
|
||||
void addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item);
|
||||
void addTalkTextItemsToRenderQueue();
|
||||
int16 getTalkTextDuration();
|
||||
void finishTalkTextItems();
|
||||
void keepTalkTextItemsAlive();
|
||||
|
||||
// Font/text
|
||||
void registerFont(uint fontIndex, uint resIndex);
|
||||
void drawGuiTextMulti(byte *textData);
|
||||
int16 wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wrapState);
|
||||
void drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex, GuiTextWrapState &wrapState);
|
||||
|
||||
int16 drawString(int16 x, int16 y, byte color, uint fontResIndex, const byte *text, int len = -1, int16 *ywobble = NULL, bool outline = false);
|
||||
void drawChar(const Font &font, byte *dest, int16 x, int16 y, byte ch, byte color, bool outline);
|
||||
|
||||
void drawSurface(int16 x, int16 y, Graphics::Surface *surface);
|
||||
|
||||
void saveState(Common::WriteStream *out);
|
||||
void loadState(Common::ReadStream *in);
|
||||
|
||||
uint getFontResIndex(int fontNum) const { return _fontResIndexArray[fontNum]; }
|
||||
|
||||
//protected:
|
||||
public:
|
||||
|
||||
struct VerbLineItem {
|
||||
int16 slotIndex;
|
||||
int16 slotOffset;
|
||||
};
|
||||
|
||||
struct Rect {
|
||||
int16 x, y, width, height;
|
||||
};
|
||||
|
||||
ToltecsEngine *_vm;
|
||||
|
||||
byte *_frontScreen, *_backScreen;
|
||||
|
||||
uint _fontResIndexArray[10];
|
||||
byte _fontColor1, _fontColor2;
|
||||
|
||||
// Screen shaking
|
||||
bool _shakeActive;
|
||||
int16 _shakeCounterInit, _shakeCounter;
|
||||
int _shakePos;
|
||||
|
||||
// Verb line
|
||||
int16 _verbLineNum;
|
||||
VerbLineItem _verbLineItems[8];
|
||||
int16 _verbLineX, _verbLineY, _verbLineWidth;
|
||||
int16 _verbLineCount;
|
||||
|
||||
// Talk text
|
||||
int16 _talkTextX, _talkTextY;
|
||||
int16 _talkTextMaxWidth;
|
||||
byte _talkTextFontColor;
|
||||
int16 _talkTextItemNum;
|
||||
TalkTextItem _talkTextItems[5];
|
||||
|
||||
RenderQueue *_renderQueue;
|
||||
bool _fullRefresh;
|
||||
bool _guiRefresh;
|
||||
|
||||
bool createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem &sprite);
|
||||
void addDrawRequest(const DrawRequest &drawRequest);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_SCREEN_H */
|
1117
engines/toltecs/script.cpp
Normal file
1117
engines/toltecs/script.cpp
Normal file
File diff suppressed because it is too large
Load Diff
184
engines/toltecs/script.h
Normal file
184
engines/toltecs/script.h
Normal file
@ -0,0 +1,184 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_SCRIPT_H
|
||||
#define TOLTECS_SCRIPT_H
|
||||
|
||||
#include "common/func.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
const int kMaxScriptSlots = 50;
|
||||
const int kScriptStackSize = 4096 + 4;
|
||||
|
||||
enum VarType {
|
||||
vtByte,
|
||||
vtWord
|
||||
};
|
||||
|
||||
typedef Common::Functor0<void> ScriptFunction;
|
||||
|
||||
class ScriptInterpreter {
|
||||
public:
|
||||
ScriptInterpreter(ToltecsEngine *vm);
|
||||
~ScriptInterpreter();
|
||||
|
||||
void loadScript(uint resIndex, uint slotIndex);
|
||||
void setMainScript(uint slotIndex);
|
||||
void runScript();
|
||||
|
||||
byte *getSlotData(int slotIndex) const { return _slots[slotIndex].data; }
|
||||
|
||||
VarType getGameVarType(uint variable);
|
||||
int16 getGameVar(uint variable);
|
||||
void setGameVar(uint variable, int16 value);
|
||||
|
||||
void saveState(Common::WriteStream *out);
|
||||
void loadState(Common::ReadStream *in);
|
||||
|
||||
protected:
|
||||
|
||||
struct ScriptRegs {
|
||||
int16 reg0;
|
||||
int16 reg1;
|
||||
int16 reg2;
|
||||
int16 reg3;
|
||||
int16 reg4;
|
||||
int16 reg5;
|
||||
int16 reg6;
|
||||
int16 sp;
|
||||
int16 reg8;
|
||||
};
|
||||
|
||||
struct ScriptSlot {
|
||||
byte *data;
|
||||
int32 size;
|
||||
uint resIndex;
|
||||
};
|
||||
|
||||
ToltecsEngine *_vm;
|
||||
Common::Array<const ScriptFunction*> _scriptFuncs;
|
||||
Common::Array<const char *> _scriptFuncNames;
|
||||
|
||||
byte *_stack;
|
||||
|
||||
byte *_code, *_subCode;
|
||||
byte *_localData;
|
||||
bool _switchLocalDataNear, _switchLocalDataFar, _switchLocalDataToStack;
|
||||
bool _cmpBitTest;
|
||||
|
||||
ScriptSlot _slots[kMaxScriptSlots];
|
||||
|
||||
ScriptRegs _regs;
|
||||
int16 _savedSp;
|
||||
|
||||
byte readByte();
|
||||
int16 readInt16();
|
||||
|
||||
void execOpcode(byte opcode);
|
||||
|
||||
void setupScriptFunctions();
|
||||
void execScriptFunction(uint16 index);
|
||||
|
||||
byte arg8(int16 offset);
|
||||
int16 arg16(int16 offset);
|
||||
|
||||
void pushInt16(int16 value);
|
||||
int16 popInt16();
|
||||
|
||||
void localWrite8(int16 offset, byte value);
|
||||
byte localRead8(int16 offset);
|
||||
void localWrite16(int16 offset, int16 value);
|
||||
int16 localRead16(int16 offset);
|
||||
byte *localPtr(int16 offset);
|
||||
|
||||
void sfNop();
|
||||
void sfGetGameVar();
|
||||
void sfSetGameVar();
|
||||
void sfUpdateScreen();
|
||||
void sfGetRandomNumber();
|
||||
void sfDrawGuiTextMulti();
|
||||
void sfUpdateVerbLine();
|
||||
void sfSetFontColor();
|
||||
void sfGetTalkTextDuration();
|
||||
void sfTalk();
|
||||
void sfFindPaletteFragment();
|
||||
void sfClearPaletteFragments();
|
||||
void sfAddPaletteFragment();
|
||||
void sfSetDeltaAnimPalette();
|
||||
void sfSetUnkPaletteEffect();
|
||||
void sfBuildColorTransTable();
|
||||
void sfSetDeltaMainPalette();
|
||||
void sfLoadScript();
|
||||
void sfRegisterFont();
|
||||
void sfLoadAddPalette();
|
||||
void sfLoadScene();
|
||||
void sfSetGuiHeight();
|
||||
void sfFindMouseInRectIndex1();
|
||||
void sfFindMouseInRectIndex2();
|
||||
void sfDrawGuiImage();
|
||||
void sfAddAnimatedSpriteNoLoop();
|
||||
void sfAddAnimatedSprite();
|
||||
void sfAddStaticSprite();
|
||||
void sfAddAnimatedSpriteScaled();
|
||||
void sfFindPath();
|
||||
void sfWalk();
|
||||
void sfScrollCameraUp();
|
||||
void sfScrollCameraDown();
|
||||
void sfScrollCameraLeft();
|
||||
void sfScrollCameraRight();
|
||||
void sfScrollCameraUpEx();
|
||||
void sfScrollCameraDownEx();
|
||||
void sfScrollCameraLeftEx();
|
||||
void sfScrollCameraRightEx();
|
||||
void sfSetCamera();
|
||||
void sfGetCameraChanged();
|
||||
void sfGetRgbModifiertAtPoint();
|
||||
void sfStartAnim();
|
||||
void sfAnimNextFrame();
|
||||
void sfGetAnimFrameNumber();
|
||||
void sfGetAnimStatus();
|
||||
void sfStartShakeScreen();
|
||||
void sfStopShakeScreen();
|
||||
void sfStartSequence();
|
||||
void sfEndSequence();
|
||||
void sfSetSequenceVolume();
|
||||
void sfPlayPositionalSound();
|
||||
void sfPlaySound2();
|
||||
void sfClearScreen();
|
||||
void sfHandleInput();
|
||||
void sfRunOptionsScreen();
|
||||
void sfPrecacheSprites();
|
||||
void sfPrecacheSounds1();
|
||||
void sfDeletePrecachedFiles();
|
||||
void sfPrecacheSounds2();
|
||||
void sfRestoreStackPtr();
|
||||
void sfSaveStackPtr();
|
||||
void sfPlayMovie();
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_H */
|
408
engines/toltecs/segmap.cpp
Normal file
408
engines/toltecs/segmap.cpp
Normal file
@ -0,0 +1,408 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/render.h"
|
||||
#include "toltecs/segmap.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
SegmentMap::SegmentMap(ToltecsEngine *vm) : _vm(vm) {
|
||||
}
|
||||
|
||||
SegmentMap::~SegmentMap() {
|
||||
freeSegmapMaskRectSurfaces();
|
||||
}
|
||||
|
||||
void SegmentMap::load(byte *source) {
|
||||
|
||||
freeSegmapMaskRectSurfaces();
|
||||
_maskRects.clear();
|
||||
_pathRects.clear();
|
||||
_infoRects.clear();
|
||||
|
||||
// Load mask rects
|
||||
byte *maskData = source + 2;
|
||||
uint16 maskSize = READ_LE_UINT16(source);
|
||||
source += 2;
|
||||
uint16 maskRectCount = READ_LE_UINT16(source);
|
||||
source += 2;
|
||||
uint16 maskRectDataSize = maskRectCount * 12 + 2;
|
||||
|
||||
debug(0, "SegmentMap::load() maskRectCount = %d", maskRectCount);
|
||||
|
||||
for (uint16 i = 0; i < maskRectCount; i++) {
|
||||
SegmapMaskRect maskRect;
|
||||
int16 maskOffset;
|
||||
maskRect.y = READ_LE_UINT16(source);
|
||||
maskRect.x = READ_LE_UINT16(source + 2);
|
||||
maskRect.height = READ_LE_UINT16(source + 4);
|
||||
maskRect.width = READ_LE_UINT16(source + 6);
|
||||
maskOffset = READ_LE_UINT16(source + 8);
|
||||
maskRect.priority = READ_LE_UINT16(source + 10);
|
||||
loadSegmapMaskRectSurface(maskData + maskOffset, maskRect);
|
||||
|
||||
debug(0, "SegmentMap::load() (%d, %d, %d, %d, %04X, %d)",
|
||||
maskRect.x, maskRect.y, maskRect.width, maskRect.height, maskOffset, maskRect.priority);
|
||||
|
||||
source += 12;
|
||||
_maskRects.push_back(maskRect);
|
||||
}
|
||||
|
||||
source += maskSize - maskRectDataSize;
|
||||
|
||||
// Load path rects
|
||||
|
||||
source += 2; // skip rects array size
|
||||
|
||||
uint16 pathRectCount = READ_LE_UINT16(source);
|
||||
source += 2;
|
||||
|
||||
debug(0, "SegmentMap::load() pathRectCount = %d", pathRectCount);
|
||||
|
||||
for (uint16 i = 0; i < pathRectCount; i++) {
|
||||
SegmapPathRect pathRect;
|
||||
pathRect.y1 = READ_LE_UINT16(source);
|
||||
pathRect.x1 = READ_LE_UINT16(source + 2);
|
||||
pathRect.y2 = pathRect.y1 + READ_LE_UINT16(source + 4);
|
||||
pathRect.x2 = pathRect.x1 + READ_LE_UINT16(source + 6);
|
||||
|
||||
debug(0, "SegmentMap::load() (%d, %d, %d, %d)", pathRect.x1, pathRect.y1, pathRect.x2, pathRect.y2);
|
||||
|
||||
source += 8;
|
||||
_pathRects.push_back(pathRect);
|
||||
}
|
||||
|
||||
// Load info rects
|
||||
|
||||
source += 2; // skip rects array size
|
||||
|
||||
uint16 infoRectCount = READ_LE_UINT16(source);
|
||||
source += 2;
|
||||
debug(0, "SegmentMap::load() infoRectCount = %d", infoRectCount);
|
||||
for (uint16 i = 0; i < infoRectCount; i++) {
|
||||
SegmapInfoRect infoRect;
|
||||
infoRect.y = READ_LE_UINT16(source);
|
||||
infoRect.x = READ_LE_UINT16(source + 2);
|
||||
infoRect.height = READ_LE_UINT16(source + 4);
|
||||
infoRect.width = READ_LE_UINT16(source + 6);
|
||||
infoRect.id = source[8];
|
||||
infoRect.a = source[9];
|
||||
infoRect.b = source[10];
|
||||
infoRect.c = source[11];
|
||||
|
||||
debug(0, "SegmentMap::load() (%d, %d, %d, %d) (%d, %d, %d, %d)",
|
||||
infoRect.x, infoRect.y, infoRect.width, infoRect.height,
|
||||
infoRect.id, (int8)infoRect.a, (int8)infoRect.b, (int8)infoRect.c);
|
||||
|
||||
source += 12;
|
||||
_infoRects.push_back(infoRect);
|
||||
}
|
||||
|
||||
// TODO Other stuff
|
||||
|
||||
|
||||
}
|
||||
|
||||
int16 SegmentMap::findPathRectAtPoint(int16 x, int16 y) {
|
||||
for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
|
||||
if (y >= _pathRects[rectIndex].y1 && y <= _pathRects[rectIndex].y2 &&
|
||||
x >= _pathRects[rectIndex].x1 && x <= _pathRects[rectIndex].x2) {
|
||||
return rectIndex;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SegmentMap::adjustPathPoint(int16 &x, int16 &y) {
|
||||
|
||||
if (findPathRectAtPoint(x, y) != -1)
|
||||
return;
|
||||
|
||||
uint32 minDistance = 0xFFFFFFFF, distance;
|
||||
int16 adjustedX = 0, adjustedY = 0, x2, y2;
|
||||
|
||||
for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
|
||||
|
||||
if (x >= _pathRects[rectIndex].x1 && x < _pathRects[rectIndex].x2) {
|
||||
x2 = x;
|
||||
} else if (ABS(x - _pathRects[rectIndex].x1) >= ABS(x - _pathRects[rectIndex].x2)) {
|
||||
x2 = _pathRects[rectIndex].x2;
|
||||
} else {
|
||||
x2 = _pathRects[rectIndex].x1;
|
||||
}
|
||||
|
||||
if (ABS(y - _pathRects[rectIndex].y1) >= ABS(y - _pathRects[rectIndex].y2)) {
|
||||
y2 = _pathRects[rectIndex].y2;
|
||||
} else {
|
||||
y2 = _pathRects[rectIndex].y1;
|
||||
}
|
||||
|
||||
distance = ABS(y - y2) + ABS(x - x2);
|
||||
if (distance < minDistance) {
|
||||
if (x >= _pathRects[rectIndex].x1 && x <= _pathRects[rectIndex].x2) {
|
||||
adjustedX = x;
|
||||
} else {
|
||||
adjustedX = x2;
|
||||
}
|
||||
if (y >= _pathRects[rectIndex].y1 && y <= _pathRects[rectIndex].y2) {
|
||||
adjustedY = y;
|
||||
} else {
|
||||
adjustedY = y2;
|
||||
}
|
||||
minDistance = distance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
x = adjustedX;
|
||||
y = adjustedY;
|
||||
|
||||
}
|
||||
|
||||
int16 SegmentMap::findNextPathRect(int16 srcRectIndex, int16 destX, int16 destY) {
|
||||
|
||||
int16 result;
|
||||
uint16 minDistance, distance;
|
||||
int16 x1, y1, x2, y2;
|
||||
int16 xmin, xmax, ymax, ymin;
|
||||
|
||||
result = -1;
|
||||
minDistance = 0xFFFF;
|
||||
|
||||
x1 = _pathRects[srcRectIndex].x1;
|
||||
y1 = _pathRects[srcRectIndex].y1;
|
||||
x2 = _pathRects[srcRectIndex].x2;
|
||||
y2 = _pathRects[srcRectIndex].y2;
|
||||
|
||||
for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
|
||||
|
||||
int16 nodeX = -1, nodeY = -1;
|
||||
|
||||
// Check if the current rectangle is connected to the source rectangle
|
||||
if (x1 == _pathRects[rectIndex].x2 && y1 < _pathRects[rectIndex].y2 && y2 > _pathRects[rectIndex].y1) {
|
||||
nodeX = x1;
|
||||
} else if (x2 == _pathRects[rectIndex].x1 && y1 < _pathRects[rectIndex].y2 && y2 > _pathRects[rectIndex].y1) {
|
||||
nodeX = x2 - 1;
|
||||
} else if (y1 == _pathRects[rectIndex].y2 && x1 < _pathRects[rectIndex].x2 && x2 > _pathRects[rectIndex].x1) {
|
||||
nodeY = y1;
|
||||
} else if (y2 == _pathRects[rectIndex].y1 && x1 < _pathRects[rectIndex].x2 && x2 > _pathRects[rectIndex].x1) {
|
||||
nodeY = y2 - 1;
|
||||
} else
|
||||
continue;
|
||||
|
||||
if (nodeX == -1) {
|
||||
xmin = MAX<int16>(x1, _pathRects[rectIndex].x1);
|
||||
xmax = MIN<int16>(x2, _pathRects[rectIndex].x2) - 1;
|
||||
if (destX > xmin && destX < xmax) {
|
||||
nodeX = destX;
|
||||
} else if (ABS(destX - xmin) >= ABS(destX - xmax)) {
|
||||
nodeX = xmax - 1;
|
||||
} else {
|
||||
nodeX = xmin;
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeY == -1) {
|
||||
ymin = MAX<int16>(y1, _pathRects[rectIndex].y1);
|
||||
ymax = MIN<int16>(y2, _pathRects[rectIndex].y2) - 1;
|
||||
if (destY > ymin && destY < ymax) {
|
||||
nodeY = destY;
|
||||
} else if (ABS(destY - ymin) >= ABS(destY - ymax)) {
|
||||
nodeY = ymax - 1;
|
||||
} else {
|
||||
nodeY = ymin;
|
||||
}
|
||||
}
|
||||
|
||||
distance = ABS(destX - nodeX) + ABS(destY - nodeY);
|
||||
|
||||
for (uint i = 0; i < _closedPathRectsCount; i++) {
|
||||
if (rectIndex == _closedPathRects[i]) {
|
||||
distance = minDistance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _deadEndPathRectsCount; i++) {
|
||||
if (rectIndex == _deadEndPathRects[i]) {
|
||||
distance = minDistance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (distance < minDistance) {
|
||||
result = rectIndex;
|
||||
minDistance = distance;
|
||||
_pathNodes[_pathNodesCount].x = nodeX;
|
||||
_pathNodes[_pathNodesCount].y = nodeY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct LineData {
|
||||
int pitch;
|
||||
byte *surf;
|
||||
};
|
||||
|
||||
void plotProc(int x, int y, int color, void *data) {
|
||||
LineData *ld = (LineData*)data;
|
||||
ld->surf[x + y * ld->pitch] = color;
|
||||
}
|
||||
|
||||
void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 sourceX, int16 sourceY) {
|
||||
|
||||
// TODO: Writes to pointsArray aren't endian-safe yet
|
||||
|
||||
int16 currentRectIndex, destRectIndex;
|
||||
int16 pointsCount;
|
||||
|
||||
debug(0, "SegmentMap::findPath(fromX: %d; fromY: %d; toX: %d; toY: %d)", sourceX, sourceY, destX, destY);
|
||||
|
||||
_deadEndPathRectsCount = 0;
|
||||
_closedPathRectsCount = 0;
|
||||
_pathNodesCount = 0;
|
||||
|
||||
pointsCount = 2;
|
||||
|
||||
adjustPathPoint(sourceX, sourceY);
|
||||
currentRectIndex = findPathRectAtPoint(sourceX, sourceY);
|
||||
|
||||
adjustPathPoint(destX, destY);
|
||||
destRectIndex = findPathRectAtPoint(destX, destY);
|
||||
|
||||
if (currentRectIndex != -1) {
|
||||
if (destRectIndex != currentRectIndex) {
|
||||
while (1) {
|
||||
do {
|
||||
_closedPathRects[_closedPathRectsCount++] = currentRectIndex;
|
||||
currentRectIndex = findNextPathRect(currentRectIndex, destX, destY);
|
||||
_pathNodesCount++;
|
||||
} while (currentRectIndex != -1 && currentRectIndex != destRectIndex);
|
||||
if (currentRectIndex != -1 && currentRectIndex == destRectIndex)
|
||||
break;
|
||||
_deadEndPathRects[_deadEndPathRectsCount++] = _closedPathRects[--_closedPathRectsCount];
|
||||
_pathNodesCount -= 2;
|
||||
currentRectIndex = _closedPathRects[--_closedPathRectsCount];
|
||||
}
|
||||
for (int16 i = 0; i < _pathNodesCount; i++) {
|
||||
pointsArray[pointsCount++] = _pathNodes[i].y;
|
||||
pointsArray[pointsCount++] = _pathNodes[i].x;
|
||||
}
|
||||
}
|
||||
|
||||
pointsArray[pointsCount++] = destY;
|
||||
pointsArray[pointsCount++] = destX;
|
||||
|
||||
pointsArray[0] = 0;
|
||||
pointsArray[1] = _pathNodesCount + 1;
|
||||
}
|
||||
|
||||
debug(0, "SegmentMap::findPath() count = %d", pointsArray[1]);
|
||||
|
||||
#if 0 // DEBUG: Draw the path we found
|
||||
int sx = sourceX, sy = sourceY;
|
||||
LineData ld;
|
||||
ld.pitch = _vm->_sceneWidth;
|
||||
ld.surf = _vm->_screen->_backScreen;
|
||||
for (int16 i = 0; i < pointsArray[1] * 2; i+=2) {
|
||||
debug(0, "x = %d; y = %d", pointsArray[3+i], pointsArray[2+i]);
|
||||
Graphics::drawLine(sx, sy, pointsArray[3+i], pointsArray[2+i], 0xFF, plotProc, &ld);
|
||||
sx = pointsArray[3+i];
|
||||
sy = pointsArray[2+i];
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int8 SegmentMap::getScalingAtPoint(int16 x, int16 y) {
|
||||
int8 scaling = 0;
|
||||
for (uint i = 0; i < _infoRects.size(); i++) {
|
||||
if (_infoRects[i].id == 0 && _infoRects[i].isPointInside(x, y)) {
|
||||
int8 topScaling = (int8)_infoRects[i].b;
|
||||
int8 bottomScaling = (int8)_infoRects[i].c;
|
||||
if (y - _infoRects[i].y != 0) {
|
||||
scaling = (ABS(y - _infoRects[i].y) * (bottomScaling - topScaling) / _infoRects[i].height) + topScaling;
|
||||
}
|
||||
}
|
||||
}
|
||||
debug(0, "SegmentMap::getScalingAtPoint(%d, %d) %d", x, y, scaling);
|
||||
return scaling;
|
||||
}
|
||||
|
||||
void SegmentMap::getRgbModifiertAtPoint(int16 x, int16 y, int16 id, byte &r, byte &g, byte &b) {
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
for (uint i = 0; i < _infoRects.size(); i++) {
|
||||
if (_infoRects[i].id == id && _infoRects[i].isPointInside(x, y)) {
|
||||
r = _infoRects[i].a;
|
||||
g = _infoRects[i].b;
|
||||
b = _infoRects[i].c;
|
||||
}
|
||||
}
|
||||
debug(0, "SegmentMap::getRgbModifiertAtPoint() r: %d; g: %d; b: %d", r, g, b);
|
||||
}
|
||||
|
||||
void SegmentMap::loadSegmapMaskRectSurface(byte *maskData, SegmapMaskRect &maskRect) {
|
||||
|
||||
maskRect.surface = new Graphics::Surface();
|
||||
maskRect.surface->create(maskRect.width, maskRect.height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
byte *backScreen = _vm->_screen->_backScreen + maskRect.x + (maskRect.y * _vm->_sceneWidth);
|
||||
byte *dest = (byte*)maskRect.surface->getBasePtr(0, 0);
|
||||
|
||||
for (int16 h = 0; h < maskRect.height; h++) {
|
||||
int16 w = maskRect.width;
|
||||
while (w > 0) {
|
||||
byte mask = *maskData++;
|
||||
byte count = mask & 0x7F;
|
||||
if (mask & 0x80)
|
||||
memcpy(dest, backScreen, count);
|
||||
else
|
||||
memset(dest, 0xFF, count);
|
||||
w -= count;
|
||||
dest += count;
|
||||
backScreen += count;
|
||||
}
|
||||
backScreen += _vm->_sceneWidth - maskRect.width;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SegmentMap::freeSegmapMaskRectSurfaces() {
|
||||
for (uint i = 0; i < _maskRects.size(); i++) {
|
||||
delete _maskRects[i].surface;
|
||||
}
|
||||
}
|
||||
|
||||
void SegmentMap::addMasksToRenderQueue() {
|
||||
for (uint i = 0; i < _maskRects.size(); i++) {
|
||||
_vm->_screen->_renderQueue->addMask(_maskRects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
116
engines/toltecs/segmap.h
Normal file
116
engines/toltecs/segmap.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_SEGMAP_H
|
||||
#define TOLTECS_SEGMAP_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
#include "toltecs/screen.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
struct ScriptWalk {
|
||||
int16 y, x;
|
||||
int16 y1, x1, y2, x2;
|
||||
int16 yerror, xerror;
|
||||
int16 mulValue;
|
||||
int16 scaling;
|
||||
};
|
||||
|
||||
struct SegmapMaskRect {
|
||||
int16 x, y;
|
||||
int16 width, height;
|
||||
int16 priority;
|
||||
Graphics::Surface *surface;
|
||||
};
|
||||
|
||||
class SegmentMap {
|
||||
public:
|
||||
SegmentMap(ToltecsEngine *vm);
|
||||
~SegmentMap();
|
||||
|
||||
void load(byte *source);
|
||||
|
||||
int16 findPathRectAtPoint(int16 x, int16 y);
|
||||
void adjustPathPoint(int16 &x, int16 &y);
|
||||
|
||||
void findPath(int16 *pointsArray, int16 destX, int16 destY, int16 sourceX, int16 sourceY);
|
||||
|
||||
int8 getScalingAtPoint(int16 x, int16 y);
|
||||
void getRgbModifiertAtPoint(int16 x, int16 y, int16 id, byte &r, byte &g, byte &b);
|
||||
|
||||
void addMasksToRenderQueue();
|
||||
|
||||
//protected:
|
||||
public: // for debugging purposes
|
||||
|
||||
struct SegmapPathRect {
|
||||
int16 x1, y1, x2, y2;
|
||||
};
|
||||
|
||||
struct SegmapInfoRect {
|
||||
int16 y, x;
|
||||
int16 height, width;
|
||||
byte id;
|
||||
byte a, b, c;
|
||||
inline bool isPointInside(int16 px, int16 py) {
|
||||
return py >= y && py <= y + height && px >= x && px <= x + width;
|
||||
}
|
||||
};
|
||||
|
||||
struct PathPoint {
|
||||
int16 y, x;
|
||||
};
|
||||
|
||||
typedef Common::Array<SegmapMaskRect> SegmapMaskRectArray;
|
||||
typedef Common::Array<SegmapPathRect> SegmapPathRectArray;
|
||||
typedef Common::Array<SegmapInfoRect> SegmapInfoRectArray;
|
||||
|
||||
ToltecsEngine *_vm;
|
||||
|
||||
SegmapMaskRectArray _maskRects;
|
||||
byte *_maskRectData;
|
||||
|
||||
SegmapPathRectArray _pathRects;
|
||||
SegmapInfoRectArray _infoRects;
|
||||
|
||||
int16 _deadEndPathRects[1000];
|
||||
uint _deadEndPathRectsCount;
|
||||
|
||||
int16 _closedPathRects[1000];
|
||||
uint _closedPathRectsCount;
|
||||
|
||||
PathPoint _pathNodes[1000];
|
||||
int16 _pathNodesCount;
|
||||
|
||||
int16 findNextPathRect(int16 srcRectIndex, int16 destX, int16 destY);
|
||||
|
||||
void loadSegmapMaskRectSurface(byte *maskData, SegmapMaskRect &maskRect);
|
||||
void freeSegmapMaskRectSurfaces();
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_SEGMAP_H */
|
188
engines/toltecs/sound.cpp
Normal file
188
engines/toltecs/sound.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/resource.h"
|
||||
#include "toltecs/segmap.h"
|
||||
#include "toltecs/sound.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
Sound::Sound(ToltecsEngine *vm) : _vm(vm) {
|
||||
for (int i = 0; i < kMaxChannels; i++) {
|
||||
channels[i].type = kChannelTypeEmpty;
|
||||
channels[i].resIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Sound::~Sound() {
|
||||
}
|
||||
|
||||
void Sound::playSpeech(int16 resIndex) {
|
||||
debug(0, "playSpeech(%d)", resIndex);
|
||||
internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0);
|
||||
}
|
||||
|
||||
void Sound::playSound(int16 resIndex, int16 type, int16 volume) {
|
||||
|
||||
// TODO: Use the right volumes
|
||||
|
||||
debug(0, "playSound(%d, %d, %d)", resIndex, type, volume);
|
||||
|
||||
if (volume == -1 || type == -2) {
|
||||
if (type == kChannelTypeBackground) {
|
||||
internalPlaySound(resIndex, type, 50 /*TODO*/, 0);
|
||||
} else {
|
||||
internalPlaySound(resIndex, type, 100 /*TODO*/, 0);
|
||||
}
|
||||
} else {
|
||||
internalPlaySound(resIndex, type, 100 /*TODO*/, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) {
|
||||
|
||||
debug(0, "playSoundAtPos(%d, %d, %d)", resIndex, x, y);
|
||||
|
||||
int16 volume, panning = 0, deltaX = 0;
|
||||
int8 scaling = _vm->_segmap->getScalingAtPoint(x, y);
|
||||
|
||||
if (scaling >= 0)
|
||||
volume = 50 + ABS(scaling) / 2;
|
||||
else
|
||||
volume = 50 - ABS(scaling) / 2;
|
||||
|
||||
if (_vm->_cameraX > x)
|
||||
deltaX = _vm->_cameraX - x;
|
||||
else if (_vm->_cameraX + 640 < x)
|
||||
deltaX = x - (_vm->_cameraX + 640);
|
||||
if (deltaX > 600)
|
||||
deltaX = 600;
|
||||
|
||||
volume = ((100 - deltaX / 6) * volume) / 100;
|
||||
|
||||
if (_vm->_cameraX + 320 != x) {
|
||||
panning = CLIP(x - (_vm->_cameraX + 320), -381, 381) / 3;
|
||||
}
|
||||
|
||||
internalPlaySound(resIndex, 1, volume, panning);
|
||||
|
||||
}
|
||||
|
||||
void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning) {
|
||||
|
||||
if (resIndex == -1) {
|
||||
// Stop all sounds
|
||||
_vm->_mixer->stopAll();
|
||||
_vm->_screen->keepTalkTextItemsAlive();
|
||||
for (int i = 0; i < kMaxChannels; i++) {
|
||||
channels[i].type = kChannelTypeEmpty;
|
||||
channels[i].resIndex = -1;
|
||||
}
|
||||
} else if (type == -2) {
|
||||
// Stop sounds with specified resIndex
|
||||
for (int i = 0; i < kMaxChannels; i++) {
|
||||
if (channels[i].resIndex == resIndex) {
|
||||
_vm->_mixer->stopHandle(channels[i].handle);
|
||||
channels[i].type = kChannelTypeEmpty;
|
||||
channels[i].resIndex = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if (type == -3) {
|
||||
// Stop speech and play new sound
|
||||
stopSpeech();
|
||||
}
|
||||
|
||||
// Play new sound in empty channel
|
||||
int freeChannel = -1;
|
||||
for (int i = 0; i < kMaxChannels; i++) {
|
||||
if (channels[i].type == kChannelTypeEmpty || !_vm->_mixer->isSoundHandleActive(channels[i].handle)) {
|
||||
freeChannel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all channels are in use no new sound will be played
|
||||
if (freeChannel >= 0) {
|
||||
|
||||
Resource *soundResource = _vm->_res->load(resIndex);
|
||||
|
||||
Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
|
||||
Audio::makeRawStream(soundResource->data, soundResource->size, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO),
|
||||
type == kChannelTypeBackground ? 0 : 1);
|
||||
|
||||
channels[freeChannel].type = type;
|
||||
channels[freeChannel].resIndex = resIndex;
|
||||
|
||||
Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType;
|
||||
/*
|
||||
switch (type) {
|
||||
}
|
||||
*/
|
||||
|
||||
_vm->_mixer->playStream(soundType, &channels[freeChannel].handle,
|
||||
stream, -1, volume, panning);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sound::updateSpeech() {
|
||||
for (int i = 0; i < kMaxChannels; i++) {
|
||||
if (channels[i].type == kChannelTypeSpeech && _vm->_mixer->isSoundHandleActive(channels[i].handle)) {
|
||||
_vm->_screen->keepTalkTextItemsAlive();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::stopSpeech() {
|
||||
for (int i = 0; i < kMaxChannels; i++) {
|
||||
if (channels[i].type == kChannelTypeSpeech) {
|
||||
_vm->_mixer->stopHandle(channels[i].handle);
|
||||
_vm->_screen->keepTalkTextItemsAlive();
|
||||
channels[i].type = kChannelTypeEmpty;
|
||||
channels[i].resIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::stopAll() {
|
||||
for (int i = 0; i < kMaxChannels; i++) {
|
||||
_vm->_mixer->stopHandle(channels[i].handle);
|
||||
_vm->_screen->keepTalkTextItemsAlive();
|
||||
channels[i].type = kChannelTypeEmpty;
|
||||
channels[i].resIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
74
engines/toltecs/sound.h
Normal file
74
engines/toltecs/sound.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 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_SOUND_H
|
||||
#define TOLTECS_SOUND_H
|
||||
|
||||
#include "audio/mixer.h" // for Audio::SoundHandle
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
// 0x1219
|
||||
|
||||
enum SoundChannelType {
|
||||
kChannelTypeEmpty = 0,
|
||||
kChannelTypeBackground = -1,
|
||||
kChannelTypeSfx = -2,
|
||||
kChannelTypeSpeech = -3
|
||||
};
|
||||
|
||||
struct SoundChannel {
|
||||
int16 resIndex;
|
||||
int16 type;
|
||||
Audio::SoundHandle handle;
|
||||
};
|
||||
|
||||
const int kMaxChannels = 4;
|
||||
|
||||
class Sound {
|
||||
public:
|
||||
Sound(ToltecsEngine *vm);
|
||||
~Sound();
|
||||
|
||||
void playSpeech(int16 resIndex);
|
||||
void playSound(int16 resIndex, int16 type, int16 volume);
|
||||
void playSoundAtPos(int16 resIndex, int16 x, int16 y);
|
||||
void updateSpeech();
|
||||
void stopSpeech();
|
||||
void stopAll();
|
||||
|
||||
protected:
|
||||
ToltecsEngine *_vm;
|
||||
|
||||
SoundChannel channels[kMaxChannels];
|
||||
|
||||
void internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning);
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_SOUND_H */
|
509
engines/toltecs/sprite.cpp
Normal file
509
engines/toltecs/sprite.cpp
Normal file
@ -0,0 +1,509 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/palette.h"
|
||||
#include "toltecs/render.h"
|
||||
#include "toltecs/resource.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
class SpriteReader : public SpriteFilter {
|
||||
public:
|
||||
SpriteReader(byte *source, const SpriteDrawItem &sprite) : SpriteFilter(sprite), _source(source) {
|
||||
_curWidth = _sprite->origWidth;
|
||||
_curHeight = _sprite->origHeight;
|
||||
}
|
||||
SpriteReaderStatus readPacket(PixelPacket &packet) {
|
||||
if (_sprite->flags & 0x40) {
|
||||
// shadow sprite
|
||||
packet.count = _source[0] & 0x7F;
|
||||
if (_source[0] & 0x80)
|
||||
packet.pixel = 1;
|
||||
else
|
||||
packet.pixel = 0;
|
||||
_source++;
|
||||
} else if (_sprite->flags & 0x10) {
|
||||
// 256-color sprite
|
||||
packet.pixel = *_source++;
|
||||
packet.count = *_source++;
|
||||
} else {
|
||||
// 16-color sprite
|
||||
packet.count = _source[0] & 0x0F;
|
||||
packet.pixel = (_source[0] & 0xF0) >> 4;
|
||||
_source++;
|
||||
}
|
||||
_curWidth -= packet.count;
|
||||
if (_curWidth <= 0) {
|
||||
_curHeight--;
|
||||
if (_curHeight == 0) {
|
||||
return kSrsEndOfSprite;
|
||||
} else {
|
||||
_curWidth = _sprite->origWidth;
|
||||
return kSrsEndOfLine;
|
||||
}
|
||||
} else {
|
||||
return kSrsPixelsLeft;
|
||||
}
|
||||
}
|
||||
byte *getSource() {
|
||||
return _source;
|
||||
}
|
||||
void setSource(byte *source) {
|
||||
_source = source;
|
||||
_curHeight++;
|
||||
}
|
||||
protected:
|
||||
byte *_source;
|
||||
int16 _curWidth, _curHeight;
|
||||
};
|
||||
|
||||
class SpriteFilterScaleDown : public SpriteFilter {
|
||||
public:
|
||||
SpriteFilterScaleDown(const SpriteDrawItem &sprite, SpriteReader *reader) : SpriteFilter(sprite), _reader(reader) {
|
||||
_height = _sprite->height;
|
||||
_yerror = _sprite->yerror;
|
||||
_origHeight = _sprite->origHeight;
|
||||
_scalerStatus = 0;
|
||||
}
|
||||
SpriteReaderStatus readPacket(PixelPacket &packet) {
|
||||
SpriteReaderStatus status = kSrsPixelsLeft;
|
||||
if (_scalerStatus == 0) {
|
||||
_xerror = _sprite->xdelta;
|
||||
_yerror -= 100;
|
||||
while (_yerror <= 0) {
|
||||
do {
|
||||
status = _reader->readPacket(packet);
|
||||
} while (status == kSrsPixelsLeft);
|
||||
_yerror += _sprite->ydelta - 100;
|
||||
}
|
||||
if (status == kSrsEndOfSprite)
|
||||
return kSrsEndOfSprite;
|
||||
_scalerStatus = 1;
|
||||
}
|
||||
if (_scalerStatus == 1) {
|
||||
status = _reader->readPacket(packet);
|
||||
byte updcount = packet.count;
|
||||
while (updcount--) {
|
||||
_xerror -= 100;
|
||||
if (_xerror <= 0) {
|
||||
if (packet.count > 0)
|
||||
packet.count--;
|
||||
_xerror += _sprite->xdelta;
|
||||
}
|
||||
}
|
||||
if (status == kSrsEndOfLine) {
|
||||
if (--_height == 0)
|
||||
return kSrsEndOfSprite;
|
||||
_scalerStatus = 0;
|
||||
return kSrsEndOfLine;
|
||||
}
|
||||
}
|
||||
return kSrsPixelsLeft;
|
||||
}
|
||||
protected:
|
||||
SpriteReader *_reader;
|
||||
int16 _xerror, _yerror;
|
||||
int16 _height;
|
||||
int16 _origHeight;
|
||||
int _scalerStatus;
|
||||
};
|
||||
|
||||
class SpriteFilterScaleUp : public SpriteFilter {
|
||||
public:
|
||||
SpriteFilterScaleUp(const SpriteDrawItem &sprite, SpriteReader *reader) : SpriteFilter(sprite), _reader(reader) {
|
||||
_height = _sprite->height;
|
||||
_yerror = _sprite->yerror;
|
||||
_origHeight = _sprite->origHeight;
|
||||
_scalerStatus = 0;
|
||||
}
|
||||
SpriteReaderStatus readPacket(PixelPacket &packet) {
|
||||
SpriteReaderStatus status;
|
||||
if (_scalerStatus == 0) {
|
||||
_xerror = _sprite->xdelta;
|
||||
_sourcep = _reader->getSource();
|
||||
_scalerStatus = 1;
|
||||
}
|
||||
if (_scalerStatus == 1) {
|
||||
status = _reader->readPacket(packet);
|
||||
byte updcount = packet.count;
|
||||
while (updcount--) {
|
||||
_xerror -= 100;
|
||||
if (_xerror <= 0) {
|
||||
packet.count++;
|
||||
_xerror += _sprite->xdelta;
|
||||
}
|
||||
}
|
||||
if (status == kSrsEndOfLine) {
|
||||
if (--_height == 0)
|
||||
return kSrsEndOfSprite;
|
||||
_yerror -= 100;
|
||||
if (_yerror <= 0) {
|
||||
_reader->setSource(_sourcep);
|
||||
_yerror += _sprite->ydelta + 100;
|
||||
}
|
||||
_scalerStatus = 0;
|
||||
return kSrsEndOfLine;
|
||||
}
|
||||
}
|
||||
return kSrsPixelsLeft;
|
||||
}
|
||||
protected:
|
||||
SpriteReader *_reader;
|
||||
byte *_sourcep;
|
||||
int16 _xerror, _yerror;
|
||||
int16 _height;
|
||||
int16 _origHeight;
|
||||
int _scalerStatus;
|
||||
};
|
||||
|
||||
bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem &sprite) {
|
||||
int16 scaleValueX, scaleValueY;
|
||||
int16 xoffs, yoffs;
|
||||
byte *spriteData;
|
||||
int16 frameNum;
|
||||
|
||||
memset(&sprite, 0, sizeof(SpriteDrawItem));
|
||||
|
||||
if (drawRequest.flags == 0xFFFF)
|
||||
return false;
|
||||
|
||||
frameNum = drawRequest.flags & 0x0FFF;
|
||||
|
||||
sprite.flags = 0;
|
||||
sprite.baseColor = drawRequest.baseColor;
|
||||
sprite.x = drawRequest.x;
|
||||
sprite.y = drawRequest.y;
|
||||
sprite.priority = drawRequest.y;
|
||||
sprite.resIndex = drawRequest.resIndex;
|
||||
sprite.frameNum = frameNum;
|
||||
|
||||
spriteData = _vm->_res->load(drawRequest.resIndex)->data;
|
||||
|
||||
if (drawRequest.flags & 0x1000) {
|
||||
sprite.flags |= 4;
|
||||
}
|
||||
|
||||
if (drawRequest.flags & 0x2000) {
|
||||
sprite.flags |= 0x10;
|
||||
}
|
||||
|
||||
if (drawRequest.flags & 0x4000) {
|
||||
sprite.flags |= 0x40;
|
||||
}
|
||||
|
||||
// First initialize the sprite item with the values from the sprite resource
|
||||
|
||||
SpriteFrameEntry spriteFrameEntry(spriteData + frameNum * 12);
|
||||
|
||||
if (spriteFrameEntry.w == 0 || spriteFrameEntry.h == 0)
|
||||
return false;
|
||||
|
||||
sprite.offset = spriteFrameEntry.offset;
|
||||
|
||||
sprite.width = spriteFrameEntry.w;
|
||||
sprite.height = spriteFrameEntry.h;
|
||||
sprite.origWidth = spriteFrameEntry.w;
|
||||
sprite.origHeight = spriteFrameEntry.h;
|
||||
|
||||
if (drawRequest.flags & 0x1000) {
|
||||
xoffs = spriteFrameEntry.w - spriteFrameEntry.x;
|
||||
} else {
|
||||
xoffs = spriteFrameEntry.x;
|
||||
}
|
||||
|
||||
yoffs = spriteFrameEntry.y;
|
||||
|
||||
// If the sprite should be scaled we need to initialize some values now
|
||||
|
||||
if (drawRequest.scaling != 0) {
|
||||
|
||||
byte scaleValue = ABS(drawRequest.scaling);
|
||||
|
||||
scaleValueX = scaleValue * sprite.origWidth;
|
||||
sprite.xdelta = (10000 * sprite.origWidth) / scaleValueX;
|
||||
scaleValueX /= 100;
|
||||
|
||||
scaleValueY = scaleValue * sprite.origHeight;
|
||||
sprite.ydelta = (10000 * sprite.origHeight) / scaleValueY;
|
||||
scaleValueY /= 100;
|
||||
|
||||
if (drawRequest.scaling > 0) {
|
||||
sprite.flags |= 2;
|
||||
sprite.width = sprite.origWidth + scaleValueX;
|
||||
sprite.height = sprite.origHeight + scaleValueY;
|
||||
xoffs += (xoffs * scaleValue) / 100;
|
||||
yoffs += (yoffs * scaleValue) / 100;
|
||||
} else {
|
||||
sprite.flags |= 1;
|
||||
sprite.width = sprite.origWidth - scaleValueX;
|
||||
sprite.height = sprite.origHeight - 1 - scaleValueY;
|
||||
if (sprite.width <= 0 || sprite.height <= 0)
|
||||
return false;
|
||||
xoffs -= (xoffs * scaleValue) / 100;
|
||||
yoffs -= (yoffs * scaleValue) / 100;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sprite.x -= xoffs;
|
||||
sprite.y -= yoffs;
|
||||
|
||||
sprite.yerror = sprite.ydelta;
|
||||
|
||||
// Now we check if the sprite needs to be clipped
|
||||
|
||||
// Clip Y
|
||||
if (sprite.y - _vm->_cameraY < 0) {
|
||||
|
||||
int16 clipHeight = ABS(sprite.y - _vm->_cameraY);
|
||||
int16 skipHeight = clipHeight;
|
||||
byte *spriteFrameData;
|
||||
|
||||
sprite.height -= clipHeight;
|
||||
if (sprite.height <= 0)
|
||||
return false;
|
||||
|
||||
sprite.y = _vm->_cameraY;
|
||||
|
||||
// If the sprite is scaled
|
||||
if (sprite.flags & 3) {
|
||||
int16 chopHeight = sprite.ydelta;
|
||||
if ((sprite.flags & 2) == 0) {
|
||||
do {
|
||||
chopHeight -= 100;
|
||||
if (chopHeight <= 0) {
|
||||
skipHeight++;
|
||||
chopHeight += sprite.ydelta;
|
||||
} else {
|
||||
clipHeight--;
|
||||
}
|
||||
} while (clipHeight > 0);
|
||||
} else {
|
||||
do {
|
||||
chopHeight -= 100;
|
||||
if (chopHeight < 0) {
|
||||
skipHeight--;
|
||||
chopHeight += sprite.ydelta + 100;
|
||||
}
|
||||
clipHeight--;
|
||||
} while (clipHeight > 0);
|
||||
}
|
||||
sprite.yerror = chopHeight;
|
||||
}
|
||||
|
||||
spriteFrameData = spriteData + sprite.offset;
|
||||
// Now the sprite's offset is adjusted to point to the starting line
|
||||
if ((sprite.flags & 0x10) == 0) {
|
||||
while (skipHeight--) {
|
||||
int16 lineWidth = 0;
|
||||
while (lineWidth < sprite.origWidth) {
|
||||
sprite.offset++;
|
||||
lineWidth += spriteFrameData[0] & 0x0F;
|
||||
spriteFrameData++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (skipHeight--) {
|
||||
int16 lineWidth = 0;
|
||||
while (lineWidth < sprite.origWidth) {
|
||||
sprite.offset += 2;
|
||||
lineWidth += spriteFrameData[1];
|
||||
spriteFrameData += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (sprite.y + sprite.height - _vm->_cameraY - _vm->_cameraHeight > 0)
|
||||
sprite.height -= sprite.y + sprite.height - _vm->_cameraY - _vm->_cameraHeight;
|
||||
if (sprite.height <= 0)
|
||||
return false;
|
||||
|
||||
sprite.skipX = 0;
|
||||
|
||||
if (drawRequest.flags & 0x1000) {
|
||||
// Left border
|
||||
if (sprite.x - _vm->_cameraX < 0) {
|
||||
sprite.width -= ABS(sprite.x - _vm->_cameraX);
|
||||
sprite.x = _vm->_cameraX;
|
||||
}
|
||||
// Right border
|
||||
if (sprite.x + sprite.width - _vm->_cameraX - 640 > 0) {
|
||||
sprite.flags |= 8;
|
||||
sprite.skipX = sprite.x + sprite.width - _vm->_cameraX - 640;
|
||||
sprite.width -= sprite.skipX;
|
||||
}
|
||||
} else {
|
||||
// Left border
|
||||
if (sprite.x - _vm->_cameraX < 0) {
|
||||
sprite.flags |= 8;
|
||||
sprite.skipX = ABS(sprite.x - _vm->_cameraX);
|
||||
sprite.width -= sprite.skipX;
|
||||
sprite.x = _vm->_cameraX;
|
||||
}
|
||||
// Right border
|
||||
if (sprite.x + sprite.width - _vm->_cameraX - 640 > 0) {
|
||||
sprite.flags |= 8;
|
||||
sprite.width -= sprite.x + sprite.width - _vm->_cameraX - 640;
|
||||
}
|
||||
}
|
||||
|
||||
if (sprite.width <= 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Screen::addDrawRequest(const DrawRequest &drawRequest) {
|
||||
SpriteDrawItem sprite;
|
||||
if (createSpriteDrawItem(drawRequest, sprite))
|
||||
_renderQueue->addSprite(sprite);
|
||||
}
|
||||
|
||||
void Screen::drawSprite(const SpriteDrawItem &sprite) {
|
||||
|
||||
debug(0, "Screen::drawSprite() x = %d; y = %d; flags = %04X; resIndex = %d; offset = %08X; drawX = %d; drawY = %d",
|
||||
sprite.x, sprite.y, sprite.flags, sprite.resIndex, sprite.offset,
|
||||
sprite.x - _vm->_cameraX, sprite.y - _vm->_cameraY);
|
||||
debug(0, "Screen::drawSprite() width = %d; height = %d; origWidth = %d; origHeight = %d",
|
||||
sprite.width, sprite.height, sprite.origWidth, sprite.origHeight);
|
||||
|
||||
byte *source = _vm->_res->load(sprite.resIndex)->data + sprite.offset;
|
||||
byte *dest = _frontScreen + sprite.x + sprite.y * 640;
|
||||
|
||||
SpriteReader spriteReader(source, sprite);
|
||||
|
||||
if (sprite.flags & 0x40) {
|
||||
// Shadow sprites
|
||||
if (sprite.flags & 1) {
|
||||
SpriteFilterScaleDown spriteScaler(sprite, &spriteReader);
|
||||
drawSpriteCore(dest, spriteScaler, sprite);
|
||||
} else if (sprite.flags & 2) {
|
||||
SpriteFilterScaleUp spriteScaler(sprite, &spriteReader);
|
||||
drawSpriteCore(dest, spriteScaler, sprite);
|
||||
} else {
|
||||
drawSpriteCore(dest, spriteReader, sprite);
|
||||
}
|
||||
} else if (sprite.flags & 0x10) {
|
||||
// 256 color sprite
|
||||
drawSpriteCore(dest, spriteReader, sprite);
|
||||
} else {
|
||||
// 16 color sprite
|
||||
if (sprite.flags & 1) {
|
||||
SpriteFilterScaleDown spriteScaler(sprite, &spriteReader);
|
||||
drawSpriteCore(dest, spriteScaler, sprite);
|
||||
} else if (sprite.flags & 2) {
|
||||
SpriteFilterScaleUp spriteScaler(sprite, &spriteReader);
|
||||
drawSpriteCore(dest, spriteScaler, sprite);
|
||||
} else {
|
||||
drawSpriteCore(dest, spriteReader, sprite);
|
||||
}
|
||||
}
|
||||
|
||||
debug(0, "Screen::drawSprite() ok");
|
||||
|
||||
}
|
||||
|
||||
void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawItem &sprite) {
|
||||
|
||||
int16 destInc;
|
||||
|
||||
if (sprite.flags & 4) {
|
||||
destInc = -1;
|
||||
dest += sprite.width;
|
||||
} else {
|
||||
destInc = 1;
|
||||
}
|
||||
|
||||
SpriteReaderStatus status;
|
||||
PixelPacket packet;
|
||||
|
||||
byte *destp = dest;
|
||||
int16 skipX = sprite.skipX;
|
||||
|
||||
int16 w = sprite.width;
|
||||
int16 h = sprite.height;
|
||||
|
||||
do {
|
||||
status = reader.readPacket(packet);
|
||||
|
||||
if (skipX > 0) {
|
||||
while (skipX > 0) {
|
||||
skipX -= packet.count;
|
||||
if (skipX < 0) {
|
||||
packet.count = ABS(skipX);
|
||||
break;
|
||||
}
|
||||
status = reader.readPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
if (w - packet.count < 0)
|
||||
packet.count = w;
|
||||
|
||||
w -= packet.count;
|
||||
|
||||
if (((sprite.flags & 0x40) && (packet.pixel != 0)) ||
|
||||
((sprite.flags & 0x10) && (packet.pixel != 0xFF)) ||
|
||||
(!(sprite.flags & 0x10) && (packet.pixel != 0)))
|
||||
{
|
||||
if (sprite.flags & 0x40) {
|
||||
while (packet.count--) {
|
||||
*dest = _vm->_palette->getColorTransPixel(*dest);
|
||||
dest += destInc;
|
||||
}
|
||||
} else {
|
||||
if (sprite.flags & 0x10) {
|
||||
packet.pixel = ((packet.pixel << 4) & 0xF0) | ((packet.pixel >> 4) & 0x0F);
|
||||
} else {
|
||||
packet.pixel += sprite.baseColor - 1;
|
||||
}
|
||||
while (packet.count--) {
|
||||
*dest = packet.pixel;
|
||||
dest += destInc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dest += packet.count * destInc;
|
||||
}
|
||||
|
||||
if (status == kSrsEndOfLine || w <= 0) {
|
||||
if (w <= 0) {
|
||||
while (status == kSrsPixelsLeft) {
|
||||
status = reader.readPacket(packet);
|
||||
}
|
||||
}
|
||||
dest = destp + 640;
|
||||
destp = dest;
|
||||
skipX = sprite.skipX;
|
||||
w = sprite.width;
|
||||
h--;
|
||||
}
|
||||
|
||||
} while (status != kSrsEndOfSprite && h > 0);
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
654
engines/toltecs/toltecs.cpp
Normal file
654
engines/toltecs/toltecs.cpp
Normal file
@ -0,0 +1,654 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/events.h"
|
||||
#include "common/random.h"
|
||||
#include "common/str.h"
|
||||
#include "common/error.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "base/plugins.h"
|
||||
#include "base/version.h"
|
||||
|
||||
#include "graphics/cursorman.h"
|
||||
|
||||
#include "engines/util.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "toltecs/toltecs.h"
|
||||
#include "toltecs/animation.h"
|
||||
#include "toltecs/menu.h"
|
||||
#include "toltecs/movie.h"
|
||||
#include "toltecs/music.h"
|
||||
#include "toltecs/palette.h"
|
||||
#include "toltecs/render.h"
|
||||
#include "toltecs/resource.h"
|
||||
#include "toltecs/script.h"
|
||||
#include "toltecs/screen.h"
|
||||
#include "toltecs/segmap.h"
|
||||
#include "toltecs/sound.h"
|
||||
#include "toltecs/microtiles.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
struct GameSettings {
|
||||
const char *gameid;
|
||||
const char *description;
|
||||
byte id;
|
||||
uint32 features;
|
||||
const char *detectname;
|
||||
};
|
||||
|
||||
ToltecsEngine::ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
|
||||
|
||||
// Setup mixer
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
|
||||
|
||||
_rnd = new Common::RandomSource("toltecs");
|
||||
}
|
||||
|
||||
ToltecsEngine::~ToltecsEngine() {
|
||||
delete _rnd;
|
||||
}
|
||||
|
||||
void ToltecsEngine::syncSoundSettings() {
|
||||
/*
|
||||
_music->setVolume(ConfMan.getInt("music_volume"));
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("sfx_volume"));
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
|
||||
*/
|
||||
}
|
||||
|
||||
Common::Error ToltecsEngine::run() {
|
||||
initGraphics(640, 400, true);
|
||||
|
||||
_isSaveAllowed = true;
|
||||
|
||||
_counter01 = 0;
|
||||
_counter02 = 0;
|
||||
_movieSceneFlag = false;
|
||||
_flag01 = 0;
|
||||
|
||||
_saveLoadRequested = 0;
|
||||
|
||||
_cameraX = 0;
|
||||
_cameraY = 0;
|
||||
_newCameraX = 0;
|
||||
_newCameraY = 0;
|
||||
_cameraHeight = 0;
|
||||
|
||||
_guiHeight = 26;
|
||||
|
||||
_sceneWidth = 0;
|
||||
_sceneHeight = 0;
|
||||
|
||||
_doSpeech = true;
|
||||
_doText = true;
|
||||
|
||||
_walkSpeedY = 5;
|
||||
_walkSpeedX = 1;
|
||||
|
||||
_mouseX = 0;
|
||||
_mouseY = 0;
|
||||
_mouseDblClickTicks = 60;
|
||||
_mouseWaitForRelease = false;
|
||||
_mouseButton = 0;
|
||||
_mouseDisabled = 0;
|
||||
_leftButtonDown = false;
|
||||
_rightButtonDown = false;
|
||||
|
||||
_arc = new ArchiveReader();
|
||||
_arc->openArchive("WESTERN");
|
||||
|
||||
_res = new ResourceCache(this);
|
||||
|
||||
_screen = new Screen(this);
|
||||
|
||||
_script = new ScriptInterpreter(this);
|
||||
_anim = new AnimationPlayer(this);
|
||||
_palette = new Palette(this);
|
||||
_segmap = new SegmentMap(this);
|
||||
_moviePlayer = new MoviePlayer(this);
|
||||
_musicPlayer = new MusicPlayer();
|
||||
_menuSystem = new MenuSystem(this);
|
||||
|
||||
_sound = new Sound(this);
|
||||
|
||||
syncSoundSettings();
|
||||
|
||||
CursorMan.showMouse(true);
|
||||
|
||||
setupSysStrings();
|
||||
|
||||
//#define TEST_MENU
|
||||
#ifdef TEST_MENU
|
||||
_screen->registerFont(0, 0x0D);
|
||||
_screen->registerFont(1, 0x0E);
|
||||
_screen->loadMouseCursor(12);
|
||||
_palette->loadAddPalette(9, 224);
|
||||
_palette->setDeltaPalette(_palette->getMainPalette(), 7, 0, 31, 224);
|
||||
_screen->finishTalkTextItems();
|
||||
_screen->clearSprites();
|
||||
_menuSystem->run();
|
||||
/*
|
||||
while (1) {
|
||||
//updateInput();
|
||||
_menuSystem->update();
|
||||
updateScreen();
|
||||
}
|
||||
*/
|
||||
return Common::kNoError;
|
||||
#endif
|
||||
|
||||
// Start main game loop
|
||||
_script->loadScript(0, 0);
|
||||
_script->setMainScript(0);
|
||||
if (ConfMan.hasKey("save_slot")) {
|
||||
int saveSlot = ConfMan.getInt("save_slot");
|
||||
if (saveSlot >= 0 && saveSlot <= 99) {
|
||||
_screen->loadMouseCursor(12);
|
||||
loadGameState(saveSlot);
|
||||
}
|
||||
}
|
||||
_script->runScript();
|
||||
|
||||
_musicPlayer->stopAndClear();
|
||||
_sound->stopAll();
|
||||
|
||||
delete _arc;
|
||||
delete _res;
|
||||
delete _screen;
|
||||
delete _script;
|
||||
delete _anim;
|
||||
delete _palette;
|
||||
delete _segmap;
|
||||
delete _musicPlayer;
|
||||
delete _moviePlayer;
|
||||
delete _menuSystem;
|
||||
|
||||
delete _sound;
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
void ToltecsEngine::setupSysStrings() {
|
||||
Resource *sysStringsResource = _res->load(15);
|
||||
const char *sysStrings = (const char*)sysStringsResource->data;
|
||||
for (int i = 0; i < kSysStrCount; i++) {
|
||||
debug(1, "sysStrings[%d] = [%s]", i, sysStrings);
|
||||
_sysStrings[i] = sysStrings;
|
||||
sysStrings += strlen(sysStrings) + 1;
|
||||
}
|
||||
// TODO: Set yes/no chars
|
||||
}
|
||||
|
||||
void ToltecsEngine::requestSavegame(int slotNum, Common::String &description) {
|
||||
_saveLoadRequested = 2;
|
||||
_saveLoadSlot = slotNum;
|
||||
_saveLoadDescription = description;
|
||||
}
|
||||
|
||||
void ToltecsEngine::requestLoadgame(int slotNum) {
|
||||
_saveLoadRequested = 1;
|
||||
_saveLoadSlot = slotNum;
|
||||
}
|
||||
|
||||
void ToltecsEngine::loadScene(uint resIndex) {
|
||||
|
||||
Resource *sceneResource = _res->load(resIndex);
|
||||
byte *scene = sceneResource->data;
|
||||
|
||||
uint32 imageSize = READ_LE_UINT32(scene);
|
||||
_sceneResIndex = resIndex;
|
||||
_sceneHeight = READ_LE_UINT16(scene + 4);
|
||||
_sceneWidth = READ_LE_UINT16(scene + 6);
|
||||
|
||||
// Load scene palette
|
||||
_palette->loadAddPaletteFrom(scene + 8, 0, 128);
|
||||
|
||||
// Load scene background
|
||||
byte *source = scene + 392;
|
||||
byte *destp = _screen->_backScreen;
|
||||
byte *destEnd = destp + _sceneWidth * _sceneHeight;
|
||||
while (destp < destEnd) {
|
||||
int count = 1;
|
||||
byte pixel = *source++;
|
||||
if (pixel & 0x80) {
|
||||
pixel &= 0x7F;
|
||||
count = *source++;
|
||||
count += 2;
|
||||
}
|
||||
memset(destp, pixel, count);
|
||||
destp += count;
|
||||
}
|
||||
|
||||
debug(0, "_sceneWidth = %d; _sceneHeight = %d", _sceneWidth, _sceneHeight);
|
||||
|
||||
// Load scene segmap
|
||||
_segmap->load(scene + imageSize + 4);
|
||||
|
||||
_screen->_fullRefresh = true;
|
||||
_screen->_renderQueue->clear();
|
||||
|
||||
}
|
||||
|
||||
void ToltecsEngine::updateScreen() {
|
||||
|
||||
_sound->updateSpeech();
|
||||
|
||||
_screen->updateShakeScreen();
|
||||
|
||||
// TODO: Set quit flag
|
||||
if (shouldQuit())
|
||||
return;
|
||||
|
||||
if (!_movieSceneFlag)
|
||||
updateInput();
|
||||
else
|
||||
_mouseButton = 0;
|
||||
|
||||
// TODO? Check keyb
|
||||
|
||||
_counter01--;
|
||||
if (_counter01 <= 0) {
|
||||
_counter01 = MIN(_counter02, 30);
|
||||
_counter02 = 0;
|
||||
drawScreen();
|
||||
_flag01 = 1;
|
||||
_counter02 = 1;
|
||||
} else {
|
||||
_screen->clearSprites();
|
||||
_flag01 = 0;
|
||||
}
|
||||
|
||||
static uint32 prevUpdateTime = 0;
|
||||
uint32 currUpdateTime;
|
||||
do {
|
||||
currUpdateTime = _system->getMillis();
|
||||
_counter02 = (currUpdateTime - prevUpdateTime) / 13;
|
||||
} while (_counter02 == 0);
|
||||
prevUpdateTime = currUpdateTime;
|
||||
|
||||
}
|
||||
|
||||
void ToltecsEngine::drawScreen() {
|
||||
|
||||
// FIXME: Quick hack, sometimes cameraY was negative (the code in updateCamera was at fault)
|
||||
if (_cameraY < 0) _cameraY = 0;
|
||||
|
||||
_segmap->addMasksToRenderQueue();
|
||||
_screen->addTalkTextItemsToRenderQueue();
|
||||
|
||||
_screen->_renderQueue->update();
|
||||
|
||||
//debug("_guiHeight = %d\n", _guiHeight);
|
||||
|
||||
if (_screen->_guiRefresh && _guiHeight > 0 && _cameraHeight > 0) {
|
||||
// Update the GUI when needed and it's visible
|
||||
_system->copyRectToScreen((const byte *)_screen->_frontScreen + _cameraHeight * 640,
|
||||
640, 0, _cameraHeight, 640, _guiHeight);
|
||||
_screen->_guiRefresh = false;
|
||||
}
|
||||
|
||||
_system->updateScreen();
|
||||
|
||||
updateCamera();
|
||||
|
||||
}
|
||||
|
||||
void ToltecsEngine::updateInput() {
|
||||
|
||||
Common::Event event;
|
||||
Common::EventManager *eventMan = _system->getEventManager();
|
||||
while (eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
_keyState = event.kbd;
|
||||
|
||||
//debug("key: flags = %02X; keycode = %d", _keyState.flags, _keyState.keycode);
|
||||
|
||||
// FIXME: This is just for debugging
|
||||
switch (event.kbd.keycode) {
|
||||
case Common::KEYCODE_F7:
|
||||
savegame("toltecs.001", "Quicksave");
|
||||
break;
|
||||
case Common::KEYCODE_F9:
|
||||
loadgame("toltecs.001");
|
||||
break;
|
||||
case Common::KEYCODE_ESCAPE:
|
||||
// Skip current dialog line, if a dialog is active
|
||||
if (_screen->getTalkTextDuration() > 0) {
|
||||
_sound->stopSpeech();
|
||||
_screen->finishTalkTextItems();
|
||||
_keyState.reset(); // event consumed
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case Common::EVENT_KEYUP:
|
||||
_keyState.reset();
|
||||
break;
|
||||
case Common::EVENT_QUIT:
|
||||
quitGame();
|
||||
break;
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
_mouseX = event.mouse.x;
|
||||
_mouseY = event.mouse.y;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
_mouseX = event.mouse.x;
|
||||
_mouseY = event.mouse.y;
|
||||
_leftButtonDown = true;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
_mouseX = event.mouse.x;
|
||||
_mouseY = event.mouse.y;
|
||||
_leftButtonDown = false;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
_mouseX = event.mouse.x;
|
||||
_mouseY = event.mouse.y;
|
||||
_rightButtonDown = true;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
_mouseX = event.mouse.x;
|
||||
_mouseY = event.mouse.y;
|
||||
_rightButtonDown = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_mouseDisabled) {
|
||||
|
||||
if (_mouseDblClickTicks > 0)
|
||||
_mouseDblClickTicks--;
|
||||
|
||||
byte mouseButtons = 0;
|
||||
if (_leftButtonDown)
|
||||
mouseButtons |= 1;
|
||||
if (_rightButtonDown)
|
||||
mouseButtons |= 2;
|
||||
|
||||
if (mouseButtons != 0) {
|
||||
if (!_mouseWaitForRelease) {
|
||||
_mouseButton = mouseButtons;
|
||||
if (_mouseDblClickTicks > 0)
|
||||
_mouseButton = 0x80;
|
||||
//if (_mouseButton == 0x80) debug("DBL!");
|
||||
_mouseDblClickTicks = 30; // maybe TODO
|
||||
_mouseWaitForRelease = true;
|
||||
} else {
|
||||
_mouseButton = 0;
|
||||
}
|
||||
} else {
|
||||
_mouseWaitForRelease = false;
|
||||
_mouseButton = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ToltecsEngine::setGuiHeight(int16 guiHeight) {
|
||||
if (guiHeight != _guiHeight) {
|
||||
_guiHeight = guiHeight;
|
||||
_cameraHeight = 400 - _guiHeight;
|
||||
_screen->_guiRefresh = true;
|
||||
debug(0, "ToltecsEngine::setGuiHeight() _guiHeight = %d; _cameraHeight = %d", _guiHeight, _cameraHeight);
|
||||
// TODO: clearScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void ToltecsEngine::setCamera(int16 x, int16 y) {
|
||||
|
||||
_screen->finishTalkTextItems();
|
||||
|
||||
_screen->clearSprites();
|
||||
|
||||
_cameraX = x;
|
||||
_newCameraX = x;
|
||||
|
||||
_cameraY = y;
|
||||
_newCameraY = y;
|
||||
|
||||
}
|
||||
|
||||
bool ToltecsEngine::getCameraChanged() {
|
||||
return _cameraX != _newCameraX || _cameraY != _newCameraY;
|
||||
}
|
||||
|
||||
void ToltecsEngine::scrollCameraUp(int16 delta) {
|
||||
if (_newCameraY > 0) {
|
||||
if (_newCameraY < delta)
|
||||
_newCameraY = 0;
|
||||
else
|
||||
_newCameraY -= delta;
|
||||
}
|
||||
}
|
||||
|
||||
void ToltecsEngine::scrollCameraDown(int16 delta) {
|
||||
debug(0, "ToltecsEngine::scrollCameraDown(%d)", delta);
|
||||
if (_newCameraY != _sceneHeight - _cameraHeight) {
|
||||
if (_sceneHeight - _cameraHeight < _newCameraY + delta)
|
||||
delta += (_sceneHeight - _cameraHeight) - (delta + _newCameraY);
|
||||
_newCameraY += delta;
|
||||
debug(0, "ToltecsEngine::scrollCameraDown() _newCameraY = %d; delta = %d", _newCameraY, delta);
|
||||
}
|
||||
}
|
||||
|
||||
void ToltecsEngine::scrollCameraLeft(int16 delta) {
|
||||
if (_newCameraX > 0) {
|
||||
if (_newCameraX < delta)
|
||||
_newCameraX = 0;
|
||||
else
|
||||
_newCameraX -= delta;
|
||||
}
|
||||
}
|
||||
|
||||
void ToltecsEngine::scrollCameraRight(int16 delta) {
|
||||
debug(0, "ToltecsEngine::scrollCameraRight(%d)", delta);
|
||||
if (_newCameraX != _sceneWidth - 640) {
|
||||
if (_sceneWidth - 640 < delta + _newCameraX)
|
||||
delta += (_sceneWidth - 640) - (delta + _newCameraX);
|
||||
_newCameraX += delta;
|
||||
debug(0, "ToltecsEngine::scrollCameraRight() _newCameraX = %d; delta = %d", _newCameraY, delta);
|
||||
}
|
||||
}
|
||||
|
||||
void ToltecsEngine::updateCamera() {
|
||||
|
||||
if (_cameraX != _newCameraX) {
|
||||
_cameraX = _newCameraX;
|
||||
_screen->_fullRefresh = true;
|
||||
_screen->finishTalkTextItems();
|
||||
}
|
||||
|
||||
if (_cameraY != _newCameraY) {
|
||||
_cameraY = _newCameraY;
|
||||
_screen->_fullRefresh = true;
|
||||
_screen->finishTalkTextItems();
|
||||
}
|
||||
|
||||
//debug(0, "ToltecsEngine::updateCamera() _cameraX = %d; _cameraY = %d", _cameraX, _cameraY);
|
||||
|
||||
}
|
||||
|
||||
void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) {
|
||||
|
||||
byte *scanData = _script->getSlotData(slotIndex) + slotOffset;
|
||||
|
||||
while (*scanData < 0xF0) {
|
||||
if (*scanData == 0x19) {
|
||||
scanData++;
|
||||
} else if (*scanData == 0x14) {
|
||||
scanData++;
|
||||
} else if (*scanData == 0x0A) {
|
||||
scanData += 4;
|
||||
} else if (*scanData < 0x0A) {
|
||||
scanData++;
|
||||
}
|
||||
scanData++;
|
||||
}
|
||||
|
||||
if (*scanData == 0xFE) {
|
||||
if (_doSpeech) {
|
||||
int16 resIndex = READ_LE_UINT16(scanData + 1);
|
||||
debug(0, "ToltecsEngine::talk() playSound(resIndex: %d)", resIndex);
|
||||
_sound->playSpeech(resIndex);
|
||||
}
|
||||
if (_doText) {
|
||||
_screen->updateTalkText(slotIndex, slotOffset);
|
||||
} else {
|
||||
_screen->keepTalkTextItemsAlive();
|
||||
}
|
||||
} else {
|
||||
_screen->updateTalkText(slotIndex, slotOffset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ToltecsEngine::walk(byte *walkData) {
|
||||
|
||||
int16 xdelta, ydelta, v8, v10, v11;
|
||||
int16 xstep, ystep;
|
||||
ScriptWalk walkInfo;
|
||||
|
||||
walkInfo.y = READ_LE_UINT16(walkData + 0);
|
||||
walkInfo.x = READ_LE_UINT16(walkData + 2);
|
||||
walkInfo.y1 = READ_LE_UINT16(walkData + 4);
|
||||
walkInfo.x1 = READ_LE_UINT16(walkData + 6);
|
||||
walkInfo.y2 = READ_LE_UINT16(walkData + 8);
|
||||
walkInfo.x2 = READ_LE_UINT16(walkData + 10);
|
||||
walkInfo.yerror = READ_LE_UINT16(walkData + 12);
|
||||
walkInfo.xerror = READ_LE_UINT16(walkData + 14);
|
||||
walkInfo.mulValue = READ_LE_UINT16(walkData + 16);
|
||||
walkInfo.scaling = READ_LE_UINT16(walkData + 18);
|
||||
|
||||
walkInfo.scaling = -_segmap->getScalingAtPoint(walkInfo.x, walkInfo.y);
|
||||
|
||||
if (walkInfo.y1 < walkInfo.y2)
|
||||
ystep = -1;
|
||||
else
|
||||
ystep = 1;
|
||||
ydelta = ABS(walkInfo.y1 - walkInfo.y2) * _walkSpeedY;
|
||||
|
||||
if (walkInfo.x1 < walkInfo.x2)
|
||||
xstep = -1;
|
||||
else
|
||||
xstep = 1;
|
||||
xdelta = ABS(walkInfo.x1 - walkInfo.x2) * _walkSpeedX;
|
||||
|
||||
debug(0, "ToltecsEngine::walk() xdelta = %d; ydelta = %d", xdelta, ydelta);
|
||||
|
||||
if (xdelta > ydelta)
|
||||
SWAP(xdelta, ydelta);
|
||||
|
||||
v8 = 100 * xdelta;
|
||||
if (v8 != 0) {
|
||||
if (walkInfo.scaling > 0)
|
||||
v8 -= v8 * ABS(walkInfo.scaling) / 100;
|
||||
else
|
||||
v8 += v8 * ABS(walkInfo.scaling) / 100;
|
||||
if (ydelta != 0)
|
||||
v8 /= ydelta;
|
||||
}
|
||||
|
||||
if (ydelta > ABS(walkInfo.x1 - walkInfo.x2) * _walkSpeedX) {
|
||||
v10 = 100 - walkInfo.scaling;
|
||||
v11 = v8;
|
||||
} else {
|
||||
v10 = v8;
|
||||
v11 = 100 - walkInfo.scaling;
|
||||
}
|
||||
|
||||
walkInfo.yerror += walkInfo.mulValue * v10;
|
||||
while (walkInfo.yerror >= 100 * _walkSpeedY) {
|
||||
walkInfo.yerror -= 100 * _walkSpeedY;
|
||||
if (walkInfo.y == walkInfo.y1) {
|
||||
walkInfo.x = walkInfo.x1;
|
||||
break;
|
||||
}
|
||||
walkInfo.y += ystep;
|
||||
}
|
||||
|
||||
walkInfo.xerror += walkInfo.mulValue * v11;
|
||||
while (walkInfo.xerror >= 100 * _walkSpeedX) {
|
||||
walkInfo.xerror -= 100 * _walkSpeedX;
|
||||
if (walkInfo.x == walkInfo.x1) {
|
||||
walkInfo.y = walkInfo.y1;
|
||||
break;
|
||||
}
|
||||
walkInfo.x += xstep;
|
||||
}
|
||||
|
||||
WRITE_LE_UINT16(walkData + 0, walkInfo.y);
|
||||
WRITE_LE_UINT16(walkData + 2, walkInfo.x);
|
||||
WRITE_LE_UINT16(walkData + 4, walkInfo.y1);
|
||||
WRITE_LE_UINT16(walkData + 6, walkInfo.x1);
|
||||
WRITE_LE_UINT16(walkData + 8, walkInfo.y2);
|
||||
WRITE_LE_UINT16(walkData + 10, walkInfo.x2);
|
||||
WRITE_LE_UINT16(walkData + 12, walkInfo.yerror);
|
||||
WRITE_LE_UINT16(walkData + 14, walkInfo.xerror);
|
||||
WRITE_LE_UINT16(walkData + 16, walkInfo.mulValue);
|
||||
WRITE_LE_UINT16(walkData + 18, walkInfo.scaling);
|
||||
|
||||
}
|
||||
|
||||
int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
|
||||
byte *rectDataEnd) {
|
||||
|
||||
rectData += index * itemSize;
|
||||
|
||||
while (rectData < rectDataEnd) {
|
||||
int16 rectY = READ_LE_UINT16(rectData);
|
||||
if (rectY == -10)
|
||||
break;
|
||||
int16 rectX = READ_LE_UINT16(rectData + 2);
|
||||
int16 rectH = READ_LE_UINT16(rectData + 4);
|
||||
int16 rectW = READ_LE_UINT16(rectData + 6);
|
||||
|
||||
debug(0, "x = %d; y = %d; x1 = %d; y2 = %d; w = %d; h = %d",
|
||||
x, y, rectX, rectY, rectW, rectH);
|
||||
|
||||
if (x >= rectX && x <= rectX + rectW && y >= rectY && y <= rectY + rectH) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
rectData += itemSize;
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace Toltecs
|
213
engines/toltecs/toltecs.h
Normal file
213
engines/toltecs/toltecs.h
Normal file
@ -0,0 +1,213 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TOLTECS_H
|
||||
#define TOLTECS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/events.h"
|
||||
#include "common/file.h"
|
||||
#include "common/keyboard.h"
|
||||
#include "common/random.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Toltecs {
|
||||
|
||||
struct ToltecsGameDescription;
|
||||
|
||||
class AnimationPlayer;
|
||||
class ArchiveReader;
|
||||
class Input;
|
||||
class MenuSystem;
|
||||
class MoviePlayer;
|
||||
class MusicPlayer;
|
||||
class Palette;
|
||||
class ResourceCache;
|
||||
class ScriptInterpreter;
|
||||
class Screen;
|
||||
class SegmentMap;
|
||||
class Sound;
|
||||
|
||||
enum SysString {
|
||||
kStrLoadingPleaseWait,
|
||||
kStrWhatCanIDoForYou,
|
||||
kStrLoad,
|
||||
kStrSave,
|
||||
kStrTextOn,
|
||||
kStrTextOff,
|
||||
kStrVoicesOn,
|
||||
kStrVoicesOff,
|
||||
kStrVolume,
|
||||
kStrPlay,
|
||||
kStrQuit,
|
||||
kStrLoadGame,
|
||||
kStrSaveGame,
|
||||
kStrAdjustVolume,
|
||||
kStrMaster,
|
||||
kStrVoices,
|
||||
kStrMusic,
|
||||
kStrSoundFx,
|
||||
kStrBackground,
|
||||
kStrCancel,
|
||||
kStrDone,
|
||||
kStrAreYouSure,
|
||||
kStrYes,
|
||||
kStrNo,
|
||||
kSysStrCount
|
||||
};
|
||||
|
||||
class ToltecsEngine : public ::Engine {
|
||||
Common::KeyState _keyPressed;
|
||||
|
||||
protected:
|
||||
Common::Error run();
|
||||
// void shutdown();
|
||||
|
||||
public:
|
||||
ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc);
|
||||
virtual ~ToltecsEngine();
|
||||
|
||||
virtual bool hasFeature(EngineFeature f) const;
|
||||
virtual void syncSoundSettings();
|
||||
|
||||
Common::RandomSource *_rnd;
|
||||
const ToltecsGameDescription *_gameDescription;
|
||||
uint32 getFeatures() const;
|
||||
Common::Language getLanguage() const;
|
||||
const Common::String& getTargetName() const { return _targetName; }
|
||||
|
||||
void setupSysStrings();
|
||||
void requestSavegame(int slotNum, Common::String &description);
|
||||
void requestLoadgame(int slotNum);
|
||||
|
||||
void loadScene(uint resIndex);
|
||||
|
||||
void updateScreen();
|
||||
void drawScreen();
|
||||
void updateInput();
|
||||
|
||||
void setGuiHeight(int16 guiHeight);
|
||||
|
||||
void setCamera(int16 x, int16 y);
|
||||
bool getCameraChanged();
|
||||
void scrollCameraUp(int16 delta);
|
||||
void scrollCameraDown(int16 delta);
|
||||
void scrollCameraLeft(int16 delta);
|
||||
void scrollCameraRight(int16 delta);
|
||||
void updateCamera();
|
||||
|
||||
void talk(int16 slotIndex, int16 slotOffset);
|
||||
|
||||
void walk(byte *walkData);
|
||||
|
||||
int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
|
||||
byte *rectDataEnd);
|
||||
|
||||
public:
|
||||
|
||||
AnimationPlayer *_anim;
|
||||
ArchiveReader *_arc;
|
||||
Input *_input;
|
||||
MenuSystem *_menuSystem;
|
||||
MoviePlayer *_moviePlayer;
|
||||
MusicPlayer *_musicPlayer;
|
||||
Palette *_palette;
|
||||
ResourceCache *_res;
|
||||
ScriptInterpreter *_script;
|
||||
Screen *_screen;
|
||||
SegmentMap *_segmap;
|
||||
Sound *_sound;
|
||||
|
||||
Common::String _sysStrings[kSysStrCount];
|
||||
|
||||
int _saveLoadRequested;
|
||||
int _saveLoadSlot;
|
||||
Common::String _saveLoadDescription;
|
||||
|
||||
uint _sceneResIndex;
|
||||
int16 _sceneWidth, _sceneHeight;
|
||||
|
||||
int _counter01, _counter02;
|
||||
bool _movieSceneFlag;
|
||||
byte _flag01;
|
||||
|
||||
int16 _cameraX, _cameraY;
|
||||
int16 _newCameraX, _newCameraY;
|
||||
int16 _cameraHeight;
|
||||
int16 _guiHeight;
|
||||
|
||||
bool _doSpeech, _doText;
|
||||
|
||||
int16 _walkSpeedY, _walkSpeedX;
|
||||
|
||||
Common::KeyState _keyState;
|
||||
int16 _mouseX, _mouseY;
|
||||
int16 _mouseDblClickTicks;
|
||||
bool _mouseWaitForRelease;
|
||||
byte _mouseButton;
|
||||
int16 _mouseDisabled;
|
||||
bool _leftButtonDown, _rightButtonDown;
|
||||
|
||||
const char *getSysString(int index) const { return _sysStrings[index].c_str(); }
|
||||
|
||||
/* Save/load */
|
||||
|
||||
enum kReadSaveHeaderError {
|
||||
kRSHENoError = 0,
|
||||
kRSHEInvalidType = 1,
|
||||
kRSHEInvalidVersion = 2,
|
||||
kRSHEIoError = 3
|
||||
};
|
||||
|
||||
struct SaveHeader {
|
||||
Common::String description;
|
||||
uint32 version;
|
||||
byte gameID;
|
||||
uint32 flags;
|
||||
Graphics::Surface *thumbnail;
|
||||
};
|
||||
|
||||
bool _isSaveAllowed;
|
||||
|
||||
bool canLoadGameStateCurrently() { return _isSaveAllowed; }
|
||||
bool canSaveGameStateCurrently() { return _isSaveAllowed; }
|
||||
Common::Error loadGameState(int slot);
|
||||
Common::Error saveGameState(int slot, const Common::String &description);
|
||||
void savegame(const char *filename, const char *description);
|
||||
void loadgame(const char *filename);
|
||||
|
||||
const char *getSavegameFilename(int num);
|
||||
static Common::String getSavegameFilename(const Common::String &target, int num);
|
||||
|
||||
static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace Toltecs
|
||||
|
||||
#endif /* TOLTECS_H */
|
Loading…
Reference in New Issue
Block a user