Initial commit

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

3
engines/toltecs/POTFILES Normal file
View File

@@ -0,0 +1,3 @@
engines/toltecs/detection.cpp
engines/toltecs/menu.cpp
engines/toltecs/metaengine.cpp

View File

@@ -0,0 +1,173 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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]();
_resIndex = 0;
_width = _height = 0;
_frameNumber = 0;
_frameCount = 0;
_keepFrameCounter = 0;
_curFrameSize = _nextFrameSize = 0;
_nextFrameOffset = 0;
_firstNextFrameSize = 0;
_firstNextFrameOffset = 0;
_firstCurFrameSize = 0;
}
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,67 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine toltecs "3 Skulls of the Toltecs" yes "" "" "highres" "midi"

View File

@@ -0,0 +1,78 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "gui/debugger.h"
#include "toltecs/console.h"
//#include "toltecs/palette.h"
#include "toltecs/resource.h"
//#include "toltecs/sound.h"
#include "toltecs/toltecs.h"
namespace Toltecs {
Console::Console(ToltecsEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("room", WRAP_METHOD(Console, Cmd_Room));
registerCmd("dump", WRAP_METHOD(Console, Cmd_Dump));
}
Console::~Console() {
}
bool Console::Cmd_Room(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Current room number is %d\n", _vm->_sceneResIndex);
#if 0
debugPrintf("Calling this command with the room number changes the room\n");
debugPrintf("WARNING: It's a bad idea to warp to rooms with this, as the room object scripts are not loaded\n");
#endif
return true;
#if 0
} else {
int roomNum = atoi(argv[1]);
// sfClearPaletteFragments
_vm->_palette->clearFragments();
// sfLoadScene
_vm->_sound->stopAll();
_vm->_res->purgeCache();
_vm->loadScene(roomNum);
#endif
}
return false;
}
bool Console::Cmd_Dump(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Usage: dump <resource number>\n");
return true;
}
int resNum = atoi(argv[1]);
_vm->_arc->dump(resNum);
debugPrintf("Resource %d has been dumped to disk\n", resNum);
return true;
}
} // End of namespace Toltecs

44
engines/toltecs/console.h Normal file
View File

@@ -0,0 +1,44 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOLTECS_CONSOLE_H
#define TOLTECS_CONSOLE_H
#include "gui/debugger.h"
namespace Toltecs {
class ToltecsEngine;
class Console : public GUI::Debugger {
public:
Console(ToltecsEngine *vm);
~Console(void) override;
private:
ToltecsEngine *_vm;
bool Cmd_Dump(int argc, const char **argv);
bool Cmd_Room(int argc, const char **argv);
};
} // End of namespace Toltecs
#endif

View File

@@ -0,0 +1,4 @@
begin_section("Toltecs");
add_person("Benjamin Haisch", "john_doe", "");
add_person("Filippos Karapetis", "bluegr", "");
end_section();

View File

@@ -0,0 +1,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "common/savefile.h"
#include "common/str-array.h"
#include "common/system.h"
#include "toltecs/toltecs.h"
#include "toltecs/detection.h"
static const PlainGameDescriptor toltecsGames[] = {
{"toltecs", "3 Skulls of the Toltecs"},
{0, 0}
};
#include "toltecs/detection_tables.h"
class ToltecsMetaEngineDetection : public AdvancedMetaEngineDetection<Toltecs::ToltecsGameDescription> {
public:
ToltecsMetaEngineDetection() : AdvancedMetaEngineDetection(Toltecs::gameDescriptions, toltecsGames) {
_flags = kADFlagMatchFullPaths;
}
const char *getName() const override {
return "toltecs";
}
const char *getEngineName() const override {
return "3 Skulls of the Toltecs";
}
const char *getOriginalCopyright() const override {
return "3 Skulls of the Toltecs (C) Revistronic 1996";
}
};
REGISTER_PLUGIN_STATIC(TOLTECS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ToltecsMetaEngineDetection);

View File

@@ -0,0 +1,37 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOLTECS_DETECTION_H
#define TOLTECS_DETECTION_H
namespace Toltecs {
struct ToltecsGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
};
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
} // End of namespace Toltecs
#endif // TOLTECS_DETECTION_H

View File

@@ -0,0 +1,216 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Toltecs {
static const ToltecsGameDescription gameDescriptions[] = {
{
// 3 Skulls of the Toltecs English version
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "05472037e9cfde146e953c434e74f0f4", 337643527),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs English version (alternate)
// From bug #6393
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "a9c9cfef9d05b8f7a5573b626fa4ea87", 337643527),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs PIRATE CD-RIP version (no audio)
// == DO NOT RE-ADD ==
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "56d0da91ec3db8ac869594357584e851", 104804435),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_PIRATED,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs Russian version
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "ba1742d3193b68ceb9434e2ab7a09a9b", 391462783),
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs German version
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "1a3292bad8e0bb5701800c73531dd75e", 345176617),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs Polish version
// Reported by cachaito in Trac#11134
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "8ec48dd4e52a822d314418f1d3284e64", 337646148),
Common::PL_POL,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs French version
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "4fb845635cbdac732453fe23be350df9", 327269545),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs Spanish version
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "479f468beccc1b0ce5873ec523d1380e", 308391018),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs Hungarian version
// From bug #5902
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "69a5572e75409d8c6230b787faa353af", 337647960),
Common::HU_HUN,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs Czech version
// Reported by AfBu in Trac#11263
{
"toltecs",
0,
AD_ENTRY1s("WESTERN", "57503131c0217c76b07d0b5c14805631", 337644552),
Common::CS_CZE,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs English Demo version
{
"toltecs",
"Demo",
AD_ENTRY1s("WESTERN", "53a0abd1c0bc5cad8ba18f0e56877705", 46241833),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs English Demo version (original file layout)
{
"toltecs",
"Demo",
AD_ENTRY1s("english.pdi/WESTERN", "53a0abd1c0bc5cad8ba18f0e56877705", 46241833),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// 3 Skulls of the Toltecs German Demo version
{
"toltecs",
"Demo",
AD_ENTRY1s("WESTERN", "1c85e82712d24f1d5c1ea2a66ddd75c2", 47730038),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
},
},
{
// Fenimore Fillmore: 3 Skulls of the Toltecs, 2019 Casual Brothers remaster (GOG, Steam)
{
"toltecs",
MetaEngineDetection::GAME_NOT_IMPLEMENTED, // Reason for being unsupported
AD_ENTRY1s("RData.lzma", "e0adae53ab5e821595a64032a4c2d5bc", 653477695),
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_REMASTERED | ADGF_UNSUPPORTED,
GUIO1(GUIO_NONE)
}
},
{ AD_TABLE_END_MARKER }
};
} // End of namespace Toltecs

717
engines/toltecs/menu.cpp Normal file
View File

