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:
Willem Jan Palenstijn 2011-11-20 22:46:58 +01:00
commit 2613e7f587
34 changed files with 7881 additions and 0 deletions

View File

@ -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
View File

@ -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

View File

@ -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
View 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)

View File

@ -0,0 +1,164 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
*/
#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

View File

@ -0,0 +1,69 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
*/
#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 */

View 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
View 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
View 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 */

View 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

View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
// 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
View 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
View 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
View 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
View File

@ -0,0 +1,99 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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 */

View File

@ -0,0 +1,128 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
*/
#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

View 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 */

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

184
engines/toltecs/script.h Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,74 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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
View 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
View 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
View 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 */