ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

effects.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/myst3/database.h"
00024 #include "engines/myst3/effects.h"
00025 #include "engines/myst3/gfx.h"
00026 #include "engines/myst3/myst3.h"
00027 #include "engines/myst3/state.h"
00028 #include "engines/myst3/sound.h"
00029 
00030 #include "graphics/surface.h"
00031 
00032 namespace Myst3 {
00033 
00034 Effect::FaceMask::FaceMask() :
00035         surface(nullptr) {
00036 
00037     for (uint i = 0; i < 10; i++) {
00038         for (uint j = 0; j < 10; j++) {
00039             block[i][j] = false;
00040         }
00041     }
00042 }
00043 
00044 Effect::FaceMask::~FaceMask() {
00045     if (surface) {
00046         surface->free();
00047     }
00048 
00049     delete surface;
00050 }
00051 
00052 Common::Rect Effect::FaceMask::getBlockRect(uint x, uint y) {
00053     Common::Rect rect = Common::Rect(64, 64);
00054     rect.translate(x * 64, y * 64);
00055     return rect;
00056 }
00057 
00058 Effect::Effect(Myst3Engine *vm) :
00059         _vm(vm) {
00060 }
00061 
00062 Effect::~Effect() {
00063     for (FaceMaskMap::iterator it = _facesMasks.begin(); it != _facesMasks.end(); it++) {
00064         delete it->_value;
00065     }
00066 }
00067 
00068 bool Effect::loadMasks(const Common::String &room, uint32 id, DirectorySubEntry::ResourceType type) {
00069     bool isFrame = _vm->_state->getViewType() == kFrame;
00070 
00071     // Load the mask of each face
00072     for (uint i = 0; i < 6; i++) {
00073         const DirectorySubEntry *desc = _vm->getFileDescription(room, id, i + 1, type);
00074 
00075         if (desc) {
00076             Common::SeekableReadStream *data = desc->getData();
00077 
00078             // Check if we are overriding an existing mask
00079             delete _facesMasks[i];
00080             _facesMasks[i] = loadMask(data);
00081 
00082             // Frame masks are vertically flipped for some reason
00083             if (isFrame) {
00084                 _vm->_gfx->flipVertical(_facesMasks[i]->surface);
00085             }
00086 
00087             delete data;
00088         }
00089     }
00090 
00091     if (_facesMasks.empty())
00092         return false;
00093 
00094     return true;
00095 }
00096 
00097 Effect::FaceMask *Effect::loadMask(Common::SeekableReadStream *maskStream) {
00098     FaceMask *mask = new FaceMask();
00099     mask->surface = new Graphics::Surface();
00100     mask->surface->create(640, 640, Graphics::PixelFormat::createFormatCLUT8());
00101 
00102     uint32 headerOffset = 0;
00103     uint32 dataOffset = 0;
00104 
00105     while (headerOffset < 400) {
00106         int blockX = (headerOffset / sizeof(dataOffset)) % 10;
00107         int blockY = (headerOffset / sizeof(dataOffset)) / 10;
00108 
00109         maskStream->seek(headerOffset, SEEK_SET);
00110         dataOffset = maskStream->readUint32LE();
00111         headerOffset = maskStream->pos();
00112 
00113         if (dataOffset != 0) {
00114             maskStream->seek(dataOffset, SEEK_SET);
00115 
00116             for(int i = 63; i >= 0; i--) {
00117                 int x = 0;
00118                 byte numValues = maskStream->readByte();
00119                 for (int j = 0; j < numValues; j++) {
00120                     byte repeat = maskStream->readByte();
00121                     byte value = maskStream->readByte();
00122                     for (int k = 0; k < repeat; k++) {
00123                         ((uint8*)mask->surface->getPixels())[((blockY * 64) + i) * 640 + blockX * 64 + x] = value;
00124                         x++;
00125                     }
00126 
00127                     // If a block has at least one non zero value, mark it as active
00128                     if (value != 0) {
00129                         mask->block[blockX][blockY] = true;
00130                     }
00131                 }
00132             }
00133         }
00134     }
00135 
00136     return mask;
00137 }
00138 
00139 Common::Rect Effect::getUpdateRectForFace(uint face) {
00140     FaceMask *mask = _facesMasks.getVal(face);
00141     if (!mask)
00142         error("No mask for face %d", face);
00143 
00144     Common::Rect rect;
00145 
00146     // Build a rectangle containing all the active effect blocks
00147     for (uint i = 0; i < 10; i++) {
00148         for (uint j = 0; j < 10; j++) {
00149             if (mask->block[i][j]) {
00150                 if (rect.isEmpty()) {
00151                     rect = FaceMask::getBlockRect(i, j);
00152                 } else {
00153                     rect.extend(FaceMask::getBlockRect(i, j));
00154                 }
00155             }
00156         }
00157     }
00158 
00159     return rect;
00160 }
00161 
00162 WaterEffect::WaterEffect(Myst3Engine *vm) :
00163         Effect(vm),
00164         _lastUpdate(0),
00165         _step(0) {
00166 }
00167 
00168 WaterEffect::~WaterEffect() {
00169 }
00170 
00171 WaterEffect *WaterEffect::create(Myst3Engine *vm, uint32 id) {
00172     WaterEffect *s = new WaterEffect(vm);
00173 
00174     if (!s->loadMasks("", id, DirectorySubEntry::kWaterEffectMask)) {
00175         delete s;
00176         return 0;
00177     }
00178 
00179     return s;
00180 }
00181 
00182 bool WaterEffect::isRunning() {
00183     return _vm->_state->getWaterEffectActive()
00184             && _vm->_state->getWaterEffectRunning();
00185 }
00186 
00187 bool WaterEffect::update() {
00188     if (!isRunning()) {
00189         return false;
00190     }
00191 
00192     if (g_system->getMillis() - _lastUpdate >= 1000 / (uint32)_vm->_state->getWaterEffectSpeed()) {
00193         _lastUpdate = g_system->getMillis();
00194 
00195         _step++;
00196         if (_step > _vm->_state->getWaterEffectMaxStep())
00197             _step = 0;
00198 
00199         float position = _step / (float)_vm->_state->getWaterEffectMaxStep();
00200 
00201         doStep(position, _vm->_state->getViewType() == kFrame);
00202 
00203         return true;
00204     }
00205 
00206     return false;
00207 }
00208 
00209 void WaterEffect::doStep(float position, bool isFrame) {
00210     double timeOffset;
00211     double frequency;
00212     double ampl;
00213 
00214     timeOffset = position * 2 * M_PI;
00215     frequency = _vm->_state->getWaterEffectFrequency() * 0.1;
00216 
00217     ampl = _vm->_state->getWaterEffectAmpl() / 10.0 / 2.0;
00218     for (uint i = 0; i < 640; i++) {
00219         double ampl1;
00220         if (i < 320)
00221             ampl1 = i / 320 + 1.0;
00222         else
00223             ampl1 = (640 - i) / 320 + 1.0;
00224 
00225         _bottomDisplacement[i] = sin(i / 640.0 * frequency * 2 * M_PI + timeOffset) / 2 * ampl1 * ampl;
00226     }
00227 
00228     // FIXME: The original sets this to WaterEffectAttenuation, which causes
00229     // glitches here
00230     uint32 attenuation = 640;
00231     for (uint i = 0; i < attenuation; i++) {
00232         double ampl2 = attenuation / (attenuation - i + 1.0);
00233 
00234         int8 value = sin(i / 640.0 * frequency * 2 * M_PI * ampl2 + timeOffset) / 2 * 1.0 / ampl2 * ampl;
00235 
00236         if (!isFrame) {
00237             _verticalDisplacement[i] = value;
00238         } else {
00239             _verticalDisplacement[attenuation - 1 - i] = value;
00240         }
00241     }
00242 
00243     for (uint i = 0; i < 640; i++) {
00244         double ampl3 = sin(i / 640.0 * frequency * 2 * M_PI + timeOffset) / 2.0;
00245 
00246         _horizontalDisplacements[0][i] = ampl3 * 1.25 * ampl + 0.5;
00247         _horizontalDisplacements[1][i] = ampl3 * 1.00 * ampl + 0.5;
00248         _horizontalDisplacements[2][i] = ampl3 * 0.75 * ampl + 0.5;
00249         _horizontalDisplacements[3][i] = ampl3 * 0.50 * ampl + 0.5;
00250         _horizontalDisplacements[4][i] = ampl3 * 0.25 * ampl + 0.5;
00251     }
00252 }
00253 
00254 void WaterEffect::applyForFace(uint face, Graphics::Surface *src, Graphics::Surface *dst) {
00255     if (!isRunning()) {
00256         return;
00257     }
00258 
00259     FaceMask *mask = _facesMasks.getVal(face);
00260 
00261     if (!mask)
00262         error("No mask for face %d", face);
00263 
00264     apply(src, dst, mask->surface, face == 1, _vm->_state->getWaterEffectAmpl());
00265 }
00266 
00267 void WaterEffect::apply(Graphics::Surface *src, Graphics::Surface *dst, Graphics::Surface *mask, bool bottomFace, int32 waterEffectAmpl) {
00268     int32 waterEffectAttenuation = _vm->_state->getWaterEffectAttenuation();
00269     int32 waterEffectAmplOffset = _vm->_state->getWaterEffectAmplOffset();
00270 
00271     int8 *hDisplacement = nullptr;
00272     int8 *vDisplacement = nullptr;
00273 
00274     if (bottomFace) {
00275         hDisplacement = _bottomDisplacement;
00276         vDisplacement = _bottomDisplacement;
00277     } else {
00278         vDisplacement = _verticalDisplacement;
00279     }
00280 
00281     uint32 *dstPtr = (uint32 *)dst->getPixels();
00282     byte *maskPtr = (byte *)mask->getPixels();
00283 
00284     for (uint y = 0; y < dst->h; y++) {
00285         if (!bottomFace) {
00286             uint32 strength = (320 * (9 - y / 64)) / waterEffectAttenuation;
00287             if (strength > 4)
00288                 strength = 4;
00289             hDisplacement = _horizontalDisplacements[strength];
00290         }
00291 
00292         for (uint x = 0; x < dst->w; x++) {
00293             int8 maskValue = *maskPtr;
00294 
00295             if (maskValue != 0) {
00296                 int8 xOffset = hDisplacement[x];
00297                 int8 yOffset = vDisplacement[y];
00298 
00299                 if (maskValue < 8) {
00300                     maskValue -= waterEffectAmplOffset;
00301                     if (maskValue < 0) {
00302                         maskValue = 0;
00303                     }
00304 
00305                     if (xOffset >= 0) {
00306                         if (xOffset > maskValue)
00307                             xOffset = maskValue;
00308                     } else {
00309                         if (-xOffset > maskValue)
00310                             xOffset = -maskValue;
00311                     }
00312                     if (yOffset >= 0) {
00313                         if (yOffset > maskValue)
00314                             yOffset = maskValue;
00315                     } else {
00316                         if (-yOffset > maskValue)
00317                             yOffset = -maskValue;
00318                     }
00319                 }
00320 
00321                 uint32 srcValue1 = *(uint32 *) src->getBasePtr(x + xOffset, y + yOffset);
00322                 uint32 srcValue2 = *(uint32 *) src->getBasePtr(x, y);
00323 
00324 #ifdef SCUMM_BIG_ENDIAN
00325                 *dstPtr = 0x000000FF | ((0x7F7F7F00 & (srcValue1 >> 1)) + (0x7F7F7F00 & (srcValue2 >> 1)));
00326 #else
00327                 *dstPtr = 0xFF000000 | ((0x007F7F7F & (srcValue1 >> 1)) + (0x007F7F7F & (srcValue2 >> 1)));
00328 #endif
00329             }
00330 
00331             maskPtr++;
00332             dstPtr++;
00333         }
00334     }
00335 }
00336 
00337 LavaEffect::LavaEffect(Myst3Engine *vm) :
00338         Effect(vm),
00339         _lastUpdate(0),
00340         _step(0) {
00341 }
00342 
00343 LavaEffect::~LavaEffect() {
00344 
00345 }
00346 
00347 LavaEffect *LavaEffect::create(Myst3Engine *vm, uint32 id) {
00348     LavaEffect *s = new LavaEffect(vm);
00349 
00350     if (!s->loadMasks("", id, DirectorySubEntry::kLavaEffectMask)) {
00351         delete s;
00352         return 0;
00353     }
00354 
00355     return s;
00356 }
00357 
00358 bool LavaEffect::update() {
00359     if (!_vm->_state->getLavaEffectActive()) {
00360         return false;
00361     }
00362 
00363     if (g_system->getMillis() - _lastUpdate >= 1000 / (uint32)_vm->_state->getLavaEffectSpeed()) {
00364         _lastUpdate = g_system->getMillis();
00365 
00366         _step += _vm->_state->getLavaEffectStepSize();
00367 
00368         doStep(_step, _vm->_state->getLavaEffectAmpl() / 10);
00369 
00370         if (_step > 256)
00371             _step -= 256;
00372 
00373         return true;
00374     }
00375 
00376     return false;
00377 }
00378 
00379 void LavaEffect::doStep(int32 position, float ampl) {
00380     for (uint i = 0; i < 256; i++) {
00381         _displacement[i] = (sin((i + position) * 2 * M_PI / 256.0) + 1.0) * ampl;
00382     }
00383 }
00384 
00385 void LavaEffect::applyForFace(uint face, Graphics::Surface *src, Graphics::Surface *dst) {
00386     if (!_vm->_state->getLavaEffectActive()) {
00387         return;
00388     }
00389 
00390     FaceMask *mask = _facesMasks.getVal(face);
00391 
00392     if (!mask)
00393         error("No mask for face %d", face);
00394 
00395     uint32 *dstPtr = (uint32 *)dst->getPixels();
00396     byte *maskPtr = (byte *)mask->surface->getPixels();
00397 
00398     for (uint y = 0; y < dst->h; y++) {
00399         for (uint x = 0; x < dst->w; x++) {
00400             uint8 maskValue = *maskPtr;
00401 
00402             if (maskValue != 0) {
00403                 int32 xOffset= _displacement[(maskValue + y) % 256];
00404                 int32 yOffset = _displacement[maskValue % 256];
00405                 int32 maxOffset = (maskValue >> 6) & 0x3;
00406 
00407                 if (yOffset > maxOffset) {
00408                     yOffset = maxOffset;
00409                 }
00410                 if (xOffset > maxOffset) {
00411                     xOffset = maxOffset;
00412                 }
00413 
00414 //              uint32 srcValue1 = *(uint32 *)src->getBasePtr(x + xOffset, y + yOffset);
00415 //              uint32 srcValue2 = *(uint32 *)src->getBasePtr(x, y);
00416 //
00417 //              *dstPtr = 0xFF000000 | ((0x007F7F7F & (srcValue1 >> 1)) + (0x007F7F7F & (srcValue2 >> 1)));
00418 
00419                 // TODO: The original does "blending" as above, but strangely
00420                 // this looks more like the original rendering
00421                 *dstPtr = *(uint32 *)src->getBasePtr(x + xOffset, y + yOffset);
00422             }
00423 
00424             maskPtr++;
00425             dstPtr++;
00426         }
00427     }
00428 }
00429 
00430 MagnetEffect::MagnetEffect(Myst3Engine *vm) :
00431         Effect(vm),
00432         _lastSoundId(0),
00433         _lastTime(0),
00434         _position(0),
00435         _lastAmpl(0),
00436         _shakeStrength(nullptr) {
00437 }
00438 
00439 MagnetEffect::~MagnetEffect() {
00440     delete _shakeStrength;
00441 }
00442 
00443 MagnetEffect *MagnetEffect::create(Myst3Engine *vm, uint32 id) {
00444     if (!vm->_state->getMagnetEffectSound()) {
00445         return nullptr;
00446     }
00447 
00448     MagnetEffect *s = new MagnetEffect(vm);
00449     s->loadMasks("", id, DirectorySubEntry::kMagneticEffectMask);
00450     return s;
00451 }
00452 
00453 bool MagnetEffect::update() {
00454     int32 soundId = _vm->_state->getMagnetEffectSound();
00455     if (!soundId) {
00456         // The effect is no longer active
00457         _lastSoundId = 0;
00458         _vm->_state->setMagnetEffectUnk3(0);
00459 
00460         delete _shakeStrength;
00461         _shakeStrength = nullptr;
00462 
00463         return false;
00464     }
00465 
00466     if (soundId != _lastSoundId) {
00467         // The sound changed since last update
00468         _lastSoundId = soundId;
00469 
00470         const DirectorySubEntry *desc = _vm->getFileDescription("", _vm->_state->getMagnetEffectNode(), 0, DirectorySubEntry::kRawData);
00471         if (!desc)
00472             error("Magnet effect support file %d does not exist", _vm->_state->getMagnetEffectNode());
00473 
00474         delete _shakeStrength;
00475         _shakeStrength = desc->getData();
00476     }
00477 
00478     int32 soundPosition = _vm->_sound->playedFrames(soundId);
00479     if (_shakeStrength && soundPosition >= 0) {
00480         // Update the shake amplitude according to the position in the playing sound.
00481         // This has no in-game effect (same as original) due to var 122 being 0.
00482         _shakeStrength->seek(soundPosition, SEEK_SET);
00483         _vm->_state->setMagnetEffectUnk3(_shakeStrength->readByte());
00484 
00485         // Update the vertical displacements
00486         float ampl = (_vm->_state->getMagnetEffectUnk1() + _vm->_state->getMagnetEffectUnk3())
00487                 / (float)_vm->_state->getMagnetEffectUnk2();
00488 
00489         if (ampl != _lastAmpl) {
00490             for (uint i = 0; i < 256; i++) {
00491                 _verticalDisplacement[i] = sin(i * 2 * M_PI / 255.0) * ampl;
00492             }
00493 
00494             _lastAmpl = ampl;
00495         }
00496 
00497         // Update the position in the effect cycle
00498         uint32 time = g_system->getMillis();
00499         if (_lastTime) {
00500             _position += (float)_vm->_state->getMagnetEffectSpeed() * (time - _lastTime) / 1000 / 10;
00501 
00502             while (_position > 1.0) {
00503                 _position -= 1.0;
00504             }
00505         }
00506         _lastTime = time;
00507     } else {
00508         _vm->_state->setMagnetEffectUnk3(0);
00509     }
00510 
00511     return true;
00512 }
00513 
00514 void MagnetEffect::applyForFace(uint face, Graphics::Surface *src, Graphics::Surface *dst) {
00515     FaceMask *mask = _facesMasks.getVal(face);
00516 
00517     if (!mask)
00518         error("No mask for face %d", face);
00519 
00520     apply(src, dst, mask->surface, _position * 256.0);
00521 }
00522 
00523 void MagnetEffect::apply(Graphics::Surface *src, Graphics::Surface *dst, Graphics::Surface *mask, int32 position) {
00524     uint32 *dstPtr = (uint32 *)dst->getPixels();
00525     byte *maskPtr = (byte *)mask->getPixels();
00526 
00527     for (uint y = 0; y < dst->h; y++) {
00528         for (uint x = 0; x < dst->w; x++) {
00529             uint8 maskValue = *maskPtr;
00530 
00531             if (maskValue != 0) {
00532                 int32 displacement = _verticalDisplacement[(maskValue + position) % 256];
00533                 int32 displacedY = CLIP<int32>(y + displacement, 0, src->h - 1);
00534 
00535                 uint32 srcValue1 = *(uint32 *) src->getBasePtr(x, displacedY);
00536                 uint32 srcValue2 = *(uint32 *) src->getBasePtr(x, y);
00537 
00538 #ifdef SCUMM_BIG_ENDIAN
00539                 *dstPtr = 0x000000FF | ((0x7F7F7F00 & (srcValue1 >> 1)) + (0x7F7F7F00 & (srcValue2 >> 1)));
00540 #else
00541                 *dstPtr = 0xFF000000 | ((0x007F7F7F & (srcValue1 >> 1)) + (0x007F7F7F & (srcValue2 >> 1)));
00542 #endif
00543             }
00544 
00545             maskPtr++;
00546             dstPtr++;
00547         }
00548     }
00549 }
00550 
00551 ShakeEffect::ShakeEffect(Myst3Engine *vm) :
00552         Effect(vm),
00553         _lastTick(0),
00554         _magnetEffectShakeStep(0),
00555         _pitchOffset(0),
00556         _headingOffset(0) {
00557 }
00558 
00559 ShakeEffect::~ShakeEffect() {
00560 }
00561 
00562 ShakeEffect *ShakeEffect::create(Myst3Engine *vm) {
00563     if (vm->_state->getShakeEffectAmpl() == 0) {
00564         return nullptr;
00565     }
00566 
00567     return new ShakeEffect(vm);
00568 }
00569 
00570 bool ShakeEffect::update() {
00571     // Check if the effect is active
00572     int32 ampl = _vm->_state->getShakeEffectAmpl();
00573     if (ampl == 0) {
00574         return false;
00575     }
00576 
00577     // Check if the effect needs to be updated
00578     uint tick = _vm->_state->getTickCount();
00579     if (tick < _lastTick + _vm->_state->getShakeEffectTickPeriod()) {
00580         return false;
00581     }
00582 
00583     if (_vm->_state->getMagnetEffectUnk3()) {
00584         // If the magnet effect is also active, use its parameters
00585         float magnetEffectAmpl = (_vm->_state->getMagnetEffectUnk1() + _vm->_state->getMagnetEffectUnk3()) / 32.0;
00586 
00587         float shakeEffectAmpl;
00588         if (_magnetEffectShakeStep >= 2) {
00589             shakeEffectAmpl = ampl;
00590         } else {
00591             shakeEffectAmpl = -ampl;
00592         }
00593         _pitchOffset = shakeEffectAmpl / 200.0 * magnetEffectAmpl;
00594 
00595         if (_magnetEffectShakeStep >= 1 && _magnetEffectShakeStep <= 2) {
00596             shakeEffectAmpl = ampl;
00597         } else {
00598             shakeEffectAmpl = -ampl;
00599         }
00600         _headingOffset = shakeEffectAmpl / 200.0 * magnetEffectAmpl;
00601 
00602         _magnetEffectShakeStep++;
00603         _magnetEffectShakeStep %= 3;
00604     } else {
00605         // Shake effect only
00606         uint randomAmpl;
00607 
00608         randomAmpl = _vm->_rnd->getRandomNumberRng(0, ampl);
00609         _pitchOffset = (randomAmpl - ampl / 2.0) / 100.0;
00610 
00611         randomAmpl = _vm->_rnd->getRandomNumberRng(0, ampl);
00612         _headingOffset = (randomAmpl - ampl / 2.0) / 100.0;
00613     }
00614 
00615     _lastTick = tick;
00616 
00617     return true;
00618 }
00619 
00620 void ShakeEffect::applyForFace(uint face, Graphics::Surface* src, Graphics::Surface* dst) {
00621 }
00622 
00623 RotationEffect::RotationEffect(Myst3Engine *vm) :
00624         Effect(vm),
00625         _lastUpdate(0),
00626         _headingOffset(0) {
00627 }
00628 
00629 RotationEffect::~RotationEffect() {
00630 }
00631 
00632 RotationEffect *RotationEffect::create(Myst3Engine *vm) {
00633     if (vm->_state->getRotationEffectSpeed() == 0) {
00634         return nullptr;
00635     }
00636 
00637     return new RotationEffect(vm);
00638 }
00639 
00640 bool RotationEffect::update() {
00641     // Check if the effect is active
00642     int32 speed = _vm->_state->getRotationEffectSpeed();
00643     if (speed == 0) {
00644         return false;
00645     }
00646 
00647     if (_lastUpdate != 0) {
00648         _headingOffset = speed * (g_system->getMillis() - _lastUpdate) / 1000.0;
00649     }
00650 
00651     _lastUpdate = g_system->getMillis();
00652 
00653     return true;
00654 }
00655 
00656 void RotationEffect::applyForFace(uint face, Graphics::Surface* src, Graphics::Surface* dst) {
00657 }
00658 
00659 bool ShieldEffect::loadPattern() {
00660     // Read the shield effect support data
00661     const DirectorySubEntry *desc = _vm->getFileDescription("NARA", 10000, 0, DirectorySubEntry::kRawData);
00662     if (!desc) {
00663         return false;
00664     }
00665 
00666     Common::MemoryReadStream *stream = desc->getData();
00667     if (stream->size() != 4096) {
00668         error("Incorrect shield effect support file size %d", stream->size());
00669     }
00670 
00671     stream->read(_pattern, 4096);
00672 
00673     delete stream;
00674 
00675     return true;
00676 }
00677 
00678 ShieldEffect::ShieldEffect(Myst3Engine *vm):
00679     Effect(vm),
00680     _lastTick(0),
00681     _amplitude(1.0),
00682     _amplitudeIncrement(1.0 / 64.0) {
00683 }
00684 
00685 ShieldEffect::~ShieldEffect() {
00686 
00687 }
00688 
00689 ShieldEffect *ShieldEffect::create(Myst3Engine *vm, uint32 id) {
00690     uint32 room = vm->_state->getLocationRoom();
00691     uint32 node = vm->_state->getLocationNode();
00692 
00693     // This effect can only be found on Narayan cube nodes
00694     if (room != kRoomNarayan || node >= 100)
00695         return nullptr;
00696 
00697     ShieldEffect *s = new ShieldEffect(vm);
00698 
00699     if (!s->loadPattern()) {
00700         delete s;
00701         return nullptr; // We don't have the effect file
00702     }
00703 
00704     bool outerShieldUp = vm->_state->getOuterShieldUp();
00705     bool innerShieldUp = vm->_state->getInnerShieldUp();
00706     int32 saavedroStatus = vm->_state->getSaavedroStatus();
00707 
00708     bool hasMasks = false;
00709 
00710     int32 innerShieldMaskNode = 0;
00711     if (innerShieldUp) {
00712         innerShieldMaskNode = node + 100;
00713     }
00714 
00715     if (outerShieldUp) {
00716         hasMasks |= s->loadMasks("NARA", node + 300, DirectorySubEntry::kShieldEffectMask);
00717         if (saavedroStatus == 2) {
00718             innerShieldMaskNode = node + 200;
00719         }
00720     }
00721 
00722     if (innerShieldMaskNode) {
00723         hasMasks |= s->loadMasks("NARA", innerShieldMaskNode, DirectorySubEntry::kShieldEffectMask);
00724     }
00725 
00726     if (innerShieldMaskNode && innerShieldUp && node > 6) {
00727         hasMasks |= s->loadMasks("NARA", node + 100, DirectorySubEntry::kShieldEffectMask);
00728     }
00729 
00730     if (!hasMasks) {
00731         delete s;
00732         return nullptr;
00733     }
00734 
00735     return s;
00736 }
00737 
00738 bool ShieldEffect::update() {
00739     if (_vm->_state->getTickCount() == _lastTick)
00740         return false;
00741 
00742     _lastTick = _vm->_state->getTickCount();
00743 
00744     // Update the amplitude, varying between 1.0 and 4.0
00745     _amplitude += _amplitudeIncrement;
00746 
00747     if (_amplitude >= 4.0) {
00748         _amplitude = 4.0;
00749         _amplitudeIncrement = -1.0 / 64.0;
00750     } else if (_amplitude <= 1.0) {
00751         _amplitude = 1.0;
00752         _amplitudeIncrement = 1.0 / 64.0;
00753     }
00754 
00755     // Update the support data
00756     for (uint i = 0; i < ARRAYSIZE(_pattern); i++) {
00757         _pattern[i] += 2; // Intentional overflow
00758     }
00759 
00760     // Update the displacement offsets
00761     for (uint i = 0; i < 256; i++) {
00762         _displacement[i] = (sin(i * 2 * M_PI / 255.0) + 1.0) * _amplitude;
00763     }
00764 
00765     return true;
00766 }
00767 
00768 void ShieldEffect::applyForFace(uint face, Graphics::Surface *src, Graphics::Surface *dst) {
00769     if (!_vm->_state->getShieldEffectActive()) {
00770         return;
00771     }
00772 
00773     FaceMask *mask = _facesMasks.getVal(face);
00774 
00775     if (!mask)
00776         error("No mask for face %d", face);
00777 
00778     uint32 *dstPtr = (uint32 *)dst->getPixels();
00779     byte *maskPtr = (byte *)mask->surface->getPixels();
00780 
00781     for (uint y = 0; y < dst->h; y++) {
00782         for (uint x = 0; x < dst->w; x++) {
00783             uint8 maskValue = *maskPtr;
00784 
00785             if (maskValue != 0) {
00786                 int32 yOffset = _displacement[_pattern[(y % 64) * 64 + (x % 64)]];
00787 
00788                 if (yOffset > maskValue) {
00789                     yOffset = maskValue;
00790                 }
00791 
00792                 *dstPtr = *(uint32 *)src->getBasePtr(x, y + yOffset);
00793             }
00794 
00795             maskPtr++;
00796             dstPtr++;
00797         }
00798     }
00799 }
00800 
00801 } // End of namespace Myst3


Generated on Sat Feb 16 2019 05:00:49 for ResidualVM by doxygen 1.7.1
curved edge   curved edge