@@ -0,0 +1,717 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "audio/mixer.h"
#include "common/savefile.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "gui/saveload.h"
#include "toltecs/toltecs.h"
#include "toltecs/menu.h"
#include "toltecs/palette.h"
#include "toltecs/render.h"
#include "toltecs/resource.h"
#include "backends/keymapper/keymapper.h"
namespace Toltecs {
MenuSystem::MenuSystem(ToltecsEngine *vm) : _vm(vm) {
_background = nullptr;
_running = false;
_currMenuID = _newMenuID = kMenuIdNone;
_currItemID = kItemIdNone;
_top = 0;
_savegameListTopIndex = 0;
_editingDescription = false;
_editingDescriptionID = kItemIdNone;
_editingDescriptionItem = nullptr;
_needRedraw = false;
_returnToGame = false;
}
MenuSystem::~MenuSystem() {
}
int MenuSystem::run(MenuID menuId) {
_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 = menuId;
_currItemID = kItemIdNone;
_editingDescription = false;
_running = true;
_top = 30 - _vm->_guiHeight / 2;
_needRedraw = false;
_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, 247, 225, 229);
memcpy(_background->getPixels(), _vm->_screen->_frontScreen, 640 * 400);
if (menuId == kMenuIdMain)
_returnToGame = false;
else
_returnToGame = true;
while (_running) {
update();
_vm->_system->updateScreen();
}
// Restore original background
memcpy(_vm->_screen->_frontScreen, backgroundOrig.getBasePtr(0,0), 640 * 400);
_vm->_system->copyRectToScreen(_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(_vm->_screen->_frontScreen + 39 * 640 + 60, 640, 60, 39, 520, 247);
_vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, _top, 640, 400 - _top);
_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_RETURN_TO_LAUNCHER:
case Common::EVENT_QUIT:
_running = false;
break;
case Common::EVENT_MOUSEMOVE:
handleMouseMove(event.mouse.x, event.mouse.y);
break;
case Common::EVENT_LBUTTONUP:
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.enabled = true;
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;
//// Now we turn on the keymapper
Common::Keymapper *keymapper = _vm->getEventManager()->getKeymapper();
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
keymapper->getKeymap("toltecs-default")->setEnabled(true);
}
}
}
ItemID MenuSystem::findItemAt(int x, int y) {
for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); ++iter) {
if ((*iter).enabled && (*iter).rect.contains(x, y - _top))
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();
if (width & 1)
width++;
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) {
_items.clear();
memcpy(_vm->_screen->_frontScreen, _background->getPixels(), 640 * 400);
switch (menuID) {
case kMenuIdMain:
drawString(0, 75, 320, 1, 229, _vm->getSysString(kStrWhatCanIDoForYou));
addClickTextItem(kItemIdLoad, 0, 116, 320, 0, _vm->getSysString(kStrLoad), 253, 255);
addClickTextItem(kItemIdSave, 0, 136, 320, 0, _vm->getSysString(kStrSave), 253, 255);
addClickTextItem(kItemIdToggleText, 0, 166, 320, 0, _vm->getSysString(_vm->_cfgText ? kStrTextOn : kStrTextOff), 253, 255);
addClickTextItem(kItemIdToggleVoices, 0, 186, 320, 0, _vm->getSysString(_vm->_cfgVoices ? kStrVoicesOn : kStrVoicesOff), 253, 255);
addClickTextItem(kItemIdVolumesMenu, 0, 216, 320, 0, _vm->getSysString(kStrVolume), 253, 255);
addClickTextItem(kItemIdPlay, 0, 246, 320, 0, _vm->getSysString(kStrPlay), 253, 255);
addClickTextItem(kItemIdQuit, 0, 276, 320, 0, _vm->getSysString(kStrQuit), 253, 255);
break;
case kMenuIdLoad:
if (ConfMan.getBool("originalsaveload")) {
shadeRect(80, 92, 440, 141, 226, 225);
drawString(0, 75, 320, 1, 229, _vm->getSysString(kStrLoadGame));
addClickTextItem(kItemIdSavegameUp, 0, 156, 545, 1, "^", 253, 255);
addClickTextItem(kItemIdSavegameDown, 0, 196, 545, 1, "\\", 253, 255);
addClickTextItem(kItemIdCancel, 0, 276, 320, 0, _vm->getSysString(kStrCancel), 253, 255);
for (int i = 1; i <= 7; i++) {
Common::String saveDesc = Common::String::format("SAVEGAME %d", i);
addClickTextItem((ItemID)(kItemIdSavegame1 + i - 1), 0, 116 + 20 * (i - 1), 300, 0, saveDesc.c_str(), 231, 234);
}
loadSavegamesList();
setSavegameCaptions(true);
} else {
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
int slot = dialog->runModalWithCurrentTarget();
delete dialog;
if (slot >= 0) {
_vm->requestLoadgame(slot);
_running = false;
} else {
if (_returnToGame)
_running = false;
else
_newMenuID = kMenuIdMain;
}
}
break;
case kMenuIdSave:
if (ConfMan.getBool("originalsaveload")) {
shadeRect(80, 92, 440, 141, 226, 225);
drawString(0, 75, 320, 1, 229, _vm->getSysString(kStrSaveGame));
addClickTextItem(kItemIdSavegameUp, 0, 156, 545, 1, "^", 253, 255);
addClickTextItem(kItemIdSavegameDown, 0, 196, 545, 1, "\\", 253, 255);
addClickTextItem(kItemIdCancel, 0, 276, 320, 0, _vm->getSysString(kStrCancel), 253, 255);
for (int i = 1; i <= 7; i++) {
Common::String saveDesc = Common::String::format("SAVEGAME %d", i);
addClickTextItem((ItemID)(kItemIdSavegame1 + i - 1), 0, 116 + 20 * (i - 1), 300, 0, saveDesc.c_str(), 231, 234);
}
int newSlotNum = loadSavegamesList() + 1;
_savegames.push_back(SavegameItem(newSlotNum, Common::String::format("GAME %04d", _savegames.size())));
setSavegameCaptions(true);
} else {
GUI::SaveLoadChooser dialog(_("Save game:"), _("Save"), true);
int slot = dialog.runModalWithCurrentTarget();
Common::String desc = dialog.getResultString();
if (desc.empty()) {
// Create our own description for the saved game, the user didn't enter one
desc = dialog.createDefaultSaveDescription(slot);
}
if (slot >= 0) {
_vm->requestSavegame(slot, desc);
_running = false;
} else {
if (_returnToGame)
_running = false;
else
_newMenuID = kMenuIdMain;
}
}
break;
case kMenuIdVolumes:
drawString(0, 75, 320, 1, 229, _vm->getSysString(kStrAdjustVolume));
drawString(0, 131, 200, 0, 246, _vm->getSysString(kStrMaster));
drawString(0, 156, 200, 0, 244, _vm->getSysString(kStrVoices));
drawString(0, 181, 200, 0, 244, _vm->getSysString(kStrMusic));
drawString(0, 206, 200, 0, 244, _vm->getSysString(kStrSoundFx));
drawString(0, 231, 200, 0, 244, _vm->getSysString(kStrBackground));
addClickTextItem(kItemIdDone, 0, 276, 200, 0, _vm->getSysString(kStrDone), 253, 255);
addClickTextItem(kItemIdCancel, 0, 276, 440, 0, _vm->getSysString(kStrCancel), 253, 255);
addClickTextItem(kItemIdMasterDown, 0, 131 + 25 * 0, 348, 1, "[", 243, 246);
addClickTextItem(kItemIdVoicesDown, 0, 131 + 25 * 1, 348, 1, "[", 243, 246);
addClickTextItem(kItemIdMusicDown, 0, 131 + 25 * 2, 348, 1, "[", 243, 246);
addClickTextItem(kItemIdSoundFXDown, 0, 131 + 25 * 3, 348, 1, "[", 243, 246);
addClickTextItem(kItemIdBackgroundDown, 0, 131 + 25 * 4, 348, 1, "[", 243, 246);
addClickTextItem(kItemIdMasterUp, 0, 131 + 25 * 0, 372, 1, "]", 243, 246);
addClickTextItem(kItemIdVoicesUp, 0, 131 + 25 * 1, 372, 1, "]", 243, 246);
addClickTextItem(kItemIdMusicUp, 0, 131 + 25 * 2, 372, 1, "]", 243, 246);
addClickTextItem(kItemIdSoundFXUp, 0, 131 + 25 * 3, 372, 1, "]", 243, 246);
addClickTextItem(kItemIdBackgroundUp, 0, 131 + 25 * 4, 372, 1, "]", 243, 246);
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) {
if ((*iter).enabled)
drawItem((*iter).id, false);
}
// Check if the mouse is already over an item
_currItemID = kItemIdNone;
Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
handleMouseMove(mousePos.x, mousePos.y);
}
void MenuSystem::enableItem(ItemID id) {
Item *item = getItem(id);
if (item) {
item->enabled = true;
drawItem(id, false);
_currItemID = kItemIdNone;
Common::Point mousePos = _vm->_system->getEventManager()->getMousePos();
handleMouseMove(mousePos.x, mousePos.y);
}
}
void MenuSystem::disableItem(ItemID id) {
Item *item = getItem(id);
if (item) {
item->enabled = false;
restoreRect(item->rect.left, item->rect.top, item->rect.width(), item->rect.height());
if (_currItemID == id) {
_currItemID = kItemIdNone;
}
}
}
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(!_vm->_cfgText, true);
if (!_vm->_cfgVoices && !_vm->_cfgText)
setCfgVoices(true, false);
break;
case kItemIdToggleVoices:
setCfgVoices(!_vm->_cfgVoices, true);
if (!_vm->_cfgVoices && !_vm->_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 *)_vm->_screen->_frontScreen + x + y * 640;
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) {
int width = font.getTextWidth((const byte*)text);
if (width & 1)
width++;
x = x + w - width / 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, 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) {
if (id >= kItemIdSavegame1 && id <= kItemIdSavegame7)
return &_savegames[_savegameListTopIndex + id - kItemIdSavegame1];
else
return NULL;
}
void MenuSystem::setSavegameCaptions(bool scrollToBottom) {
int size = _savegames.size();
if (scrollToBottom && size > 0) {
while (_savegameListTopIndex + 7 <= size)
_savegameListTopIndex += 6;
}
int index = _savegameListTopIndex;
for (int i = 1; i <= 7; i++)
setItemCaption(getItem((ItemID)(kItemIdSavegame1 + i - 1)), index < size ? _savegames[index++]._description.c_str() : "");
if (_savegameListTopIndex == 0) {
disableItem(kItemIdSavegameUp);
} else {
enableItem(kItemIdSavegameUp);
}
if (_savegameListTopIndex + 7 > size) {
disableItem(kItemIdSavegameDown);
} else {
enableItem(kItemIdSavegameDown);
}
}
void MenuSystem::scrollSavegames(int delta) {
int newPos = CLIP<int>(_savegameListTopIndex + delta, 0, _savegames.size() - 1);
_savegameListTopIndex = newPos;
restoreRect(80, 92, 440, 140);
setSavegameCaptions(false);
for (int i = 1; i <= 7; i++)
drawItem((ItemID)(kItemIdSavegame1 + i - 1), 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 {
Common::Keymapper *keymapper = _vm->getEventManager()->getKeymapper();
// Now we turn off the keymapper so it does not interfere with full text input
keymapper->getKeymap("toltecs-default")->setEnabled(false);
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
_editingDescription = true;
_editingDescriptionItem = getItem(id);
_editingDescriptionID = id;
_editingDescriptionItem->activeColor = 249;
_editingDescriptionItem->defaultColor = 249;
drawItem(_editingDescriptionID, true);
}
}
void MenuSystem::setCfgText(bool value, bool active) {
if (_vm->_cfgText != value) {
Item *item = getItem(kItemIdToggleText);
_vm->_cfgText = value;
restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2);
setItemCaption(item, _vm->getSysString(_vm->_cfgText ? kStrTextOn : kStrTextOff));
drawItem(kItemIdToggleText, true);
ConfMan.setBool("subtitles", value);
}
}
void MenuSystem::setCfgVoices(bool value, bool active) {
if (_vm->_cfgVoices != value) {
Item *item = getItem(kItemIdToggleVoices);
_vm->_cfgVoices = value;
restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2);
setItemCaption(item, _vm->getSysString(_vm->_cfgVoices ? kStrVoicesOn : kStrVoicesOff));
drawItem(kItemIdToggleVoices, true);
ConfMan.setBool("speech_mute", !value);
}
}
void MenuSystem::drawVolumeBar(ItemID itemID) {
int w = 440, y, volume;
char text[21];
switch (itemID) {
case kItemIdMaster: // unused in ScummVM, always 20
y = 130 + 25 * 0;
volume = 20;
break;
case kItemIdVoices:
y = 130 + 25 * 1;
volume = _vm->_cfgVoicesVolume;
break;
case kItemIdMusic:
y = 130 + 25 * 2;
volume = _vm->_cfgMusicVolume;
break;
case kItemIdSoundFX:
y = 130 + 25 * 3;
volume = _vm->_cfgSoundFXVolume;
break;
case kItemIdBackground: // unused in ScummVM, always 20
y = 130 + 25 * 4;
volume = 20;
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) {
byte newVolume;
switch (itemID) {
case kItemIdVoices:
_vm->_cfgVoicesVolume = CLIP(_vm->_cfgVoicesVolume + delta, 0, 20);
// Always round volume up instead of down.
newVolume = (_vm->_cfgVoicesVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20;
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, newVolume);
ConfMan.setInt("speech_volume", newVolume);
break;
case kItemIdMusic:
_vm->_cfgMusicVolume = CLIP(_vm->_cfgMusicVolume + delta, 0, 20);
newVolume = (_vm->_cfgMusicVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20;
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, newVolume);
ConfMan.setInt("music_volume", newVolume);
break;
case kItemIdSoundFX:
_vm->_cfgSoundFXVolume = CLIP(_vm->_cfgSoundFXVolume + delta, 0, 20);
newVolume = (_vm->_cfgSoundFXVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20;
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, newVolume);
ConfMan.setInt("sfx_volume", newVolume);
break;
case kItemIdMaster:
case kItemIdBackground:
// unused in ScummVM
break;
default:
return;
}
_vm->syncSoundSettings();
drawVolumeBar(itemID);
}
} // End of namespace Toltecs

