aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlyssa Milburn2011-07-17 23:03:51 +0200
committerAlyssa Milburn2011-07-17 23:03:51 +0200
commit1608d5a2afbb288a8c3a77b5b252eea402f8ad19 (patch)
tree02e987bd9d2fbf72c02321fefca225dec439fbb0
parent85df146ad42c79644d1916a9f2660603a8294d4a (diff)
downloadscummvm-rg350-1608d5a2afbb288a8c3a77b5b252eea402f8ad19.tar.gz
scummvm-rg350-1608d5a2afbb288a8c3a77b5b252eea402f8ad19.tar.bz2
scummvm-rg350-1608d5a2afbb288a8c3a77b5b252eea402f8ad19.zip
COMPOSER: Various additions/improvements to sprite/mouse code.
-rw-r--r--engines/composer/composer.cpp359
-rw-r--r--engines/composer/composer.h53
2 files changed, 356 insertions, 56 deletions
diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp
index accbe7dda5..a4ec9feb97 100644
--- a/engines/composer/composer.cpp
+++ b/engines/composer/composer.cpp
@@ -133,6 +133,17 @@ enum {
kFuncGetSpriteSize = 35029
};
+bool Sprite::contains(const Common::Point &pos) const {
+ Common::Point adjustedPos = pos - _pos;
+
+ if (adjustedPos.x < 0 || adjustedPos.x >= _surface.w)
+ return false;
+ if (adjustedPos.y < 0 || adjustedPos.y >= _surface.h)
+ return false;
+ byte *pixels = (byte *)_surface.pixels;
+ return (pixels[(_surface.h - adjustedPos.y - 1) * _surface.w + adjustedPos.x] == 0);
+}
+
// TODO: params: x, y, event param for done
Animation::Animation(Common::SeekableReadStream *stream, uint16 id, Common::Point basePos, uint32 eventParam)
: _stream(stream), _id(id), _basePos(basePos), _eventParam(eventParam) {
@@ -252,6 +263,68 @@ Common::SeekableReadStream *Pipe::getResource(uint32 tag, uint16 id, bool buffer
return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
}
+Button::Button(Common::SeekableReadStream *stream, uint16 id) {
+ _id = id;
+
+ _type = stream->readUint16LE();
+ _active = (_type & 0x8000) ? true : false;
+ _type &= 0xfff;
+ debug(9, "button: type %d, active %d", _type, _active);
+
+ _zorder = stream->readUint16LE();
+ _scriptId = stream->readUint16LE();
+ _scriptIdRollOn = stream->readUint16LE();
+ _scriptIdRollOff = stream->readUint16LE();
+
+ stream->skip(4);
+
+ uint16 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();
+ debug(9, "button: (%d, %d, %d, %d)", _rect.left, _rect.top, _rect.right, _rect.bottom);
+ break;
+ case kButtonSprites:
+ for (uint i = 0; i < size; i++) {
+ _spriteIds.push_back(stream->readSint16LE());
+ }
+ break;
+ default:
+ error("unknown button type %d", _type);
+ }
+
+ 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.height() / 2;
+ int16 b = _rect.width() / 2;
+ if (!a || !b)
+ return false;
+ Common::Point adjustedPos = pos - Common::Point(_rect.left + a, _rect.top + b);
+ return ((adjustedPos.x*adjustedPos.x)/a*2 + (adjustedPos.y*adjustedPos.y)/b*2 < 1);
+ }
+ case kButtonSprites:
+ return false;
+ default:
+ error("internal error (button type %d)", _type);
+ }
+}
+
void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventParam) {
// First, we check if this animation is already playing,
// and if it is, we sabotage that running one first.
@@ -493,22 +566,58 @@ void ComposerEngine::processAnimFrame() {
}
}
-void ComposerEngine::addSprite(uint16 id, uint16 animId, uint16 zorder, const Common::Point &pos) {
- // TODO: re-use old sprite
- removeSprite(id, animId);
+bool ComposerEngine::spriteVisible(uint16 id, uint16 animId) {
+ for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
+ if (i->_id != id)
+ continue;
+ if (i->_animId && animId && (i->_animId != animId))
+ continue;
+ return true;
+ }
+
+ return false;
+}
+void ComposerEngine::addSprite(uint16 id, uint16 animId, uint16 zorder, const Common::Point &pos) {
Sprite sprite;
- sprite.id = id;
- sprite.animId = animId;
- sprite.zorder = zorder;
- sprite.pos = pos;
- if (!initSprite(sprite)) {
- warning("ignoring addSprite on invalid sprite %d", id);
- return;
+ bool foundSprite = false;
+
+ // re-use old sprite, if any (the BMAP for this id might well have
+ // changed in the meantime, but the scripts depend on that!)
+ for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
+ if (i->_id != id)
+ continue;
+ if (i->_animId && animId && (i->_animId != animId))
+ continue;
+
+ // if the zordering is identical, modify it in-place
+ if (i->_zorder == zorder) {
+ i->_animId = animId;
+ i->_pos = pos;
+ return;
+ }
+
+ // otherwise, take a copy and remove it from the list
+ sprite = *i;
+ foundSprite = true;
+ _sprites.erase(i);
+ break;
+ }
+
+ sprite._animId = animId;
+ sprite._zorder = zorder;
+ sprite._pos = pos;
+
+ if (!foundSprite) {
+ sprite._id = id;
+ if (!initSprite(sprite)) {
+ warning("ignoring addSprite on invalid sprite %d", id);
+ return;
+ }
}
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
- if (sprite.zorder <= i->zorder)
+ if (sprite._zorder <= i->_zorder)
continue;
// insert *before* this sprite
_sprites.insert(i, sprite);
@@ -519,15 +628,49 @@ void ComposerEngine::addSprite(uint16 id, uint16 animId, uint16 zorder, const Co
void ComposerEngine::removeSprite(uint16 id, uint16 animId) {
for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
- if (i->id != id)
+ if (id && i->_id != id)
continue;
- if (i->animId && animId && (i->animId != animId))
+ if (i->_animId && animId && (i->_animId != animId))
continue;
- i->surface.free();
+ i->_surface.free();
i = _sprites.reverse_erase(i);
+ if (id)
+ break;
}
}
+const Sprite *ComposerEngine::getSpriteAtPos(const Common::Point &pos) {
+ for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
+ if (i->contains(pos))
+ return &(*i);
+ }
+
+ return NULL;
+}
+
+const Button *ComposerEngine::getButtonFor(const Sprite *sprite, const Common::Point &pos) {
+ for (Common::List<Button>::iterator i = _buttons.begin(); i != _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;
+}
+
ComposerEngine::ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
_rnd = new Common::RandomSource("composer");
_audioStream = NULL;
@@ -555,6 +698,11 @@ Common::Error ComposerEngine::run() {
_queuedScripts[i]._scriptId = 0;
}
+ _mouseVisible = true;
+ _mouseEnabled = false;
+ _mouseSpriteId = 0;
+ _lastButton = NULL;
+
_directoriesToStrip = 1;
if (!_bookIni.loadFromFile("book.ini")) {
_directoriesToStrip = 0;
@@ -570,6 +718,7 @@ Common::Error ComposerEngine::run() {
height = atoi(getStringFromConfig("Common", "Height").c_str());
initGraphics(width, height, true);
_surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ _needsUpdate = true;
loadLibrary(0);
@@ -607,19 +756,17 @@ Common::Error ComposerEngine::run() {
else
lastDrawTime += frameTime;
- for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
- drawSprite(*i);
- }
-
- _system->copyRectToScreen((byte *)_surface.pixels, _surface.pitch, 0, 0, _surface.w, _surface.h);
- _system->updateScreen();
+ redraw();
processAnimFrame();
+ } else if (_needsUpdate) {
+ redraw();
}
while (_eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
+ onMouseDown(event.mouse);
break;
case Common::EVENT_LBUTTONUP:
@@ -629,6 +776,7 @@ Common::Error ComposerEngine::run() {
break;
case Common::EVENT_MOUSEMOVE:
+ onMouseMove(event.mouse);
break;
case Common::EVENT_KEYDOWN:
@@ -650,7 +798,7 @@ Common::Error ComposerEngine::run() {
break;
}
- runEvent(5, event.kbd.keycode, 0, 0);
+ onKeyDown(event.kbd.keycode);
break;
case Common::EVENT_QUIT:
@@ -669,6 +817,79 @@ Common::Error ComposerEngine::run() {
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;
+
+ // TODO: other buttons?
+ uint16 buttonsDown = 1; // MK_LBUTTON
+
+ uint16 spriteId = sprite ? sprite->_id : 0;
+ runScript(button->_scriptId, 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)
+ runScript(_lastButton->_scriptIdRollOff, _lastButton->_id, buttonsDown, 0);
+ _lastButton = button;
+ if (_lastButton)
+ runScript(_lastButton->_scriptIdRollOn, _lastButton->_id, buttonsDown, 0);
+ }
+
+ if (_mouseSpriteId) {
+ addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset);
+ _needsUpdate = true;
+ }
+}
+
+void ComposerEngine::onKeyDown(uint16 keyCode) {
+ runEvent(5, keyCode, 0, 0);
+}
+
+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 (!_mouseSpriteId)
+ return;
+
+ if (visible && !_mouseVisible) {
+ _mouseVisible = true;
+ addSprite(_mouseSpriteId, 0, 0, _lastMousePos - _mouseOffset);
+ onMouseMove(_lastMousePos);
+ } else if (!visible && _mouseVisible) {
+ _mouseVisible = false;
+ removeSprite(_mouseSpriteId, 0);
+ }
+}
+
Common::String ComposerEngine::getStringFromConfig(const Common::String &section, const Common::String &key) {
Common::String value;
if (!_bookIni.getKey(key, section, value))
@@ -722,6 +943,14 @@ void ComposerEngine::loadLibrary(uint id) {
Common::hexdump(buf, stream->size());
delete stream;*/
+ 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);
+ _buttons.push_back(button);
+ }
+
// add background sprite, if it exists
if (hasResource(ID_BMAP, 1000))
addSprite(1000, 0, 0xffff, Common::Point());
@@ -732,6 +961,9 @@ void ComposerEngine::loadLibrary(uint id) {
// Run the startup script.
runScript(1000, 0, 0, 0);
+ _mouseEnabled = true;
+ onMouseMove(_lastMousePos);
+
runEvent(3, id, 0, 0);
}
@@ -750,9 +982,12 @@ void ComposerEngine::unloadLibrary(uint id) {
_pipes.clear();
for (Common::List<Sprite>::iterator j = _sprites.begin(); j != _sprites.end(); j++) {
- j->surface.free();
+ j->_surface.free();
}
_sprites.clear();
+ _buttons.clear();
+
+ _lastButton = NULL;
_mixer->stopAll();
_audioStream = NULL;
@@ -1218,28 +1453,40 @@ int16 ComposerEngine::scriptFuncCall(uint16 id, int16 param1, int16 param2, int1
_queuedScripts[param1]._scriptId = 0;
return 0;
case kFuncSetCursor:
- warning("ignoring kSetCursor(%d, %d, %d)", param1, param2, param3);
- // TODO: return old cursor
- return 0;
+ debug(3, "kSetCursor(%d, (%d, %d))", param1, param2, param3);
+ {
+ uint16 oldCursor = _mouseSpriteId;
+ setCursor(param1, Common::Point(param2, param3));
+ return oldCursor;
+ }
case kFuncGetCursor:
- warning("ignoring kFuncGetCursor()");
- // TODO: return cursor
- return 0;
+ debug(3, "kFuncGetCursor()");
+ return _mouseSpriteId;
case kFuncShowCursor:
- // TODO
- warning("ignoring kFuncShowCursor(%d)", param1);
+ debug(3, "kFuncShowCursor()");
+ setCursorVisible(true);
return 0;
case kFuncHideCursor:
- // TODO
- warning("ignoring kFuncHideCursor(%d)", param1);
+ debug(3, "kFuncHideCursor()");
+ setCursorVisible(false);
return 0;
case kFuncActivateButton:
- // TODO
- warning("ignoring kFuncActivateButton(%d)", param1);
+ debug(3, "kFuncActivateButton(%d)", param1);
+ for (Common::List<Button>::iterator i = _buttons.begin(); i != _buttons.end(); i++) {
+ if (i->_id != param1)
+ continue;
+ i->_active = true;
+ }
+ onMouseMove(_lastMousePos);
return 1;
case kFuncDeactivateButton:
- // TODO
- warning("ignoring kFuncDeactivateButton(%d)", param1);
+ debug(3, "kFuncDeactivateButton(%d)", param1);
+ for (Common::List<Button>::iterator i = _buttons.begin(); i != _buttons.end(); i++) {
+ if (i->_id != param1)
+ continue;
+ i->_active = false;
+ }
+ onMouseMove(_lastMousePos);
return 1;
case kFuncNewPage:
debug(3, "kFuncNewPage(%d, %d)", param1, param2);
@@ -1321,6 +1568,16 @@ int16 ComposerEngine::scriptFuncCall(uint16 id, int16 param1, int16 param2, int1
}
}
+void ComposerEngine::redraw() {
+ for (Common::List<Sprite>::iterator i = _sprites.begin(); i != _sprites.end(); i++) {
+ drawSprite(*i);
+ }
+
+ _system->copyRectToScreen((byte *)_surface.pixels, _surface.pitch, 0, 0, _surface.w, _surface.h);
+ _system->updateScreen();
+ _needsUpdate = false;
+}
+
void ComposerEngine::loadCTBL(uint id, uint fadePercent) {
Common::SeekableReadStream *stream = getResource(ID_CTBL, id);
@@ -1497,14 +1754,14 @@ void ComposerEngine::decompressBitmap(uint16 type, Common::SeekableReadStream *s
bool ComposerEngine::initSprite(Sprite &sprite) {
Common::SeekableReadStream *stream = NULL;
- if (hasResource(ID_BMAP, sprite.id))
- stream = getResource(ID_BMAP, sprite.id);
+ if (hasResource(ID_BMAP, sprite._id))
+ stream = getResource(ID_BMAP, sprite._id);
else
for (Common::List<Pipe *>::iterator k = _pipes.begin(); k != _pipes.end(); k++) {
Pipe *pipe = *k;
- if (!pipe->hasResource(ID_BMAP, sprite.id))
+ if (!pipe->hasResource(ID_BMAP, sprite._id))
continue;
- stream = pipe->getResource(ID_BMAP, sprite.id, false);
+ stream = pipe->getResource(ID_BMAP, sprite._id, false);
break;
}
@@ -1512,14 +1769,16 @@ bool ComposerEngine::initSprite(Sprite &sprite) {
return false;
uint16 type = stream->readUint16LE();
- uint16 height = stream->readUint16LE();
- uint16 width = stream->readUint16LE();
+ int16 height = stream->readSint16LE();
+ int16 width = stream->readSint16LE();
uint32 size = stream->readUint32LE();
debug(1, "loading BMAP: type %d, width %d, height %d, size %d", type, width, height, size);
- if (width && height) {
- sprite.surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
- decompressBitmap(type, stream, (byte *)sprite.surface.pixels, size, width, height);
+ if (width > 0 && height > 0) {
+ sprite._surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ decompressBitmap(type, stream, (byte *)sprite._surface.pixels, size, width, height);
+ } else {
+ warning("ignoring sprite with size %dx%d", width, height);
}
delete stream;
@@ -1527,15 +1786,15 @@ bool ComposerEngine::initSprite(Sprite &sprite) {
}
void ComposerEngine::drawSprite(const Sprite &sprite) {
- int x = sprite.pos.x;
- int y = sprite.pos.y;
+ int x = sprite._pos.x;
+ int y = sprite._pos.y;
// incoming data is BMP-style (bottom-up), so flip it
byte *pixels = (byte *)_surface.pixels;
- for (uint j = 0; j < sprite.surface.h; j++) {
- byte *in = (byte *)sprite.surface.pixels + (sprite.surface.h - j - 1) * sprite.surface.w;
+ for (int j = 0; j < sprite._surface.h; j++) {
+ byte *in = (byte *)sprite._surface.pixels + (sprite._surface.h - j - 1) * sprite._surface.w;
byte *out = pixels + ((j + y) * _surface.w) + x;
- for (uint i = 0; i < sprite.surface.w; i++)
+ for (uint i = 0; i < sprite._surface.w; i++)
if (in[i])
out[i] = in[i];
}
diff --git a/engines/composer/composer.h b/engines/composer/composer.h
index 8f22174592..4c87a6e6df 100644
--- a/engines/composer/composer.h
+++ b/engines/composer/composer.h
@@ -60,11 +60,13 @@ class Archive;
class ComposerEngine;
struct Sprite {
- uint16 id;
- uint16 animId;
- uint16 zorder;
- Common::Point pos;
- Graphics::Surface surface;
+ uint16 _id;
+ uint16 _animId;
+ uint16 _zorder;
+ Common::Point _pos;
+ Graphics::Surface _surface;
+
+ bool contains(const Common::Point &pos) const;
};
struct AnimationEntry {
@@ -122,9 +124,29 @@ protected:
uint32 _offset;
};
+enum {
+ kButtonRect = 0,
+ kButtonEllipse = 1,
+ kButtonSprites = 4
+};
+
class Button {
public:
- Button(ComposerEngine *vm, uint16 id);
+ Button() { }
+ Button(Common::SeekableReadStream *stream, uint16 id);
+
+ bool contains(const Common::Point &pos) const;
+
+ uint16 _id;
+ uint16 _type;
+ uint16 _zorder;
+ uint16 _scriptId;
+ uint16 _scriptIdRollOn;
+ uint16 _scriptIdRollOff;
+ bool _active;
+
+ Common::Rect _rect;
+ Common::Array<uint16> _spriteIds;
};
struct Library {
@@ -170,7 +192,9 @@ private:
Audio::SoundHandle _soundHandle;
Audio::QueuingAudioStream *_audioStream;
+ bool _needsUpdate;
Graphics::Surface _surface;
+ Common::List<Button> _buttons;
Common::List<Sprite> _sprites;
uint _directoriesToStrip;
@@ -185,6 +209,19 @@ private:
Common::List<Animation *> _anims;
Common::List<Pipe *> _pipes;
+ void onMouseDown(const Common::Point &pos);
+ void onMouseMove(const Common::Point &pos);
+ void onKeyDown(uint16 keyCode);
+ void setCursor(uint16 id, const Common::Point &offset);
+ void setCursorVisible(bool visible);
+
+ bool _mouseEnabled;
+ bool _mouseVisible;
+ Common::Point _lastMousePos;
+ const Button *_lastButton;
+ uint16 _mouseSpriteId;
+ Common::Point _mouseOffset;
+
Common::String getStringFromConfig(const Common::String &section, const Common::String &key);
Common::String getFilename(const Common::String &section, uint id);
void loadLibrary(uint id);
@@ -206,9 +243,13 @@ private:
void playWaveForAnim(uint16 id, bool bufferingOnly);
void processAnimFrame();
+ bool spriteVisible(uint16 id, uint16 animId);
void addSprite(uint16 id, uint16 animId, uint16 zorder, const Common::Point &pos);
void removeSprite(uint16 id, uint16 animId);
+ const Sprite *getSpriteAtPos(const Common::Point &pos);
+ const Button *getButtonFor(const Sprite *sprite, const Common::Point &pos);
+ void redraw();
void loadCTBL(uint id, uint fadePercent);
void decompressBitmap(uint16 type, Common::SeekableReadStream *stream, byte *buffer, uint32 size, uint width, uint height);
bool initSprite(Sprite &sprite);