aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk/riven_graphics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mohawk/riven_graphics.cpp')
-rw-r--r--engines/mohawk/riven_graphics.cpp463
1 files changed, 463 insertions, 0 deletions
diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp
index d5cb3536a1..573eddf7d8 100644
--- a/engines/mohawk/riven_graphics.cpp
+++ b/engines/mohawk/riven_graphics.cpp
@@ -57,6 +57,7 @@ RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm
_creditsPos = 0;
_transitionSpeed = 0;
+ _fliesEffect = nullptr;
}
RivenGraphics::~RivenGraphics() {
@@ -65,6 +66,7 @@ RivenGraphics::~RivenGraphics() {
_mainScreen->free();
delete _mainScreen;
delete _bitmapDecoder;
+ delete _fliesEffect;
}
MohawkSurface *RivenGraphics::decodeImage(uint16 id) {
@@ -447,4 +449,465 @@ void RivenGraphics::applyScreenUpdate(bool force) {
}
}
+void RivenGraphics::setFliesEffect(uint16 count, bool fireflies) {
+ delete _fliesEffect;
+ _fliesEffect = new FliesEffect(_vm, count, fireflies);
+}
+
+void RivenGraphics::clearFliesEffect() {
+ delete _fliesEffect;
+ _fliesEffect = nullptr;
+}
+
+void RivenGraphics::runFliesEffect() {
+ if (_fliesEffect) {
+ _fliesEffect->update();
+ }
+}
+
+Graphics::Surface *RivenGraphics::getBackScreen() {
+ return _mainScreen;
+}
+
+Graphics::Surface *RivenGraphics::getEffectScreen() {
+ return _effectScreen;
+}
+
+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 - 3);
+ int posY = _vm->_rnd->getRandomNumber(_gameRect.bottom - 3);
+
+ 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