159
engines/toltecs/menu.h Normal file
View File

@@ -0,0 +1,159 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOLTECS_MENU_H
#define TOLTECS_MENU_H
#include "common/array.h"
#include "common/str-array.h"
namespace Toltecs {
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(MenuID menuId);
void update();
void handleEvents();
protected:
struct Item {
bool enabled;
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;
bool _returnToGame;
Common::Array<Item> _items;
Common::Array<SavegameItem> _savegames;
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 enableItem(ItemID id);
void disableItem(ItemID id);
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(bool scrollToBottom);
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,273 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/savefile.h"
#include "common/str-array.h"
#include "common/system.h"
#include "common/translation.h"
#include "toltecs/toltecs.h"
#include "toltecs/detection.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
namespace Toltecs {
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_ORIGINAL_SAVELOAD,
{
_s("Use original save/load screens"),
_s("Use the original save/load screens instead of the ScummVM ones"),
"originalsaveload",
false,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
uint32 ToltecsEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
Common::Language ToltecsEngine::getLanguage() const {
return _gameDescription->desc.language;
}
} // End of namespace Toltecs
class ToltecsMetaEngine : public AdvancedMetaEngine<Toltecs::ToltecsGameDescription> {
public:
const char *getName() const override {
return "toltecs";
}
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return Toltecs::optionsList;
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const Toltecs::ToltecsGameDescription *desc) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
bool removeSaveState(const char *target, int slot) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool ToltecsMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSavesSupportCreationDate) ||
(f == kSavesSupportPlayTime) ||
(f == kSimpleSavesNames);
}
bool Toltecs::ToltecsEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
Common::Error ToltecsMetaEngine::createInstance(OSystem *syst, Engine **engine, const Toltecs::ToltecsGameDescription *desc) const {
*engine = new Toltecs::ToltecsEngine(syst,desc);
return Common::kNoError;
}
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());
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, header) == Toltecs::ToltecsEngine::kRSHENoError) {
saveList.push_back(SaveStateDescriptor(this, slotNum, header.description));
}
delete in;
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int ToltecsMetaEngine::getMaximumSaveSlot() const {
return 999;
}
bool ToltecsMetaEngine::removeSaveState(const char *target, int slot) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::String filename = Toltecs::ToltecsEngine::getSavegameFilename(target, slot);
bool success = 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,
if (slotNum > slot) {
saveFileMan->renameSavefile(file->c_str(), filename.c_str());
filename = Toltecs::ToltecsEngine::getSavegameFilename(target, ++slot);
}
}
return success;
}
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, header, false);
delete in;
if (error == Toltecs::ToltecsEngine::kRSHENoError) {
SaveStateDescriptor desc(this, slot, header.description);
desc.setThumbnail(header.thumbnail);
if (header.version > 0) {
int day = (header.saveDate >> 24) & 0xFF;
int month = (header.saveDate >> 16) & 0xFF;
int year = header.saveDate & 0xFFFF;
desc.setSaveDate(year, month, day);
int hour = (header.saveTime >> 16) & 0xFF;
int minutes = (header.saveTime >> 8) & 0xFF;
desc.setSaveTime(hour, minutes);
desc.setPlayTime(header.playTime * 1000);
}
return desc;
}
}
return SaveStateDescriptor();
}
Common::KeymapArray ToltecsMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Toltecs;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "toltecs-default", _("Default keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Move / Select"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Move / Perform default action"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Action("SKIPDLG", _("Skip dialog"));
act->setCustomEngineActionEvent(kActionSkipDialog);
act->addDefaultInputMapping("SPACE");
act->addDefaultInputMapping("JOY_X");
engineKeyMap->addAction(act);
act = new Action("OPENSAVEMENU", _("Save game"));
act->setCustomEngineActionEvent(kActionOpenSaveMenu);
act->addDefaultInputMapping("F5");
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
engineKeyMap->addAction(act);
act = new Action("OPENLOADMENU", _("Load game"));
act->setCustomEngineActionEvent(kActionOpenLoadMenu);
act->addDefaultInputMapping("F7");
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
engineKeyMap->addAction(act);
act = new Action("SKIPMOVIE", _("Skip cutscene"));
act->setCustomEngineActionEvent(kActionSkipMovie);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
engineKeyMap->addAction(act);
act = new Action("MENUOPEN", _("Menu"));
act->setCustomEngineActionEvent(kActionMenuOpen);
act->addDefaultInputMapping("F10");
act->addDefaultInputMapping("JOY_START");
engineKeyMap->addAction(act);
act = new Action("SKIPRIDE", _("Skip ride"));
act->setCustomEngineActionEvent(kActionSkipRide);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
engineKeyMap->addAction(act);
return Keymap::arrayOf(engineKeyMap);
}
#if PLUGIN_ENABLED_DYNAMIC(TOLTECS)
REGISTER_PLUGIN_DYNAMIC(TOLTECS, PLUGIN_TYPE_ENGINE, ToltecsMetaEngine);
#else
REGISTER_PLUGIN_STATIC(TOLTECS, PLUGIN_TYPE_ENGINE, ToltecsMetaEngine);
#endif

