diff options
Diffstat (limited to 'engines/cine')
-rw-r--r-- | engines/cine/anim.cpp | 24 | ||||
-rw-r--r-- | engines/cine/bg.cpp | 6 | ||||
-rw-r--r-- | engines/cine/cine.cpp | 22 | ||||
-rw-r--r-- | engines/cine/cine.h | 6 | ||||
-rw-r--r-- | engines/cine/detection.cpp | 34 | ||||
-rw-r--r-- | engines/cine/gfx.cpp | 81 | ||||
-rw-r--r-- | engines/cine/gfx.h | 4 | ||||
-rw-r--r-- | engines/cine/main_loop.cpp | 98 | ||||
-rw-r--r-- | engines/cine/main_loop.h | 5 | ||||
-rw-r--r-- | engines/cine/part.cpp | 18 | ||||
-rw-r--r-- | engines/cine/part.h | 2 | ||||
-rw-r--r-- | engines/cine/prc.cpp | 2 | ||||
-rw-r--r-- | engines/cine/script_fw.cpp | 10 | ||||
-rw-r--r-- | engines/cine/script_os.cpp | 24 | ||||
-rw-r--r-- | engines/cine/various.cpp | 90 | ||||
-rw-r--r-- | engines/cine/various.h | 1 |
16 files changed, 376 insertions, 51 deletions
diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index eb820804a6..c19435c59a 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -535,10 +535,14 @@ int loadSpl(const char *resourceName, int16 idx) { /*! \brief Load 1bpp mask * \param resourceName Mask filename * \param idx Target index in animDataTable (-1 if any empty space will do) - * \return The number of the animDataTable entry after the loaded mask + * \return The number of the animDataTable entry after the loaded mask (-1 if error) */ int loadMsk(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + int entry = 0; byte *dataPtr = readBundleFile(foundFileIdx); byte *ptr; @@ -562,10 +566,14 @@ int loadMsk(const char *resourceName, int16 idx) { /*! \brief Load animation * \param resourceName Animation filename * \param idx Target index in animDataTable (-1 if any empty space will do) - * \return The number of the animDataTable entry after the loaded animation + * \return The number of the animDataTable entry after the loaded animation (-1 if error) */ int loadAni(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + int entry = 0; byte *dataPtr = readBundleFile(foundFileIdx); byte *ptr; @@ -651,7 +659,7 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) { /*! \brief Load image set * \param resourceName Image set filename * \param idx Target index in animDataTable (-1 if any empty space will do) - * \return The number of the animDataTable entry after the loaded image set + * \return The number of the animDataTable entry after the loaded image set (-1 if error) */ int loadSet(const char *resourceName, int16 idx) { AnimHeader2Struct header2; @@ -661,6 +669,10 @@ int loadSet(const char *resourceName, int16 idx) { byte *ptr, *startOfDataPtr, *dataPtr, *origDataPtr; int type; + if (foundFileIdx < 0) { + return -1; + } + origDataPtr = dataPtr = readBundleFile(foundFileIdx); assert(!memcmp(dataPtr, "SET", 3)); ptr = dataPtr + 4; @@ -708,10 +720,14 @@ int loadSet(const char *resourceName, int16 idx) { /*! \brief Load SEQ data into animDataTable * \param resourceName SEQ data filename * \param idx Target index in animDataTable (-1 if any empty space will do) - * \return The number of the animDataTable entry after the loaded SEQ data + * \return The number of the animDataTable entry after the loaded SEQ data (-1 if error) */ int loadSeq(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + byte *dataPtr = readBundleFile(foundFileIdx); int entry = idx < 0 ? emptyAnimSpace() : idx; diff --git a/engines/cine/bg.cpp b/engines/cine/bg.cpp index 2a4e7f0ab1..45bfae7925 100644 --- a/engines/cine/bg.cpp +++ b/engines/cine/bg.cpp @@ -56,7 +56,7 @@ byte loadCtFW(const char *ctName) { header[i] = readS.readUint16BE(); } - gfxConvertSpriteToRaw(page3Raw, ptr + 0x80, 160, 200); + gfxConvertSpriteToRaw(collisionPage, ptr + 0x80, 160, 200); free(dataPtr); return 0; @@ -74,10 +74,10 @@ byte loadCtOS(const char *ctName) { ptr += 2; if (bpp == 8) { - memcpy(page3Raw, ptr + 256 * 3, 320 * 200); + memcpy(collisionPage, ptr + 256 * 3, 320 * 200); renderer->loadCt256(ptr, ctName); } else { - gfxConvertSpriteToRaw(page3Raw, ptr + 32, 160, 200); + gfxConvertSpriteToRaw(collisionPage, ptr + 32, 160, 200); renderer->loadCt16(ptr, ctName); } diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index 50aefe5ff9..127d390f66 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -56,6 +56,10 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng // Setup mixer _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + // Use music volume for plain sound types (At least the Adlib player uses a plain sound type + // so previously the music and sfx volume controls didn't affect it at all). + // FIXME: Make Adlib player differentiate between playing sound effects and music and remove this. + _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("music_volume")); g_cine = this; @@ -99,14 +103,28 @@ int CineEngine::go() { mainLoop(1); delete renderer; - delete[] page3Raw; + delete[] collisionPage; delete g_sound; return _eventMan->shouldRTL(); } +int CineEngine::getTimerDelay() const { + return (10923000 * _timerDelayMultiplier) / 1193180; +} + +/*! \brief Modify game speed + * \param speedChange Negative values slow game down, positive values speed it up, zero does nothing + * \return Timer delay multiplier's value after the game speed change + */ +int CineEngine::modifyGameSpeed(int speedChange) { + // If we want more speed we decrement the timer delay multiplier and vice versa. + _timerDelayMultiplier = CLIP(_timerDelayMultiplier - speedChange, 1, 50); + return _timerDelayMultiplier; +} void CineEngine::initialize() { + _timerDelayMultiplier = 12; // Set default speed setupOpcodes(); initLanguage(g_cine->getLanguage()); @@ -117,7 +135,7 @@ void CineEngine::initialize() { renderer = new FWRenderer; } - page3Raw = new byte[320 * 200]; + collisionPage = new byte[320 * 200]; textDataPtr = (byte *)malloc(8000); partBuffer = (PartBuffer *)malloc(NUM_MAX_PARTDATA * sizeof(PartBuffer)); diff --git a/engines/cine/cine.h b/engines/cine/cine.h index eaae555812..6011036eb1 100644 --- a/engines/cine/cine.h +++ b/engines/cine/cine.h @@ -59,7 +59,8 @@ enum CineGameType { enum CineGameFeatures { GF_CD = 1 << 0, GF_DEMO = 1 << 1, - GF_ALT_FONT = 1 << 2 + GF_ALT_FONT = 1 << 2, + GF_CRYPTED_BOOT_PRC = 1 << 3 }; struct CINEGameDescription; @@ -86,6 +87,8 @@ public: bool loadSaveDirectory(void); void makeSystemMenu(void); + int modifyGameSpeed(int speedChange); + int getTimerDelay() const; const CINEGameDescription *_gameDescription; Common::File _partFileHandle; @@ -109,6 +112,7 @@ private: void readVolCnf(); bool _preLoad; + int _timerDelayMultiplier; }; extern CineEngine *g_cine; diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp index 899e4c4754..1dc6f89c49 100644 --- a/engines/cine/detection.cpp +++ b/engines/cine/detection.cpp @@ -76,6 +76,25 @@ static const CINEGameDescription gameDescriptions[] = { 0, }, + // This is a CD version of Future Wars published by Sony. + // This version has a crypted AUTO00.PRC. + { + { + "fw", + "Sony CD version", + { + { "AUTO00.PRC", 0, "4fe1e7930b38e3c63f0f2474d471bf8f", -1}, + { "PART01", 0, "61d003202d301c29dd399acfb1354310", -1}, + { NULL, 0, NULL, 0} + }, + Common::EN_USA, + Common::kPlatformPC, + Common::ADGF_CD + }, + GType_FW, + GF_CD | GF_CRYPTED_BOOT_PRC, + }, + { // This is the version included in the UK "Classic Collection" { @@ -251,6 +270,21 @@ static const CINEGameDescription gameDescriptions[] = { }, { + // This is a 16 color PC version (It came on three 720kB 3.5" disks). + // The protagonist is named John Glames in this version. + { + "os", + "", + AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"), + Common::EN_GRB, + Common::kPlatformPC, + Common::ADGF_NO_FLAGS + }, + GType_OS, + 0, + }, + + { { "os", "", diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp index cbddf0fc59..b882c9760e 100644 --- a/engines/cine/gfx.cpp +++ b/engines/cine/gfx.cpp @@ -36,7 +36,7 @@ namespace Cine { -byte *page3Raw; +byte *collisionPage; FWRenderer *renderer = NULL; static const byte mouseCursorNormal[] = { @@ -282,20 +282,43 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte colo */ void FWRenderer::drawPlainBox(int x, int y, int width, int height, byte color) { int i; - byte *dest = _backBuffer + y * 320 + x; + // Handle horizontally flipped boxes if (width < 0) { x += width; - width = -width; + width = ABS(width); } + // Handle vertically flipped boxes if (height < 0) { y += height; - height = -height; + height = ABS(height); } - for (i = 0; i < height; i++) { - memset(dest + i * 320, color, width); + // Handle horizontal boundaries + if (x < 0) { + width += x; // Remove invisible columns + x = 0; // Start drawing at the screen's left border + } else if (x > 319) { + // Nothing left to draw as we're over the screen's right border + width = 0; + } + + // Handle vertical boundaries + if (y < 0) { + height += y; // Remove invisible rows + y = 0; // Start drawing at the screen's top border + } else if (y > 199) { + // Nothing left to draw as we're below the screen's bottom border + height = 0; + } + + // Draw the box if it's not empty + if (width > 0 && height > 0) { + byte *dest = _backBuffer + y * 320 + x; + for (i = 0; i < height; i++) { + memset(dest + i * 320, color, width); + } } } @@ -422,6 +445,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { _messageLen += messageTable[it->objIdx].size(); drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color); + waitForPlayerClick = 1; break; // action failure message @@ -433,6 +457,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { width = width > 300 ? 300 : width; drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, 4); + waitForPlayerClick = 1; break; // bitmap @@ -617,6 +642,11 @@ void FWRenderer::saveBgNames(Common::OutSaveFile &fHandle) { fHandle.write(_bgName, 13); } +const char *FWRenderer::getBgName(uint idx) const { + assert(idx == 0); + return _bgName; +} + /*! \brief Restore active and backup palette from save * \param fHandle Savefile open for reading */ @@ -1011,8 +1041,12 @@ void OSRenderer::drawBackground() { assert(scroll); - memcpy(_backBuffer, main + mainShift, mainSize); - memcpy(_backBuffer + mainSize, scroll, mainShift); + if (mainSize > 0) { // Just a precaution + memcpy(_backBuffer, main + mainShift, mainSize); + } + if (mainShift > 0) { // Just a precaution + memcpy(_backBuffer + mainSize, scroll, mainShift); + } } } @@ -1041,6 +1075,19 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { delete[] mask; break; + // game message + case 2: + if (it->objIdx >= messageTable.size()) { + return; + } + + _messageLen += messageTable[it->objIdx].size(); + drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color); + if (it->color >= 0) { // This test isn't in Future Wars's implementation + waitForPlayerClick = 1; + } + break; + // bitmap case 4: if (objectTable[it->objIdx].frame >= 0) { @@ -1051,16 +1098,27 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { // masked background case 20: assert(it->objIdx < NUM_MAX_OBJECT); + var5 = it->x; // A global variable updated here! obj = objectTable + it->objIdx; sprite = animDataTable + obj->frame; - if (obj->frame < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) { + if (obj->frame < 0 || it->x < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) { break; } maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y); break; + // TODO: Figure out what this overlay type is and name it + // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing) + case 22: { + assert(it->objIdx < NUM_MAX_OBJECT); + obj = objectTable + it->objIdx; + byte transCol = obj->part & 0x0F; + drawPlainBox(obj->x, obj->y, obj->frame, obj->costume, transCol); + break; + } + // something else default: FWRenderer::renderOverlay(it); @@ -1332,6 +1390,11 @@ void OSRenderer::saveBgNames(Common::OutSaveFile &fHandle) { } } +const char *OSRenderer::getBgName(uint idx) const { + assert(idx < 9); + return _bgTable[idx].name; +} + /*! \brief Fade to black * \bug Operation Stealth sometimes seems to fade to black using * transformPalette resulting in double fadeout diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h index 6a3aa1ef89..078954e3b9 100644 --- a/engines/cine/gfx.h +++ b/engines/cine/gfx.h @@ -111,6 +111,7 @@ public: virtual uint getScroll() const; virtual void removeBg(unsigned int idx); virtual void saveBgNames(Common::OutSaveFile &fHandle); + virtual const char *getBgName(uint idx = 0) const; virtual void refreshPalette(); virtual void reloadPalette(); @@ -168,6 +169,7 @@ public: uint getScroll() const; void removeBg(unsigned int idx); void saveBgNames(Common::OutSaveFile &fHandle); + const char *getBgName(uint idx = 0) const; void refreshPalette(); void reloadPalette(); @@ -181,7 +183,7 @@ public: void gfxDrawSprite(byte *src4, uint16 sw, uint16 sh, byte *dst4, int16 sx, int16 sy); -extern byte *page3Raw; +extern byte *collisionPage; extern FWRenderer *renderer; void setMouseCursor(int cursor); diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp index deac4fd57f..9361eb3822 100644 --- a/engines/cine/main_loop.cpp +++ b/engines/cine/main_loop.cpp @@ -121,11 +121,68 @@ static void processEvent(Common::Event &event) { g_cine->makeSystemMenu(); } break; + case Common::KEYCODE_MINUS: + case Common::KEYCODE_KP_MINUS: + g_cine->modifyGameSpeed(-1); // Slower + break; + case Common::KEYCODE_PLUS: + case Common::KEYCODE_KP_PLUS: + g_cine->modifyGameSpeed(+1); // Faster + break; + case Common::KEYCODE_LEFT: + case Common::KEYCODE_KP4: + moveUsingKeyboard(-1, 0); // Left + break; + case Common::KEYCODE_RIGHT: + case Common::KEYCODE_KP6: + moveUsingKeyboard(+1, 0); // Right + break; + case Common::KEYCODE_UP: + case Common::KEYCODE_KP8: + moveUsingKeyboard(0, +1); // Up + break; + case Common::KEYCODE_DOWN: + case Common::KEYCODE_KP2: + moveUsingKeyboard(0, -1); // Down + break; + case Common::KEYCODE_KP9: + moveUsingKeyboard(+1, +1); // Up & Right + break; + case Common::KEYCODE_KP7: + moveUsingKeyboard(-1, +1); // Up & Left + break; + case Common::KEYCODE_KP1: + moveUsingKeyboard(-1, -1); // Down & Left + break; + case Common::KEYCODE_KP3: + moveUsingKeyboard(+1, -1); // Down & Right + break; default: lastKeyStroke = event.kbd.keycode; break; } break; + case Common::EVENT_KEYUP: + switch (event.kbd.keycode) { + case Common::KEYCODE_KP5: // Emulated left mouse button click + case Common::KEYCODE_LEFT: // Left + case Common::KEYCODE_KP4: // Left + case Common::KEYCODE_RIGHT: // Right + case Common::KEYCODE_KP6: // Right + case Common::KEYCODE_UP: // Up + case Common::KEYCODE_KP8: // Up + case Common::KEYCODE_DOWN: // Down + case Common::KEYCODE_KP2: // Down + case Common::KEYCODE_KP9: // Up & Right + case Common::KEYCODE_KP7: // Up & Left + case Common::KEYCODE_KP1: // Down & Left + case Common::KEYCODE_KP3: // Down & Right + // Stop ego movement made with keyboard when releasing a known key + moveUsingKeyboard(0, 0); + break; + default: + break; + } default: break; } @@ -134,7 +191,7 @@ static void processEvent(Common::Event &event) { void manageEvents() { Common::EventManager *eventMan = g_system->getEventManager(); - uint32 nextFrame = g_system->getMillis() + kGameTimerDelay * kGameSpeed; + uint32 nextFrame = g_system->getMillis() + g_cine->getTimerDelay(); do { Common::Event event; while (eventMan->pollEvent(event)) { @@ -239,6 +296,38 @@ void CineEngine::mainLoop(int bootScriptIdx) { } do { + // HACK: Force amount of oxygen left to maximum during Operation Stealth's first arcade sequence. + // This makes it possible to pass the arcade sequence for now. + // FIXME: Remove the hack and make the first arcade sequence normally playable. + if (g_cine->getGameType() == Cine::GType_OS) { + Common::String bgName(renderer->getBgName()); + // Check if the background is one of the three backgrounds + // that are only used during the first arcade sequence. + if (bgName == "28.PI1" || bgName == "29.PI1" || bgName == "30.PI1") { + static const uint oxygenObjNum = 202, maxOxygen = 264; + // Force the amount of oxygen left to the maximum. + objectTable[oxygenObjNum].x = maxOxygen; + } + } + + // HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck. + // After the first arcade sequence the player comes up stairs from + // the water in Santa Paragua's downtown in front of the flower shop. + // Previously he was completely stuck after getting up the stairs. + // If the background is the one used in the flower shop scene ("21.PI1") + // and the player is at the exact location after getting up the stairs + // then we just nudge him a tiny bit away from the stairs and voila, he's free! + // Maybe the real problem behind all this is collision data related as it looks + // like there's some boundary right there near position (204, 110) which we can + // jump over by moving the character to (204, 109). The script handling the + // flower shop scene is AIRPORT.PRC's 13th script. + // FIXME: Remove the hack and solve what's really causing the problem in the first place. + if (g_cine->getGameType() == Cine::GType_OS) { + if (scumm_stricmp(renderer->getBgName(), "21.PI1") == 0 && objectTable[1].x == 204 && objectTable[1].y == 110) { + objectTable[1].y--; // Move the player character upward on-screen by one pixel + } + } + stopMusicAfterFadeOut(); di = executePlayerInput(); @@ -271,6 +360,11 @@ void CineEngine::mainLoop(int bootScriptIdx) { renderer->drawFrame(); } + // NOTE: In the original Future Wars and Operation Stealth messages + // were removed when running the drawOverlays function which is + // currently called from the renderer's drawFrame function. + removeMessages(); + if (waitForPlayerClick) { playerAction = false; @@ -300,8 +394,6 @@ void CineEngine::mainLoop(int bootScriptIdx) { } while (mouseButton != 0); waitForPlayerClick = 0; - - removeMessages(); } if (checkForPendingDataLoadSwitch) { diff --git a/engines/cine/main_loop.h b/engines/cine/main_loop.h index a2f828fd34..c729b324ca 100644 --- a/engines/cine/main_loop.h +++ b/engines/cine/main_loop.h @@ -28,11 +28,6 @@ namespace Cine { -enum { - kGameTimerDelay = 1000 / (1193180 / 10923), - kGameSpeed = 12 -}; - void mainLoop(int bootScriptIdx); void manageEvents(); diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp index 88f2dcef52..d605bdd623 100644 --- a/engines/cine/part.cpp +++ b/engines/cine/part.cpp @@ -259,7 +259,13 @@ byte *readBundleSoundFile(const char *entryName, uint32 *size) { return data; } -byte *readFile(const char *filename) { +/*! \brief Rotate byte value to the left by n bits */ +byte rolByte(byte value, uint n) { + n %= 8; + return (byte) ((value << n) | (value >> (8 - n))); +} + +byte *readFile(const char *filename, bool crypted) { Common::File in; in.open(filename); @@ -272,6 +278,16 @@ byte *readFile(const char *filename) { byte *dataPtr = (byte *)malloc(size); in.read(dataPtr, size); + // The Sony published CD version of Future Wars has its + // AUTO00.PRC file's bytes rotated to the right by one. + // So we decode the so called crypting by rotating all + // the bytes to the left by one. + if (crypted) { + for (uint index = 0; index < size; index++) { + dataPtr[index] = rolByte(dataPtr[index], 1); + } + } + return dataPtr; } diff --git a/engines/cine/part.h b/engines/cine/part.h index 2a979e4879..72dc944db3 100644 --- a/engines/cine/part.h +++ b/engines/cine/part.h @@ -48,7 +48,7 @@ void readFromPart(int16 idx, byte *dataPtr); byte *readBundleFile(int16 foundFileIdx); byte *readBundleSoundFile(const char *entryName, uint32 *size = 0); -byte *readFile(const char *filename); +byte *readFile(const char *filename, bool crypted = false); void checkDataDisk(int16 param); diff --git a/engines/cine/prc.cpp b/engines/cine/prc.cpp index 657edf96af..797a354c4f 100644 --- a/engines/cine/prc.cpp +++ b/engines/cine/prc.cpp @@ -64,7 +64,7 @@ bool loadPrc(const char *pPrcName) { checkDataDisk(-1); if ((g_cine->getGameType() == Cine::GType_FW) && (!scumm_stricmp(pPrcName, BOOT_PRC_NAME) || !scumm_stricmp(pPrcName, "demo.prc"))) { - scriptPtr = dataPtr = readFile(pPrcName); + scriptPtr = dataPtr = readFile(pPrcName, (g_cine->getFeatures() & GF_CRYPTED_BOOT_PRC) != 0); } else { scriptPtr = dataPtr = readBundleFile(findFileInBundle(pPrcName)); } diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index e761a0c8e4..97f45488f2 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -1319,6 +1319,7 @@ int FWScript::o1_loadBg() { return 0; } +/*! \brief Load collision table data */ int FWScript::o1_loadCt() { const char *param = getNextString(); @@ -1789,7 +1790,14 @@ int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneI int16 result = 0; for (int16 i = 0; i < numZones; i++) { - idx = getZoneFromPositionRaw(page3Raw, lx + i, ly, 320); + // Don't try to read data in Operation Stealth if position isn't in 320x200 screen bounds. + if (g_cine->getGameType() == Cine::GType_OS) { + if ((lx + i) < 0 || (lx + i) > 319 || ly < 0 || ly > 199) { + continue; + } + } + + idx = getZoneFromPositionRaw(collisionPage, lx + i, ly, 320); assert(idx >= 0 && idx < NUM_MAX_ZONE); diff --git a/engines/cine/script_os.cpp b/engines/cine/script_os.cpp index 0289a2a0bc..e8f16ebfcc 100644 --- a/engines/cine/script_os.cpp +++ b/engines/cine/script_os.cpp @@ -365,6 +365,7 @@ FWScript *OSScriptInfo::create(const RawObjectScript &script, int16 index, const // OPERATION STEALTH opcodes // ------------------------------------------------------------------------ +/*! \brief Load collision table data */ int FWScript::o2_loadCt() { const char *param = getNextString(); @@ -636,7 +637,28 @@ int FWScript::o2_loadAbs() { const char *param2 = getNextString(); debugC(5, kCineDebugScript, "Line: %d: loadABS(%d,%s)", _line, param1, param2); - loadResource(param2, param1); + // Load the resource to an absolute position + if (loadResource(param2, param1) == -1) { // Check if the loading failed + // WORKAROUND: In the 256 color PC version of Operation Stealth when + // walking out of the airport in Santa Paragua to the street the + // player character should be seen as a grey silhuette while walking + // behind the glass. But actually the player character is completely + // invisible when walking behind the glass because the animation files + // used are wrongly loaded. In AIRPORT.PRC's 6th script there are + // calls loadAbs("JOHN01.ANI", 73) and loadAbs("JOHN02.ANI", 37) to + // load the animations involved but no such files are found with the + // game. Corresponding SET-files are found though. As it worked and + // looked fine when I tried loading them instead of the missing ANI + // files I'm doing so here. NOTE: At least the German Amiga version + // of Operation Stealth seems to have all the files involved + // (JOHN01.ANI, JOHN02.ANI, JOHN01.SET and JOHN02.SET). + if (scumm_stricmp(param2, "JOHN01.ANI") == 0 && param1 == 73) { + loadResource("JOHN01.SET", param1); + } else if (scumm_stricmp(param2, "JOHN02.ANI") == 0 && param1 == 37) { + loadResource("JOHN02.SET", param1); + } + } + return 0; } diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index c490756403..0ead13e3ab 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -94,11 +94,23 @@ int16 saveVar2; byte isInPause = 0; -// TODO: Implement inputVar0's changes in the program -// Currently inputVar0 isn't updated anywhere even though it's used at least in processSeqListElement. -uint16 inputVar0 = 0; -byte inputVar1 = 0; -uint16 inputVar2 = 0, inputVar3 = 0; +/*! \brief Values used by the xMoveKeyb variable */ +enum xMoveKeybEnums { + kKeybMoveCenterX = 0, + kKeybMoveRight = 1, + kKeybMoveLeft = 2 +}; + +/*! \brief Values used by the yMoveKeyb variable */ +enum yMoveKeybEnums { + kKeybMoveCenterY = 0, + kKeybMoveDown = 1, + kKeybMoveUp = 2 +}; + +uint16 xMoveKeyb = kKeybMoveCenterX; +bool egoMovedWithKeyboard = false; +uint16 yMoveKeyb = kKeybMoveCenterY; SelectedObjStruct currentSelectedObject; @@ -115,6 +127,31 @@ int16 objListTab[20]; uint16 zoneData[NUM_MAX_ZONE]; uint16 zoneQuery[NUM_MAX_ZONE]; //!< Only exists in Operation Stealth +/*! \brief Move the player character using the keyboard + * \param x Negative values move left, positive right, zero not at all + * \param y Negative values move down, positive up, zero not at all + * NOTE: If both x and y are zero then the character stops + * FIXME: This seems to only work in Operation Stealth. May need code changes somewhere else... + */ +void moveUsingKeyboard(int x, int y) { + if (x > 0) { + xMoveKeyb = kKeybMoveRight; + } else if (x < 0) { + xMoveKeyb = kKeybMoveLeft; + } else { + xMoveKeyb = kKeybMoveCenterX; + } + + if (y > 0) { + yMoveKeyb = kKeybMoveUp; + } else if (y < 0) { + yMoveKeyb = kKeybMoveDown; + } else { + yMoveKeyb = kKeybMoveCenterY; + } + + egoMovedWithKeyboard = x || y; +} void stopMusicAfterFadeOut(void) { // if (g_sfxPlayer->_fadeOutCounter != 0 && g_sfxPlayer->_fadeOutCounter < 100) { @@ -139,7 +176,6 @@ void addPlayerCommandMessage(int16 cmd) { tmp.type = 3; overlayList.push_back(tmp); - waitForPlayerClick = 1; } int16 getRelEntryForObject(uint16 param1, uint16 param2, SelectedObjStruct *pSelectedObject) { @@ -1516,6 +1552,7 @@ void makeCommandLine(void) { } if (!disableSystemMenu) { + isDrawCommandEnabled = 1; renderer->setCommand(commandBuffer); } } @@ -1688,6 +1725,11 @@ uint16 executePlayerInput(void) { } if (allowPlayerInput) { + if (isDrawCommandEnabled) { + renderer->setCommand(commandBuffer); + isDrawCommandEnabled = 0; + } + getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY); while (mouseButton && currentEntry < 200) { @@ -1868,8 +1910,8 @@ uint16 executePlayerInput(void) { var_2 = 0; } - if (inputVar1 && allowPlayerInput) { // use keyboard - inputVar1 = 0; + if (egoMovedWithKeyboard && allowPlayerInput) { // use keyboard + egoMovedWithKeyboard = false; switch (globalVars[VAR_MOUSE_X_MODE]) { case 1: @@ -1901,8 +1943,8 @@ uint16 executePlayerInput(void) { globalVars[VAR_MOUSE_X_POS] = mouseX; globalVars[VAR_MOUSE_Y_POS] = mouseY; } else { - if (inputVar2) { - if (inputVar2 == 2) { + if (xMoveKeyb) { + if (xMoveKeyb == kKeybMoveLeft) { globalVars[VAR_MOUSE_X_POS] = 1; } else { globalVars[VAR_MOUSE_X_POS] = 320; @@ -1911,8 +1953,8 @@ uint16 executePlayerInput(void) { globalVars[VAR_MOUSE_X_POS] = mouseX; } - if (inputVar3) { - if (inputVar3 == 2) { + if (yMoveKeyb) { + if (yMoveKeyb == kKeybMoveUp) { globalVars[VAR_MOUSE_Y_POS] = 1; } else { globalVars[VAR_MOUSE_Y_POS] = 200; @@ -1994,9 +2036,22 @@ void drawSprite(Common::List<overlay>::iterator it, const byte *spritePtr, const void removeMessages() { Common::List<overlay>::iterator it; + bool remove; for (it = overlayList.begin(); it != overlayList.end(); ) { - if (it->type == 2 || it->type == 3) { + if (g_cine->getGameType() == Cine::GType_OS) { + // NOTE: These are really removeOverlay calls that have been deferred. + // In Operation Stealth's disassembly elements are removed from the + // overlay list right in the drawOverlays function (And actually in + // some other places too) and that's where incrementing a the overlay's + // last parameter by one if it's negative and testing it for positivity + // comes from too. + remove = it->type == 3 || (it->type == 2 && (it->color >= 0 || ++it->color >= 0)); + } else { // Future Wars + remove = it->type == 2 || it->type == 3; + } + + if (remove) { it = overlayList.erase(it); } else { ++it; @@ -2079,7 +2134,6 @@ void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 par tmp.color = param5; overlayList.push_back(tmp); - waitForPlayerClick = 1; } Common::List<SeqListElement> seqList; @@ -2338,9 +2392,9 @@ void processSeqListElement(SeqListElement &element) { } computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, x2, y2); } else { - if (inputVar0 && allowPlayerInput) { + if (xMoveKeyb && allowPlayerInput) { int16 adder = param1 + 1; - if (inputVar0 != 1) { + if (xMoveKeyb != kKeybMoveRight) { adder = -adder; } // FIXME: In Operation Stealth's disassembly global variable 251 is used here @@ -2349,9 +2403,9 @@ void processSeqListElement(SeqListElement &element) { globalVars[VAR_MOUSE_X_POS] = globalVars[251] = ptr1[4] + x + adder; } - if (inputVar1 && allowPlayerInput) { + if (yMoveKeyb && allowPlayerInput) { int16 adder = param2 + 1; - if (inputVar1 != 1) { + if (yMoveKeyb != kKeybMoveDown) { adder = -adder; } // TODO: Name currently unnamed global variable 252 diff --git a/engines/cine/various.h b/engines/cine/various.h index 5f24d502be..acab15e235 100644 --- a/engines/cine/various.h +++ b/engines/cine/various.h @@ -143,6 +143,7 @@ void processSeqList(void); void resetGfxEntityEntry(uint16 objIdx); bool makeTextEntryMenu(const char *caption, char *string, int strLen, int y); +void moveUsingKeyboard(int x, int y); } // End of namespace Cine |