mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-17 15:18:11 +00:00
1223 lines
35 KiB
C++
1223 lines
35 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 "mohawk/resource.h"
|
|
#include "mohawk/riven.h"
|
|
#include "mohawk/riven_card.h"
|
|
#include "mohawk/riven_graphics.h"
|
|
#include "mohawk/riven_sound.h"
|
|
#include "mohawk/riven_stack.h"
|
|
#include "mohawk/riven_video.h"
|
|
|
|
#include "common/system.h"
|
|
#include "engines/util.h"
|
|
#include "graphics/colormasks.h"
|
|
|
|
namespace Mohawk {
|
|
|
|
class TransitionEffect {
|
|
public:
|
|
TransitionEffect(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
|
|
RivenTransition type, uint duration, const Common::Rect &rect) :
|
|
_system(system),
|
|
_mainScreen(mainScreen),
|
|
_effectScreen(effectScreen),
|
|
_type(type),
|
|
_duration(duration),
|
|
_timeBased(false),
|
|
_rect(rect) {
|
|
}
|
|
|
|
virtual ~TransitionEffect() {}
|
|
|
|
bool isTimeBased() const { return _timeBased; }
|
|
|
|
virtual bool drawFrame(uint32 elapsed) = 0;
|
|
|
|
protected:
|
|
Common::Rect makeDirectionalInitalArea() const {
|
|
Common::Rect initialArea = _rect;
|
|
switch (_type) {
|
|
case kRivenTransitionWipeLeft:
|
|
case kRivenTransitionPanLeft:
|
|
initialArea.left = _rect.right;
|
|
break;
|
|
case kRivenTransitionWipeRight:
|
|
case kRivenTransitionPanRight:
|
|
initialArea.right = _rect.left;
|
|
break;
|
|
case kRivenTransitionWipeUp:
|
|
case kRivenTransitionPanUp:
|
|
initialArea.top = _rect.bottom;
|
|
break;
|
|
case kRivenTransitionWipeDown:
|
|
case kRivenTransitionPanDown:
|
|
initialArea.bottom = _rect.top;
|
|
break;
|
|
default:
|
|
error("Unhandled transition type: %d", _type);
|
|
}
|
|
|
|
return initialArea;
|
|
}
|
|
|
|
OSystem *_system;
|
|
|
|
RivenTransition _type;
|
|
uint _duration;
|
|
Common::Rect _rect;
|
|
bool _timeBased;
|
|
|
|
Graphics::Surface *_mainScreen;
|
|
Graphics::Surface *_effectScreen;
|
|
};
|
|
|
|
class TransitionEffectWipe : public TransitionEffect {
|
|
public:
|
|
TransitionEffectWipe(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
|
|
RivenTransition type, uint duration, const Common::Rect &rect) :
|
|
TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
|
|
|
|
_timeBased = true;
|
|
_lastCopyArea = makeDirectionalInitalArea();
|
|
}
|
|
|
|
bool drawFrame(uint32 elapsed) override {
|
|
Common::Rect copyArea;
|
|
switch (_type) {
|
|
case kRivenTransitionWipeLeft:
|
|
copyArea.top = _lastCopyArea.top;
|
|
copyArea.bottom = _lastCopyArea.bottom;
|
|
copyArea.right = _lastCopyArea.left;
|
|
copyArea.left = _rect.width() - elapsed * _rect.width() / _duration;
|
|
break;
|
|
case kRivenTransitionWipeRight:
|
|
copyArea.top = _lastCopyArea.top;
|
|
copyArea.bottom = _lastCopyArea.bottom;
|
|
copyArea.left = _lastCopyArea.right;
|
|
copyArea.right = elapsed * _rect.width() / _duration;
|
|
break;
|
|
case kRivenTransitionWipeUp:
|
|
copyArea.left = _lastCopyArea.left;
|
|
copyArea.right = _lastCopyArea.right;
|
|
copyArea.bottom = _lastCopyArea.top;
|
|
copyArea.top = _rect.height() - elapsed * _rect.height() / _duration;
|
|
break;
|
|
case kRivenTransitionWipeDown:
|
|
copyArea.left = _lastCopyArea.left;
|
|
copyArea.right = _lastCopyArea.right;
|
|
copyArea.top = _lastCopyArea.bottom;
|
|
copyArea.bottom = elapsed * _rect.height() / _duration;
|
|
break;
|
|
default:
|
|
error("Unhandled transition type: %d", _type);
|
|
}
|
|
|
|
_lastCopyArea = copyArea;
|
|
|
|
if (copyArea.isEmpty()) {
|
|
// Nothing to draw
|
|
return false;
|
|
}
|
|
|
|
_effectScreen->copyRectToSurface(*_mainScreen, copyArea.left, copyArea.top, copyArea);
|
|
_system->copyRectToScreen(_effectScreen->getBasePtr(copyArea.left, copyArea.top), _effectScreen->pitch,
|
|
copyArea.left, copyArea.top, copyArea.width(), copyArea.height());
|
|
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
Common::Rect _lastCopyArea;
|
|
};
|
|
|
|
class TransitionEffectPan : public TransitionEffect {
|
|
public:
|
|
TransitionEffectPan(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
|
|
RivenTransition type, uint duration, const Common::Rect &rect, int16 offset) :
|
|
TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
|
|
|
|
_timeBased = true;
|
|
_offset = offset;
|
|
_initialArea = makeDirectionalInitalArea();
|
|
complete = false;
|
|
}
|
|
|
|
bool drawFrame(uint32 elapsed) override {
|
|
Common::Rect newArea;
|
|
switch (_type) {
|
|
case kRivenTransitionPanLeft:
|
|
newArea.top = _initialArea.top;
|
|
newArea.bottom = _initialArea.bottom;
|
|
newArea.right = _initialArea.right;
|
|
newArea.left = _rect.width() - elapsed * _rect.width() / _duration;
|
|
break;
|
|
case kRivenTransitionPanRight:
|
|
newArea.top = _initialArea.top;
|
|
newArea.bottom = _initialArea.bottom;
|
|
newArea.left = _initialArea.left;
|
|
newArea.right = elapsed * _rect.width() / _duration;
|
|
break;
|
|
case kRivenTransitionPanUp:
|
|
newArea.left = _initialArea.left;
|
|
newArea.right = _initialArea.right;
|
|
newArea.bottom = _initialArea.bottom;
|
|
newArea.top = _rect.height() - elapsed * _rect.height() / _duration;
|
|
break;
|
|
case kRivenTransitionPanDown:
|
|
newArea.left = _initialArea.left;
|
|
newArea.right = _initialArea.right;
|
|
newArea.top = _initialArea.top;
|
|
newArea.bottom = elapsed * _rect.height() / _duration;
|
|
break;
|
|
default:
|
|
error("Unhandled transition type: %d", _type);
|
|
}
|
|
|
|
if (newArea.isEmpty()) {
|
|
// Nothing to draw
|
|
return false;
|
|
}
|
|
|
|
Common::Rect oldArea;
|
|
if (newArea != _rect) {
|
|
oldArea = Common::Rect(
|
|
newArea.right != _rect.right ? _rect.left + newArea.width() : _rect.left,
|
|
newArea.bottom != _rect.bottom ? _rect.top + newArea.height() : _rect.top,
|
|
newArea.left != _rect.left ? _rect.right - newArea.width() : _rect.right,
|
|
newArea.top != _rect.top ? _rect.bottom - newArea.height() : _rect.bottom
|
|
);
|
|
}
|
|
|
|
int oldX = newArea.left != _rect.left ? _rect.left + newArea.width() : _rect.left;
|
|
int oldY = newArea.top != _rect.top ? _rect.top + newArea.height() : _rect.top;
|
|
|
|
int newX = newArea.right != _rect.right ? _rect.left + oldArea.width() : _rect.left;
|
|
int newY = newArea.bottom != _rect.bottom ? _rect.top + oldArea.height() : _rect.top;
|
|
|
|
if (_offset != -1) {
|
|
if (_type == kRivenTransitionPanDown && oldArea.height() - _offset > 0) {
|
|
newY -= _offset;
|
|
} else if (_type == kRivenTransitionPanUp && newArea.height() + _offset < _rect.height()) {
|
|
newY += _offset;
|
|
} else if (_type == kRivenTransitionPanRight && oldArea.width() - _offset > 0) {
|
|
newX -= _offset;
|
|
} else if (_type == kRivenTransitionPanLeft && newArea.width() + _offset < _rect.width()) {
|
|
newX += _offset;
|
|
} else {
|
|
newX = 0;
|
|
newY = 0;
|
|
newArea = _rect;
|
|
oldArea = Common::Rect();
|
|
}
|
|
}
|
|
|
|
if (!oldArea.isEmpty()) {
|
|
_system->copyRectToScreen(_effectScreen->getBasePtr(oldX, oldY), _effectScreen->pitch,
|
|
oldArea.left, oldArea.top, oldArea.width(), oldArea.height());
|
|
}
|
|
|
|
if (!newArea.isEmpty()) {
|
|
_system->copyRectToScreen(_mainScreen->getBasePtr(newX, newY), _mainScreen->pitch,
|
|
newArea.left, newArea.top, newArea.width(), newArea.height());
|
|
}
|
|
|
|
if (newArea == _rect) {
|
|
_effectScreen->copyRectToSurface(*_mainScreen, _rect.left, _rect.top, _rect);
|
|
return true; // The transition is complete
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private:
|
|
Common::Rect _initialArea;
|
|
int16 _offset;
|
|
bool complete;
|
|
};
|
|
|
|
class TransitionEffectBlend : public TransitionEffect {
|
|
public:
|
|
TransitionEffectBlend(OSystem *system, Graphics::Surface *mainScreen, Graphics::Surface *effectScreen,
|
|
RivenTransition type, uint duration, const Common::Rect &rect) :
|
|
TransitionEffect(system, mainScreen, effectScreen, type, duration, rect) {
|
|
|
|
_timeBased = false;
|
|
}
|
|
|
|
bool drawFrame(uint32 elapsed) override {
|
|
assert(_effectScreen->format == _mainScreen->format);
|
|
assert(_effectScreen->format == _system->getScreenFormat());
|
|
|
|
if (elapsed == _duration) {
|
|
_effectScreen->copyRectToSurface(*_mainScreen, 0, 0, Common::Rect(_mainScreen->w, _mainScreen->h));
|
|
_system->copyRectToScreen(_effectScreen->getBasePtr(0, 0), _effectScreen->pitch, 0, 0, _effectScreen->w, _effectScreen->h);
|
|
return true; // The transition is complete
|
|
} else {
|
|
Graphics::Surface *screen = _system->lockScreen();
|
|
|
|
uint alpha = elapsed * 255 / _duration;
|
|
for (uint y = 0; y < _mainScreen->h; y++) {
|
|
uint16 *src1 = (uint16 *) _mainScreen->getBasePtr(0, y);
|
|
uint16 *src2 = (uint16 *) _effectScreen->getBasePtr(0, y);
|
|
uint16 *dst = (uint16 *) screen->getBasePtr(0, y);
|
|
for (uint x = 0; x < _mainScreen->w; x++) {
|
|
uint8 r1, g1, b1, r2, g2, b2;
|
|
Graphics::colorToRGB< Graphics::ColorMasks<565> >(*src1++, r1, g1, b1);
|
|
Graphics::colorToRGB< Graphics::ColorMasks<565> >(*src2++, r2, g2, b2);
|
|
|
|
uint r = r1 * alpha + r2 * (255 - alpha);
|
|
uint g = g1 * alpha + g2 * (255 - alpha);
|
|
uint b = b1 * alpha + b2 * (255 - alpha);
|
|
|
|
r /= 255;
|
|
g /= 255;
|
|
b /= 255;
|
|
|
|
*dst++ = (uint16) Graphics::RGBToColor< Graphics::ColorMasks<565> >(r, g, b);
|
|
}
|
|
}
|
|
|
|
_system->unlockScreen();
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) :
|
|
GraphicsManager(),
|
|
_vm(vm),
|
|
_screenUpdateNesting(0),
|
|
_screenUpdateRunning(false),
|
|
_enableCardUpdateScript(true),
|
|
_scheduledTransition(kRivenTransitionNone),
|
|
_dirtyScreen(false),
|
|
_creditsImage(302),
|
|
_creditsPos(0),
|
|
_transitionMode(kRivenTransitionModeFastest),
|
|
_transitionOffset(-1),
|
|
_waterEffect(nullptr),
|
|
_fliesEffect(nullptr),
|
|
_transitionFrames(0),
|
|
_transitionDuration(0) {
|
|
_bitmapDecoder = new MohawkBitmap();
|
|
|
|
// Restrict ourselves to a single pixel format to simplify the effects implementation
|
|
_pixelFormat = Graphics::createPixelFormat<565>();
|
|
initGraphics(608, 436, &_pixelFormat);
|
|
|
|
// The actual game graphics only take up the first 392 rows. The inventory
|
|
// occupies the rest of the screen and we don't use the buffer to hold that.
|
|
_mainScreen = new Graphics::Surface();
|
|
_mainScreen->create(608, 392, _pixelFormat);
|
|
|
|
_effectScreen = new Graphics::Surface();
|
|
_effectScreen->create(608, 392, _pixelFormat);
|
|
}
|
|
|
|
RivenGraphics::~RivenGraphics() {
|
|
_effectScreen->free();
|
|
delete _effectScreen;
|
|
_mainScreen->free();
|
|
delete _mainScreen;
|
|
delete _bitmapDecoder;
|
|
clearFliesEffect();
|
|
clearWaterEffect();
|
|
}
|
|
|
|
MohawkSurface *RivenGraphics::decodeImage(uint16 id) {
|
|
MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getResource(ID_TBMP, id));
|
|
surface->convertToTrueColor();
|
|
return surface;
|
|
}
|
|
|
|
void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom) {
|
|
Graphics::Surface *surface = findImage(image)->getSurface();
|
|
|
|
beginScreenUpdate();
|
|
|
|
// Clip the width to fit on the screen. Fixes some images.
|
|
if (left + surface->w > 608)
|
|
surface->w = 608 - left;
|
|
|
|
for (uint16 i = 0; i < surface->h; i++)
|
|
memcpy(_mainScreen->getBasePtr(left, i + top), surface->getBasePtr(0, i), surface->w * surface->format.bytesPerPixel);
|
|
|
|
_dirtyScreen = true;
|
|
applyScreenUpdate();
|
|
}
|
|
|
|
void RivenGraphics::copySurfaceToScreen(Graphics::Surface *src, uint32 x, uint32 y) {
|
|
beginScreenUpdate();
|
|
|
|
int w = src->w;
|
|
int h = src->h;
|
|
|
|
// Clip the width to fit on the screen. Fixes some images.
|
|
if (x + w > 608)
|
|
w = 608 - x;
|
|
|
|
if (y + h > 436)
|
|
h = 346 - y;
|
|
|
|
for (uint16 i = 0; i < h; i++)
|
|
memcpy(_mainScreen->getBasePtr(x, i + y), src->getBasePtr(0, i), w * src->format.bytesPerPixel);
|
|
|
|
_dirtyScreen = true;
|
|
applyScreenUpdate();
|
|
}
|
|
|
|
void RivenGraphics::updateScreen() {
|
|
if (_dirtyScreen) {
|
|
// Copy to screen if there's no transition. Otherwise transition.
|
|
if (_scheduledTransition == kRivenTransitionNone
|
|
|| _transitionMode == kRivenTransitionModeDisabled) {
|
|
const Common::Rect updateRect = Common::Rect(0, 0, 608, 392);
|
|
|
|
// mainScreen -> effectScreen -> systemScreen
|
|
_effectScreen->copyRectToSurface(*_mainScreen, updateRect.left, updateRect.top, updateRect);
|
|
_vm->_system->copyRectToScreen(_effectScreen->getBasePtr(updateRect.left, updateRect.top), _effectScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height());
|
|
|
|
_scheduledTransition = kRivenTransitionNone;
|
|
} else {
|
|
runScheduledTransition();
|
|
}
|
|
|
|
_dirtyScreen = false;
|
|
}
|
|
}
|
|
|
|
void RivenGraphics::scheduleWaterEffect(uint16 sfxeID) {
|
|
clearWaterEffect();
|
|
|
|
_waterEffect = new WaterEffect(_vm, sfxeID);
|
|
}
|
|
|
|
void RivenGraphics::clearWaterEffect() {
|
|
delete _waterEffect;
|
|
_waterEffect = nullptr;
|
|
}
|
|
|
|
WaterEffect::WaterEffect(MohawkEngine_Riven *vm, uint16 sfxeID) :
|
|
_vm(vm) {
|
|
Common::SeekableReadStream *sfxeStream = _vm->getResource(ID_SFXE, sfxeID);
|
|
|
|
if (sfxeStream->readUint16BE() != 'SL')
|
|
error ("Unknown sfxe tag");
|
|
|
|
// Read in header info
|
|
uint16 frameCount = sfxeStream->readUint16BE();
|
|
uint32 offsetTablePosition = sfxeStream->readUint32BE();
|
|
_rect.left = sfxeStream->readUint16BE();
|
|
_rect.top = sfxeStream->readUint16BE();
|
|
_rect.right = sfxeStream->readUint16BE();
|
|
_rect.bottom = sfxeStream->readUint16BE();
|
|
_speed = sfxeStream->readUint16BE();
|
|
// Skip the rest of the fields...
|
|
|
|
// Read in offsets
|
|
sfxeStream->seek(offsetTablePosition);
|
|
Common::Array<uint32> frameOffsets;
|
|
frameOffsets.resize(frameCount);
|
|
for (uint16 i = 0; i < frameCount; i++)
|
|
frameOffsets[i] = sfxeStream->readUint32BE();
|
|
|
|
// Read in the scripts
|
|
sfxeStream->seek(frameOffsets[0]);
|
|
for (uint16 i = 0; i < frameCount; i++) {
|
|
uint scriptLength = (i == frameCount - 1) ? sfxeStream->size() - frameOffsets[i] : frameOffsets[i + 1] - frameOffsets[i];
|
|
_frameScripts.push_back(sfxeStream->readStream(scriptLength));
|
|
}
|
|
|
|
// Set it to the first frame
|
|
_curFrame = 0;
|
|
_lastFrameTime = 0;
|
|
|
|
delete sfxeStream;
|
|
}
|
|
|
|
void WaterEffect::update() {
|
|
if (_vm->_system->getMillis() <= _lastFrameTime + 1000 / _speed) {
|
|
return; // Nothing to do yet
|
|
}
|
|
|
|
// Make sure the script is at the starting point
|
|
Common::SeekableReadStream *script = _frameScripts[_curFrame];
|
|
script->seek(0);
|
|
|
|
Graphics::Surface *screen = _vm->_system->lockScreen();
|
|
Graphics::Surface *mainScreen = _vm->_gfx->getBackScreen();
|
|
assert(screen->format == mainScreen->format);
|
|
|
|
// Run script
|
|
uint16 curRow = 0;
|
|
for (uint16 op = script->readUint16BE(); op != 4; op = script->readUint16BE()) {
|
|
if (op == 1) { // Increment Row
|
|
curRow++;
|
|
} else if (op == 3) { // Copy Pixels
|
|
uint16 dstLeft = script->readUint16BE();
|
|
uint16 srcLeft = script->readUint16BE();
|
|
uint16 srcTop = script->readUint16BE();
|
|
uint16 rowWidth = script->readUint16BE();
|
|
|
|
byte *src = (byte *)mainScreen->getBasePtr(srcLeft, srcTop);
|
|
byte *dst = (byte *)screen->getBasePtr(dstLeft, curRow + _rect.top);
|
|
|
|
memcpy(dst, src, rowWidth * screen->format.bytesPerPixel);
|
|
} else if (op != 4) { // End of Script
|
|
error ("Unknown SFXE opcode %d", op);
|
|
}
|
|
}
|
|
|
|
_vm->_system->unlockScreen();
|
|
|
|
// Increment frame
|
|
_curFrame++;
|
|
if (_curFrame == _frameScripts.size())
|
|
_curFrame = 0;
|
|
|
|
// Set the new time
|
|
_lastFrameTime = _vm->_system->getMillis();
|
|
}
|
|
|
|
WaterEffect::~WaterEffect() {
|
|
for (uint i = 0; i < _frameScripts.size(); i++) {
|
|
delete _frameScripts[i];
|
|
}
|
|
}
|
|
|
|
void RivenGraphics::setTransitionMode(RivenTransitionMode mode) {
|
|
_transitionMode = mode;
|
|
switch (_transitionMode) {
|
|
case kRivenTransitionModeFastest:
|
|
_transitionFrames = 8;
|
|
_transitionDuration = 300;
|
|
break;
|
|
case kRivenTransitionModeNormal:
|
|
_transitionFrames = 16;
|
|
_transitionDuration = 500;
|
|
break;
|
|
case kRivenTransitionModeBest:
|
|
_transitionFrames = 32;
|
|
_transitionDuration = 700;
|
|
break;
|
|
case kRivenTransitionModeDisabled:
|
|
_transitionFrames = 0;
|
|
_transitionDuration = 0;
|
|
break;
|
|
default:
|
|
error("Unknown transition mode %d", _transitionMode);
|
|
}
|
|
}
|
|
|
|
void RivenGraphics::scheduleTransition(RivenTransition id, const Common::Rect &rect) {
|
|
_scheduledTransition = id;
|
|
_transitionRect = rect;
|
|
|
|
RivenHotspot *hotspot = _vm->getCard()->getCurHotspot();
|
|
if (hotspot) {
|
|
_transitionOffset = hotspot->getTransitionOffset();
|
|
} else {
|
|
_transitionOffset = -1;
|
|
}
|
|
}
|
|
|
|
void RivenGraphics::runScheduledTransition() {
|
|
if (_scheduledTransition == kRivenTransitionNone)
|
|
return;
|
|
|
|
// Note: Transitions 0-11 are actual transitions, but none are used in-game.
|
|
// There's no point in implementing them if they're not used. These extra
|
|
// transitions were found by hacking scripts.
|
|
|
|
TransitionEffect *effect = nullptr;
|
|
switch (_scheduledTransition) {
|
|
case kRivenTransitionWipeLeft:
|
|
case kRivenTransitionWipeRight:
|
|
case kRivenTransitionWipeUp:
|
|
case kRivenTransitionWipeDown: {
|
|
effect = new TransitionEffectWipe(_vm->_system, _mainScreen, _effectScreen,
|
|
_scheduledTransition, _transitionDuration, _transitionRect);
|
|
break;
|
|
}
|
|
case kRivenTransitionPanLeft:
|
|
case kRivenTransitionPanRight:
|
|
case kRivenTransitionPanUp:
|
|
case kRivenTransitionPanDown: {
|
|
effect = new TransitionEffectPan(_vm->_system, _mainScreen, _effectScreen,
|
|
_scheduledTransition, _transitionDuration, _transitionRect, _transitionOffset);
|
|
break;
|
|
}
|
|
case kRivenTransitionBlend:
|
|
case kRivenTransitionBlend2: // (tspit CARD 155)
|
|
effect = new TransitionEffectBlend(_vm->_system, _mainScreen, _effectScreen,
|
|
_scheduledTransition, _transitionFrames, _transitionRect);
|
|
break;
|
|
default:
|
|
error("Unhandled transition type: %d", _scheduledTransition);
|
|
}
|
|
|
|
if (effect->isTimeBased()) {
|
|
uint32 startTime = _vm->_system->getMillis();
|
|
uint32 timeElapsed = 0;
|
|
bool transitionComplete = false;
|
|
while (timeElapsed < _transitionDuration && !transitionComplete && !_vm->hasGameEnded()) {
|
|
transitionComplete = effect->drawFrame(timeElapsed);
|
|
|
|
_vm->doFrame();
|
|
timeElapsed = _vm->_system->getMillis() - startTime;
|
|
}
|
|
|
|
if (!transitionComplete) {
|
|
effect->drawFrame(_transitionDuration);
|
|
}
|
|
} else {
|
|
for (uint frame = 1; frame <= _transitionFrames && !_vm->hasGameEnded(); frame++) {
|
|
effect->drawFrame(frame);
|
|
|
|
_vm->doFrame();
|
|
}
|
|
}
|
|
delete effect;
|
|
|
|
_scheduledTransition = kRivenTransitionNone; // Clear scheduled transition
|
|
_transitionOffset = -1;
|
|
}
|
|
|
|
void RivenGraphics::clearMainScreen() {
|
|
_mainScreen->fillRect(Common::Rect(0, 0, 608, 392), _pixelFormat.RGBToColor(0, 0, 0));
|
|
}
|
|
|
|
void RivenGraphics::fadeToBlack() {
|
|
// The transition speed is forced to best here
|
|
setTransitionMode(kRivenTransitionModeBest);
|
|
scheduleTransition(kRivenTransitionBlend);
|
|
clearMainScreen();
|
|
runScheduledTransition();
|
|
}
|
|
|
|
void RivenGraphics::drawExtrasImageToScreen(uint16 id, const Common::Rect &rect) {
|
|
MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
|
|
mhkSurface->convertToTrueColor();
|
|
Graphics::Surface *surface = mhkSurface->getSurface();
|
|
|
|
_vm->_system->copyRectToScreen(surface->getPixels(), surface->pitch, rect.left, rect.top, surface->w, surface->h);
|
|
|
|
delete mhkSurface;
|
|
}
|
|
|
|
void RivenGraphics::drawRect(const Common::Rect &rect, bool active) {
|
|
// Useful with debugging. Shows where hotspots are on the screen and whether or not they're active.
|
|
Graphics::Surface *screen = _vm->_system->lockScreen();
|
|
|
|
if (active)
|
|
screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0));
|
|
else
|
|
screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0));
|
|
|
|
_vm->_system->unlockScreen();
|
|
}
|
|
|
|
void RivenGraphics::drawImageRect(uint16 id, const Common::Rect &srcRect, const Common::Rect &dstRect) {
|
|
// Draw tBMP id from srcRect to dstRect
|
|
Graphics::Surface *surface = findImage(id)->getSurface();
|
|
|
|
assert(srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height());
|
|
|
|
for (uint16 i = 0; i < srcRect.height(); i++)
|
|
memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(srcRect.left, i + srcRect.top), srcRect.width() * surface->format.bytesPerPixel);
|
|
|
|
_dirtyScreen = true;
|
|
}
|
|
|
|
void RivenGraphics::drawExtrasImage(uint16 id, const Common::Rect &dstRect) {
|
|
MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
|
|
mhkSurface->convertToTrueColor();
|
|
Graphics::Surface *surface = mhkSurface->getSurface();
|
|
|
|
assert(dstRect.width() == surface->w);
|
|
|
|
for (uint16 i = 0; i < surface->h; i++)
|
|
memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(0, i), surface->pitch);
|
|
|
|
delete mhkSurface;
|
|
_dirtyScreen = true;
|
|
}
|
|
|
|
void RivenGraphics::beginCredits() {
|
|
// Clear the old cache
|
|
clearCache();
|
|
|
|
// Now cache all the credits images
|
|
for (uint16 i = 302; i <= 320; i++) {
|
|
MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, i));
|
|
surface->convertToTrueColor();
|
|
addImageToCache(i, surface);
|
|
}
|
|
|
|
// And clear our screen too
|
|
clearMainScreen();
|
|
_effectScreen->fillRect(Common::Rect(0, 0, 608, 392), _pixelFormat.RGBToColor(0, 0, 0));
|
|
}
|
|
|
|
void RivenGraphics::updateCredits() {
|
|
if ((_creditsImage == 303 || _creditsImage == 304) && _creditsPos == 0)
|
|
fadeToBlack();
|
|
|
|
if (_creditsImage < 304) {
|
|
// For the first two credit images, they are faded from black to the image and then out again
|
|
scheduleTransition(kRivenTransitionBlend);
|
|
|
|
Graphics::Surface *frame = findImage(_creditsImage++)->getSurface();
|
|
|
|
for (int y = 0; y < frame->h; y++)
|
|
memcpy(_mainScreen->getBasePtr(124, y), frame->getBasePtr(0, y), frame->pitch);
|
|
|
|
runScheduledTransition();
|
|
} else {
|
|
// Otheriwse, we're scrolling
|
|
// Move the screen up one row
|
|
memmove(_mainScreen->getPixels(), _mainScreen->getBasePtr(0, 1), _mainScreen->pitch * (_mainScreen->h - 1));
|
|
|
|
// Only update as long as we're not before the last frame
|
|
// Otherwise, we're just moving up a row (which we already did)
|
|
if (_creditsImage <= 320) {
|
|
// Copy the next row to the bottom of the screen
|
|
Graphics::Surface *frame = findImage(_creditsImage)->getSurface();
|
|
memcpy(_mainScreen->getBasePtr(124, _mainScreen->h - 1), frame->getBasePtr(0, _creditsPos), frame->pitch);
|
|
_creditsPos++;
|
|
|
|
if (_creditsPos == _mainScreen->h) {
|
|
_creditsImage++;
|
|
_creditsPos = 0;
|
|
}
|
|
}
|
|
|
|
// Now flush the new screen
|
|
_vm->_system->copyRectToScreen(_mainScreen->getPixels(), _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h);
|
|
}
|
|
}
|
|
|
|
void RivenGraphics::beginScreenUpdate() {
|
|
_screenUpdateNesting++;
|
|
}
|
|
|
|
void RivenGraphics::applyScreenUpdate(bool force) {
|
|
if (force) {
|
|
_screenUpdateNesting = 0;
|
|
} else {
|
|
_screenUpdateNesting--;
|
|
}
|
|
|
|
// The screen is only updated when the outermost screen update ends
|
|
if (_screenUpdateNesting <= 0 && !_screenUpdateRunning) {
|
|
_screenUpdateRunning = true;
|
|
|
|
if (_enableCardUpdateScript) {
|
|
_vm->getCard()->runScript(kCardUpdateScript);
|
|
}
|
|
_vm->_sound->triggerDrawSound();
|
|
updateScreen();
|
|
|
|
_screenUpdateNesting = 0;
|
|
_screenUpdateRunning = false;
|
|
}
|
|
}
|
|
|
|
void RivenGraphics::setFliesEffect(uint16 count, bool fireflies) {
|
|
delete _fliesEffect;
|
|
_fliesEffect = new FliesEffect(_vm, count, fireflies);
|
|
}
|
|
|
|
void RivenGraphics::clearFliesEffect() {
|
|
delete _fliesEffect;
|
|
_fliesEffect = nullptr;
|
|
}
|
|
|
|
Graphics::Surface *RivenGraphics::getBackScreen() {
|
|
return _mainScreen;
|
|
}
|
|
|
|
Graphics::Surface *RivenGraphics::getEffectScreen() {
|
|
return _effectScreen;
|
|
}
|
|
|
|
void RivenGraphics::updateEffects() {
|
|
if (_waterEffect && _vm->_vars["waterenabled"] != 0) {
|
|
_waterEffect->update();
|
|
}
|
|
|
|
if (_fliesEffect) {
|
|
_fliesEffect->update();
|
|
}
|
|
}
|
|
|
|
void RivenGraphics::copySystemRectToScreen(const Common::Rect &rect) {
|
|
Graphics::Surface *screen = _vm->_system->lockScreen();
|
|
_mainScreen->copyRectToSurface(*screen, rect.left, rect.top, rect);
|
|
_effectScreen->copyRectToSurface(*screen, rect.left, rect.top, rect);
|
|
_vm->_system->unlockScreen();
|
|
}
|
|
|
|
void RivenGraphics::enableCardUpdateScript(bool enable) {
|
|
_enableCardUpdateScript = enable;
|
|
}
|
|
|
|
const FliesEffect::FliesEffectData FliesEffect::_firefliesParameters = {
|
|
true,
|
|
true,
|
|
true,
|
|
true,
|
|
3.0,
|
|
0.7,
|
|
40,
|
|
2.0,
|
|
1.0,
|
|
8447718,
|
|
30,
|
|
10
|
|
};
|
|
|
|
const FliesEffect::FliesEffectData FliesEffect::_fliesParameters = {
|
|
false,
|
|
false,
|
|
false,
|
|
true,
|
|
8.0,
|
|
3.0,
|
|
80,
|
|
3.0,
|
|
1.0,
|
|
661528,
|
|
30,
|
|
10
|
|
};
|
|
|
|
FliesEffect::FliesEffect(MohawkEngine_Riven *vm, uint16 count, bool fireflies) :
|
|
_vm(vm) {
|
|
|
|
_effectSurface = _vm->_gfx->getEffectScreen();
|
|
_backSurface = _vm->_gfx->getBackScreen();
|
|
_gameRect = Common::Rect(608, 392);
|
|
|
|
if (fireflies) {
|
|
_parameters = &_firefliesParameters;
|
|
} else {
|
|
_parameters = &_fliesParameters;
|
|
}
|
|
|
|
_updatePeriodMs = 66;
|
|
_nextUpdateTime = _vm->_system->getMillis();
|
|
|
|
initFlies(count);
|
|
}
|
|
|
|
FliesEffect::~FliesEffect() {
|
|
|
|
}
|
|
|
|
void FliesEffect::initFlies(uint16 count) {
|
|
_fly.resize(count);
|
|
for (uint16 i = 0; i < _fly.size(); i++) {
|
|
initFlyRandomPosition(i);
|
|
}
|
|
}
|
|
|
|
void FliesEffect::initFlyRandomPosition(uint index) {
|
|
int posX = _vm->_rnd->getRandomNumber(_gameRect.right - 4);
|
|
int posY = _vm->_rnd->getRandomNumber(_gameRect.bottom - 4);
|
|
|
|
if (posY < 100) {
|
|
posY = 100;
|
|
}
|
|
|
|
initFlyAtPosition(index, posX, posY, 15);
|
|
}
|
|
|
|
int FliesEffect::randomBetween(int min, int max) {
|
|
return _vm->_rnd->getRandomNumber(max - min) + min;
|
|
}
|
|
|
|
void FliesEffect::initFlyAtPosition(uint index, int posX, int posY, int posZ) {
|
|
FliesEffectEntry &fly = _fly[index];
|
|
|
|
fly.posX = posX;
|
|
fly.posXFloat = posX;
|
|
fly.posY = posY;
|
|
fly.posYFloat = posY;
|
|
fly.posZ = posZ;
|
|
fly.light = true;
|
|
|
|
fly.framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration);
|
|
|
|
fly.hasBlur = false;
|
|
fly.directionAngleRad = randomBetween(0, 300) / 100.0f;
|
|
fly.directionAngleRadZ = randomBetween(0, 300) / 100.0f;
|
|
fly.speed = randomBetween(0, 100) / 100.0f;
|
|
}
|
|
|
|
void FliesEffect::update() {
|
|
if (_nextUpdateTime <= _vm->_system->getMillis()) {
|
|
_nextUpdateTime = _updatePeriodMs + _vm->_system->getMillis();
|
|
|
|
updateFlies();
|
|
draw();
|
|
updateScreen();
|
|
}
|
|
}
|
|
|
|
void FliesEffect::updateFlies() {
|
|
for (uint i = 0; i < _fly.size(); i++) {
|
|
updateFlyPosition(i);
|
|
|
|
if (_fly[i].posX < 1 || _fly[i].posX > _gameRect.right - 4 || _fly[i].posY > _gameRect.bottom - 4) {
|
|
initFlyRandomPosition(i);
|
|
}
|
|
|
|
if (_parameters->lightable) {
|
|
_fly[i].framesTillLightSwitch--;
|
|
|
|
if (_fly[i].framesTillLightSwitch <= 0) {
|
|
_fly[i].light = !_fly[i].light;
|
|
_fly[i].framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration);
|
|
_fly[i].hasBlur = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FliesEffect::updateFlyPosition(uint index) {
|
|
FliesEffectEntry &fly = _fly[index];
|
|
|
|
if (fly.directionAngleRad > 2.0f * M_PI) {
|
|
fly.directionAngleRad = fly.directionAngleRad - 2.0f * M_PI;
|
|
}
|
|
if (fly.directionAngleRad < 0.0f) {
|
|
fly.directionAngleRad = fly.directionAngleRad + 2.0f * M_PI;
|
|
}
|
|
if (fly.directionAngleRadZ > 2.0f * M_PI) {
|
|
fly.directionAngleRadZ = fly.directionAngleRadZ - 2.0f * M_PI;
|
|
}
|
|
if (fly.directionAngleRadZ < 0.0f) {
|
|
fly.directionAngleRadZ = fly.directionAngleRadZ + 2.0f * M_PI;
|
|
}
|
|
fly.posXFloat += cos(fly.directionAngleRad) * fly.speed;
|
|
fly.posYFloat += sin(fly.directionAngleRad) * fly.speed;
|
|
fly.posX = fly.posXFloat;
|
|
fly.posY = fly.posYFloat;
|
|
selectAlphaMap(
|
|
fly.posXFloat - fly.posX >= 0.5,
|
|
fly.posYFloat - fly.posY >= 0.5,
|
|
&fly.alphaMap,
|
|
&fly.width,
|
|
&fly.height);
|
|
fly.posZFloat += cos(fly.directionAngleRadZ) * (fly.speed / 2.0f);
|
|
fly.posZ = fly.posZFloat;
|
|
if (_parameters->canBlur && fly.speed > _parameters->blurSpeedTreshold) {
|
|
fly.hasBlur = true;
|
|
float blurPosXFloat = cos(fly.directionAngleRad + M_PI) * _parameters->blurDistance + fly.posXFloat;
|
|
float blurPosYFloat = sin(fly.directionAngleRad + M_PI) * _parameters->blurDistance + fly.posYFloat;
|
|
|
|
fly.blurPosX = blurPosXFloat;
|
|
fly.blurPosY = blurPosYFloat;
|
|
selectAlphaMap(
|
|
blurPosXFloat - fly.blurPosX >= 0.5,
|
|
blurPosYFloat - fly.blurPosY >= 0.5,
|
|
&fly.blurAlphaMap,
|
|
&fly.blurWidth,
|
|
&fly.blurHeight);
|
|
}
|
|
if (fly.posY >= 100) {
|
|
int maxAngularSpeed = _parameters->maxAcceleration;
|
|
if (fly.posZ > 15) {
|
|
maxAngularSpeed /= 2;
|
|
}
|
|
int angularSpeed = randomBetween(-maxAngularSpeed, maxAngularSpeed);
|
|
fly.directionAngleRad += angularSpeed / 100.0f;
|
|
} else {
|
|
// Make the flies go down if they are too high in the screen
|
|
int angularSpeed = randomBetween(0, 50);
|
|
if (fly.directionAngleRad >= M_PI / 2.0f && fly.directionAngleRad <= 3.0f * M_PI / 2.0f) {
|
|
// Going down
|
|
fly.directionAngleRad -= angularSpeed / 100.0f;
|
|
} else {
|
|
// Going up
|
|
fly.directionAngleRad += angularSpeed / 100.0f;
|
|
}
|
|
if (fly.posY < 1) {
|
|
initFlyRandomPosition(index);
|
|
}
|
|
}
|
|
if (fly.posZ >= 0) {
|
|
int distanceToScreenEdge;
|
|
if (fly.posX / 10 >= (_gameRect.right - fly.posX) / 10) {
|
|
distanceToScreenEdge = (_gameRect.right - fly.posX) / 10;
|
|
} else {
|
|
distanceToScreenEdge = fly.posX / 10;
|
|
}
|
|
if (distanceToScreenEdge > (_gameRect.bottom - fly.posY) / 10) {
|
|
distanceToScreenEdge = (_gameRect.bottom - fly.posY) / 10;
|
|
}
|
|
if (distanceToScreenEdge > 30) {
|
|
distanceToScreenEdge = 30;
|
|
}
|
|
if (fly.posZ <= distanceToScreenEdge) {
|
|
fly.directionAngleRadZ += randomBetween(-_parameters->maxAcceleration, _parameters->maxAcceleration) / 100.0f;
|
|
} else {
|
|
fly.posZ = distanceToScreenEdge;
|
|
fly.directionAngleRadZ += M_PI;
|
|
}
|
|
} else {
|
|
fly.posZ = 0;
|
|
fly.directionAngleRadZ += M_PI;
|
|
}
|
|
float minSpeed = _parameters->minSpeed - fly.posZ / 40.0f;
|
|
float maxSpeed = _parameters->maxSpeed - fly.posZ / 20.0f;
|
|
fly.speed += randomBetween(-_parameters->maxAcceleration, _parameters->maxAcceleration) / 100.0f;
|
|
if (fly.speed > maxSpeed) {
|
|
fly.speed -= randomBetween(0, 50) / 100.0f;
|
|
}
|
|
if (fly.speed < minSpeed) {
|
|
fly.speed += randomBetween(0, 50) / 100.0f;
|
|
}
|
|
}
|
|
|
|
void FliesEffect::selectAlphaMap(bool horGridOffset, bool vertGridoffset, const uint16 **alphaMap, uint *width, uint *height) {
|
|
static const uint16 alpha1[12] = {
|
|
8, 16, 8,
|
|
16, 32, 16,
|
|
8, 16, 8,
|
|
0, 0, 0
|
|
};
|
|
|
|
static const uint16 alpha2[12] = {
|
|
4, 12, 12, 4,
|
|
8, 24, 24, 8,
|
|
4, 12, 12, 4
|
|
};
|
|
|
|
static const uint16 alpha3[12] = {
|
|
4, 8, 4,
|
|
12, 24, 12,
|
|
12, 24, 12,
|
|
4, 8, 4
|
|
};
|
|
|
|
static const uint16 alpha4[16] = {
|
|
2, 6, 6, 2,
|
|
6, 18, 18, 6,
|
|
6, 18, 18, 6,
|
|
2, 6, 6, 2
|
|
};
|
|
|
|
static const uint16 alpha5[12] = {
|
|
4, 8, 4,
|
|
8, 32, 8,
|
|
4, 8, 4,
|
|
0, 0, 0
|
|
};
|
|
|
|
static const uint16 alpha6[12] = {
|
|
2, 6, 6, 2,
|
|
4, 24, 24, 4,
|
|
2, 6, 6, 2
|
|
};
|
|
|
|
static const uint16 alpha7[12] = {
|
|
2, 4, 2,
|
|
6, 24, 6,
|
|
6, 24, 6,
|
|
2, 4, 2
|
|
};
|
|
|
|
static const uint16 alpha8[16] = {
|
|
1, 3, 3, 1,
|
|
3, 18, 18, 3,
|
|
3, 18, 18, 3,
|
|
1, 3, 3, 1
|
|
};
|
|
|
|
struct AlphaMap {
|
|
bool horizontalGridOffset;
|
|
bool verticalGridOffset;
|
|
bool isLarge;
|
|
uint16 width;
|
|
uint16 height;
|
|
const uint16 *pixels;
|
|
};
|
|
|
|
static const AlphaMap alphaSelector[] = {
|
|
{ true, true, true, 4, 4, alpha4 },
|
|
{ true, true, false, 4, 4, alpha8 },
|
|
{ true, false, true, 4, 3, alpha2 },
|
|
{ true, false, false, 4, 3, alpha6 },
|
|
{ false, true, true, 3, 4, alpha3 },
|
|
{ false, true, false, 3, 4, alpha7 },
|
|
{ false, false, true, 3, 3, alpha1 },
|
|
{ false, false, false, 3, 3, alpha5 }
|
|
};
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(alphaSelector); i++) {
|
|
if (alphaSelector[i].horizontalGridOffset == horGridOffset
|
|
&& alphaSelector[i].verticalGridOffset == vertGridoffset
|
|
&& alphaSelector[i].isLarge == _parameters->isLarge) {
|
|
*alphaMap = alphaSelector[i].pixels;
|
|
*width = alphaSelector[i].width;
|
|
*height = alphaSelector[i].height;
|
|
return;
|
|
}
|
|
}
|
|
|
|
error("Unknown flies alpha map case");
|
|
}
|
|
|
|
void FliesEffect::draw() {
|
|
const Graphics::PixelFormat format = _effectSurface->format;
|
|
|
|
for (uint i = 0; i < _fly.size(); i++) {
|
|
FliesEffectEntry &fly = _fly[i];
|
|
uint32 color = _parameters->color32;
|
|
if (!fly.light) {
|
|
color = _fliesParameters.color32;
|
|
}
|
|
|
|
bool hoveringBrightBackground = false;
|
|
for (uint y = 0; y < fly.height; y++) {
|
|
uint16 *pixel = (uint16 *) _effectSurface->getBasePtr(fly.posX, fly.posY + y);
|
|
|
|
for (uint x = 0; x < fly.width; x++) {
|
|
byte r, g, b;
|
|
format.colorToRGB(*pixel, r, g, b);
|
|
|
|
if (_parameters->unlightIfTooBright) {
|
|
if (r >= 192 || g >= 192 || b >= 192) {
|
|
hoveringBrightBackground = true;
|
|
}
|
|
}
|
|
colorBlending(color, r, g, b, fly.alphaMap[fly.width * y + x] - fly.posZ);
|
|
|
|
*pixel = format.RGBToColor(r, g, b);
|
|
++pixel;
|
|
}
|
|
}
|
|
|
|
Common::Rect drawRect = Common::Rect(fly.width, fly.height);
|
|
drawRect.translate(fly.posX, fly.posY);
|
|
addToScreenDirtyRects(drawRect);
|
|
addToEffectsDirtyRects(drawRect);
|
|
|
|
if (fly.hasBlur) {
|
|
for (uint y = 0; y < fly.blurHeight; y++) {
|
|
uint16 *pixel = (uint16 *) _effectSurface->getBasePtr(fly.blurPosX, fly.blurPosY + y);
|
|
for (uint x = 0; x < fly.blurWidth; x++) {
|
|
byte r, g, b;
|
|
format.colorToRGB(*pixel, r, g, b);
|
|
|
|
colorBlending(color, r, g, b, fly.blurAlphaMap[fly.blurWidth * y + x] - fly.posZ);
|
|
|
|
*pixel = format.RGBToColor(r, g, b);
|
|
++pixel;
|
|
}
|
|
}
|
|
|
|
Common::Rect drawRect2 = Common::Rect(fly.blurWidth, fly.blurHeight);
|
|
drawRect2.translate(fly.blurPosX, fly.blurPosY);
|
|
addToScreenDirtyRects(drawRect2);
|
|
addToEffectsDirtyRects(drawRect2);
|
|
|
|
fly.hasBlur = false;
|
|
}
|
|
|
|
if (hoveringBrightBackground) {
|
|
fly.hasBlur = false;
|
|
if (_parameters->lightable) {
|
|
fly.light = false;
|
|
fly.framesTillLightSwitch = randomBetween(_parameters->minFramesLit, _parameters->minFramesLit + _parameters->maxLightDuration);
|
|
}
|
|
|
|
if (_vm->_rnd->getRandomBit()) {
|
|
fly.directionAngleRad += M_PI / 2.0;
|
|
} else {
|
|
fly.directionAngleRad -= M_PI / 2.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FliesEffect::colorBlending(uint32 flyColor, byte &r, byte &g, byte &b, int alpha) {
|
|
alpha = CLIP(alpha, 0, 32);
|
|
byte flyR = (flyColor & 0x000000FF) >> 0;
|
|
byte flyG = (flyColor & 0x0000FF00) >> 8;
|
|
byte flyB = (flyColor & 0x00FF0000) >> 16;
|
|
|
|
r = (32 * r + alpha * (flyR - r)) / 32;
|
|
g = (32 * g + alpha * (flyG - g)) / 32;
|
|
b = (32 * b + alpha * (flyB - b)) / 32;
|
|
}
|
|
|
|
void FliesEffect::updateScreen() {
|
|
for (uint i = 0; i < _screenSurfaceDirtyRects.size(); i++) {
|
|
const Common::Rect &rect = _screenSurfaceDirtyRects[i];
|
|
_vm->_system->copyRectToScreen(_effectSurface->getBasePtr(rect.left, rect.top),
|
|
_effectSurface->pitch, rect.left, rect.top,
|
|
rect.width(), rect.height()
|
|
);
|
|
}
|
|
_screenSurfaceDirtyRects.clear();
|
|
|
|
restoreEffectsSurface();
|
|
}
|
|
|
|
void FliesEffect::addToScreenDirtyRects(const Common::Rect &rect) {
|
|
for (uint i = 0; i < _screenSurfaceDirtyRects.size(); i++) {
|
|
if (rect.intersects(_screenSurfaceDirtyRects[i])) {
|
|
_screenSurfaceDirtyRects[i].extend(rect);
|
|
return;
|
|
}
|
|
}
|
|
|
|
_screenSurfaceDirtyRects.push_back(rect);
|
|
}
|
|
|
|
void FliesEffect::addToEffectsDirtyRects(const Common::Rect &rect) {
|
|
for (uint i = 0; i < _effectsSurfaceDirtyRects.size(); i++) {
|
|
if (rect.intersects(_effectsSurfaceDirtyRects[i])) {
|
|
_effectsSurfaceDirtyRects[i].extend(rect);
|
|
return;
|
|
}
|
|
}
|
|
|
|
_effectsSurfaceDirtyRects.push_back(rect);
|
|
}
|
|
|
|
void FliesEffect::restoreEffectsSurface() {
|
|
for (uint i = 0; i < _effectsSurfaceDirtyRects.size(); i++) {
|
|
const Common::Rect &rect = _effectsSurfaceDirtyRects[i];
|
|
_effectSurface->copyRectToSurface(*_backSurface, rect.left, rect.top, rect);
|
|
addToScreenDirtyRects(rect);
|
|
}
|
|
|
|
_effectsSurfaceDirtyRects.clear();
|
|
}
|
|
|
|
} // End of namespace Mohawk
|