View File

@@ -0,0 +1,206 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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) {
BoundingBox boundingBox = _tiles[i];
if (isBoundingBoxEmpty(boundingBox)) {
++i;
continue;
}
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
if (TileX1(boundingBox) == TileSize - 1 && x != _tilesW - 1) { // check if the tile continues
bool finish = false;
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 = true;
}
}
}
#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
++i;
} // for (x = 0; x < _tilesW; ++x)
} // for (y = 0; y < _tilesH; ++y)
*num_rects = n_rects;
//delete glom;
return rects;
}
} // End of namespace Toltecs

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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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

32
engines/toltecs/module.mk Normal file
View File

@@ -0,0 +1,32 @@
MODULE := engines/toltecs
MODULE_OBJS = \
animation.o \
console.o \
menu.o \
metaengine.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
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

298
engines/toltecs/movie.cpp Normal file
View File

@@ -0,0 +1,298 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "audio/audiostream.h"
#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), _isPlaying(false), _lastPrefetchOfs(0), _framesPerSoundChunk(0), _endPos(0), _audioStream(0) {
}
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];
_isPlaying = true;
_vm->_isSaveAllowed = false;
memset(moviePalette, 0, sizeof(moviePalette));
_vm->_screen->finishTalkTextItems();
_vm->_arc->openResource(resIndex);
_endPos = _vm->_arc->pos() + _vm->_arc->getResourceSize(resIndex);
/*_frameCount = */_vm->_arc->readUint32LE();
uint32 chunkCount = _vm->_arc->readUint32LE();
// TODO: Figure out rest of the header
_vm->_arc->readUint32LE();
_vm->_arc->readUint32LE();
_framesPerSoundChunk = _vm->_arc->readUint32LE();
int rate = _vm->_arc->readUint32LE();
_vm->_sceneWidth = 640;
_vm->_sceneHeight = 400;
_vm->_cameraHeight = 400;
_vm->_cameraX = 0;
_vm->_cameraY = 0;
_vm->_guiHeight = 0;
_audioStream = Audio::makeQueuingAudioStream(rate, false);
_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle, _audioStream);
_lastPrefetchOfs = 0;
fetchAudioChunks();
byte *chunkBuffer = NULL;
uint32 chunkBufferSize = 0;
uint32 frame = 0;
bool abortMovie = false;
uint32 soundChunkFramesLeft = 0;
while (chunkCount-- && !abortMovie) {
byte chunkType = _vm->_arc->readByte();
uint32 chunkSize = _vm->_arc->readUint32LE();
debug(0, "chunkType = %d; chunkSize = %d", chunkType, chunkSize);
// Skip audio chunks - we've already queued them in
// fetchAudioChunks()
if (chunkType == kChunkAudio) {
_vm->_arc->skip(chunkSize);
soundChunkFramesLeft += _framesPerSoundChunk;
} else {
// Only reallocate the chunk buffer if the new chunk is bigger
if (chunkSize > chunkBufferSize) {
delete[] chunkBuffer;
chunkBuffer = new byte[chunkSize];
chunkBufferSize = chunkSize;
}
_vm->_arc->read(chunkBuffer, chunkSize);
}
switch (chunkType) {
case kChunkFirstImage:
case kChunkSubsequentImages:
unpackRle(chunkBuffer, _vm->_screen->_backScreen);
_vm->_screen->_fullRefresh = true;
if (--soundChunkFramesLeft <= _framesPerSoundChunk) {
fetchAudioChunks();
}
while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < (1000 * frame) / 9) {
if (_vm->_screen->_shakeActive && _vm->_screen->updateShakeScreen()) {
_vm->_screen->_fullRefresh = true;
}
if (!handleInput())
abortMovie = true;
_vm->drawScreen();
// Note: drawScreen() calls delayMillis()
}
frame++;
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:
memcpy(_vm->_script->getSlotData(subtitleSlot), chunkBuffer, chunkSize);
// The last character of the subtitle determines if it should
// always be displayed or not. If it's 0xFF, it should always be
// displayed, otherwise, if it's 0xFE, it can be toggled.
_vm->_screen->updateTalkText(subtitleSlot, 0, (chunkBuffer[chunkSize - 1] == 0xFF));
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, (int)_vm->_arc->pos() - 5 - chunkSize);
}
if (!handleInput())
abortMovie = true;
}
delete[] chunkBuffer;
_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;
_isPlaying = false;
}
void MoviePlayer::fetchAudioChunks() {
uint32 startOfs = _vm->_arc->pos();
uint prefetchChunkCount = 0;
if (_lastPrefetchOfs != 0)
_vm->_arc->seek(_lastPrefetchOfs, SEEK_SET);
while (prefetchChunkCount < _framesPerSoundChunk / 2 && _vm->_arc->pos() < _endPos) {
byte chunkType = _vm->_arc->readByte();
uint32 chunkSize = _vm->_arc->readUint32LE();
if (chunkType == kChunkAudio) {
byte *chunkBuffer = (byte *)malloc(chunkSize);
_vm->_arc->read(chunkBuffer, chunkSize);
_audioStream->queueBuffer(chunkBuffer, chunkSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
chunkBuffer = NULL;
prefetchChunkCount++;
} 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; // 640x400
//int packedSize = 0;
while (size > 0) {
byte a = *source++;
byte b = *source++;
//packedSize += 2;
if (a == 0) {
dest += b;
size -= b;
} else {
memset(dest, b, a);
dest += a;
size -= a;
}
}
//debug("Packed RLE size: %d", packedSize);
}
bool MoviePlayer::handleInput() {
Common::Event event;
Common::EventManager *eventMan = g_system->getEventManager();
while (eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
switch (event.customType) {
case kActionSkipMovie:
return false;
case kActionMenuOpen:
// TODO: The original would bring up a stripped down
// main menu dialog, without the save/restore options.
break;
default:
break;
}
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_RBUTTONDOWN:
return false;
case Common::EVENT_QUIT:
return false;
default:
break;
}
}
return !_vm->shouldQuit();
}
} // End of namespace Toltecs

