mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-24 21:21:05 +00:00

This flag is removed for a few reasons: * Engines universally set this flag to true for widths > 320, which made it redundant everywhere; * This flag functioned primarily as a "force 1x scaler" flag, since its behaviour was almost completely undocumented and users would need to figure out that they'd need an explicit non-default scaler set to get a scaler to operate at widths > 320; * (Most importantly) engines should not be in the business of deciding how the backend may choose to render its virtual screen. The choice of rendering behaviour belongs to the user, and the backend, in that order. A nearby future commit restores the default1x scaler behaviour in the SDL backend code for the moment, but in the future it is my hope that there will be a better configuration UI to allow users to specify how they want scaling to work for high resolutions.
690 lines
19 KiB
C++
690 lines
19 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
#include "common/scummsys.h"
|
|
|
|
#include "common/config-manager.h"
|
|
#include "common/events.h"
|
|
#include "common/random.h"
|
|
#include "common/keyboard.h"
|
|
|
|
#include "graphics/cursorman.h"
|
|
#include "graphics/surface.h"
|
|
#include "graphics/pixelformat.h"
|
|
#include "graphics/wincursor.h"
|
|
|
|
#include "engines/util.h"
|
|
|
|
#include "composer/composer.h"
|
|
#include "composer/graphics.h"
|
|
#include "composer/resource.h"
|
|
#include "composer/console.h"
|
|
|
|
namespace Composer {
|
|
|
|
ComposerEngine::ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
|
|
_rnd = new Common::RandomSource("composer");
|
|
_audioStream = NULL;
|
|
_currSoundPriority = 0;
|
|
_currentTime = 0;
|
|
_lastTime = 0;
|
|
_needsUpdate = true;
|
|
_directoriesToStrip = 1;
|
|
_mouseVisible = true;
|
|
_mouseEnabled = false;
|
|
_mouseSpriteId = 0;
|
|
_lastButton = NULL;
|
|
_console = NULL;
|
|
}
|
|
|
|
ComposerEngine::~ComposerEngine() {
|
|
DebugMan.clearAllDebugChannels();
|
|
|
|
stopPipes();
|
|
for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++)
|
|
delete *i;
|
|
for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++)
|
|
delete *i;
|
|
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++)
|
|
delete i->_archive;
|
|
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++)
|
|
i->_surface.free();
|
|
|
|
delete _rnd;
|
|
delete _console;
|
|
}
|
|
|
|
Common::Error ComposerEngine::run() {
|
|
Common::Event event;
|
|
|
|
_vars.resize(1000);
|
|
for (uint i = 0; i < _vars.size(); i++)
|
|
_vars[i] = 0;
|
|
|
|
_queuedScripts.resize(10);
|
|
for (uint i = 0; i < _queuedScripts.size(); i++) {
|
|
_queuedScripts[i]._count = 0;
|
|
_queuedScripts[i]._scriptId = 0;
|
|
}
|
|
|
|
if (!_bookIni.loadFromFile("book.ini")) {
|
|
_directoriesToStrip = 0;
|
|
if (!_bookIni.loadFromFile("programs/book.ini")) {
|
|
// mac version?
|
|
if (!_bookIni.loadFromFile("Darby the Dragon.ini"))
|
|
if (!_bookIni.loadFromFile("Gregory.ini"))
|
|
error("failed to find book.ini");
|
|
}
|
|
}
|
|
|
|
uint width = 640;
|
|
if (_bookIni.hasKey("Width", "Common"))
|
|
width = atoi(getStringFromConfig("Common", "Width").c_str());
|
|
uint height = 480;
|
|
if (_bookIni.hasKey("Height", "Common"))
|
|
height = atoi(getStringFromConfig("Common", "Height").c_str());
|
|
initGraphics(width, height);
|
|
_screen.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
|
|
|
Graphics::Cursor *cursor = Graphics::makeDefaultWinCursor();
|
|
CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(),
|
|
cursor->getHotspotY(), cursor->getKeyColor());
|
|
CursorMan.replaceCursorPalette(cursor->getPalette(), cursor->getPaletteStartIndex(), cursor->getPaletteCount());
|
|
delete cursor;
|
|
|
|
_console = new Console(this);
|
|
|
|
loadLibrary(0);
|
|
|
|
uint fps = atoi(getStringFromConfig("Common", "FPS").c_str());
|
|
uint frameTime = 125; // Default to 125ms (1000/8)
|
|
if (fps != 0)
|
|
frameTime = 1000 / fps;
|
|
else
|
|
warning("FPS in book.ini is zero. Defaulting to 8...");
|
|
uint32 lastDrawTime = 0;
|
|
_lastSaveTime = _system->getMillis();
|
|
|
|
bool loadFromLauncher = ConfMan.hasKey("save_slot");
|
|
|
|
while (!shouldQuit()) {
|
|
for (uint i = 0; i < _pendingPageChanges.size(); i++) {
|
|
if (_pendingPageChanges[i]._remove)
|
|
unloadLibrary(_pendingPageChanges[i]._pageId);
|
|
else
|
|
loadLibrary(_pendingPageChanges[i]._pageId);
|
|
|
|
lastDrawTime = 0;
|
|
}
|
|
_pendingPageChanges.clear();
|
|
|
|
uint32 thisTime = _system->getMillis();
|
|
// maintain our own internal timing, since otherwise we get
|
|
// confused when starved of CPU (for example when the user
|
|
// is dragging the scummvm window around)
|
|
if (thisTime > _lastTime + frameTime)
|
|
_currentTime += frameTime;
|
|
else
|
|
_currentTime += thisTime - _lastTime;
|
|
_lastTime = thisTime;
|
|
|
|
for (uint i = 0; i < _queuedScripts.size(); i++) {
|
|
QueuedScript &script = _queuedScripts[i];
|
|
if (!script._count)
|
|
continue;
|
|
if (script._baseTime + script._duration > _currentTime)
|
|
continue;
|
|
if (script._count != 0xffffffff)
|
|
script._count--;
|
|
script._baseTime = _currentTime;
|
|
runScript(script._scriptId, i, 0, 0);
|
|
}
|
|
|
|
if (lastDrawTime + frameTime <= thisTime) {
|
|
// catch up if we're more than 2 frames behind
|
|
if (lastDrawTime + (frameTime * 2) <= thisTime)
|
|
lastDrawTime = thisTime;
|
|
else
|
|
lastDrawTime += frameTime;
|
|
|
|
tickOldScripts();
|
|
|
|
redraw();
|
|
|
|
processAnimFrame();
|
|
} else if (_needsUpdate) {
|
|
redraw();
|
|
}
|
|
if (loadFromLauncher) {
|
|
loadGameState(ConfMan.getInt("save_slot"));
|
|
loadFromLauncher = false;
|
|
}
|
|
if (shouldPerformAutoSave(_lastSaveTime))
|
|
saveGameState(0, "Autosave");
|
|
while (_eventMan->pollEvent(event)) {
|
|
switch (event.type) {
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
onMouseDown(event.mouse);
|
|
break;
|
|
|
|
case Common::EVENT_LBUTTONUP:
|
|
break;
|
|
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
break;
|
|
|
|
case Common::EVENT_MOUSEMOVE:
|
|
onMouseMove(event.mouse);
|
|
break;
|
|
|
|
case Common::EVENT_KEYDOWN:
|
|
switch (event.kbd.keycode) {
|
|
case Common::KEYCODE_d:
|
|
if (event.kbd.hasFlags(Common::KBD_CTRL)) {
|
|
// Start the debugger
|
|
getDebugger()->attach();
|
|
getDebugger()->onFrame();
|
|
}
|
|
break;
|
|
|
|
case Common::KEYCODE_q:
|
|
if (event.kbd.hasFlags(Common::KBD_CTRL))
|
|
quitGame();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
onKeyDown(event.kbd.keycode);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
_system->delayMillis(20);
|
|
}
|
|
|
|
_screen.free();
|
|
|
|
return Common::kNoError;
|
|
}
|
|
|
|
void ComposerEngine::onMouseDown(const Common::Point &pos) {
|
|
if (!_mouseEnabled || !_mouseVisible)
|
|
return;
|
|
|
|
const Sprite *sprite = getSpriteAtPos(pos);
|
|
const Button *button = getButtonFor(sprite, pos);
|
|
if (!button)
|
|
return;
|
|
|
|
debug(3, "mouseDown on button id %d", button->_id);
|
|
|
|
// TODO: other buttons?
|
|
uint16 buttonsDown = 1; // MK_LBUTTON
|
|
|
|
uint16 spriteId = sprite ? sprite->_id : 0;
|
|
runScript(button->_scriptId, (getGameType() == GType_ComposerV1) ? 0 : button->_id, buttonsDown, spriteId);
|
|
}
|
|
|
|
void ComposerEngine::onMouseMove(const Common::Point &pos) {
|
|
_lastMousePos = pos;
|
|
|
|
if (!_mouseEnabled || !_mouseVisible)
|
|
return;
|
|
|
|
// TODO: do we need to keep track of this?
|
|
uint buttonsDown = 0;
|
|
|
|
const Sprite *sprite = getSpriteAtPos(pos);
|
|
const Button *button = getButtonFor(sprite, pos);
|
|
if (_lastButton != button) {
|
|
if (_lastButton && _lastButton->_scriptIdRollOff)
|
|
runScript(_lastButton->_scriptIdRollOff, (getGameType() == GType_ComposerV1) ? 0 : _lastButton->_id, buttonsDown, 0);
|
|
_lastButton = button;
|
|
if (_lastButton && _lastButton->_scriptIdRollOn)
|
|
runScript(_lastButton->_scriptIdRollOn, (getGameType() == GType_ComposerV1) ? 0 : _lastButton->_id, buttonsDown, 0);
|
|
}
|
|
|
|
if (_mouseSpriteId) {
|
|
addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset);
|
|
}
|
|
_needsUpdate = true;
|
|
}
|
|
|
|
void ComposerEngine::onKeyDown(uint16 keyCode) {
|
|
runEvent(kEventKeyDown, keyCode, 0, 0);
|
|
runEvent(kEventChar, keyCode, 0, 0);
|
|
|
|
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) {
|
|
for (Common::List<KeyboardHandler>::iterator j = i->_keyboardHandlers.begin(); j != i->_keyboardHandlers.end(); j++) {
|
|
const KeyboardHandler &handler = *j;
|
|
if (keyCode != handler.keyId)
|
|
continue;
|
|
|
|
int modifiers = g_system->getEventManager()->getModifierState();
|
|
switch (handler.modifierId) {
|
|
case 0x10: // shift
|
|
if (!(modifiers & Common::KBD_SHIFT))
|
|
continue;
|
|
break;
|
|
case 0x11: // control
|
|
if (!(modifiers & Common::KBD_CTRL))
|
|
continue;
|
|
break;
|
|
case 0:
|
|
break;
|
|
default:
|
|
warning("unknown keyb modifier %d", handler.modifierId);
|
|
continue;
|
|
}
|
|
|
|
runScript(handler.scriptId);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ComposerEngine::setCursor(uint16 id, const Common::Point &offset) {
|
|
_mouseOffset = offset;
|
|
if (_mouseSpriteId == id)
|
|
return;
|
|
|
|
if (_mouseSpriteId && _mouseVisible) {
|
|
removeSprite(_mouseSpriteId, 0);
|
|
}
|
|
_mouseSpriteId = id;
|
|
if (_mouseSpriteId && _mouseVisible) {
|
|
addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset);
|
|
}
|
|
}
|
|
|
|
void ComposerEngine::setCursorVisible(bool visible) {
|
|
if (visible && !_mouseVisible) {
|
|
_mouseVisible = true;
|
|
if (_mouseSpriteId)
|
|
addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset);
|
|
else
|
|
CursorMan.showMouse(true);
|
|
onMouseMove(_lastMousePos);
|
|
} else if (!visible && _mouseVisible) {
|
|
_mouseVisible = false;
|
|
if (_mouseSpriteId)
|
|
removeSprite(_mouseSpriteId, 0);
|
|
else
|
|
CursorMan.showMouse(false);
|
|
}
|
|
}
|
|
|
|
Common::String ComposerEngine::getStringFromConfig(const Common::String §ion, const Common::String &key) {
|
|
Common::String value;
|
|
if (!_bookIni.getKey(key, section, value))
|
|
error("failed to find key '%s' in section '%s' of book config", key.c_str(), section.c_str());
|
|
return value;
|
|
}
|
|
|
|
Common::String ComposerEngine::getFilename(const Common::String §ion, uint id) {
|
|
Common::String key = Common::String::format("%d", id);
|
|
Common::String filename = getStringFromConfig(section, key);
|
|
|
|
return mangleFilename(filename);
|
|
}
|
|
|
|
Common::String ComposerEngine::mangleFilename(Common::String filename) {
|
|
while (filename.size() && (filename[0] == '~' || filename[0] == ':' || filename[0] == '\\'))
|
|
filename = filename.c_str() + 1;
|
|
|
|
uint slashesToStrip = _directoriesToStrip;
|
|
while (slashesToStrip--) {
|
|
for (uint i = 0; i < filename.size(); i++) {
|
|
if (filename[i] != '\\' && filename[i] != ':')
|
|
continue;
|
|
filename = filename.c_str() + i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Common::String outFilename;
|
|
for (uint i = 0; i < filename.size(); i++) {
|
|
if (filename[i] == '\\' || filename[i] == ':')
|
|
outFilename += '/';
|
|
else
|
|
outFilename += filename[i];
|
|
}
|
|
return outFilename;
|
|
}
|
|
|
|
void ComposerEngine::loadLibrary(uint id) {
|
|
if (getGameType() == GType_ComposerV1 && !_libraries.empty()) {
|
|
// kill the previous page, starting with any scripts running on it
|
|
|
|
for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++)
|
|
delete *i;
|
|
_oldScripts.clear();
|
|
|
|
Library *library = &_libraries.front();
|
|
unloadLibrary(library->_id);
|
|
}
|
|
|
|
Common::String filename;
|
|
Common::String oldGroup = _bookGroup;
|
|
if (getGameType() == GType_ComposerV1) {
|
|
if (!id || _bookGroup.empty())
|
|
filename = getStringFromConfig("Common", "StartPage");
|
|
else
|
|
filename = getStringFromConfig(_bookGroup, Common::String::format("%d", id));
|
|
filename = mangleFilename(filename);
|
|
|
|
// bookGroup is the basename of the path.
|
|
// TODO: tidy this up.
|
|
_bookGroup.clear();
|
|
for (uint i = 0; i < filename.size(); i++) {
|
|
if (filename[i] == '~' || filename[i] == '/' || filename[i] == ':')
|
|
continue;
|
|
for (uint j = 0; j < filename.size(); j++) {
|
|
if (filename[j] == '/') {
|
|
_bookGroup.clear();
|
|
continue;
|
|
}
|
|
if (filename[j] == '.')
|
|
break;
|
|
_bookGroup += filename[j];
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
if (!id)
|
|
id = atoi(getStringFromConfig("Common", "StartUp").c_str());
|
|
filename = getFilename("Libs", id);
|
|
}
|
|
|
|
Library library;
|
|
|
|
library._id = id;
|
|
library._group = oldGroup;
|
|
library._archive = new ComposerArchive();
|
|
if (!library._archive->openFile(filename))
|
|
error("failed to open '%s'", filename.c_str());
|
|
_libraries.push_front(library);
|
|
|
|
Library &newLib = _libraries.front();
|
|
|
|
Common::Array<uint16> buttonResources = library._archive->getResourceIDList(ID_BUTN);
|
|
for (uint i = 0; i < buttonResources.size(); i++) {
|
|
uint16 buttonId = buttonResources[i];
|
|
Common::SeekableReadStream *stream = library._archive->getResource(ID_BUTN, buttonId);
|
|
Button button(stream, buttonId, getGameType());
|
|
|
|
bool inserted = false;
|
|
for (Common::List<Button>::iterator b = newLib._buttons.begin(); b != newLib._buttons.end(); b++) {
|
|
if (button._zorder < b->_zorder)
|
|
continue;
|
|
newLib._buttons.insert(b, button);
|
|
inserted = true;
|
|
break;
|
|
}
|
|
if (!inserted)
|
|
newLib._buttons.push_back(button);
|
|
}
|
|
|
|
Common::Array<uint16> ambientResources = library._archive->getResourceIDList(ID_AMBI);
|
|
for (uint i = 0; i < ambientResources.size(); i++) {
|
|
Common::SeekableReadStream *stream = library._archive->getResource(ID_AMBI, ambientResources[i]);
|
|
Button button(stream);
|
|
newLib._buttons.insert(newLib._buttons.begin(), button);
|
|
}
|
|
|
|
Common::Array<uint16> accelResources = library._archive->getResourceIDList(ID_ACEL);
|
|
for (uint i = 0; i < accelResources.size(); i++) {
|
|
Common::SeekableReadStream *stream = library._archive->getResource(ID_ACEL, accelResources[i]);
|
|
KeyboardHandler handler;
|
|
handler.keyId = stream->readUint16LE();
|
|
handler.modifierId = stream->readUint16LE();
|
|
handler.scriptId = stream->readUint16LE();
|
|
newLib._keyboardHandlers.push_back(handler);
|
|
}
|
|
|
|
Common::Array<uint16> randResources = library._archive->getResourceIDList(ID_RAND);
|
|
for (uint i = 0; i < randResources.size(); i++) {
|
|
Common::SeekableReadStream *stream = library._archive->getResource(ID_RAND, randResources[i]);
|
|
Common::Array<RandomEvent> &events = _randomEvents[randResources[i]];
|
|
uint16 count = stream->readUint16LE();
|
|
for (uint j = 0; j < count; j++) {
|
|
RandomEvent random;
|
|
random.scriptId = stream->readUint16LE();
|
|
random.weight = stream->readUint16LE();
|
|
events.push_back(random);
|
|
}
|
|
delete stream;
|
|
}
|
|
|
|
// add background sprite, if it exists
|
|
if (hasResource(ID_BMAP, 1000))
|
|
setBackground(1000);
|
|
|
|
// TODO: better CTBL logic
|
|
loadCTBL(1000, 100);
|
|
|
|
// Run the startup script.
|
|
runScript(1000, 0, 0, 0);
|
|
|
|
_mouseEnabled = true;
|
|
onMouseMove(_lastMousePos);
|
|
|
|
runEvent(kEventLoad, id, 0, 0);
|
|
}
|
|
|
|
void ComposerEngine::unloadLibrary(uint id) {
|
|
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++) {
|
|
if (i->_id != id)
|
|
continue;
|
|
|
|
for (Common::List<Animation *>::iterator j = _anims.begin(); j != _anims.end(); j++) {
|
|
delete *j;
|
|
}
|
|
_anims.clear();
|
|
stopPipes();
|
|
|
|
_randomEvents.clear();
|
|
|
|
for (Common::List<Sprite>::iterator j = _sprites.begin(); j != _sprites.end(); j++) {
|
|
j->_surface.free();
|
|
}
|
|
_sprites.clear();
|
|
i->_buttons.clear();
|
|
|
|
_lastButton = NULL;
|
|
|
|
_mixer->stopAll();
|
|
_audioStream = NULL;
|
|
|
|
for (uint j = 0; j < _queuedScripts.size(); j++) {
|
|
_queuedScripts[j]._count = 0;
|
|
_queuedScripts[j]._scriptId = 0;
|
|
}
|
|
|
|
delete i->_archive;
|
|
_libraries.erase(i);
|
|
|
|
runEvent(kEventUnload, id, 0, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
error("tried to unload library %d, which isn't loaded", id);
|
|
}
|
|
|
|
bool ComposerEngine::hasResource(uint32 tag, uint16 id) {
|
|
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++)
|
|
if (i->_archive->hasResource(tag, id))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
Common::SeekableReadStream *ComposerEngine::getResource(uint32 tag, uint16 id) {
|
|
for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++)
|
|
if (i->_archive->hasResource(tag, id))
|
|
return i->_archive->getResource(tag, id);
|
|
|
|
error("No loaded library contains '%s' %04x", tag2str(tag), id);
|
|
}
|
|
|
|
Button::Button(Common::SeekableReadStream *stream, uint16 id, uint gameType) {
|
|
_id = id;
|
|
|
|
_type = stream->readUint16LE();
|
|
_active = (_type & 0x8000) ? true : false;
|
|
bool hasRollover = (gameType == GType_ComposerV1) && (_type & 0x4000);
|
|
_type &= 0xfff;
|
|
debug(9, "button %d: type %d, active %d", id, _type, _active);
|
|
|
|
uint16 size = 4;
|
|
if (gameType == GType_ComposerV1) {
|
|
stream->skip(2);
|
|
|
|
_zorder = 0;
|
|
_scriptId = stream->readUint16LE();
|
|
_scriptIdRollOn = 0;
|
|
_scriptIdRollOff = 0;
|
|
} else {
|
|
_zorder = stream->readUint16LE();
|
|
_scriptId = stream->readUint16LE();
|
|
_scriptIdRollOn = stream->readUint16LE();
|
|
_scriptIdRollOff = stream->readUint16LE();
|
|
|
|
stream->skip(4);
|
|
|
|
size = stream->readUint16LE();
|
|
}
|
|
|
|
switch (_type) {
|
|
case kButtonRect:
|
|
case kButtonEllipse:
|
|
if (size != 4)
|
|
error("button %d of type %d had %d points, not 4", id, _type, size);
|
|
_rect.left = stream->readSint16LE();
|
|
_rect.top = stream->readSint16LE();
|
|
_rect.right = stream->readSint16LE();
|
|
_rect.bottom = stream->readSint16LE();
|
|
break;
|
|
case kButtonSprites:
|
|
if (gameType == GType_ComposerV1)
|
|
error("encountered kButtonSprites in V1 data");
|
|
for (uint i = 0; i < size; i++) {
|
|
_spriteIds.push_back(stream->readUint16LE());
|
|
}
|
|
break;
|
|
default:
|
|
error("unknown button type %d", _type);
|
|
}
|
|
|
|
if (hasRollover) {
|
|
_scriptIdRollOn = stream->readUint16LE();
|
|
_scriptIdRollOff = stream->readUint16LE();
|
|
}
|
|
|
|
delete stream;
|
|
}
|
|
|
|
// AMBI-style button
|
|
Button::Button(Common::SeekableReadStream *stream) {
|
|
_id = 0;
|
|
_zorder = 0;
|
|
_active = true;
|
|
_type = kButtonSprites;
|
|
_scriptIdRollOn = 0;
|
|
_scriptIdRollOff = 0;
|
|
|
|
_scriptId = stream->readUint16LE();
|
|
|
|
uint16 count = stream->readUint16LE();
|
|
for (uint j = 0; j < count; j++) {
|
|
uint16 spriteId = stream->readUint16LE();
|
|
_spriteIds.push_back(spriteId);
|
|
}
|
|
|
|
delete stream;
|
|
}
|
|
|
|
bool Button::contains(const Common::Point &pos) const {
|
|
switch (_type) {
|
|
case kButtonRect:
|
|
return _rect.contains(pos);
|
|
case kButtonEllipse:
|
|
if (!_rect.contains(pos))
|
|
return false;
|
|
{
|
|
int16 a = _rect.width() / 2;
|
|
int16 b = _rect.height() / 2;
|
|
if (!a || !b)
|
|
return false;
|
|
Common::Point adjustedPos = pos - Common::Point(_rect.left + a, _rect.top + b);
|
|
return ((adjustedPos.x*adjustedPos.x)/(a*a) + (adjustedPos.y*adjustedPos.y)/(b*b) < 1);
|
|
}
|
|
case kButtonSprites:
|
|
return false;
|
|
default:
|
|
error("internal error (button type %d)", _type);
|
|
}
|
|
}
|
|
|
|
const Button *ComposerEngine::getButtonFor(const Sprite *sprite, const Common::Point &pos) {
|
|
for (Common::List<Library>::iterator l = _libraries.begin(); l != _libraries.end(); l++) {
|
|
for (Common::List<Button>::iterator i = l->_buttons.reverse_begin(); i != l->_buttons.end(); --i) {
|
|
if (!i->_active)
|
|
continue;
|
|
|
|
if (i->_spriteIds.empty()) {
|
|
if (i->contains(pos))
|
|
return &(*i);
|
|
continue;
|
|
}
|
|
|
|
if (!sprite)
|
|
continue;
|
|
|
|
for (uint j = 0; j < i->_spriteIds.size(); j++) {
|
|
if (i->_spriteIds[j] == sprite->_id)
|
|
return &(*i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void ComposerEngine::setButtonActive(uint16 id, bool active) {
|
|
for (Common::List<Library>::iterator l = _libraries.begin(); l != _libraries.end(); l++) {
|
|
for (Common::List<Button>::iterator i = l->_buttons.begin(); i != l->_buttons.end(); i++) {
|
|
if (i->_id != id)
|
|
continue;
|
|
i->_active = active;
|
|
}
|
|
}
|
|
|
|
onMouseMove(_lastMousePos);
|
|
}
|
|
|
|
} // End of namespace Composer
|