Initial commit
This commit is contained in:
567
engines/pegasus/elements.cpp
Normal file
567
engines/pegasus/elements.cpp
Normal file
@@ -0,0 +1,567 @@
|
||||
/* 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.
|
||||
*
|
||||
* Additional copyright for this file:
|
||||
* Copyright (C) 1995-1997 Presto Studios, Inc.
|
||||
*
|
||||
* 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/macresman.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "pegasus/elements.h"
|
||||
#include "pegasus/graphics.h"
|
||||
#include "pegasus/surface.h"
|
||||
|
||||
namespace Pegasus {
|
||||
|
||||
DisplayElement::DisplayElement(const DisplayElementID id) : IDObject(id) {
|
||||
_elementIsDisplaying = false;
|
||||
_elementIsVisible = false;
|
||||
_elementOrder = 0;
|
||||
_triggeredElement = this;
|
||||
_nextElement = nullptr;
|
||||
}
|
||||
|
||||
DisplayElement::~DisplayElement() {
|
||||
if (isDisplaying())
|
||||
g_vm->_gfx->removeDisplayElement(this);
|
||||
}
|
||||
|
||||
void DisplayElement::setDisplayOrder(const DisplayOrder order) {
|
||||
if (_elementOrder != order) {
|
||||
_elementOrder = order;
|
||||
if (isDisplaying()) {
|
||||
g_vm->_gfx->removeDisplayElement(this);
|
||||
g_vm->_gfx->addDisplayElement(this);
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayElement::startDisplaying() {
|
||||
if (!isDisplaying()) {
|
||||
g_vm->_gfx->addDisplayElement(this);
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayElement::stopDisplaying() {
|
||||
if (isDisplaying()) {
|
||||
triggerRedraw();
|
||||
g_vm->_gfx->removeDisplayElement(this);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayElement::setBounds(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) {
|
||||
setBounds(Common::Rect(left, top, right, bottom));
|
||||
}
|
||||
|
||||
void DisplayElement::getBounds(Common::Rect &r) const {
|
||||
r = _bounds;
|
||||
}
|
||||
|
||||
void DisplayElement::sizeElement(const CoordType h, const CoordType v) {
|
||||
Common::Rect newBounds = _bounds;
|
||||
newBounds.right = _bounds.left + h;
|
||||
newBounds.bottom = _bounds.top + v;
|
||||
setBounds(newBounds);
|
||||
}
|
||||
|
||||
void DisplayElement::moveElementTo(const CoordType h, const CoordType v) {
|
||||
Common::Rect newBounds = _bounds;
|
||||
newBounds.moveTo(h, v);
|
||||
setBounds(newBounds);
|
||||
}
|
||||
|
||||
void DisplayElement::moveElement(const CoordType dh, const CoordType dv) {
|
||||
Common::Rect newBounds = _bounds;
|
||||
newBounds.translate(dh, dv);
|
||||
setBounds(newBounds);
|
||||
}
|
||||
|
||||
void DisplayElement::getLocation(CoordType &h, CoordType &v) const {
|
||||
h = _bounds.left;
|
||||
v = _bounds.top;
|
||||
}
|
||||
|
||||
void DisplayElement::centerElementAt(const CoordType h, const CoordType v) {
|
||||
Common::Rect newBounds = _bounds;
|
||||
newBounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2));
|
||||
setBounds(newBounds);
|
||||
}
|
||||
|
||||
void DisplayElement::getCenter(CoordType &h, CoordType &v) const {
|
||||
h = (_bounds.left + _bounds.right) / 2;
|
||||
v = (_bounds.top + _bounds.bottom) / 2;
|
||||
}
|
||||
|
||||
void DisplayElement::setBounds(const Common::Rect &r) {
|
||||
if (r != _bounds) {
|
||||
triggerRedraw();
|
||||
_bounds = r;
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayElement::hide() {
|
||||
if (_elementIsVisible) {
|
||||
triggerRedraw();
|
||||
_elementIsVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayElement::show() {
|
||||
if (!_elementIsVisible) {
|
||||
_elementIsVisible = true;
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
// Only invalidates this element's bounding rectangle if all these conditions are true:
|
||||
// -- The triggered element is this element.
|
||||
// -- The element is displaying on the display list.
|
||||
// -- The element is visible.
|
||||
// -- The element is part of the active layer OR is one of the reserved items.
|
||||
void DisplayElement::triggerRedraw() {
|
||||
GraphicsManager *gfx = g_vm->_gfx;
|
||||
|
||||
if (_triggeredElement == this) {
|
||||
if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer()))
|
||||
gfx->invalRect(_bounds);
|
||||
} else {
|
||||
_triggeredElement->triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayElement::setTriggeredElement(DisplayElement *element) {
|
||||
if (element)
|
||||
_triggeredElement = element;
|
||||
else
|
||||
_triggeredElement = this;
|
||||
}
|
||||
|
||||
bool DisplayElement::validToDraw(DisplayOrder backLayer, DisplayOrder frontLayer) {
|
||||
return isDisplaying() && _elementIsVisible &&
|
||||
(getObjectID() <= kHighestReservedElementID ||
|
||||
(getDisplayOrder() >= backLayer &&
|
||||
getDisplayOrder() <= frontLayer));
|
||||
}
|
||||
|
||||
DropHighlight::DropHighlight(const DisplayElementID id) : DisplayElement(id) {
|
||||
_highlightColor = 0;
|
||||
_thickness = 2;
|
||||
_cornerDiameter = 0;
|
||||
}
|
||||
|
||||
void DropHighlight::draw(const Common::Rect &) {
|
||||
Graphics::Surface *screen = g_vm->_gfx->getWorkArea();
|
||||
|
||||
// Since this is only used in two different ways, I'm only
|
||||
// going to implement it in those two ways. Deal with it.
|
||||
|
||||
Common::Rect rect = _bounds;
|
||||
rect.grow(-_thickness);
|
||||
screen->frameRect(rect, _highlightColor);
|
||||
rect.grow(1);
|
||||
screen->frameRect(rect, _highlightColor);
|
||||
|
||||
if (_cornerDiameter == 8 && _thickness == 4) {
|
||||
rect.grow(1);
|
||||
screen->frameRect(rect, _highlightColor);
|
||||
screen->hLine(rect.left + 1, rect.top - 1, rect.right - 2, _highlightColor);
|
||||
screen->hLine(rect.left + 1, rect.bottom, rect.right - 2, _highlightColor);
|
||||
screen->vLine(rect.left - 1, rect.top + 1, rect.bottom - 2, _highlightColor);
|
||||
screen->vLine(rect.right, rect.top + 1, rect.bottom - 2, _highlightColor);
|
||||
}
|
||||
}
|
||||
|
||||
IdlerAnimation::IdlerAnimation(const DisplayElementID id) : Animation(id) {
|
||||
_lastTime = 0xffffffff;
|
||||
}
|
||||
|
||||
void IdlerAnimation::startDisplaying() {
|
||||
if (!isDisplaying()) {
|
||||
Animation::startDisplaying();
|
||||
startIdling();
|
||||
}
|
||||
}
|
||||
|
||||
void IdlerAnimation::stopDisplaying() {
|
||||
if (isDisplaying()) {
|
||||
Animation::stopDisplaying();
|
||||
stopIdling();
|
||||
}
|
||||
}
|
||||
|
||||
void IdlerAnimation::useIdleTime() {
|
||||
uint32 currentTime = getTime();
|
||||
|
||||
if (currentTime != _lastTime) {
|
||||
_lastTime = currentTime;
|
||||
timeChanged(_lastTime);
|
||||
}
|
||||
}
|
||||
|
||||
void IdlerAnimation::timeChanged(const TimeValue) {
|
||||
triggerRedraw();
|
||||
}
|
||||
|
||||
FrameSequence::FrameSequence(const DisplayElementID id) : IdlerAnimation(id) {
|
||||
_duration = 0;
|
||||
_currentFrameNum = 0;
|
||||
_resFork = new Common::MacResManager();
|
||||
_numFrames = 0;
|
||||
}
|
||||
|
||||
FrameSequence::~FrameSequence() {
|
||||
delete _resFork;
|
||||
}
|
||||
|
||||
void FrameSequence::useFileName(const Common::Path &fileName) {
|
||||
_resFork->open(fileName);
|
||||
}
|
||||
|
||||
void FrameSequence::openFrameSequence() {
|
||||
if (!_resFork->hasResFork())
|
||||
return;
|
||||
|
||||
Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80);
|
||||
|
||||
if (!res)
|
||||
return;
|
||||
|
||||
uint32 scale = res->readUint32BE();
|
||||
_bounds.top = res->readUint16BE();
|
||||
_bounds.left = res->readUint16BE();
|
||||
_bounds.bottom = res->readUint16BE();
|
||||
_bounds.right = res->readUint16BE();
|
||||
_numFrames = res->readUint16BE();
|
||||
_duration = 0;
|
||||
|
||||
_frameTimes.clear();
|
||||
for (uint32 i = 0; i < _numFrames; i++) {
|
||||
TimeValue time = res->readUint32BE();
|
||||
_frameTimes.push_back(_duration);
|
||||
_duration += time;
|
||||
}
|
||||
|
||||
setScale(scale);
|
||||
setSegment(0, _duration);
|
||||
setTime(0);
|
||||
_currentFrameNum = 0;
|
||||
newFrame(_currentFrameNum);
|
||||
triggerRedraw();
|
||||
|
||||
delete res;
|
||||
}
|
||||
|
||||
void FrameSequence::closeFrameSequence() {
|
||||
stop();
|
||||
_resFork->close();
|
||||
_duration = 0;
|
||||
_numFrames = 0;
|
||||
_frameTimes.clear();
|
||||
}
|
||||
|
||||
void FrameSequence::timeChanged(const TimeValue time) {
|
||||
int16 frameNum = 0;
|
||||
for (int16 i = _numFrames - 1; i >= 0; i--) {
|
||||
if (_frameTimes[i] < time) {
|
||||
frameNum = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frameNum != _currentFrameNum) {
|
||||
_currentFrameNum = frameNum;
|
||||
newFrame(_currentFrameNum);
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
void FrameSequence::setFrameNum(const int16 frameNum) {
|
||||
int16 f = CLIP<int>(frameNum, 0, _numFrames);
|
||||
|
||||
if (_currentFrameNum != f) {
|
||||
_currentFrameNum = f;
|
||||
setTime(_frameTimes[f]);
|
||||
newFrame(f);
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
bool FrameSequence::isSequenceOpen() const {
|
||||
return _numFrames != 0;
|
||||
}
|
||||
|
||||
Sprite::Sprite(const DisplayElementID id) : DisplayElement(id) {
|
||||
_numFrames = 0;
|
||||
_currentFrameNum = 0xffffffff;
|
||||
_currentFrame = nullptr;
|
||||
}
|
||||
|
||||
Sprite::~Sprite() {
|
||||
discardFrames();
|
||||
}
|
||||
|
||||
void Sprite::discardFrames() {
|
||||
if (!_frameArray.empty()) {
|
||||
for (uint32 i = 0; i < _numFrames; i++) {
|
||||
SpriteFrame *frame = _frameArray[i].frame;
|
||||
frame->_referenceCount--;
|
||||
if (frame->_referenceCount == 0)
|
||||
delete frame;
|
||||
}
|
||||
|
||||
_frameArray.clear();
|
||||
_numFrames = 0;
|
||||
_currentFrame = nullptr;
|
||||
_currentFrameNum = 0xffffffff;
|
||||
setBounds(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::addPICTResourceFrame(const ResIDType pictID, bool transparent, const CoordType left, const CoordType top) {
|
||||
SpriteFrame *frame = new SpriteFrame();
|
||||
frame->initFromPICTResource(g_vm->_resFork, pictID, transparent);
|
||||
addFrame(frame, left, top);
|
||||
}
|
||||
|
||||
uint32 Sprite::addFrame(SpriteFrame *frame, const CoordType left, const CoordType top) {
|
||||
SpriteFrameRec frameRecord;
|
||||
frameRecord.frame = frame;
|
||||
frameRecord.frameLeft = left;
|
||||
frameRecord.frameTop = top;
|
||||
_frameArray.push_back(frameRecord);
|
||||
_numFrames++;
|
||||
frame->_referenceCount++;
|
||||
|
||||
Common::Rect frameBounds;
|
||||
frame->getSurfaceBounds(frameBounds);
|
||||
|
||||
// 9/3/96
|
||||
// BB Should this be + left or - left?
|
||||
frameBounds.moveTo(_bounds.left + left, _bounds.top + top);
|
||||
|
||||
frameBounds.extend(_bounds);
|
||||
|
||||
if (_bounds != frameBounds)
|
||||
setBounds(frameBounds);
|
||||
|
||||
return _numFrames - 1;
|
||||
}
|
||||
|
||||
void Sprite::removeFrame(const uint32 frameNum) {
|
||||
_frameArray[frameNum].frame->_referenceCount--;
|
||||
if (_frameArray[frameNum].frame->_referenceCount == 0)
|
||||
delete _frameArray[frameNum].frame;
|
||||
|
||||
// Calculate the new bounds
|
||||
Common::Rect frameBounds;
|
||||
for (uint32 i = 0; i < _numFrames; i++) {
|
||||
if (i == frameNum)
|
||||
continue;
|
||||
|
||||
Common::Rect r;
|
||||
_frameArray[i].frame->getSurfaceBounds(r);
|
||||
r.translate(_frameArray[i].frameLeft, _frameArray[i].frameTop);
|
||||
frameBounds.extend(r);
|
||||
}
|
||||
|
||||
_frameArray.remove_at(frameNum);
|
||||
|
||||
frameBounds.moveTo(_bounds.left, _bounds.top);
|
||||
setBounds(frameBounds);
|
||||
|
||||
if (_currentFrameNum == frameNum)
|
||||
triggerRedraw();
|
||||
else if (_currentFrameNum != 0xffffffff && _currentFrameNum > frameNum)
|
||||
--_currentFrameNum;
|
||||
}
|
||||
|
||||
void Sprite::setCurrentFrameIndex(const int32 frameNum) {
|
||||
if (frameNum < 0) {
|
||||
if (_currentFrameNum != 0xffffffff) {
|
||||
_currentFrameNum = 0xffffffff;
|
||||
_currentFrame = nullptr;
|
||||
triggerRedraw();
|
||||
}
|
||||
} else if (_numFrames > 0) {
|
||||
uint32 f = frameNum % _numFrames;
|
||||
if (f != _currentFrameNum) {
|
||||
_currentFrameNum = f;
|
||||
_currentFrame = &_frameArray[f];
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpriteFrame *Sprite::getFrame(const int32 index) {
|
||||
if (index < 0 || (uint32)index >= _numFrames)
|
||||
return nullptr;
|
||||
|
||||
return _frameArray[index].frame;
|
||||
}
|
||||
|
||||
void Sprite::draw(const Common::Rect &r) {
|
||||
if (_currentFrame) {
|
||||
Common::Rect frameBounds;
|
||||
_currentFrame->frame->getSurfaceBounds(frameBounds);
|
||||
|
||||
frameBounds.translate(_bounds.left + _currentFrame->frameLeft, _bounds.top + _currentFrame->frameTop);
|
||||
Common::Rect r1 = frameBounds.findIntersectingRect(r);
|
||||
|
||||
Common::Rect r2 = frameBounds;
|
||||
r2.translate(-_bounds.left - _currentFrame->frameLeft, -_bounds.top - _currentFrame->frameTop);
|
||||
|
||||
_currentFrame->frame->drawImage(r2, r1);
|
||||
}
|
||||
}
|
||||
|
||||
SpriteSequence::SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID) :
|
||||
FrameSequence(id), _sprite(spriteID), _transparent(false) {
|
||||
}
|
||||
|
||||
void SpriteSequence::openFrameSequence() {
|
||||
if (!isSequenceOpen()) {
|
||||
FrameSequence::openFrameSequence();
|
||||
|
||||
if (isSequenceOpen()) {
|
||||
uint32 numFrames = getNumFrames();
|
||||
|
||||
for (uint32 i = 0; i < numFrames; ++i) {
|
||||
SpriteFrame *frame = new SpriteFrame();
|
||||
frame->initFromPICTResource(_resFork, i + 0x80, _transparent);
|
||||
_sprite.addFrame(frame, 0, 0);
|
||||
}
|
||||
|
||||
_sprite.setBounds(_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteSequence::closeFrameSequence() {
|
||||
if (isSequenceOpen()) {
|
||||
FrameSequence::closeFrameSequence();
|
||||
_sprite.discardFrames();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteSequence::setBounds(const Common::Rect &bounds) {
|
||||
FrameSequence::setBounds(bounds);
|
||||
_sprite.setBounds(_bounds);
|
||||
}
|
||||
|
||||
void SpriteSequence::draw(const Common::Rect &r) {
|
||||
_sprite.draw(r);
|
||||
}
|
||||
|
||||
void SpriteSequence::newFrame(const uint16 frame) {
|
||||
_sprite.setCurrentFrameIndex(frame);
|
||||
}
|
||||
|
||||
#define DRAW_PIXEL() \
|
||||
if (bytesPerPixel == 2) \
|
||||
*((uint16 *)dst) = black; \
|
||||
else \
|
||||
*((uint32 *)dst) = black; \
|
||||
dst += bytesPerPixel
|
||||
|
||||
#define SKIP_PIXEL() \
|
||||
dst += bytesPerPixel
|
||||
|
||||
void ScreenDimmer::draw(const Common::Rect &r) {
|
||||
// We're going to emulate QuickDraw's srcOr+gray mode here
|
||||
// In this mode, every other y column is all black (odd-columns).
|
||||
// Basically, every row does three black and then one transparent
|
||||
// repeatedly.
|
||||
|
||||
// The output is identical to the original
|
||||
|
||||
uint32 black = g_system->getScreenFormat().RGBToColor(0, 0, 0);
|
||||
Graphics::Surface *screen = g_vm->_gfx->getWorkArea();
|
||||
byte bytesPerPixel = g_system->getScreenFormat().bytesPerPixel;
|
||||
|
||||
// We're currently doing it to the whole screen to simplify the code
|
||||
|
||||
for (int y = 0; y < 480; y++) {
|
||||
byte *dst = (byte *)screen->getBasePtr(0, y);
|
||||
|
||||
for (int x = 0; x < 640; x += 4) {
|
||||
if (y & 1) {
|
||||
DRAW_PIXEL();
|
||||
DRAW_PIXEL();
|
||||
SKIP_PIXEL();
|
||||
DRAW_PIXEL();
|
||||
} else {
|
||||
SKIP_PIXEL();
|
||||
DRAW_PIXEL();
|
||||
DRAW_PIXEL();
|
||||
DRAW_PIXEL();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef DRAW_PIXEL
|
||||
#undef SKIP_PIXEL
|
||||
|
||||
SoundLevel::SoundLevel(const DisplayElementID id) : DisplayElement(id) {
|
||||
_soundLevel = 0;
|
||||
}
|
||||
|
||||
void SoundLevel::incrementLevel() {
|
||||
if (_soundLevel < 12) {
|
||||
_soundLevel++;
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
void SoundLevel::decrementLevel() {
|
||||
if (_soundLevel > 0) {
|
||||
_soundLevel--;
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
uint16 SoundLevel::getSoundLevel() {
|
||||
return CLIP<int>(_soundLevel * 22, 0, 256);
|
||||
}
|
||||
|
||||
void SoundLevel::setSoundLevel(uint16 level) {
|
||||
uint16 newLevel = (level + 21) / 22;
|
||||
|
||||
if (newLevel != _soundLevel) {
|
||||
_soundLevel = newLevel;
|
||||
triggerRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
void SoundLevel::draw(const Common::Rect &r) {
|
||||
Common::Rect levelRect(_bounds.right + (8 * (_soundLevel - 12)), _bounds.top, _bounds.right, _bounds.bottom);
|
||||
levelRect = r.findIntersectingRect(levelRect);
|
||||
|
||||
if (!levelRect.isEmpty()) {
|
||||
Graphics::Surface *screen = g_vm->_gfx->getWorkArea();
|
||||
screen->fillRect(levelRect, g_system->getScreenFormat().RGBToColor(0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Pegasus
|
||||
Reference in New Issue
Block a user