64
engines/toltecs/movie.h Normal file
View File

@@ -0,0 +1,64 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOLTECS_MOVIE_H
#define TOLTECS_MOVIE_H
#include "audio/mixer.h" // for Audio::SoundHandle
namespace Audio {
class QueuingAudioStream;
}
namespace Toltecs {
class MoviePlayer {
public:
MoviePlayer(ToltecsEngine *vm);
~MoviePlayer();
void playMovie(uint resIndex);
bool isPlaying() { return _isPlaying; }
protected:
ToltecsEngine *_vm;
Audio::QueuingAudioStream *_audioStream;
Audio::SoundHandle _audioStreamHandle;
bool _isPlaying;
uint32 _framesPerSoundChunk;
int32 _lastPrefetchOfs, _endPos;
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 */

160
engines/toltecs/music.cpp Normal file
View File

@@ -0,0 +1,160 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "audio/midiparser.h"
#include "audio/miles.h"
#include "common/textconsole.h"
#include "toltecs/toltecs.h"
#include "toltecs/music.h"
#include "toltecs/resource.h"
namespace Toltecs {
MusicPlayer::MusicPlayer(bool isGM) : _isGM(isGM), _buffer(NULL) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
MusicType musicType = MidiDriver::getMusicType(dev);
switch (musicType) {
case MT_ADLIB:
_milesAudioMode = true;
_driver = Audio::MidiDriver_Miles_AdLib_create("SAMPLE.AD", "SAMPLE.OPL");
break;
case MT_MT32:
// Not recommended since it sounds awful, but apparently the
// original sounded just as bad. I guess MT-32 support was
// added by default, not because anyone actually put any work
// into it.
_milesAudioMode = true;
_driver = Audio::MidiDriver_Miles_MT32_create("");
break;
default:
_milesAudioMode = false;
MidiPlayer::createDriver();
break;
}
int ret = _driver->open();
if (ret == 0) {
if (musicType != MT_ADLIB) {
if (musicType == MT_MT32 || _nativeMT32)
_driver->sendMT32Reset();
else
_driver->sendGMReset();
}
_driver->setTimerCallback(this, &timerCallback);
}
}
void MusicPlayer::send(uint32 b) {
if (_milesAudioMode) {
_driver->send(b);
return;
}
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;
syncVolume();
_isLooping = loop;
_isPlaying = true;
} else {
delete parser;
}
}
void MusicPlayer::stopAndClear() {
Common::StackLock lock(_mutex);
stop();
delete[] _buffer;
_buffer = NULL;
}
Music::Music(ArchiveReader *arc) : MusicPlayer(true), _arc(arc) {
_sequenceResIndex = -1;
}
void Music::playSequence(int16 sequenceResIndex) {
_sequenceResIndex = sequenceResIndex;
int32 resourceSize = _arc->getResourceSize(sequenceResIndex);
byte *data = new byte[resourceSize];
_arc->openResource(sequenceResIndex);
_arc->read(data, resourceSize);
_arc->closeResource();
if (!memcmp(data, "FORM", 4))
playMIDI(data, resourceSize, true); // music tracks are always looping
else
// Sanity check: this should never occur
error("playSequence: resource %d isn't XMIDI", sequenceResIndex);
delete[] data;
}
void Music::stopSequence() {
_sequenceResIndex = -1;
stopAndClear();
}
void Music::saveState(Common::WriteStream *out) {
out->writeSint16LE(_sequenceResIndex);
}
void Music::loadState(Common::ReadStream *in) {
_sequenceResIndex = in->readSint16LE();
if (_sequenceResIndex >= 0)
playSequence(_sequenceResIndex);
}
} // End of namespace Made

71
engines/toltecs/music.h Normal file
View File

