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

puzzles.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/puzzles.h"
00024 #include "engines/myst3/ambient.h"
00025 #include "engines/myst3/menu.h"
00026 #include "engines/myst3/myst3.h"
00027 #include "engines/myst3/node.h"
00028 #include "engines/myst3/state.h"
00029 #include "engines/myst3/sound.h"
00030 
00031 #include "common/config-manager.h"
00032 
00033 namespace Myst3 {
00034 
00035 Puzzles::Puzzles(Myst3Engine *vm) :
00036         _vm(vm) {
00037 }
00038 
00039 Puzzles::~Puzzles() {
00040 }
00041 
00042 void Puzzles::run(uint16 id, uint16 arg0, uint16 arg1, uint16 arg2) {
00043     switch (id) {
00044     case 1:
00045         leversBall(arg0);
00046         break;
00047     case 2:
00048         tesla(arg0, arg1, arg2);
00049         break;
00050     case 3:
00051         resonanceRingControl();
00052         break;
00053     case 4:
00054         resonanceRingsLaunchBall();
00055         break;
00056     case 5:
00057         resonanceRingsLights();
00058         break;
00059     case 6:
00060         pinball(arg0);
00061         break;
00062     case 7:
00063         weightDrag(arg0, arg1);
00064         break;
00065     case 8:
00066         journalSaavedro(arg0);
00067         break;
00068     case 9:
00069         journalAtrus(arg0, arg1);
00070         break;
00071     case 10:
00072         symbolCodesInit(arg0, arg1, arg2);
00073         break;
00074     case 11:
00075         symbolCodesClick(arg0);
00076         break;
00077     case 12:
00078         railRoadSwitchs();
00079         break;
00080     case 13:
00081         rollercoaster();
00082         break;
00083     case 14:
00084         projectorLoadBitmap(arg0);
00085         break;
00086     case 15:
00087         projectorAddSpotItem(arg0, arg1, arg2);
00088         break;
00089     case 16:
00090         projectorUpdateCoordinates();
00091         break;
00092     case 17:
00093         _vm->settingsLoadToVars();
00094         break;
00095     case 18:
00096         _vm->settingsApplyFromVars();
00097         break;
00098     case 19:
00099         settingsSave();
00100         break;
00101     case 20:
00102         _vm->_menu->saveLoadAction(arg0, arg1);
00103         break;
00104     case 21:
00105         mainMenu(arg0);
00106         break;
00107     case 22:
00108         updateSoundScriptTimer();
00109         break;
00110     case 23:
00111         _vm->loadNodeSubtitles(arg0);
00112         break;
00113     case 25:
00114         checkCanSave(); // Xbox specific
00115         break;
00116     default:
00117         warning("Puzzle %d is not implemented", id);
00118     }
00119 }
00120 
00121 void Puzzles::_drawForVarHelper(int16 var, int32 startValue, int32 endValue) {
00122     uint startTick = _vm->_state->getTickCount();
00123     uint currentTick = startTick;
00124     uint numValues = abs(endValue - startValue);
00125     uint endTick = startTick + 2 * numValues;
00126 
00127     int16 var2 = var;
00128 
00129     if (var < 0)
00130         var = -var;
00131     if (var2 < 0)
00132         var2 = -var2 + 1;
00133 
00134     if (startTick < endTick) {
00135         int currentValue = -9999;
00136         while (1) {
00137             int nextValue = (currentTick - startTick) / 2;
00138             if (currentValue != nextValue) {
00139                 currentValue = nextValue;
00140 
00141                 int16 varValue;
00142                 if (endValue > startValue)
00143                     varValue = startValue + currentValue;
00144                 else
00145                     varValue = startValue - currentValue;
00146 
00147                 _vm->_state->setVar(var, varValue);
00148                 _vm->_state->setVar(var2, varValue);
00149             }
00150 
00151             _vm->processInput(false);
00152             _vm->drawFrame();
00153             currentTick = _vm->_state->getTickCount();
00154 
00155             if (currentTick > endTick || _vm->shouldQuit())
00156                 break;
00157         }
00158     }
00159 
00160     _vm->_state->setVar(var, endValue);
00161     _vm->_state->setVar(var2, endValue);
00162 }
00163 
00164 void Puzzles::_drawXTicks(uint16 ticks) {
00165     uint32 endTick = _vm->_state->getTickCount() + ticks;
00166 
00167     while (_vm->_state->getTickCount() < endTick && !_vm->shouldQuit()) {
00168         _vm->processInput(false);
00169         _vm->drawFrame();
00170     }
00171 }
00172 
00173 void Puzzles::leversBall(int16 var) {
00174     struct NewPosition {
00175         bool newLeft;
00176         bool newRight;
00177         uint16 newBallPosition;
00178         uint16 movieStart;
00179         uint16 movieEnd;
00180         uint16 movieBallStart;
00181         uint16 movieBallEnd;
00182     };
00183 
00184     struct Move {
00185         int16 oldLeft;
00186         int16 oldRight;
00187         uint16 oldBallPosition;
00188         NewPosition p[2];
00189     };
00190 
00191     static const Move moves[] = {
00192         {   0,   1,   2, { { 1,   1,   2, 127, 147,   0,   0 }, { 0,   0,   0, 703, 735,   0,   0 } } },
00193         {   0,   0,   4, { { 1,   0,   4,  43,  63,   0,   0 }, { 0,   1,   4,  64,  84,   0,   0 } } },
00194         {   0,   0,   1, { { 1,   0,   1,  85, 105,   0,   0 }, { 0,   1,   1,  22,  42,   0,   0 } } },
00195         {   1,   0,   4, { { 1,   1,   3, 514, 534, 169, 217 }, { 0,   0,   4, 577, 597,   0,   0 } } },
00196         {   1,   0,   3, { { 1,   1,   3, 493, 513,   0,   0 }, { 0,   0,   4, 451, 471, 410, 450 } } },
00197         {   1,   0,   1, { { 1,   1,   2, 472, 492, 312, 360 }, { 0,   0,   1, 598, 618,   0,   0 } } },
00198         {   0,   1,   4, { { 1,   1,   3, 148, 168, 169, 217 }, { 0,   0,   4, 619, 639,   0,   0 } } },
00199         {   0,   1,   2, { { 1,   1,   2, 127, 147,   0,   0 }, { 0,   0,   1,   1,  21, 271, 311 } } },
00200         {   0,   1,   1, { { 1,   1,   2, 106, 126, 312, 360 }, { 0,   0,   1, 640, 660,   0,   0 } } },
00201         {   1,   1,   3, { { 1,   0,   3, 661, 681,   0,   0 }, { 0,   1,   2, 535, 555, 218, 270 } } },
00202         {   1,   1,   2, { { 1,   0,   3, 556, 575, 361, 409 }, { 0,   1,   2, 682, 702,   0,   0 } } },
00203         {   0,   0,   0, { { 1,   0,   0, 757, 777,   0,   0 }, { 0,   1,   0, 736, 756,   0,   0 } } },
00204         {   1,   0,   0, { { 1,   1,   0, 799, 819,   0,   0 }, { 0,   0,   0, 841, 861,   0,   0 } } },
00205         {   0,   1,   0, { { 1,   1,   0, 778, 798,   0,   0 }, { 0,   0,   0, 820, 840,   0,   0 } } },
00206         {   1,   1,   0, { { 1,   0,   0, 883, 903,   0,   0 }, { 0,   1,   0, 862, 882,   0,   0 } } },
00207         {  -1,   0,   0, { { 0,   0,   0,   0,   0,   0,   0 }, { 0,   0,   0,   0,   0,   0,   0 } } }
00208     };
00209 
00210     uint16 oldPosition = _vm->_state->getBallPosition();
00211     uint16 oldLeverLeft = _vm->_state->getBallLeverLeft();
00212     uint16 oldLeverRight = _vm->_state->getBallLeverRight();
00213 
00214     // Toggle lever position
00215     _vm->_state->setVar(var, !_vm->_state->getVar(var));
00216 
00217     uint16 newLeverLeft = _vm->_state->getBallLeverLeft();
00218     uint16 newLeverRight = _vm->_state->getBallLeverRight();
00219 
00220     const Move *move = nullptr;
00221     for (uint i = _vm->_state->getBallDoorOpen() ? 0 : 1; i < ARRAYSIZE(moves); i++)
00222         if (moves[i].oldBallPosition == oldPosition
00223                 && moves[i].oldLeft == oldLeverLeft
00224                 && moves[i].oldRight == oldLeverRight) {
00225             move = &moves[i];
00226             break;
00227         }
00228 
00229     if (!move)
00230         error("Unable to find move with old levers l:%d r:%d p:%d", oldLeverLeft, oldLeverRight, oldPosition);
00231 
00232     const NewPosition *position = nullptr;
00233     for (uint i = 0; i < ARRAYSIZE(move->p); i++)
00234         if (move->p[i].newLeft == newLeverLeft
00235                 && move->p[i].newRight == newLeverRight) {
00236             position = &move->p[i];
00237             break;
00238         }
00239 
00240     if (!position)
00241         error("Unable to find position with levers l:%d r:%d", newLeverLeft, newLeverRight);
00242 
00243     _vm->_sound->playEffect(789, 50);
00244     _drawForVarHelper(35, position->movieStart, position->movieEnd);
00245 
00246     if (position->newBallPosition != oldPosition) {
00247         uint16 sound;
00248         if (position->newBallPosition == 0) {
00249             sound = 792;
00250         } else if (position->newBallPosition == 1 || position->newBallPosition == 4) {
00251             sound = 790;
00252         } else {
00253             sound = 791;
00254         }
00255 
00256         _vm->_sound->playEffect(sound, 50);
00257 
00258         if (position->movieBallStart != 0) {
00259             _drawForVarHelper(35, position->movieBallStart, position->movieBallEnd);
00260         }
00261     }
00262 
00263     _vm->_state->setBallPosition(position->newBallPosition);
00264     _vm->_state->setBallFrame(_vm->_state->getVar(35));
00265 }
00266 
00267 void Puzzles::tesla(int16 movie, int16 var, int16 move) {
00268     uint16 node = _vm->_state->getLocationNode();
00269 
00270     int16 movieStart = 0;
00271     switch (node) {
00272     case 114:
00273         movieStart = 0;
00274         break;
00275     case 116:
00276         movieStart = 320;
00277         break;
00278     case 118:
00279         movieStart = 240;
00280         break;
00281     case 120:
00282         movieStart = 160;
00283         break;
00284     case 122:
00285         movieStart = 80;
00286         break;
00287     }
00288 
00289     _vm->_state->setTeslaMovieStart(movieStart);
00290 
00291     uint16 position = movieStart + _vm->_state->getVar(var);
00292 
00293     if (position > 400)
00294         position -= 400;
00295 
00296     _vm->_state->setVar(32, node % 100);
00297     _vm->_state->setVar(33, node % 100 + 10000);
00298 
00299     if (movie) {
00300         _vm->_sound->playEffect(1243, 100);
00301         _vm->_state->setMovieSynchronized(true);
00302         _vm->playSimpleMovie(movie);
00303     }
00304 
00305     if (move) {
00306         uint16 sound = _vm->_rnd->getRandomNumberRng(1244, 1245);
00307         _vm->_sound->playEffect(sound, 100);
00308     }
00309 
00310     if (move > 0) {
00311         _drawForVarHelper(var - 303, position + 1, position + 19);
00312         position += 20;
00313     } else if (move < 0) {
00314         if (position == 1)
00315             position = 401;
00316 
00317         _drawForVarHelper(var - 303, position - 1, position - 19);
00318         position -= 20;
00319     }
00320 
00321     if (position < 1)
00322         position = 381;
00323     else if (position > 400)
00324         position = 1;
00325 
00326     _vm->_state->setVar(var - 303, position);
00327 
00328     int16 absPosition = position - movieStart;
00329 
00330     if (absPosition < 1)
00331         absPosition += 400;
00332 
00333     _vm->_state->setVar(var, absPosition);
00334 
00335     bool puzzleSolved = _vm->_state->getTeslaTopAligned() == 1
00336             && _vm->_state->getTeslaMiddleAligned() == 1
00337             && _vm->_state->getTeslaBottomAligned() == 1;
00338 
00339     _vm->_state->setTeslaAllAligned(puzzleSolved);
00340 }
00341 
00342 void Puzzles::resonanceRingControl() {
00343     static const uint16 frames[] = { 0, 24, 1, 5, 10, 15, 0, 0, 0 };
00344 
00345     uint16 startPos = _vm->_state->getVar(29);
00346     uint16 destPos = _vm->_state->getVar(27);
00347 
00348     int16 startFrame = frames[startPos] - 27;
00349     int16 destFrame = frames[destPos];
00350 
00351     // Choose the shortest direction
00352     for (int16 i = destFrame - startFrame; abs(i) > 14; i -= 27)
00353         startFrame += 27;
00354 
00355     // Play the movie, taking care of the limit case
00356     if (destFrame >= startFrame) {
00357         if (startFrame < 1) {
00358             _drawForVarHelper(28, startFrame + 27, 27);
00359             _drawForVarHelper(28, 1, destFrame);
00360             return;
00361         }
00362     } else {
00363         if (startFrame > 27) {
00364             _drawForVarHelper(28, startFrame - 27, 1);
00365             _drawForVarHelper(28, 27, destFrame);
00366             return;
00367         }
00368     }
00369     if (startFrame)
00370         _drawForVarHelper(28, startFrame, destFrame);
00371 }
00372 
00373 void Puzzles::resonanceRingsLaunchBall() {
00374     struct TrackFrames {
00375         uint16 ringFrame;
00376         uint16 var;
00377         uint16 num;
00378         uint16 shatterStartFrame;
00379         uint16 shatterEndFrame;
00380     };
00381 
00382     static const TrackFrames tracks[] = {
00383         { 38, 436, 1, 182, 190 },
00384         { 74, 434, 2, 194, 214 },
00385         { 104, 437, 3, 215, 224 },
00386         { 138, 435, 4, 225, 234 },
00387         { 166, 438, 5, 235, 244 },
00388         { 0, 0, 0, 0, 0 }
00389     };
00390 
00391     struct LightFrames {
00392         uint16 startFrame;
00393         uint16 endFrame;
00394         uint16 num;
00395     };
00396 
00397     static const LightFrames lights[] = {
00398         { 26, 44, 1 },
00399         { 66, 85, 2 },
00400         { 89, 118, 3 },
00401         { 126, 150, 4 },
00402         { 154, 180, 5 }
00403     };
00404 
00405     bool ballShattered = false;
00406     bool lastIsOnLightButton = false;
00407     int32 lightStatus = 0;
00408     uint part = 0;
00409     uint16 buttonVar = 0;
00410     int32 ballMoviePlaying;
00411     int32 boardMoviePlaying;
00412 
00413     do {
00414         _vm->processInput(false);
00415         _vm->drawFrame();
00416 
00417         ballMoviePlaying = _vm->_state->getVar(27);
00418         boardMoviePlaying = _vm->_state->getVar(34);
00419 
00420         if (ballMoviePlaying && tracks[part].ringFrame) {
00421             int32 currentFrame = _vm->_state->getVar(30);
00422 
00423             if (!ballShattered && currentFrame >= tracks[part].ringFrame) {
00424                 int32 value = _vm->_state->getVar(tracks[part].var);
00425 
00426                 if (value == tracks[part].num) {
00427                     // Correct ring order, go to next track part
00428                     part++;
00429                 } else {
00430                     // Incorrect ring order, shatter ball
00431                     ballShattered = true;
00432                     _vm->_sound->playEffect(1010, 50);
00433 
00434                     _vm->_state->setVar(28, tracks[part].shatterStartFrame);
00435                     _vm->_state->setVar(29, tracks[part].shatterEndFrame);
00436                     _vm->_state->setVar(31, tracks[part].shatterStartFrame);
00437                 }
00438             }
00439         }
00440 
00441         bool isOnLightButton = false;
00442 
00443         const LightFrames *frames = 0;
00444         int32 currentLightFrame = _vm->_state->getVar(33);
00445 
00446         // Look is the mini ball is on a light button
00447         for (uint j = 0; j < ARRAYSIZE(lights); j++)
00448             if (currentLightFrame >= lights[j].startFrame && currentLightFrame <= lights[j].endFrame) {
00449                 frames = &lights[j];
00450                 break;
00451             }
00452 
00453         // If ball on light button, turn it off
00454         if (frames) {
00455             for (uint j = 0; j < 5; j++) {
00456                 int32 ringValue = _vm->_state->getVar(434 + j);
00457                 if (ringValue == frames->num)
00458                     _vm->_state->setVar(38 + j, true);
00459             }
00460 
00461             isOnLightButton = true;
00462             buttonVar = 438 + frames->num;
00463         }
00464 
00465         // Restore previous light value
00466         if (lastIsOnLightButton != isOnLightButton) {
00467             lastIsOnLightButton = isOnLightButton;
00468             if (isOnLightButton) {
00469                 lightStatus = _vm->_state->getVar(buttonVar);
00470                 _vm->_state->setVar(buttonVar, 0);
00471             } else {
00472                 _vm->_state->setVar(buttonVar, lightStatus);
00473 
00474                 for (uint j = 0; j < 5; j++)
00475                     _vm->_state->setVar(38 + j, false);
00476             }
00477 
00478             _vm->_ambient->playCurrentNode(100, 2);
00479         }
00480     } while ((ballMoviePlaying || boardMoviePlaying) && !_vm->shouldQuit());
00481 
00482     _vm->_state->setResonanceRingsSolved(!ballShattered);
00483 }
00484 
00485 void Puzzles::resonanceRingsLights() {
00486     // Turn off all lights
00487     for (uint i = 0; i < 5; i++)
00488         _vm->_state->setVar(439 + i, false);
00489 
00490     // For each button / ring value
00491     for (uint i = 0; i < 5; i++) {
00492         // For each light
00493         for (uint j = 0; j < 5; j++) {
00494             // Ring selector value
00495             uint32 ringValue = _vm->_state->getVar(434 + j);
00496             if (ringValue == i + 1) {
00497                 // Button state
00498                 uint32 buttonState = _vm->_state->getVar(43 + i);
00499                 if (buttonState) {
00500                     uint32 oldValue = _vm->_state->getVar(444 + i);
00501                     _vm->_state->setVar(439 + i, oldValue);
00502                     _vm->_state->setVar(38 + j, true);
00503                 } else {
00504                     _vm->_state->setVar(38 + j, false);
00505                 }
00506             }
00507         }
00508     }
00509 
00510     _vm->_ambient->playCurrentNode(100, 2);
00511 }
00512 
00513 void Puzzles::pinball(int16 var) {
00514     static const byte remainingPegsFrames[] = { 2, 15, 25, 32 };
00515 
00516     static const PegCombination leftPegs[] = {
00517         { 10101, { 0, 1, 0, 0, 0 }, {   0,   0,   0 }, 300 },
00518         { 10102, { 1, 1, 0, 0, 0 }, {  49,   0,   0 }, 310 },
00519         { 10103, { 0, 1, 0, 1, 0 }, { 200,   0,   0 }, 310 },
00520         { 10104, { 0, 1, 1, 0, 0 }, { 150,   0,   0 }, 305 },
00521         { 10105, { 0, 1, 0, 0, 1 }, { 250,   0,   0 }, 305 },
00522         { 10106, { 1, 1, 0, 1, 0 }, {  49, 205,   0 }, 310 },
00523         { 10107, { 1, 1, 1, 0, 0 }, {  49, 155,   0 }, 309 },
00524         { 10108, { 1, 1, 0, 0, 1 }, {  49, 253,   0 }, 310 },
00525         { 10109, { 0, 1, 1, 1, 0 }, { 150, 205,   0 }, 310 },
00526         { 10110, { 0, 1, 0, 1, 1 }, { 199, 254,   0 }, 309 },
00527         { 10111, { 0, 1, 1, 0, 1 }, { 150, 254,   0 }, 309 },
00528         { 10112, { 1, 1, 1, 1, 0 }, {  49, 155, 210 }, 315 },
00529         { 10113, { 1, 1, 0, 1, 1 }, {  49, 205, 260 }, 315 },
00530         { 10114, { 1, 1, 1, 0, 1 }, {  49, 155, 260 }, 315 },
00531         { 10115, { 0, 1, 1, 1, 1 }, { 150, 205, 260 }, 315 }
00532     };
00533 
00534     static const PegCombination rightPegs[] = {
00535         { 10201, { 0, 0, 0, 0, 0 }, {   0,   0,   0 }, 300 },
00536         { 10202, { 1, 0, 0, 0, 0 }, { 250,   0,   0 }, 305 },
00537         { 10203, { 0, 1, 0, 0, 0 }, { 200,   0,   0 }, 305 },
00538         { 10204, { 0, 0, 1, 0, 0 }, { 150,   0,   0 }, 305 },
00539         { 10205, { 0, 0, 0, 1, 0 }, { 100,   0,   0 }, 305 },
00540         { 10206, { 0, 0, 0, 0, 1 }, {  50,   0,   0 }, 305 },
00541         { 10207, { 1, 1, 0, 0, 0 }, { 200, 255,   0 }, 305 },
00542         { 10208, { 1, 0, 1, 0, 0 }, { 150, 255,   0 }, 310 },
00543         { 10209, { 1, 0, 0, 1, 0 }, { 100, 255,   0 }, 310 },
00544         { 10210, { 1, 0, 0, 0, 1 }, {  50, 255,   0 }, 310 },
00545         { 10211, { 0, 1, 1, 0, 0 }, { 150, 205,   0 }, 310 },
00546         { 10212, { 0, 1, 0, 1, 0 }, { 100, 205,   0 }, 310 },
00547         { 10213, { 0, 1, 0, 0, 1 }, {  50, 205,   0 }, 310 },
00548         { 10214, { 0, 0, 1, 1, 0 }, { 100, 155,   0 }, 310 },
00549         { 10215, { 0, 0, 1, 0, 1 }, {  50, 155,   0 }, 210 },
00550         { 10216, { 0, 0, 0, 1, 1 }, {  50, 105,   0 }, 310 },
00551         { 10217, { 1, 1, 1, 0, 0 }, { 150, 205, 260 }, 315 },
00552         { 10218, { 1, 1, 0, 1, 0 }, { 100, 205, 260 }, 315 },
00553         { 10219, { 1, 1, 0, 0, 1 }, {  50, 205, 260 }, 312 },
00554         { 10220, { 1, 0, 1, 1, 0 }, { 100, 155, 260 }, 314 },
00555         { 10221, { 1, 0, 1, 0, 1 }, {  50, 155, 259 }, 315 },
00556         { 10222, { 1, 0, 0, 1, 1 }, {  50, 105, 260 }, 315 },
00557         { 10223, { 0, 1, 1, 1, 0 }, { 100, 155, 210 }, 315 },
00558         { 10224, { 0, 1, 1, 0, 1 }, {  50, 155, 210 }, 315 },
00559         { 10225, { 0, 1, 0, 1, 1 }, {  50, 105, 210 }, 315 },
00560         { 10226, { 0, 0, 1, 1, 1 }, {  50, 105, 155 }, 315 }
00561     };
00562 
00563     struct BallJump {
00564         int16 positionLeft;
00565         int16 positionRight;
00566         int16 filter;
00567         int16 startFrame;
00568         int16 endFrame;
00569         int16 sound;
00570         int16 targetLeftFrame;
00571         int16 tragetRightFrame;
00572         int16 type;
00573     };
00574 
00575     static const BallJump jumps[] = {
00576         {   0, 450,  1,   16,   28, 1021, 250, 550, 0 },
00577         {   0, 450, -1,   29,   41, 1021, 500, 550, 3 },
00578         {   0, 200,  0,   42,   57, 1023, 300, 500, 0 },
00579         {   0, 200, -1,   58,   74, 1023, 550, 500, 3 },
00580         {   0, 250,  1,   75,   90, 1023, 350, 550, 0 },
00581         {   0, 250, -1,   91,  106, 1023, 500, 550, 3 },
00582         {   0, 300,  0,  107,  119, 1021, 400, 500, 0 },
00583         {   0, 300, -1,  120,  132, 1021, 550, 500, 3 },
00584         {   0, 400,  0,  133,  165, 1022, 500, 500, 2 },
00585         {   0, 400,  1, 1039, 1071, 1022, 550, 500, 2 },
00586         {   0, 350,  0,  166,  198, 1022, 500, 550, 2 },
00587         {   0, 350,  1, 1072, 1109, 1022, 550, 550, 2 },
00588         { 250,   0,  1,  801,  815, 1021, 550, 450, 0 },
00589         { 250,   0, -1,  816,  827, 1021, 550, 500, 4 },
00590         { 300,   0,  0,  828,  845, 1023, 500, 200, 0 },
00591         { 300,   0, -1,  846,  858, 1023, 500, 550, 3 },
00592         { 350,   0,  1,  859,  876, 1023, 550, 250, 0 },
00593         { 350,   0, -1,    0,    0,    0, 550, 500, 1 },
00594         { 400,   0,  0,  893,  907, 1021, 500, 300, 0 },
00595         { 400,   0,  1, 1267, 1278, 1023, 500, 550, 3 },
00596         { 200,   0,  1,  908,  940, 1022, 500, 550, 2 },
00597         { 200,   0,  0,  974, 1006, 1022, 500, 500, 2 },
00598         { 450,   0,  1,  941,  973, 1022, 550, 550, 2 },
00599         { 450,   0,  0, 1007, 1038, 1022, 550, 500, 2 }
00600     };
00601 
00602     struct BallExpireFrames {
00603         uint16 leftPosition;
00604         uint16 rightPosition;
00605         uint16 startFrame;
00606         uint16 endFrame;
00607     };
00608 
00609     static const BallExpireFrames ballExpireFrames[] = {
00610         { 200, 200, 1105, 1131 },
00611         { 250, 250, 1132, 1158 },
00612         { 300, 300, 1159, 1185 },
00613         { 350, 350, 1186, 1212 },
00614         { 400, 400, 1213, 1239 },
00615         { 450, 450, 1240, 1266 }
00616     };
00617 
00618     // Toggle peg state
00619     if (var > 0) {
00620         int32 value = _vm->_state->getVar(var);
00621         if (value) {
00622             _vm->_state->setVar(var, 0);
00623         } else {
00624             _vm->_state->setVar(var, 1);
00625 
00626             // Play the "peg clicks into spot sound"
00627             if (!_vm->_sound->isPlaying(1024)) {
00628                 _vm->_sound->playEffect(1024, 100);
00629             }
00630         }
00631     }
00632 
00633     // Remaining pegs movie
00634     uint32 pegs = _vm->_state->getPinballRemainingPegs();
00635     uint32 frame = remainingPegsFrames[pegs];
00636     _vm->_state->setVar(33, frame);
00637 
00638     // Choose pegs movie according to peg combination
00639     const PegCombination *leftComb = _pinballFindCombination(461, leftPegs, ARRAYSIZE(leftPegs));
00640     if (!leftComb)
00641         error("Unable to find correct left pegs combination");
00642     _vm->_state->setVar(31, leftComb->movie - 10100);
00643 
00644     const PegCombination *rightComb = _pinballFindCombination(466, rightPegs, ARRAYSIZE(rightPegs));
00645     if (!rightComb)
00646         error("Unable to find correct right pegs combination");
00647     _vm->_state->setVar(32, rightComb->movie - 10200);
00648 
00649     if (var >= 0)
00650         return;
00651 
00652     _vm->_state->setWaterEffectRunning(false);
00653 
00654     // Remove the default panel movies
00655     _vm->removeMovie(10116);
00656     _vm->removeMovie(10227);
00657 
00658     // Set up left panel movie with the correct combination
00659     _vm->_state->setMoviePreloadToMemory(true);
00660     _vm->_state->setMovieScriptDriven(true);
00661     _vm->_state->setMovieNextFrameGetVar(31);
00662     _vm->loadMovie(leftComb->movie, 1, false, true);
00663     _vm->_state->setVar(31, 2);
00664 
00665     // Set up right panel movie with the correct combination
00666     _vm->_state->setMoviePreloadToMemory(true);
00667     _vm->_state->setMovieScriptDriven(true);
00668     _vm->_state->setMovieNextFrameGetVar(32);
00669     _vm->loadMovie(rightComb->movie, 1, false, true);
00670     _vm->_state->setVar(32, 2);
00671 
00672     // Launch sound
00673     _vm->_sound->playEffect(1021, 50);
00674     _drawForVarHelper(-34, 2, 15);
00675     _drawXTicks(30);
00676 
00677     int32 leftSideFrame = 250;
00678     int32 rightSideFrame = 500;
00679     _vm->_state->setVar(34, 250);
00680     _vm->_state->setVar(35, 500);
00681     int32 leftPanelFrame = 2;
00682     int32 rightPanelFrame = 2;
00683     int32 ballOnLeftSide = 1;
00684     int32 ballOnRightSide = 0;
00685     int32 ballShouldExpire = 0;
00686     int32 ballCrashed = 0;
00687     int32 leftToRightJumpCountDown = 0;
00688     int32 rightToLeftJumpCountdown = 0;
00689     int32 ballJumpedFromLeftSide = 1;
00690     int32 ballJumpedFromRightSide = 0;
00691     int32 jumpType = -1;
00692 
00693     while (1) {
00694         _drawXTicks(1);
00695 
00696         bool shouldRotate;
00697         if (leftToRightJumpCountDown >= 3 || rightToLeftJumpCountdown >= 3) {
00698             shouldRotate = false;
00699             _vm->_sound->stopEffect(1025, 7);
00700         } else {
00701             shouldRotate = true;
00702             _vm->_sound->playEffectLooping(1025, 50, ballOnLeftSide != 0 ? 150 : 210, 95);
00703         }
00704 
00705         if (ballOnLeftSide && shouldRotate) {
00706             ++leftSideFrame;
00707 
00708             if (ballJumpedFromLeftSide) {
00709                 if (leftSideFrame >= 500)
00710                     leftSideFrame = 200;
00711             } else {
00712                 if (leftSideFrame >= 800)
00713                     leftSideFrame = 500;
00714             }
00715 
00716             _vm->_state->setVar(34, leftSideFrame);
00717         }
00718 
00719         if (ballOnRightSide && shouldRotate) {
00720             rightSideFrame++;
00721 
00722             if (ballJumpedFromRightSide) {
00723                 if (rightSideFrame >= 500)
00724                     rightSideFrame = 200;
00725             } else {
00726                 if (rightSideFrame >= 800)
00727                     rightSideFrame = 500;
00728             }
00729 
00730             _vm->_state->setVar(35, rightSideFrame);
00731         }
00732 
00733         if (ballOnLeftSide) {
00734             leftPanelFrame++;
00735             _vm->_state->setVar(31, leftPanelFrame);
00736 
00737             for (uint i = 0; i < 3; i++) {
00738                 if (leftComb->pegFrames[i] == leftPanelFrame) {
00739                     _vm->_sound->playEffect(1027, 50);
00740                     leftToRightJumpCountDown = 5;
00741                 }
00742             }
00743 
00744             if (leftPanelFrame == leftComb->expireFrame) {
00745                 ballShouldExpire = 1;
00746                 ballOnLeftSide = 0;
00747             }
00748         }
00749 
00750         if (ballOnRightSide) {
00751             rightPanelFrame++;
00752             _vm->_state->setVar(32, rightPanelFrame);
00753 
00754             for (uint i = 0; i < 3; i++) {
00755                 if (rightComb->pegFrames[i] == rightPanelFrame) {
00756                     _vm->_sound->playEffect(1027, 50);
00757                     rightToLeftJumpCountdown = 5;
00758                 }
00759             }
00760 
00761             if (rightPanelFrame == rightComb->expireFrame) {
00762                 ballShouldExpire = 1;
00763                 ballOnRightSide = 0;
00764             }
00765         }
00766 
00767         bool ballShouldJump = false;
00768         if (leftToRightJumpCountDown) {
00769             --leftToRightJumpCountDown;
00770             if (!leftToRightJumpCountDown) {
00771                 ballOnLeftSide = 0;
00772                 ballShouldJump = true;
00773             }
00774         }
00775 
00776         if (rightToLeftJumpCountdown) {
00777             --rightToLeftJumpCountdown;
00778             if (!rightToLeftJumpCountdown) {
00779                 ballOnRightSide = 0;
00780                 ballShouldJump = true;
00781             }
00782         }
00783 
00784         if (ballShouldJump) {
00785             _vm->_sound->stopEffect(1025, 7);
00786             _drawXTicks(30);
00787 
00788             int32 jumpPositionLeft = 50 * ((leftSideFrame + 25) / 50);
00789             int32 jumpPositionRight = 50 * ((rightSideFrame + 25) / 50);
00790 
00791             const BallJump *jump = 0;
00792 
00793             for (uint i = 0; i < ARRAYSIZE(jumps); i++) {
00794                 int32 filter = jumps[i].filter;
00795                 if (filter != -1) {
00796                     if (filter) {
00797                         if (!(jumpPositionLeft % 100))
00798                             continue;
00799                     } else {
00800                         if (jumpPositionLeft % 100)
00801                             continue;
00802                     }
00803                 }
00804 
00805                 if (abs(jumps[i].positionRight - jumpPositionRight) < 10) {
00806                     ballOnRightSide = 0;
00807                     ballOnLeftSide = 1;
00808                     ballJumpedFromRightSide = 0;
00809                     ballJumpedFromLeftSide = 1;
00810                     jump = &jumps[i];
00811                     break;
00812                 }
00813             }
00814 
00815             for (uint i = 0; i < ARRAYSIZE(jumps); i++) {
00816                 int32 filter = jumps[i].filter;
00817                 if (filter != -1) {
00818                     if (filter) {
00819                         if (!(jumpPositionRight % 100))
00820                             continue;
00821                     } else {
00822                         if (jumpPositionRight % 100)
00823                             continue;
00824                     }
00825                 }
00826 
00827                 if (abs(jumps[i].positionLeft - jumpPositionLeft) < 10) {
00828                     ballOnLeftSide = 0;
00829                     ballOnRightSide = 1;
00830                     ballJumpedFromRightSide = 1;
00831                     ballJumpedFromLeftSide = 0;
00832                     jump = &jumps[i];
00833                     break;
00834                 }
00835             }
00836 
00837             if (!jump)
00838                 error("Bad orb jump combo %d %d", jumpPositionLeft, jumpPositionRight);
00839 
00840             jumpType = jump->type;
00841 
00842             int32 sound = jump->sound;
00843             if (sound)
00844                 _vm->_sound->playEffect(sound, 50);
00845 
00846             int32 jumpStartFrame = jump->startFrame;
00847             if (jumpStartFrame)
00848                 _drawForVarHelper(-34, jumpStartFrame, jump->endFrame);
00849 
00850             if (jumpType == 3) {
00851                 _drawXTicks(6);
00852                 _vm->_sound->playEffect(1028, 50);
00853             } else if (jumpType == 1 || jumpType == 4) {
00854                 _vm->_state->setVar(26, jumpType);
00855                 _vm->_state->setWaterEffectRunning(true);
00856                 _vm->_sound->stopEffect(1025, 7);
00857                 return;
00858             }
00859 
00860             leftSideFrame = jump->targetLeftFrame;
00861             rightSideFrame = jump->tragetRightFrame;
00862             _vm->_state->setVar(34, leftSideFrame);
00863             _vm->_state->setVar(35, rightSideFrame);
00864 
00865             if (jumpType >= 2)
00866                 ballCrashed = 1;
00867 
00868             _drawXTicks(30);
00869         }
00870 
00871         if (ballShouldExpire) {
00872             leftSideFrame = 50 * ((leftSideFrame + 25) / 50);
00873             rightSideFrame = 50 * ((rightSideFrame + 25) / 50);
00874 
00875             if (leftSideFrame == 500)
00876                 leftSideFrame = 200;
00877 
00878             if (rightSideFrame == 500)
00879                 rightSideFrame = 200;
00880 
00881             _vm->_sound->stopEffect(1025, 7);
00882             _vm->_sound->playEffectFadeInOut(1005, 65, 0, 0, 5, 60, 20);
00883             _drawXTicks(55);
00884             _vm->_sound->playEffect(1010, 50);
00885 
00886             for (uint i = 0; i < ARRAYSIZE(ballExpireFrames); i++) {
00887                 if (ballJumpedFromLeftSide && ballExpireFrames[i].leftPosition == leftSideFrame) {
00888                     _drawForVarHelper(34, ballExpireFrames[i].startFrame, ballExpireFrames[i].endFrame);
00889                     break;
00890                 }
00891 
00892                 if (ballJumpedFromRightSide && ballExpireFrames[i].rightPosition == rightSideFrame) {
00893                     _drawForVarHelper(35, ballExpireFrames[i].startFrame, ballExpireFrames[i].endFrame);
00894                     break;
00895                 }
00896             }
00897 
00898             _drawXTicks(15);
00899             break;
00900         }
00901 
00902         if (ballCrashed)
00903             break;
00904     }
00905 
00906     if (ballCrashed || ballShouldExpire) {
00907         if (leftSideFrame < 500)
00908             leftSideFrame += 300;
00909         if (rightSideFrame < 500)
00910             rightSideFrame += 300;
00911 
00912         int32 crashedLeftFrame = ((((leftSideFrame + 25) / 50) >> 4) & 1) != 0 ? 550 : 500;
00913         int32 crashedRightFrame = ((((rightSideFrame + 25) / 50) >> 4) & 1) != 0 ? 550 : 500;
00914 
00915         while (1) {
00916             bool moviePlaying = false;
00917             if ((leftComb->movie != 10101 || leftPanelFrame > 2)
00918                     && leftPanelFrame != leftComb->expireFrame) {
00919 
00920                 if (leftToRightJumpCountDown) {
00921                     --leftToRightJumpCountDown;
00922                 }
00923                 if (!leftToRightJumpCountDown) {
00924                     _vm->_state->setVar(34, crashedLeftFrame);
00925                     crashedLeftFrame++;
00926                 }
00927 
00928                 _vm->_state->setVar(31, leftPanelFrame);
00929 
00930                 ++leftPanelFrame;
00931                 leftSideFrame = leftPanelFrame;
00932 
00933                 for (uint i = 0; i < 3; i++) {
00934                     if (leftComb->pegFrames[i] == leftSideFrame) {
00935                         _vm->_sound->playEffect(1027, 50);
00936                         leftToRightJumpCountDown = 5;
00937                     }
00938                 }
00939 
00940                 moviePlaying = true;
00941             }
00942 
00943             if (!moviePlaying) {
00944                 if ((rightComb->movie != 10201 || rightPanelFrame > 2)
00945                     && rightPanelFrame != rightComb->expireFrame) {
00946 
00947                     if (rightToLeftJumpCountdown) {
00948                         --rightToLeftJumpCountdown;
00949                     }
00950                     if (!rightToLeftJumpCountdown) {
00951                         _vm->_state->setVar(35, crashedRightFrame);
00952                         crashedRightFrame++;
00953                     }
00954 
00955                     _vm->_state->setVar(32, rightPanelFrame);
00956 
00957                     ++rightPanelFrame;
00958                     rightSideFrame = rightPanelFrame;
00959 
00960                     for (uint i = 0; i < 3; i++) {
00961                         if (rightComb->pegFrames[i] == rightSideFrame) {
00962                             _vm->_sound->playEffect(1027, 50);
00963                             rightToLeftJumpCountdown = 5;
00964                         }
00965                     }
00966 
00967                     moviePlaying = true;
00968                 }
00969             }
00970 
00971             _drawXTicks(1);
00972 
00973             if (!moviePlaying) {
00974                 _vm->_state->setVar(26, jumpType);
00975                 _vm->_state->setVar(93, 1);
00976                 _vm->_sound->stopEffect(1025, 7);
00977                 return;
00978             }
00979 
00980             _vm->_sound->playEffectLooping(1025, 50);
00981         }
00982     }
00983 }
00984 
00985 const Puzzles::PegCombination *Puzzles::_pinballFindCombination(uint16 var, const PegCombination pegs[], uint16 size) {
00986     const PegCombination *combination = 0;
00987 
00988     for (uint i = 0; i < size; i++) {
00989         bool good = true;
00990         for (uint j = 0; j < 5; j++) {
00991             bool setPeg = _vm->_state->getVar(var + j);
00992             bool targetPeg = pegs[i].pegs[j];
00993             if (setPeg != targetPeg)
00994                 good = false;
00995         }
00996 
00997         if (good) {
00998             combination = &pegs[i];
00999             break;
01000         }
01001     }
01002 
01003     return combination;
01004 }
01005 
01006 void Puzzles::weightDrag(uint16 var, uint16 movie) {
01007     if (var >= 429 && var <= 432) {
01008         movie = _vm->_state->getVar(var);
01009         _vm->_state->setVar(var, 0);
01010         var = movie;
01011     }
01012 
01013     uint16 sound = 0;
01014     if (var) {
01015         switch (var) {
01016         case 423:
01017             movie = 1022;
01018             sound = 921;
01019             break;
01020         case 425:
01021             movie = 1023;
01022             sound = 921;
01023             break;
01024         case 424:
01025             movie = 1024;
01026             sound = 922;
01027             break;
01028         case 427:
01029             movie = 1025;
01030             sound = 922;
01031             break;
01032         case 426:
01033             movie = 1020;
01034             sound = 920;
01035             break;
01036         case 428:
01037             movie = 1021;
01038             sound = 920;
01039             break;
01040         default:
01041             break;
01042         }
01043 
01044         _vm->_state->setDraggedWeight(var);
01045         _vm->dragItem(var, movie, 1, 2, 26);
01046         _vm->_sound->playEffect(sound, 25);
01047     }
01048 
01049     for (uint i = 0; i < 4; i++) {
01050         int32 value = _vm->_state->getVar(429 + i);
01051         uint16 frame = 0;
01052         switch (value) {
01053         case 423:
01054         case 425:
01055             frame = 2;
01056             break;
01057         case 424:
01058         case 427:
01059             frame = 3;
01060             break;
01061         case 426:
01062         case 428:
01063             frame = 1;
01064             break;
01065         default:
01066             break;
01067         }
01068         _vm->_state->setVar(28 + i, frame);
01069         _vm->_state->setVar(32 + i, frame != 0);
01070     }
01071 }
01072 
01073 void Puzzles::journalSaavedro(int16 move) {
01074     uint16 chapter = _vm->_state->getJournalSaavedroChapter();
01075     int16 page = _vm->_state->getJournalSaavedroPageInChapter();
01076 
01077     if (!_journalSaavedroHasChapter(chapter))
01078         chapter = _journalSaavedroNextChapter(chapter, true);
01079 
01080     if (move > 0) {
01081         // Go to the next available page
01082         int16 pageCount = _journalSaavedroPageCount(chapter);
01083         page++;
01084 
01085         if (page == pageCount) {
01086             chapter = _journalSaavedroNextChapter(chapter, true);
01087             page = 0;
01088         }
01089 
01090         _vm->_state->setJournalSaavedroChapter(chapter);
01091         _vm->_state->setJournalSaavedroPageInChapter(page);
01092     } else if (move < 0) {
01093         // Go to the previous available page
01094         page--;
01095 
01096         if (page < 0) {
01097             chapter = _journalSaavedroNextChapter(chapter, false);
01098             page = _journalSaavedroPageCount(chapter) - 1;
01099         }
01100 
01101         _vm->_state->setJournalSaavedroChapter(chapter);
01102         _vm->_state->setJournalSaavedroPageInChapter(page);
01103     } else {
01104         // Display current page
01105         int16 chapterStartNode = _journalSaavedroGetNode(chapter);
01106         int16 closed = 0;
01107         int16 opened = 0;
01108         int16 lastPage = 0;
01109 
01110         if (chapter > 0) {
01111             opened = 1;
01112             if (chapter == 21)
01113                 lastPage = _journalSaavedroLastPageLastChapterValue();
01114             else
01115                 lastPage = 1;
01116 
01117         } else {
01118             closed = 1;
01119         }
01120 
01121         uint16 nodeRight;
01122         uint16 nodeLeft;
01123         if (page || !chapter) {
01124             nodeRight = chapterStartNode + page;
01125             nodeLeft = chapterStartNode + page;
01126         } else {
01127             nodeRight = chapterStartNode + page;
01128             uint16 chapterLeft = _journalSaavedroNextChapter(chapter, false);
01129             if (chapterLeft > 0)
01130                 nodeLeft = _journalSaavedroGetNode(chapterLeft + 1);
01131             else
01132                 nodeLeft = 201;
01133         }
01134 
01135         _vm->_state->setJournalSaavedroClosed(closed);
01136         _vm->_state->setJournalSaavedroOpen(opened);
01137         _vm->_state->setJournalSaavedroLastPage(lastPage);
01138 
01139         _vm->loadNodeFrame(nodeRight);
01140 
01141         // Does the left page need to be loaded from a different node?
01142         if (nodeLeft != nodeRight) {
01143             const DirectorySubEntry *jpegDesc = _vm->getFileDescription("", nodeLeft, 0, DirectorySubEntry::kFrame);
01144 
01145             if (!jpegDesc)
01146                 error("Frame %d does not exist", nodeLeft);
01147 
01148             Graphics::Surface *bitmap = Myst3Engine::decodeJpeg(jpegDesc);
01149 
01150             // Copy the left half of the node to a new surface
01151             Graphics::Surface *leftBitmap = new Graphics::Surface();
01152             leftBitmap->create(bitmap->w / 2, bitmap->h, Texture::getRGBAPixelFormat());
01153 
01154             for (uint i = 0; i < bitmap->h; i++) {
01155                 memcpy(leftBitmap->getBasePtr(0, i),
01156                         bitmap->getBasePtr(0, i),
01157                         leftBitmap->w * 4);
01158             }
01159 
01160             bitmap->free();
01161             delete bitmap;
01162 
01163             // Create a spotitem covering the left half of the screen
01164             // to display the left page
01165             SpotItemFace *leftPage = _vm->addMenuSpotItem(999, 1, Common::Rect(0, 0, leftBitmap->w, leftBitmap->h));
01166 
01167             leftPage->updateData(leftBitmap);
01168 
01169             leftBitmap->free();
01170             delete leftBitmap;
01171         }
01172     }
01173 }
01174 
01175 int16 Puzzles::_journalSaavedroLastPageLastChapterValue() {
01176     // The scripts just expect different values ...
01177     if (_vm->getPlatform() == Common::kPlatformXbox) {
01178         return 0;
01179     } else {
01180         return 2;
01181     }
01182 }
01183 
01184 uint16 Puzzles::_journalSaavedroGetNode(uint16 chapter) {
01185     const DirectorySubEntry *desc = _vm->getFileDescription("", 1200, 0, DirectorySubEntry::kNumMetadata);
01186 
01187     if (!desc)
01188         error("Node 1200 does not exist");
01189 
01190     return desc->getMiscData(chapter) + 199;
01191 }
01192 
01193 uint16 Puzzles::_journalSaavedroPageCount(uint16 chapter) {
01194     uint16 chapterStartNode = _journalSaavedroGetNode(chapter);
01195     if (chapter != 21)
01196         return _journalSaavedroGetNode(chapter + 1) - chapterStartNode;
01197     else
01198         return 1;
01199 }
01200 
01201 bool Puzzles::_journalSaavedroHasChapter(uint16 chapter) {
01202     return _vm->_state->getVar(285 + chapter) != 0;
01203 }
01204 
01205 uint16 Puzzles::_journalSaavedroNextChapter(uint16 chapter, bool forward) {
01206     do {
01207         if (forward)
01208             chapter++;
01209         else
01210             chapter--;
01211     } while (!_journalSaavedroHasChapter(chapter));
01212 
01213     return chapter;
01214 }
01215 
01216 void Puzzles::journalAtrus(uint16 node, uint16 var) {
01217     uint numPages = 0;
01218 
01219     while (_vm->getFileDescription("", node++, 0, DirectorySubEntry::kFrame))
01220         numPages++;
01221 
01222     _vm->_state->setVar(var, numPages - 1);
01223 }
01224 
01225 void Puzzles::symbolCodesInit(uint16 var, uint16 posX, uint16 posY) {
01226     struct Point {
01227         uint16 x;
01228         uint16 y;
01229     };
01230 
01231     struct CodeData {
01232         uint16 node;
01233         uint16 movie;
01234         bool flag;
01235         Point coords[20];
01236     };
01237 
01238     static const CodeData codes[] = {
01239     {
01240         144, 10144, 0,
01241         {
01242             { 296, 120 }, { 312, 128 }, { 296, 144 }, { 296, 128 }, { 312, 120 },
01243             { 328, 120 }, { 312, 144 }, { 312, 128 }, { 296, 136 }, { 312, 144 },
01244             { 296, 160 }, { 296, 144 }, { 312, 136 }, { 328, 144 }, { 312, 160 },
01245             { 312, 144 }, { 296, 112 }, { 328, 120 }, { 296, 160 }, { 288, 120 }
01246         }
01247         }, {
01248             244, 10244, 1,
01249             {
01250                 { 288, 16 }, { 336, 32 }, { 294, 72 }, { 280, 24 }, { 336, 16 },
01251                 { 376, 24 }, { 336, 72 }, { 328, 32 }, { 288, 64 }, { 336, 80 },
01252                 { 288, 120 }, { 280, 72 }, { 336, 64 }, { 384, 72 }, { 336, 120 },
01253                 { 328, 80 }, { 288, 0 }, { 384, 24 }, { 288, 120 }, { 264, 24 }
01254             }
01255         }, {
01256             148, 10148, 0,
01257             {
01258                 { 280, 24 }, { 304, 32 }, { 288, 48 }, { 280, 24 }, { 304, 24 },
01259                 { 320, 32 }, { 304, 48 }, { 296, 32 }, { 288, 40 }, { 304, 48 },
01260                 { 280, 64 }, { 280, 48 }, { 304, 48 }, { 320, 48 }, { 304, 64 },
01261                 { 296, 48 }, { 280, 16 }, { 320, 24 }, { 280, 64 }, { 272, 24 }
01262             }
01263         }, {
01264             248, 10248, 1,
01265             {
01266                 { 280, 48 }, { 320, 56 }, { 287, 88 }, { 272, 56 }, { 320, 48 },
01267                 { 360, 56 }, { 328, 96 }, { 312, 56 }, { 288, 88 }, { 320, 96 },
01268                 { 280, 128 }, { 271, 96 }, { 328, 88 }, { 360, 96 }, { 320, 128 },
01269                 { 312, 96 }, { 280, 32 }, { 360, 48 }, { 280, 128 }, { 264, 48 }
01270             }
01271         }, {
01272             348, 10348, 1,
01273             {
01274                 { 336, 24 }, { 376, 32 }, { 336, 80 }, { 328, 32 }, { 376, 24 },
01275                 { 424, 32 }, { 384, 80 }, { 368, 40 }, { 336, 72 }, { 376, 80 },
01276                 { 336, 120 }, { 328, 80 }, { 384, 72 }, { 424, 80 }, { 376, 120 },
01277                 { 368, 80 }, { 328, 8 }, { 424, 32 }, { 328, 128 }, { 312, 32 }
01278             }
01279         }, {
01280             448, 10448, 1,
01281             {
01282                 { 224, 32 }, { 264, 40 }, { 224, 80 }, { 208, 40 }, { 264, 32 },
01283                 { 304, 40 }, { 270, 88 }, { 256, 40 }, { 224, 72 }, { 264, 88 },
01284                 { 224, 128 }, { 208, 88 }, { 272, 72 }, { 312, 88 }, { 264, 128 },
01285                 { 256, 88 }, { 216, 16 }, { 312, 40 }, { 216, 128 }, { 200, 40 }
01286             }
01287         }
01288     };
01289 
01290     uint16 node = _vm->_state->getLocationNode();
01291 
01292     const CodeData *code = 0;
01293     for (uint i = 0; i < ARRAYSIZE(codes); i++)
01294         if (codes[i].node == node) {
01295             code = &codes[i];
01296             break;
01297         }
01298 
01299     if (!code)
01300         error("Unable to find puzzle data for node %d", node);
01301 
01302     int32 value = _vm->_state->getVar(var);
01303 
01304     for (uint i = 0; i < 20; i++) {
01305         if (code->flag || value & (1 << i)) {
01306             _vm->_state->setMoviePreloadToMemory(true);
01307             _vm->_state->setMovieScriptDriven(true);
01308             _vm->_state->setMovieOverridePosition(true);
01309             _vm->_state->setMovieOverridePosU(posX + code->coords[i].x);
01310             _vm->_state->setMovieOverridePosV(posY + code->coords[i].y);
01311             _vm->_state->setMovieConditionBit(i + 1);
01312             _vm->loadMovie(code->movie + i * 1000, var, false, true);
01313         }
01314     }
01315 }
01316 
01317 void Puzzles::symbolCodesClick(int16 var) {
01318     // Toggle clicked symbol element
01319     if (var > 0) {
01320         int32 value = _vm->_state->getVar(var);
01321         value ^= 1 << _vm->_state->getHotspotActiveRect();
01322         _vm->_state->setVar(var, value);
01323     }
01324 
01325     // Check puzzle with one symbol solution
01326     static const SymbolCodeSolution smallSolution = { 330080, 53575, 241719, 116411 };
01327     if (_vm->_state->getSymbolCode1AllSolved()) {
01328         bool code2Solved = _symbolCodesCheckSolution(490, smallSolution);
01329         _vm->_state->setSymbolCode2Solved(code2Solved);
01330     }
01331 
01332     // Check puzzle with 3 symbols solution
01333     static const SymbolCodeSolution solutions[] = {
01334         { 208172, 131196, 252945, 788771 },
01335         { 431060, 418863, 558738, 653337 },
01336         { 472588, 199440, 155951, 597954 }
01337     };
01338 
01339 
01340     _vm->_state->setSymbolCode1CurrentSolved(false);
01341 
01342     for (uint i = 1; i <= ARRAYSIZE(solutions); i++) {
01343         int32 solutionsFound = _symbolCodesFound();
01344 
01345         // Symbol already found, don't allow it another time
01346         if (solutionsFound & (1 << i))
01347             continue;
01348 
01349         if (_symbolCodesCheckSolution(498, solutions[i - 1])) {
01350             _vm->_state->setSymbolCode1TopSolved(i);
01351             _vm->_state->setSymbolCode1CurrentSolved(true);
01352         }
01353 
01354         if (_symbolCodesCheckSolution(503, solutions[i - 1])) {
01355             _vm->_state->setSymbolCode1LeftSolved(i);
01356             _vm->_state->setSymbolCode1CurrentSolved(true);
01357         }
01358 
01359         if (_symbolCodesCheckSolution(508, solutions[i - 1])) {
01360             _vm->_state->setSymbolCode1RightSolved(i);
01361             _vm->_state->setSymbolCode1CurrentSolved(true);
01362         }
01363     }
01364 
01365     bool allSolved = _symbolCodesFound() == 14;
01366     _vm->_state->setSymbolCode1AllSolved(allSolved);
01367 }
01368 
01369 bool Puzzles::_symbolCodesCheckSolution(uint16 var, const SymbolCodeSolution &solution) {
01370     bool solved = true;
01371 
01372     for (uint i = 0; i < ARRAYSIZE(solution); i++) {
01373         int32 value = _vm->_state->getVar(var + i);
01374         if (value != solution[i]) {
01375             solved = false;
01376             break;
01377         }
01378     }
01379 
01380     return solved;
01381 }
01382 
01383 int32 Puzzles::_symbolCodesFound() {
01384     int32 top = _vm->_state->getSymbolCode1TopSolved();
01385     int32 left = _vm->_state->getSymbolCode1LeftSolved();
01386     int32 right = _vm->_state->getSymbolCode1RightSolved();
01387 
01388     return (1 << top) | (1 << left) | (1 << right);
01389 }
01390 
01391 void Puzzles::railRoadSwitchs() {
01392     uint16 index = _vm->_state->getHotspotActiveRect();
01393     uint16 startFrame = _vm->_state->getVar(449 + index);
01394     uint16 endFrame;
01395 
01396     switch (startFrame) {
01397     case 1:
01398         endFrame = 4;
01399         break;
01400     case 4:
01401         endFrame = 7;
01402         break;
01403     case 7:
01404         endFrame = 10;
01405         break;
01406     case 10:
01407         endFrame = 12;
01408         break;
01409     default:
01410         error("Bad railroad switchs start value %d", startFrame);
01411         return;
01412     }
01413 
01414     _drawForVarHelper(28 + index, startFrame, endFrame);
01415 
01416     if (endFrame == 12)
01417         endFrame = 1;
01418 
01419     _vm->_state->setVar(28 + index, endFrame);
01420     _vm->_state->setVar(449 + index, endFrame);
01421 }
01422 
01423 void Puzzles::rollercoaster() {
01424     static const uint8 map1[][8] = {
01425         { 3, 9, 9, 0, 7, 9, 9, 4 },
01426         { 2, 6, 0, 9, 9, 9, 1, 9 },
01427         { 6, 9, 4, 9, 2, 9, 0, 9 },
01428         { 4, 9, 6, 9, 0, 9, 2, 9 },
01429         { 9, 4, 7, 9, 1, 9, 9, 2 },
01430         { 2, 9, 0, 9, 7, 9, 9, 4 },
01431         { 6, 9, 9, 7, 9, 9, 0, 3 },
01432         { 4, 9, 7, 6, 0, 9, 3, 2 },
01433         { 9, 5, 9, 9, 6, 1, 4, 9 }
01434     };
01435 
01436     static const uint8 map2[][8] = {
01437         {   0,  0,  26, 57,  40,  0,   0,  0 },
01438         { 100,  0,  36, 67,  50, 41,  12,  0 },
01439         {   0,  0,   0,  0,  60, 51,  22,  0 },
01440         {  14, 25,  56, 87,  70,  0, 103,  0 },
01441         {  24, 35,  66, 97,  80, 71,  42, 13 },
01442         {  34,  0, 101,  0,  90, 81,  52, 23 },
01443         {  44, 55,  86,  0,   0,  0,   0,  0 },
01444         {  54, 65,  96,  0, 102,  0,  72, 43 },
01445         {  64,  0,   0,  0,   0,  0,  82, 53 }
01446     };
01447 
01448     int32 entryPoint = _vm->_state->getVar(26);
01449     int32 movie = 0;
01450     int32 exitPoint = 0;
01451 
01452     if (_vm->_state->getVar(38 + entryPoint - 100)) {
01453         _vm->_state->setVar(42, 0);
01454         _vm->_state->setVar(26, 0);
01455         return;
01456     }
01457 
01458     _vm->_state->setVar(38 + entryPoint - 100, 1);
01459 
01460     switch (entryPoint) {
01461     case 100:
01462         _vm->_state->setVar(42, 0);
01463         _vm->_state->setVar(26, 1);
01464         return;
01465     case 101:
01466         movie = 12007;
01467         exitPoint = 93;
01468         break;
01469     case 102:
01470         movie = 14007;
01471         exitPoint = 75;
01472         break;
01473     case 103:
01474         movie = 16007;
01475         exitPoint = 17;
01476         break;
01477     default:
01478         _vm->_state->setVar(42, 0);
01479         _vm->_state->setVar(26, 0);
01480         return;
01481     }
01482 
01483     int32 recursion = 20;
01484     while (1) {
01485         int32 switchIndex = exitPoint / 10 - 1;
01486         int32 switchFrame = _vm->_state->getVar(449 + switchIndex);
01487         int32 switchPosition = 2 * (switchFrame - 1) / 3;
01488 
01489         int32 direction = map1[switchIndex][(exitPoint % 10 - switchPosition) & 7];
01490 
01491         if (direction != 9)
01492             exitPoint = map2[switchIndex][(switchPosition + direction) & 7];
01493         else
01494             exitPoint = 0;
01495 
01496         if (!recursion)
01497             break;
01498 
01499         recursion--;
01500 
01501         if (exitPoint <= 0 || exitPoint >= 100) {
01502             _vm->_state->setVar(42, exitPoint);
01503             _vm->_state->setVar(26, movie);
01504             return;
01505         }
01506     }
01507 
01508     _vm->_state->setVar(42, 0);
01509     _vm->_state->setVar(26, movie);
01510 }
01511 
01512 void Puzzles::mainMenu(uint16 action) {
01513     _vm->setMenuAction(action);
01514 }
01515 
01516 static void copySurfaceRect(Graphics::Surface *dest, const Common::Point &destPoint, const Graphics::Surface *src) {
01517     for (uint16 i = 0; i < src->h; i++)
01518         memcpy(dest->getBasePtr(destPoint.x, i + destPoint.y), src->getBasePtr(0, i), src->pitch);
01519 }
01520 
01521 void Puzzles::projectorLoadBitmap(uint16 bitmap) {
01522     assert(_vm->_projectorBackground == 0 && "Previous background not yet used.");
01523 
01524     // This surface is freed by the destructor of the movie that uses it
01525     _vm->_projectorBackground = new Graphics::Surface();
01526     _vm->_projectorBackground->create(1024, 1024, Texture::getRGBAPixelFormat());
01527 
01528     const DirectorySubEntry *movieDesc = _vm->getFileDescription("", bitmap, 0, DirectorySubEntry::kStillMovie);
01529 
01530     if (!movieDesc)
01531         error("Movie %d does not exist", bitmap);
01532 
01533     // Rebuild the complete background image from the frames of the bink movie
01534     Common::MemoryReadStream *movieStream = movieDesc->getData();
01535     Video::BinkDecoder bink;
01536     bink.setDefaultHighColorFormat(Texture::getRGBAPixelFormat());
01537     bink.loadStream(movieStream);
01538     bink.start();
01539 
01540     for (uint i = 0; i < 1024; i += 256) {
01541         for (uint j = 0; j < 1024; j += 256) {
01542             const Graphics::Surface *frame = bink.decodeNextFrame();
01543             copySurfaceRect(_vm->_projectorBackground, Common::Point(j, i), frame);
01544         }
01545     }
01546 }
01547 
01548 void Puzzles::projectorAddSpotItem(uint16 bitmap, uint16 x, uint16 y) {
01549     assert(_vm->_projectorBackground != 0 && "Projector background already used.");
01550 
01551     // Nothing to do if the spotitem is not enabled
01552     if (!_vm->_state->getVar(26))
01553         return;
01554 
01555     const DirectorySubEntry *movieDesc = _vm->getFileDescription("", bitmap, 0, DirectorySubEntry::kStillMovie);
01556 
01557     if (!movieDesc)
01558         error("Movie %d does not exist", bitmap);
01559 
01560     // Rebuild the complete background image from the frames of the bink movie
01561     Common::MemoryReadStream *movieStream = movieDesc->getData();
01562     Video::BinkDecoder bink;
01563     bink.setDefaultHighColorFormat(Texture::getRGBAPixelFormat());
01564     bink.loadStream(movieStream);
01565     bink.start();
01566 
01567     const Graphics::Surface *frame = bink.decodeNextFrame();
01568     copySurfaceRect(_vm->_projectorBackground, Common::Point(x, y), frame);
01569 }
01570 
01571 void Puzzles::projectorUpdateCoordinates() {
01572     int16 x = CLIP<int16>(_vm->_state->getProjectorX(), 840, 9400);
01573     int16 y = CLIP<int16>(_vm->_state->getProjectorY(), 840, 9400);
01574     int16 zoom = CLIP<int16>(_vm->_state->getProjectorZoom(), 1280, 5120);
01575     int16 blur = CLIP<int16>(_vm->_state->getProjectorBlur(), 400, 2470);
01576 
01577     int16 halfZoom = zoom / 2;
01578     if (x - halfZoom < 0)
01579         x = halfZoom;
01580     if (x + halfZoom > 10240)
01581         x = 10240 - halfZoom;
01582     if (y - halfZoom < 0)
01583         y = halfZoom;
01584     if (y + halfZoom > 10240)
01585         y = 10240 - halfZoom;
01586 
01587     int16 angleXOffset = _vm->_state->getProjectorAngleXOffset();
01588     int16 angleYOffset = _vm->_state->getProjectorAngleYOffset();
01589     int16 angleZoomOffset = _vm->_state->getProjectorAngleZoomOffset();
01590     int16 angleBlurOffset = _vm->_state->getProjectorAngleBlurOffset();
01591 
01592     int16 angleX = (angleXOffset + 200 * (5 * x - 4200) / 8560) % 1000;
01593     int16 angleY = (angleYOffset + 200 * (5 * y - 4200) / 8560) % 1000;
01594     int16 angleZoom = (angleZoomOffset + 200 * (5 * zoom - 6400) / 3840) % 1000;
01595     int16 angleBlur = (angleBlurOffset + 200 * (5 * blur - 2000) / 2070) % 1000;
01596 
01597     _vm->_state->setProjectorAngleX(angleX);
01598     _vm->_state->setProjectorAngleY(angleY);
01599     _vm->_state->setProjectorAngleZoom(angleZoom);
01600     _vm->_state->setProjectorAngleBlur(angleBlur);
01601 
01602     _vm->_state->setProjectorX(x);
01603     _vm->_state->setProjectorY(y);
01604     _vm->_state->setProjectorZoom(zoom);
01605     _vm->_state->setProjectorBlur(blur);
01606 }
01607 
01608 void Puzzles::settingsSave() {
01609     ConfMan.flushToDisk();
01610 }
01611 
01612 void Puzzles::updateSoundScriptTimer() {
01613     int frequency = 15 * ConfMan.getInt("music_frequency") / 100;
01614     if (_vm->_state->getSoundScriptsPaused()) {
01615         _vm->_state->setSoundScriptsTimer(60 * (20 - frequency));
01616     } else {
01617         _vm->_state->setSoundScriptsTimer(60 * (frequency + 5));
01618     }
01619 }
01620 
01621 void Puzzles::checkCanSave() {
01622     // There is no reason to forbid saving games with ResidualVM,
01623     // since there is no notion of memory card, free blocks and such.
01624     _vm->_state->setStateCanSave(true);
01625 }
01626 
01627 } // End of namespace Myst3


Generated on Sat Jul 13 2019 05:01:10 for ResidualVM by doxygen 1.7.1
curved edge   curved edge