@@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Music class
#ifndef MADE_MUSIC_H
#define MADE_MUSIC_H
#include "audio/midiplayer.h"
#include "common/stream.h"
namespace Toltecs {
class ArchiveReader;
class MusicPlayer : public Audio::MidiPlayer {
public:
MusicPlayer(bool isGM = true);
void playMIDI(const byte *data, uint32 size, bool loop = false);
void stopAndClear();
// MidiDriver_BASE interface implementation
void send(uint32 b) override;
protected:
bool _isGM;
private:
byte *_buffer;
bool _milesAudioMode;
};
class Music : public MusicPlayer {
public:
Music(ArchiveReader *arc);
~Music() override {}
void playSequence(int16 sequenceResIndex);
void stopSequence();
void saveState(Common::WriteStream *out);
void loadState(Common::ReadStream *in);
private:
int16 _sequenceResIndex;
ArchiveReader *_arc;
};
} // End of namespace Toltecs
#endif

227
engines/toltecs/palette.cpp Normal file
View File

@@ -0,0 +1,227 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/paletteman.h"
#include "toltecs/toltecs.h"
#include "toltecs/palette.h"
#include "toltecs/resource.h"
namespace Toltecs {
Palette::Palette(ToltecsEngine *vm) : _vm(vm) {
clearFragments();
memset(_mainPalette, 0, sizeof(_mainPalette));
memset(_animPalette, 0, sizeof(_animPalette));
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, int8 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();
}
byte Palette::getMatchingColor(byte r, byte g, byte b) {
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;
}
}
return bestIndex;
}
void Palette::buildColorTransTable(byte limit, int8 deltaValue, byte mask) {
byte r = 0, g = 0, b = 0;
mask &= 7;
if (deltaValue < 0) // unused
error("buildColorTransTable called with a negative delta value(limit %d, delta %d, mask %02X)", limit, deltaValue, mask);
for (int i = 0; i < 256; i++) {
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;
}
_colorTransTable[i] = getMatchingColor(r, g, b);
}
}
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

83
engines/toltecs/palette.h Normal file
View File

@@ -0,0 +1,83 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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, int8 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();
byte getMatchingColor(byte r, byte g, byte b);
void buildColorTransTable(byte limit, int8 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/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(_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;
default:
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(_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

97
engines/toltecs/render.h Normal file
View File

@@ -0,0 +1,97 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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,124 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "toltecs/toltecs.h"
#include "toltecs/resource.h"
namespace Toltecs {
/* ArchiveReader */
ArchiveReader::ArchiveReader() {
_offsets = 0;
}
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) {
int32 resourceSize = getResourceSize(resIndex);
byte *data = new byte[resourceSize];
Common::Path fn(Common::String::format("toltecs_res.%03d", 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);
int32 curPos = _vm->_arc->pos();
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();
_vm->_arc->seek(curPos);
_cache[resIndex] = resItem;
return resItem;
}
}
} // End of namespace Toltecs

View File

@@ -0,0 +1,83 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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() override;
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);
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,228 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/savefile.h"
#include "graphics/thumbnail.h"
#include "toltecs/toltecs.h"
#include "toltecs/animation.h"
#include "toltecs/music.h"
#include "toltecs/palette.h"
#include "toltecs/script.h"
#include "toltecs/screen.h"
#include "toltecs/sound.h"
namespace Toltecs {
/* TODO:
- Saving during an animation (AnimationPlayer) is not working correctly yet
- Maybe switch to SCUMM/Tinsel serialization approach?
*/
#define TOLTECS_SAVEGAME_VERSION 4
WARN_UNUSED_RESULT ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail) {
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 (!Graphics::loadThumbnail(*in, header.thumbnail, skipThumbnail)) {
return kRSHEIoError;
}
// Not used yet, reserved for future usage
header.gameID = in->readByte();
header.flags = in->readUint32LE();
if (header.version >= 1) {
header.saveDate = in->readUint32LE();
header.saveTime = in->readUint32LE();
header.playTime = in->readUint32LE();
} else {
header.saveDate = 0;
header.saveTime = 0;
header.playTime = 0;
}
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;
}
TimeDate curTime;
g_system->getTimeAndDate(curTime);
// Header start
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);
uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
uint32 saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
uint32 playTime = g_engine->getTotalPlayTime() / 1000;
out->writeUint32LE(saveDate);
out->writeUint32LE(saveTime);
out->writeUint32LE(playTime);
// Header end
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);
_sound->saveState(out);
_music->saveState(out);
out->finalize();
delete out;
}
void ToltecsEngine::loadgame(const char *filename) {
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
if (!in) {
warning("Can't open file '%s', game not loaded", filename);
return;
}
SaveHeader header;
kReadSaveHeaderError errorCode = readSaveHeader(in, header);
if (errorCode != kRSHENoError) {
warning("Error loading savegame '%s'", filename);
delete in;
return;
}
_sound->stopAll();
_music->stopSequence();
g_engine->setTotalPlayTime(header.playTime * 1000);
_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);
if (header.version >= 2)
_sound->loadState(in, header.version);
if (header.version >= 3)
_music->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, bool isAutosave) {
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];
Common::sprintf_s(extension, "%03d", num);
return target + "." + extension;
}
} // End of namespace Toltecs

795
engines/toltecs/screen.cpp Normal file
View File

@@ -0,0 +1,795 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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;
_shakeTime = 0;
_shakeCounterInit = 0;
_shakeCounter = 0;
_shakePos = 0;
_shakeTime = 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(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;
_shakeTime = 0;
_shakeCounterInit = shakeCounter;
_shakeCounter = shakeCounter;
_shakePos = 0;
}
void Screen::stopShakeScreen() {
_shakeActive = false;
_vm->_system->setShakePos(0, 0);
}
bool Screen::updateShakeScreen() {
// Assume shaking happens no more often than 50 times per second
if (_shakeActive && _vm->_system->getMillis() - _shakeTime >= 20) {
_shakeTime = _vm->_system->getMillis();
_shakeCounter--;
if (_shakeCounter == 0) {
_shakeCounter = _shakeCounterInit;
_shakePos ^= 8;
_vm->_system->setShakePos(0, _shakePos);
return true;
}
}
return false;
}
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 = READ_LE_UINT16(&spriteArray[0]);
//debug(0, "count = %d", count);
for (int16 index = 1; index <= count; index++) {
byte *spriteItem = data + READ_LE_UINT16(&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::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 - 1, 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 - 1, 0xF9, 0xFF, _fontResIndexArray[0], wrapState);
_guiRefresh = true;
}
void Screen::updateTalkText(int16 slotIndex, int16 slotOffset, bool alwaysDisplayed) {
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;
item->alwaysDisplayed = alwaysDisplayed;
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;
if (!_vm->_cfgText && !item->alwaysDisplayed)
return;
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;
}
}
}
bool Screen::isTalkTextActive(int16 slotIndex) {
for (int16 i = 0; i <= _talkTextItemNum; i++) {
if (_talkTextItems[i].slotIndex == slotIndex && _talkTextItems[i].duration > 0)
return true;
}
return false;
}
int16 Screen::getTalkTextDuration() {
return _talkTextItems[_talkTextItemNum].duration;
}
void Screen::finishTalkTextItem(int16 slotIndex) {
for (int16 i = 0; i <= _talkTextItemNum; i++) {
if (_talkTextItems[i].slotIndex == slotIndex) {
_talkTextItems[i].duration = 0;
}
}
}
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 - 1, _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->getPixels();
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();
_talkTextItems[i].alwaysDisplayed = false;
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

252
engines/toltecs/screen.h Normal file
View File

@@ -0,0 +1,252 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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];
bool alwaysDisplayed;
};
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();
bool updateShakeScreen();
// Sprite list
void addStaticSprite(byte *spriteItem);
void addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode);
// 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, bool alwaysDisplayed);
void addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item);
void addTalkTextItemsToRenderQueue();
int16 getTalkTextDuration();
bool isTalkTextActive(int16 slotIndex);
void finishTalkTextItem(int16 slotIndex);
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;
uint32 _shakeTime;
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 */

1077
engines/toltecs/script.cpp Normal file

File diff suppressed because it is too large Load Diff

183
engines/toltecs/script.h Normal file
View File

@@ -0,0 +1,183 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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; }
int16 getGameVar(uint variable);
void setGameVar(uint variable, int16 value);
void saveState(Common::WriteStream *out);
void loadState(Common::ReadStream *in);
void setSwitchLocalDataNear(bool newValue) { _switchLocalDataNear = newValue; }
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, *_codeStart;
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 */

412
engines/toltecs/segmap.cpp Normal file
View File

@@ -0,0 +1,412 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "toltecs/toltecs.h"
#include "toltecs/render.h"
#include "toltecs/segmap.h"
namespace Toltecs {
SegmentMap::SegmentMap(ToltecsEngine *vm) : _vm(vm) {
_maskRectData = NULL;
memset(_deadEndPathRects, 0, sizeof(_closedPathRects));
_closedPathRectsCount = 0;
_deadEndPathRectsCount = 0;
_pathNodesCount = 0;
}
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) {
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];
assert(_pathNodesCount >= 2);
_pathNodesCount -= 2;
currentRectIndex = _closedPathRects[--_closedPathRectsCount];
}
for (int16 i = 0; i < _pathNodesCount; i++) {
pointsArray[pointsCount++] = TO_LE_16(_pathNodes[i].y);
pointsArray[pointsCount++] = TO_LE_16(_pathNodes[i].x);
}
}
pointsArray[pointsCount++] = TO_LE_16(destY);
pointsArray[pointsCount++] = TO_LE_16(destX);
pointsArray[0] = 0;
pointsArray[1] = TO_LE_16(_pathNodesCount + 1);
}
debug(0, "SegmentMap::findPath() count = %d", FROM_LE_16(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 < FROM_LE_16(pointsArray[1]) * 2; i+=2) {
const int x = FROM_LE_16(pointsArray[3+i]);
const int y = FROM_LE_16(pointsArray[2+1]);
debug(0, "x = %d; y = %d", x, y);
Graphics::drawLine(sx, sy, x, y, 0xFF, plotProc, &ld);
sx = x;
sy = y;
}
#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->getPixels();
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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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;
PathPoint() : x(0), y(0) {}
};
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 */

220
engines/toltecs/sound.cpp Normal file
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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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++) {
clearChannel(i);
}
}
Sound::~Sound() {
}
void Sound::clearChannel(int channel) {
channels[channel].type = kChannelTypeEmpty;
channels[channel].resIndex = -1;
channels[channel].volume = 0;
channels[channel].panning = 0;
}
void Sound::playSpeech(int16 resIndex) {
debug(0, "playSpeech(%d)", resIndex);
if (_vm->_cfgVoices)
internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0);
}
void Sound::playSound(int16 resIndex, int16 type, int16 volume) {
debug(0, "playSound(%d, %d, %d)", resIndex, type, volume);
internalPlaySound(resIndex, type, volume, 0);
}
void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) {
debug(0, "playSoundAtPos(%d, %d, %d)", resIndex, x, y);
int16 volume = 50 + ABS(_vm->_segmap->getScalingAtPoint(x, y)) / 2;
int16 panning = 0, deltaX = 0;
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) {
// Change the game's sound volume (0 - 100) to Scummvm's scale (0 - 255)
volume = (volume == -1) ? 255 : volume * 255 / 100;
if (resIndex == -1) {
// Stop all sounds
_vm->_mixer->stopAll();
_vm->_screen->keepTalkTextItemsAlive();
for (int i = 0; i < kMaxChannels; i++) {
clearChannel(i);
}
} 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);
clearChannel(i);
}
}
} else {
if (type == kChannelTypeSpeech) {
// 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;
channels[freeChannel].volume = volume;
channels[freeChannel].panning = panning;
Audio::Mixer::SoundType soundType = getScummVMSoundType((SoundChannelType)type);
_vm->_mixer->playStream(soundType, &channels[freeChannel].handle,
stream, -1, volume, panning);
} // if (freeChannel >= 0)
} // resIndex
}
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();
clearChannel(i);
}
}
}
void Sound::stopAll() {
for (int i = 0; i < kMaxChannels; i++) {
_vm->_mixer->stopHandle(channels[i].handle);
_vm->_screen->keepTalkTextItemsAlive();
clearChannel(i);
}
}
void Sound::saveState(Common::WriteStream *out) {
for (int i = 0; i < kMaxChannels; i++) {
out->writeSint16LE(channels[i].type);
out->writeSint16LE(channels[i].resIndex);
out->writeSint16LE(channels[i].volume);
out->writeSint16LE(channels[i].panning);
}
}
void Sound::loadState(Common::ReadStream *in, int version) {
for (int i = 0; i < kMaxChannels; i++) {
channels[i].type = in->readSint16LE();
channels[i].resIndex = in->readSint16LE();
if (version < 4) {
channels[i].volume = (channels[i].type == kChannelTypeBackground) ? 50 : 100;
channels[i].panning = 0;
} else {
channels[i].volume = in->readSint16LE();
channels[i].panning = in->readSint16LE();
}
if (channels[i].type != kChannelTypeEmpty) {
Resource *soundResource = _vm->_res->load(channels[i].resIndex);
Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
Audio::makeRawStream(soundResource->data,
soundResource->size, 22050, Audio::FLAG_UNSIGNED,
DisposeAfterUse::NO),
channels[i].type == kChannelTypeBackground ? 0 : 1);
Audio::Mixer::SoundType soundType = getScummVMSoundType((SoundChannelType)channels[i].type);
_vm->_mixer->playStream(soundType, &channels[i].handle,
stream, -1, channels[i].volume, channels[i].panning);
}
}
}
Audio::Mixer::SoundType Sound::getScummVMSoundType(SoundChannelType type) const {
switch (type) {
case kChannelTypeBackground:
case kChannelTypeSfx:
return Audio::Mixer::kSFXSoundType;
case kChannelTypeSpeech:
return Audio::Mixer::kSpeechSoundType;
break;
default:
return Audio::Mixer::kSFXSoundType;
break;
}
}
} // End of namespace Toltecs

78
engines/toltecs/sound.h Normal file
View File

@@ -0,0 +1,78 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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;
int16 volume;
int16 panning;
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();
void saveState(Common::WriteStream *out);
void loadState(Common::ReadStream *in, int version);
protected:
ToltecsEngine *_vm;
SoundChannel channels[kMaxChannels];
void clearChannel(int channel);
void internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning);
Audio::Mixer::SoundType getScummVMSoundType(SoundChannelType type) const;
};
} // End of namespace Toltecs
#endif /* TOLTECS_SOUND_H */

510
engines/toltecs/sprite.cpp Normal file
View File

@@ -0,0 +1,510 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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) override {
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;
_xerror = 0;
}
SpriteReaderStatus readPacket(PixelPacket &packet) override {
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;
_sourcep = 0;
_xerror = 0;
}
SpriteReaderStatus readPacket(PixelPacket &packet) override {
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

701
engines/toltecs/toltecs.cpp Normal file
View File

@@ -0,0 +1,701 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/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/console.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) {
_rnd = new Common::RandomSource("toltecs");
_sceneResIndex = 0;
_sceneWidth = _sceneHeight = 0;
_counter01 = _counter02 = 0;
_movieSceneFlag = false;
_flag01 = 0;
_cameraX = _cameraY = _newCameraX = _newCameraY = 0;
_cameraHeight = 0;
_guiHeight = 26;
_doSpeech = true;
_doText = true;
_walkSpeedY = 5;
_walkSpeedX = 1;
_action = kActionNone;
_mouseX = 0;
_mouseY = 0;
_mouseDblClickTicks = 60;
_mouseWaitForRelease = false;
_mouseButton = 0;
_mouseDisabled = 0;
_leftButtonDown = false;
_rightButtonDown = false;
_arc = nullptr;
_res = nullptr;
_screen = nullptr;
_script = nullptr;
_anim = nullptr;
_palette = nullptr;
_segmap = nullptr;
_moviePlayer = nullptr;
_music = nullptr;
_menuSystem = nullptr;
_sound = nullptr;
_cfgText = ConfMan.getBool("subtitles");
_cfgVoices = !ConfMan.getBool("speech_mute");
_saveLoadRequested = 0;
_isSaveAllowed = true;
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "english.pdi");
}
ToltecsEngine::~ToltecsEngine() {
delete _rnd;
}
Common::Error ToltecsEngine::run() {
initGraphics(640, 400);
_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);
_music = new Music(_arc);
_menuSystem = new MenuSystem(this);
_sound = new Sound(this);
setDebugger(new Console(this));
_cfgText = ConfMan.getBool("subtitles");
_cfgVoices = !ConfMan.getBool("speech_mute");
bool mute = false;
if (ConfMan.hasKey("mute"))
mute = ConfMan.getBool("mute");
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mute ? 0 : ConfMan.getInt("speech_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, mute ? 0 : ConfMan.getInt("music_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mute ? 0 : ConfMan.getInt("sfx_volume"));
syncSoundSettings();
CursorMan.showMouse(true);
setupSysStrings();
#if 0
// Menu test
_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();
_menuSystem->run();
/*
while (1) {
//updateInput();
_menuSystem->update();
updateScreen();
}
*/
return Common::kNoError;
#endif
// Start main game loop
setTotalPlayTime(0);
_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();
_music->stopSequence();
_sound->stopAll();
delete _arc;
delete _res;
delete _screen;
delete _script;
delete _anim;
delete _palette;
delete _segmap;
delete _music;
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 {
_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(_screen->_frontScreen + _cameraHeight * 640,
640, 0, _cameraHeight, 640, _guiHeight);
_screen->_guiRefresh = false;
}
_system->updateScreen();
_system->delayMillis(10);
updateCamera();
}
void ToltecsEngine::updateInput() {
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
while (eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
_action = event.customType;
//debug("key: flags = %02X; keycode = %d", _keyState.flags, _keyState.keycode);
switch (event.customType) {
case kActionOpenSaveMenu:
showMenu(kMenuIdSave);
break;
case kActionOpenLoadMenu:
showMenu(kMenuIdLoad);
break;
case kActionSkipDialog:
// Skip current dialog line, if a dialog is active
if (_screen->getTalkTextDuration() > 0) {
_sound->stopSpeech();
_screen->finishTalkTextItems();
_action = kActionNone; // event consumed
}
break;
default:
break;
}
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
_action = kActionNone;
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;
} // switch
} // while
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();
_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;
// If there's another talk text at the requested slot and it's still
// active, don't overwrite it. Fixes bug #6224.
if (_screen->isTalkTextActive(slotIndex))
return;
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, false);
} else {
_screen->keepTalkTextItemsAlive();
}
} else {
_screen->updateTalkText(slotIndex, slotOffset, true);
}
}
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;
}
void ToltecsEngine::showMenu(MenuID menuId) {
_screen->loadMouseCursor(12);
_palette->loadAddPalette(9, 224);
_palette->setDeltaPalette(_palette->getMainPalette(), 7, 0, 31, 224);
_screen->finishTalkTextItems();
CursorMan.showMouse(true);
_menuSystem->run(menuId);
_action = kActionNone;
_script->setSwitchLocalDataNear(true);
}
void ToltecsEngine::syncSoundSettings() {
Engine::syncSoundSettings();
bool mute = false;
if (ConfMan.hasKey("mute"))
mute = ConfMan.getBool("mute");
_cfgVoicesVolume = (mute ? 0 : ConfMan.getInt("speech_volume")) * 20 / Audio::Mixer::kMaxChannelVolume;
_cfgMusicVolume = (mute ? 0 : ConfMan.getInt("music_volume")) * 20 / Audio::Mixer::kMaxChannelVolume;
_cfgSoundFXVolume = (mute ? 0 : ConfMan.getInt("sfx_volume")) * 20 / Audio::Mixer::kMaxChannelVolume;
}
} // End of namespace Toltecs

240
engines/toltecs/toltecs.h Normal file
View File

@@ -0,0 +1,240 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOLTECS_TOLTECS_H
#define TOLTECS_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"
#include "gui/debugger.h"
#include "toltecs/console.h"
namespace Toltecs {
struct ToltecsGameDescription;
class AnimationPlayer;
class ArchiveReader;
class Input;
class MenuSystem;
class MoviePlayer;
class Music;
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
};
enum TOLTECSAction {
kActionNone,
kActionSkipDialog,
kActionOpenSaveMenu,
kActionOpenLoadMenu,
kActionSkipMovie,
kActionMenuOpen,
kActionSkipRide,
};
enum MenuID {
kMenuIdNone,
kMenuIdMain,
kMenuIdSave,
kMenuIdLoad,
kMenuIdVolumes
};
class ToltecsEngine : public ::Engine {
protected:
Common::Error run() override;
// void shutdown();
public:
ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc);
~ToltecsEngine() override;
bool hasFeature(EngineFeature f) const override;
Common::RandomSource *_rnd;
const ToltecsGameDescription *_gameDescription;
uint32 getFeatures() const;
Common::Language getLanguage() const;
const Common::String& getTargetName() const { return _targetName; }
void syncSoundSettings() override;
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 showMenu(MenuID menuId);
void talk(int16 slotIndex, int16 slotOffset);
void walk(byte *walkData);
int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize,
byte *rectDataEnd);
int _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume;
bool _cfgText, _cfgVoices;
public:
AnimationPlayer *_anim;
ArchiveReader *_arc;
Input *_input;
MenuSystem *_menuSystem;
MoviePlayer *_moviePlayer;
Music *_music;
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::CustomEventType _action;
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;
uint32 saveDate;
uint32 saveTime;
uint32 playTime;
Graphics::Surface *thumbnail;
};
bool _isSaveAllowed;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { return _isSaveAllowed; }
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { return _isSaveAllowed; }
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &description, bool isAutosave = false) override;
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);
WARN_UNUSED_RESULT static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail = true);
};
} // End of namespace Toltecs
#endif /* TOLTECS_TOLTECS_H */