diff options
author | Paul Gilbert | 2012-10-31 09:34:38 +1100 |
---|---|---|
committer | Paul Gilbert | 2012-10-31 09:34:38 +1100 |
commit | 798ddfaab500bb212f620cf095328eee5eb140a4 (patch) | |
tree | 55b5d0b90affd88063c04b7ff62fea1616b83e80 /engines | |
parent | ef663f95a516d8fe47a245653d418c047361281a (diff) | |
parent | fdc80fd952120ecb8a4941edd4c2e404cdc5fa33 (diff) | |
download | scummvm-rg350-798ddfaab500bb212f620cf095328eee5eb140a4.tar.gz scummvm-rg350-798ddfaab500bb212f620cf095328eee5eb140a4.tar.bz2 scummvm-rg350-798ddfaab500bb212f620cf095328eee5eb140a4.zip |
Merge branch 'master' into hopkins
Diffstat (limited to 'engines')
562 files changed, 65875 insertions, 7256 deletions
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index 8c19d03691..3eec33abe5 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -194,7 +194,7 @@ protected: /** * A map containing all the extra game GUI options the engine supports. - */ + */ const ADExtraGuiOptionsMap * const _extraGuiOptions; /** @@ -212,7 +212,7 @@ protected: * * Used to override gameid. * This is a recommended setting to prevent global gameid pollution. - * With this option set, the gameid effectively turns into engineid. + * With this option set, the gameid effectively turns into engineid. * * FIXME: This field actually removes a feature (gameid) in order to * address a more generic problem. We should find a better way to diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index 45c00a76ac..98ffca22ed 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -219,7 +219,7 @@ void AgiEngine::processEvents() { case Common::KEYCODE_F6: key = 0x4000; break; - case Common::KEYCODE_F7: + case Common::KEYCODE_F7: key = 0x4100; break; case Common::KEYCODE_F8: diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h index ab0e9a1fe4..9d67b15adb 100644 --- a/engines/agi/detection_tables.h +++ b/engines/agi/detection_tables.h @@ -706,10 +706,10 @@ static const AGIGameDescription gameDescriptions[] = { FANMADE("Go West, Young Hippie", "ff31484ea465441cb5f3a0f8e956b716"), FANMADE("Good Man (demo v3.41)", "3facd8a8f856b7b6e0f6c3200274d88c"), - GAME_LVFPNF("agi-fanmade", "Groza (russian) [AGDS sample]", "logdir", "421da3a18004122a966d64ab6bd86d2e", -1, + GAME_LVFPNF("agi-fanmade", "Groza (russian) [AGDS sample]", "logdir", "421da3a18004122a966d64ab6bd86d2e", -1, Common::RU_RUS, 0x2440, GF_AGDS, GID_FANMADE, Common::kPlatformPC,GType_V2), - GAME_LVFPNF("agi-fanmade", "Get Outta Space Quest", "logdir", "aaea5b4a348acb669d13b0e6f22d4dc9", -1, + GAME_LVFPNF("agi-fanmade", "Get Outta Space Quest", "logdir", "aaea5b4a348acb669d13b0e6f22d4dc9", -1, Common::EN_ANY, 0x2440, GF_FANMADE, GID_GETOUTTASQ, Common::kPlatformPC,GType_V2), FANMADE_F("Half-Death - Terror At White-Mesa", "b62c05d0ace878261392073f57ae788c", GF_AGIMOUSE), diff --git a/engines/agi/loader_v1.cpp b/engines/agi/loader_v1.cpp index c6a3e66705..189c98ee98 100644 --- a/engines/agi/loader_v1.cpp +++ b/engines/agi/loader_v1.cpp @@ -64,7 +64,7 @@ int AgiLoader_v1::detectGame() { int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) { Common::File fp; - + if (!fp.open(_filenameDisk0)) return errBadFileOpen; @@ -73,13 +73,13 @@ int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; } - + fp.seek(offset, SEEK_SET); for (int i = 0; i <= max; i++) { int b0 = fp.readByte(); int b1 = fp.readByte(); int b2 = fp.readByte(); - + if (b0 == 0xFF && b1 == 0xFF && b2 == 0xFF) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; @@ -98,7 +98,7 @@ int AgiLoader_v1::loadDir_DDP(AgiDir *agid, int offset, int max) { int AgiLoader_v1::loadDir_BC(AgiDir *agid, int offset, int max) { Common::File fp; - + if (!fp.open(_filenameDisk0)) return errBadFileOpen; @@ -107,13 +107,13 @@ int AgiLoader_v1::loadDir_BC(AgiDir *agid, int offset, int max) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; } - + fp.seek(offset, SEEK_SET); for (int i = 0; i <= max; i++) { int b0 = fp.readByte(); int b1 = fp.readByte(); int b2 = fp.readByte(); - + if (b0 == 0xFF && b1 == 0xFF && b2 == 0xFF) { agid[i].volume = 0xFF; agid[i].offset = _EMPTY; @@ -171,7 +171,7 @@ uint8 *AgiLoader_v1::loadVolRes(struct AgiDir *agid) { if (offset == _EMPTY) return NULL; - + if (offset > IMAGE_SIZE) { fp.open(_filenameDisk1); offset -= IMAGE_SIZE; @@ -191,7 +191,7 @@ uint8 *AgiLoader_v1::loadVolRes(struct AgiDir *agid) { agid->len = fp.readUint16LE(); data = (uint8 *)calloc(1, agid->len + 32); fp.read(data, agid->len); - + fp.close(); return data; diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp index cac1701596..d23a5a2e27 100644 --- a/engines/agi/menu.cpp +++ b/engines/agi/menu.cpp @@ -289,7 +289,7 @@ bool Menu::keyhandler(int key) { _vm->_game.clockEnabled = false; drawMenuBar(); } - + // Mouse handling if (_vm->_mouse.button) { int hmenu, vmenu; diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp index 7e04328a67..5334407eb8 100644 --- a/engines/agi/op_cmd.cpp +++ b/engines/agi/op_cmd.cpp @@ -1146,7 +1146,7 @@ void cmdFollowEgo(AgiGame *state, uint8 *p) { vt.parm1 = p1 > vt.stepSize ? p1 : vt.stepSize; vt.parm2 = p2; vt.parm3 = 0xff; - + if (getVersion() < 0x2000) { _v[p2] = 0; vt.flags |= fUpdate | fAnimated; @@ -1270,7 +1270,7 @@ void cmdVersion(AgiGame *state, uint8 *p) { // no Sierra as it wraps textbox Common::String verMsg = TITLE " v%s"; - + int ver = getVersion(); int maj = (ver >> 12) & 0xf; int min = ver & 0xfff; @@ -1839,7 +1839,7 @@ int AgiEngine::runLogic(int n) { // ip = 2; // warning("running logic %d\n", n); // } - + if (_game.exitAllLogics) break; } diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp index a44c68e0fc..4d5e6fffe1 100644 --- a/engines/agi/op_test.cpp +++ b/engines/agi/op_test.cpp @@ -403,7 +403,7 @@ int AgiEngine::testIfCode(int lognum) { case 0xFF: endTest = true; continue; - + default: // Evaluate the command and skip the rest of the instruction _agiCondCommands[op](state, p); diff --git a/engines/agi/opcodes.cpp b/engines/agi/opcodes.cpp index 29fb860635..7a427bd94f 100644 --- a/engines/agi/opcodes.cpp +++ b/engines/agi/opcodes.cpp @@ -130,7 +130,7 @@ AgiInstruction insV1[] = { { "...", "", &cmdUnknown }, // 4E # show.obj { "load.logics", "n", &cmdLoadLogic }, // 4F # load.global.logics { "display", "nnns", &cmdDisplay }, // 50 TODO: 4 vs 3 args - { "prevent.input???", "", &cmdUnknown }, // 51 + { "prevent.input???", "", &cmdUnknown }, // 51 { "...", "", &cmdUnknown }, // 52 # nop { "...", "n", &cmdUnknown }, // 53 # text.screen { "...", "", &cmdUnknown }, // 54 ??? diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index 3e63da756d..d451a799a0 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -821,7 +821,7 @@ int AgiEngine::scummVMSaveLoadDialog(bool isSave) { if (slot < 0) return true; - + if (isSave) return doSave(slot, desc); else diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp index d21baa450f..5bffca5765 100644 --- a/engines/agi/sound_pcjr.cpp +++ b/engines/agi/sound_pcjr.cpp @@ -234,7 +234,7 @@ int SoundGenPCJr::getNextNote(int ch) // if tone isn't touched.. it should be inited so it just plays silence // return 0 if it's passing more data // return -1 if it's passing nothing (end of data) -int SoundGenPCJr::getNextNote_v2(int ch) { +int SoundGenPCJr::getNextNote_v2(int ch) { ToneChan *tpcm; SndGenChan *chan; const byte *data; @@ -308,7 +308,7 @@ int SoundGenPCJr::getNextNote_v1(int ch) { _channel[ch].attenuationCopy = 0x0F; return -1; } - + // In the V1 player the default duration for a row is 3 ticks if (duration > 0) { duration--; diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 1886a74ab1..4877be2647 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -64,7 +64,7 @@ void AgiEngine::printText2(int l, const char *msg, int foff, int xoff, int yoff, // Note: there were extra checks for *m being a cursor character // here (1, 2 or 3), which have been removed, as the cursor - // character is no longer printed via this function. + // character is no longer printed via this function. if (*m >= 0x20) { int ypos = (y1 * CHAR_LINES) + yoff; @@ -73,7 +73,7 @@ void AgiEngine::printText2(int l, const char *msg, int foff, int xoff, int yoff, if (xpos >= GFX_WIDTH) continue; - + _gfx->putTextCharacter(l, xpos, ypos, *m, fg, bg, checkerboard); if (x1 > maxx) diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp index 4400112247..9c5b3d349a 100644 --- a/engines/agi/words.cpp +++ b/engines/agi/words.cpp @@ -41,7 +41,7 @@ int AgiEngine::loadWords_v1(Common::File &f) { int k; debug(0, "Loading dictionary"); - + // Loop through alphabet, as words in the dictionary file are sorted by // first character f.seek(f.pos() + 26 * 2, SEEK_SET); @@ -131,7 +131,7 @@ int AgiEngine::findWord(const char *word, int *flen) { *flen = 0; Common::Array<AgiWord *> &a = _game.words[c]; - + for (int i = 0; i < (int)a.size(); i++) { int wlen = strlen(a[i]->word); // Keep looking till we find the word itself, or the whole phrase. diff --git a/engines/agos/midiparser_s1d.cpp b/engines/agos/midiparser_s1d.cpp index 9ca87436fc..bef7199a98 100644 --- a/engines/agos/midiparser_s1d.cpp +++ b/engines/agos/midiparser_s1d.cpp @@ -35,7 +35,7 @@ namespace AGOS { class MidiParser_S1D : public MidiParser { private: byte *_data; - bool _no_delta; + bool _noDelta; struct Loop { uint16 timer; @@ -49,7 +49,7 @@ protected: void resetTracking(); public: - MidiParser_S1D() : _data(0), _no_delta(false) {} + MidiParser_S1D() : _data(0), _noDelta(false) {} bool loadMusic(byte *data, uint32 size); }; @@ -75,14 +75,14 @@ void MidiParser_S1D::chainEvent(EventInfo &info) { } void MidiParser_S1D::parseNextEvent(EventInfo &info) { - info.start = _position._play_pos; + info.start = _position._playPos; info.length = 0; - info.delta = _no_delta ? 0 : readVLQ2(_position._play_pos); - _no_delta = false; + info.delta = _noDelta ? 0 : readVLQ2(_position._playPos); + _noDelta = false; - info.event = *_position._play_pos++; + info.event = *_position._playPos++; if (!(info.event & 0x80)) { - _no_delta = true; + _noDelta = true; info.event |= 0x80; } @@ -94,34 +94,43 @@ void MidiParser_S1D::parseNextEvent(EventInfo &info) { } else { switch (info.command()) { case 0x8: // note off - info.basic.param1 = *_position._play_pos++; + info.basic.param1 = *_position._playPos++; info.basic.param2 = 0; break; case 0x9: // note on - info.basic.param1 = *_position._play_pos++; - info.basic.param2 = *_position._play_pos++; + info.basic.param1 = *_position._playPos++; + info.basic.param2 = *_position._playPos++; + // Rewrite note on events with velocity 0 as note off events. + // This is the actual meaning of this, but theoretically this + // should not need to be rewritten, since all MIDI devices should + // interpret it like that. On the other hand all our MidiParser + // implementations do it and there seems to be code in MidiParser + // which relies on this for tracking active notes. + if (info.basic.param2 == 0) { + info.event = info.channel() | 0x80; + } break; case 0xA: { // loop control // In case the stop mode(?) is set to 0x80 this will stop the // track over here. - const int16 loopIterations = int8(*_position._play_pos++); + const int16 loopIterations = int8(*_position._playPos++); if (!loopIterations) { - _loops[info.channel()].start = _position._play_pos; + _loops[info.channel()].start = _position._playPos; } else { if (!_loops[info.channel()].timer) { if (_loops[info.channel()].start) { _loops[info.channel()].timer = uint16(loopIterations); - _loops[info.channel()].end = _position._play_pos; + _loops[info.channel()].end = _position._playPos; // Go to the start of the loop - _position._play_pos = _loops[info.channel()].start; + _position._playPos = _loops[info.channel()].start; } } else { if (_loops[info.channel()].timer) - _position._play_pos = _loops[info.channel()].start; + _position._playPos = _loops[info.channel()].start; --_loops[info.channel()].timer; } } @@ -141,13 +150,13 @@ void MidiParser_S1D::parseNextEvent(EventInfo &info) { break; case 0xC: // program change - info.basic.param1 = *_position._play_pos++; + info.basic.param1 = *_position._playPos++; info.basic.param2 = 0; break; case 0xD: // jump to loop end if (_loops[info.channel()].end) - _position._play_pos = _loops[info.channel()].end; + _position._playPos = _loops[info.channel()].end; // We need to read the next midi event here. Since we can not // safely pass this event to the MIDI event processing. @@ -178,7 +187,7 @@ bool MidiParser_S1D::loadMusic(byte *data, uint32 size) { pos += 1; // And now we're at the actual data. Only one track. - _num_tracks = 1; + _numTracks = 1; _data = pos; _tracks[0] = pos; @@ -194,7 +203,7 @@ bool MidiParser_S1D::loadMusic(byte *data, uint32 size) { void MidiParser_S1D::resetTracking() { MidiParser::resetTracking(); // The first event never contains any delta. - _no_delta = true; + _noDelta = true; memset(_loops, 0, sizeof(_loops)); } diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp index c6bca1a6e6..e13fa214d1 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -142,7 +142,7 @@ void AGOSEngine_Feeble::quickLoadOrSave() { } #endif -// The function uses segments of code from the original game scripts +// The function uses segments of code from the original game scripts // to allow quick loading and saving, but isn't perfect. // // Unfortuntely this allows loading and saving in locations, @@ -1424,7 +1424,7 @@ bool AGOSEngine_Elvira2::loadGame(const char *filename, bool restartMode) { // The floppy disk versions of Simon the Sorcerer 2 block changing // to scrolling rooms, if the copy protection fails. But the copy // protection flags are never set in the CD version. - // Setting this copy protection flag, allows saved games to be shared + // Setting this copy protection flag, allows saved games to be shared // between all versions of Simon the Sorcerer 2. if (getGameType() == GType_SIMON2) { setBitFlag(135, 1); diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp index 85c449eafc..bec41bbbd3 100644 --- a/engines/agos/sound.cpp +++ b/engines/agos/sound.cpp @@ -297,7 +297,7 @@ Audio::AudioStream *RawSound::makeAudioStream(uint sound) { warning("RawSound::makeAudioStream: Could not open file \"%s\"", _filename.c_str()); return NULL; } - + file->seek(_offsets[sound], SEEK_SET); uint size = file->readUint32BE(); return Audio::makeRawStream(new Common::SeekableSubReadStream(file, _offsets[sound] + 4, _offsets[sound] + 4 + size, DisposeAfterUse::YES), 22050, _flags, DisposeAfterUse::YES); diff --git a/engines/cge/bitmap.cpp b/engines/cge/bitmap.cpp index 4f85957b3d..7089c8e0d1 100644 --- a/engines/cge/bitmap.cpp +++ b/engines/cge/bitmap.cpp @@ -94,7 +94,7 @@ Bitmap::Bitmap(CGEEngine *vm, uint16 w, uint16 h, uint8 fill) // Replicate across the entire table for (HideDesc *hdP = b + 1; hdP < (b + _h); hdP++) *hdP = *b; - + b->_skip = 0; // fix the first entry _v = v; _b = b; @@ -357,7 +357,7 @@ bool Bitmap::loadVBM(EncryptedStream *f) { // Read in the palette byte palData[kPalSize]; f->read(palData, kPalSize); - + const byte *srcP = palData; for (int idx = 0; idx < kPalCount; idx++, srcP += 3) { _vm->_bitmapPalette[idx]._r = *srcP; diff --git a/engines/cge/cge_main.cpp b/engines/cge/cge_main.cpp index ecbfba2502..f4f1cd3e0b 100644 --- a/engines/cge/cge_main.cpp +++ b/engines/cge/cge_main.cpp @@ -280,7 +280,7 @@ Common::Error CGEEngine::loadGameState(int slot) { sceneDown(); _hero->park(); resetGame(); - + // If music is playing, kill it. if (_music) _midiPlayer->killMidi(); @@ -508,7 +508,7 @@ void CGEEngine::loadMapping() { if (!cf.err()) { // Move to the data for the given room cf.seek((_now - 1) * kMapArrSize); - + // Read in the data for (int z = 0; z < kMapZCnt; ++z) { cf.read(&_clusterMap[z][0], kMapXCnt); @@ -772,7 +772,7 @@ void System::touch(uint16 mask, int x, int y, Common::KeyCode keyCode) { if (mask & kEventKeyb) { if (keyCode == Common::KEYCODE_ESCAPE) { - // The original was calling keyClick() + // The original was calling keyClick() // The sound is uselessly annoying and noisy, so it has been removed _vm->killText(); if (_vm->_startupMode == 1) { @@ -1044,7 +1044,7 @@ void CGEEngine::loadSprite(const char *fname, int ref, int scene, int col = 0, i uint16 len; for (line = sprf.readLine(); !sprf.eos(); line = sprf.readLine()) { - len = line.size(); + len = line.size(); lcnt++; strcpy(tmpStr, line.c_str()); if (len == 0 || *tmpStr == '.') diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp index 2e04b82026..3d6c24d68b 100644 --- a/engines/cge/detection.cpp +++ b/engines/cge/detection.cpp @@ -198,7 +198,7 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const { SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int slot) const { Common::String fileName = Common::String::format("%s.%03d", target, slot); Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName); - + if (f) { CGE::SavegameHeader header; @@ -229,7 +229,7 @@ SaveStateDescriptor CGEMetaEngine::querySaveMetaInfos(const char *target, int sl return desc; } } - + return SaveStateDescriptor(); } diff --git a/engines/cge/events.h b/engines/cge/events.h index 522aa67905..ab8d87212d 100644 --- a/engines/cge/events.h +++ b/engines/cge/events.h @@ -105,7 +105,7 @@ private: void handleEvents(); public: EventManager(CGEEngine *vm); - void poll(); + void poll(); void clearEvent(Sprite *spr); CGEEvent &getNextEvent(); diff --git a/engines/cge/fileio.cpp b/engines/cge/fileio.cpp index f23105d823..609d5e86aa 100644 --- a/engines/cge/fileio.cpp +++ b/engines/cge/fileio.cpp @@ -98,7 +98,7 @@ uint16 ResourceManager::XCrypt(void *buf, uint16 length) { for (uint16 i = 0; i < length; i++) *b++ ^= kCryptSeed; - + return kCryptSeed; } diff --git a/engines/cge/text.cpp b/engines/cge/text.cpp index fd4120d49d..a8ce8777c5 100644 --- a/engines/cge/text.cpp +++ b/engines/cge/text.cpp @@ -63,7 +63,7 @@ int16 Text::count() { Common::String line; char tmpStr[kLineMax + 1]; - + int counter = 0; for (line = tf.readLine(); !tf.eos(); line = tf.readLine()) { diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index 60168831a1..075a59cfb6 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -202,13 +202,13 @@ AnimData::AnimData(const AnimData &src) : _width(src._width), if (src._data) { _data = new byte[_size]; assert(_data); - memcpy(_data, src._data, _size*sizeof(byte)); + memcpy(_data, src._data, _size * sizeof(byte)); } if (src._mask) { _mask = new byte[_size]; assert(_mask); - memcpy(_mask, src._mask, _size*sizeof(byte)); + memcpy(_mask, src._mask, _size * sizeof(byte)); } memset(_name, 0, sizeof(_name)); @@ -272,8 +272,7 @@ byte AnimData::getColor(int x, int y) { * @param transparent Transparent color (for ANIM_MASKSPRITE) */ void AnimData::load(byte *d, int type, uint16 w, uint16 h, int16 file, - int16 frame, const char *n, byte transparent) { - + int16 frame, const char *n, byte transparent) { assert(d); if (_data) { @@ -299,7 +298,7 @@ void AnimData::load(byte *d, int type, uint16 w, uint16 h, int16 file, _size = w * h; _data = new byte[_size]; assert(_data); - memcpy(_data, d, _size*sizeof(byte)); + memcpy(_data, d, _size * sizeof(byte)); break; case ANIM_MASK: @@ -536,7 +535,7 @@ int loadSpl(const char *resourceName, int16 idx) { entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - g_cine->_animDataTable[entry].load(dataPtr, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName); + g_cine->_animDataTable[entry].load(dataPtr + 0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize - 0x16, 1, foundFileIdx, 0, currentPartName); free(dataPtr); return entry + 1; @@ -546,9 +545,10 @@ int loadSpl(const char *resourceName, int16 idx) { * Load 1bpp mask * @param resourceName Mask filename * @param idx Target index in animDataTable (-1 if any empty space will do) + * @param frameIndex frame of animation to load (-1 for all frames) * @return The number of the animDataTable entry after the loaded mask (-1 if error) */ -int loadMsk(const char *resourceName, int16 idx) { +int loadMsk(const char *resourceName, int16 idx, int16 frameIndex) { int16 foundFileIdx = findFileInBundle(resourceName); if (foundFileIdx < 0) { return -1; @@ -563,9 +563,18 @@ int loadMsk(const char *resourceName, int16 idx) { loadAnimHeader(animHeader, readS); ptr = dataPtr + 0x16; + int16 startFrame = 0; + int16 endFrame = animHeader.numFrames; + + if (frameIndex >= 0) { + startFrame = frameIndex; + endFrame = frameIndex + 1; + ptr += frameIndex * animHeader.frameWidth * animHeader.frameHeight; + } + entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - for (int16 i = 0; i < animHeader.numFrames; i++, entry++) { + for (int16 i = startFrame; i < endFrame; i++, entry++) { g_cine->_animDataTable[entry].load(ptr, ANIM_MASK, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName); ptr += animHeader.frameWidth * animHeader.frameHeight; } @@ -578,9 +587,10 @@ int loadMsk(const char *resourceName, int16 idx) { * Load animation * @param resourceName Animation filename * @param idx Target index in animDataTable (-1 if any empty space will do) + * @param frameIndex frame of animation to load (-1 for all frames) * @return The number of the animDataTable entry after the loaded animation (-1 if error) */ -int loadAni(const char *resourceName, int16 idx) { +int loadAni(const char *resourceName, int16 idx, int16 frameIndex) { int16 foundFileIdx = findFileInBundle(resourceName); if (foundFileIdx < 0) { return -1; @@ -596,6 +606,15 @@ int loadAni(const char *resourceName, int16 idx) { loadAnimHeader(animHeader, readS); ptr = dataPtr + 0x16; + int16 startFrame = 0; + int16 endFrame = animHeader.numFrames; + + if (frameIndex >= 0) { + startFrame = frameIndex; + endFrame = frameIndex + 1; + ptr += frameIndex * animHeader.frameWidth * animHeader.frameHeight; + } + transparentColor = getAnimTransparentColor(resourceName); // TODO: Merge this special case hack into getAnimTransparentColor somehow. @@ -609,7 +628,7 @@ int loadAni(const char *resourceName, int16 idx) { entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - for (int16 i = 0; i < animHeader.numFrames; i++, entry++) { + for (int16 i = startFrame; i < endFrame; i++, entry++) { // special case transparency handling if (!strcmp(resourceName, "L2202.ANI")) { transparentColor = i < 2 ? 0 : 7; @@ -669,13 +688,13 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) { *(source + k) <<= 1; if (k > 0 + m) color <<= 1; - } // end k + } // end k *(dest++) = color; - } // end i - } // end m + } // end i + } // end m source += 0x10; - } // end j + } // end j } /** @@ -685,7 +704,7 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) { * @param frameIndex frame of animation to load (-1 for all frames) * @return The number of the animDataTable entry after the loaded image set (-1 if error) */ -int loadSet(const char *resourceName, int16 idx, int16 frameIndex =-1 ) { +int loadSet(const char *resourceName, int16 idx, int16 frameIndex = -1) { AnimHeader2Struct header2; uint16 numSpriteInAnim; int16 foundFileIdx = findFileInBundle(resourceName); @@ -712,10 +731,9 @@ int loadSet(const char *resourceName, int16 idx, int16 frameIndex =-1 ) { int16 startFrame = 0; int16 endFrame = numSpriteInAnim; - if(frameIndex>=0) - { + if (frameIndex >= 0) { startFrame = frameIndex; - endFrame = frameIndex+1; + endFrame = frameIndex + 1; ptr += 0x10 * frameIndex; } @@ -766,7 +784,7 @@ int loadSeq(const char *resourceName, int16 idx) { byte *dataPtr = readBundleFile(foundFileIdx); int entry = idx < 0 ? emptyAnimSpace() : idx; - g_cine->_animDataTable[entry].load(dataPtr+0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize-0x16, 1, foundFileIdx, 0, currentPartName); + g_cine->_animDataTable[entry].load(dataPtr + 0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize - 0x16, 1, foundFileIdx, 0, currentPartName); free(dataPtr); return entry + 1; } @@ -783,11 +801,11 @@ int loadResource(const char *resourceName, int16 idx, int16 frameIndex) { if (strstr(resourceName, ".SPL")) { result = loadSpl(resourceName, idx); } else if (strstr(resourceName, ".MSK")) { - result = loadMsk(resourceName, idx); + result = loadMsk(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".ANI")) { - result = loadAni(resourceName, idx); + result = loadAni(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".ANM")) { - result = loadAni(resourceName, idx); + result = loadAni(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".SET")) { result = loadSet(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".SEQ")) { diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index bbe2cd4896..aa7221f733 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -189,7 +189,18 @@ void CineEngine::initialize() { g_cine->_messageTable.clear(); resetObjectTable(); - disableSystemMenu = 1; + if (getGameType() == Cine::GType_OS) { + disableSystemMenu = 1; + } else { + // WORKAROUND: We do not save this variable in FW's savegames. + // Initializing this to 1, like we do it in the OS case, will + // cause the menu disabled when loading from the launcher or + // command line. + // A proper fix here would be to save this variable in FW's saves. + // Since it seems these are unversioned so far, there would be need + // to properly add versioning to them first. + disableSystemMenu = 0; + } var8 = 0; diff --git a/engines/cine/cine.h b/engines/cine/cine.h index 55376dce29..47edf51c30 100644 --- a/engines/cine/cine.h +++ b/engines/cine/cine.h @@ -159,7 +159,7 @@ private: bool _preLoad; int _timerDelayMultiplier; - public: +public: // TODO: These are pseudo-global vars // They better belong to appropriate classes Common::Array<AnimData> _animDataTable; diff --git a/engines/cine/console.cpp b/engines/cine/console.cpp index 0a24b2408a..4af28592e7 100644 --- a/engines/cine/console.cpp +++ b/engines/cine/console.cpp @@ -28,7 +28,7 @@ namespace Cine { bool labyrinthCheat; CineConsole::CineConsole(CineEngine *vm) : GUI::Debugger(), _vm(vm) { - DCmd_Register("labyrinthCheat", WRAP_METHOD(CineConsole, Cmd_LabyrinthCheat)); + DCmd_Register("labyrinthCheat", WRAP_METHOD(CineConsole, Cmd_LabyrinthCheat)); labyrinthCheat = false; } diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp index 7a988227f6..636c0cf8d9 100644 --- a/engines/cine/gfx.cpp +++ b/engines/cine/gfx.cpp @@ -113,7 +113,7 @@ FWRenderer::FWRenderer() : _background(NULL), _backupPal(), _cmd(""), assert(_backBuffer); memset(_backBuffer, 0, _screenSize); - memset(_bgName, 0, sizeof (_bgName)); + memset(_bgName, 0, sizeof(_bgName)); } @@ -249,7 +249,7 @@ void FWRenderer::drawCommand() { unsigned int i; int x = 10, y = _cmdY; - if(disableSystemMenu == 0) { + if (disableSystemMenu == 0) { drawPlainBox(x, y, 301, 11, 0); drawBorder(x - 1, y - 1, 302, 12, 2); @@ -307,7 +307,7 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color i++; line = fitLine(str + i, tw, words, cw); - if ( str[i + line] != '\0' && str[i + line] != 0x7C && words) { + if (str[i + line] != '\0' && str[i + line] != 0x7C && words) { space = (tw - cw) / words; extraSpace = (tw - cw) % words; } else { @@ -471,6 +471,41 @@ int FWRenderer::drawChar(char character, int x, int y) { return x; } +/** + * Clears the character glyph to black + * This function is called "undrawChar", because the original only applies + * this drawing after the original glyph has been drawn. + * Possible TODO: Find a better name. + * @param character Character to undraw + * @param x Character coordinate + * @param y Character coordinate + */ +int FWRenderer::undrawChar(char character, int x, int y) { + int width, idx; + + if (character == ' ') { + x += 5; + } else if ((width = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterWidth)) { + idx = g_cine->_textHandler.fontParamTable[(unsigned char)character].characterIdx; + const byte *sprite = g_cine->_textHandler.textTable[idx][FONT_DATA]; + for (uint i = 0; i < FONT_HEIGHT; ++i) { + byte *dst = _backBuffer + (y + i) * 320 + x; + for (uint j = 0; j < FONT_WIDTH; ++j, ++dst) { + // The original does this based on whether bit 1 of the pixel + // is set. Since that's the only bit ever set in (FW) this + // check should be fine. + // TODO: Check how Operation Stealth Amiga works + if (*sprite++) { + *dst = 0; + } + } + } + x += width + 1; + } + + return x; +} + int FWRenderer::getStringWidth(const char *str) { const char *p = str; int width = 0; @@ -969,20 +1004,29 @@ void SelectionMenu::drawMenu(FWRenderer &r, bool top) { charX = x + 4; if (i == _selection) { + int color; + if (isAmiga) { - // The original Amiga version is using a different highlight color here, - // but with our current code it is not possible to change the text color, - // thus we can not use the Amiga's color, since otherwise the text - // wouldn't be visible anymore. - r.drawPlainBox(charX, lineY, _width - 8, FONT_HEIGHT, top ? r._messageBg/*2*/ : 18); + if (top) { + color = 2; + } else { + color = 18; + } } else { - r.drawPlainBox(charX, lineY, _width - 8, 9, 0); + color = 0; } + + r.drawPlainBox(x + 2, lineY - 1, _width - 3, 9, color); } const int size = _elements[i].size(); - for (int j = 0; j < size; ++j) - charX = r.drawChar(_elements[i][j], charX, lineY); + for (int j = 0; j < size; ++j) { + if (isAmiga && i == _selection) { + charX = r.undrawChar(_elements[i][j], charX, lineY); + } else { + charX = r.drawChar(_elements[i][j], charX, lineY); + } + } } } @@ -1244,6 +1288,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; drawSprite(&(*it), sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, g_cine->_objectTable[it->objIdx].x, g_cine->_objectTable[it->objIdx].y, g_cine->_objectTable[it->objIdx].part, sprite->_bpp); break; + // game message case 2: if (it->objIdx >= g_cine->_messageTable.size()) { @@ -1300,7 +1345,7 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { height = obj->costume; drawPlainBox(obj->x, obj->y, width, height, color); debug(5, "renderOverlay: type=%d, x=%d, y=%d, width=%d, height=%d, color=%d", - it->type, obj->x, obj->y, width, height, color); + it->type, obj->x, obj->y, width, height, color); break; // something else @@ -1424,7 +1469,7 @@ void OSRenderer::selectBg(unsigned int idx) { if (_bgTable[idx].bg) { assert(_bgTable[idx].pal.isValid() && !(_bgTable[idx].pal.empty())); - _currentBg = idx; + _currentBg = idx; } else warning("OSRenderer::selectBg(%d) - attempt to select null background", idx); reloadPalette(); @@ -1750,23 +1795,23 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi // draw the mask based on next objects in the list Common::List<overlay>::iterator it; - for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) { - if(&(*it) == overlayPtr) { + for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) { + if (&(*it) == overlayPtr) { break; } } - while(it != g_cine->_overlayList.end()) { + while (it != g_cine->_overlayList.end()) { overlay *pCurrentOverlay = &(*it); if ((pCurrentOverlay->type == 5) || ((pCurrentOverlay->type == 21) && (pCurrentOverlay->x == overlayPtr->objIdx))) { AnimData *sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; if (pMask == NULL) { - pMask = new byte[width*height]; + pMask = new byte[width * height]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { - byte spriteColor= spritePtr[width * i + j]; + byte spriteColor = spritePtr[width * i + j]; pMask[width * i + j] = spriteColor; } } @@ -1777,7 +1822,7 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi int inMaskX = (g_cine->_objectTable[it->objIdx].x + i) - x; int inMaskY = (g_cine->_objectTable[it->objIdx].y + j) - y; - if (inMaskX >=0 && inMaskX < width) { + if (inMaskX >= 0 && inMaskX < width) { if (inMaskY >= 0 && inMaskY < height) { if (sprite->_bpp == 1) { if (!sprite->getColor(i, j)) { @@ -1793,7 +1838,7 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi } // now, draw with the mask we created - if(pMask) { + if (pMask) { spritePtr = pMask; } @@ -1808,7 +1853,7 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi destPtr += i * 320; for (int j = 0; j < width; j++) { - byte color= *(spritePtr++); + byte color = *(spritePtr++); if ((transparentColor != color) && x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200) { *(destPtr++) = color; } else { diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h index 3434cf9fc2..8b8843fd72 100644 --- a/engines/cine/gfx.h +++ b/engines/cine/gfx.h @@ -152,6 +152,7 @@ protected: void drawBorder(int x, int y, int width, int height, byte color); void drawDoubleBorder(int x, int y, int width, int height, byte color); virtual int drawChar(char character, int x, int y); + virtual int undrawChar(char character, int x, int y); void drawLine(int x, int y, int width, int height, byte color); void remaskSprite(byte *mask, Common::List<overlay>::iterator it); virtual void drawBackground(); @@ -287,7 +288,7 @@ byte gfxGetColor(int16 x, int16 y, const byte *ptr, int16 width); void gfxResetRawPage(byte *pageRaw); void gfxConvertSpriteToRaw(byte *dst, const byte *src, uint16 w, uint16 h); -void gfxCopyRawPage(byte *source, byte * dest); +void gfxCopyRawPage(byte *source, byte *dest); void gfxFlipRawPage(byte *frontBuffer); void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int16 height, byte *page, int16 x, int16 y); void gfxDrawPlainBoxRaw(int16 x1, int16 y1, int16 x2, int16 y2, byte color, byte *page); diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp index f13f38a45e..c822f1cabd 100644 --- a/engines/cine/main_loop.cpp +++ b/engines/cine/main_loop.cpp @@ -180,19 +180,19 @@ static void processEvent(Common::Event &event) { case Common::KEYCODE_F11: renderer->showCollisionPage(false); break; - case Common::KEYCODE_KP5: // Emulated left mouse button click - case Common::KEYCODE_LEFT: // Left - case Common::KEYCODE_KP4: // Left + 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 + 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; @@ -217,7 +217,6 @@ void manageEvents() { g_system->delayMillis(20); } while (g_system->getMillis() < nextFrame); - g_sound->update(); mouseData.left = mouseLeft; mouseData.right = mouseRight; } @@ -434,9 +433,9 @@ void CineEngine::mainLoop(int bootScriptIdx) { hideMouse(); g_sound->stopMusic(); - // if (g_cine->getGameType() == Cine::GType_OS) { + //if (g_cine->getGameType() == Cine::GType_OS) { // freeUnkList(); - // } + //} closePart(); } diff --git a/engines/cine/object.cpp b/engines/cine/object.cpp index afd95c04b0..a75828abb1 100644 --- a/engines/cine/object.cpp +++ b/engines/cine/object.cpp @@ -59,7 +59,7 @@ void loadObject(char *pObjectName) { assert(numEntry <= NUM_MAX_OBJECT); for (i = 0; i < numEntry; i++) { - if (g_cine->_objectTable[i].costume != -2 && g_cine->_objectTable[i].costume != -3) { // flag is keep ? + if (g_cine->_objectTable[i].costume != -2 && g_cine->_objectTable[i].costume != -3) { // flag is keep? Common::MemoryReadStream readS(ptr, entrySize); g_cine->_objectTable[i].x = readS.readSint16BE(); diff --git a/engines/cine/pal.cpp b/engines/cine/pal.cpp index 779c279ea1..10077ecdc9 100644 --- a/engines/cine/pal.cpp +++ b/engines/cine/pal.cpp @@ -92,7 +92,8 @@ void loadRelatedPalette(const char *fileName) { paletteIndex = findPaletteFromName(localName); if (paletteIndex == -1) { - for (i = 0; i < 16; i++) { // generate default palette + // generate default palette + for (i = 0; i < 16; i++) { paletteBuffer1[i] = paletteBuffer2[i] = (i << 4) + i; } } else { diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp index 03cb743b46..813cbe50af 100644 --- a/engines/cine/part.cpp +++ b/engines/cine/part.cpp @@ -263,7 +263,7 @@ byte *readBundleSoundFile(const char *entryName, uint32 *size) { /** Rotate byte value to the left by n bits */ byte rolByte(byte value, uint n) { n %= 8; - return (byte) ((value << n) | (value >> (8 - n))); + return (byte)((value << n) | (value >> (8 - n))); } byte *readFile(const char *filename, bool crypted) { diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp index 20952eea52..51d2c1f6be 100644 --- a/engines/cine/saveload.cpp +++ b/engines/cine/saveload.cpp @@ -1002,7 +1002,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30); const int fileStartPos = fHandle.pos(); - for(int resourceIndex=0; resourceIndex<NUM_MAX_ANIMDATA; resourceIndex++) { + for (int resourceIndex = 0; resourceIndex < NUM_MAX_ANIMDATA; resourceIndex++) { // Seek to the start of the current animation's entry fHandle.seek(fileStartPos + resourceIndex * entrySize); // Read in the current animation entry diff --git a/engines/cine/saveload.h b/engines/cine/saveload.h index 49c9c0cef7..fd661904af 100644 --- a/engines/cine/saveload.h +++ b/engines/cine/saveload.h @@ -68,7 +68,7 @@ enum CineSaveGameFormat { }; /** Identifier for the temporary Operation Stealth savegame format. */ -static const uint32 TEMP_OS_FORMAT_ID = MKTAG('T','E','M','P'); +static const uint32 TEMP_OS_FORMAT_ID = MKTAG('T', 'E', 'M', 'P'); /** The current version number of Operation Stealth's savegame format. */ static const uint32 CURRENT_OS_SAVE_VER = 1; diff --git a/engines/cine/script.h b/engines/cine/script.h index 3fc86c585b..a07c8d6cfc 100644 --- a/engines/cine/script.h +++ b/engines/cine/script.h @@ -227,6 +227,7 @@ protected: int o1_op72(); int o1_op73(); int o1_playSample(); + int o1_playSampleSwapped(); int o1_disableSystemMenu(); int o1_loadMask5(); int o1_unloadMask5(); diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index 9cbe3c3fab..b4fe68c343 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -196,7 +196,7 @@ void FWScript::setupTable() { { 0, 0 }, { &FWScript::o1_playSample, "bbwbww" }, /* 78 */ - { &FWScript::o1_playSample, "bbwbww" }, + { &FWScript::o1_playSampleSwapped, "bbwbww" }, { &FWScript::o1_disableSystemMenu, "b" }, { &FWScript::o1_loadMask5, "b" }, { &FWScript::o1_unloadMask5, "b" } @@ -352,7 +352,7 @@ void ScriptVars::load(Common::SeekableReadStream &fHandle, unsigned int len) { * Reset all values to 0 */ void ScriptVars::reset() { - memset( _vars, 0, _size * sizeof(int16)); + memset(_vars, 0, _size * sizeof(int16)); } /** @@ -380,10 +380,10 @@ RawScript::RawScript(const FWScriptInfo &info, const byte *data, uint16 s) : * Copy constructor */ RawScript::RawScript(const RawScript &src) : _size(src._size), - _data(new byte[_size+1]), _labels(src._labels) { + _data(new byte[_size + 1]), _labels(src._labels) { assert(_data); - memcpy(_data, src._data, _size+1); + memcpy(_data, src._data, _size + 1); } /** @@ -398,7 +398,7 @@ RawScript::~RawScript() { */ RawScript &RawScript::operator=(const RawScript &src) { assert(src._data); - byte *tmp = new byte[src._size+1]; + byte *tmp = new byte[src._size + 1]; assert(tmp); _labels = src._labels; @@ -443,14 +443,14 @@ int RawScript::getNextLabel(const FWScriptInfo &info, int offset) const { pos += 2; break; case 'c': { // byte != 0 ? byte : word - uint8 test = _data[pos]; + uint8 test = _data[pos]; + pos++; + if (test) { pos++; - if (test) { - pos++; - } else { - pos += 2; - } + } else { + pos += 2; } + } break; case 'l': // label return pos; @@ -459,7 +459,7 @@ int RawScript::getNextLabel(const FWScriptInfo &info, int offset) const { ; break; case 'x': // exit script - return -pos-1; + return -pos - 1; } } } @@ -498,9 +498,7 @@ void RawScript::computeLabels(const FWScriptInfo &info) { * * computeScriptStackFromScript replacement */ -uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset) - const { - +uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset) const { assert(_data); int pos = offset; @@ -519,7 +517,7 @@ uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset) */ void RawScript::setData(const FWScriptInfo &info, const byte *data) { assert(!_data); // this function should be called only once per instance - _data = new byte[_size+1]; + _data = new byte[_size + 1]; assert(data && _data); memcpy(_data, data, _size * sizeof(byte)); @@ -553,7 +551,7 @@ byte RawScript::getByte(unsigned int pos) const { * @return Word of bytecode */ uint16 RawScript::getWord(unsigned int pos) const { - assert(_data && pos+1 < _size); + assert(_data && pos + 1 < _size); return READ_BE_UINT16(_data + pos); } @@ -566,7 +564,7 @@ uint16 RawScript::getWord(unsigned int pos) const { const char *RawScript::getString(unsigned int pos) const { assert(_data && pos < _size); - return (const char*)(_data+pos); + return (const char *)(_data + pos); } /** @@ -580,8 +578,8 @@ const char *RawScript::getString(unsigned int pos) const { * instance can be used. It leaves the instance in partially invalid state. */ RawObjectScript::RawObjectScript(uint16 s, uint16 p1, uint16 p2, uint16 p3) - : RawScript(s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) -{ } + : RawScript(s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) { +} /** * Complete constructor @@ -592,8 +590,9 @@ RawObjectScript::RawObjectScript(uint16 s, uint16 p1, uint16 p2, uint16 p3) * @param p3 Third object script parameter */ RawObjectScript::RawObjectScript(const FWScriptInfo &info, const byte *data, - uint16 s, uint16 p1, uint16 p2, uint16 p3) : RawScript(info, data, s), - _runCount(0), _param1(p1), _param2(p2), _param3(p3) { } + uint16 s, uint16 p1, uint16 p2, uint16 p3) + : RawScript(info, data, s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) { +} /** * Contructor for global scripts @@ -603,7 +602,8 @@ RawObjectScript::RawObjectScript(const FWScriptInfo &info, const byte *data, FWScript::FWScript(const RawScript &script, int16 idx) : _script(script), _pos(0), _line(0), _compare(0), _index(idx), _labels(script.labels()), _localVars(LOCAL_VARS_SIZE), - _globalVars(g_cine->_globalVars), _info(new FWScriptInfo) { } + _globalVars(g_cine->_globalVars), _info(new FWScriptInfo) { +} /** * Copy constructor @@ -611,25 +611,27 @@ FWScript::FWScript(const RawScript &script, int16 idx) : _script(script), FWScript::FWScript(const FWScript &src) : _script(src._script), _pos(src._pos), _line(src._line), _compare(src._compare), _index(src._index), _labels(src._labels), _localVars(src._localVars), - _globalVars(src._globalVars), _info(new FWScriptInfo) { } + _globalVars(src._globalVars), _info(new FWScriptInfo) { +} /** * Contructor for global scripts in derived classes * @param script Script bytecode reference * @param idx Script bytecode index */ -FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info) : - _script(script), _pos(0), _line(0), _compare(0), _index(idx), +FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info) + : _script(script), _pos(0), _line(0), _compare(0), _index(idx), _labels(script.labels()), _localVars(LOCAL_VARS_SIZE), - _globalVars(g_cine->_globalVars), _info(info) { } + _globalVars(g_cine->_globalVars), _info(info) { +} /** * Constructor for object scripts in derived classes * @param script Script bytecode reference * @param idx Script bytecode index */ -FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) : - _script(script), _pos(0), _line(0), _compare(0), _index(idx), +FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) + : _script(script), _pos(0), _line(0), _compare(0), _index(idx), _labels(script.labels()), _localVars(LOCAL_VARS_SIZE), _globalVars(g_cine->_globalVars), _info(info) { @@ -639,8 +641,8 @@ FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info) : /** * Copy constructor for derived classes */ -FWScript::FWScript(const FWScript &src, FWScriptInfo *info) : - _script(src._script), _pos(src._pos), _line(src._line), +FWScript::FWScript(const FWScript &src, FWScriptInfo *info) + : _script(src._script), _pos(src._pos), _line(src._line), _compare(src._compare), _index(src._index), _labels(src._labels), _localVars(src._localVars), _globalVars(src._globalVars), _info(info) { } @@ -704,7 +706,7 @@ void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 co int FWScript::execute() { int ret = 0; - if(_script._size) { + if (_script._size) { while (!ret) { _line = _pos; byte opcode = getNextByte(); @@ -1816,6 +1818,9 @@ int FWScript::o1_playSample() { if (g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) { if (size == 0xFFFF) { size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height; + } else if (size > g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height) { + warning("o1_playSample: Got invalid sample size %d for sample %d", size, anim); + size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height; } if (channel < 10) { // || _currentOpcode == 0x78 int channel1, channel2; @@ -1823,8 +1828,8 @@ int FWScript::o1_playSample() { channel1 = 0; channel2 = 1; } else { - channel1 = 2; - channel2 = 3; + channel1 = 3; + channel2 = 2; } g_sound->playSound(channel1, freq, data, size, -1, volume, 63, repeat); g_sound->playSound(channel2, freq, data, size, 1, volume, 0, repeat); @@ -1858,6 +1863,53 @@ int FWScript::o1_playSample() { return 0; } +int FWScript::o1_playSampleSwapped() { + // TODO: The DOS version probably does not have any stereo support here + // since the only stereo output it supports should be the Roland MT-32. + // So it probably does the same as o1_playSample here. Checking this will + // be a good idea never the less. + if (g_cine->getPlatform() == Common::kPlatformPC) { + return o1_playSample(); + } + + debugC(5, kCineDebugScript, "Line: %d: playSampleInversed()", _line); + + byte anim = getNextByte(); + byte channel = getNextByte(); + + uint16 freq = getNextWord(); + byte repeat = getNextByte(); + + int16 volume = getNextWord(); + uint16 size = getNextWord(); + + const byte *data = g_cine->_animDataTable[anim].data(); + + if (!data) { + return 0; + } + + if (size == 0xFFFF) { + size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height; + } else if (size > g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height) { + warning("o1_playSampleSwapped: Got invalid sample size %d for sample %d", size, anim); + size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height; + } + + int channel1, channel2; + if (channel == 0) { + channel1 = 1; + channel2 = 0; + } else { + channel1 = 2; + channel2 = 3; + } + + g_sound->playSound(channel1, freq, data, size, -1, volume, 63, repeat); + g_sound->playSound(channel2, freq, data, size, 1, volume, 0, repeat); + return 0; +} + int FWScript::o1_disableSystemMenu() { byte param = getNextByte(); @@ -2074,1034 +2126,970 @@ void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx) strcpy(lineBuffer, ""); switch (opcode - 1) { - case -1: - { - break; - } - case 0x0: - { - byte param1; - byte param2; - int16 param3; + case -1: { + break; + } + case 0x0: { + byte param1; + byte param2; + int16 param3; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "obj[%d]%s = %d\n", param1, getObjPramName(param2), param3); + sprintf(lineBuffer, "obj[%d]%s = %d\n", param1, getObjPramName(param2), param3); - break; - } - case 0x1: - { - byte param1; - byte param2; - byte param3; + break; + } + case 0x1: { + byte param1; + byte param2; + byte param3; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = *(localScriptPtr + position); - position++; + param3 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "var[%d]=obj[%d]%s\n", param3, param1, getObjPramName(param2)); - break; - } + sprintf(lineBuffer, "var[%d]=obj[%d]%s\n", param3, param1, getObjPramName(param2)); + break; + } case 0x2: case 0x3: case 0x4: case 0x5: - case 0x6: - { - byte param1; - byte param2; - int16 param3; - - param1 = *(localScriptPtr + position); - position++; - - param2 = *(localScriptPtr + position); - position++; - - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; - - if (opcode - 1 == 0x2) { - sprintf(lineBuffer, "obj[%d]%s+=%d\n", param1, getObjPramName(param2), param3); - } else if (opcode - 1 == 0x3) { - sprintf(lineBuffer, "obj[%d]%s-=%d\n", param1, getObjPramName(param2), param3); - } else if (opcode - 1 == 0x4) { - sprintf(lineBuffer, "obj[%d]%s+=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2)); - } else if (opcode - 1 == 0x5) { - sprintf(lineBuffer, "obj[%d]%s-=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2)); - } else if (opcode - 1 == 0x6) { - sprintf(compareString1, "obj[%d]%s", param1, getObjPramName(param2)); - sprintf(compareString2, "%d", param3); - } - break; + case 0x6: { + byte param1; + byte param2; + int16 param3; + + param1 = *(localScriptPtr + position); + position++; + + param2 = *(localScriptPtr + position); + position++; + + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; + + if (opcode - 1 == 0x2) { + sprintf(lineBuffer, "obj[%d]%s+=%d\n", param1, getObjPramName(param2), param3); + } else if (opcode - 1 == 0x3) { + sprintf(lineBuffer, "obj[%d]%s-=%d\n", param1, getObjPramName(param2), param3); + } else if (opcode - 1 == 0x4) { + sprintf(lineBuffer, "obj[%d]%s+=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2)); + } else if (opcode - 1 == 0x5) { + sprintf(lineBuffer, "obj[%d]%s-=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2)); + } else if (opcode - 1 == 0x6) { + sprintf(compareString1, "obj[%d]%s", param1, getObjPramName(param2)); + sprintf(compareString2, "%d", param3); } + break; + } case 0x7: - case 0x8: - { - byte param1; - int16 param2; - int16 param3; - int16 param4; - int16 param5; + case 0x8: { + byte param1; + int16 param2; + int16 param3; + int16 param4; + int16 param5; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - if (opcode - 1 == 0x7) { - sprintf(lineBuffer, "setupObject(Idx:%d,X:%d,Y:%d,mask:%d,frame:%d)\n", param1, param2, param3, param4, param5); - } else if (opcode - 1 == 0x8) { - sprintf(lineBuffer, "checkCollision(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); - } - break; + if (opcode - 1 == 0x7) { + sprintf(lineBuffer, "setupObject(Idx:%d,X:%d,Y:%d,mask:%d,frame:%d)\n", param1, param2, param3, param4, param5); + } else if (opcode - 1 == 0x8) { + sprintf(lineBuffer, "checkCollision(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); } - case 0x9: - { - byte param1; - int16 param2; + break; + } + case 0x9: { + byte param1; + int16 param2; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - if (param2) { - byte param3; - - param3 = *(localScriptPtr + position); - position++; - - if (param2 == 1) { - sprintf(lineBuffer, "var[%d]=var[%d]\n", param1, param3); - } else if (param2 == 2) { - sprintf(lineBuffer, "var[%d]=globalVar[%d]\n", param1, param3); - } else if (param2 == 3) { - sprintf(lineBuffer, "var[%d]=mouse.X\n", param1); - } else if (param2 == 4) { - sprintf(lineBuffer, "var[%d]=mouse.Y\n", param1); - } else if (param2 == 5) { - sprintf(lineBuffer, "var[%d]=rand() mod %d\n", param1, param3); - } else if (param2 == 8) { - sprintf(lineBuffer, "var[%d]=file[%d].packedSize\n", param1, param3); - } else if (param2 == 9) { - sprintf(lineBuffer, "var[%d]=file[%d].unpackedSize\n", param1, param3); - } else { - error("decompileScript: 0x09: param2 = %d", param2); - } - } else { - int16 param3; + if (param2) { + byte param3; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "var[%d]=%d\n", param1, param3); + if (param2 == 1) { + sprintf(lineBuffer, "var[%d]=var[%d]\n", param1, param3); + } else if (param2 == 2) { + sprintf(lineBuffer, "var[%d]=globalVar[%d]\n", param1, param3); + } else if (param2 == 3) { + sprintf(lineBuffer, "var[%d]=mouse.X\n", param1); + } else if (param2 == 4) { + sprintf(lineBuffer, "var[%d]=mouse.Y\n", param1); + } else if (param2 == 5) { + sprintf(lineBuffer, "var[%d]=rand() mod %d\n", param1, param3); + } else if (param2 == 8) { + sprintf(lineBuffer, "var[%d]=file[%d].packedSize\n", param1, param3); + } else if (param2 == 9) { + sprintf(lineBuffer, "var[%d]=file[%d].unpackedSize\n", param1, param3); + } else { + error("decompileScript: 0x09: param2 = %d", param2); } + } else { + int16 param3; - break; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; + + sprintf(lineBuffer, "var[%d]=%d\n", param1, param3); } + + break; + } case 0xA: case 0xB: case 0xC: - case 0xD: - { - byte param1; - byte param2; + case 0xD: { + byte param1; + byte param2; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); + param2 = *(localScriptPtr + position); + position++; + + if (param2) { + byte param3; + + param3 = *(localScriptPtr + position); position++; - if (param2) { - byte param3; + if (opcode - 1 == 0xA) { + sprintf(lineBuffer, "var[%d]+=var[%d]\n", param1, param3); + } else if (opcode - 1 == 0xB) { + sprintf(lineBuffer, "var[%d]-=var[%d]\n", param1, param3); + } else if (opcode - 1 == 0xC) { + sprintf(lineBuffer, "var[%d]*=var[%d]\n", param1, param3); + } else if (opcode - 1 == 0xD) { + sprintf(lineBuffer, "var[%d]/=var[%d]\n", param1, param3); + } + } else { + int16 param3; - param3 = *(localScriptPtr + position); - position++; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - if (opcode - 1 == 0xA) { - sprintf(lineBuffer, "var[%d]+=var[%d]\n", param1, param3); - } else if (opcode - 1 == 0xB) { - sprintf(lineBuffer, "var[%d]-=var[%d]\n", param1, param3); - } else if (opcode - 1 == 0xC) { - sprintf(lineBuffer, "var[%d]*=var[%d]\n", param1, param3); - } else if (opcode - 1 == 0xD) { - sprintf(lineBuffer, "var[%d]/=var[%d]\n", param1, param3); - } - } else { - int16 param3; - - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; - - if (opcode - 1 == 0xA) { - sprintf(lineBuffer, "var[%d]+=%d\n", param1, param3); - } else if (opcode - 1 == 0xB) { - sprintf(lineBuffer, "var[%d]-=%d\n", param1, param3); - } else if (opcode - 1 == 0xC) { - sprintf(lineBuffer, "var[%d]*=%d\n", param1, param3); - } else if (opcode - 1 == 0xD) { - sprintf(lineBuffer, "var[%d]/=%d\n", param1, param3); - } + if (opcode - 1 == 0xA) { + sprintf(lineBuffer, "var[%d]+=%d\n", param1, param3); + } else if (opcode - 1 == 0xB) { + sprintf(lineBuffer, "var[%d]-=%d\n", param1, param3); + } else if (opcode - 1 == 0xC) { + sprintf(lineBuffer, "var[%d]*=%d\n", param1, param3); + } else if (opcode - 1 == 0xD) { + sprintf(lineBuffer, "var[%d]/=%d\n", param1, param3); } - break; } - case 0xE: - { - byte param1; - byte param2; + break; + } + case 0xE: { + byte param1; + byte param2; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - if (param2) { - byte param3; + if (param2) { + byte param3; - param3 = *(localScriptPtr + position); - position++; + param3 = *(localScriptPtr + position); + position++; - if (param2 == 1) { - sprintf(compareString1, "var[%d]", param1); - sprintf(compareString2, "var[%d]", param3); + if (param2 == 1) { + sprintf(compareString1, "var[%d]", param1); + sprintf(compareString2, "var[%d]", param3); - } else if (param2 == 2) { - sprintf(compareString1, "var[%d]", param1); - sprintf(compareString2, "globalVar[%d]", param3); - } else { - error("decompileScript: 0x0E: param2 = %d", param2); - } + } else if (param2 == 2) { + sprintf(compareString1, "var[%d]", param1); + sprintf(compareString2, "globalVar[%d]", param3); } else { - int16 param3; + error("decompileScript: 0x0E: param2 = %d", param2); + } + } else { + int16 param3; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(compareString1, "var[%d]", param1); - sprintf(compareString2, "%d", param3); - } - break; + sprintf(compareString1, "var[%d]", param1); + sprintf(compareString2, "%d", param3); } - case 0xF: - { - byte param1; - byte param2; - byte param3; + break; + } + case 0xF: { + byte param1; + byte param2; + byte param3; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = *(localScriptPtr + position); - position++; + param3 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "obj[%d]%s=var[%d]\n", param1, getObjPramName(param2), param3); + sprintf(lineBuffer, "obj[%d]%s=var[%d]\n", param1, getObjPramName(param2), param3); - break; - } + break; + } case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: - case 0x19: - { - byte param; - - param = *(localScriptPtr + position); - position++; - - if (opcode - 1 == 0x13) { - sprintf(lineBuffer, "loadMask0(%d)\n", param); - } else if (opcode - 1 == 0x14) { - sprintf(lineBuffer, "unloadMask0(%d)\n", param); - } else if (opcode - 1 == 0x15) { - sprintf(lineBuffer, "OP_15(%d)\n", param); - } else if (opcode - 1 == 0x16) { - sprintf(lineBuffer, "loadMask1(%d)\n", param); - } else if (opcode - 1 == 0x17) { - sprintf(lineBuffer, "unloadMask0(%d)\n", param); - } else if (opcode - 1 == 0x18) { - sprintf(lineBuffer, "loadMask4(%d)\n", param); - } else if (opcode - 1 == 0x19) { - sprintf(lineBuffer, "unloadMask4(%d)\n", param); - } - break; + case 0x19: { + byte param; + + param = *(localScriptPtr + position); + position++; + + if (opcode - 1 == 0x13) { + sprintf(lineBuffer, "loadMask0(%d)\n", param); + } else if (opcode - 1 == 0x14) { + sprintf(lineBuffer, "unloadMask0(%d)\n", param); + } else if (opcode - 1 == 0x15) { + sprintf(lineBuffer, "OP_15(%d)\n", param); + } else if (opcode - 1 == 0x16) { + sprintf(lineBuffer, "loadMask1(%d)\n", param); + } else if (opcode - 1 == 0x17) { + sprintf(lineBuffer, "unloadMask0(%d)\n", param); + } else if (opcode - 1 == 0x18) { + sprintf(lineBuffer, "loadMask4(%d)\n", param); + } else if (opcode - 1 == 0x19) { + sprintf(lineBuffer, "unloadMask4(%d)\n", param); } - case 0x1A: - { - byte param; + break; + } + case 0x1A: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_1A(%d)\n", param); + sprintf(lineBuffer, "OP_1A(%d)\n", param); - break; - } - case 0x1B: - { - sprintf(lineBuffer, "bgIncrustList.clear()\n"); - break; - } - case 0x1D: - { - byte param; + break; + } + case 0x1B: { + sprintf(lineBuffer, "bgIncrustList.clear()\n"); + break; + } + case 0x1D: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "label(%d)\n", param); + sprintf(lineBuffer, "label(%d)\n", param); - break; - } - case 0x1E: - { - byte param; + break; + } + case 0x1E: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "goto(%d)\n", param); + sprintf(lineBuffer, "goto(%d)\n", param); - break; - } + break; + } // If cases case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23: - case 0x24: - { - byte param; - - param = *(localScriptPtr + position); - position++; - - if (opcode - 1 == 0x1F) { - sprintf(lineBuffer, "if(%s>%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x20) { - sprintf(lineBuffer, "if(%s>=%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x21) { - sprintf(lineBuffer, "if(%s<%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x22) { - sprintf(lineBuffer, "if(%s<=%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x23) { - sprintf(lineBuffer, "if(%s==%s) goto(%d)\n", compareString1, compareString2, param); - } else if (opcode - 1 == 0x24) { - sprintf(lineBuffer, "if(%s!=%s) goto(%d)\n", compareString1, compareString2, param); - } - break; + case 0x24: { + byte param; + + param = *(localScriptPtr + position); + position++; + + if (opcode - 1 == 0x1F) { + sprintf(lineBuffer, "if(%s>%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x20) { + sprintf(lineBuffer, "if(%s>=%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x21) { + sprintf(lineBuffer, "if(%s<%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x22) { + sprintf(lineBuffer, "if(%s<=%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x23) { + sprintf(lineBuffer, "if(%s==%s) goto(%d)\n", compareString1, compareString2, param); + } else if (opcode - 1 == 0x24) { + sprintf(lineBuffer, "if(%s!=%s) goto(%d)\n", compareString1, compareString2, param); } - case 0x25: - { - byte param; + break; + } + case 0x25: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "removeLabel(%d)\n", param); + sprintf(lineBuffer, "removeLabel(%d)\n", param); - break; - } - case 0x26: - { - byte param1; - byte param2; + break; + } + case 0x26: { + byte param1; + byte param2; - param1 = *(localScriptPtr + position); - position++; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; + param2 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "loop(--var[%d]) -> label(%d)\n", param1, param2); + sprintf(lineBuffer, "loop(--var[%d]) -> label(%d)\n", param1, param2); - break; - } + break; + } case 0x31: - case 0x32: - { - byte param; + case 0x32: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - if (opcode - 1 == 0x31) { - sprintf(lineBuffer, "startGlobalScript(%d)\n", param); - } else if (opcode - 1 == 0x32) { - sprintf(lineBuffer, "endGlobalScript(%d)\n", param); - } - break; + if (opcode - 1 == 0x31) { + sprintf(lineBuffer, "startGlobalScript(%d)\n", param); + } else if (opcode - 1 == 0x32) { + sprintf(lineBuffer, "endGlobalScript(%d)\n", param); } + break; + } case 0x3B: case 0x3C: case 0x3D: - case OP_loadPart: - { - if (opcode - 1 == 0x3B) { - sprintf(lineBuffer, "loadResource(%s)\n", localScriptPtr + position); - } else if (opcode - 1 == 0x3C) { - sprintf(lineBuffer, "loadBg(%s)\n", localScriptPtr + position); - } else if (opcode - 1 == 0x3D) { - sprintf(lineBuffer, "loadCt(%s)\n", localScriptPtr + position); - } else if (opcode - 1 == OP_loadPart) { - sprintf(lineBuffer, "loadPart(%s)\n", localScriptPtr + position); - } - - position += strlen((const char *)localScriptPtr + position) + 1; - break; - } - case 0x40: - { - sprintf(lineBuffer, "closePart()\n"); - break; - } - case OP_loadNewPrcName: - { - byte param; + case OP_loadPart: { + if (opcode - 1 == 0x3B) { + sprintf(lineBuffer, "loadResource(%s)\n", localScriptPtr + position); + } else if (opcode - 1 == 0x3C) { + sprintf(lineBuffer, "loadBg(%s)\n", localScriptPtr + position); + } else if (opcode - 1 == 0x3D) { + sprintf(lineBuffer, "loadCt(%s)\n", localScriptPtr + position); + } else if (opcode - 1 == OP_loadPart) { + sprintf(lineBuffer, "loadPart(%s)\n", localScriptPtr + position); + } + + position += strlen((const char *)localScriptPtr + position) + 1; + break; + } + case 0x40: { + sprintf(lineBuffer, "closePart()\n"); + break; + } + case OP_loadNewPrcName: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "loadPrc(%d,%s)\n", param, localScriptPtr + position); + sprintf(lineBuffer, "loadPrc(%d,%s)\n", param, localScriptPtr + position); - position += strlen((const char *)localScriptPtr + position) + 1; - break; - } - case OP_requestCheckPendingDataLoad: // nop - { - sprintf(lineBuffer, "requestCheckPendingDataLoad()\n"); - break; - } - case 0x45: - { - sprintf(lineBuffer, "blitAndFade()\n"); - break; - } - case 0x46: - { - sprintf(lineBuffer, "fadeToBlack()\n"); - break; - } - case 0x47: - { - byte param1; - byte param2; - int16 param3; - int16 param4; - int16 param5; + position += strlen((const char *)localScriptPtr + position) + 1; + break; + } + case OP_requestCheckPendingDataLoad: { // nop + sprintf(lineBuffer, "requestCheckPendingDataLoad()\n"); + break; + } + case 0x45: { + sprintf(lineBuffer, "blitAndFade()\n"); + break; + } + case 0x46: { + sprintf(lineBuffer, "fadeToBlack()\n"); + break; + } + case 0x47: { + byte param1; + byte param2; + int16 param3; + int16 param4; + int16 param5; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "transformPaletteRange(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); + sprintf(lineBuffer, "transformPaletteRange(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); - break; - } - case 0x49: - { - byte param; + break; + } + case 0x49: { + byte param; - param = *(localScriptPtr + position); - position++; + param = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "setDefaultMenuBgColor(%d)\n", param); + sprintf(lineBuffer, "setDefaultMenuBgColor(%d)\n", param); - break; - } - case 0x4F: - { - sprintf(lineBuffer, "break()\n"); - exitScript = 1; - break; - } - case 0x50: - { - sprintf(lineBuffer, "endScript()\n\n"); - break; - } - case 0x51: - { - byte param1; - int16 param2; - int16 param3; - int16 param4; - int16 param5; + break; + } + case 0x4F: { + sprintf(lineBuffer, "break()\n"); + exitScript = 1; + break; + } + case 0x50: { + sprintf(lineBuffer, "endScript()\n\n"); + break; + } + case 0x51: { + byte param1; + int16 param2; + int16 param3; + int16 param4; + int16 param5; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "message(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); + sprintf(lineBuffer, "message(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); - break; - } + break; + } case 0x52: - case 0x53: - { - byte param1; - byte param2; + case 0x53: { + byte param1; + byte param2; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - if (param2) { - byte param3; - - param3 = *(localScriptPtr + position); - position++; - - if (param2 == 1) { - if (opcode - 1 == 0x52) { - sprintf(lineBuffer, "globalVar[%d] = var[%d]\n", param1, param3); - } else if (opcode - 1 == 0x53) { - sprintf(compareString1, "globalVar[%d]", param1); - sprintf(compareString2, "var[%d]", param3); - } - } else if (param2 == 2) { - if (opcode - 1 == 0x52) { - sprintf(lineBuffer, "globalVar[%d] = globalVar[%d]\n", param1, param3); - } else if (opcode - 1 == 0x53) { - sprintf(compareString1, "globalVar[%d]", param1); - sprintf(compareString2, "globalVar[%d]", param3); - } - } else { - if (opcode - 1 == 0x52) { - error("decompileScript: 0x52: param2 = %d", param2); - } else if (opcode - 1 == 0x53) { - error("decompileScript: 0x53: param2 = %d", param2); - } - } - } else { - int16 param3; + if (param2) { + byte param3; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = *(localScriptPtr + position); + position++; + if (param2 == 1) { if (opcode - 1 == 0x52) { - sprintf(lineBuffer, "globalVar[%d] = %d\n", param1, param3); + sprintf(lineBuffer, "globalVar[%d] = var[%d]\n", param1, param3); } else if (opcode - 1 == 0x53) { sprintf(compareString1, "globalVar[%d]", param1); - sprintf(compareString2, "%d", param3); + sprintf(compareString2, "var[%d]", param3); + } + } else if (param2 == 2) { + if (opcode - 1 == 0x52) { + sprintf(lineBuffer, "globalVar[%d] = globalVar[%d]\n", param1, param3); + } else if (opcode - 1 == 0x53) { + sprintf(compareString1, "globalVar[%d]", param1); + sprintf(compareString2, "globalVar[%d]", param3); + } + } else { + if (opcode - 1 == 0x52) { + error("decompileScript: 0x52: param2 = %d", param2); + } else if (opcode - 1 == 0x53) { + error("decompileScript: 0x53: param2 = %d", param2); } } - break; - } - case 0x59: - { - sprintf(lineBuffer, "comment: %s\n", localScriptPtr + position); + } else { + int16 param3; - position += strlen((const char *)localScriptPtr + position); - break; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; + + if (opcode - 1 == 0x52) { + sprintf(lineBuffer, "globalVar[%d] = %d\n", param1, param3); + } else if (opcode - 1 == 0x53) { + sprintf(compareString1, "globalVar[%d]", param1); + sprintf(compareString2, "%d", param3); + } } - case 0x5A: - { - byte param1; - byte param2; + break; + } + case 0x59: { + sprintf(lineBuffer, "comment: %s\n", localScriptPtr + position); - param1 = *(localScriptPtr + position); - position++; + position += strlen((const char *)localScriptPtr + position); + break; + } + case 0x5A: { + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "freePartRang(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - case 0x5B: - { - sprintf(lineBuffer, "unloadAllMasks()\n"); - break; - } - case 0x65: - { - sprintf(lineBuffer, "setupTableUnk1()\n"); - break; - } - case 0x66: - { - byte param1; - int16 param2; + sprintf(lineBuffer, "freePartRang(%d,%d)\n", param1, param2); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x5B: { + sprintf(lineBuffer, "unloadAllMasks()\n"); + break; + } + case 0x65: { + sprintf(lineBuffer, "setupTableUnk1()\n"); + break; + } + case 0x66: { + byte param1; + int16 param2; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "tableUnk1[%d] = %d\n", param1, param2); + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; - } - case 0x68: - { - byte param; + sprintf(lineBuffer, "tableUnk1[%d] = %d\n", param1, param2); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x68: { + byte param; - sprintf(lineBuffer, "setPlayerCommandPosY(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x69: - { - sprintf(lineBuffer, "allowPlayerInput()\n"); - break; - } - case 0x6A: - { - sprintf(lineBuffer, "disallowPlayerInput()\n"); - break; - } - case 0x6B: - { - byte newDisk; + sprintf(lineBuffer, "setPlayerCommandPosY(%d)\n", param); - newDisk = *(localScriptPtr + position); - position++; + break; + } + case 0x69: { + sprintf(lineBuffer, "allowPlayerInput()\n"); + break; + } + case 0x6A: { + sprintf(lineBuffer, "disallowPlayerInput()\n"); + break; + } + case 0x6B: { + byte newDisk; - sprintf(lineBuffer, "changeDataDisk(%d)\n", newDisk); + newDisk = *(localScriptPtr + position); + position++; - break; - } - case 0x6D: - { - sprintf(lineBuffer, "loadDat(%s)\n", localScriptPtr + position); + sprintf(lineBuffer, "changeDataDisk(%d)\n", newDisk); - position += strlen((const char *)localScriptPtr + position) + 1; - break; - } - case 0x6E: // nop - { - sprintf(lineBuffer, "updateDat()\n"); - break; - } - case 0x6F: - { - sprintf(lineBuffer, "OP_6F() -> dat related\n"); - break; - } - case 0x70: - { - sprintf(lineBuffer, "stopSample()\n"); - break; - } - case 0x79: - { - byte param; + break; + } + case 0x6D: { + sprintf(lineBuffer, "loadDat(%s)\n", localScriptPtr + position); - param = *(localScriptPtr + position); - position++; + position += strlen((const char *)localScriptPtr + position) + 1; + break; + } + case 0x6E: { // nop + sprintf(lineBuffer, "updateDat()\n"); + break; + } + case 0x6F: { + sprintf(lineBuffer, "OP_6F() -> dat related\n"); + break; + } + case 0x70: { + sprintf(lineBuffer, "stopSample()\n"); + break; + } + case 0x79: { + byte param; - sprintf(lineBuffer, "disableSystemMenu(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } + sprintf(lineBuffer, "disableSystemMenu(%d)\n", param); + + break; + } case 0x77: - case 0x78: - { - byte param1; - byte param2; - int16 param3; - byte param4; - int16 param5; - int16 param6; + case 0x78: { + byte param1; + byte param2; + int16 param3; + byte param4; + int16 param5; + int16 param6; - param1 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = *(localScriptPtr + position); - position++; + param4 = *(localScriptPtr + position); + position++; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param6 = READ_BE_UINT16(localScriptPtr + position); - position += 2; - - if (opcode - 1 == 0x77) { - sprintf(lineBuffer, "playSample(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6); - } else if (opcode - 1 == 0x78) { - sprintf(lineBuffer, "OP_78(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6); - } + param6 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; + if (opcode - 1 == 0x77) { + sprintf(lineBuffer, "playSample(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6); + } else if (opcode - 1 == 0x78) { + sprintf(lineBuffer, "playSampleSwapped(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6); } - case 0x7A: - { - byte param; - param = *(localScriptPtr + position); - position++; + break; + } + case 0x7A: { + byte param; - sprintf(lineBuffer, "OP_7A(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x7B: // OS only - { - byte param; + sprintf(lineBuffer, "OP_7A(%d)\n", param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x7B: { // OS only + byte param; - sprintf(lineBuffer, "OP_7B(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x7F: // OS only - { - byte param1; - byte param2; - byte param3; - byte param4; - int16 param5; - int16 param6; - int16 param7; + sprintf(lineBuffer, "OP_7B(%d)\n", param); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x7F: { // OS only + byte param1; + byte param2; + byte param3; + byte param4; + int16 param5; + int16 param6; + int16 param7; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param3 = *(localScriptPtr + position); - position++; + param2 = *(localScriptPtr + position); + position++; - param4 = *(localScriptPtr + position); - position++; + param3 = *(localScriptPtr + position); + position++; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = *(localScriptPtr + position); + position++; - param6 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param7 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param6 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "OP_7F(%d,%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6, param7); + param7 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; - } - case 0x80: // OS only - { - byte param1; - byte param2; + sprintf(lineBuffer, "OP_7F(%d,%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6, param7); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x80: { // OS only + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_80(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - case 0x82: // OS only - { - byte param1; - byte param2; - uint16 param3; - uint16 param4; - byte param5; + sprintf(lineBuffer, "OP_80(%d,%d)\n", param1, param2); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x82: { // OS only + byte param1; + byte param2; + uint16 param3; + uint16 param4; + byte param5; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param2 = *(localScriptPtr + position); + position++; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = *(localScriptPtr + position); - position++; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(lineBuffer, "OP_82(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); + param5 = *(localScriptPtr + position); + position++; - break; - } - case 0x83: // OS only - { - byte param1; - byte param2; + sprintf(lineBuffer, "OP_82(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x83: { // OS only + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_83(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - case 0x89: // OS only - { - byte param; + sprintf(lineBuffer, "OP_83(%d,%d)\n", param1, param2); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x89: { // OS only + byte param; - sprintf(lineBuffer, "if(%s!=%s) goto next label(%d)\n", compareString1, compareString2, param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x8B: // OS only - { - byte param; + sprintf(lineBuffer, "if(%s!=%s) goto next label(%d)\n", compareString1, compareString2, param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x8B: { // OS only + byte param; - sprintf(lineBuffer, "OP_8B(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x8C: // OS only - { - byte param; + sprintf(lineBuffer, "OP_8B(%d)\n", param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x8C: { // OS only + byte param; - sprintf(lineBuffer, "OP_8C(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x8D: // OS only - { - int16 param1; - int16 param2; - int16 param3; - int16 param4; - int16 param5; - int16 param6; - int16 param7; - int16 param8; + sprintf(lineBuffer, "OP_8C(%d)\n", param); - param1 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + break; + } + case 0x8D: { // OS only + int16 param1; + int16 param2; + int16 param3; + int16 param4; + int16 param5; + int16 param6; + int16 param7; + int16 param8; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param1 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param3 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param4 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param3 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param5 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param4 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param6 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param5 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param7 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param6 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - param8 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + param7 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - sprintf(compareString1, "obj[%d]", param1); - sprintf(compareString2, "{%d,%d,%d,%d,%d,%d,%d}", param2, param3, param4, param5, param6, param7, param8); + param8 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; - } - case 0x8E: // OS only - { - byte param1; + sprintf(compareString1, "obj[%d]", param1); + sprintf(compareString2, "{%d,%d,%d,%d,%d,%d,%d}", param2, param3, param4, param5, param6, param7, param8); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x8E: { // OS only + byte param1; - sprintf(lineBuffer, "ADDBG(%d,%s)\n", param1, localScriptPtr + position); + param1 = *(localScriptPtr + position); + position++; - position += strlen((const char *)localScriptPtr + position); + sprintf(lineBuffer, "ADDBG(%d,%s)\n", param1, localScriptPtr + position); - break; - } - case 0x8F: // OS only - { - byte param; + position += strlen((const char *)localScriptPtr + position); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x8F: { // OS only + byte param; - sprintf(lineBuffer, "OP_8F(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x90: // OS only - { - byte param1; + sprintf(lineBuffer, "OP_8F(%d)\n", param); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0x90: { // OS only + byte param1; - sprintf(lineBuffer, "loadABS(%d,%s)\n", param1, localScriptPtr + position); + param1 = *(localScriptPtr + position); + position++; - position += strlen((const char *)localScriptPtr + position); + sprintf(lineBuffer, "loadABS(%d,%s)\n", param1, localScriptPtr + position); - break; - } - case 0x91: // OS only - { - byte param; + position += strlen((const char *)localScriptPtr + position); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x91: { // OS only + byte param; - sprintf(lineBuffer, "OP_91(%d)\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x9D: // OS only - { - byte param; + sprintf(lineBuffer, "OP_91(%d)\n", param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x9D: { // OS only + byte param; - sprintf(lineBuffer, "OP_9D(%d) -> flip img idx\n", param); + param = *(localScriptPtr + position); + position++; - break; - } - case 0x9E: // OS only - { - byte param; + sprintf(lineBuffer, "OP_9D(%d) -> flip img idx\n", param); - param = *(localScriptPtr + position); - position++; + break; + } + case 0x9E: { // OS only + byte param; - if (param) { - byte param2; + param = *(localScriptPtr + position); + position++; - param2 = *(localScriptPtr + position); - position++; + if (param) { + byte param2; - sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2); - } else { - int16 param2; + param2 = *(localScriptPtr + position); + position++; - param2 = READ_BE_UINT16(localScriptPtr + position); - position += 2; + sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2); + } else { + int16 param2; - sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2); - } + param2 = READ_BE_UINT16(localScriptPtr + position); + position += 2; - break; + sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2); } - case 0xA0: // OS only - { - byte param1; - byte param2; - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0xA0: { // OS only + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_A0(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - case 0xA1: // OS only - { - byte param1; - byte param2; + sprintf(lineBuffer, "OP_A0(%d,%d)\n", param1, param2); - param1 = *(localScriptPtr + position); - position++; + break; + } + case 0xA1: { // OS only + byte param1; + byte param2; - param2 = *(localScriptPtr + position); - position++; + param1 = *(localScriptPtr + position); + position++; - sprintf(lineBuffer, "OP_A1(%d,%d)\n", param1, param2); + param2 = *(localScriptPtr + position); + position++; - break; - } - default: - { - sprintf(lineBuffer, "Unsupported opcode %X in decompileScript\n\n", opcode - 1); - position = scriptSize; - break; - } + sprintf(lineBuffer, "OP_A1(%d,%d)\n", param1, param2); + + break; + } + default: { + sprintf(lineBuffer, "Unsupported opcode %X in decompileScript\n\n", opcode - 1); + position = scriptSize; + break; + } } - // printf(lineBuffer); + //printf(lineBuffer); strcpy(decompileBuffer[decompileBufferPosition++], lineBuffer); exitScript = 0; diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp index 52e1cdac7e..10404ae56b 100644 --- a/engines/cine/sound.cpp +++ b/engines/cine/sound.cpp @@ -153,7 +153,7 @@ const int AdLibSoundDriver::_freqTable[] = { const int AdLibSoundDriver::_freqTableCount = ARRAYSIZE(_freqTable); const int AdLibSoundDriver::_operatorsTable[] = { - 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 }; const int AdLibSoundDriver::_operatorsTableCount = ARRAYSIZE(_operatorsTable); @@ -614,7 +614,7 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in } MidiSoundDriverH32::MidiSoundDriverH32(MidiDriver *output) - : _output(output), _callback(0), _mutex() { + : _output(output), _callback(0), _mutex() { } MidiSoundDriverH32::~MidiSoundDriverH32() { @@ -731,13 +731,13 @@ void MidiSoundDriverH32::selectInstrument(int channel, int timbreGroup, int timb 0x00, 0x00, 0x00, // offset 0x00, // Timbre group _ timbreGroup * 64 + timbreNumber should be the 0x00, // Timbre number / MT-32 instrument in case timbreGroup is 0 or 1. - 0x18, // Key shift (= 0) + 0x18, // Key shift (= 0) 0x32, // Fine tune (= 0) 0x0C, // Bender Range 0x03, // Assign Mode 0x01, // Reverb Switch (= enabled) 0x00, // dummy - 0x00, // Output level + 0x00, // Output level 0x07, // Panpot (= balanced) 0x00, // dummy 0x00, // dummy @@ -998,19 +998,55 @@ void PCSound::stopSound(int channel) { } PaulaSound::PaulaSound(Audio::Mixer *mixer, CineEngine *vm) - : Sound(mixer, vm) { + : Sound(mixer, vm), _sfxTimer(0), _musicTimer(0), _musicFadeTimer(0) { _moduleStream = 0; + // The original is using the following timer frequency: + // 0.709379Mhz / 8000 = 88.672375Hz + // 1000000 / 88.672375Hz = 11277.46944863us + g_system->getTimerManager()->installTimerProc(&PaulaSound::sfxTimerProc, 11277, this, "PaulaSound::sfxTimerProc"); + // The original is using the following timer frequency: + // 0.709379Mhz / 14565 = 48.704359Hz + // 1000000 / 48.704359Hz = 20532.04313806us + g_system->getTimerManager()->installTimerProc(&PaulaSound::musicTimerProc, 20532, this, "PaulaSound::musicTimerProc"); } PaulaSound::~PaulaSound() { + Common::StackLock sfxLock(_sfxMutex); + g_system->getTimerManager()->removeTimerProc(&PaulaSound::sfxTimerProc); for (int i = 0; i < NUM_CHANNELS; ++i) { stopSound(i); } + + Common::StackLock musicLock(_musicMutex); + g_system->getTimerManager()->removeTimerProc(&PaulaSound::musicTimerProc); stopMusic(); } void PaulaSound::loadMusic(const char *name) { debugC(5, kCineDebugSound, "PaulaSound::loadMusic('%s')", name); + for (int i = 0; i < NUM_CHANNELS; ++i) { + stopSound(i); + } + + // Fade music out when there is music playing. + _musicMutex.lock(); + if (_mixer->isSoundHandleActive(_moduleHandle)) { + // Only start fade out when it is not in progress. + if (!_musicFadeTimer) { + _musicFadeTimer = 1; + } + + _musicMutex.unlock(); + while (_musicFadeTimer != 64) { + g_system->delayMillis(50); + } + } else { + _musicMutex.unlock(); + } + + Common::StackLock lock(_musicMutex); + assert(!_mixer->isSoundHandleActive(_moduleHandle)); + if (_vm->getGameType() == GType_FW) { // look for separate files Common::File f; @@ -1031,54 +1067,135 @@ void PaulaSound::loadMusic(const char *name) { void PaulaSound::playMusic() { debugC(5, kCineDebugSound, "PaulaSound::playMusic()"); + Common::StackLock lock(_musicMutex); + _mixer->stopHandle(_moduleHandle); if (_moduleStream) { + _musicFadeTimer = 0; _mixer->playStream(Audio::Mixer::kMusicSoundType, &_moduleHandle, _moduleStream); } } void PaulaSound::stopMusic() { debugC(5, kCineDebugSound, "PaulaSound::stopMusic()"); + Common::StackLock lock(_musicMutex); + _mixer->stopHandle(_moduleHandle); } void PaulaSound::fadeOutMusic() { debugC(5, kCineDebugSound, "PaulaSound::fadeOutMusic()"); - // TODO - stopMusic(); + Common::StackLock lock(_musicMutex); + + _musicFadeTimer = 1; } void PaulaSound::playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) { - // TODO: handle volume slides and repeat debugC(5, kCineDebugSound, "PaulaSound::playSound() channel %d size %d", channel, size); + Common::StackLock lock(_sfxMutex); + assert(frequency > 0); + stopSound(channel); - size = MIN<int>(size - SPL_HDR_SIZE, READ_BE_UINT16(data + 4)); - // TODO: consider skipping the header in loadSpl directly if (size > 0) { byte *sound = (byte *)malloc(size); if (sound) { - memcpy(sound, data + SPL_HDR_SIZE, size); - playSoundChannel(channel, frequency, sound, size, volume); + // Create the audio stream + memcpy(sound, data, size); + + // Clear the first and last 16 bits like in the original. + sound[0] = sound[1] = sound[size - 2] = sound[size - 1] = 0; + + Audio::SeekableAudioStream *stream = Audio::makeRawStream(sound, size, PAULA_FREQ / frequency, 0); + + // Initialize the volume control + _channelsTable[channel].initialize(volume, volumeStep, stepCount); + + // Start the sfx + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channelsTable[channel].handle, + Audio::makeLoopingAudioStream(stream, repeat ? 0 : 1), + -1, volume * Audio::Mixer::kMaxChannelVolume / 63, + _channelBalance[channel]); } } } void PaulaSound::stopSound(int channel) { debugC(5, kCineDebugSound, "PaulaSound::stopSound() channel %d", channel); - _mixer->stopHandle(_channelsTable[channel]); + Common::StackLock lock(_sfxMutex); + + _mixer->stopHandle(_channelsTable[channel].handle); } -void PaulaSound::update() { - // process volume slides and start sound playback - // TODO +void PaulaSound::sfxTimerProc(void *param) { + PaulaSound *sound = (PaulaSound *)param; + sound->sfxTimerCallback(); } -void PaulaSound::playSoundChannel(int channel, int frequency, uint8 *data, int size, int volume) { - assert(frequency > 0); - frequency = PAULA_FREQ / frequency; - Audio::AudioStream *stream = Audio::makeRawStream(data, size, frequency, 0); - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channelsTable[channel], stream); - _mixer->setChannelVolume(_channelsTable[channel], volume * Audio::Mixer::kMaxChannelVolume / 63); +void PaulaSound::sfxTimerCallback() { + Common::StackLock lock(_sfxMutex); + + if (_sfxTimer < 6) { + ++_sfxTimer; + + for (int i = 0; i < NUM_CHANNELS; ++i) { + // Only process active channels + if (!_mixer->isSoundHandleActive(_channelsTable[i].handle)) { + continue; + } + + if (_channelsTable[i].curStep) { + --_channelsTable[i].curStep; + } else { + _channelsTable[i].curStep = _channelsTable[i].stepCount; + const int volume = CLIP(_channelsTable[i].volume + _channelsTable[i].volumeStep, 0, 63); + _channelsTable[i].volume = volume; + // Unlike the original we stop silent sounds + if (volume) { + _mixer->setChannelVolume(_channelsTable[i].handle, volume * Audio::Mixer::kMaxChannelVolume / 63); + } else { + _mixer->stopHandle(_channelsTable[i].handle); + } + } + } + } else { + _sfxTimer = 0; + // Possible TODO: The original only ever started sounds here. This + // should not be noticable though. So we do not do it for now. + } } +void PaulaSound::musicTimerProc(void *param) { + PaulaSound *sound = (PaulaSound *)param; + sound->musicTimerCallback(); +} + +void PaulaSound::musicTimerCallback() { + Common::StackLock lock(_musicMutex); + + ++_musicTimer; + if (_musicTimer == 6) { + _musicTimer = 0; + if (_musicFadeTimer) { + ++_musicFadeTimer; + if (_musicFadeTimer == 64) { + stopMusic(); + } else { + if (_mixer->isSoundHandleActive(_moduleHandle)) { + _mixer->setChannelVolume(_moduleHandle, (64 - _musicFadeTimer) * Audio::Mixer::kMaxChannelVolume / 64); + } + } + } + } +} + +const int PaulaSound::_channelBalance[NUM_CHANNELS] = { + // L/R/R/L This is according to the Hardware Reference Manual. + // TODO: It seems the order is swapped for some Amiga models: + // http://www.amiga.org/forums/archive/index.php/t-7862.html + // Maybe we should consider using R/L/L/R to match Amiga 500? + // This also is a bit more drastic to what WineUAE defaults, + // which is only 70% of full panning. + -127, 127, 127, -127 +}; + } // End of namespace Cine diff --git a/engines/cine/sound.h b/engines/cine/sound.h index afc0994a26..fdb183ad34 100644 --- a/engines/cine/sound.h +++ b/engines/cine/sound.h @@ -24,6 +24,7 @@ #define CINE_SOUND_H_ #include "common/util.h" +#include "common/mutex.h" #include "audio/mixer.h" namespace Audio { @@ -47,7 +48,6 @@ public: virtual void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) = 0; virtual void stopSound(int channel) = 0; - virtual void update() {} protected: @@ -91,19 +91,39 @@ public: virtual void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat); virtual void stopSound(int channel); - virtual void update(); enum { - PAULA_FREQ = 7093789, - NUM_CHANNELS = 4, - SPL_HDR_SIZE = 22 + PAULA_FREQ = 3579545, + NUM_CHANNELS = 4 }; protected: - void playSoundChannel(int channel, int frequency, uint8 *data, int size, int volume); - - Audio::SoundHandle _channelsTable[NUM_CHANNELS]; + struct SfxChannel { + Audio::SoundHandle handle; + int volume; + int volumeStep; + int curStep; + int stepCount; + + void initialize(int vol, int volStep, int stepCnt) { + volume = vol; + volumeStep = volStep; + curStep = stepCount = stepCnt; + } + }; + SfxChannel _channelsTable[NUM_CHANNELS]; + static const int _channelBalance[NUM_CHANNELS]; + Common::Mutex _sfxMutex; + int _sfxTimer; + static void sfxTimerProc(void *param); + void sfxTimerCallback(); + + Common::Mutex _musicMutex; + int _musicTimer; + int _musicFadeTimer; + static void musicTimerProc(void *param); + void musicTimerCallback(); Audio::SoundHandle _moduleHandle; Audio::AudioStream *_moduleStream; }; diff --git a/engines/cine/texte.cpp b/engines/cine/texte.cpp index 33ea569df7..998075c6ce 100644 --- a/engines/cine/texte.cpp +++ b/engines/cine/texte.cpp @@ -88,7 +88,7 @@ static const CharacterEntry fontParamTable_standard[NUM_FONT_CHARS] = { {64, 3}, {65, 3}, { 0, 0}, { 0, 0}, {62, 2}, {74, 6}, {66, 1}, {67, 6}, {52, 6}, {53, 6}, {54, 6}, {55, 6}, {56, 6}, {57, 6}, {58, 6}, {59, 6}, {60, 6}, {61, 6}, {76, 3}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {75, 6}, - { 0, 0}, { 0, 6}, //a + { 0, 0}, { 0, 6}, //a { 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6}, { 7, 6}, { 8, 3}, { 9, 6}, {10, 6}, {11, 6}, {12, 7}, {13, 6}, {14, 6}, {15, 6}, {16, 6}, {17, 6}, {18, 6}, {19, 6}, {20, 6}, {21, 6}, {22, 7}, diff --git a/engines/cine/texte.h b/engines/cine/texte.h index dd4b7e06ee..185dc53bfd 100644 --- a/engines/cine/texte.h +++ b/engines/cine/texte.h @@ -46,7 +46,7 @@ struct CharacterEntry { }; struct TextHandler { - byte textTable[NUM_FONT_CHARS][2][FONT_WIDTH * FONT_HEIGHT]; + byte textTable[NUM_FONT_CHARS][2][FONT_WIDTH *FONT_HEIGHT]; CharacterEntry fontParamTable[NUM_FONT_CHARS]; }; diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index eccd71cf05..23f439a7a7 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -99,8 +99,7 @@ byte isInPause = 0; * Bit on = mouse button down * Bit off = mouse button up */ -enum MouseButtonState -{ +enum MouseButtonState { kLeftMouseButton = (1 << 0), kRightMouseButton = (1 << 1) }; @@ -271,7 +270,7 @@ int16 getObjectUnderCursor(uint16 x, uint16 y) { } else if (it->type == 1 && gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].data(), g_cine->_animDataTable[frame]._width * 4)) { return it->objIdx; } - } else if (it->type == 0) { // use generated mask + } else if (it->type == 0) { // use generated mask if (gfxGetBit(xdif, ydif, g_cine->_animDataTable[frame].mask(), g_cine->_animDataTable[frame]._width)) { return it->objIdx; } @@ -358,128 +357,122 @@ void CineEngine::makeSystemMenu() { systemCommand = makeMenuChoice(systemMenu, numEntry, mouseX, mouseY, 140); switch (systemCommand) { - case 0: // Pause - { - renderer->drawString(otherMessages[2], 0); - waitPlayerInput(); - break; - } - case 1: // Restart Game - { - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { - _restartRequested = true; - } - break; - } - case 2: // Quit - { - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { - quitGame(); - } - break; + case 0: { // Pause + renderer->drawString(otherMessages[2], 0); + waitPlayerInput(); + break; + } + case 1: { // Restart Game + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { + _restartRequested = true; } - case 3: // Select save drive... change ? - { - break; + break; + } + case 2: { // Quit + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { + quitGame(); } - case 4: // load game - { - if (loadSaveDirectory()) { + break; + } + case 3: { // Select save drive... change ? + break; + } + case 4: { // load game + if (loadSaveDirectory()) { // int16 selectedSave; - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180); + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180); - if (selectedSave >= 0) { - char saveNameBuffer[256]; - sprintf(saveNameBuffer, "%s.%1d", _targetName.c_str(), selectedSave); + if (selectedSave >= 0) { + char saveNameBuffer[256]; + sprintf(saveNameBuffer, "%s.%1d", _targetName.c_str(), selectedSave); - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { - char loadString[256]; + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { + char loadString[256]; - sprintf(loadString, otherMessages[3], currentSaveName[selectedSave]); - renderer->drawString(loadString, 0); + sprintf(loadString, otherMessages[3], currentSaveName[selectedSave]); + renderer->drawString(loadString, 0); - makeLoad(saveNameBuffer); - } else { - renderer->drawString(otherMessages[4], 0); - waitPlayerInput(); - checkDataDisk(-1); - } + makeLoad(saveNameBuffer); } else { renderer->drawString(otherMessages[4], 0); waitPlayerInput(); checkDataDisk(-1); } } else { - renderer->drawString(otherMessages[5], 0); + renderer->drawString(otherMessages[4], 0); waitPlayerInput(); checkDataDisk(-1); } - break; + } else { + renderer->drawString(otherMessages[5], 0); + waitPlayerInput(); + checkDataDisk(-1); } - case 5: // Save game - { - loadSaveDirectory(); - selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180); + break; + } + case 5: { // Save game + loadSaveDirectory(); + selectedSave = makeMenuChoice(currentSaveName, 10, mouseX, mouseY + 8, 180); - if (selectedSave >= 0) { - char saveFileName[256]; - char saveName[20]; - saveName[0] = 0; + if (selectedSave >= 0) { + char saveFileName[256]; + char saveName[20]; + saveName[0] = 0; - if (!makeTextEntryMenu(otherMessages[6], saveName, 20, 120)) - break; + if (!makeTextEntryMenu(otherMessages[6], saveName, 20, 120)) + break; - strncpy(currentSaveName[selectedSave], saveName, 20); + strncpy(currentSaveName[selectedSave], saveName, 20); - sprintf(saveFileName, "%s.%1d", _targetName.c_str(), selectedSave); + sprintf(saveFileName, "%s.%1d", _targetName.c_str(), selectedSave); - getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); - if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { - char saveString[256]; - Common::String tmp = Common::String::format("%s.dir", _targetName.c_str()); + getMouseData(mouseUpdateStatus, (uint16 *)&mouseButton, (uint16 *)&mouseX, (uint16 *)&mouseY); + if (!makeMenuChoice(confirmMenu, 2, mouseX, mouseY + 8, 100)) { + char saveString[256]; + Common::String tmp = Common::String::format("%s.dir", _targetName.c_str()); - Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(tmp); - if (!fHandle) { - warning("Unable to open file %s for saving", tmp.c_str()); - break; - } + Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(tmp); + if (!fHandle) { + warning("Unable to open file %s for saving", tmp.c_str()); + break; + } - fHandle->write(currentSaveName, 200); - delete fHandle; + fHandle->write(currentSaveName, 200); + delete fHandle; - sprintf(saveString, otherMessages[3], currentSaveName[selectedSave]); - renderer->drawString(saveString, 0); + sprintf(saveString, otherMessages[3], currentSaveName[selectedSave]); + renderer->drawString(saveString, 0); - makeSave(saveFileName); + makeSave(saveFileName); - checkDataDisk(-1); - } else { - renderer->drawString(otherMessages[4], 0); - waitPlayerInput(); - checkDataDisk(-1); - } + checkDataDisk(-1); + } else { + renderer->drawString(otherMessages[4], 0); + waitPlayerInput(); + checkDataDisk(-1); } - break; } + break; + } } inMenu = false; } } -void drawMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 offset, int16 color, byte* page) { - gfxDrawLine(x + offset, y + offset, x + width - offset, y + offset, color, page); // top - gfxDrawLine(x + offset, currentY + 4 - offset, x + width - offset, currentY + 4 - offset, color, page); // bottom - gfxDrawLine(x + offset, y + offset, x + offset, currentY + 4 - offset, color, page); // left - gfxDrawLine(x + width - offset, y + offset, x + width - offset, currentY + 4 - offset, color, page); // right +void drawMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 offset, int16 color, byte *page) { + gfxDrawLine(x + offset, y + offset, x + width - offset, y + offset, color, page); // top + gfxDrawLine(x + offset, currentY + 4 - offset, x + width - offset, currentY + 4 - offset, color, page); // bottom + gfxDrawLine(x + offset, y + offset, x + offset, currentY + 4 - offset, color, page); // left + gfxDrawLine(x + width - offset, y + offset, x + width - offset, currentY + 4 - offset, color, page); // right } -void drawDoubleMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 color, byte* page) { +void drawDoubleMessageBox(int16 x, int16 y, int16 width, int16 currentY, int16 color, byte *page) { drawMessageBox(x, y, width, currentY, 1, 0, page); drawMessageBox(x, y, width, currentY, 0, color, page); } @@ -581,7 +574,7 @@ void makeCommandLine() { g_cine->_commandBuffer = ""; } - if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection ? + if ((playerCommand != -1) && (choiceResultTable[playerCommand] == 2)) { // need object selection? int16 si; getMouseData(mouseUpdateStatus, &dummyU16, &x, &y); @@ -635,7 +628,7 @@ void makeCommandLine() { } if (g_cine->getGameType() == Cine::GType_OS && playerCommand != 2) { - if (playerCommand != -1 && canUseOnObject != 0) { // call use on sub object + if (playerCommand != -1 && canUseOnObject != 0) { // call use on sub object int16 si; getMouseData(mouseUpdateStatus, &dummyU16, &x, &y); @@ -742,11 +735,11 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, mainLoopSub6(); } - if (menuVar4 && currentSelection > 0) { // go up + if (menuVar4 && currentSelection > 0) { // go up currentSelection--; } - if (menuVar5) { // go down + if (menuVar5) { // go down if (height - 1 > currentSelection) { currentSelection++; } @@ -763,7 +756,7 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, } } - if (currentSelection != oldSelection) { // old != new + if (currentSelection != oldSelection) { // old != new if (needMouseSave) { hideMouse(); } @@ -789,7 +782,7 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, getMouseData(mouseUpdateStatus, &button, &dummyU16, &dummyU16); } while (button && !g_cine->shouldQuit()); - if (var_4 == 2) { // recheck + if (var_4 == 2) { // recheck if (!recheckValue) return -1; else @@ -809,7 +802,7 @@ void makeActionMenu() { getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY); if (g_cine->getGameType() == Cine::GType_OS) { - if(disableSystemMenu == 0) { + if (disableSystemMenu == 0) { playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70, true); } @@ -818,7 +811,7 @@ void makeActionMenu() { canUseOnObject = canUseOnItemTable[playerCommand]; } } else { - if(disableSystemMenu == 0) { + if (disableSystemMenu == 0) { playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70); } } @@ -1185,7 +1178,7 @@ void removeMessages() { Common::List<overlay>::iterator it; bool remove; - for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ) { + for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end();) { 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 @@ -1348,7 +1341,7 @@ void modifySeqListElement(uint16 objIdx, int16 var4Test, int16 param1, int16 par } void computeMove1(SeqListElement &element, int16 x, int16 y, int16 param1, - int16 param2, int16 x2, int16 y2) { + int16 param2, int16 x2, int16 y2) { element.var16 = 0; element.var14 = 0; @@ -1397,7 +1390,7 @@ uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &ele int16 di; debug(5, "addAni: param1 = %d, objIdx = %d, ptr = %p, element.var8 = %d, element.var14 = %d param3 = %d", - param1, objIdx, ptr, element.var8, element.var14, param3); + param1, objIdx, ptr, element.var8, element.var14, param3); // In the original an error string is set and 0 is returned if the following doesn't hold assert(ptr); @@ -1413,16 +1406,16 @@ uint16 addAni(uint16 param1, uint16 objIdx, const int8 *ptr, SeqListElement &ele di = (g_cine->_objectTable[objIdx].costume + 1) % (*ptrData); ++ptrData; // Jump over the just read byte // Here ptr2 seems to be indexing a table of structs (8 bytes per struct): - // struct { - // int8 x; // 0 (Used with checkCollision) - // int8 y; // 1 (Used with checkCollision) - // int8 numZones; // 2 (Used with checkCollision) - // int8 var3; // 3 (Not used in this function) - // int8 xAdd; // 4 (Used with an object) - // int8 yAdd; // 5 (Used with an object) - // int8 maskAdd; // 6 (Used with an object) - // int8 frameAdd; // 7 (Used with an object) - // }; + // struct { + // int8 x; // 0 (Used with checkCollision) + // int8 y; // 1 (Used with checkCollision) + // int8 numZones; // 2 (Used with checkCollision) + // int8 var3; // 3 (Not used in this function) + // int8 xAdd; // 4 (Used with an object) + // int8 yAdd; // 5 (Used with an object) + // int8 maskAdd; // 6 (Used with an object) + // int8 frameAdd; // 7 (Used with an object) + // }; ptr2 = ptrData + di * 8; // We might probably safely discard the AND by 1 here because @@ -1572,8 +1565,7 @@ void processSeqListElement(SeqListElement &element) { var_4 = -1; if ((element.var16 == 1 - && !addAni(3, element.objIdx, ptr1, element, 0, &var_4)) || (element.var16 == 2 && !addAni(2, element.objIdx, ptr1, element, 0, - &var_4))) { + && !addAni(3, element.objIdx, ptr1, element, 0, &var_4)) || (element.var16 == 2 && !addAni(2, element.objIdx, ptr1, element, 0, &var_4))) { if (element.varC == 255) { g_cine->_globalVars[VAR_MOUSE_Y_POS] = 0; } @@ -1702,9 +1694,9 @@ bool makeTextEntryMenu(const char *messagePtr, char *inputString, int stringMaxL } break; default: - if (((keycode >= 'a') && (keycode <='z')) || - ((keycode >= '0') && (keycode <='9')) || - ((keycode >= 'A') && (keycode <='Z')) || + if (((keycode >= 'a') && (keycode <= 'z')) || + ((keycode >= '0') && (keycode <= '9')) || + ((keycode >= 'A') && (keycode <= 'Z')) || (keycode == ' ')) { if (inputLength < stringMaxLength - 1) { ch[0] = keycode; diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp index 23a9d2ff85..5db778dfda 100644 --- a/engines/composer/composer.cpp +++ b/engines/composer/composer.cpp @@ -20,7 +20,7 @@ * */ #include "common/scummsys.h" - + #include "common/config-manager.h" #include "common/events.h" #include "common/file.h" @@ -102,13 +102,14 @@ Common::Error ComposerEngine::run() { if (_bookIni.hasKey("Height", "Common")) height = atoi(getStringFromConfig("Common", "Height").c_str()); initGraphics(width, height, true); - _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + _screen.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); _needsUpdate = true; Graphics::Cursor *cursor = Graphics::makeDefaultWinCursor(); CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), cursor->getHotspotX(), cursor->getHotspotY(), cursor->getKeyColor()); CursorMan.replaceCursorPalette(cursor->getPalette(), cursor->getPaletteStartIndex(), cursor->getPaletteCount()); + delete cursor; loadLibrary(0); @@ -213,6 +214,8 @@ Common::Error ComposerEngine::run() { _system->delayMillis(20); } + _screen.free(); + return Common::kNoError; } diff --git a/engines/composer/composer.h b/engines/composer/composer.h index 0f53258289..33a5356b3a 100644 --- a/engines/composer/composer.h +++ b/engines/composer/composer.h @@ -170,7 +170,7 @@ private: bool _needsUpdate; Common::Array<Common::Rect> _dirtyRects; - Graphics::Surface _surface; + Graphics::Surface _screen; Common::List<Sprite> _sprites; uint _directoriesToStrip; diff --git a/engines/composer/graphics.cpp b/engines/composer/graphics.cpp index 1314e903ae..2b68fac233 100644 --- a/engines/composer/graphics.cpp +++ b/engines/composer/graphics.cpp @@ -507,7 +507,7 @@ const Sprite *ComposerEngine::getSpriteAtPos(const Common::Point &pos) { void ComposerEngine::dirtySprite(const Sprite &sprite) { Common::Rect rect(sprite._pos.x, sprite._pos.y, sprite._pos.x + sprite._surface.w, sprite._pos.y + sprite._surface.h); - rect.clip(_surface.w, _surface.h); + rect.clip(_screen.w, _screen.h); if (rect.isEmpty()) return; @@ -541,8 +541,8 @@ void ComposerEngine::redraw() { for (uint i = 0; i < _dirtyRects.size(); i++) { const Common::Rect &rect = _dirtyRects[i]; - byte *pixels = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; - _system->copyRectToScreen(pixels, _surface.pitch, rect.left, rect.top, rect.width(), rect.height()); + byte *pixels = (byte *)_screen.pixels + (rect.top * _screen.pitch) + rect.left; + _system->copyRectToScreen(pixels, _screen.pitch, rect.left, rect.top, rect.width(), rect.height()); } _system->updateScreen(); @@ -814,16 +814,16 @@ void ComposerEngine::drawSprite(const Sprite &sprite) { int y = sprite._pos.y; // incoming data is BMP-style (bottom-up), so flip it - byte *pixels = (byte *)_surface.pixels; + byte *pixels = (byte *)_screen.pixels; for (int j = 0; j < sprite._surface.h; j++) { if (j + y < 0) continue; - if (j + y >= _surface.h) + if (j + y >= _screen.h) break; byte *in = (byte *)sprite._surface.pixels + (sprite._surface.h - j - 1) * sprite._surface.w; - byte *out = pixels + ((j + y) * _surface.w) + x; + byte *out = pixels + ((j + y) * _screen.w) + x; for (int i = 0; i < sprite._surface.w; i++) - if ((x + i >= 0) && (x + i < _surface.w) && in[i]) + if ((x + i >= 0) && (x + i < _screen.w) && in[i]) out[i] = in[i]; } } diff --git a/engines/composer/resource.cpp b/engines/composer/resource.cpp index a4e292747c..83e49971fb 100644 --- a/engines/composer/resource.cpp +++ b/engines/composer/resource.cpp @@ -240,7 +240,7 @@ bool ComposerArchive::openStream(Common::SeekableReadStream *stream) { res.flags = flags; debug(4, "Id %d, offset %d, size %d, flags %08x", id, offset, size, flags); } - + stream->seek(oldPos); } diff --git a/engines/configure.engines b/engines/configure.engines index e0052c52ee..e17e2f67e0 100644 --- a/engines/configure.engines +++ b/engines/configure.engines @@ -1,9 +1,10 @@ # This file is included from the main "configure" script -add_engine scumm "SCUMM" yes "scumm_7_8 he" +# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] +add_engine scumm "SCUMM" yes "scumm_7_8 he" "v0-v6 games" add_engine scumm_7_8 "v7 & v8 games" yes add_engine he "HE71+ games" yes add_engine agi "AGI" yes -add_engine agos "AGOS" yes "agos2" +add_engine agos "AGOS" yes "agos2" "AGOS 1 games" add_engine agos2 "AGOS 2 games" yes add_engine cge "CGE" yes add_engine cine "Cinematique evo 1" yes @@ -13,38 +14,39 @@ add_engine draci "Dragon History" yes add_engine drascula "Drascula: The Vampire Strikes Back" yes add_engine dreamweb "Dreamweb" yes add_engine gob "Gobli*ns" yes -add_engine groovie "Groovie" yes "groovie2" +add_engine groovie "Groovie" yes "groovie2" "7th Guest" add_engine groovie2 "Groovie 2 games" no add_engine hopkins "Hopkins FBI" no add_engine hugo "Hugo Trilogy" yes -add_engine kyra "Legend of Kyrandia" yes "lol eob" +add_engine kyra "Kyra" yes "lol eob" "Legend of Kyrandia 1-3" add_engine lol "Lands of Lore" yes add_engine eob "Eye of the Beholder" no -add_engine lastexpress "The Last Express" no +add_engine lastexpress "The Last Express" no "" "" "16bit" add_engine lure "Lure of the Temptress" yes add_engine made "MADE" yes -add_engine mohawk "Mohawk" yes "cstime myst riven" +add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books" add_engine cstime "Where in Time is Carmen Sandiego?" no -add_engine riven "Riven: The Sequel to Myst" no -add_engine myst "Myst" no +add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit" +add_engine myst "Myst" no "" "" "16bit" add_engine parallaction "Parallaction" yes +add_engine pegasus "The Journeyman Project: Pegasus Prime" no "" "" "16bit" add_engine queen "Flight of the Amazon Queen" yes -add_engine saga "SAGA" yes "ihnm saga2" +add_engine saga "SAGA" yes "ihnm saga2" "ITE" add_engine ihnm "IHNM" yes add_engine saga2 "SAGA 2 games" no -add_engine sci "SCI" yes "sci32" +add_engine sci "SCI" yes "sci32" "SCI 0-1.1 games" add_engine sci32 "SCI32 games" no add_engine sky "Beneath a Steel Sky" yes add_engine sword1 "Broken Sword" yes add_engine sword2 "Broken Sword II" yes -add_engine sword25 "Broken Sword 2.5" no "" "png zlib" +add_engine sword25 "Broken Sword 2.5" no "" "" "png zlib 16bit" add_engine teenagent "Teen Agent" yes add_engine testbed "TestBed: the Testing framework" no add_engine tinsel "Tinsel" yes add_engine toltecs "3 Skulls of the Toltecs" no add_engine toon "Toonstruck" yes add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes -add_engine tony "Tony Tough and the Night of Roasted Moths" no +add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit" add_engine tsage "TsAGE" yes add_engine tucker "Bud Tucker in Double Trouble" yes -add_engine wintermute "Wintermute" no "" "png zlib vorbis" +add_engine wintermute "Wintermute" no "" "" "png zlib vorbis 16bit" diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp index cbe17ea4d3..ba79df4822 100644 --- a/engines/cruise/detection.cpp +++ b/engines/cruise/detection.cpp @@ -295,7 +295,7 @@ void CruiseMetaEngine::removeSaveState(const char *target, int slot) const { SaveStateDescriptor CruiseMetaEngine::querySaveMetaInfos(const char *target, int slot) const { Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading( Cruise::CruiseEngine::getSavegameFile(slot)); - + if (f) { Cruise::CruiseSavegameHeader header; Cruise::readSavegameHeader(f, header); diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp index cf3dfaa44b..c884075093 100644 --- a/engines/dialogs.cpp +++ b/engines/dialogs.cpp @@ -229,7 +229,7 @@ void MainMenuDialog::save() { "Please consult the README for basic information, and for " "instructions on how to obtain further assistance."), status.getDesc().c_str()); GUI::MessageDialog dialog(failMessage); - dialog.runModal(); + dialog.runModal(); } close(); diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp index 61705a1e59..2d78d05933 100644 --- a/engines/draci/detection.cpp +++ b/engines/draci/detection.cpp @@ -153,7 +153,7 @@ void DraciMetaEngine::removeSaveState(const char *target, int slot) const { SaveStateDescriptor DraciMetaEngine::querySaveMetaInfos(const char *target, int slot) const { Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading( Draci::DraciEngine::getSavegameFile(slot)); - + if (f) { Draci::DraciSavegameHeader header; Draci::readSavegameHeader(f, header); diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp index 6e38d49b94..760d8b7d98 100644 --- a/engines/drascula/detection.cpp +++ b/engines/drascula/detection.cpp @@ -284,7 +284,7 @@ public: Common::StringArray filenames = saveFileMan->listSavefiles(pattern); Common::Array<int> slots; for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { - + // Obtain the last 2 digits of the filename, since they correspond to the save slot int slotNum = atoi(file->c_str() + file->size() - 2); @@ -299,7 +299,7 @@ public: // Load save index Common::String fileEpa = Common::String::format("%s.epa", target); - Common::InSaveFile *epa = saveFileMan->openForLoading(fileEpa); + Common::InSaveFile *epa = saveFileMan->openForLoading(fileEpa); // Get savegame names from index Common::String saveDesc; @@ -307,19 +307,19 @@ public: int line = 1; for (size_t i = 0; i < slots.size(); i++) { // ignore lines corresponding to unused saveslots - for (; line < slots[i]; line++) - epa->readLine(); + for (; line < slots[i]; line++) + epa->readLine(); // copy the name in the line corresponding to the save slot and truncate to 22 characters saveDesc = Common::String(epa->readLine().c_str(), 22); // handle cases where the save directory and save index are detectably out of sync - if (saveDesc == "*") + if (saveDesc == "*") saveDesc = "No name specified."; // increment line number to keep it in sync with slot number - line++; - + line++; + // Insert savegame name into list saveList.push_back(SaveStateDescriptor(slots[i], saveDesc)); } diff --git a/engines/dreamweb/dreamweb.cpp b/engines/dreamweb/dreamweb.cpp index 0ca98d5a7b..5f5d627553 100644 --- a/engines/dreamweb/dreamweb.cpp +++ b/engines/dreamweb/dreamweb.cpp @@ -516,7 +516,7 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const { case Common::IT_ITA: switch(c) { case 133: - return 'Z' + 1; + return 'Z' + 1; case 130: return 'Z' + 2; case 138: @@ -548,10 +548,10 @@ uint8 DreamWebEngine::modifyChar(uint8 c) const { return c; } } - + Common::String DreamWebEngine::modifyFileName(const char *name) { Common::String fileName(name); - + // Sanity check if (!fileName.hasPrefix("DREAMWEB.")) return fileName; diff --git a/engines/dreamweb/dreamweb.h b/engines/dreamweb/dreamweb.h index 1f6deb8566..a4597b1867 100644 --- a/engines/dreamweb/dreamweb.h +++ b/engines/dreamweb/dreamweb.h @@ -196,7 +196,7 @@ protected: // from monitor.cpp char _inputLine[64]; - char _operand1[14]; + char _operand1[64]; char _currentFile[14]; // from newplace.cpp diff --git a/engines/dreamweb/monitor.cpp b/engines/dreamweb/monitor.cpp index 4e9d8eecc1..1886a80b6a 100644 --- a/engines/dreamweb/monitor.cpp +++ b/engines/dreamweb/monitor.cpp @@ -194,7 +194,7 @@ void DreamWebEngine::printLogo() { } void DreamWebEngine::input() { - memset(_inputLine, 0, 64); + memset(_inputLine, 0, sizeof(_inputLine)); _curPos = 0; printChar(_monitorCharset, _monAdX, _monAdY, '>', 0, NULL, NULL); multiDump(_monAdX, _monAdY, 6, 8); @@ -665,7 +665,7 @@ void DreamWebEngine::searchForFiles(const char *filesString) { const char *DreamWebEngine::parser() { char *output = _operand1; - memset(output, 0, 14); + memset(output, 0, sizeof(_operand1)); *output++ = '='; diff --git a/engines/dreamweb/object.cpp b/engines/dreamweb/object.cpp index b42591ef91..1e84aba6bd 100644 --- a/engines/dreamweb/object.cpp +++ b/engines/dreamweb/object.cpp @@ -516,7 +516,7 @@ void DreamWebEngine::inToInv() { if (_mouseButton == _oldButton || !(_mouseButton & 1)) return; // notletgo2 - + delPointer(); DynObject *object = getExAd(_itemFrame); object->mapad[0] = 4; @@ -1034,7 +1034,7 @@ void DreamWebEngine::fillOpen() { size = 4; findAllOpen(); for (uint8 i = 0; i < size; ++i) { - uint8 index = _openInvList[i]._index; + uint8 index = _openInvList[i]._index; uint8 type = _openInvList[i]._type; obToInv(index, type, kInventx + i * kItempicsize, kInventy + 96); } diff --git a/engines/dreamweb/people.cpp b/engines/dreamweb/people.cpp index 36a756a49b..dbb81406cd 100644 --- a/engines/dreamweb/people.cpp +++ b/engines/dreamweb/people.cpp @@ -411,7 +411,7 @@ void DreamWebEngine::interviewer(ReelRoutine &routine) { if (routine.reelPointer() != 250 && routine.reelPointer() != 259 && checkSpeed(routine)) routine.incReelPointer(); - + showGameReel(&routine); } @@ -745,7 +745,7 @@ void DreamWebEngine::introMonks2(ReelRoutine &routine) { if (nextReelPointer == 110) { _introCount++; monks2text(); - + if (_introCount == 35) nextReelPointer = 111; else @@ -895,7 +895,7 @@ void DreamWebEngine::helicopter(ReelRoutine &routine) { nextReelPointer = 9; } } - } + } routine.setReelPointer(nextReelPointer); } @@ -1002,7 +1002,7 @@ void DreamWebEngine::businessMan(ReelRoutine &routine) { nextReelPointer = 92; } } - + routine.setReelPointer(nextReelPointer); } @@ -1037,7 +1037,7 @@ void DreamWebEngine::endGameSeq(ReelRoutine &routine) { showGameReel(&routine); routine.mapY = _mapY; - + if (routine.reelPointer() == 145) { routine.setReelPointer(146); rollEndCreditsGameWon(); @@ -1070,7 +1070,7 @@ void DreamWebEngine::poolGuard(ReelRoutine &routine) { if (checkSpeed(routine)) { uint16 nextReelPointer = routine.reelPointer() + 1; - + if (nextReelPointer != 122) { // Not end guard 1 if (nextReelPointer == 147) { @@ -1103,12 +1103,12 @@ void DreamWebEngine::poolGuard(ReelRoutine &routine) { } } } - + routine.setReelPointer(nextReelPointer); } showGameReel(&routine); - + if (routine.reelPointer() != 121 && routine.reelPointer() != 146) { _pointerMode = 0; _vars._watchingTime = 2; diff --git a/engines/dreamweb/print.cpp b/engines/dreamweb/print.cpp index 3a2c45e07b..64b9849980 100644 --- a/engines/dreamweb/print.cpp +++ b/engines/dreamweb/print.cpp @@ -212,7 +212,7 @@ const char *DreamWebEngine::monPrint(const char *string) { while (!done) { uint16 count = getNumber(_monitorCharset, (const uint8 *)iterator, 166, false, &x); - do { + do { char c = *iterator++; if (c == ':') break; diff --git a/engines/dreamweb/sprite.cpp b/engines/dreamweb/sprite.cpp index 5b6cf6a6ac..01570c907a 100644 --- a/engines/dreamweb/sprite.cpp +++ b/engines/dreamweb/sprite.cpp @@ -52,7 +52,7 @@ void DreamWebEngine::printASprite(const Sprite *sprite) { } else { x = sprite->x + _mapAdX; } - + uint8 c; if (sprite->walkFrame != 0) c = 8; @@ -97,7 +97,7 @@ void DreamWebEngine::spriteUpdate() { else { backObject(&sprite); } - + if (_nowInNewRoom == 1) break; } @@ -373,7 +373,7 @@ void DreamWebEngine::lockedDoorway(Sprite *sprite, SetObject *objData) { if (sprite->animFrame != 0) --sprite->animFrame; - + _vars._throughDoor = 0; sprite->frameNumber = objData->index = objData->frames[sprite->animFrame]; @@ -407,7 +407,7 @@ void DreamWebEngine::liftSprite(Sprite *sprite, SetObject *objData) { } sprite->animFrame = 12; sprite->frameNumber = objData->index = objData->frames[sprite->animFrame]; - } + } else if (liftFlag == 3) { //openlift if (sprite->animFrame == 12) { _vars._liftFlag = 1; @@ -672,7 +672,7 @@ static const ReelSound g_roomSound6[] = { { 255,0 } }; static const ReelSound g_roomSound8[] = { - + { 12, 51 }, { 13, 53 }, { 14, 14 }, @@ -691,7 +691,7 @@ static const ReelSound g_roomSound10[] = { { 13, 16 }, { 255,0 } }; - + static const ReelSound g_roomSound11[] = { { 13, 20 }, { 255,0 } @@ -779,7 +779,7 @@ static const ReelSound g_roomSound26[] = { { 15, 102 }, // was 90, should be mine cart { 255,0 } }; - + static const ReelSound g_roomSound27[] = { { 22, 36 }, { 13, 125 }, diff --git a/engines/dreamweb/vgagrafx.cpp b/engines/dreamweb/vgagrafx.cpp index ec306c4924..d2390fb1fd 100644 --- a/engines/dreamweb/vgagrafx.cpp +++ b/engines/dreamweb/vgagrafx.cpp @@ -23,6 +23,7 @@ #include "dreamweb/dreamweb.h" #include "engines/util.h" #include "graphics/surface.h" +#include "graphics/decoders/pcx.h" namespace DreamWeb { @@ -152,70 +153,33 @@ void DreamWebEngine::setMode() { void DreamWebEngine::showPCX(const Common::String &suffix) { Common::String name = getDatafilePrefix() + suffix; Common::File pcxFile; - if (!pcxFile.open(name)) { warning("showpcx: Could not open '%s'", name.c_str()); return; } - uint8 *mainGamePal; - int i, j; + Graphics::PCXDecoder pcx; + if (!pcx.loadStream(pcxFile)) { + warning("showpcx: Could not process '%s'", name.c_str()); + return; + } // Read the 16-color palette into the 'maingamepal' buffer. Note that // the color components have to be adjusted from 8 to 6 bits. - - pcxFile.seek(16, SEEK_SET); - mainGamePal = _mainPal; - pcxFile.read(mainGamePal, 48); - - memset(mainGamePal + 48, 0xff, 720); - for (i = 0; i < 48; i++) { - mainGamePal[i] >>= 2; + memset(_mainPal, 0xff, 256 * 3); + memcpy(_mainPal, pcx.getPalette(), 48); + for (int i = 0; i < 48; i++) { + _mainPal[i] >>= 2; } - // Decode the image data. - Graphics::Surface *s = g_system->lockScreen(); - Common::Rect rect(640, 480); - - s->fillRect(rect, 0); - pcxFile.seek(128, SEEK_SET); - - for (int y = 0; y < 480; y++) { - byte *dst = (byte *)s->getBasePtr(0, y); - int decoded = 0; - - while (decoded < 320) { - byte col = pcxFile.readByte(); - byte len; - - if ((col & 0xc0) == 0xc0) { - len = col & 0x3f; - col = pcxFile.readByte(); - } else { - len = 1; - } - - // The image uses 16 colors and is stored as four bit - // planes, one for each bit of the color, least - // significant bit plane first. - - for (i = 0; i < len; i++) { - int plane = decoded / 80; - int pos = decoded % 80; - - for (j = 0; j < 8; j++) { - byte bit = (col >> (7 - j)) & 1; - dst[8 * pos + j] |= (bit << plane); - } - - decoded++; - } - } - } - + s->fillRect(Common::Rect(640, 480), 0); + const Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy((byte *)s->getBasePtr(0, y), pcxSurface->getBasePtr(0, y), pcxSurface->w); g_system->unlockScreen(); - pcxFile.close(); } void DreamWebEngine::frameOutV(uint8 *dst, const uint8 *src, uint16 pitch, uint16 width, uint16 height, int16 x, int16 y) { @@ -370,9 +334,9 @@ void DreamWebEngine::zoom() { for (size_t j = 0; j < 23; ++j) { uint8 v = src[j]; dst[2*j+0] = v; - dst[2*j+1] = v; + dst[2*j+1] = v; dst[2*j+320] = v; - dst[2*j+321] = v; + dst[2*j+321] = v; } src += 320; dst += 320*2; diff --git a/engines/engines.mk b/engines/engines.mk index 6022a05f8a..bcf97df991 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -135,6 +135,11 @@ DEFINES += -DENABLE_PARALLACTION=$(ENABLE_PARALLACTION) MODULES += engines/parallaction endif +ifdef ENABLE_PEGASUS +DEFINES += -DENABLE_PEGASUS=$(ENABLE_PEGASUS) +MODULES += engines/pegasus +endif + ifdef ENABLE_QUEEN DEFINES += -DENABLE_QUEEN=$(ENABLE_QUEEN) MODULES += engines/queen diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp index 1a3b313649..895686b5e0 100644 --- a/engines/groovie/detection.cpp +++ b/engines/groovie/detection.cpp @@ -142,7 +142,7 @@ static const GroovieGameDescription gameDescriptions[] = { { "11h", "Demo", AD_ENTRY1s("disk.1", "aacb32ce07e0df2894bd83a3dee40c12", 70), - Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, + Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOLAUNCHLOAD, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT) }, kGroovieV2, 1 diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp index 35d7ecf886..72a61fefb2 100644 --- a/engines/groovie/roq.cpp +++ b/engines/groovie/roq.cpp @@ -288,19 +288,18 @@ bool ROQPlayer::processBlockInfo(ROQBlockHeader &blockHeader) { // them it should be just fine. _currBuf->create(width, height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0)); _prevBuf->create(width, height, Graphics::PixelFormat(3, 0, 0, 0, 0, 0, 0, 0, 0)); + } - // Clear the buffers with black YUV values - byte *ptr1 = (byte *)_currBuf->getBasePtr(0, 0); - byte *ptr2 = (byte *)_prevBuf->getBasePtr(0, 0); - for (int i = 0; i < width * height; i++) { - *ptr1++ = 0; - *ptr1++ = 128; - *ptr1++ = 128; - *ptr2++ = 0; - *ptr2++ = 128; - *ptr2++ = 128; - } - + // Clear the buffers with black YUV values + byte *ptr1 = (byte *)_currBuf->getBasePtr(0, 0); + byte *ptr2 = (byte *)_prevBuf->getBasePtr(0, 0); + for (int i = 0; i < width * height; i++) { + *ptr1++ = 0; + *ptr1++ = 128; + *ptr1++ = 128; + *ptr2++ = 0; + *ptr2++ = 128; + *ptr2++ = 128; } return true; @@ -405,7 +404,7 @@ void ROQPlayer::processBlockQuadVectorBlock(int baseX, int baseY, int8 Mx, int8 } void ROQPlayer::processBlockQuadVectorBlockSub(int baseX, int baseY, int8 Mx, int8 My) { - debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block"); + debugC(6, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block"); uint16 codingType = getCodingType(); switch (codingType) { @@ -433,7 +432,7 @@ void ROQPlayer::processBlockQuadVectorBlockSub(int baseX, int baseY, int8 Mx, in bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) { debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing still (JPEG) block"); - warning("Groovie::ROQ: JPEG frame (unfinshed)"); + warning("Groovie::ROQ: JPEG frame (unfinished)"); Graphics::JPEGDecoder *jpg = new Graphics::JPEGDecoder(); jpg->loadStream(*_file); diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp index 15ee06c82a..1758f3f6a5 100644 --- a/engines/hugo/file.cpp +++ b/engines/hugo/file.cpp @@ -32,7 +32,11 @@ #include "common/savefile.h" #include "common/textconsole.h" #include "common/config-manager.h" + +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" #include "graphics/thumbnail.h" + #include "gui/saveload.h" #include "hugo/hugo.h" @@ -88,66 +92,33 @@ const char *FileManager::getUifFilename() const { } /** - * Convert 4 planes (RGBI) data to 8-bit DIB format - * Return original plane data ptr - */ -byte *FileManager::convertPCC(byte *p, const uint16 y, const uint16 bpl, ImagePtr dataPtr) const { - debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, ImagePtr dataPtr)", y, bpl); - - dataPtr += y * bpl * 8; // Point to correct DIB line - for (int16 r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) { // Each byte in all planes - for (int8 bit = 7; bit >= 0; bit--) { // Each bit in byte - *dataPtr++ = (((p[r] >> bit & 1) << 0) | - ((p[g] >> bit & 1) << 1) | - ((p[b] >> bit & 1) << 2) | - ((p[i] >> bit & 1) << 3)); - } - } - return p; -} - -/** * Read a pcx file of length len. Use supplied seqPtr and image_p or * allocate space if NULL. Name used for errors. Returns address of seqPtr * Set first TRUE to initialize b_index (i.e. not reading a sequential image in file). */ -Seq *FileManager::readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name) { +Seq *FileManager::readPCX(Common::SeekableReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name) { debugC(1, kDebugFile, "readPCX(..., %s)", name); - // Read in the PCC header and check consistency - _PCCHeader._mfctr = f.readByte(); - _PCCHeader._vers = f.readByte(); - _PCCHeader._enc = f.readByte(); - _PCCHeader._bpx = f.readByte(); - _PCCHeader._x1 = f.readUint16LE(); - _PCCHeader._y1 = f.readUint16LE(); - _PCCHeader._x2 = f.readUint16LE(); - _PCCHeader._y2 = f.readUint16LE(); - _PCCHeader._xres = f.readUint16LE(); - _PCCHeader._yres = f.readUint16LE(); - f.read(_PCCHeader._palette, sizeof(_PCCHeader._palette)); - _PCCHeader._vmode = f.readByte(); - _PCCHeader._planes = f.readByte(); - _PCCHeader._bytesPerLine = f.readUint16LE(); - f.read(_PCCHeader._fill2, sizeof(_PCCHeader._fill2)); - - if (_PCCHeader._mfctr != 10) - error("Bad data file format: %s", name); - // Allocate memory for Seq if 0 if (seqPtr == 0) { if ((seqPtr = (Seq *)malloc(sizeof(Seq))) == 0) error("Insufficient memory to run game."); } + Graphics::PCXDecoder pcx; + if (!pcx.loadStream(f)) + error("Error while reading PCX image"); + + const Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + // Find size of image data in 8-bit DIB format // Note save of x2 - marks end of valid data before garbage - uint16 bytesPerLine4 = _PCCHeader._bytesPerLine * 4; // 4-bit bpl - seqPtr->_bytesPerLine8 = bytesPerLine4 * 2; // 8-bit bpl - seqPtr->_lines = _PCCHeader._y2 - _PCCHeader._y1 + 1; - seqPtr->_x2 = _PCCHeader._x2 - _PCCHeader._x1 + 1; + seqPtr->_lines = pcxSurface->h; + seqPtr->_x2 = seqPtr->_bytesPerLine8 = pcxSurface->w; // Size of the image - uint16 size = seqPtr->_lines * seqPtr->_bytesPerLine8; + uint16 size = pcxSurface->w * pcxSurface->h; // Allocate memory for image data if NULL if (imagePtr == 0) @@ -156,26 +127,9 @@ Seq *FileManager::readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, co assert(imagePtr); seqPtr->_imagePtr = imagePtr; + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(imagePtr + y * pcxSurface->w, pcxSurface->getBasePtr(0, y), pcxSurface->w); - // Process the image data, converting to 8-bit DIB format - uint16 y = 0; // Current line index - byte pline[kXPix]; // Hold 4 planes of data - byte *p = pline; // Ptr to above - while (y < seqPtr->_lines) { - byte c = f.readByte(); - if ((c & kRepeatMask) == kRepeatMask) { - byte d = f.readByte(); // Read data byte - for (int i = 0; i < (c & kLengthMask); i++) { - *p++ = d; - if ((uint16)(p - pline) == bytesPerLine4) - p = convertPCC(pline, y++, _PCCHeader._bytesPerLine, imagePtr); - } - } else { - *p++ = c; - if ((uint16)(p - pline) == bytesPerLine4) - p = convertPCC(pline, y++, _PCCHeader._bytesPerLine, imagePtr); - } - } return seqPtr; } diff --git a/engines/hugo/file.h b/engines/hugo/file.h index 1438bd2054..44f257a2af 100644 --- a/engines/hugo/file.h +++ b/engines/hugo/file.h @@ -112,16 +112,13 @@ protected: Common::File _sceneryArchive1; // Handle for scenery file Common::File _objectsArchive; // Handle for objects file - PCCHeader _PCCHeader; - - Seq *readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name); + Seq *readPCX(Common::SeekableReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name); // If this is the first call, read the lookup table bool _hasReadHeader; SoundHdr _soundHdr[kMaxSounds]; // Sound lookup table private: - byte *convertPCC(byte *p, const uint16 y, const uint16 bpl, ImagePtr dataPtr) const; UifHdr *getUIFHeader(const Uif id); }; diff --git a/engines/lure/decode.cpp b/engines/lure/decode.cpp index 1338559534..484126c43f 100644 --- a/engines/lure/decode.cpp +++ b/engines/lure/decode.cpp @@ -255,7 +255,7 @@ MemoryBlock *PictureDecoder::vgaDecode(MemoryBlock *src, uint32 maxOutputSize) { decrCtr(); if (shlCarry()) break; - + AL = dataIn->data()[BP + 3]; } else { decrCtr(); @@ -375,7 +375,7 @@ uint32 AnimationDecoder::decode_data(MemoryBlock *src, MemoryBlock *dest, uint32 // Main loop bool loopFlag = true; while (loopFlag) { - for (;;) { + for (;;) { carry = false; rcl(currData, carry); if (--bitCtr == 0) { diff --git a/engines/made/screenfx.cpp b/engines/made/screenfx.cpp index de9231a158..ad71f1fb49 100644 --- a/engines/made/screenfx.cpp +++ b/engines/made/screenfx.cpp @@ -200,10 +200,10 @@ void ScreenEffects::stepBlendedPalette() { setBlendedPalette(_blendedPaletteStatus._palette, _blendedPaletteStatus._newPalette, _blendedPaletteStatus._colorCount, _blendedPaletteStatus._value, _blendedPaletteStatus._maxValue); if (_blendedPaletteStatus._value == _blendedPaletteStatus._maxValue) - _blendedPaletteStatus._value++; - else - _blendedPaletteStatus._value = MIN<int16>(_blendedPaletteStatus._value + _blendedPaletteStatus._incr, _blendedPaletteStatus._maxValue); - } + _blendedPaletteStatus._value++; + else + _blendedPaletteStatus._value = MIN<int16>(_blendedPaletteStatus._value + _blendedPaletteStatus._incr, _blendedPaletteStatus._maxValue); + } } void ScreenEffects::copyFxRect(Graphics::Surface *surface, int16 x1, int16 y1, int16 x2, int16 y2) { diff --git a/engines/mohawk/bitmap.cpp b/engines/mohawk/bitmap.cpp index 952b6daec2..bc19fe2d3e 100644 --- a/engines/mohawk/bitmap.cpp +++ b/engines/mohawk/bitmap.cpp @@ -630,7 +630,7 @@ void MohawkBitmap::drawRLE8(Graphics::Surface *surface, bool isLE) { // Myst Bitmap Decoder ////////////////////////////////////////// -MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream* stream) { +MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream *stream) { uint32 uncompressedSize = stream->readUint32LE(); Common::SeekableReadStream *bmpStream = decompressLZ(stream, uncompressedSize); delete stream; @@ -652,10 +652,10 @@ MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream* stream) { } // Copy the palette to one of our own - const byte *palette = bitmapDecoder.getPalette(); byte *newPal = 0; - if (palette) { + if (bitmapDecoder.hasPalette()) { + const byte *palette = bitmapDecoder.getPalette(); newPal = (byte *)malloc(256 * 3); memcpy(newPal, palette, 256 * 3); } diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index 0ed4f38b53..b1b99722d5 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -478,7 +478,7 @@ VideoHandle VideoManager::findVideoHandle(const Common::String &filename) { return NULL_VID_HANDLE; } -int32 VideoManager::getCurFrame(VideoHandle handle) { +int VideoManager::getCurFrame(VideoHandle handle) { assert(handle != NULL_VID_HANDLE); return _videoStreams[handle]->getCurFrame(); } diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index 9dddcde09b..6d2783936d 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -98,7 +98,7 @@ public: // Handle functions VideoHandle findVideoHandle(uint16 id); VideoHandle findVideoHandle(const Common::String &filename); - int32 getCurFrame(VideoHandle handle); + int getCurFrame(VideoHandle handle); uint32 getFrameCount(VideoHandle handle); uint32 getTime(VideoHandle handle); uint32 getDuration(VideoHandle videoHandle); diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp new file mode 100644 index 0000000000..134e5cfbf3 --- /dev/null +++ b/engines/parallaction/adlib.cpp @@ -0,0 +1,808 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/system.h" + +#include "audio/fmopl.h" +#include "audio/mpu401.h" +#include "audio/softsynth/emumidi.h" + +namespace Parallaction { + +const uint kNumVoices = 9; +// adlib FM voices 0-5 +const uint kNumMelodic = 6; +// adlib FM voice 6 and 7-8 +const uint kNumPercussion = 5; + +// mask for maximum volume level +#define LEVEL_MASK 0x7f + +struct OPLOperator { + uint8 characteristic; // amplitude modulation, vibrato, envelope, keyboard scaling, modulator frequency + uint8 levels; + uint8 attackDecay; + uint8 sustainRelease; + uint8 waveform; +}; + +struct MelodicProgram { + OPLOperator op[2]; + uint8 feedbackAlgo; +}; + +struct PercussionNote { + OPLOperator op[2]; + uint8 feedbackAlgo; + uint8 percussion; + uint8 valid; + uint16 frequency; + uint8 octave; +}; + +static const MelodicProgram melodicPrograms[128] = { + {{{ 0x1, 0x51, 0xf2, 0xb2, 0x0 }, { 0x11, 0x0, 0xf2, 0xa2, 0x0 }}, 0x0 }, + {{{ 0xc2, 0x4b, 0xf1, 0x53, 0x0 }, { 0xd2, 0x0, 0xf2, 0x74, 0x0 }}, 0x4 }, + {{{ 0x81, 0x9d, 0xf2, 0x74, 0x0 }, { 0x13, 0x0, 0xf2, 0xf1, 0x0 }}, 0x6 }, + {{{ 0x3, 0x4f, 0xf1, 0x53, 0x0 }, { 0x17, 0x3, 0xf2, 0x74, 0x0 }}, 0x6 }, + {{{ 0xd1, 0x81, 0x81, 0x73, 0x2 }, { 0xd4, 0x0, 0xe1, 0x34, 0x0 }}, 0x3 }, + {{{ 0x1, 0x0, 0x94, 0xa6, 0x0 }, { 0x2, 0x0, 0x83, 0x26, 0x0 }}, 0x1 }, + {{{ 0xf3, 0x84, 0x81, 0x2, 0x1 }, { 0x55, 0x80, 0xdd, 0x3, 0x0 }}, 0x4 }, + {{{ 0x5, 0x8a, 0xf2, 0x26, 0x0 }, { 0x1, 0x80, 0xf3, 0x48, 0x0 }}, 0x0 }, + {{{ 0x32, 0x0, 0xb1, 0x14, 0x0 }, { 0x12, 0x0, 0xfd, 0x36, 0x0 }}, 0x3 }, + {{{ 0x1, 0x0, 0x82, 0xa, 0x2 }, { 0x2, 0x0, 0x85, 0x15, 0x0 }}, 0x3 }, + {{{ 0xd1, 0x1, 0x97, 0xaa, 0x0 }, { 0x4, 0xd, 0xf3, 0xa5, 0x1 }}, 0x9 }, + {{{ 0x17, 0x0, 0xf2, 0x62, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 }, + {{{ 0x6, 0x0, 0xff, 0xf4, 0x0 }, { 0xc4, 0x0, 0xf8, 0xb5, 0x0 }}, 0xe }, + {{{ 0xc0, 0x81, 0xf2, 0x13, 0x2 }, { 0xc0, 0xc1, 0xf3, 0x14, 0x2 }}, 0xb }, + {{{ 0x44, 0x53, 0xf5, 0x31, 0x0 }, { 0x60, 0x80, 0xfd, 0x22, 0x0 }}, 0x6 }, + {{{ 0xe0, 0x80, 0xf4, 0xf2, 0x0 }, { 0x61, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 }, + {{{ 0xc1, 0x6, 0x83, 0x23, 0x0 }, { 0xc1, 0x4, 0xf0, 0x26, 0x0 }}, 0x1 }, + {{{ 0x26, 0x0, 0xf4, 0xb6, 0x0 }, { 0x21, 0x0, 0x81, 0x4b, 0x0 }}, 0x1 }, + {{{ 0x24, 0x80, 0xff, 0xf, 0x0 }, { 0x21, 0x80, 0xff, 0xf, 0x0 }}, 0x1 }, + {{{ 0x24, 0x4f, 0xf2, 0xb, 0x0 }, { 0x31, 0x0, 0x52, 0xb, 0x0 }}, 0xb }, + {{{ 0x31, 0x8, 0x81, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0x0 }, + {{{ 0x70, 0xc5, 0x52, 0x11, 0x1 }, { 0x71, 0x80, 0x31, 0xfe, 0x1 }}, 0x0 }, + {{{ 0x51, 0x88, 0x10, 0xf0, 0x0 }, { 0x42, 0x83, 0x40, 0xfc, 0x0 }}, 0x8 }, + {{{ 0xf0, 0xd9, 0x81, 0x3, 0x0 }, { 0xb1, 0x80, 0xf1, 0x5, 0x0 }}, 0xa }, + {{{ 0x21, 0x4f, 0xf1, 0x31, 0x0 }, { 0x2, 0x80, 0xc3, 0x45, 0x0 }}, 0x0 }, + {{{ 0x7, 0x8f, 0x9c, 0x33, 0x1 }, { 0x1, 0x80, 0x8a, 0x13, 0x0 }}, 0x0 }, + {{{ 0x21, 0x40, 0xf1, 0x31, 0x0 }, { 0x6, 0x80, 0xf4, 0x44, 0x0 }}, 0x0 }, + {{{ 0x21, 0x40, 0xf1, 0x31, 0x3 }, { 0x81, 0x0, 0xf4, 0x44, 0x2 }}, 0x2 }, + {{{ 0x11, 0x8d, 0xfd, 0x11, 0x0 }, { 0x11, 0x80, 0xfd, 0x11, 0x0 }}, 0x8 }, + {{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xf1, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xcd, 0x9e, 0x55, 0xd1, 0x0 }, { 0xd1, 0x0, 0xf2, 0x71, 0x0 }}, 0xe }, + {{{ 0x1, 0x0, 0xf2, 0x88, 0x0 }, { 0x1, 0x0, 0xf5, 0x88, 0x0 }}, 0x1 }, + {{{ 0x30, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0x6 }, + {{{ 0x0, 0x10, 0xf4, 0xd9, 0x0 }, { 0x0, 0x0, 0xf5, 0xd7, 0x0 }}, 0x4 }, + {{{ 0x1, 0x4c, 0xf2, 0x50, 0x0 }, { 0x1, 0x40, 0xd2, 0x59, 0x0 }}, 0x8 }, + {{{ 0x20, 0x11, 0xe2, 0x8a, 0x0 }, { 0x20, 0x0, 0xe4, 0xa8, 0x0 }}, 0xa }, + {{{ 0x21, 0x40, 0x7b, 0x4, 0x1 }, { 0x21, 0x0, 0x75, 0x72, 0x0 }}, 0x2 }, + {{{ 0x31, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0xa }, + {{{ 0x1, 0xc, 0xf5, 0x2f, 0x1 }, { 0x0, 0x80, 0xf5, 0x5c, 0x0 }}, 0x0 }, + {{{ 0xb0, 0x1c, 0x81, 0x3, 0x2 }, { 0x20, 0x0, 0x54, 0x67, 0x2 }}, 0xe }, + {{{ 0x1, 0x0, 0xf1, 0x65, 0x0 }, { 0x1, 0x80, 0xa3, 0xa8, 0x2 }}, 0x1 }, + {{{ 0xe1, 0x4f, 0xc1, 0xd3, 0x2 }, { 0x21, 0x0, 0x32, 0x74, 0x1 }}, 0x0 }, + {{{ 0x2, 0x0, 0xf6, 0x16, 0x0 }, { 0x12, 0x0, 0xf2, 0xf8, 0x0 }}, 0x1 }, + {{{ 0xe0, 0x63, 0xf8, 0xf3, 0x0 }, { 0x70, 0x80, 0xf7, 0xf3, 0x0 }}, 0x4 }, + {{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 }, + {{{ 0x21, 0x16, 0xb0, 0x81, 0x1 }, { 0x22, 0x0, 0xb3, 0x13, 0x1 }}, 0xc }, + {{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x30, 0x0, 0x90, 0xf, 0x0 }}, 0x6 }, + {{{ 0x0, 0x10, 0xf1, 0xf2, 0x2 }, { 0x1, 0x0, 0xf1, 0xf2, 0x3 }}, 0x0 }, + {{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 }, + {{{ 0xb1, 0x3, 0x55, 0x3, 0x0 }, { 0xb1, 0x3, 0x8, 0xa, 0x0 }}, 0x9 }, + {{{ 0x22, 0x0, 0xa9, 0x34, 0x1 }, { 0x1, 0x0, 0xa2, 0x42, 0x2 }}, 0x2 }, + {{{ 0xa0, 0xdc, 0x81, 0x31, 0x3 }, { 0xb1, 0x80, 0xf1, 0x1, 0x3 }}, 0x0 }, + {{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 }, + {{{ 0xf1, 0x80, 0xa0, 0x72, 0x0 }, { 0x74, 0x0, 0x90, 0x22, 0x0 }}, 0x9 }, + {{{ 0xe1, 0x13, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0xf0, 0xfc, 0x1 }}, 0xa }, + {{{ 0x31, 0x1c, 0x41, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x21, 0x1c, 0x53, 0x1d, 0x0 }, { 0xa1, 0x80, 0x52, 0x3b, 0x0 }}, 0xc }, + {{{ 0x21, 0x1d, 0xa4, 0xae, 0x1 }, { 0x21, 0x0, 0xb1, 0x9e, 0x0 }}, 0xc }, + {{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa }, + {{{ 0xe1, 0x15, 0x71, 0xae, 0x0 }, { 0xe2, 0x0, 0x81, 0x9e, 0x0 }}, 0xe }, + {{{ 0x21, 0x16, 0x71, 0xae, 0x0 }, { 0x21, 0x0, 0x81, 0x9e, 0x0 }}, 0xe }, + {{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x21, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0x22, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0x23, 0x4f, 0x81, 0x53, 0x0 }, { 0x34, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa }, + {{{ 0x71, 0xc5, 0x6e, 0x17, 0x0 }, { 0x22, 0x5, 0x8b, 0xe, 0x0 }}, 0x2 }, + {{{ 0xe6, 0x27, 0x70, 0xf, 0x1 }, { 0xe3, 0x0, 0x60, 0x9f, 0x0 }}, 0xa }, + {{{ 0x30, 0xc8, 0xd5, 0x19, 0x0 }, { 0xb1, 0x80, 0x61, 0x1b, 0x0 }}, 0xc }, + {{{ 0x32, 0x9a, 0x51, 0x1b, 0x0 }, { 0xa1, 0x82, 0xa2, 0x3b, 0x0 }}, 0xc }, + {{{ 0xad, 0x3, 0x74, 0x29, 0x0 }, { 0xa2, 0x82, 0x73, 0x29, 0x0 }}, 0x7 }, + {{{ 0x21, 0x83, 0x74, 0x17, 0x0 }, { 0x62, 0x8d, 0x65, 0x17, 0x0 }}, 0x7 }, + {{{ 0x94, 0xb, 0x85, 0xff, 0x1 }, { 0x13, 0x0, 0x74, 0xff, 0x0 }}, 0xc }, + {{{ 0x74, 0x87, 0xa4, 0x2, 0x0 }, { 0xd6, 0x80, 0x45, 0x42, 0x0 }}, 0x2 }, + {{{ 0xb3, 0x85, 0x76, 0x21, 0x1 }, { 0x20, 0x0, 0x3d, 0xc1, 0x0 }}, 0x6 }, + {{{ 0x17, 0x4f, 0xf2, 0x61, 0x0 }, { 0x12, 0x8, 0xf1, 0xb4, 0x0 }}, 0x8 }, + {{{ 0x4f, 0x86, 0x65, 0x1, 0x0 }, { 0x1f, 0x0, 0x32, 0x74, 0x0 }}, 0x4 }, + {{{ 0xe1, 0x23, 0x71, 0xae, 0x0 }, { 0xe4, 0x0, 0x82, 0x9e, 0x0 }}, 0xa }, + {{{ 0x11, 0x86, 0xf2, 0xbd, 0x0 }, { 0x4, 0x80, 0xa0, 0x9b, 0x1 }}, 0x8 }, + {{{ 0x20, 0x90, 0xf5, 0x9e, 0x2 }, { 0x11, 0x0, 0xf4, 0x5b, 0x3 }}, 0xc }, + {{{ 0xf0, 0x80, 0x34, 0xe4, 0x0 }, { 0x7e, 0x0, 0xa2, 0x6, 0x0 }}, 0x8 }, + {{{ 0x90, 0xf, 0xff, 0x1, 0x3 }, { 0x0, 0x0, 0x1f, 0x1, 0x0 }}, 0xe }, + {{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x33, 0x0, 0x90, 0xf, 0x0 }}, 0x6 }, + {{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0x93, 0x6, 0xc1, 0x4, 0x1 }, { 0x82, 0x0, 0x51, 0x9, 0x0 }}, 0x6 }, + {{{ 0xa0, 0x0, 0x96, 0x33, 0x0 }, { 0x20, 0x0, 0x55, 0x2b, 0x0 }}, 0x6 }, + {{{ 0x0, 0xc0, 0xff, 0x5, 0x0 }, { 0x0, 0x0, 0xff, 0x5, 0x3 }}, 0x0 }, + {{{ 0x4, 0x8, 0xf8, 0x7, 0x0 }, { 0x1, 0x0, 0x82, 0x74, 0x0 }}, 0x8 }, + {{{ 0x0, 0x0, 0x2f, 0x5, 0x0 }, { 0x20, 0x0, 0xff, 0x5, 0x3 }}, 0xa }, + {{{ 0x93, 0x0, 0xf7, 0x7, 0x2 }, { 0x0, 0x0, 0xf7, 0x7, 0x0 }}, 0xa }, + {{{ 0x0, 0x40, 0x80, 0x7a, 0x0 }, { 0xc4, 0x0, 0xc0, 0x7e, 0x0 }}, 0x8 }, + {{{ 0x90, 0x80, 0x55, 0xf5, 0x0 }, { 0x0, 0x0, 0x55, 0xf5, 0x0 }}, 0x8 }, + {{{ 0xe1, 0x80, 0x34, 0xe4, 0x0 }, { 0x69, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 }, + {{{ 0x3, 0x2, 0xf0, 0xff, 0x3 }, { 0x11, 0x80, 0xf0, 0xff, 0x2 }}, 0x2 }, + {{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 }, + {{{ 0x0, 0x0, 0x2f, 0x1, 0x0 }, { 0x0, 0x0, 0xff, 0x1, 0x0 }}, 0x4 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0x93, 0x85, 0x3f, 0x6, 0x1 }, { 0x0, 0x0, 0x5f, 0x7, 0x0 }}, 0x6 }, + {{{ 0x6, 0x0, 0xa0, 0xf0, 0x0 }, { 0x44, 0x0, 0xc5, 0x75, 0x0 }}, 0xe }, + {{{ 0x60, 0x0, 0x10, 0x81, 0x0 }, { 0x20, 0x8c, 0x12, 0x91, 0x0 }}, 0xe }, + {{{ 0x1, 0x40, 0xf1, 0x53, 0x0 }, { 0x8, 0x40, 0xf1, 0x53, 0x0 }}, 0x0 }, + {{{ 0x31, 0x0, 0x56, 0x31, 0x0 }, { 0x16, 0x0, 0x7d, 0x41, 0x0 }}, 0x0 }, + {{{ 0x0, 0x10, 0xf2, 0x72, 0x0 }, { 0x13, 0x0, 0xf2, 0x72, 0x0 }}, 0xc }, + {{{ 0x10, 0x0, 0x75, 0x93, 0x1 }, { 0x1, 0x0, 0xf5, 0x82, 0x1 }}, 0x0 }, + {{{ 0x0, 0x0, 0xf6, 0xff, 0x2 }, { 0x0, 0x0, 0xf6, 0xff, 0x0 }}, 0x8 }, + {{{ 0x30, 0x0, 0xff, 0xa0, 0x3 }, { 0x63, 0x0, 0x65, 0xb, 0x2 }}, 0x0 }, + {{{ 0x2a, 0x0, 0xf6, 0x87, 0x0 }, { 0x2b, 0x0, 0x76, 0x25, 0x0 }}, 0x0 }, + {{{ 0x85, 0x0, 0xb8, 0x84, 0x0 }, { 0x43, 0x0, 0xe5, 0x8f, 0x0 }}, 0x6 }, + {{{ 0x7, 0x4f, 0xf2, 0x60, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 }, + {{{ 0x5, 0x40, 0xb3, 0xd3, 0x0 }, { 0x86, 0x80, 0xf2, 0x24, 0x0 }}, 0x2 }, + {{{ 0xd0, 0x0, 0x11, 0xcf, 0x0 }, { 0xd1, 0x0, 0xf4, 0xe8, 0x3 }}, 0x0 }, + {{{ 0x5, 0x4e, 0xda, 0x25, 0x2 }, { 0x1, 0x0, 0xf9, 0x15, 0x0 }}, 0xa }, + {{{ 0x3, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 }, + {{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xf9, 0x5, 0x0 }}, 0x0 }, + {{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xf1, 0x41, 0x11, 0x11, 0x0 }, { 0xf1, 0x41, 0x11, 0x11, 0x0 }}, 0x2 }, + {{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 }, + {{{ 0x1, 0x0, 0x2f, 0x1, 0x0 }, { 0x1, 0x0, 0xaf, 0x1, 0x3 }}, 0xf }, + {{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 }, + {{{ 0xc0, 0x4f, 0xf1, 0x3, 0x0 }, { 0xbe, 0xc, 0x10, 0x1, 0x0 }}, 0x2 }, + {{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 }, + {{{ 0x81, 0x47, 0xf1, 0x83, 0x0 }, { 0xa2, 0x4, 0x91, 0x86, 0x0 }}, 0x6 }, + {{{ 0xf0, 0xc0, 0xff, 0xff, 0x3 }, { 0xe5, 0x0, 0xfb, 0xf0, 0x0 }}, 0xe }, + {{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 } +}; + +static const PercussionNote percussionNotes[47] = { + {{{ 0x0, 0xb, 0xa8, 0x38, 0x0 }, { 0x0, 0x0, 0xd6, 0x49, 0x0 }}, 0x0, 0x4, 0x1, 0x97, 0x4 }, + {{{ 0xc0, 0xc0, 0xf8, 0x3f, 0x2 }, { 0xc0, 0x0, 0xf6, 0x8e, 0x0 }}, 0x0, 0x4, 0x1, 0xf7, 0x4 }, + {{{ 0xc0, 0x80, 0xc9, 0xab, 0x0 }, { 0xeb, 0x40, 0xb5, 0xf6, 0x0 }}, 0x1, 0x3, 0x1, 0x6a, 0x6 }, + {{{ 0xc, 0x0, 0xd8, 0xa6, 0x0 }, { 0x0, 0x0, 0xd6, 0x4f, 0x0 }}, 0x1, 0x3, 0x1, 0x6c, 0x5 }, + {{{ 0x1, 0x0, 0xe2, 0xd2, 0x0 }, { 0x3, 0x41, 0x8f, 0x48, 0x49 }}, 0xc, 0x4, 0x1, 0x2f, 0x5 }, + {{{ 0x0, 0x0, 0xc8, 0x58, 0x3 }, { 0x0, 0x0, 0xf6, 0x4f, 0x0 }}, 0x9, 0x3, 0x1, 0x108, 0x4 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf2, 0xff, 0xe0, 0x50, 0x52 }}, 0x5d, 0x2, 0x1, 0x9f, 0x5 }, + {{{ 0xe, 0x9, 0xb9, 0x47, 0x0 }, { 0xeb, 0x40, 0xf5, 0xe6, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 }, + {{{ 0x0, 0x0, 0xd6, 0x83, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xc7, 0x5 }, + {{{ 0x1, 0x9, 0x89, 0x67, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x0, 0x1, 0x80, 0x6 }, + {{{ 0x1, 0x0, 0xd6, 0x96, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xed, 0x5 }, + {{{ 0x0, 0x9, 0xa9, 0x55, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 }, + {{{ 0x2, 0x0, 0xc6, 0x96, 0x0 }, { 0xe0, 0x0, 0xe0, 0x40, 0x0 }}, 0x1, 0x2, 0x1, 0x123, 0x5 }, + {{{ 0x5, 0x0, 0xf6, 0x56, 0x0 }, { 0xf7, 0xff, 0xb3, 0x90, 0x4f }}, 0x1, 0x2, 0x1, 0x15b, 0x5 }, + {{{ 0x1, 0x0, 0xf7, 0x14, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x1ac, 0x5 }, + {{{ 0x0, 0x0, 0xf6, 0x56, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x1, 0x2, 0x1, 0x18b, 0x5 }, + {{{ 0x0, 0x83, 0xfb, 0x5, 0x0 }, { 0xf7, 0x41, 0x39, 0x90, 0x79 }}, 0x1, 0x1, 0x1, 0xc8, 0x5 }, + {{{ 0x0, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0xf9, 0x5 }, + {{{ 0x1, 0x0, 0xa0, 0x5, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x27a, 0x6 }, + {{{ 0x0, 0x5, 0xf3, 0x6, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x108, 0x7 }, + {{{ 0x1, 0x0, 0xf9, 0x34, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x147, 0x4 }, + {{{ 0x0, 0x0, 0xf7, 0x16, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x120, 0x6 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x42, 0x6 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x6d, 0x5 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 } +}; + +const uint16 melodicFrequencies[36] = { + 0x55, 0x5a, 0x60, 0x66, 0x6c, 0x72, 0x79, 0x80, 0x88, + 0x90, 0x99, 0xa1, 0xab, 0xb5, 0xc0, 0xcc, 0xd8, 0xe5, + 0xf2, 0x101, 0x110, 0x120, 0x132, 0x143, 0x156, 0x16b, 0x181, + 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x286 +}; + +class AdLibDriver; + +class AdLibChannel : public MidiChannel_MPU401 { +public: + void reset(); + + uint8 _program; + uint8 _volume; + uint8 _pedal; +}; + +struct MelodicVoice { + bool _used; + uint8 _channel; + uint8 _program; + + uint8 _key; + uint32 _timestamp; + uint16 _frequency; + int8 _octave; +}; + +class AdLibDriver : public MidiDriver_Emulated { +public: + AdLibDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { + for (uint i = 0; i < 16; ++i) + _channels[i].init(this, i); + } + + int open(); + void close(); + void send(uint32 b); + MidiChannel *allocateChannel(); + MidiChannel *getPercussionChannel() { return &_channels[9]; } + + bool isStereo() const { return false; } + int getRate() const { return _mixer->getOutputRate(); } + + void generateSamples(int16 *buf, int len); + +protected: + OPL::OPL *_opl; + AdLibChannel _channels[16]; + MelodicVoice _voices[kNumMelodic]; + uint8 _notesPerPercussion[kNumPercussion]; + + uint _lastVoice; + + uint8 _percussionMask; + + void noteOff(uint8 channel, uint8 note); + void noteOn(uint8 channel, uint8 note, uint8 velocity); + void allNotesOff(); + void setModulationWheel(uint8 channel, uint8 value); + void setFootController(uint8 channel, uint8 value); + void setVolume(uint8 channel, uint8 value); + void setPitchBend(uint8 channel, int16 value); + + void playNote(uint8 voice, uint8 octave, uint16 frequency); + + void programOperatorSimple(uint8 offset, const OPLOperator &op); + void programOperator(uint8 offset, const OPLOperator &op); + void setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume); + + void setupPercussion(const PercussionNote ¬e); + void playPercussion(uint8 channel, const PercussionNote ¬e, uint8 velocity); + + void programMelodicVoice(uint8 voice, uint8 program); + void playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity); + void muteMelodicVoice(uint8 voice); + + void initVoices(); +}; + +MidiDriver *createAdLibDriver() { + return new AdLibDriver(g_system->getMixer()); +} + +void AdLibChannel::reset() { + _program = 0; + _volume = 127; + _pedal = 0; +} + +/* + bit 7 - Clear: AM depth is 1 dB + bit 6 - Clear: Vibrato depth is 7 cent + bit 5 - Set: Rhythm enabled (6 melodic voices) + bit 4 - Bass drum off + bit 3 - Snare drum off + bit 2 - Tom tom off + bit 1 - Cymbal off + bit 0 - Hi Hat off +*/ +const uint8 kDefaultPercussionMask = 0x20; + +int AdLibDriver::open() { + if (_isOpen) + return MERR_ALREADY_OPEN; + + MidiDriver_Emulated::open(); + + _opl = OPL::Config::create(); + _opl->init(getRate()); + _opl->writeReg(0x1, 0x20); // set bit 5 (enable all waveforms) + + // Reset the OPL registers. + for (uint i = 0; i < kNumVoices; ++i) { + _opl->writeReg(0xA0 + i, 0); // frequency + _opl->writeReg(0xB0 + i, 0); // key on + _opl->writeReg(0xC0 + i, 0); // feedback + } + _opl->writeReg(0xBD, kDefaultPercussionMask); + + initVoices(); + + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + return 0; +} + +void AdLibDriver::close() { + if (!_isOpen) + return; + + _isOpen = false; + _mixer->stopHandle(_mixerSoundHandle); + + delete _opl; +} + +void AdLibDriver::send(uint32 b) { + uint channel = b & 0xf; + uint cmd = (b >> 4) & 0xf; + uint param1 = (b >> 8) & 0xff; + uint param2 = (b >> 16) & 0xff; + + switch (cmd) { + case 8: + noteOff(channel, param1); + break; + case 9: + // TODO: map volume? + noteOn(channel, param1, param2); + break; + case 11: + // controller change + switch (param1) { + case 1: + setModulationWheel(channel, param2); + break; + case 4: + setFootController(channel, param2); + break; + case 7: + setVolume(channel, param2); + break; + case 123: + // all notes off + allNotesOff(); + break; + } + break; + case 12: + // program change + _channels[channel]._program = param1; + break; + case 14: + setPitchBend(channel, (param1 | (param2 << 7)) - 0x2000); + break; + } +} + +void AdLibDriver::noteOff(uint8 channel, uint8 note) { + if (channel == 9) { + if (note < 35 || note > 81) + return; + + _percussionMask &= ~(1 << percussionNotes[note - 35].percussion); + _opl->writeReg(0xBD, _percussionMask); + return; + } + + for (int i = kNumMelodic - 1; i >= 0; --i) { + if (_voices[i]._channel != channel) + continue; + if (_voices[i]._key != note) + continue; + muteMelodicVoice(i); + _voices[i]._used = false; + return; + } + + //debug(1, "failed to find voice off for channel %d, note %d", channel, note); +} + +void AdLibDriver::noteOn(uint8 channel, uint8 note, uint8 velocity) { + if (channel == 9) { + if (note < 35 || note > 81) + return; + + const PercussionNote &info = percussionNotes[note - 35]; + if (!info.valid) + return; + + if (note != _notesPerPercussion[info.percussion]) { + setupPercussion(info); + _notesPerPercussion[info.percussion] = note; + } + + playPercussion(channel, info, velocity); + return; + } + + if (velocity == 0) { + noteOff(channel, note); + return; + } + + // We want to play a note on a melodic (voice) channel. + + // First, look for a voice playing the same note with the same program. + for (uint i = 0; i < kNumMelodic; ++i) { + if (_voices[i]._channel != channel || _voices[i]._key != note) + continue; + if (_voices[i]._program != _channels[channel]._program) + continue; + muteMelodicVoice(i); + playMelodicNote(i, channel, note, velocity); + return; + } + + // The loops below try to start at _lastVoice and find a voice to use. + // They ignore _lastVoice itself, and update _lastVoice if they succeed. + + // Then, try finding a melodic voice with the same program. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._used) + continue; + if (_voices[i]._program != _channels[channel]._program) + continue; + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Then, try finding a free melodic voice of any kind. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._used) + continue; + programMelodicVoice(i, _channels[channel]._program); + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Then just try finding a melodic voice with the same program, + // and steal it. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._program != _channels[channel]._program) + continue; + muteMelodicVoice(i); + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Finally, just take control of the channel used least recently. + uint voiceId = 0; + uint32 bestTimestamp = 0xffffffff; + for (uint i = 0; i < kNumMelodic; ++i) + if (bestTimestamp > _voices[i]._timestamp) { + voiceId = i; + bestTimestamp = _voices[i]._timestamp; + } + + //debug(1, "ran out of voices for channel %d, note %d, program %d: reused voice %d", channel, note, _channels[channel]._program, voiceId); + programMelodicVoice(voiceId, _channels[channel]._program); + playMelodicNote(voiceId, channel, note, velocity); + _lastVoice = voiceId; +} + +// TODO: this doesn't match original +void AdLibDriver::allNotesOff() { + for (uint i = 0; i < kNumMelodic; ++i) { + muteMelodicVoice(i); + _voices[i]._used = false; + } + + _percussionMask = kDefaultPercussionMask; + _opl->writeReg(0xBD, kDefaultPercussionMask); +} + +void AdLibDriver::setModulationWheel(uint8 channel, uint8 value) { + if (value >= 64) + _percussionMask |= 0x80; + else + _percussionMask &= 0x7f; + + _opl->writeReg(0xBD, _percussionMask); +} + +void AdLibDriver::setFootController(uint8 channel, uint8 value) { + _channels[channel]._pedal = (value >= 64); +} + +void AdLibDriver::setVolume(uint8 channel, uint8 value) { + _channels[channel]._volume = value; +} + +void AdLibDriver::setPitchBend(uint8 channel, int16 value) { + for (uint i = 0; i < kNumMelodic; ++i) { + if (_voices[i]._channel != channel || !_voices[i]._used) + continue; + + // index into frequency table + uint f = 12 + (_voices[i]._key % 12); + + int16 bendAmount = value; + if (bendAmount > 0) { + // bend up two semitones + bendAmount *= (melodicFrequencies[f + 2] - melodicFrequencies[f]); + } else { + // bend down two semitones + bendAmount *= (melodicFrequencies[f] - melodicFrequencies[f - 2]); + } + bendAmount /= 0x2000; + bendAmount += melodicFrequencies[f]; // add the base frequency + playNote(i, _voices[i]._octave, bendAmount); + _voices[i]._timestamp = g_system->getMillis(); + } +} + +void AdLibDriver::playNote(uint8 voice, uint8 octave, uint16 frequency) { + /* Percussions are always fed keyOn = 0 even to set the note, as they are activated using the + BD register instead. I wonder if they can just be fed the same value as melodic voice and + be done with it. */ + uint8 keyOn = (voice < kNumMelodic) ? 0x20 : 0; + + // key on, octave, high 2 bits of frequency + _opl->writeReg(0xB0 + voice, keyOn | ((octave & 7) << 2) | ((frequency >> 8) & 3)); + // low 8 bits of frequency + _opl->writeReg(0xA0 + voice, frequency & 0xff); +} + +void AdLibDriver::programOperatorSimple(uint8 offset, const OPLOperator &op) { + _opl->writeReg(0x40 + offset, op.levels & LEVEL_MASK); + _opl->writeReg(0x60 + offset, op.attackDecay); + _opl->writeReg(0x80 + offset, op.sustainRelease); +} + +void AdLibDriver::programOperator(uint8 offset, const OPLOperator &op) { + _opl->writeReg(0x20 + offset, op.characteristic); + _opl->writeReg(0x60 + offset, op.attackDecay); + _opl->writeReg(0x80 + offset, op.sustainRelease); + _opl->writeReg(0xE0 + offset, op.waveform); + _opl->writeReg(0x40 + offset, op.levels); +} + +const uint16 adlibLogVolume[] = { + 0, 37, 58, 73, 85, 95, 103, 110, 116, 121, 127, 131, 135, 139, 143, 146, + 149, 153, 155, 158, 161, 163, 165, 168, 170, 172, 174, 176, 178, 179, 181, 183, + 184, 186, 188, 189, 191, 192, 193, 195, 196, 197, 198, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 219, + 220, 221, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 231, + 232, 233, 233, 234, 234, 235, 236, 236, 237, 237, 238, 239, 239, 240, 240, 241, + 241, 242, 242, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, + 249, 250, 250, 251, 251, 252, 252, 253, 253, 253, 254, 254, 255, 255, 256, 256, + 256 +}; + +void AdLibDriver::setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume) { + uint8 programLevel = LEVEL_MASK; + if (!forceVolume) + programLevel -= (op.levels & LEVEL_MASK); + + uint32 noteLevel = adlibLogVolume[velocity]; + uint32 channelLevel = adlibLogVolume[_channels[channel]._volume]; + // programLevel comes from the static data and is probably already in the correct logarithmic scale + uint32 finalLevel = LEVEL_MASK - ((noteLevel * channelLevel * programLevel) >> 16); + + // high 2 bits are scaling level, the rest is (inversed) volume + _opl->writeReg(0x40 + offset, (op.levels & 0xc0) | (finalLevel & 0x3f)); +} + +const uint8 operatorOffsetsForPercussion[] = { + 0x11, // hi-hat + 0x15, // cymbal + 0x12, // tom tom + 0x14 // snare drum +}; + +void AdLibDriver::setupPercussion(const PercussionNote ¬e) { + if (note.percussion < 4) { + // simple percussion (1 operator) + + // turn off relevant percussion + _percussionMask &= ~(1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + + programOperatorSimple(operatorOffsetsForPercussion[note.percussion], note.op[0]); + return; + } + + // bass drum (2 operators) + + // turn off bass drum + _percussionMask &= ~(0x10); + _opl->writeReg(0xBD, _percussionMask); + + programOperator(0x10, note.op[0]); + programOperator(0x13, note.op[1]); + + _opl->writeReg(0xC0 + 6, note.feedbackAlgo); +} + +void AdLibDriver::playPercussion(uint8 channel, const PercussionNote ¬e, uint8 velocity) { + if (note.percussion < 4) { + // simple percussion (1 operator) + + // turn off relevant percussion + _percussionMask &= ~(1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + + setOperatorLevel(operatorOffsetsForPercussion[note.percussion], note.op[0], velocity, channel, true); + + if (note.percussion == 2) { + // tom tom + playNote(8, note.octave, note.frequency); + } else if (note.percussion == 3) { + // snare drum + playNote(7, note.octave, note.frequency); + } + + // turn on relevant percussion + _percussionMask |= (1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + return; + } + + // turn off bass drum + _percussionMask &= ~(0x10); + _opl->writeReg(0xBD, _percussionMask); + + if (note.feedbackAlgo & 1) { + // operators 1 and 2 in additive synthesis + setOperatorLevel(0x10, note.op[0], velocity, channel, true); + setOperatorLevel(0x13, note.op[1], velocity, channel, true); + } else { + // operator 2 is modulating operator 1 + setOperatorLevel(0x13, note.op[1], velocity, channel, true); + } + + playNote(6, note.octave, note.frequency); + + // turn on bass drum + _percussionMask |= 0x10; + _opl->writeReg(0xBD, _percussionMask); +} + +const uint8 offset1ForMelodic[kNumVoices] = { 0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12 }; +const uint8 offset2ForMelodic[kNumVoices] = { 0x3, 0x4, 0x5, 0xb, 0xc, 0xd, 0x13, 0x14, 0x15 }; + +void AdLibDriver::programMelodicVoice(uint8 voice, uint8 program) { + assert(program < 128); + assert(voice < kNumMelodic); + + const MelodicProgram &info = melodicPrograms[program]; + uint8 offset1 = offset1ForMelodic[voice]; + uint8 offset2 = offset2ForMelodic[voice]; + + // Start at lowest volume. + _opl->writeReg(0x40 + offset1, LEVEL_MASK); + _opl->writeReg(0x40 + offset2, LEVEL_MASK); + + muteMelodicVoice(voice); + + programOperator(offset1, info.op[0]); + programOperator(offset2, info.op[1]); + + _opl->writeReg(0xC0 + voice, info.feedbackAlgo); +} + +void AdLibDriver::playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity) { + assert(voice < kNumMelodic); + + uint8 octave = note / 12; + uint8 f = 12 + (note % 12); + + if (octave > 7) + octave = 7; + + const MelodicProgram &info = melodicPrograms[_channels[channel]._program]; + uint8 offset1 = offset1ForMelodic[voice]; + uint8 offset2 = offset2ForMelodic[voice]; + + if (info.feedbackAlgo & 1) { + setOperatorLevel(offset1, info.op[0], velocity, channel, false); + setOperatorLevel(offset2, info.op[1], velocity, channel, false); + } else { + setOperatorLevel(offset2, info.op[1], velocity, channel, true); + } + + playNote(voice, octave, melodicFrequencies[f]); + + _voices[voice]._program = _channels[channel]._program; + _voices[voice]._key = note; + _voices[voice]._channel = channel; + _voices[voice]._timestamp = g_system->getMillis(); + _voices[voice]._frequency = melodicFrequencies[f]; + _voices[voice]._octave = octave; + _voices[voice]._used = true; +} + +void AdLibDriver::muteMelodicVoice(uint8 voice) { + _opl->writeReg(0xB0 + voice, 0 | ((_voices[voice]._octave & 7) << 2) | ((_voices[voice]._frequency >> 8) & 3)); +} + +MidiChannel *AdLibDriver::allocateChannel() { + for (uint i = 0; i < 16; ++i) { + if (i == 9) + continue; + + if (_channels[i].allocate()) + return &_channels[i]; + } + + return NULL; +} + +void AdLibDriver::generateSamples(int16 *buf, int len) { + memset(buf, 0, sizeof(int16) * len); + _opl->readBuffer(buf, len); +} + +void AdLibDriver::initVoices() { + _percussionMask = kDefaultPercussionMask; + _opl->writeReg(0xBD, _percussionMask); + + for (uint i = 0; i < 16; ++i) + _channels[i].reset(); + + for (uint i = 0; i < kNumMelodic; ++i) { + _voices[i]._key = 0xff; + _voices[i]._program = 0xff; + _voices[i]._channel = 0xff; + _voices[i]._timestamp = 0; + _voices[i]._frequency = 0; + _voices[i]._octave = 0; + _voices[i]._used = false; + } + + for (uint i = 0; i < kNumPercussion; ++i) + _notesPerPercussion[i] = 0xff; + + _lastVoice = 0; +} + +} // namespace Parallaction diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp index 64885c7ff3..c1720a1a8e 100644 --- a/engines/parallaction/callables_ns.cpp +++ b/engines/parallaction/callables_ns.cpp @@ -275,7 +275,7 @@ void Parallaction_ns::_c_contaFoglie(void *parm) { if (num_foglie != 6) return; - _globalFlags |= 0x1000; + g_globalFlags |= 0x1000; return; } @@ -286,7 +286,7 @@ void Parallaction_ns::_c_zeroFoglie(void *parm) { } void Parallaction_ns::_c_trasformata(void *parm) { - _engineFlags ^= kEngineTransformedDonna; + g_engineFlags ^= kEngineTransformedDonna; // No need to invoke changeCharacter here, as // transformation happens on a location switch // and character change is automatically triggered. @@ -295,11 +295,11 @@ void Parallaction_ns::_c_trasformata(void *parm) { void Parallaction_ns::_c_offMouse(void *parm) { _input->setMouseState(MOUSE_DISABLED); - _engineFlags |= kEngineBlockInput; + g_engineFlags |= kEngineBlockInput; } void Parallaction_ns::_c_onMouse(void *parm) { - _engineFlags &= ~kEngineBlockInput; + g_engineFlags &= ~kEngineBlockInput; _input->setMouseState(MOUSE_ENABLED_SHOW); } @@ -389,7 +389,7 @@ void Parallaction_ns::_c_finito(void *parm) { } void Parallaction_ns::_c_ridux(void *parm) { - changeCharacter(_minidinoName); + changeCharacter(g_minidinoName); return; } @@ -444,7 +444,7 @@ void Parallaction_ns::_c_startIntro(void *parm) { _soundManI->playMusic(); } - _engineFlags |= kEngineBlockInput; + g_engineFlags |= kEngineBlockInput; _input->setMouseState(MOUSE_DISABLED); _intro = true; } diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp index 0cb329e0f0..25acac9b06 100644 --- a/engines/parallaction/debug.cpp +++ b/engines/parallaction/debug.cpp @@ -103,7 +103,7 @@ bool Debugger::Cmd_Locations(int argc, const char **argv) { bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) { - uint32 flags = _globalFlags; + uint32 flags = g_globalFlags; DebugPrintf("+------------------------------+---------+\n" "| flag name | value |\n" @@ -128,10 +128,10 @@ bool Debugger::Cmd_ToggleGlobalFlag(int argc, const char **argv) { DebugPrintf("invalid flag '%s'\n", argv[1]); } else { i--; - if ((_globalFlags & (1 << i)) == 0) - _globalFlags |= (1 << i); + if ((g_globalFlags & (1 << i)) == 0) + g_globalFlags |= (1 << i); else - _globalFlags &= ~(1 << i); + g_globalFlags &= ~(1 << i); } break; diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp index e0bd6a6677..78cc23311f 100644 --- a/engines/parallaction/dialogue.cpp +++ b/engines/parallaction/dialogue.cpp @@ -192,7 +192,7 @@ void DialogueManager::transitionToState(DialogueState newState) { bool DialogueManager::testAnswerFlags(Answer *a) { uint32 flags = _vm->getLocationFlags(); if (a->_yesFlags & kFlagsGlobal) - flags = _globalFlags | kFlagsGlobal; + flags = g_globalFlags | kFlagsGlobal; return ((a->_yesFlags & flags) == a->_yesFlags) && ((a->_noFlags & ~flags) == a->_noFlags); } @@ -370,9 +370,9 @@ protected: bool _askPassword; bool checkPassword() { - return ((!scumm_stricmp(_vm->_char.getBaseName(), _doughName) && _vm->_password.hasPrefix("1732461")) || - (!scumm_stricmp(_vm->_char.getBaseName(), _donnaName) && _vm->_password.hasPrefix("1622")) || - (!scumm_stricmp(_vm->_char.getBaseName(), _dinoName) && _vm->_password.hasPrefix("179"))); + return ((!scumm_stricmp(_vm->_char.getBaseName(), g_doughName) && _vm->_password.hasPrefix("1732461")) || + (!scumm_stricmp(_vm->_char.getBaseName(), g_donnaName) && _vm->_password.hasPrefix("1622")) || + (!scumm_stricmp(_vm->_char.getBaseName(), g_dinoName) && _vm->_password.hasPrefix("179"))); } void resetPassword() { diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index ee981a2c7d..8988897456 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -31,7 +31,7 @@ namespace Parallaction { -extern byte _braAmigaFramesDefaultPalette[]; +extern byte braAmigaFramesDefaultPalette[]; struct Sprite { uint16 size; @@ -475,7 +475,7 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { } delete stream; } else { - p = _braAmigaFramesDefaultPalette; + p = braAmigaFramesDefaultPalette; for (i = 0; i < 16; i++) { r = *p >> 2; p++; diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp index 8d4afd6847..bad854525d 100644 --- a/engines/parallaction/disk_ns.cpp +++ b/engines/parallaction/disk_ns.cpp @@ -262,8 +262,15 @@ Common::SeekableReadStream *DosDisk_ns::tryOpenFile(const char* name) { Script* Disk_ns::loadLocation(const char *name) { char path[PATH_LEN]; + const char *charName = _vm->_char.getBaseName(); - sprintf(path, "%s%s/%s.loc", _vm->_char.getBaseName(), _language.c_str(), name); + // WORKAROUND: Special case for the Multilingual DOS version: during the ending + // sequence, it tries to load a non-existing file using "Dinor" as a character + // name. In this case, the character name should be just "dino". + if (!strcmp(charName, "Dinor")) + charName = "dino"; + + sprintf(path, "%s%s/%s.loc", charName, _language.c_str(), name); debugC(3, kDebugDisk, "Disk_ns::loadLocation(%s): trying '%s'", name, path); Common::SeekableReadStream *stream = tryOpenFile(path); @@ -328,7 +335,7 @@ GfxObj* DosDisk_ns::loadTalk(const char *name) { } char v20[30]; - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { sprintf(v20, "%stta.cnv", name); } else { sprintf(v20, "%stal.cnv", name); diff --git a/engines/parallaction/exec.cpp b/engines/parallaction/exec.cpp index 8594d02641..122abf9e0e 100644 --- a/engines/parallaction/exec.cpp +++ b/engines/parallaction/exec.cpp @@ -56,7 +56,7 @@ void ProgramExec::runScript(ProgramPtr script, AnimationPtr a) { } void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator last) { - if (_engineFlags & kEnginePauseJobs) { + if (g_engineFlags & kEnginePauseJobs) { return; } @@ -110,7 +110,7 @@ void CommandExec::runList(CommandList::iterator first, CommandList::iterator las } if (cmd->_flagsOn & kFlagsGlobal) { - useFlags = _globalFlags | kFlagsGlobal; + useFlags = g_globalFlags | kFlagsGlobal; useLocalFlags = false; } else { useFlags = _vm->getLocationFlags(); @@ -182,7 +182,7 @@ void CommandExec::suspend() { } void CommandExec::runSuspended() { - if (_engineFlags & kEngineWalking) { + if (g_engineFlags & kEngineWalking) { return; } diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp index 658ef5af82..985ea29311 100644 --- a/engines/parallaction/exec_br.cpp +++ b/engines/parallaction/exec_br.cpp @@ -307,7 +307,7 @@ DECLARE_COMMAND_OPCODE(testsfx) { DECLARE_COMMAND_OPCODE(ret) { - _engineFlags |= kEngineReturn; + g_engineFlags |= kEngineReturn; } @@ -327,7 +327,7 @@ DECLARE_INSTRUCTION_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(clear) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags &= ~ctxt._cmd->_flags; + g_globalFlags &= ~ctxt._cmd->_flags; } else { _vm->clearLocationFlags(ctxt._cmd->_flags); } @@ -356,7 +356,7 @@ DECLARE_COMMAND_OPCODE(get) { DECLARE_COMMAND_OPCODE(toggle) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags ^= ctxt._cmd->_flags; + g_globalFlags ^= ctxt._cmd->_flags; } else { _vm->toggleLocationFlags(ctxt._cmd->_flags); } @@ -373,7 +373,7 @@ DECLARE_COMMAND_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(set) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags |= ctxt._cmd->_flags; + g_globalFlags |= ctxt._cmd->_flags; } else { _vm->setLocationFlags(ctxt._cmd->_flags); } diff --git a/engines/parallaction/exec_ns.cpp b/engines/parallaction/exec_ns.cpp index d8fbdea971..3ea4332e50 100644 --- a/engines/parallaction/exec_ns.cpp +++ b/engines/parallaction/exec_ns.cpp @@ -151,7 +151,7 @@ DECLARE_INSTRUCTION_OPCODE(call) { DECLARE_INSTRUCTION_OPCODE(wait) { - if (_engineFlags & kEngineWalking) { + if (g_engineFlags & kEngineWalking) { ctxt._ip--; ctxt._suspend = true; } @@ -198,7 +198,7 @@ DECLARE_COMMAND_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(set) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags |= ctxt._cmd->_flags; + g_globalFlags |= ctxt._cmd->_flags; } else { _vm->setLocationFlags(ctxt._cmd->_flags); } @@ -208,7 +208,7 @@ DECLARE_COMMAND_OPCODE(set) { DECLARE_COMMAND_OPCODE(clear) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags &= ~ctxt._cmd->_flags; + g_globalFlags &= ~ctxt._cmd->_flags; } else { _vm->clearLocationFlags(ctxt._cmd->_flags); } @@ -267,7 +267,7 @@ DECLARE_COMMAND_OPCODE(call) { DECLARE_COMMAND_OPCODE(toggle) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags ^= ctxt._cmd->_flags; + g_globalFlags ^= ctxt._cmd->_flags; } else { _vm->toggleLocationFlags(ctxt._cmd->_flags); } diff --git a/engines/parallaction/font.cpp b/engines/parallaction/font.cpp index d4c9aefd32..3b40960381 100644 --- a/engines/parallaction/font.cpp +++ b/engines/parallaction/font.cpp @@ -29,7 +29,7 @@ namespace Parallaction { -extern byte _amigaTopazFont[]; +extern byte amigaTopazFont[]; class BraFont : public Font { @@ -675,7 +675,7 @@ void Parallaction_ns::initFonts() { _introFont = _disk->loadFont("slide"); } else { _dialogueFont = _disk->loadFont("comic"); - Common::MemoryReadStream stream(_amigaTopazFont, 2600, DisposeAfterUse::NO); + Common::MemoryReadStream stream(amigaTopazFont, 2600, DisposeAfterUse::NO); _labelFont = new AmigaFont(stream); _menuFont = _disk->loadFont("slide"); _introFont = _disk->loadFont("intro"); diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp index 852235ce34..a9889cc7af 100644 --- a/engines/parallaction/gfxbase.cpp +++ b/engines/parallaction/gfxbase.cpp @@ -152,22 +152,22 @@ void Gfx::freeCharacterObjects() { freeDialogueObjects(); } -void BackgroundInfo::loadGfxObjMask(const char *name, GfxObj *obj) { +void BackgroundInfo::loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj) { debugC(1, kDebugGraphics, "BackgroundInfo::loadGfxObjMask(\"%s\")", name); Common::Rect rect; obj->getRect(0, rect); - MaskBuffer *buf = _vm->_disk->loadMask(name, rect.width(), rect.height()); + MaskBuffer *buf = vm->_disk->loadMask(name, rect.width(), rect.height()); obj->_maskId = addMaskPatch(buf); obj->_hasMask = true; } -void BackgroundInfo::loadGfxObjPath(const char *name, GfxObj *obj) { +void BackgroundInfo::loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj) { Common::Rect rect; obj->getRect(0, rect); - PathBuffer *buf = _vm->_disk->loadPath(name, rect.width(), rect.height()); + PathBuffer *buf = vm->_disk->loadPath(name, rect.width(), rect.height()); obj->_pathId = addPathPatch(buf); obj->_hasPath = true; @@ -226,6 +226,11 @@ void Gfx::drawGfxObject(GfxObj *obj, Graphics::Surface &surf) { rect.translate(x, y); data = obj->getData(obj->frame); + // WORKAROUND: During the end credits, game scripts try to show a + // non-existing frame. We change it to an existing one here. + if (obj->frame == 14 && obj->getNum() == 9 && !strcmp(obj->getName(), "Dinor")) + obj->frame = 8; + if (obj->getSize(obj->frame) == obj->getRawSize(obj->frame)) { blt(rect, data, &surf, obj->layer, obj->scale, obj->transparentKey); } else { diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index 9855830478..59cd02e6ef 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -80,11 +80,11 @@ void drawCircle(int xCenter, int yCenter, int radius, int color, void (*plotProc Palette::Palette() { - int gameType = _vm->getGameType(); + int gameType = g_vm->getGameType(); if (gameType == GType_Nippon) { _colors = 32; - _hb = (_vm->getPlatform() == Common::kPlatformAmiga); + _hb = (g_vm->getPlatform() == Common::kPlatformAmiga); } else if (gameType == GType_BRA) { _colors = 256; diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h index f8cb4b3647..e9daabb194 100644 --- a/engines/parallaction/graphics.h +++ b/engines/parallaction/graphics.h @@ -377,14 +377,14 @@ public: void toggleMaskPatch(uint id, int x, int y, bool apply); uint16 getMaskLayer(uint16 z) const; void finalizeMask(); - void loadGfxObjMask(const char *name, GfxObj *obj); + void loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj); // path management bool hasPath(); uint addPathPatch(PathBuffer *patch); void togglePathPatch(uint id, int x, int y, bool apply); void finalizePath(); - void loadGfxObjPath(const char *name, GfxObj *obj); + void loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj); }; diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp index 3794aeae29..082c37f666 100644 --- a/engines/parallaction/gui_ns.cpp +++ b/engines/parallaction/gui_ns.cpp @@ -787,7 +787,7 @@ public: } destroyLabels(); - _engineFlags &= ~kEngineBlockInput; + g_engineFlags &= ~kEngineBlockInput; return _helper->getState("selectcharacter"); } diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index 453bf9849d..4fbd9b99cc 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -188,15 +188,15 @@ int Input::updateGameInput() { int event = kEvNone; if (!isMouseEnabled() || - (_engineFlags & kEngineBlockInput) || - (_engineFlags & kEngineWalking) || - (_engineFlags & kEngineChangeLocation)) { + (g_engineFlags & kEngineBlockInput) || + (g_engineFlags & kEngineWalking) || + (g_engineFlags & kEngineChangeLocation)) { debugC(3, kDebugInput, "updateGameInput: input flags (mouse: %i, block: %i, walking: %i, changeloc: %i)", isMouseEnabled(), - (_engineFlags & kEngineBlockInput) == 0, - (_engineFlags & kEngineWalking) == 0, - (_engineFlags & kEngineChangeLocation) == 0 + (g_engineFlags & kEngineBlockInput) == 0, + (g_engineFlags & kEngineWalking) == 0, + (g_engineFlags & kEngineChangeLocation) == 0 ); return event; @@ -289,7 +289,7 @@ void Input::walkTo(const Common::Point &dest) { bool Input::translateGameInput() { - if (_engineFlags & kEnginePauseJobs) { + if (g_engineFlags & kEnginePauseJobs) { return false; } @@ -312,7 +312,7 @@ bool Input::translateGameInput() { // test if mouse is hovering on an interactive zone for the currently selected inventory item ZonePtr z = _vm->hitZone(_activeItem._id, mousePos.x, mousePos.y); - if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) { + if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((g_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) { walkTo(mousePos); return true; } @@ -361,7 +361,7 @@ void Input::enterInventoryMode() { if (hitCharacter) { if (_activeItem._id != 0) { _activeItem._index = (_activeItem._id >> 16) & 0xFFFF; - _engineFlags |= kEngineDragging; + g_engineFlags |= kEngineDragging; } else { setArrowCursor(); } @@ -384,9 +384,9 @@ void Input::exitInventoryMode() { int pos = _vm->getHoverInventoryItem(mousePos.x, mousePos.y); _vm->highlightInventoryItem(-1); // disable - if ((_engineFlags & kEngineDragging)) { + if ((g_engineFlags & kEngineDragging)) { - _engineFlags &= ~kEngineDragging; + g_engineFlags &= ~kEngineDragging; ZonePtr z = _vm->hitZone(kZoneMerge, _activeItem._index, _vm->getInventoryItemIndex(pos)); if (z) { diff --git a/engines/parallaction/module.mk b/engines/parallaction/module.mk index d65653cd92..36572a51df 100644 --- a/engines/parallaction/module.mk +++ b/engines/parallaction/module.mk @@ -1,6 +1,7 @@ MODULE := engines/parallaction MODULE_OBJS := \ + adlib.o \ balloons.o \ callables_br.o \ callables_ns.o \ diff --git a/engines/parallaction/objects.cpp b/engines/parallaction/objects.cpp index d3529c5dd9..50556c3ec4 100644 --- a/engines/parallaction/objects.cpp +++ b/engines/parallaction/objects.cpp @@ -203,7 +203,7 @@ Zone::Zone() { } Zone::~Zone() { - _vm->_gfx->unregisterLabel(_label); + g_vm->_gfx->unregisterLabel(_label); delete _label; } @@ -325,7 +325,7 @@ int16 ScriptVar::getValue() { } if (_flags & kParaRandom) { - return (_vm->_rnd.getRandomNumber(65536) * _value) >> 16; + return (g_vm->_rnd.getRandomNumber(65536) * _value) >> 16; } error("Parameter is not an r-value"); diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp index 3b1b7d54a0..e6ef53aa78 100644 --- a/engines/parallaction/parallaction.cpp +++ b/engines/parallaction/parallaction.cpp @@ -33,12 +33,12 @@ #include "parallaction/walk.h" namespace Parallaction { -Parallaction *_vm = NULL; +Parallaction *g_vm = NULL; // public stuff -char _saveData1[30] = { '\0' }; -uint32 _engineFlags = 0; -uint32 _globalFlags = 0; +char g_saveData1[30] = { '\0' }; +uint32 g_engineFlags = 0; +uint32 g_globalFlags = 0; // private stuff @@ -48,7 +48,7 @@ Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gam // Setup mixer syncSoundSettings(); - _vm = this; + g_vm = this; DebugMan.addDebugChannel(kDebugDialogue, "dialogue", "Dialogues debug level"); DebugMan.addDebugChannel(kDebugParser, "parser", "Parser debug level"); DebugMan.addDebugChannel(kDebugDisk, "disk", "Disk debug level"); @@ -87,7 +87,7 @@ Parallaction::~Parallaction() { Common::Error Parallaction::init() { _gameType = getGameType(); - _engineFlags = 0; + g_engineFlags = 0; _objectsNames = NULL; _globalFlagsNames = NULL; _location._hasSound = false; @@ -129,13 +129,9 @@ GUI::Debugger *Parallaction::getDebugger() { return _debugger; } -bool canScroll() { - return (_vm->_gfx->_backgroundInfo->width > _vm->_screenWidth); -} - void Parallaction::updateView() { - if ((_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) { + if ((g_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) { return; } @@ -147,14 +143,14 @@ void Parallaction::updateView() { void Parallaction::pauseJobs() { debugC(9, kDebugExec, "pausing jobs execution"); - _engineFlags |= kEnginePauseJobs; + g_engineFlags |= kEnginePauseJobs; return; } void Parallaction::resumeJobs() { debugC(9, kDebugExec, "resuming jobs execution"); - _engineFlags &= ~kEnginePauseJobs; + g_engineFlags &= ~kEnginePauseJobs; return; } @@ -265,7 +261,7 @@ void Parallaction::runGameFrame(int event) { if (shouldQuit()) return; - if (_engineFlags & kEngineChangeLocation) { + if (g_engineFlags & kEngineChangeLocation) { changeLocation(); } @@ -900,14 +896,14 @@ void CharacterName::bind(const char *name) { if (!_dummy) { if (!strcmp(name, "donna")) { - _engineFlags &= ~kEngineTransformedDonna; + g_engineFlags &= ~kEngineTransformedDonna; } else { - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { _suffix = _suffixTras; } else { const char *s = strstr(name, "tras"); if (s) { - _engineFlags |= kEngineTransformedDonna; + g_engineFlags |= kEngineTransformedDonna; _suffix = _suffixTras; end = s; } @@ -953,7 +949,7 @@ void Parallaction::beep() { void Parallaction::scheduleLocationSwitch(const char *location) { debugC(9, kDebugExec, "scheduleLocationSwitch(%s)\n", location); _newLocationName = location; - _engineFlags |= kEngineChangeLocation; + g_engineFlags |= kEngineChangeLocation; } } // End of namespace Parallaction diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index 0d56b62e2f..2dbb0227d6 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -104,17 +104,17 @@ struct PARALLACTIONGameDescription; -extern uint32 _engineFlags; -extern char _saveData1[]; -extern uint32 _globalFlags; -extern const char *_dinoName; -extern const char *_donnaName; -extern const char *_doughName; -extern const char *_drkiName; -extern const char *_minidinoName; -extern const char *_minidonnaName; -extern const char *_minidoughName; -extern const char *_minidrkiName; +extern uint32 g_engineFlags; +extern char g_saveData1[]; +extern uint32 g_globalFlags; +extern const char *g_dinoName; +extern const char *g_donnaName; +extern const char *g_doughName; +extern const char *g_drkiName; +extern const char *g_minidinoName; +extern const char *g_minidonnaName; +extern const char *g_minidoughName; +extern const char *g_minidrkiName; @@ -601,7 +601,7 @@ private: void _c_password(void *); }; -extern Parallaction *_vm; +extern Parallaction *g_vm; } // End of namespace Parallaction diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index 658a8e8795..07755fac5f 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -79,7 +79,7 @@ Common::Error Parallaction_br::init() { _cmdExec = new CommandExec_br(this); _programExec = new ProgramExec_br(this); - _walker = new PathWalker_BR; + _walker = new PathWalker_BR(this); _part = -1; _nextPart = -1; @@ -161,10 +161,10 @@ Common::Error Parallaction_br::go() { // initCharacter(); - while (((_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) { + while (((g_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) { runGame(); } - _engineFlags &= ~kEngineReturn; + g_engineFlags &= ~kEngineReturn; cleanupGame(); } @@ -259,7 +259,7 @@ void Parallaction_br::cleanupGame() { _countersNames = 0; _numLocations = 0; - _globalFlags = 0; + g_globalFlags = 0; memset(_localFlags, 0, sizeof(_localFlags)); memset(_locationNames, 0, sizeof(_locationNames)); memset(_zoneFlags, 0, sizeof(_zoneFlags)); @@ -275,7 +275,7 @@ void Parallaction_br::changeLocation() { cleanupGame(); // more cleanup needed for part changes (see also saveload) - _globalFlags = 0; + g_globalFlags = 0; cleanInventory(true); strcpy(_characterName1, "null"); @@ -358,7 +358,7 @@ void Parallaction_br::changeLocation() { // TODO: implement the music commands which control music execution _soundMan->execute(SC_PLAYMUSIC); - _engineFlags &= ~kEngineChangeLocation; + g_engineFlags &= ~kEngineChangeLocation; _newLocationName.clear(); _nextPart = -1; } @@ -548,7 +548,7 @@ void Parallaction_br::scheduleWalk(int16 x, int16 y, bool fromUser) { } } - _engineFlags |= kEngineWalking; + g_engineFlags |= kEngineWalking; } void Parallaction_br::setFollower(const Common::String &name) { diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp index 0b92db1f0a..d33be0aa47 100644 --- a/engines/parallaction/parallaction_ns.cpp +++ b/engines/parallaction/parallaction_ns.cpp @@ -182,7 +182,7 @@ Common::Error Parallaction_ns::init() { _cmdExec = new CommandExec_ns(this); _programExec = new ProgramExec_ns(this); - _walker = new PathWalker_NS; + _walker = new PathWalker_NS(this); _sarcophagusDeltaX = 0; _movingSarcophagus = false; @@ -310,6 +310,7 @@ void Parallaction_ns::changeBackground(const char* background, const char* mask, _system->delayMillis(20); _gfx->setPalette(pal); _gfx->updateScreen(); + return; } if (path == 0) { @@ -382,8 +383,8 @@ void Parallaction_ns::changeLocation() { changeCharacter(locname.character()); } - strcpy(_saveData1, locname.location()); - parseLocation(_saveData1); + strcpy(g_saveData1, locname.location()); + parseLocation(g_saveData1); if (_location._startPosition.x != -1000) { _char._ani->setX(_location._startPosition.x); @@ -399,7 +400,7 @@ void Parallaction_ns::changeLocation() { // BUG #1837503: kEngineChangeLocation flag must be cleared *before* commands // and acommands are executed, so that it can be set again if needed. - _engineFlags &= ~kEngineChangeLocation; + g_engineFlags &= ~kEngineChangeLocation; _cmdExec->run(_location._commands); @@ -412,6 +413,11 @@ void Parallaction_ns::changeLocation() { if (!_intro) { _input->setMouseState(oldMouseState); + // WORKAROUND: Fix a script bug in the Multilingual DOS version of + // Nippon Safes: the mouse cursor is incorrectly hidden outside the + // cave at the end of the game. Fix it here. + if (!strcmp(_location._name, "ingressocav")) + _input->setMouseState(MOUSE_ENABLED_SHOW); } debugC(1, kDebugExec, "changeLocation() done"); @@ -526,10 +532,10 @@ void Parallaction_ns::cleanupGame() { _soundManI->stopMusic(); _inTestResult = false; - _engineFlags &= ~kEngineTransformedDonna; + g_engineFlags &= ~kEngineTransformedDonna; _numLocations = 0; - _globalFlags = 0; + g_globalFlags = 0; memset(_localFlags, 0, sizeof(_localFlags)); memset(_locationNames, 0, sizeof(_locationNames)); @@ -553,7 +559,7 @@ void Parallaction_ns::scheduleWalk(int16 x, int16 y, bool fromUser) { } _walker->buildPath(a, x, y); - _engineFlags |= kEngineWalking; + g_engineFlags |= kEngineWalking; } }// namespace Parallaction diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp index 0904dbf655..e7f1b1b1ed 100644 --- a/engines/parallaction/parser_br.cpp +++ b/engines/parallaction/parser_br.cpp @@ -767,10 +767,10 @@ void LocationParser_br::parseGetData(ZonePtr z) { data->_gfxobj = obj; } else if (!scumm_stricmp(_tokens[0], "mask")) { - _out->_info->loadGfxObjMask(_tokens[1], data->_gfxobj); + _out->_info->loadGfxObjMask(_vm, _tokens[1], data->_gfxobj); } else if (!scumm_stricmp(_tokens[0], "path")) { - _out->_info->loadGfxObjPath(_tokens[1], data->_gfxobj); + _out->_info->loadGfxObjPath(_vm, _tokens[1], data->_gfxobj); } else if (!scumm_stricmp(_tokens[0], "icon")) { data->_getIcon = 4 + _vm->_objectsNames->lookup(_tokens[1]); diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp index f1d1db53e9..41ff74f0b4 100644 --- a/engines/parallaction/parser_ns.cpp +++ b/engines/parallaction/parser_ns.cpp @@ -246,7 +246,7 @@ DECLARE_ANIM_PARSER(file) { char vC8[200]; strcpy(vC8, _tokens[1]); - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { if (!scumm_stricmp(_tokens[1], "donnap") || !scumm_stricmp(_tokens[1], "donnapa")) { strcat(vC8, "tras"); } diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp index 8de2d89b18..2ecc5377a4 100644 --- a/engines/parallaction/saveload.cpp +++ b/engines/parallaction/saveload.cpp @@ -88,7 +88,7 @@ void SaveLoad_ns::doLoadGame(uint16 slot) { _vm->_score = atoi(s.c_str()); s = f->readLine(); - _globalFlags = atoi(s.c_str()); + g_globalFlags = atoi(s.c_str()); s = f->readLine(); _vm->_numLocations = atoi(s.c_str()); @@ -151,7 +151,7 @@ void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) { sprintf(s, "%s\n", _vm->_char.getFullName()); f->writeString(s); - sprintf(s, "%s\n", _saveData1); + sprintf(s, "%s\n", g_saveData1); f->writeString(s); sprintf(s, "%d\n", _vm->_char._ani->getX()); f->writeString(s); @@ -159,7 +159,7 @@ void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) { f->writeString(s); sprintf(s, "%d\n", _vm->_score); f->writeString(s); - sprintf(s, "%u\n", _globalFlags); + sprintf(s, "%u\n", g_globalFlags); f->writeString(s); sprintf(s, "%d\n", _vm->_numLocations); diff --git a/engines/parallaction/sound.h b/engines/parallaction/sound.h index e875e69334..e12e50e278 100644 --- a/engines/parallaction/sound.h +++ b/engines/parallaction/sound.h @@ -33,6 +33,7 @@ #define PATH_LEN 200 class MidiParser; +class MidiDriver; namespace Parallaction { @@ -41,6 +42,7 @@ class MidiPlayer; class Parallaction_br; class MidiPlayer_MSC; +MidiDriver *createAdLibDriver(); class SoundManImpl { public: diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp index 0925e55309..ad510eb1f1 100644 --- a/engines/parallaction/sound_br.cpp +++ b/engines/parallaction/sound_br.cpp @@ -91,20 +91,20 @@ public: }; void MidiParser_MSC::parseMetaEvent(EventInfo &info) { - uint8 type = read1(_position._play_pos); - uint8 len = read1(_position._play_pos); + uint8 type = read1(_position._playPos); + uint8 len = read1(_position._playPos); info.ext.type = type; info.length = len; info.ext.data = 0; if (type == 0x51) { - info.ext.data = _position._play_pos; + info.ext.data = _position._playPos; } else { warning("unknown meta event 0x%02X", type); info.ext.type = 0; } - _position._play_pos += len; + _position._playPos += len; } void MidiParser_MSC::parseMidiEvent(EventInfo &info) { @@ -116,13 +116,13 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) { case 0xA: case 0xB: case 0xE: - info.basic.param1 = read1(_position._play_pos); - info.basic.param2 = read1(_position._play_pos); + info.basic.param1 = read1(_position._playPos); + info.basic.param2 = read1(_position._playPos); break; case 0xC: case 0xD: - info.basic.param1 = read1(_position._play_pos); + info.basic.param1 = read1(_position._playPos); info.basic.param2 = 0; break; @@ -135,9 +135,9 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) { } void MidiParser_MSC::parseNextEvent(EventInfo &info) { - info.start = _position._play_pos; + info.start = _position._playPos; - if (_position._play_pos >= _trackEnd) { + if (_position._playPos >= _trackEnd) { // fake an end-of-track meta event info.delta = 0; info.event = 0xFF; @@ -146,8 +146,9 @@ void MidiParser_MSC::parseNextEvent(EventInfo &info) { return; } - info.delta = readVLQ(_position._play_pos); - info.event = read1(_position._play_pos); + info.length = 0; + info.delta = readVLQ(_position._playPos); + info.event = read1(_position._playPos); if (info.event == 0xFF) { parseMetaEvent(info); @@ -155,7 +156,7 @@ void MidiParser_MSC::parseNextEvent(EventInfo &info) { } if (info.event < 0x80) { - _position._play_pos--; + _position._playPos--; info.event = _lastEvent; } @@ -185,7 +186,7 @@ bool MidiParser_MSC::loadMusic(byte *data, uint32 size) { _lastEvent = 0; _trackEnd = data + size; - _num_tracks = 1; + _numTracks = 1; _tracks[0] = pos; setTempo(500000); @@ -224,7 +225,12 @@ MidiPlayer_MSC::MidiPlayer_MSC() : _paused(false) { MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM); - _driver = MidiDriver::createMidi(dev); + const MusicType musicType = MidiDriver::getMusicType(dev); + if (musicType == MT_ADLIB) { + _driver = createAdLibDriver(); + } else { + _driver = MidiDriver::createMidi(dev); + } assert(_driver); int ret = _driver->open(); diff --git a/engines/parallaction/sound_ns.cpp b/engines/parallaction/sound_ns.cpp index dcc71e4f2f..0ee3d73556 100644 --- a/engines/parallaction/sound_ns.cpp +++ b/engines/parallaction/sound_ns.cpp @@ -168,13 +168,13 @@ void DosSoundMan_ns::playCharacterMusic(const char *character) { char *name = const_cast<char *>(character); const char *newMusicFile = 0; - if (!scumm_stricmp(name, _dinoName)) { + if (!scumm_stricmp(name, g_dinoName)) { newMusicFile = "dino"; } else - if (!scumm_stricmp(name, _donnaName)) { + if (!scumm_stricmp(name, g_donnaName)) { newMusicFile = "donna"; } else - if (!scumm_stricmp(name, _doughName)) { + if (!scumm_stricmp(name, g_doughName)) { newMusicFile = "nuts"; } else { warning("unknown character '%s' in DosSoundMan_ns_ns::playCharacterMusic", character); diff --git a/engines/parallaction/staticres.cpp b/engines/parallaction/staticres.cpp index 73e78cae3c..f09b1241bc 100644 --- a/engines/parallaction/staticres.cpp +++ b/engines/parallaction/staticres.cpp @@ -89,14 +89,14 @@ byte Input::_resMouseArrow_BR_Amiga[512] = { /* This palette snippet is used for animations in Big Red Adventure. */ -byte _braAmigaFramesDefaultPalette[48] = { +byte braAmigaFramesDefaultPalette[48] = { 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00, 0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE, 0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF, }; -byte _amigaTopazFont[2600] = { +byte amigaTopazFont[2600] = { 0x00, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x02, 0x79, 0x70, 0xff, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, @@ -263,7 +263,7 @@ byte _amigaTopazFont[2600] = { }; -const char *_callableNamesRes_ns[] = { +const char *callableNamesRes_ns[] = { "Projector", "HBOff", "StartIntro", @@ -292,7 +292,7 @@ const char *_callableNamesRes_ns[] = { }; -const char *_callableNamesRes_br[] = { +const char *callableNamesRes_br[] = { "blufade", "resetpalette", "ferrcycle", @@ -301,15 +301,15 @@ const char *_callableNamesRes_br[] = { "password" }; -const char *_dinoName = "dino"; -const char *_donnaName = "donna"; -const char *_doughName = "dough"; -const char *_drkiName = "drki"; +const char *g_dinoName = "dino"; +const char *g_donnaName = "donna"; +const char *g_doughName = "dough"; +const char *g_drkiName = "drki"; -const char *_minidinoName = "minidino"; -const char *_minidonnaName = "minidonna"; -const char *_minidoughName = "minidough"; -const char *_minidrkiName = "minidrki"; +const char *g_minidinoName = "minidino"; +const char *g_minidonnaName = "minidonna"; +const char *g_minidoughName = "minidough"; +const char *g_minidrkiName = "minidrki"; #define CALLABLE_NS(x) &Parallaction_ns::x @@ -391,7 +391,7 @@ const Parallaction_br::Callable Parallaction_br::_amigaCallables[] = { void Parallaction_ns::initResources() { - _callableNames = new Table(ARRAYSIZE(_callableNamesRes_ns), _callableNamesRes_ns); + _callableNames = new Table(ARRAYSIZE(callableNamesRes_ns), callableNamesRes_ns); _localFlagNames = new FixedTable(NUM_LOCATIONS, 1); _localFlagNames->addData("visited"); @@ -406,7 +406,7 @@ void Parallaction_ns::initResources() { void Parallaction_br::initResources() { - _callableNames = new Table(ARRAYSIZE(_callableNamesRes_br), _callableNamesRes_br); + _callableNames = new Table(ARRAYSIZE(callableNamesRes_br), callableNamesRes_br); _localFlagNames = new FixedTable(NUM_LOCATIONS, 2); _localFlagNames->addData("visited"); diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp index 53237db4ef..19162cd7db 100644 --- a/engines/parallaction/walk.cpp +++ b/engines/parallaction/walk.cpp @@ -55,27 +55,27 @@ WalkFrames _char24WalkFrames_NS = { }; static int getPathWidth() { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("getPathWidth() _path is NULL!"); return 0; } else - return _vm->_gfx->_backgroundInfo->_path->w; + return g_vm->_gfx->_backgroundInfo->_path->w; } static int getPathHeight() { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("getPathHeight() _path is NULL!"); return 0; } else - return _vm->_gfx->_backgroundInfo->_path->h; + return g_vm->_gfx->_backgroundInfo->_path->h; } static bool isPathClear(uint16 x, uint16 y) { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("isPathClear() _path is NULL!"); return false; } else - return (_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false); + return (g_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false); } // adjusts position towards nearest walkable point @@ -306,7 +306,7 @@ void PathWalker_NS::checkDoor(const Common::Point &foot) { } void PathWalker_NS::finalizeWalk() { - _engineFlags &= ~kEngineWalking; + g_engineFlags &= ~kEngineWalking; Common::Point foot; _a->getFoot(foot); @@ -316,7 +316,7 @@ void PathWalker_NS::finalizeWalk() { } void PathWalker_NS::walk() { - if ((_engineFlags & kEngineWalking) == 0) { + if ((g_engineFlags & kEngineWalking) == 0) { return; } @@ -382,7 +382,7 @@ void PathWalker_NS::updateDirection(const Common::Point& pos, const Common::Poin _a->setF(frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction]); } -PathWalker_NS::PathWalker_NS() : _direction(WALK_DOWN), _step(0) { +PathWalker_NS::PathWalker_NS(Parallaction *vm) : _direction(WALK_DOWN), _step(0), _vm(vm) { } bool PathWalker_BR::directPathExists(const Common::Point &from, const Common::Point &to) { @@ -481,7 +481,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) { } void PathWalker_BR::finalizeWalk(State &s) { - _engineFlags &= ~kEngineWalking; + g_engineFlags &= ~kEngineWalking; Common::Point foot; _character._a->getFoot(foot); @@ -508,8 +508,8 @@ void PathWalker_BR::finalizeWalk(State &s) { #if 0 // TODO: Input::walkTo must be extended to support destination frame in addition to coordinates - if (_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput() - _engineFlags &= ~FINAL_WALK_FRAME; + if (g_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput() + g_engineFlags &= ~FINAL_WALK_FRAME; _ch._a->_frame = _moveToF; // from readInput()... } else { _ch._a->_frame = _dirFrame; // from walk() @@ -523,7 +523,7 @@ void PathWalker_BR::finalizeWalk(State &s) { } void PathWalker_BR::walk() { - if ((_engineFlags & kEngineWalking) == 0) { + if ((g_engineFlags & kEngineWalking) == 0) { return; } @@ -714,7 +714,7 @@ void PathWalker_BR::doWalk(State &s) { } } -PathWalker_BR::PathWalker_BR() { +PathWalker_BR::PathWalker_BR(Parallaction *vm) : _vm(vm) { _character._active = false; _character._step = 0; _follower._active = false; diff --git a/engines/parallaction/walk.h b/engines/parallaction/walk.h index 6796991f9d..ae6db6eaf1 100644 --- a/engines/parallaction/walk.h +++ b/engines/parallaction/walk.h @@ -49,8 +49,10 @@ class PathWalker_NS { void checkDoor(const Common::Point &foot); void updateDirection(const Common::Point& pos, const Common::Point& to); + Parallaction *_vm; + public: - PathWalker_NS(); + PathWalker_NS(Parallaction *vm); void buildPath(AnimationPtr a, uint16 x, uint16 y); void walk(); @@ -79,8 +81,10 @@ class PathWalker_BR { void doWalk(State &s); void checkTrap(const Common::Point &p); + Parallaction *_vm; + public: - PathWalker_BR(); + PathWalker_BR(Parallaction *vm); ~PathWalker_BR() { } void setCharacterPath(AnimationPtr a, uint16 x, uint16 y); diff --git a/engines/pegasus/ai/ai_action.cpp b/engines/pegasus/ai/ai_action.cpp new file mode 100644 index 0000000000..38d639038f --- /dev/null +++ b/engines/pegasus/ai/ai_action.cpp @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" + +namespace Pegasus { + +AICompoundAction::~AICompoundAction() { + for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++) + delete *it; +} + +void AICompoundAction::performAIAction(AIRule *rule) { + for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++) + (*it)->performAIAction(rule); +} + +AIPlayMessageAction::AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const InputBits interruptionFilter) { + _movieName = movieName; + _keepLastFrame = keepLastFrame; + _interruptionFilter = interruptionFilter; +} + +void AIPlayMessageAction::performAIAction(AIRule *) { + if (g_AIArea) { + g_AIArea->checkMiddleArea(); + g_AIArea->playAIMovie(kRightAreaSignature, _movieName, _keepLastFrame, _interruptionFilter); + } +} + +AIStartTimerAction::AIStartTimerAction(AITimerCondition *timerCondition) { + _timerCondition = timerCondition; +} + +void AIStartTimerAction::performAIAction(AIRule *) { + _timerCondition->startTimer(); +} + +AIActivateRuleAction::AIActivateRuleAction(AIRule *rule) { + _rule = rule; +} + +void AIActivateRuleAction::performAIAction(AIRule *) { + _rule->activateRule(); +} + +AIDeactivateRuleAction::AIDeactivateRuleAction(AIRule *rule) { + _rule = rule; +} + +void AIDeactivateRuleAction::performAIAction(AIRule *) { + _rule->deactivateRule(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_action.h b/engines/pegasus/ai/ai_action.h new file mode 100644 index 0000000000..6eac976f9c --- /dev/null +++ b/engines/pegasus/ai/ai_action.h @@ -0,0 +1,136 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_AI_AIACTION_H +#define PEGASUS_AI_AIACTION_H + +#include "common/list.h" + +#include "pegasus/input.h" +#include "pegasus/types.h" + +namespace Pegasus { + +class AIRule; +class AITimerCondition; + +///////////////////////////////////////////// +// +// AIAction + +class AIAction { +friend class AIRule; +public: + AIAction() { _actionCount = 1; } + virtual ~AIAction() {} + + virtual void performAIAction(AIRule *) = 0; + + void setActionCount(const uint32 count) { _actionCount = count; } + +protected: + uint32 _actionCount; +}; + +typedef Common::List<AIAction *> AIActionList; + +///////////////////////////////////////////// +// +// AICompoundAction + +class AICompoundAction : public AIAction { +public: + AICompoundAction() {} + virtual ~AICompoundAction(); + + void addAction(AIAction *action) { _compoundActions.push_back(action); } + + virtual void performAIAction(AIRule *); + +protected: + AIActionList _compoundActions; +}; + +///////////////////////////////////////////// +// +// AIPlayMessageAction + +class AIPlayMessageAction : public AIAction { +public: + AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const InputBits = kWarningInterruption); + + virtual void performAIAction(AIRule *); + +protected: + Common::String _movieName; + InputBits _interruptionFilter; + bool _keepLastFrame; +}; + +///////////////////////////////////////////// +// +// AIStartTimerAction + +class AIStartTimerAction : public AIAction { +public: + AIStartTimerAction(AITimerCondition *); + + virtual void performAIAction(AIRule *); + +protected: + AITimerCondition *_timerCondition; +}; + +///////////////////////////////////////////// +// +// AIActivateRuleAction + +class AIActivateRuleAction : public AIAction { +public: + AIActivateRuleAction(AIRule *); + + virtual void performAIAction(AIRule *); + +protected: + AIRule *_rule; +}; + +///////////////////////////////////////////// +// +// AIDeactivateRuleAction + +class AIDeactivateRuleAction : public AIAction { +public: + AIDeactivateRuleAction(AIRule *rule); + + virtual void performAIAction(AIRule *); + +protected: + AIRule *_rule; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_area.cpp b/engines/pegasus/ai/ai_area.cpp new file mode 100644 index 0000000000..5ac8af8812 --- /dev/null +++ b/engines/pegasus/ai/ai_area.cpp @@ -0,0 +1,613 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/memstream.h" + +#include "pegasus/cursor.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +AIArea *g_AIArea = 0; + +AIArea::AIArea(InputHandler *nextHandler) : InputHandler(nextHandler), _leftAreaMovie(kAILeftAreaID), + _middleAreaMovie(kAIMiddleAreaID), _rightAreaMovie(kAIRightAreaID), _AIMovie(kAIMovieID) { + g_AIArea = this; + _leftAreaOwner = kNoClientSignature; + _middleAreaOwner = kNoClientSignature; + _rightAreaOwner = kNoClientSignature; + _leftInventoryTime = 0xffffffff; + _middleInventoryTime = 0xffffffff; + _middleBiochipTime = 0xffffffff; + _rightBiochipTime = 0xffffffff; + _lockCount = 0; + startIdling(); +} + +AIArea::~AIArea() { + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip && currentBiochip->isSelected()) + currentBiochip->giveUpSharedArea(); + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->isSelected()) + currentItem->giveUpSharedArea(); + } + + stopIdling(); + + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + delete *it; + + g_AIArea = 0; +} + +// Save last state of AI rules... +void AIArea::saveAIState() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + delete vm->_aiSaveStream; + + Common::MemoryWriteStreamDynamic out; + writeAIRules(&out); + + vm->_aiSaveStream = new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES); +} + +void AIArea::restoreAIState() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (vm->_aiSaveStream) + readAIRules(vm->_aiSaveStream); +} + +void AIArea::writeAIRules(Common::WriteStream *stream) { + _AIRules.writeAIRules(stream); +} + +void AIArea::readAIRules(Common::ReadStream *stream) { + _AIRules.readAIRules(stream); +} + +void AIArea::initAIArea() { + allocateSurface(Common::Rect(0, 0, 384, 96)); + + _leftAreaMovie.shareSurface(this); + _leftAreaMovie.initFromMovieFile("Images/Items/Left Area Movie"); + _leftAreaMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); + _leftAreaMovie.setDisplayOrder(kAILeftAreaOrder); + _leftAreaMovie.startDisplaying(); + _leftAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _middleAreaMovie.shareSurface(this); + _middleAreaMovie.initFromMovieFile("Images/Items/Middle Area Movie"); + _middleAreaMovie.moveElementTo(kAIMiddleAreaLeft, kAIMiddleAreaTop); + _middleAreaMovie.moveMovieBoxTo(kAIMiddleAreaLeft - kAILeftAreaLeft, 0); + _middleAreaMovie.setDisplayOrder(kAIMiddleAreaOrder); + _middleAreaMovie.startDisplaying(); + _middleAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _rightAreaMovie.shareSurface(this); + _rightAreaMovie.initFromMovieFile("Images/Items/Right Area Movie"); + _rightAreaMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); + _rightAreaMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); + _rightAreaMovie.setDisplayOrder(kAIRightAreaOrder); + _rightAreaMovie.startDisplaying(); + _rightAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + + _AIMovie.setDisplayOrder(kAIMovieOrder); +} + +void AIArea::setAIVolume(const uint16 volume) { + _leftAreaMovie.setVolume(volume); + _middleAreaMovie.setVolume(volume); + _rightAreaMovie.setVolume(volume); +} + +// There are only so many legal combinations of client/area. +// Here is the list of supported pairs: +// kInventorySignature kLeftAreaSignature +// kInventorySignature kMiddleAreaSignature +// kBiochipSignature kMiddleAreaSignature +// kBiochipSignature kRightAreaSignature +// kAISignature kLeftAreaSignature +// Further, the kAISignature never sets a static frame time in the left area, +// but only plays a sequence. + +// If this function is called while a sequence is playing, it will just "remember" +// the time value, so that when the sequence finishes, the new time is asserted. + +void AIArea::setAIAreaToTime(const LowerClientSignature client, const LowerAreaSignature area, const TimeValue time) { + switch (area) { + case kLeftAreaSignature: + // Only support kInventorySignature client, since AI never calls SetAIAreaToTime. + _leftAreaMovie.setSegment(0, _leftAreaMovie.getDuration()); + + if (time == 0xffffffff) { + _leftAreaMovie.hide(); + _leftAreaOwner = kNoClientSignature; + } else { + setLeftMovieTime(time); + } + break; + case kMiddleAreaSignature: + // Only support kInventorySignature and kBiochipSignature clients. + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); + + if (time == 0xffffffff) { + if (client == kInventorySignature) { + if (_middleBiochipTime != 0xffffffff) { + setMiddleMovieTime(kBiochipSignature, _middleBiochipTime); + } else { + _middleAreaMovie.hide(); + _middleAreaOwner = kNoClientSignature; + } + } else { // client == kBiochipSignature + if (_middleInventoryTime != 0xffffffff) { + setMiddleMovieTime(kInventorySignature, _middleInventoryTime); + } else { + _middleAreaMovie.hide(); + _middleAreaOwner = kNoClientSignature; + } + } + } else { + setMiddleMovieTime(client, time); + } + break; + case kRightAreaSignature: + // Only support kBiochipSignature client. + _rightAreaMovie.setSegment(0, _rightAreaMovie.getDuration()); + + if (time == 0xffffffff) { + _rightAreaMovie.hide(); + _rightAreaOwner = kNoClientSignature; + } else { + setRightMovieTime(time); + } + break; + } +} + +// Plays a sequence on an area. When the sequence ends, the previous image +// is restored. +// Also, is input disabled or not? +// Easy answer: yes. + +// There are only so many legal combinations of client/area. +// Here is the list of supported pairs: +// kBiochipSignature kMiddleAreaSignature +// kBiochipSignature kRightAreaSignature +// kInventorySignature kMiddleAreaSignature + +void AIArea::playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lockAIOut(); + + switch (area) { + case kLeftAreaSignature: + break; + case kMiddleAreaSignature: + if (_middleAreaOwner == kInventorySignature) + _middleInventoryTime = _middleAreaMovie.getTime(); + else if (_middleAreaOwner == kBiochipSignature) + _middleBiochipTime = _middleAreaMovie.getTime(); + + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(start, stop); + _middleAreaMovie.setTime(start); + _middleAreaMovie.show(); + _middleAreaMovie.start(); + vm->_cursor->hide(); + + while (_middleAreaMovie.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _middleAreaMovie.stop(); + vm->_cursor->hideUntilMoved(); + + if (_middleAreaOwner == kInventorySignature) + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleInventoryTime); + else if (_middleAreaOwner == kBiochipSignature) + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleBiochipTime); + else + setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, 0xffffffff); + break; + case kRightAreaSignature: + _rightBiochipTime = _rightAreaMovie.getTime(); + _rightAreaMovie.setSegment(start, stop); + _rightAreaMovie.setTime(start); + _rightAreaMovie.show(); + _rightAreaMovie.start(); + vm->_cursor->hide(); + + while (_rightAreaMovie.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _rightAreaMovie.stop(); + vm->_cursor->hideUntilMoved(); + setAIAreaToTime(_rightAreaOwner, kRightAreaSignature, _rightBiochipTime); + break; + } + + unlockAI(); +} + +bool AIArea::playAIMovie(const LowerAreaSignature area, const Common::String &movieName, bool keepLastFrame, const InputBits interruptFilter) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lockAIOut(); + + InputDevice.waitInput(interruptFilter); + if (_AIMovie.isMovieValid()) + _AIMovie.releaseMovie(); + + _AIMovie.shareSurface(this); + _AIMovie.initFromMovieFile(movieName); + + if (area == kLeftAreaSignature) { + _AIMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); + _leftAreaMovie.hide(); + } else { + _AIMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); + _AIMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); + _rightAreaMovie.hide(); + } + + _AIMovie.setTime(0); + _AIMovie.startDisplaying(); + _AIMovie.show(); + _AIMovie.redrawMovieWorld(); + _AIMovie.setVolume(vm->getSoundFXLevel()); + _AIMovie.start(); + vm->_cursor->hide(); + + bool result = true; + bool saveAllowed = vm->swapSaveAllowed(false); + bool openAllowed = vm->swapLoadAllowed(false); + + while (_AIMovie.isRunning()) { + Input input; + InputDevice.getInput(input, interruptFilter); + + if (input.anyInput() || vm->shouldQuit() || vm->saveRequested() || vm->loadRequested()) { + result = false; + break; + } + + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + _AIMovie.stop(); + + vm->swapSaveAllowed(saveAllowed); + vm->swapLoadAllowed(openAllowed); + + // This used to keep the last frame up even if the movie was interrupted. + // However, this only occurs in the recalibration, where interruption means skip the + // whole thing, so skipping causes the AI to go away even when keepLastFrame is true. + + if (!(result && keepLastFrame)) { + _AIMovie.stopDisplaying(); + _AIMovie.releaseMovie(); + + if (area == kLeftAreaSignature) { + _leftAreaMovie.setTime(_leftInventoryTime); + _leftAreaMovie.show(); + _leftAreaMovie.redrawMovieWorld(); + } else { + _rightAreaMovie.setTime(_rightBiochipTime); + _rightAreaMovie.show(); + _rightAreaMovie.redrawMovieWorld(); + } + } + + vm->_cursor->hideUntilMoved(); + unlockAI(); + return result; +} + +// Only implemented for kMiddleAreaSignature, kInventorySignature +void AIArea::loopAIAreaSequence(const LowerClientSignature owner, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) { + if (area == kMiddleAreaSignature && owner == kInventorySignature && owner == _middleAreaOwner) { + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setSegment(start, stop); + _middleAreaMovie.setFlags(kLoopTimeBase); + _middleAreaMovie.setTime(start); + _middleAreaMovie.show(); + _middleAreaMovie.start(); + } +} + +// Only called by kInventorySignature. +void AIArea::setLeftMovieTime(const TimeValue time) { + if (!_AIMovie.isSurfaceValid()) { + _leftAreaMovie.setTime(time); + _leftAreaMovie.show(); + _leftAreaMovie.redrawMovieWorld(); + } + + _leftAreaOwner = kInventorySignature; + _leftInventoryTime = time; +} + +void AIArea::setMiddleMovieTime(const LowerClientSignature client, const TimeValue time) { + if (client == kInventorySignature) { + _middleInventoryTime = time; + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip && currentBiochip->isSelected()) + currentBiochip->giveUpSharedArea(); + } + } else { + _middleBiochipTime = time; + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->isSelected()) + currentItem->giveUpSharedArea(); + } + } + + _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); + _middleAreaMovie.stop(); + _middleAreaMovie.setFlags(0); + _middleAreaMovie.setTime(time); + _middleAreaMovie.show(); + _middleAreaMovie.redrawMovieWorld(); + _middleAreaOwner = client; +} + +// Only called by kBiochipSignature. +void AIArea::setRightMovieTime(const TimeValue time) { + if (!_AIMovie.isSurfaceValid()) { + // Can't do it when the AI movie is up... + _rightAreaMovie.setTime(time); + _rightAreaMovie.show(); + _rightAreaMovie.redrawMovieWorld(); + } + + _rightAreaOwner = kBiochipSignature; + _rightBiochipTime = time; +} + +void AIArea::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (JMPPPInput::isToggleAIMiddleInput(input)) + toggleMiddleAreaOwner(); + else + InputHandler::handleInput(input, cursorSpot); +} + +void AIArea::toggleMiddleAreaOwner() { + if (_middleAreaOwner == kInventorySignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip) { + setMiddleMovieTime(kBiochipSignature, currentBiochip->getSharedAreaTime()); + currentBiochip->takeSharedArea(); + } + } else if (_middleAreaOwner == kBiochipSignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem) { + setMiddleMovieTime(kInventorySignature, currentItem->getSharedAreaTime()); + currentItem->takeSharedArea(); + } + } +} + +void AIArea::activateHotspots() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + if (currentBiochip) + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->activateAIHotspots(); + break; + case kPegasusBiochip: + if (!vm->isDemo()) + ((PegasusChip *)currentBiochip)->activatePegasusHotspots(); + break; + case kOpticalBiochip: + ((OpticalChip *)currentBiochip)->activateOpticalHotspots(); + break; + } + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + if (currentItem && currentItem->getObjectID() == kAirMask) + ((AirMask *)currentItem)->activateAirMaskHotspots(); + } + + InputHandler::activateHotspots(); +} + +void AIArea::clickInHotspot(const Input &input, const Hotspot *hotspot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + bool handled = false; + + if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (currentBiochip) { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + if ((hotspot->getHotspotFlags() & kAIBiochipSpotFlag) != 0) { + ((AIChip *)currentBiochip)->clickInAIHotspot(hotspot->getObjectID()); + handled = true; + } + break; + case kPegasusBiochip: + if (!vm->isDemo() && ((hotspot->getHotspotFlags() & kPegasusBiochipSpotFlag) != 0)) { + ((PegasusChip *)currentBiochip)->clickInPegasusHotspot(); + handled = true; + } + break; + case kOpticalBiochip: + if ((hotspot->getHotspotFlags() & kOpticalBiochipSpotFlag) != 0) { + ((OpticalChip *)currentBiochip)->clickInOpticalHotspot(hotspot->getObjectID()); + handled = true; + } + break; + } + } + } else if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + + if (currentItem) { + switch (currentItem->getObjectID()) { + case kAirMask: + if ((hotspot->getHotspotFlags() & kAirMaskSpotFlag) != 0) { + ((AirMask *)currentItem)->clickInAirMaskHotspot(); + handled = true; + } + break; + } + } + } + + if (!handled) + InputHandler::clickInHotspot(input, hotspot); +} + +void AIArea::lockAIOut() { + if (_lockCount == 0) + stopIdling(); + + _lockCount++; +} + +void AIArea::unlockAI() { + if (_lockCount > 0) { + _lockCount--; + if (_lockCount == 0) + startIdling(); + } +} + +void AIArea::forceAIUnlocked() { + if (_lockCount > 0) { + _lockCount = 1; + unlockAI(); + } +} + +void AIArea::checkRules() { + if (_lockCount == 0 && ((PegasusEngine *)g_engine)->playerAlive()) + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + if ((*it)->fireRule()) + break; +} + +void AIArea::useIdleTime() { + checkRules(); +} + +void AIArea::addAIRule(AIRule *rule) { + _AIRules.push_back(rule); +} + +void AIArea::removeAllRules() { + for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) + delete *it; + + _AIRules.clear(); +} + +void AIArea::checkMiddleArea() { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (currentBiochip) { + if (_middleAreaOwner == kBiochipSignature) { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->setUpAIChip(); + break; + case kPegasusBiochip: + ((PegasusChip *)currentBiochip)->setUpPegasusChip(); + break; + } + } else { + switch (currentBiochip->getObjectID()) { + case kAIBiochip: + ((AIChip *)currentBiochip)->setUpAIChipRude(); + break; + case kPegasusBiochip: + ((PegasusChip *)currentBiochip)->setUpPegasusChipRude(); + break; + } + } + } +} + +TimeValue AIArea::getBigInfoTime() { + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + return currentItem->getInfoLeftTime(); + } else if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + return currentBiochip->getInfoLeftTime(); + } + + return 0xffffffff; +} + +void AIArea::getSmallInfoSegment(TimeValue &start, TimeValue &stop) { + if (_middleAreaOwner == kInventorySignature) { + InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + currentItem->getInfoRightTimes(start, stop); + } else if (_middleAreaOwner == kBiochipSignature) { + BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + currentBiochip->getInfoRightTimes(start, stop); + } else { + start = 0xffffffff; + stop = 0xffffffff; + } +} + +LowerClientSignature AIArea::getMiddleAreaOwner() { + return _middleAreaOwner; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_area.h b/engines/pegasus/ai/ai_area.h new file mode 100644 index 0000000000..806e6ef6bb --- /dev/null +++ b/engines/pegasus/ai/ai_area.h @@ -0,0 +1,172 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_AI_AIAREA_H +#define PEGASUS_AI_AIAREA_H + +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/timers.h" +#include "pegasus/ai/ai_rule.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +/* + + The AI area is the area at the bottom of the screen. There are three areas within + the AI area: + 1) the inventory/AI help area + 2) the middle area + 3) the biochip display area + + Area 1 is used for displaying the current inventory item. When the player changes the + current item, either by selecting a new one in the inventory list or by picking + up a new item, area 1 updates to show the new item. + + If the AI decides to give a message, the AI's head temporarily takes over area 1 + for the duration of the message, then goes away, returning the area to the current + inventory item. + + Area 2 is used to display the current inventory item's state, the current biochip's + state, and any extra information from the AI. The contention for this area is + resolved as follows: + -- If the AI needs to use the area while giving a message in area 1, it takes over + area 2 for the duration of its message. +*** This is not true. + -- If the player selects a new inventory item, the inventory item's state gets + displayed immediately. + -- If the player selects a new biochip, the biochip's state info gets displayed + immediately. + -- If any auto drawing is to occur, it seizes the area as soon as the drawing is + to occur. For example, the mapping biochip does auto drawing every time the + player takes a step. The only exception to this rule is if the AI is presenting + a warning. When the AI seizes areas 1 and 2, it preempts all other uses. + Some inventory items and biochips can cause arbitrary drawing to occur in this area + at arbitrary times. The map biochip is one example which causes drawing when the + player takes a step. Another example is the poison gas canister, which flashes in + this area to indicate a dangerous compound. + + Area 3 is used to display the current biochip. When the player changes the current + biochip, either by selecting a new one from the biochip list or by picking up a + new one, area 3 updates to show the new item. In addition, some biochips can play + animation in this area. + +*/ + +namespace Pegasus { + +class AIArea : public Surface, public Idler, public InputHandler { +public: + AIArea(InputHandler *); + virtual ~AIArea(); + + void writeAIRules(Common::WriteStream *stream); + void readAIRules(Common::ReadStream *stream); + + void initAIArea(); + + void saveAIState(); + void restoreAIState(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + + void setAIVolume(const uint16); + + // There are only so many legal combinations of client/area. + // Here is the list of supported pairs: + // kInventorySignature kLeftAreaSignature + // kInventorySignature kMiddleAreaSignature + // kBiochipSignature kMiddleAreaSignature + // kBiochipSignature kRightAreaSignature + // kAISignature kLeftAreaSignature + // Further, the kAISignature never sets a static frame time in the left area, + // but only plays a sequence from the AI movie. + void setAIAreaToTime(const LowerClientSignature, const LowerAreaSignature, const TimeValue); + + // The "Play" functions play the requested sequence synchronously. + void playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature, const TimeValue, const TimeValue); + + // For PlayAIMovie, it is assumed that the client is the AI itself. + // This is used to play AI messages as well as Optical Memory video. + // Returns true if the movie played all the way through, false if it was interrupted. + bool playAIMovie(const LowerAreaSignature, const Common::String &movieName, bool keepLastFrame, const InputBits); + + // Loop the requested sequence indefinitely. + void loopAIAreaSequence(const LowerClientSignature, const LowerAreaSignature, const TimeValue, const TimeValue); + + void addAIRule(AIRule *); + + // Remove and delete all rules. + void removeAllRules(); + + void lockAIOut(); + void unlockAI(); + void forceAIUnlocked(); + + void checkMiddleArea(); + void checkRules(); + + LowerClientSignature getMiddleAreaOwner(); + void toggleMiddleAreaOwner(); + + TimeValue getBigInfoTime(); + void getSmallInfoSegment(TimeValue &, TimeValue &); + +protected: + void useIdleTime(); + + void setLeftMovieTime(const TimeValue); + void setMiddleMovieTime(const LowerClientSignature, const TimeValue); + void setRightMovieTime(const TimeValue); + + Movie _leftAreaMovie; + Movie _middleAreaMovie; + Movie _rightAreaMovie; + Movie _AIMovie; + + LowerClientSignature _leftAreaOwner; + LowerClientSignature _middleAreaOwner; + LowerClientSignature _rightAreaOwner; + + TimeValue _leftInventoryTime; + TimeValue _middleInventoryTime; + TimeValue _middleBiochipTime; + TimeValue _rightBiochipTime; + + AIRuleList _AIRules; + + uint _lockCount; +}; + +extern AIArea *g_AIArea; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_condition.cpp b/engines/pegasus/ai/ai_condition.cpp new file mode 100644 index 0000000000..9fc9272566 --- /dev/null +++ b/engines/pegasus/ai/ai_condition.cpp @@ -0,0 +1,290 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +AIOneChildCondition::AIOneChildCondition(AICondition *child) { + _child = child; +} + +AIOneChildCondition::~AIOneChildCondition() { + delete _child; +} + +void AIOneChildCondition::writeAICondition(Common::WriteStream *stream) { + if (_child) + _child->writeAICondition(stream); +} + +void AIOneChildCondition::readAICondition(Common::ReadStream *stream) { + if (_child) + _child->readAICondition(stream); +} + +AITwoChildrenCondition::AITwoChildrenCondition(AICondition *leftChild, AICondition *rightChild) { + _leftChild = leftChild; + _rightChild = rightChild; +} + +AITwoChildrenCondition::~AITwoChildrenCondition() { + delete _leftChild; + delete _rightChild; +} + +void AITwoChildrenCondition::writeAICondition(Common::WriteStream *stream) { + if (_leftChild) + _leftChild->writeAICondition(stream); + + if (_rightChild) + _rightChild->writeAICondition(stream); +} + +void AITwoChildrenCondition::readAICondition(Common::ReadStream *stream) { + if (_leftChild) + _leftChild->readAICondition(stream); + + if (_rightChild) + _rightChild->readAICondition(stream); +} + +AINotCondition::AINotCondition(AICondition* child) : AIOneChildCondition(child) { +} + +bool AINotCondition::fireCondition() { + return _child && !_child->fireCondition(); +} + +AIAndCondition::AIAndCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) { +} + +bool AIAndCondition::fireCondition() { + return _leftChild && _leftChild->fireCondition() && _rightChild && _rightChild->fireCondition(); +} + +AIOrCondition::AIOrCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) { +} + +bool AIOrCondition::fireCondition() { + return (_leftChild && _leftChild->fireCondition()) || (_rightChild && _rightChild->fireCondition()); +} + +AITimerCondition::AITimerCondition(const TimeValue time, const TimeScale scale, const bool shouldStartTimer) { + _timerFuse.primeFuse(time, scale); + _timerFuse.setFunctor(new Common::Functor0Mem<void, AITimerCondition>(this, &AITimerCondition::fire)); + _fired = false; + + if (shouldStartTimer) + startTimer(); +} + +void AITimerCondition::startTimer() { + _fired = false; + _timerFuse.lightFuse(); +} + +void AITimerCondition::stopTimer() { + _timerFuse.stopFuse(); +} + +void AITimerCondition::writeAICondition(Common::WriteStream *stream) { + stream->writeByte(_timerFuse.isFuseLit()); + stream->writeByte(_fired); + stream->writeUint32BE(_timerFuse.getTimeRemaining()); + stream->writeUint32BE(_timerFuse.getFuseScale()); +} + +void AITimerCondition::readAICondition(Common::ReadStream *stream) { + bool running = stream->readByte(); + _fired = stream->readByte(); + TimeValue time = stream->readUint32BE(); + TimeScale scale = stream->readUint32BE(); + + _timerFuse.stopFuse(); + _timerFuse.primeFuse(time, scale); + + if (running) + _timerFuse.lightFuse(); +} + +bool AITimerCondition::fireCondition() { + return _fired; +} + +void AITimerCondition::fire() { + _fired = true; +} + +AILocationCondition::AILocationCondition(uint32 maxLocations) { + _numLocations = 0; + _maxLocations = maxLocations; + _locations = new RoomViewID[maxLocations]; +} + +AILocationCondition::~AILocationCondition() { + delete[] _locations; +} + +void AILocationCondition::addLocation(const RoomViewID location) { + if (_numLocations < _maxLocations) + _locations[_numLocations++] = location; +} + +bool AILocationCondition::fireCondition() { + RoomViewID test = GameState.getCurrentRoomAndView(), *p; + uint32 i; + + for (i = 0, p = _locations; i < _numLocations; i++, p++) { + if (test == *p) { + *p = MakeRoomView(kNoRoomID, kNoDirection); + return true; + } + } + + return false; +} + +void AILocationCondition::writeAICondition(Common::WriteStream *stream) { + stream->writeUint32BE(_maxLocations); + stream->writeUint32BE(_numLocations); + + uint32 i; + RoomViewID *p; + for (i = 0, p = _locations; i < _numLocations; i++, p++) + stream->writeUint32BE(*p); +} + +void AILocationCondition::readAICondition(Common::ReadStream *stream) { + uint32 maxLocations = stream->readUint32BE(); + + if (_maxLocations != maxLocations) { + delete[] _locations; + _locations = new RoomViewID[maxLocations]; + _maxLocations = maxLocations; + } + + _numLocations = stream->readUint32BE(); + + uint32 i; + RoomViewID *p; + for (i = 0, p = _locations; i < _numLocations; i++, p++) + *p = stream->readUint32BE(); +} + +AIDoorOpenedCondition::AIDoorOpenedCondition(RoomViewID doorLocation) { + _doorLocation = doorLocation; +} + +bool AIDoorOpenedCondition::fireCondition() { + return GameState.getCurrentRoomAndView() == _doorLocation && GameState.isCurrentDoorOpen(); +} + +AIHasItemCondition::AIHasItemCondition(const ItemID item) { + _item = item; +} + +bool AIHasItemCondition::fireCondition() { + return _item == kNoItemID || GameState.isTakenItemID(_item); +} + +AIDoesntHaveItemCondition::AIDoesntHaveItemCondition(const ItemID item) { + _item = item; +} + +bool AIDoesntHaveItemCondition::fireCondition() { + return _item == kNoItemID || !GameState.isTakenItemID(_item); +} + +AICurrentItemCondition::AICurrentItemCondition(const ItemID item) { + _item = item; +} + +bool AICurrentItemCondition::fireCondition() { + InventoryItem *item = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + + if (_item == kNoItemID) + return item == 0; + + return item != 0 && item->getObjectID() == _item; +} + +AICurrentBiochipCondition::AICurrentBiochipCondition(const ItemID biochip) { + _biochip = biochip; +} + +bool AICurrentBiochipCondition::fireCondition() { + BiochipItem *biochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + + if (_biochip == kNoItemID) + return biochip == 0; + + return biochip != 0 && biochip->getObjectID() == _biochip; +} + +AIItemStateCondition::AIItemStateCondition(const ItemID item, const ItemState state) { + _item = item; + _state = state; +} + +bool AIItemStateCondition::fireCondition() { + Item *item = g_allItems.findItemByID(_item); + return item != 0 && item->getItemState() == _state; +} + +AIEnergyMonitorCondition::AIEnergyMonitorCondition(const int32 energyThreshold) { + _energyThreshold = energyThreshold; +} + +bool AIEnergyMonitorCondition::fireCondition() { + return g_energyMonitor != 0 && g_energyMonitor->getCurrentEnergy() < _energyThreshold; +} + +AILastExtraCondition::AILastExtraCondition(const ExtraID lastExtra) { + _lastExtra = lastExtra; +} + +bool AILastExtraCondition::fireCondition() { + return g_neighborhood && (ExtraID)g_neighborhood->getLastExtra() == _lastExtra; +} + +AICondition *makeLocationAndDoesntHaveItemCondition(const RoomID room, const DirectionConstant direction, const ItemID item) { + AILocationCondition *location = new AILocationCondition(1); + location->addLocation(MakeRoomView(room, direction)); + + AIDoesntHaveItemCondition *doesntHaveItem = new AIDoesntHaveItemCondition(item); + + return new AIAndCondition(location, doesntHaveItem); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_condition.h b/engines/pegasus/ai/ai_condition.h new file mode 100644 index 0000000000..ae85168a36 --- /dev/null +++ b/engines/pegasus/ai/ai_condition.h @@ -0,0 +1,287 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_AI_AICONDITION_H +#define PEGASUS_AI_AICONDITION_H + +#include "pegasus/timers.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +///////////////////////////////////////////// +// +// AICondition + +class AICondition { +public: + AICondition() {} + virtual ~AICondition() {} + + virtual bool fireCondition() = 0; + + // Only need these for conditions that are dynamic, like timer conditions... + // other conditions, like item related conditions, which don't change during + // the run of an environment, are completely initted when the environment + // is created. + virtual void writeAICondition(Common::WriteStream *) {} + virtual void readAICondition(Common::ReadStream *) {} +}; + +///////////////////////////////////////////// +// +// AIOneChildCondition + +class AIOneChildCondition : public AICondition { +public: + AIOneChildCondition(AICondition *); + virtual ~AIOneChildCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + AICondition *_child; +}; + +///////////////////////////////////////////// +// +// AITwoChildrenCondition + +class AITwoChildrenCondition : public AICondition { +public: + AITwoChildrenCondition(AICondition *, AICondition *); + virtual ~AITwoChildrenCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + AICondition *_leftChild, *_rightChild; +}; + +///////////////////////////////////////////// +// +// AINotCondition + +class AINotCondition : public AIOneChildCondition { +public: + AINotCondition(AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AIAndCondition + +class AIAndCondition : public AITwoChildrenCondition { +public: + AIAndCondition(AICondition *, AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AIOrCondition + +class AIOrCondition : public AITwoChildrenCondition { +public: + AIOrCondition(AICondition *, AICondition *); + + virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AITimerCondition + +class AITimerCondition : public AICondition { +public: + AITimerCondition(const TimeValue, const TimeScale, const bool); + + void startTimer(); + void stopTimer(); + + virtual bool fireCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + void fire(); + + FuseFunction _timerFuse; + bool _fired; +}; + +///////////////////////////////////////////// +// +// AILocationCondition + +class AILocationCondition : public AICondition { +public: + AILocationCondition(uint32); + virtual ~AILocationCondition(); + + void addLocation(RoomViewID); + virtual bool fireCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + uint32 _numLocations, _maxLocations; + RoomViewID *_locations; +}; + +///////////////////////////////////////////// +// +// AIDoorOpenedCondition + +class AIDoorOpenedCondition : public AICondition { +public: + AIDoorOpenedCondition(RoomViewID); + virtual ~AIDoorOpenedCondition() {} + + virtual bool fireCondition(); + +protected: + RoomViewID _doorLocation; +}; + +///////////////////////////////////////////// +// +// AIHasItemCondition + +class AIHasItemCondition : public AICondition { +public: + AIHasItemCondition(const ItemID); + + virtual bool fireCondition(); + +protected: + ItemID _item; +}; + +///////////////////////////////////////////// +// +// AIDoesntHaveItemCondition + +class AIDoesntHaveItemCondition : public AICondition { +public: + AIDoesntHaveItemCondition(const ItemID); + + virtual bool fireCondition(); + +protected: + ItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentItemCondition + +class AICurrentItemCondition : public AICondition { +public: + AICurrentItemCondition(const ItemID); + + virtual bool fireCondition(); + +protected: + ItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentBiochipCondition + +class AICurrentBiochipCondition : public AICondition { +public: + AICurrentBiochipCondition(const ItemID); + + virtual bool fireCondition(); + +protected: + ItemID _biochip; +}; + +///////////////////////////////////////////// +// +// AIItemStateCondition + +class AIItemStateCondition : public AICondition { +public: + AIItemStateCondition(const ItemID, const ItemState); + + virtual bool fireCondition(); + +protected: + ItemID _item; + ItemState _state; +}; + +///////////////////////////////////////////// +// +// AIEnergyMonitorCondition + +class AIEnergyMonitorCondition : public AICondition { +public: + AIEnergyMonitorCondition(const int32); + + virtual bool fireCondition(); + +protected: + int32 _energyThreshold; +}; + +///////////////////////////////////////////// +// +// AILastExtraCondition + +class AILastExtraCondition : public AICondition { +public: + AILastExtraCondition(const ExtraID); + + virtual bool fireCondition(); + +protected: + ExtraID _lastExtra; +}; + +///////////////////////////////////////////// +// +// Helper functions + +AICondition *makeLocationAndDoesntHaveItemCondition(const RoomID room, const DirectionConstant direction, const ItemID item); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_rule.cpp b/engines/pegasus/ai/ai_rule.cpp new file mode 100644 index 0000000000..3aaa530a4a --- /dev/null +++ b/engines/pegasus/ai/ai_rule.cpp @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/ai/ai_rule.h" + +namespace Pegasus { + +bool AIRule::fireRule() { + if (_ruleActive && _ruleCondition && _ruleAction && _ruleCondition->fireCondition()) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + _ruleAction->performAIAction(this); + + if (--_ruleAction->_actionCount <= 0) + deactivateRule(); + + if (g_AIArea) + g_AIArea->unlockAI(); + + return true; + } + + return false; +} + +void AIRule::writeAIRule(Common::WriteStream *stream) { + stream->writeByte(_ruleActive); + + if (_ruleCondition) + _ruleCondition->writeAICondition(stream); +} + +void AIRule::readAIRule(Common::ReadStream *stream) { + _ruleActive = stream->readByte(); + + if (_ruleCondition) + _ruleCondition->readAICondition(stream); +} + +void AIRuleList::writeAIRules(Common::WriteStream *stream) { + for (AIRuleList::iterator it = begin(); it != end(); it++) + (*it)->writeAIRule(stream); +} + +void AIRuleList::readAIRules(Common::ReadStream *stream) { + for (AIRuleList::iterator it = begin(); it != end(); it++) + (*it)->readAIRule(stream); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_rule.h b/engines/pegasus/ai/ai_rule.h new file mode 100644 index 0000000000..aa2ca07332 --- /dev/null +++ b/engines/pegasus/ai/ai_rule.h @@ -0,0 +1,86 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_AI_AIRULE_H +#define PEGASUS_AI_AIRULE_H + +#include "common/list.h" + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_condition.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class AICondition; +class AIAction; + +class AIRule { +public: + AIRule(AICondition *condition, AIAction *rule) { + _ruleCondition = condition; + _ruleAction = rule; + _ruleActive = true; + } + + ~AIRule() { + if (_ruleCondition) + delete _ruleCondition; + + if (_ruleAction) + delete _ruleAction; + } + + bool fireRule(); + + void activateRule() { _ruleActive = true; } + void deactivateRule() { _ruleActive = false; } + bool isRuleActive() { return _ruleActive; } + + void writeAIRule(Common::WriteStream *); + void readAIRule(Common::ReadStream *); + +protected: + AICondition *_ruleCondition; + AIAction *_ruleAction; + bool _ruleActive; +}; + +class AIRuleList : public Common::List<AIRule *> { +public: + AIRuleList() {} + ~AIRuleList() {} + + void writeAIRules(Common::WriteStream *); + void readAIRules(Common::ReadStream *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/compass.cpp b/engines/pegasus/compass.cpp new file mode 100644 index 0000000000..5de8b91b11 --- /dev/null +++ b/engines/pegasus/compass.cpp @@ -0,0 +1,82 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/compass.h" + +namespace Pegasus { + +Compass *g_compass = 0; + +Compass::Compass() : FaderAnimation(kCompassID) { + // Initialize it to east... + setFaderValue(90); + g_compass = this; +} + +Compass::~Compass() { + g_compass = 0; +} + +void Compass::initCompass() { + if (!isCompassValid()) { + Common::Rect r; + _compassImage.initFromPICTFile("Images/Compass/Compass"); + _compassImage.getSurfaceBounds(r); + r.right = kCompassWidth; + setBounds(r); + } +} + +void Compass::deallocateCompass() { + _compassImage.deallocateSurface(); +} + +void Compass::setFaderValue(const int32 angle) { + int16 a = angle % 360; + + if (a < 0) + a += 360; + + FaderAnimation::setFaderValue(a); +} + +void Compass::draw(const Common::Rect &r1) { + if (_compassImage.isSurfaceValid()) { + Common::Rect bounds; + getBounds(bounds); + + Common::Rect r2; + _compassImage.getSurfaceBounds(r2); + + CoordType width = r2.width(); + CoordType offsetH = width / 10 - bounds.width() / 2 + (getFaderValue() * width) / 450 - bounds.left; + CoordType offsetV = -bounds.top; + r2 = r1; + r2.translate(offsetH, offsetV); + _compassImage.drawImage(r2, r1); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/compass.h b/engines/pegasus/compass.h new file mode 100644 index 0000000000..67a8e06294 --- /dev/null +++ b/engines/pegasus/compass.h @@ -0,0 +1,58 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_COMPASS_H +#define PEGASUS_COMPASS_H + +#include "pegasus/fader.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +// Compass is defined with 0 as north, 90 east, 180 south, 270 west. +// Clockwise rotation increases the angle, counterclockwise rotation decreases the angle. + +class Compass : public FaderAnimation { +public: + Compass(); + virtual ~Compass(); + + void initCompass(); + void deallocateCompass(); + bool isCompassValid() const { return _compassImage.isSurfaceValid(); } + + void setFaderValue(const int32); + +protected: + void draw(const Common::Rect &); + + Frame _compassImage; +}; + +extern Compass *g_compass; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/console.cpp b/engines/pegasus/console.cpp new file mode 100644 index 0000000000..64bd0ba5f2 --- /dev/null +++ b/engines/pegasus/console.cpp @@ -0,0 +1,102 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/console.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +PegasusConsole::PegasusConsole(PegasusEngine *vm) : GUI::Debugger(), _vm(vm) { + DCmd_Register("die", WRAP_METHOD(PegasusConsole, Cmd_Die)); + + // These functions are non-demo specific + if (!_vm->isDemo()) + DCmd_Register("jump", WRAP_METHOD(PegasusConsole, Cmd_Jump)); +} + +PegasusConsole::~PegasusConsole() { +} + +bool PegasusConsole::Cmd_Die(int argc, const char **argv) { + if (argc == 1) { + DebugPrintf("Usage: die <death reason>\n"); + return true; + } + + int reason = atoi(argv[1]); + + bool invalidReason = (reason == 0 || reason > kPlayerWonGame); + + if (!invalidReason && _vm->isDemo()) + invalidReason = (reason != kDeathFallOffCliff) && (reason != kDeathEatenByDinosaur) && + (reason != kDeathStranded) && (reason != kPlayerWonGame); + + + if (invalidReason) { + DebugPrintf("Invalid death reason %d\n", reason); + return true; + } + + _vm->die(atoi(argv[1])); + return false; +} + +bool PegasusConsole::Cmd_Jump(int argc, const char **argv) { + if (!g_interface) { + // TODO + DebugPrintf("Cannot jump without interface set up\n"); + return true; + } + + // TODO: Default room/direction for each neighborhood + + if (argc < 4) { + DebugPrintf("Usage: jump <neighborhood> <room> <direction>\n"); + return true; + } + + NeighborhoodID neighborhood = (NeighborhoodID)atoi(argv[1]); + RoomID room = (RoomID)atoi(argv[2]); + DirectionConstant direction = (DirectionConstant)atoi(argv[3]); + + if ((neighborhood < kCaldoriaID || neighborhood > kNoradDeltaID || neighborhood == kFinalTSAID) && + neighborhood != kNoradSubChaseID) { + DebugPrintf("Invalid neighborhood %d", neighborhood); + return true; + } + + // No real way to check room validity at this point + + if (direction > kWest) { + DebugPrintf("Invalid direction %d", direction); + return true; + } + + // Here we go! + // TODO: Can't clear menu since the engine is paused + _vm->jumpToNewEnvironment(neighborhood, room, direction); + return false; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/console.h b/engines/pegasus/console.h new file mode 100644 index 0000000000..f00ee25294 --- /dev/null +++ b/engines/pegasus/console.h @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_CONSOLE_H +#define PEGASUS_CONSOLE_H + +#include "gui/debugger.h" + +namespace Pegasus { + +class PegasusEngine; + +class PegasusConsole : public GUI::Debugger { +public: + PegasusConsole(PegasusEngine *vm); + virtual ~PegasusConsole(); + +private: + bool Cmd_Die(int argc, const char **argv); + bool Cmd_Jump(int argc, const char **argv); + + PegasusEngine *_vm; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/constants.h b/engines/pegasus/constants.h new file mode 100644 index 0000000000..f81d2197ac --- /dev/null +++ b/engines/pegasus/constants.h @@ -0,0 +1,729 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_CONSTANTS_H +#define PEGASUS_CONSTANTS_H + +#include "common/endian.h" +#include "common/rect.h" + +#include "pegasus/types.h" + +namespace Pegasus { + +// TODO: Organize these + +static const GameID kGameIDNothing = -1; + +static const ActorID kNoActorID = kGameIDNothing; +static const ActorID kPlayerID = 0; +static const ItemID kNoItemID = kGameIDNothing; +static const RoomID kNoRoomID = kGameIDNothing; +static const ExtraID kNoExtraID = 0xFFFFFFFF; +static const NeighborhoodID kNoNeighborhoodID = kGameIDNothing; +static const AlternateID kNoAlternateID = 0; +static const GameMenuCommand kMenuCmdNoCommand = 0; + +static const HotSpotActivationID kActivateHotSpotAlways = 0; +static const HotSpotActivationID kActivateHotSpotNever = -1; + +static const ItemState kNoItemState = -1; + +static const DirectionConstant kNoDirection = 0xFF; +static const DirectionConstant kNorth = 0; +static const DirectionConstant kSouth = 1; +static const DirectionConstant kEast = 2; +static const DirectionConstant kWest = 3; + +static const TurnDirection kNoTurn = 0xFF; +static const TurnDirection kTurnLeft = 0; +static const TurnDirection kTurnRight = 1; +static const TurnDirection kTurnUp = 2; +static const TurnDirection kTurnDown = 3; +static const TurnDirection kMaxTurns = 4; + +static const GameMode kNoMode = -1; +static const GameMode kModeNavigation = 0; +static const GameMode kLastGameShellMode = kModeNavigation; + +static const CanMoveForwardReason kCanMoveForward = 0; +static const CanMoveForwardReason kCantMoveBlocked = kCanMoveForward + 1; +static const CanMoveForwardReason kCantMoveDoorClosed = kCantMoveBlocked + 1; +static const CanMoveForwardReason kCantMoveDoorLocked = kCantMoveDoorClosed + 1; +static const CanMoveForwardReason kCantMoveLastReason = kCantMoveDoorLocked; + +static const CanTurnReason kCanTurn = 0; +static const CanTurnReason kCantTurnNoTurn = kCanTurn + 1; +static const CanTurnReason kCantTurnLastReason = kCantTurnNoTurn; + +static const CanOpenDoorReason kCanOpenDoor = 0; +static const CanOpenDoorReason kCantOpenNoDoor = kCanOpenDoor + 1; +static const CanOpenDoorReason kCantOpenLocked = kCantOpenNoDoor + 1; +static const CanOpenDoorReason kCantOpenAlreadyOpen = kCantOpenLocked + 1; +static const CanOpenDoorReason kCantOpenLastReason = kCantOpenAlreadyOpen; + +static const DisplayElementID kNoDisplayElement = -1; +static const DisplayElementID kHighestReservedElementID = -2; + +static const DisplayElementID kCursorID = kHighestReservedElementID; +static const DisplayElementID kLoadScreenID = kCursorID - 1; + +static const DisplayOrder kMinAvailableOrder = 0; +static const DisplayOrder kMaxAvailableOrder = 999998; +static const DisplayOrder kLoadScreenOrder = 900000; +static const DisplayOrder kCursorOrder = 1000000; + +static const HotSpotID kNoHotSpotID = -1; +static const HotSpotFlags kNoHotSpotFlags = 0; +static const HotSpotFlags kAllHotSpotFlags = ~kNoHotSpotFlags; + +static const NotificationFlags kNoNotificationFlags = 0; + +static const DisplayElementID kCurrentDragSpriteID = 1000; + +static const TimeScale kDefaultTimeScale = 600; + +// Ticks per second. + +static const TimeScale kOneTickPerSecond = 1; +static const TimeScale kTwoTicksPerSecond = 2; +static const TimeScale kFifteenTicksPerSecond = 15; +static const TimeScale kThirtyTicksPerSecond = 30; +static const TimeScale kSixtyTicksPerSecond = 60; +static const TimeScale kMovieTicksPerSecond = 600; + +// These times are in seconds. + +static const TimeValue kOneSecond = 1; +static const TimeValue kTwoSeconds = 2; +static const TimeValue kThreeSeconds = 3; +static const TimeValue kFourSeconds = 4; +static const TimeValue kFiveSeconds = 5; +static const TimeValue kSixSeconds = 6; +static const TimeValue kSevenSeconds = 7; +static const TimeValue kEightSeconds = 8; +static const TimeValue kNineSeconds = 9; +static const TimeValue kTenSeconds = 10; +static const TimeValue kElevenSeconds = 11; +static const TimeValue kTwelveSeconds = 12; +static const TimeValue kThirteenSeconds = 13; +static const TimeValue kFourteenSeconds = 14; +static const TimeValue kFifteenSeconds = 15; +static const TimeValue kSixteenSeconds = 16; +static const TimeValue kSeventeenSeconds = 17; +static const TimeValue kEighteenSeconds = 18; +static const TimeValue kNineteenSeconds = 19; +static const TimeValue kTwentySeconds = 20; +static const TimeValue kThirtySeconds = 30; +static const TimeValue kFortySeconds = 40; +static const TimeValue kFiftySeconds = 50; +static const TimeValue kSixtySeconds = 60; +static const TimeValue kOneMinute = 60; +static const TimeValue kTwoMinutes = kOneMinute * 2; +static const TimeValue kThreeMinutes = kOneMinute * 3; +static const TimeValue kFourMinutes = kOneMinute * 4; +static const TimeValue kFiveMinutes = kOneMinute * 5; +static const TimeValue kSixMinutes = kOneMinute * 6; +static const TimeValue kSevenMinutes = kOneMinute * 7; +static const TimeValue kEightMinutes = kOneMinute * 8; +static const TimeValue kNineMinutes = kOneMinute * 9; +static const TimeValue kTenMinutes = kOneMinute * 10; +static const TimeValue kElevenMinutes = kOneMinute * 11; +static const TimeValue kTwelveMinutes = kOneMinute * 12; +static const TimeValue kThirteenMinutes = kOneMinute * 13; +static const TimeValue kFourteenMinutes = kOneMinute * 14; +static const TimeValue kFifteenMinutes = kOneMinute * 15; +static const TimeValue kSixteenMinutes = kOneMinute * 16; +static const TimeValue kSeventeenMinutes = kOneMinute * 17; +static const TimeValue kEighteenMinutes = kOneMinute * 18; +static const TimeValue kNineteenMinutes = kOneMinute * 19; +static const TimeValue kTwentyMinutes = kOneMinute * 20; +static const TimeValue kThirtyMinutes = kOneMinute * 30; +static const TimeValue kFortyMinutes = kOneMinute * 40; +static const TimeValue kFiftyMinutes = kOneMinute * 50; +static const TimeValue kOneHour = kOneMinute * 60; +static const TimeValue kTwoHours = kOneHour * 2; + +// Common times. + +static const TimeValue kHalfSecondPerTwoTicks = kTwoTicksPerSecond / 2; +static const TimeValue kHalfSecondPerThirtyTicks = kThirtyTicksPerSecond / 2; +static const TimeValue kHalfSecondPerSixtyTicks = kSixtyTicksPerSecond / 2; + +static const TimeValue kOneSecondPerTwoTicks = kTwoTicksPerSecond; +static const TimeValue kOneSecondPerThirtyTicks = kThirtyTicksPerSecond; +static const TimeValue kOneSecondPerSixtyTicks = kSixtyTicksPerSecond; + +static const TimeValue kOneMinutePerFifteenTicks = kOneMinute * kFifteenTicksPerSecond; +static const TimeValue kFiveMinutesPerFifteenTicks = kFiveMinutes * kFifteenTicksPerSecond; +static const TimeValue kTenMinutesPerFifteenTicks = kTenMinutes * kFifteenTicksPerSecond; + +static const TimeValue kOneMinutePerThirtyTicks = kOneMinute * kThirtyTicksPerSecond; +static const TimeValue kFiveMinutesPerThirtyTicks = kFiveMinutes * kThirtyTicksPerSecond; +static const TimeValue kTenMinutesPerThirtyTicks = kTenMinutes * kThirtyTicksPerSecond; + +static const TimeValue kOneMinutePerSixtyTicks = kOneMinute * kSixtyTicksPerSecond; +static const TimeValue kFiveMinutesPerSixtyTicks = kFiveMinutes * kSixtyTicksPerSecond; +static const TimeValue kTenMinutesPerSixtyTicks = kTenMinutes * kSixtyTicksPerSecond; + +// Time in seconds you can hang around Caldoria without going to work... +static const TimeValue kLateWarning2TimeLimit = kFiveMinutes; +static const TimeValue kLateWarning3TimeLimit = kTenMinutes; + +static const TimeValue kSinclairShootsTimeLimit = kThreeMinutes; +static const TimeValue kCardBombCountDownTime = kTwelveSeconds; + +static const TimeValue kOxyMaskFullTime = kThirtyMinutes; + +static const TimeValue kTSAUncreatedTimeLimit = kFiveMinutes; +static const TimeValue kRipTimeLimit = kTenMinutesPerFifteenTicks; +static const TimeScale kRipTimeScale = kFifteenTicksPerSecond; + +static const TimeValue kIntroTimeOut = kThirtySeconds; + +static const TimeValue kMarsRobotPatienceLimit = kFifteenSeconds; +static const TimeValue kLockFreezeTimeLmit = kFifteenSeconds; +static const TimeValue kSpaceChaseTimeLimit = kTenMinutes; +static const TimeValue kVacuumSurvivalTimeLimit = kThirtySeconds; +static const TimeValue kColorMatchingTimeLimit = kFourMinutes; +static const TimeScale kJunkTimeScale = kFifteenTicksPerSecond; +static const TimeValue kJunkDropBaseTime = kFiveSeconds; +static const TimeValue kJunkDropSlopTime = kThreeSeconds; +static const TimeValue kJunkTravelTime = kTenSeconds * kJunkTimeScale; +static const TimeValue kCollisionReboundTime = kOneSecond * kJunkTimeScale; +static const TimeValue kWeaponReboundTime = kTwoSeconds * kJunkTimeScale; + +static const TimeValue kGawkAtRobotTime = kTenSeconds; +static const TimeValue kGawkAtRobotTime2 = kThirteenSeconds; +static const TimeValue kPlasmaImpactTime = kTwoSeconds; + +static const TimeValue kNoradAirMaskTimeLimit = kOneMinute + kFifteenSeconds; + +static const NotificationID kNeighborhoodNotificationID = 1; +static const NotificationID kLastNeighborhoodNotificationID = kNeighborhoodNotificationID; + +static const NotificationFlags kNeighborhoodMovieCompletedFlag = 1; +static const NotificationFlags kMoveForwardCompletedFlag = kNeighborhoodMovieCompletedFlag << 1; +static const NotificationFlags kStrideCompletedFlag = kMoveForwardCompletedFlag << 1; +static const NotificationFlags kTurnCompletedFlag = kStrideCompletedFlag << 1; +static const NotificationFlags kSpotCompletedFlag = kTurnCompletedFlag << 1; +static const NotificationFlags kDoorOpenCompletedFlag = kSpotCompletedFlag << 1; +static const NotificationFlags kExtraCompletedFlag = kDoorOpenCompletedFlag << 1; +static const NotificationFlags kSpotSoundCompletedFlag = kExtraCompletedFlag << 1; +static const NotificationFlags kDelayCompletedFlag = kSpotSoundCompletedFlag << 1; +static const NotificationFlags kActionRequestCompletedFlag = kDelayCompletedFlag << 1; +static const NotificationFlags kDeathExtraCompletedFlag = kActionRequestCompletedFlag << 1; +static const NotificationFlags kLastNeighborhoodNotificationFlag = kDeathExtraCompletedFlag; + +static const NotificationFlags kNeighborhoodFlags = kNeighborhoodMovieCompletedFlag | + kMoveForwardCompletedFlag | + kStrideCompletedFlag | + kTurnCompletedFlag | + kSpotCompletedFlag | + kDoorOpenCompletedFlag | + kExtraCompletedFlag | + kSpotSoundCompletedFlag | + kDelayCompletedFlag | + kActionRequestCompletedFlag | + kDeathExtraCompletedFlag; + +static const uint32 kPegasusPrimeCreator = MKTAG('J', 'P', 'P', 'P'); +static const uint32 kPegasusPrimeContinueType = MKTAG('P', 'P', 'C', 'T'); + +static const uint32 kPegasusPrimeDisk1GameType = MKTAG('P', 'P', 'G', '1'); +static const uint32 kPegasusPrimeDisk2GameType = MKTAG('P', 'P', 'G', '2'); +static const uint32 kPegasusPrimeDisk3GameType = MKTAG('P', 'P', 'G', '3'); +static const uint32 kPegasusPrimeDisk4GameType = MKTAG('P', 'P', 'G', '4'); + +// We only support one of the save versions; the rest are from betas +// and we are not supporting them. +static const uint32 kPegasusPrimeVersion = 0x00009019; + +static const char kNormalSave = 0; +static const char kContinueSave = 1; + +// Display IDs. + +static const DisplayElementID kNavMovieID = 1; +static const DisplayElementID kTurnPushID = 2; + +static const DisplayElementID kMaxGameShellDisplayID = kTurnPushID; + +// Display ordering. + +static const DisplayOrder kNavLayer = 10000; +static const DisplayOrder kNavMovieOrder = kNavLayer; +static const DisplayOrder kTurnPushOrder = kNavMovieOrder + 1; + +///////////////////////////////////////////// +// +// Display IDs. + +static const DisplayElementID kScreenDimmerID = kMaxGameShellDisplayID + 1; +static const DisplayElementID kInterface1ID = kScreenDimmerID + 1; +static const DisplayElementID kInterface2ID = kInterface1ID + 1; +static const DisplayElementID kInterface3ID = kInterface2ID + 1; +static const DisplayElementID kInterface4ID = kInterface3ID + 1; +static const DisplayElementID kDateID = kInterface4ID + 1; +static const DisplayElementID kCompassID = kDateID + 1; +static const DisplayElementID kInventoryPushID = kCompassID + 1; +static const DisplayElementID kInventoryLidID = kInventoryPushID + 1; +static const DisplayElementID kBiochipPushID = kInventoryLidID + 1; +static const DisplayElementID kBiochipLidID = kBiochipPushID + 1; +static const DisplayElementID kEnergyBarID = kBiochipLidID + 1; +static const DisplayElementID kWarningLightID = kEnergyBarID + 1; +static const DisplayElementID kAILeftAreaID = kWarningLightID + 1; +static const DisplayElementID kAIMiddleAreaID = kAILeftAreaID + 1; +static const DisplayElementID kAIRightAreaID = kAIMiddleAreaID + 1; +static const DisplayElementID kAIMovieID = kAIRightAreaID + 1; +static const DisplayElementID kInventoryDropHighlightID = kAIMovieID + 1; +static const DisplayElementID kBiochipDropHighlightID = kInventoryDropHighlightID + 1; + +static const DisplayElementID kDraggingSpriteID = 1000; + +static const DisplayElementID kCroppedMovieID = 2000; + +static const DisplayElementID kNeighborhoodDisplayID = 3000; + +static const DisplayElementID kItemPictureBaseID = 5000; + +static const CoordType kNavAreaLeft = 64; +static const CoordType kNavAreaTop = 64; + +static const CoordType kBackground1Left = 0; +static const CoordType kBackground1Top = 64; + +static const CoordType kBackground2Left = 0; +static const CoordType kBackground2Top = 0; + +static const CoordType kBackground3Left = 576; +static const CoordType kBackground3Top = 64; + +static const CoordType kBackground4Left = 0; +static const CoordType kBackground4Top = 320; + +static const CoordType kOverviewControllerLeft = 540; +static const CoordType kOverviewControllerTop = 348; + +static const CoordType kSwapLeft = 194; +static const CoordType kSwapTop = 116; + +static const CoordType kSwapHiliteLeft = 200; +static const CoordType kSwapHiliteTop = 206; + +static const CoordType kDateLeft = 136; +static const CoordType kDateTop = 44; + +static const CoordType kCompassLeft = 222; +static const CoordType kCompassTop = 42; +static const CoordType kCompassWidth = 92; + +static const CoordType kInventoryPushLeft = 74; +static const CoordType kInventoryPushTop = 92; + +static const CoordType kInventoryLidLeft = 74; +static const CoordType kInventoryLidTop = 316; + +static const CoordType kBiochipPushLeft = 362; +static const CoordType kBiochipPushTop = 192; + +static const CoordType kBiochipLidLeft = 362; +static const CoordType kBiochipLidTop = 316; + +static const CoordType kInventoryDropLeft = 0; +static const CoordType kInventoryDropTop = 320; +static const CoordType kInventoryDropRight = 232; +static const CoordType kInventoryDropBottom = 480; + +static const CoordType kBiochipDropLeft = 302; +static const CoordType kBiochipDropTop = 320; +static const CoordType kBiochipDropRight = 640; +static const CoordType kBiochipDropBottom = 480; + +static const CoordType kFinalMessageLeft = kInventoryPushLeft + 1; +static const CoordType kFinalMessageTop = kInventoryPushTop + 24; + +///////////////////////////////////////////// +// +// Notifications. + +static const NotificationID kJMPDCShellNotificationID = kLastNeighborhoodNotificationID + 1; +static const NotificationID kInterfaceNotificationID = kJMPDCShellNotificationID + 1; +static const NotificationID kAINotificationID = kInterfaceNotificationID + 1; +static const NotificationID kNoradNotificationID = kAINotificationID + 1; +static const NotificationID kNoradECRNotificationID = kNoradNotificationID + 1; +static const NotificationID kNoradFillingStationNotificationID = kNoradECRNotificationID + 1; +static const NotificationID kNoradPressureNotificationID = kNoradFillingStationNotificationID + 1; +static const NotificationID kNoradUtilityNotificationID = kNoradPressureNotificationID + 1; +static const NotificationID kNoradElevatorNotificationID = kNoradUtilityNotificationID + 1; +static const NotificationID kNoradSubPlatformNotificationID = kNoradElevatorNotificationID + 1; +static const NotificationID kSubControlNotificationID = kNoradSubPlatformNotificationID + 1; +static const NotificationID kNoradGreenBallNotificationID = kSubControlNotificationID + 1; +static const NotificationID kNoradGlobeNotificationID = kNoradGreenBallNotificationID + 1; +static const NotificationID kCaldoriaVidPhoneNotificationID = kNoradGlobeNotificationID + 1; +static const NotificationID kCaldoriaMessagesNotificationID = kCaldoriaVidPhoneNotificationID + 1; +static const NotificationID kCaldoriaBombTimerNotificationID = kCaldoriaMessagesNotificationID + 1; + +// Sent to the shell by fShellNotification. +static const NotificationFlags kGameStartingFlag = 1; +static const NotificationFlags kNeedNewJumpFlag = kGameStartingFlag << 1; +static const NotificationFlags kPlayerDiedFlag = kNeedNewJumpFlag << 1; + +static const NotificationFlags kJMPShellNotificationFlags = kGameStartingFlag | + kNeedNewJumpFlag | + kPlayerDiedFlag; + +// Sent to the interface. +static const NotificationFlags kInventoryLidOpenFlag = 1; +static const NotificationFlags kInventoryLidClosedFlag = kInventoryLidOpenFlag << 1; +static const NotificationFlags kInventoryDrawerUpFlag = kInventoryLidClosedFlag << 1; +static const NotificationFlags kInventoryDrawerDownFlag = kInventoryDrawerUpFlag << 1; +static const NotificationFlags kBiochipLidOpenFlag = kInventoryDrawerDownFlag << 1; +static const NotificationFlags kBiochipLidClosedFlag = kBiochipLidOpenFlag << 1; +static const NotificationFlags kBiochipDrawerUpFlag = kBiochipLidClosedFlag << 1; +static const NotificationFlags kBiochipDrawerDownFlag = kBiochipDrawerUpFlag << 1; + +static const NotificationFlags kInterfaceNotificationFlags = kInventoryLidOpenFlag | + kInventoryLidClosedFlag | + kInventoryDrawerUpFlag | + kInventoryDrawerDownFlag | + kBiochipLidOpenFlag | + kBiochipLidClosedFlag | + kBiochipDrawerUpFlag | + kBiochipDrawerDownFlag; + +// Hot spots. + +// Neighborhood hot spots. + +static const HotSpotID kFirstNeighborhoodSpotID = 5000; + +// kShellSpotFlag is a flag which marks all hot spots which belong to the shell, like +// the current item and current biochip spots. +static const HotSpotFlags kShellSpotFlag = 1; +// kNeighborhoodSpotFlag is a flag which marks all hot spots which belong to a +// neighborhood, like buttons on walls and so on. +static const HotSpotFlags kNeighborhoodSpotFlag = kShellSpotFlag << 1; +// kZoomInSpotFlag is a flag which marks all hot spots which indicate a zoom. +static const HotSpotFlags kZoomInSpotFlag = kNeighborhoodSpotFlag << 1; +// kZoomOutSpotFlag is a flag which marks all hot spots which indicate a zoom. +static const HotSpotFlags kZoomOutSpotFlag = kZoomInSpotFlag << 1; + +static const HotSpotFlags kClickSpotFlag = kZoomOutSpotFlag << 1; +static const HotSpotFlags kPlayExtraSpotFlag = kClickSpotFlag << 1; +static const HotSpotFlags kPickUpItemSpotFlag = kPlayExtraSpotFlag << 1; +static const HotSpotFlags kDropItemSpotFlag = kPickUpItemSpotFlag << 1; +static const HotSpotFlags kOpenDoorSpotFlag = kDropItemSpotFlag << 1; + +static const HotSpotFlags kZoomSpotFlags = kZoomInSpotFlag | kZoomOutSpotFlag; + +static const HotSpotFlags kHighestGameShellSpotFlag = kOpenDoorSpotFlag; + +///////////////////////////////////////////// +// +// Hot spots. + +// Shell hot spots. +// The shell reserves all hot spot IDs from 0 to 999 + +static const HotSpotID kCurrentItemSpotID = 0; +static const HotSpotID kCurrentBiochipSpotID = kCurrentItemSpotID + 1; + +static const HotSpotID kInventoryDropSpotID = kCurrentBiochipSpotID + 1; +static const HotSpotID kBiochipDropSpotID = kInventoryDropSpotID + 1; + +static const HotSpotID kInfoReturnSpotID = kBiochipDropSpotID + 1; + +static const HotSpotID kAIHint1SpotID = kInfoReturnSpotID + 1; +static const HotSpotID kAIHint2SpotID = kAIHint1SpotID + 1; +static const HotSpotID kAIHint3SpotID = kAIHint2SpotID + 1; +static const HotSpotID kAISolveSpotID = kAIHint3SpotID + 1; +static const HotSpotID kAIBriefingSpotID = kAISolveSpotID + 1; +static const HotSpotID kAIScanSpotID = kAIBriefingSpotID + 1; + +static const HotSpotID kPegasusRecallSpotID = kAIScanSpotID + 1; + +static const HotSpotID kAriesSpotID = kPegasusRecallSpotID + 1; +static const HotSpotID kMercurySpotID = kAriesSpotID + 1; +static const HotSpotID kPoseidonSpotID = kMercurySpotID + 1; + +static const HotSpotID kAirMaskToggleSpotID = kPoseidonSpotID + 1; + +static const HotSpotID kShuttleEnergySpotID = kAirMaskToggleSpotID + 1; +static const HotSpotID kShuttleGravitonSpotID = kShuttleEnergySpotID + 1; +static const HotSpotID kShuttleTractorSpotID = kShuttleGravitonSpotID + 1; +static const HotSpotID kShuttleViewSpotID = kShuttleTractorSpotID + 1; +static const HotSpotID kShuttleTransportSpotID = kShuttleViewSpotID + 1; + +// Most of these are obsolete: + +// kInventoryDropSpotFlag is a flag which marks hot spots which are valid drop spots +// for inventory items. +// static const HotSpotFlags kInventoryDropSpotFlag = kHighestGameShellSpotFlag << 1; + +// kBiochipDropSpotFlag is a flag which marks hot spots which are valid drop spots +// for biochips. +// static const HotSpotFlags kBiochipDropSpotFlag = kInventoryDropSpotFlag << 1; + +// kInventorySpotFlag is a flag which marks hot spots which indicate inventory items +// in the environment. +// static const HotSpotFlags kInventorySpotFlag = kBiochipDropSpotFlag << 1; + +// kBiochipSpotFlag is a flag which marks hot spots which indicate biochips +// in the environment. +static const HotSpotFlags kPickUpBiochipSpotFlag = kHighestGameShellSpotFlag << 1; +static const HotSpotFlags kDropBiochipSpotFlag = kPickUpBiochipSpotFlag << 1; + +static const HotSpotFlags kInfoReturnSpotFlag = kDropBiochipSpotFlag << 1; + +// Biochip and inventory hot spot flags... + +static const HotSpotFlags kAIBiochipSpotFlag = kInfoReturnSpotFlag << 1; +static const HotSpotFlags kPegasusBiochipSpotFlag = kAIBiochipSpotFlag << 1; +static const HotSpotFlags kOpticalBiochipSpotFlag = kPegasusBiochipSpotFlag << 1; +static const HotSpotFlags kAirMaskSpotFlag = kOpticalBiochipSpotFlag << 1; + +static const HotSpotFlags kJMPClickingSpotFlags = kClickSpotFlag | + kPlayExtraSpotFlag | + kOpenDoorSpotFlag | + kInfoReturnSpotFlag | + kAIBiochipSpotFlag | + kPegasusBiochipSpotFlag | + kOpticalBiochipSpotFlag | + kAirMaskSpotFlag; + +static const int32 kMainMenuID = 1; +static const int32 kPauseMenuID = 2; +static const int32 kCreditsMenuID = 3; +static const int32 kDeathMenuID = 4; + +///////////////////////////////////////////// +// +// Menu commands. + +static const GameMenuCommand kMenuCmdOverview = kMenuCmdNoCommand + 1; +static const GameMenuCommand kMenuCmdStartAdventure = kMenuCmdOverview + 1; +static const GameMenuCommand kMenuCmdStartWalkthrough = kMenuCmdStartAdventure + 1; +static const GameMenuCommand kMenuCmdRestore = kMenuCmdStartWalkthrough + 1; +static const GameMenuCommand kMenuCmdCredits = kMenuCmdRestore + 1; +static const GameMenuCommand kMenuCmdQuit = kMenuCmdCredits + 1; + +static const GameMenuCommand kMenuCmdDeathContinue = kMenuCmdQuit + 1; + +static const GameMenuCommand kMenuCmdDeathQuitDemo = kMenuCmdDeathContinue + 1; +static const GameMenuCommand kMenuCmdDeathMainMenuDemo = kMenuCmdDeathQuitDemo + 1; + +static const GameMenuCommand kMenuCmdDeathRestore = kMenuCmdDeathMainMenuDemo + 1; +static const GameMenuCommand kMenuCmdDeathMainMenu = kMenuCmdDeathRestore + 1; + +static const GameMenuCommand kMenuCmdPauseSave = kMenuCmdDeathMainMenu + 1; +static const GameMenuCommand kMenuCmdPauseContinue = kMenuCmdPauseSave + 1; +static const GameMenuCommand kMenuCmdPauseRestore = kMenuCmdPauseContinue + 1; +static const GameMenuCommand kMenuCmdPauseQuit = kMenuCmdPauseRestore + 1; + +static const GameMenuCommand kMenuCmdCreditsMainMenu = kMenuCmdPauseQuit + 1; + +static const GameMenuCommand kMenuCmdCancelRestart = kMenuCmdCreditsMainMenu + 1; +static const GameMenuCommand kMenuCmdEjectRestart = kMenuCmdCancelRestart + 1; + +static const TimeValue kMenuButtonHiliteTime = 20; +static const TimeScale kMenuButtonHiliteScale = kSixtyTicksPerSecond; + +// PICT resources: + +// Warning light PICTs: + +static const ResIDType kLightOffID = 128; +static const ResIDType kLightYellowID = 129; +static const ResIDType kLightOrangeID = 130; +static const ResIDType kLightRedID = 131; + +// Date PICTs: + +static const ResIDType kDatePrehistoricID = 138; +static const ResIDType kDate2112ID = 139; +static const ResIDType kDate2185ID = 140; +static const ResIDType kDate2310ID = 141; +static const ResIDType kDate2318ID = 142; + +///////////////////////////////////////////// +// +// Display Order + +static const DisplayOrder kCroppedMovieLayer = 11000; + +static const DisplayOrder kMonitorLayer = 12000; + +static const DisplayOrder kDragSpriteLayer = 15000; +static const DisplayOrder kDragSpriteOrder = kDragSpriteLayer; + +static const DisplayOrder kInterfaceLayer = 20000; +static const DisplayOrder kBackground1Order = kInterfaceLayer; +static const DisplayOrder kBackground2Order = kBackground1Order + 1; +static const DisplayOrder kBackground3Order = kBackground2Order + 1; +static const DisplayOrder kBackground4Order = kBackground3Order + 1; +static const DisplayOrder kDateOrder = kBackground4Order + 1; +static const DisplayOrder kCompassOrder = kDateOrder + 1; +static const DisplayOrder kEnergyBarOrder = kCompassOrder + 1; +static const DisplayOrder kEnergyLightOrder = kEnergyBarOrder + 1; + +static const DisplayOrder kAILayer = 22000; +static const DisplayOrder kAILeftAreaOrder = kAILayer; +static const DisplayOrder kAIMiddleAreaOrder = kAILeftAreaOrder + 1; +static const DisplayOrder kAIRightAreaOrder = kAIMiddleAreaOrder + 1; +static const DisplayOrder kAIMovieOrder = kAIRightAreaOrder + 1; + +static const DisplayOrder kHilitesLayer = 23000; +static const DisplayOrder kInventoryHiliteOrder = kHilitesLayer; +static const DisplayOrder kBiochipHiliteOrder = kInventoryHiliteOrder + 1; + +static const DisplayOrder kPanelsLayer = 25000; +static const DisplayOrder kInventoryPushOrder = kPanelsLayer; +static const DisplayOrder kInventoryLidOrder = kInventoryPushOrder + 1; +static const DisplayOrder kBiochipPushOrder = kInventoryLidOrder + 1; +static const DisplayOrder kBiochipLidOrder = kBiochipPushOrder + 1; +static const DisplayOrder kFinalMessageOrder = kBiochipLidOrder + 1; + +static const DisplayOrder kInfoLayer = 26000; +static const DisplayOrder kInfoBackgroundOrder = kInfoLayer; +static const DisplayOrder kInfoSpinOrder = kInfoBackgroundOrder + 1; + +static const DisplayOrder kScreenDimmerOrder = 30000; + +static const DisplayOrder kPauseScreenLayer = 31000; +static const DisplayOrder kPauseMenuOrder = kPauseScreenLayer; +static const DisplayOrder kSaveGameOrder = kPauseMenuOrder + 1; +static const DisplayOrder kContinueOrder = kSaveGameOrder + 1; +static const DisplayOrder kRestoreOrder = kContinueOrder + 1; +static const DisplayOrder kSoundFXOrder = kRestoreOrder + 1; +static const DisplayOrder kAmbienceOrder = kSoundFXOrder + 1; +static const DisplayOrder kWalkthruOrder = kAmbienceOrder + 1; +static const DisplayOrder kQuitToMainMenuOrder = kWalkthruOrder + 1; +static const DisplayOrder kPauseLargeHiliteOrder = kQuitToMainMenuOrder + 1; +static const DisplayOrder kPauseSmallHiliteOrder = kPauseLargeHiliteOrder + 1; + +///////////////////////////////////////////// +// +// Death reasons. +enum { + // Caldoria + kDeathUncreatedInCaldoria = 1, + kDeathCardBomb, + kDeathShotBySinclair, + kDeathSinclairShotDelegate, + kDeathNuclearExplosion, + + // TSA + kDeathUncreatedInTSA, + kDeathShotByTSARobots, + + // Prehistoric + kDeathFallOffCliff, + kDeathEatenByDinosaur, + kDeathStranded, + + // Norad + kDeathGassedInNorad, + kDeathArrestedInNorad, + kDeathWokeUpNorad, + kDeathSubDestroyed, // Unused + kDeathRobotThroughNoradDoor, + kDeathRobotSubControlRoom, + + // Mars + kDeathWrongShuttleLock, + kDeathArrestedInMars, + kDeathRunOverByPod, + kDeathDidntGetOutOfWay, + kDeathReactorBurn, + kDeathDidntFindMarsBomb, + kDeathDidntDisarmMarsBomb, + kDeathNoMaskInMaze, + kDeathNoAirInMaze, + kDeathGroundByMazebot, + kDeathMissedOreBucket, + kDeathDidntLeaveBucket, + kDeathRanIntoCanyonWall, // Unused + kDeathRanIntoSpaceJunk, + + // WSC + kDeathDidntStopPoison, + kDeathArrestedInWSC, + kDeathHitByPlasma, + kDeathShotOnCatwalk, + + // Winning + kPlayerWonGame +}; + +static const CoordType kAILeftAreaLeft = 76; +static const CoordType kAILeftAreaTop = 334; + +static const CoordType kAILeftAreaWidth = 96; +static const CoordType kAILeftAreaHeight = 96; + +static const CoordType kAIMiddleAreaLeft = 172; +static const CoordType kAIMiddleAreaTop = 334; + +static const CoordType kAIMiddleAreaWidth = 192; +static const CoordType kAIMiddleAreaHeight = 96; + +static const CoordType kAIRightAreaLeft = 364; +static const CoordType kAIRightAreaTop = 334; + +static const CoordType kAIRightAreaWidth = 96; +static const CoordType kAIRightAreaHeight = 96; + +enum { + kTSAPlayerNotArrived, // initial state, must be zero + kTSAPlayerForcedReview, // Player must watch TBP before rip occurs. + kTSAPlayerDetectedRip, // Player finished TBP, rip alarm just went off. + kTSAPlayerNeedsHistoricalLog, // Player is instructed to get historical log + kTSAPlayerGotHistoricalLog, + kTSAPlayerInstalledHistoricalLog, + kTSABossSawHistoricalLog, + kRobotsAtCommandCenter, + kRobotsAtFrontDoor, + kRobotsAtReadyRoom, + kPlayerLockedInPegasus, + kPlayerOnWayToPrehistoric, + kPlayerWentToPrehistoric, + kPlayerOnWayToNorad, + kPlayerOnWayToMars, + kPlayerOnWayToWSC, + kPlayerFinishedWithTSA +}; + +///////////////////////////////////////////// +// +// Mode static constants. + +static const GameMode kModeInventoryPick = kLastGameShellMode + 1; +static const GameMode kModeBiochipPick = kModeInventoryPick + 1; +static const GameMode kModeInfoScreen = kModeBiochipPick + 1; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/cursor.cpp b/engines/pegasus/cursor.cpp new file mode 100644 index 0000000000..5babdf34af --- /dev/null +++ b/engines/pegasus/cursor.cpp @@ -0,0 +1,213 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/cursorman.h" +#include "graphics/surface.h" +#include "graphics/decoders/pict.h" + +#include "pegasus/cursor.h" +#include "pegasus/graphics.h" +#include "pegasus/pegasus.h" + +namespace Pegasus { + +Cursor::Cursor() { + _cursorObscured = false; + _index = -1; + startIdling(); +} + +Cursor::~Cursor() { + for (uint32 i = 0; i < _info.size(); i++) { + if (_info[i].surface) { + _info[i].surface->free(); + delete _info[i].surface; + } + delete[] _info[i].palette; + } + + stopIdling(); +} + +void Cursor::addCursorFrames(uint16 id) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + Common::SeekableReadStream *cursStream = vm->_resFork->getResource(MKTAG('C', 'u', 'r', 's'), id); + if (!cursStream) + error("Could not load cursor frames set %d", id); + + uint16 frameCount = cursStream->readUint16BE(); + for (uint16 i = 0; i < frameCount; i++) { + CursorInfo info; + info.tag = cursStream->readUint16BE(); + info.hotspot.x = cursStream->readUint16BE(); + info.hotspot.y = cursStream->readUint16BE(); + info.surface = 0; + info.palette = 0; + info.colorCount = 0; + _info.push_back(info); + } + + delete cursStream; + + setCurrentFrameIndex(0); +} + +void Cursor::setCurrentFrameIndex(int32 index) { + if (_index != index) { + _index = index; + if (index != -1) { + loadCursorImage(_info[index]); + CursorMan.replaceCursorPalette(_info[index].palette, 0, _info[index].colorCount); + CursorMan.replaceCursor((byte *)_info[index].surface->pixels, _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, 0); + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); + } + } +} + +int32 Cursor::getCurrentFrameIndex() const { + return _index; +} + +void Cursor::show() { + if (!isVisible()) + CursorMan.showMouse(true); + + _cursorObscured = false; + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +} + +void Cursor::hide() { + CursorMan.showMouse(false); + setCurrentFrameIndex(0); + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +} + +void Cursor::hideUntilMoved() { + if (!_cursorObscured) { + hide(); + _cursorObscured = true; + } +} + +void Cursor::useIdleTime() { + if (g_system->getEventManager()->getMousePos() != _cursorLocation) { + _cursorLocation = g_system->getEventManager()->getMousePos(); + if (_index != -1 && _cursorObscured) + show(); + ((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); + } +} + +void Cursor::getCursorLocation(Common::Point &pt) const { + pt = _cursorLocation; +} + +bool Cursor::isVisible() { + return CursorMan.isVisible(); +} + +void Cursor::loadCursorImage(CursorInfo &cursorInfo) { + if (cursorInfo.surface) + return; + + cursorInfo.surface = new Graphics::Surface(); + + PegasusEngine *vm = (PegasusEngine *)g_engine; + Common::SeekableReadStream *cicnStream = vm->_resFork->getResource(MKTAG('c', 'i', 'c', 'n'), cursorInfo.tag); + + if (!cicnStream) + error("Failed to find color icon %d", cursorInfo.tag); + + // PixMap section + Graphics::PICTDecoder::PixMap pixMap = Graphics::PICTDecoder::readPixMap(*cicnStream); + + // Mask section + cicnStream->readUint32BE(); // mask baseAddr + uint16 maskRowBytes = cicnStream->readUint16BE(); // mask rowBytes + cicnStream->skip(3 * 2); // mask rect + /* uint16 maskHeight = */ cicnStream->readUint16BE(); + + // Bitmap section + cicnStream->readUint32BE(); // baseAddr + uint16 rowBytes = cicnStream->readUint16BE(); + cicnStream->readUint16BE(); // top + cicnStream->readUint16BE(); // left + uint16 height = cicnStream->readUint16BE(); // bottom + cicnStream->readUint16BE(); // right + + // Data section + cicnStream->readUint32BE(); // icon handle + cicnStream->skip(maskRowBytes * height); // FIXME: maskHeight doesn't work here, though the specs say it should + cicnStream->skip(rowBytes * height); + + // Palette section + cicnStream->readUint32BE(); // always 0 + cicnStream->readUint16BE(); // always 0 + cursorInfo.colorCount = cicnStream->readUint16BE() + 1; + + cursorInfo.palette = new byte[cursorInfo.colorCount * 3]; + for (uint16 i = 0; i < cursorInfo.colorCount; i++) { + cicnStream->readUint16BE(); + cursorInfo.palette[i * 3] = cicnStream->readUint16BE() >> 8; + cursorInfo.palette[i * 3 + 1] = cicnStream->readUint16BE() >> 8; + cursorInfo.palette[i * 3 + 2] = cicnStream->readUint16BE() >> 8; + } + + // PixMap data + if (pixMap.pixelSize == 8) { + cursorInfo.surface->create(pixMap.rowBytes, pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); + cicnStream->read(cursorInfo.surface->pixels, pixMap.rowBytes * pixMap.bounds.height()); + + // While this looks sensible, it actually doesn't work for some cursors + // (ie. the 'can grab' hand) + //cursorInfo.surface->w = pixMap.bounds.width(); + } else if (pixMap.pixelSize == 1) { + cursorInfo.surface->create(pixMap.bounds.width(), pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); + + for (int y = 0; y < pixMap.bounds.height(); y++) { + byte *line = (byte *)cursorInfo.surface->getBasePtr(0, y); + + for (int x = 0; x < pixMap.bounds.width();) { + byte b = cicnStream->readByte(); + + for (int i = 0; i < 8; i++) { + *line++ = ((b & (1 << (7 - i))) != 0) ? 1 : 0; + + if (++x == pixMap.bounds.width()) + break; + } + } + } + } else { + error("Unhandled %dbpp cicn images", pixMap.pixelSize); + } + + delete cicnStream; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/cursor.h b/engines/pegasus/cursor.h new file mode 100644 index 0000000000..ada82e3967 --- /dev/null +++ b/engines/pegasus/cursor.h @@ -0,0 +1,84 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_CURSOR_H +#define PEGASUS_CURSOR_H + +#include "common/array.h" +#include "common/rect.h" + +#include "pegasus/timers.h" + +namespace Graphics { + struct Surface; +} + +namespace Pegasus { + +// The original cursor code was in the graphics code directly, +// unlike ScummVM where we have the cursor code separate. We're +// going to go with CursorManager here and therefore not inherit +// from the Sprite class. + +class Cursor : private Idler { +public: + Cursor(); + virtual ~Cursor(); + + void addCursorFrames(uint16 id); + + void setCurrentFrameIndex(int32 index); + int32 getCurrentFrameIndex() const; + + void show(); + void hide(); + void hideUntilMoved(); + bool isVisible(); + + void getCursorLocation(Common::Point &) const; + +protected: + virtual void useIdleTime(); + +private: + struct CursorInfo { + uint16 tag; + Common::Point hotspot; + Graphics::Surface *surface; + byte *palette; + uint16 colorCount; + }; + + Common::Point _cursorLocation; + Common::Array<CursorInfo> _info; + bool _cursorObscured; + int _index; + + void loadCursorImage(CursorInfo &cursorInfo); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp new file mode 100644 index 0000000000..908005b665 --- /dev/null +++ b/engines/pegasus/detection.cpp @@ -0,0 +1,157 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "base/plugins.h" + +#include "engines/advancedDetector.h" +#include "common/config-manager.h" +#include "common/file.h" +#include "common/savefile.h" + +#include "pegasus/pegasus.h" + +namespace Pegasus { + +struct PegasusGameDescription { + ADGameDescription desc; +}; + +bool PegasusEngine::hasFeature(EngineFeature f) const { + return + (f == kSupportsRTL) + || (f == kSupportsLoadingDuringRuntime) + || (f == kSupportsSavingDuringRuntime); +} + +bool PegasusEngine::isDemo() const { + return (_gameDescription->desc.flags & ADGF_DEMO) != 0; +} + +} // End of namespace Pegasus + +static const PlainGameDescriptor pegasusGames[] = { + {"pegasus", "The Journeyman Project: Pegasus Prime"}, + {0, 0} +}; + + +namespace Pegasus { + +static const PegasusGameDescription gameDescriptions[] = { + { + { + "pegasus", + "", + AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2009943), + Common::EN_ANY, + Common::kPlatformMacintosh, + ADGF_MACRESFORK, + GUIO0() + }, + }, + + { + { + "pegasus", + "Demo", + AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 360129), + Common::EN_ANY, + Common::kPlatformMacintosh, + ADGF_MACRESFORK|ADGF_DEMO, + GUIO1(GUIO_NOLAUNCHLOAD) + }, + }, + + { AD_TABLE_END_MARKER } +}; + +} // End of namespace Pegasus + + +class PegasusMetaEngine : public AdvancedMetaEngine { +public: + PegasusMetaEngine() : AdvancedMetaEngine(Pegasus::gameDescriptions, sizeof(Pegasus::PegasusGameDescription), pegasusGames) { + _singleid = "pegasus"; + } + + virtual const char *getName() const { + return "The Journeyman Project: Pegasus Prime"; + } + + virtual const char *getOriginalCopyright() const { + return "The Journeyman Project: Pegasus Prime (C) Presto Studios"; + } + + virtual bool hasFeature(MetaEngineFeature f) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual SaveStateList listSaves(const char *target) const; + virtual int getMaximumSaveSlot() const { return 999; } + virtual void removeSaveState(const char *target, int slot) const; +}; + +bool PegasusMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) + || (f == kSupportsLoadingDuringStartup) + || (f == kSupportsDeleteSave); +} + +SaveStateList PegasusMetaEngine::listSaves(const char *target) const { + // The original had no pattern, so the user must rename theirs + // Note that we ignore the target because saves are compatible between + // all versions + Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav"); + + SaveStateList saveList; + for (uint32 i = 0; i < filenames.size(); i++) { + // Isolate the description from the file name + Common::String desc = filenames[i].c_str() + 8; + for (int j = 0; j < 4; j++) + desc.deleteLastChar(); + + saveList.push_back(SaveStateDescriptor(i, desc)); + } + + return saveList; +} + +void PegasusMetaEngine::removeSaveState(const char *target, int slot) const { + // See listSaves() for info on the pattern + Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav"); + g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str()); +} + +bool PegasusMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + const Pegasus::PegasusGameDescription *gd = (const Pegasus::PegasusGameDescription *)desc; + + if (gd) + *engine = new Pegasus::PegasusEngine(syst, gd); + + return (gd != 0); +} + +#if PLUGIN_ENABLED_DYNAMIC(PEGASUS) + REGISTER_PLUGIN_DYNAMIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine); +#else + REGISTER_PLUGIN_STATIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine); +#endif + diff --git a/engines/pegasus/elements.cpp b/engines/pegasus/elements.cpp new file mode 100644 index 0000000000..c84d555444 --- /dev/null +++ b/engines/pegasus/elements.cpp @@ -0,0 +1,568 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/macresman.h" +#include "common/stream.h" + +#include "pegasus/elements.h" +#include "pegasus/graphics.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +DisplayElement::DisplayElement(const DisplayElementID id) : IDObject(id) { + _elementIsDisplaying = false; + _elementIsVisible = false; + _elementOrder = 0; + _triggeredElement = this; + _nextElement = 0; +} + +DisplayElement::~DisplayElement() { + if (isDisplaying()) + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); +} + +void DisplayElement::setDisplayOrder(const DisplayOrder order) { + if (_elementOrder != order) { + _elementOrder = order; + if (isDisplaying()) { + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); + ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this); + triggerRedraw(); + } + } +} + +void DisplayElement::startDisplaying() { + if (!isDisplaying()) { + ((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this); + triggerRedraw(); + } +} + +void DisplayElement::stopDisplaying() { + if (isDisplaying()) { + triggerRedraw(); + ((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); + } +} + +void DisplayElement::setBounds(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) { + setBounds(Common::Rect(left, top, right, bottom)); +} + +void DisplayElement::getBounds(Common::Rect &r) const { + r = _bounds; +} + +void DisplayElement::sizeElement(const CoordType h, const CoordType v) { + Common::Rect newBounds = _bounds; + newBounds.right = _bounds.left + h; + newBounds.bottom = _bounds.top + v; + setBounds(newBounds); +} + +void DisplayElement::moveElementTo(const CoordType h, const CoordType v) { + Common::Rect newBounds = _bounds; + newBounds.moveTo(h, v); + setBounds(newBounds); +} + +void DisplayElement::moveElement(const CoordType dh, const CoordType dv) { + Common::Rect newBounds = _bounds; + newBounds.translate(dh, dv); + setBounds(newBounds); +} + +void DisplayElement::getLocation(CoordType &h, CoordType &v) const { + h = _bounds.left; + v = _bounds.top; +} + +void DisplayElement::centerElementAt(const CoordType h, const CoordType v) { + Common::Rect newBounds = _bounds; + newBounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2)); + setBounds(newBounds); +} + +void DisplayElement::getCenter(CoordType &h, CoordType &v) const { + h = (_bounds.left + _bounds.right) / 2; + v = (_bounds.top + _bounds.bottom) / 2; +} + +void DisplayElement::setBounds(const Common::Rect &r) { + if (r != _bounds) { + triggerRedraw(); + _bounds = r; + triggerRedraw(); + } +} + +void DisplayElement::hide() { + if (_elementIsVisible) { + triggerRedraw(); + _elementIsVisible = false; + } +} + +void DisplayElement::show() { + if (!_elementIsVisible) { + _elementIsVisible = true; + triggerRedraw(); + } +} + +// Only invalidates this element's bounding rectangle if all these conditions are true: +// -- The triggered element is this element. +// -- The element is displaying on the display list. +// -- The element is visible. +// -- The element is part of the active layer OR is one of the reserved items. +void DisplayElement::triggerRedraw() { + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + + if (_triggeredElement == this) { + if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer())) + gfx->invalRect(_bounds); + } else { + _triggeredElement->triggerRedraw(); + } +} + +void DisplayElement::setTriggeredElement(DisplayElement *element) { + if (element) + _triggeredElement = element; + else + _triggeredElement = this; +} + +bool DisplayElement::validToDraw(DisplayOrder backLayer, DisplayOrder frontLayer) { + return isDisplaying() && _elementIsVisible && + (getObjectID() <= kHighestReservedElementID || + (getDisplayOrder() >= backLayer && + getDisplayOrder() <= frontLayer)); +} + +DropHighlight::DropHighlight(const DisplayElementID id) : DisplayElement(id) { + _highlightColor = 0; + _thickness = 2; + _cornerDiameter = 0; +} + +void DropHighlight::draw(const Common::Rect &) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + // Since this is only used in two different ways, I'm only + // going to implement it in those two ways. Deal with it. + + Common::Rect rect = _bounds; + rect.grow(-_thickness); + screen->frameRect(rect, _highlightColor); + rect.grow(1); + screen->frameRect(rect, _highlightColor); + + if (_cornerDiameter == 8 && _thickness == 4) { + rect.grow(1); + screen->frameRect(rect, _highlightColor); + screen->hLine(rect.left + 1, rect.top - 1, rect.right - 2, _highlightColor); + screen->hLine(rect.left + 1, rect.bottom, rect.right - 2, _highlightColor); + screen->vLine(rect.left - 1, rect.top + 1, rect.bottom - 2, _highlightColor); + screen->vLine(rect.right, rect.top + 1, rect.bottom - 2, _highlightColor); + } +} + +IdlerAnimation::IdlerAnimation(const DisplayElementID id) : Animation(id) { + _lastTime = 0xffffffff; +} + +void IdlerAnimation::startDisplaying() { + if (!isDisplaying()) { + Animation::startDisplaying(); + startIdling(); + } +} + +void IdlerAnimation::stopDisplaying() { + if (isDisplaying()) { + Animation::stopDisplaying(); + stopIdling(); + } +} + +void IdlerAnimation::useIdleTime() { + uint32 currentTime = getTime(); + + if (currentTime != _lastTime) { + _lastTime = currentTime; + timeChanged(_lastTime); + } +} + +void IdlerAnimation::timeChanged(const TimeValue) { + triggerRedraw(); +} + +FrameSequence::FrameSequence(const DisplayElementID id) : IdlerAnimation(id) { + _duration = 0; + _currentFrameNum = 0; + _resFork = new Common::MacResManager(); + _numFrames = 0; +} + +FrameSequence::~FrameSequence() { + delete _resFork; +} + +void FrameSequence::useFileName(const Common::String &fileName) { + _resFork->open(fileName); +} + +void FrameSequence::openFrameSequence() { + if (!_resFork->hasResFork()) + return; + + Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80); + + if (!res) + return; + + uint32 scale = res->readUint32BE(); + _bounds.top = res->readUint16BE(); + _bounds.left = res->readUint16BE(); + _bounds.bottom = res->readUint16BE(); + _bounds.right = res->readUint16BE(); + _numFrames = res->readUint16BE(); + _duration = 0; + + _frameTimes.clear(); + for (uint32 i = 0; i < _numFrames; i++) { + TimeValue time = res->readUint32BE(); + _duration += time; + _frameTimes.push_back(_duration); + } + + setScale(scale); + setSegment(0, _duration); + setTime(0); + _currentFrameNum = 0; + newFrame(_currentFrameNum); + triggerRedraw(); + + delete res; +} + +void FrameSequence::closeFrameSequence() { + stop(); + _resFork->close(); + _duration = 0; + _numFrames = 0; + _frameTimes.clear(); +} + +void FrameSequence::timeChanged(const TimeValue time) { + int16 frameNum = 0; + for (int16 i = _numFrames - 1; i >= 0; i--) { + if (_frameTimes[i] < time) { + frameNum = i; + break; + } + } + + if (frameNum != _currentFrameNum) { + _currentFrameNum = frameNum; + newFrame(_currentFrameNum); + triggerRedraw(); + } +} + +void FrameSequence::setFrameNum(const int16 frameNum) { + int16 f = CLIP<int>(frameNum, 0, _numFrames); + + if (_currentFrameNum != f) { + _currentFrameNum = f; + setTime(_frameTimes[f]); + newFrame(f); + triggerRedraw(); + } +} + +bool FrameSequence::isSequenceOpen() const { + return _numFrames != 0; +} + +Sprite::Sprite(const DisplayElementID id) : DisplayElement(id) { + _numFrames = 0; + _currentFrameNum = 0xffffffff; + _currentFrame = 0; +} + +Sprite::~Sprite() { + discardFrames(); +} + +void Sprite::discardFrames() { + if (!_frameArray.empty()) { + for (uint32 i = 0; i < _numFrames; i++) { + SpriteFrame *frame = _frameArray[i].frame; + frame->_referenceCount--; + if (frame->_referenceCount == 0) + delete frame; + } + + _frameArray.clear(); + _numFrames = 0; + _currentFrame = 0; + _currentFrameNum = 0xffffffff; + setBounds(0, 0, 0, 0); + } +} + +void Sprite::addPICTResourceFrame(const ResIDType pictID, bool transparent, const CoordType left, const CoordType top) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, pictID, transparent); + addFrame(frame, left, top); +} + +uint32 Sprite::addFrame(SpriteFrame *frame, const CoordType left, const CoordType top) { + SpriteFrameRec frameRecord; + frameRecord.frame = frame; + frameRecord.frameLeft = left; + frameRecord.frameTop = top; + _frameArray.push_back(frameRecord); + _numFrames++; + frame->_referenceCount++; + + Common::Rect frameBounds; + frame->getSurfaceBounds(frameBounds); + + // 9/3/96 + // BB Should this be + left or - left? + frameBounds.moveTo(_bounds.left + left, _bounds.top + top); + + frameBounds.extend(_bounds); + + if (_bounds != frameBounds) + setBounds(frameBounds); + + return _numFrames - 1; +} + +void Sprite::removeFrame(const uint32 frameNum) { + _frameArray[frameNum].frame->_referenceCount--; + if (_frameArray[frameNum].frame->_referenceCount == 0) + delete _frameArray[frameNum].frame; + + // Calculate the new bounds + Common::Rect frameBounds; + for (uint32 i = 0; i < _numFrames; i++) { + if (i == frameNum) + continue; + + Common::Rect r; + _frameArray[i].frame->getSurfaceBounds(r); + r.translate(_frameArray[i].frameLeft, _frameArray[i].frameTop); + frameBounds.extend(r); + } + + _frameArray.remove_at(frameNum); + + frameBounds.moveTo(_bounds.left, _bounds.top); + setBounds(frameBounds); + + if (_currentFrameNum == frameNum) + triggerRedraw(); + else if (_currentFrameNum != 0xffffffff && _currentFrameNum > frameNum) + --_currentFrameNum; +} + +void Sprite::setCurrentFrameIndex(const int32 frameNum) { + if (frameNum < 0) { + if (_currentFrameNum != 0xffffffff) { + _currentFrameNum = 0xffffffff; + _currentFrame = 0; + triggerRedraw(); + } + } else if (_numFrames > 0) { + uint32 f = frameNum % _numFrames; + if (f != _currentFrameNum) { + _currentFrameNum = f; + _currentFrame = &_frameArray[f]; + triggerRedraw(); + } + } +} + +SpriteFrame *Sprite::getFrame(const int32 index) { + if (index < 0 || (uint32)index >= _numFrames) + return 0; + + return _frameArray[index].frame; +} + +void Sprite::draw(const Common::Rect &r) { + if (_currentFrame) { + Common::Rect frameBounds; + _currentFrame->frame->getSurfaceBounds(frameBounds); + + frameBounds.translate(_bounds.left + _currentFrame->frameLeft, _bounds.top + _currentFrame->frameTop); + Common::Rect r1 = frameBounds.findIntersectingRect(r); + + Common::Rect r2 = frameBounds; + r2.translate(-_bounds.left - _currentFrame->frameLeft, -_bounds.top - _currentFrame->frameTop); + + _currentFrame->frame->drawImage(r2, r1); + } +} + +SpriteSequence::SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID) : + FrameSequence(id), _sprite(spriteID), _transparent(false) { +} + +void SpriteSequence::openFrameSequence() { + if (!isSequenceOpen()) { + FrameSequence::openFrameSequence(); + + if (isSequenceOpen()) { + uint32 numFrames = getNumFrames(); + + for (uint32 i = 0; i < numFrames; ++i) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(_resFork, i + 0x80, _transparent); + _sprite.addFrame(frame, 0, 0); + } + + _sprite.setBounds(_bounds); + } + } +} + +void SpriteSequence::closeFrameSequence() { + if (isSequenceOpen()) { + FrameSequence::closeFrameSequence(); + _sprite.discardFrames(); + } +} + +void SpriteSequence::setBounds(const Common::Rect &bounds) { + FrameSequence::setBounds(bounds); + _sprite.setBounds(_bounds); +} + +void SpriteSequence::draw(const Common::Rect &r) { + _sprite.draw(r); +} + +void SpriteSequence::newFrame(const uint16 frame) { + _sprite.setCurrentFrameIndex(frame); +} + +#define DRAW_PIXEL() \ + if (bytesPerPixel == 2) \ + *((uint16 *)dst) = black; \ + else \ + *((uint32 *)dst) = black; \ + dst += bytesPerPixel + +#define SKIP_PIXEL() \ + dst += bytesPerPixel + +void ScreenDimmer::draw(const Common::Rect &r) { + // We're going to emulate QuickDraw's srcOr+gray mode here + // In this mode, every other y column is all black (odd-columns). + // Basically, every row does three black and then one transparent + // repeatedly. + + // The output is identical to the original + + uint32 black = g_system->getScreenFormat().RGBToColor(0, 0, 0); + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + byte bytesPerPixel = g_system->getScreenFormat().bytesPerPixel; + + // We're currently doing it to the whole screen to simplify the code + + for (int y = 0; y < 480; y++) { + byte *dst = (byte *)screen->getBasePtr(0, y); + + for (int x = 0; x < 640; x += 4) { + if (y & 1) { + DRAW_PIXEL(); + DRAW_PIXEL(); + SKIP_PIXEL(); + DRAW_PIXEL(); + } else { + SKIP_PIXEL(); + DRAW_PIXEL(); + DRAW_PIXEL(); + DRAW_PIXEL(); + } + } + } +} + +#undef DRAW_PIXEL +#undef SKIP_PIXEL + +SoundLevel::SoundLevel(const DisplayElementID id) : DisplayElement(id) { + _soundLevel = 0; +} + +void SoundLevel::incrementLevel() { + if (_soundLevel < 12) { + _soundLevel++; + triggerRedraw(); + } +} + +void SoundLevel::decrementLevel() { + if (_soundLevel > 0) { + _soundLevel--; + triggerRedraw(); + } +} + +uint16 SoundLevel::getSoundLevel() { + return CLIP<int>(_soundLevel * 22, 0, 256); +} + +void SoundLevel::setSoundLevel(uint16 level) { + uint16 newLevel = (level + 21) / 22; + + if (newLevel != _soundLevel) { + _soundLevel = newLevel; + triggerRedraw(); + } +} + +void SoundLevel::draw(const Common::Rect &r) { + Common::Rect levelRect(_bounds.right + (8 * (_soundLevel - 12)), _bounds.top, _bounds.right, _bounds.bottom); + levelRect = r.findIntersectingRect(levelRect); + + if (!levelRect.isEmpty()) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + screen->fillRect(levelRect, g_system->getScreenFormat().RGBToColor(0, 0, 0)); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/elements.h b/engines/pegasus/elements.h new file mode 100644 index 0000000000..140553f675 --- /dev/null +++ b/engines/pegasus/elements.h @@ -0,0 +1,254 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ELEMENTS_H +#define PEGASUS_ELEMENTS_H + +#include "common/array.h" +#include "common/rect.h" +#include "common/str.h" +#include "common/system.h" +#include "graphics/surface.h" + +#include "pegasus/timers.h" +#include "pegasus/util.h" + +namespace Common { + class MacResManager; +} + +namespace Pegasus { + +class DisplayElement : public IDObject { +friend class GraphicsManager; +public: + DisplayElement(const DisplayElementID); + virtual ~DisplayElement(); + + void setDisplayOrder(const DisplayOrder); + DisplayOrder getDisplayOrder() const { return _elementOrder; } + + bool validToDraw(DisplayOrder, DisplayOrder); + + virtual void draw(const Common::Rect&) {} + bool isDisplaying() { return _elementIsDisplaying; } + virtual void startDisplaying(); + virtual void stopDisplaying(); + + virtual void show(); + virtual void hide(); + bool isVisible() { return _elementIsVisible; } + + // triggerRedraw only triggers a draw if the element is displaying and visible. + void triggerRedraw(); + void setTriggeredElement(DisplayElement *); + + virtual void setBounds(const CoordType, const CoordType, const CoordType, const CoordType); + virtual void setBounds(const Common::Rect &); + virtual void getBounds(Common::Rect &) const; + virtual void sizeElement(const CoordType, const CoordType); + virtual void moveElementTo(const CoordType, const CoordType); + virtual void moveElement(const CoordType, const CoordType); + virtual void getLocation(CoordType &, CoordType &) const; + virtual void getCenter(CoordType &, CoordType &) const; + virtual void centerElementAt(const CoordType, const CoordType); + +protected: + Common::Rect _bounds; + bool _elementIsVisible; + DisplayElement *_triggeredElement; + + // Used only by PegasusEngine + bool _elementIsDisplaying; + DisplayOrder _elementOrder; + DisplayElement *_nextElement; +}; + +// I'm using the proper "highlight" instead of the evil +// QuickDraw "hilite" :P (deal with it!) +class DropHighlight : public DisplayElement { +public: + DropHighlight(const DisplayElementID); + virtual ~DropHighlight() {} + + void setHighlightColor(const uint32 &highlight) { _highlightColor = highlight; } + void getHighlightColor(uint32 &highlight) const { highlight = _highlightColor; } + + void setHighlightThickness(const uint16 thickness) { _thickness = thickness; } + uint16 getHighlightThickness() const { return _thickness; } + + void setHighlightCornerDiameter(const uint16 diameter) { _cornerDiameter = diameter; } + uint16 getHighlightCornerDiameter() const { return _cornerDiameter; } + + virtual void draw(const Common::Rect&); + +protected: + uint32 _highlightColor; + uint16 _thickness; + uint16 _cornerDiameter; +}; + +class Animation : public DisplayElement, public DynamicElement { +public: + Animation(const DisplayElementID id) : DisplayElement(id) {} +}; + +class IdlerAnimation : public Animation, public Idler { +public: + IdlerAnimation(const DisplayElementID); + + virtual void startDisplaying(); + virtual void stopDisplaying(); + + TimeValue getLastTime() const { return _lastTime; } + +protected: + virtual void useIdleTime(); + virtual void timeChanged(const TimeValue); + + TimeValue _lastTime; +}; + +// This class reads PICT resources and plays them like a movie. +// Assumes there is a resource of type 'PFrm' describing the time values for each +// PICT frame, as well as the total time in the movie. +// Assumes that PICT frames begin at PICT 128 + +class FrameSequence : public IdlerAnimation { +public: + FrameSequence(const DisplayElementID); + virtual ~FrameSequence(); + + void useFileName(const Common::String &fileName); + + virtual void openFrameSequence(); + virtual void closeFrameSequence(); + bool isSequenceOpen() const; + + uint16 getNumFrames() const { return _numFrames; } + virtual uint16 getFrameNum() const { return _currentFrameNum; } + virtual void setFrameNum(const int16); + +protected: + virtual void timeChanged(const TimeValue); + virtual void newFrame(const uint16) {} + + Common::MacResManager *_resFork; + TimeValue _duration; + + uint16 _numFrames; + Common::Array<TimeValue> _frameTimes; + + uint16 _currentFrameNum; +}; + +class SpriteFrame; + +class Sprite : public DisplayElement { +friend class SpriteFrame; +public: + Sprite(const DisplayElementID); + virtual ~Sprite(); + + virtual void addPICTResourceFrame(const ResIDType, const bool, const CoordType, const CoordType); + virtual uint32 addFrame(SpriteFrame *, const CoordType, const CoordType); + virtual void removeFrame(const uint32); + virtual void discardFrames(); + + // Setting the current frame. + // If the index is negative, sets the current frame to NULL and hides the sprite. + // If the index is larger than the number of frames in the sprite, the number + // is treated modulo the number of frames. + virtual void setCurrentFrameIndex(const int32); + virtual uint32 getCurrentFrameIndex() const { return _currentFrameNum; } + + virtual SpriteFrame *getFrame(const int32); + + virtual void draw(const Common::Rect &); + + uint32 getNumFrames() const { return _numFrames; } + +protected: + struct SpriteFrameRec { + SpriteFrame *frame; + CoordType frameLeft; + CoordType frameTop; + }; + + uint32 _numFrames; + uint32 _currentFrameNum; + SpriteFrameRec *_currentFrame; + Common::Array<SpriteFrameRec> _frameArray; +}; + +class SpriteSequence : public FrameSequence { +public: + SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID); + virtual ~SpriteSequence() {} + + void useTransparent(bool transparent) { _transparent = transparent; } + + virtual void openFrameSequence(); + virtual void closeFrameSequence(); + + virtual void draw(const Common::Rect &); + + virtual void setBounds(const Common::Rect &); + +protected: + virtual void newFrame(const uint16); + + bool _transparent; + Sprite _sprite; +}; + +class ScreenDimmer : public DisplayElement { +public: + ScreenDimmer() : DisplayElement(kScreenDimmerID) {} + virtual ~ScreenDimmer() {} + + virtual void draw(const Common::Rect &); +}; + +class SoundLevel : public DisplayElement { +public: + SoundLevel(const DisplayElementID); + virtual ~SoundLevel() {} + + void incrementLevel(); + void decrementLevel(); + + uint16 getSoundLevel(); + void setSoundLevel(uint16); + + void draw(const Common::Rect &); + +protected: + uint16 _soundLevel; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/energymonitor.cpp b/engines/pegasus/energymonitor.cpp new file mode 100644 index 0000000000..8aa77eb341 --- /dev/null +++ b/engines/pegasus/energymonitor.cpp @@ -0,0 +1,296 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +Blinker::Blinker() { + _sprite = 0; + _frame1 = -1; + _frame2 = -1; + _blinkDuration = 0; +} + +void Blinker::startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale) { + stopBlinking(); + _sprite = sprite; + _frame1 = frame1; + _frame2 = frame2; + _blinkDuration = blinkDuration; + setScale(blinkScale); + setSegment(0, blinkDuration * numBlinks * 2, blinkScale); + setTime(0); + start(); +} + +void Blinker::stopBlinking() { + if (_sprite) { + _sprite->setCurrentFrameIndex(_frame2); + _sprite = 0; + stop(); + } +} + +void Blinker::timeChanged(const TimeValue time) { + if (_sprite && _blinkDuration != 0) { + if (((time / _blinkDuration) & 1) != 0 || time == getDuration()) { + _sprite->setCurrentFrameIndex(_frame2); + if (!isRunning()) + stopBlinking(); + } else { + _sprite->setCurrentFrameIndex(_frame1); + } + } +} + +static const NotificationFlags kEnergyExpiredFlag = 1; + +EnergyMonitor *g_energyMonitor = 0; + +EnergyMonitor::EnergyMonitor() : IdlerAnimation(kEnergyBarID), _energyLight(kWarningLightID) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _stage = kStageNoStage; + + _calibrating = false; + _dontFlash = false; + + setBounds(338, 48, 434, 54); + + setDisplayOrder(kEnergyBarOrder); + startDisplaying(); + + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightOffID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightYellowID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightOrangeID); + _energyLight.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, kLightRedID); + _energyLight.addFrame(frame, 0, 0); + + _energyLight.setBounds(540, 35, 600, 59); + _energyLight.setDisplayOrder(kEnergyLightOrder); + _energyLight.startDisplaying(); + + setScale(1); + setSegment(0, kMaxJMPEnergy); + + setEnergyValue(kCasualEnergy); + + g_energyMonitor = this; +} + +EnergyMonitor::~EnergyMonitor() { + g_energyMonitor = 0; +} + +void EnergyMonitor::setEnergyValue(const uint32 value) { + if (isRunning()) { + stop(); + setTime(getStop() - value); + start(); + } else { + setTime(getStop() - value); + } +} + +void EnergyMonitor::startEnergyDraining() { + if (!isRunning()) { + _energyLight.show(); + start(); + show(); + } +} + +void EnergyMonitor::setEnergyDrainRate(Common::Rational rate) { + setRate(rate); +} + +Common::Rational EnergyMonitor::getEnergyDrainRate() { + return getRate(); +} + +void EnergyMonitor::stopEnergyDraining() { + if (isRunning()) { + stop(); + _energyLight.hide(); + hide(); + } +} + +void EnergyMonitor::drainEnergy(const int32 delta) { + setTime(getTime() + delta); +} + +int32 EnergyMonitor::getCurrentEnergy() { + return kMaxJMPEnergy - getTime(); +} + +void EnergyMonitor::timeChanged(const TimeValue currentTime) { + if (currentTime == getStop()) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + if (vm->getEnergyDeathReason() != -1) + vm->die(vm->getEnergyDeathReason()); + } else { + uint32 currentEnergy = kMaxJMPEnergy - currentTime; + + EnergyStage newStage; + if (currentEnergy > kWorriedEnergy) + newStage = kStageCasual; + else if (currentEnergy > kNervousEnergy) + newStage = kStageWorried; + else if (currentEnergy > kPanicStrickenEnergy) + newStage = kStageNervous; + else + newStage = kStagePanicStricken; + + if (_stage != newStage) { + uint32 newFrame; + + switch (newStage) { + case kStageCasual: + _barColor = g_system->getScreenFormat().RGBToColor(0x48, 0xB0, 0xD8); + newFrame = kFrameLightOff; + break; + case kStageWorried: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0xC0, 0x30); + newFrame = kFrameLightYellow; + break; + case kStageNervous: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x78, 0x38); + newFrame = kFrameLightOrange; + break; + case kStagePanicStricken: + _barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x40, 0x38); + newFrame = kFrameLightRed; + break; + default: + error("no stage in energy monitor?"); + break; + } + + _stage = newStage; + uint32 oldFrame = _energyLight.getCurrentFrameIndex(); + + if (!_calibrating) { + if (oldFrame > newFrame || oldFrame == 0xffffffff || _dontFlash) { + _energyLight.setCurrentFrameIndex(newFrame); + _dontFlash = false; + } else { + _lightBlinker.startBlinking(&_energyLight, oldFrame, newFrame, 4, 1, 3); + triggerRedraw(); + } + } + } + + Common::Rect r; + calcLevelRect(r); + if (r != _levelRect) { + _levelRect = r; + triggerRedraw(); + } + } +} + +void EnergyMonitor::calcLevelRect(Common::Rect &r) { + if (getStop() == 0) { + r = Common::Rect(); + } else { + getBounds(r); + r.left = r.right - r.width() * (kMaxJMPEnergy - getTime()) / getStop(); + } +} + +void EnergyMonitor::draw(const Common::Rect &r) { + Common::Rect r2 = r.findIntersectingRect(_levelRect); + + if (!r2.isEmpty()) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + screen->fillRect(r2, _barColor); + } +} + +void EnergyMonitor::calibrateEnergyBar() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _calibrating = true; + + vm->setEnergyDeathReason(-1); + + uint32 numFrames = _energyLight.getNumFrames(); + for (uint32 i = 1; i < numFrames; i++) { + _energyLight.setCurrentFrameIndex(i); + _energyLight.show(); + vm->delayShell(1, 3); + _energyLight.hide(); + vm->delayShell(1, 3); + } + + _energyLight.setCurrentFrameIndex(0); + _energyLight.hide(); + + show(); + setEnergyValue(0); + setEnergyDrainRate(-(int32)kMaxJMPEnergy / 2); + + // Make sure warning light is hidden... + _energyLight.hide(); + while (getCurrentEnergy() != (int32)kMaxJMPEnergy) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + setEnergyDrainRate(0); + hide(); + + _calibrating = false; +} + +void EnergyMonitor::restoreLastEnergyValue() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _dontFlash = true; + setEnergyValue(vm->getSavedEnergyValue()); + vm->resetEnergyDeathReason(); +} + +void EnergyMonitor::saveCurrentEnergyValue() { + ((PegasusEngine *)g_engine)->setLastEnergyValue(getCurrentEnergy()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/energymonitor.h b/engines/pegasus/energymonitor.h new file mode 100644 index 0000000000..02377d515a --- /dev/null +++ b/engines/pegasus/energymonitor.h @@ -0,0 +1,111 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ENERGYMONITOR_H +#define PEGASUS_ENERGYMONITOR_H + +#include "pegasus/elements.h" + +namespace Pegasus { + +class Sprite; + +class Blinker : private IdlerTimeBase { +public: + Blinker(); + virtual ~Blinker() {} + + void startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale); + void stopBlinking(); + +protected: + virtual void timeChanged(const TimeValue); + + Sprite *_sprite; + int32 _frame1; + int32 _frame2; + TimeValue _blinkDuration; +}; + +// Energy monitor constants. + +// These are in seconds. +// Max is two hours +static const uint32 kMaxJMPEnergy = 7200; + +static const uint32 kCasualEnergy = kMaxJMPEnergy * 100 / 100; // 100% +static const uint32 kWorriedEnergy = kMaxJMPEnergy * 50 / 100; // 50% +static const uint32 kNervousEnergy = kMaxJMPEnergy * 25 / 100; // 25% +static const uint32 kPanicStrickenEnergy = kMaxJMPEnergy * 5 / 100; // 5% + +static const uint32 kFullEnergy = kCasualEnergy; + +static const uint32 kFrameLightOff = 0; +static const uint32 kFrameLightYellow = 1; +static const uint32 kFrameLightOrange = 2; +static const uint32 kFrameLightRed = 3; + +static const int kEnergyDrainNormal = 1; +static const int kMarsReactorEnergyDrainNoShield = 6; +static const int kMarsReactorEnergyDrainWithShield = 3; +static const int kWSCPoisonEnergyDrainWithDart = 20; +static const int kWSCPoisonEnergyDrainNoDart = 10; + +class EnergyMonitor : private IdlerAnimation { +public: + EnergyMonitor(); + virtual ~EnergyMonitor(); + + void setEnergyValue(const uint32); + void startEnergyDraining(); + void setEnergyDrainRate(Common::Rational); + Common::Rational getEnergyDrainRate(); + void stopEnergyDraining(); + void drainEnergy(const int32); + int32 getCurrentEnergy(); + + void restoreLastEnergyValue(); + void saveCurrentEnergyValue(); + + void calibrateEnergyBar(); + +protected: + void timeChanged(const TimeValue); + void calcLevelRect(Common::Rect &); + void draw(const Common::Rect &); + + uint32 _barColor; + Common::Rect _levelRect; + EnergyStage _stage; + Sprite _energyLight; + Blinker _lightBlinker; + bool _calibrating, _dontFlash; +}; + +extern EnergyMonitor *g_energyMonitor; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/fader.cpp b/engines/pegasus/fader.cpp new file mode 100644 index 0000000000..a2bbf22944 --- /dev/null +++ b/engines/pegasus/fader.cpp @@ -0,0 +1,218 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/fader.h" +#include "pegasus/pegasus.h" +#include "pegasus/sound.h" +#include "pegasus/util.h" + +namespace Pegasus { + +Fader::Fader() { + _currentValue = 0; + _currentFaderMove._numKnots = 0; +} + +void Fader::setFaderValue(const int32 newValue) { + _currentValue = newValue; +} + +bool Fader::initFaderMove(const FaderMoveSpec &spec) { + bool faderMoves = false; + int32 value = 0; + + if (spec._numKnots > 0) { + stopFader(); + value = spec._knots[0].knotValue; + TimeValue startTime = spec._knots[0].knotTime; + + if (startTime != 0xffffffff) { + if (spec._numKnots > 1) { + TimeValue stopTime = spec._knots[spec._numKnots - 1].knotTime; + + if (spec._faderScale > 0) { + if (stopTime > startTime) { + for (uint32 i = 1; i < spec._numKnots; ++i) { + if (spec._knots[i - 1].knotValue != spec._knots[i].knotValue) { + faderMoves = true; + break; + } + } + + if (faderMoves) + _currentFaderMove = spec; + } else if (spec._knots[spec._numKnots - 1].knotValue != value) { + value = spec._knots[spec._numKnots - 1].knotValue; + } + } + } + } + } + + setFaderValue(value); + return faderMoves; +} + +void Fader::startFader(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(0); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + } +} + +void Fader::startFaderSync(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(0); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + + while (isFading()) { + ((PegasusEngine *)g_engine)->checkCallBacks(); + useIdleTime(); + } + + // Once more, for good measure, to make sure that there are no boundary + // condition problems. + useIdleTime(); + stopFader(); + } +} + +void Fader::loopFader(const FaderMoveSpec &spec) { + if (initFaderMove(spec)) { + setFlags(kLoopTimeBase); + setScale(spec._faderScale); + setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); + setTime(spec._knots[0].knotTime); + start(); + } +} + +void Fader::stopFader() { + stop(); +} + +void Fader::pauseFader() { + stopFader(); +} + +void Fader::continueFader() { + if (getTime() < getStop()) + start(); +} + +void Fader::timeChanged(const TimeValue newTime) { + if (_currentFaderMove._numKnots != 0) { + uint32 i; + for (i = 0; i < _currentFaderMove._numKnots; i++) + if (_currentFaderMove._knots[i].knotTime > newTime) + break; + + int32 newValue; + if (i == 0) + newValue = _currentFaderMove._knots[0].knotValue; + else if (i == _currentFaderMove._numKnots) + newValue = _currentFaderMove._knots[i - 1].knotValue; + else + newValue = linearInterp(_currentFaderMove._knots[i - 1].knotTime, _currentFaderMove._knots[i].knotTime, newTime, _currentFaderMove._knots[i - 1].knotValue, _currentFaderMove._knots[i].knotValue); + + if (newValue != _currentValue) + setFaderValue(newValue); + } +} + +void FaderMoveSpec::makeOneKnotFaderSpec(const int32 knotValue) { + _numKnots = 1; + _knots[0].knotTime = 0; + _knots[0].knotValue = knotValue; +} + +void FaderMoveSpec::makeTwoKnotFaderSpec(const TimeScale faderScale, const TimeValue time1, const int32 value1, const TimeValue time2, const int32 value2) { + _numKnots = 2; + _faderScale = faderScale; + _knots[0].knotTime = time1; + _knots[0].knotValue = value1; + _knots[1].knotTime = time2; + _knots[1].knotValue = value2; +} + +void FaderMoveSpec::insertFaderKnot(const TimeValue knotTime, const int32 knotValue) { + if (_numKnots != kMaxFaderKnots) { + uint32 index; + for (index = 0; index < _numKnots; index++) { + if (knotTime == _knots[index].knotTime) { + _knots[index].knotValue = knotValue; + return; + } else if (knotTime < _knots[index].knotTime) { + break; + } + } + + for (uint32 i = _numKnots; i > index; i--) + _knots[i] = _knots[i - 1]; + + _knots[index].knotTime = knotTime; + _knots[index].knotValue = knotValue; + _numKnots++; + } +} + +void FaderAnimation::setFaderValue(const int32 newValue) { + if (getFaderValue() != newValue) { + Fader::setFaderValue(newValue); + triggerRedraw(); + } +} + +SoundFader::SoundFader() { + _sound = 0; + _masterVolume = 0xff; +} + +void SoundFader::attachSound(Sound *sound) { + if (!sound && isFading()) + stopFader(); + + _sound = sound; +} + +void SoundFader::setFaderValue(const int32 newVolume) { + if (_sound) + _sound->setVolume((newVolume * _masterVolume) >> 8); + + _currentValue = newVolume; +} + +void SoundFader::setMasterVolume(const uint16 masterVolume) { + _masterVolume = masterVolume; + setFaderValue(getFaderValue()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/fader.h b/engines/pegasus/fader.h new file mode 100644 index 0000000000..0a8cd549e6 --- /dev/null +++ b/engines/pegasus/fader.h @@ -0,0 +1,130 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_FADER_H +#define PEGASUS_FADER_H + +#include "pegasus/elements.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Fader; + +class FaderMoveSpec { +friend class Fader; +public: + FaderMoveSpec() { + _faderScale = kDefaultTimeScale; + _numKnots = 0; + } + + FaderMoveSpec(const TimeScale scale) { + _faderScale = scale; + _numKnots = 0; + } + + void setFaderScale(const TimeScale scale) { _faderScale = scale; } + TimeScale getFaderScale() const { return _faderScale; } + + void makeOneKnotFaderSpec(const int32); + void makeTwoKnotFaderSpec(const TimeScale, const TimeValue, const int32, const TimeValue, const int32); + + void insertFaderKnot(const TimeValue, const int32); + + uint32 getNumKnots() const { return _numKnots; } + TimeValue getNthKnotTime(const uint32 index) const { return _knots[index].knotTime; } + int32 getNthKnotValue(const uint32 index) const { return _knots[index].knotValue; } + +protected: + struct FaderKnot { + TimeValue knotTime; + int32 knotValue; + }; + + TimeScale _faderScale; + uint32 _numKnots; + + static const uint32 kMaxFaderKnots = 20; + FaderKnot _knots[kMaxFaderKnots]; +}; + +class Fader : public IdlerTimeBase { +public: + Fader(); + virtual ~Fader() {} + + virtual void setFaderValue(const int32); + int32 getFaderValue() const { return _currentValue; } + virtual void startFader(const FaderMoveSpec &); + virtual void startFaderSync(const FaderMoveSpec &); + virtual void loopFader(const FaderMoveSpec &); + virtual void stopFader(); + virtual bool isFading() { return isRunning(); } + + void pauseFader(); + void continueFader(); + + void getCurrentFaderMove(FaderMoveSpec &spec) { spec = _currentFaderMove; } + +protected: + bool initFaderMove(const FaderMoveSpec &); + virtual void timeChanged(const TimeValue); + + int32 _currentValue; + FaderMoveSpec _currentFaderMove; +}; + +class FaderAnimation : public DisplayElement, public Fader { +public: + FaderAnimation(const DisplayElementID id) : DisplayElement(id) {} + virtual ~FaderAnimation() {} + + void setFaderValue(const int32); +}; + +class Sound; + +class SoundFader : public Fader { +friend class Sound; +public: + SoundFader(); + virtual ~SoundFader() {} + + void setFaderValue(const int32); + + void setMasterVolume(const uint16); + uint16 getMasterVolume() const { return _masterVolume; } + +protected: + void attachSound(Sound *); + + Sound *_sound; + uint16 _masterVolume; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/gamestate.cpp b/engines/pegasus/gamestate.cpp new file mode 100644 index 0000000000..7a4e657e02 --- /dev/null +++ b/engines/pegasus/gamestate.cpp @@ -0,0 +1,2359 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/error.h" +#include "common/stream.h" + +#include "pegasus/constants.h" +#include "pegasus/gamestate.h" +#include "pegasus/scoring.h" + +namespace Common { +DECLARE_SINGLETON(Pegasus::GameStateManager); +} + +namespace Pegasus { + +Common::Error GameStateManager::writeGameState(Common::WriteStream *stream) { + stream->writeUint16BE(_currentNeighborhood); + stream->writeUint16BE(_currentRoom); + stream->writeByte(_currentDirection); + stream->writeUint16BE(_nexNeighborhoodID); + stream->writeUint16BE(_nextRoomID); + stream->writeByte(_nextDirection); + stream->writeUint16BE(_lastNeighborhood); + stream->writeUint16BE(_lastRoom); + stream->writeByte(_lastDirection); + stream->writeUint16BE(_openDoorRoom); + stream->writeByte(_openDoorDirection); + + _globalFlags.writeToStream(stream); + _scoringFlags.writeToStream(stream); + _itemTakenFlags.writeToStream(stream); + + writeCaldoriaState(stream); + writeTSAState(stream); + writePrehistoricState(stream); + writeNoradState(stream); + writeMarsState(stream); + writeWSCState(stream); + + if (stream->err()) + return Common::kWritingFailed; + + return Common::kNoError; +} + +Common::Error GameStateManager::readGameState(Common::ReadStream *stream) { + _currentNeighborhood = stream->readUint16BE(); + _currentRoom = stream->readUint16BE(); + _currentDirection = stream->readByte(); + _nexNeighborhoodID = stream->readUint16BE(); + _nextRoomID = stream->readUint16BE(); + _nextDirection = stream->readByte(); + _lastNeighborhood = stream->readUint16BE(); + _lastRoom = stream->readUint16BE(); + _lastDirection = stream->readByte(); + _openDoorRoom = stream->readUint16BE(); + _openDoorDirection = stream->readByte(); + + _globalFlags.readFromStream(stream); + _scoringFlags.readFromStream(stream); + _itemTakenFlags.readFromStream(stream); + + readCaldoriaState(stream); + readTSAState(stream); + readPrehistoricState(stream); + readNoradState(stream); + readMarsState(stream); + readWSCState(stream); + + if (stream->err()) + return Common::kReadingFailed; + + return Common::kNoError; +} + +void GameStateManager::resetGameState() { + _currentNeighborhood = kNoNeighborhoodID; + _currentRoom = kNoRoomID; + _currentDirection = kNoDirection; + _nexNeighborhoodID = kNoNeighborhoodID; + _nextRoomID = kNoRoomID; + _nextDirection = kNoDirection; + _lastNeighborhood = kNoNeighborhoodID; + _lastRoom = kNoRoomID; + _lastDirection = kNoDirection; + _openDoorRoom = kNoRoomID; + _openDoorDirection = kNoDirection; + + _globalFlags.clearAllFlags(); + _scoringFlags.clearAllFlags(); + _itemTakenFlags.clearAllFlags(); + + resetCaldoriaState(); + resetTSAState(); + resetPrehistoricState(); + resetNoradState(); + resetMarsState(); + resetWSCState(); +} + +void GameStateManager::getCurrentLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { + neighborhood = _currentNeighborhood; + room = _currentRoom; + direction = _currentDirection; +} + +void GameStateManager::setCurrentLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + _lastNeighborhood = _currentNeighborhood; + _lastRoom = _currentRoom; + _lastDirection = _currentDirection; + _currentNeighborhood = neighborhood; + _currentRoom = room; + _currentDirection = direction; +} + +NeighborhoodID GameStateManager::getCurrentNeighborhood() { + return _currentNeighborhood; +} + +void GameStateManager::setCurrentNeighborhood(const NeighborhoodID neighborhood) { + _lastNeighborhood = _currentNeighborhood; + _currentNeighborhood = neighborhood; +} + +RoomID GameStateManager::getCurrentRoom() { + return _currentRoom; +} + +void GameStateManager::setCurrentRoom(const RoomID room) { + _lastRoom = _currentRoom; + _currentRoom = room; +} + +DirectionConstant GameStateManager::getCurrentDirection() { + return _currentDirection; +} + +void GameStateManager::setCurrentDirection(const DirectionConstant direction) { + _lastDirection = _currentDirection; + _currentDirection = direction; +} + +RoomViewID GameStateManager::getCurrentRoomAndView() { + return MakeRoomView(_currentRoom, _currentDirection); +} + +void GameStateManager::getNextLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { + neighborhood = _nexNeighborhoodID; + room = _nextRoomID; + direction = _nextDirection; +} + +void GameStateManager::setNextLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + _nexNeighborhoodID = neighborhood; + _nextRoomID = room; + _nextDirection = direction; +} + +NeighborhoodID GameStateManager::getNextNeighborhood() { + return _nexNeighborhoodID; +} + +void GameStateManager::setNextNeighborhood(const NeighborhoodID neighborhood) { + _nexNeighborhoodID = neighborhood; +} + +RoomID GameStateManager::getNextRoom() { + return _nextRoomID; +} + +void GameStateManager::setNextRoom(const RoomID room) { + _nextRoomID = room; +} + +DirectionConstant GameStateManager::getNextDirection() { + return _nextDirection; +} + +void GameStateManager::setNextDirection(const DirectionConstant direction) { + _nextDirection = direction; +} + +void GameStateManager::getLastLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { + neighborhood = _currentNeighborhood; + room = _currentRoom; + direction = _currentDirection; +} + +void GameStateManager::setLastLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + _currentNeighborhood = neighborhood; + _currentRoom = room; + _currentDirection = direction; +} + +NeighborhoodID GameStateManager::getLastNeighborhood() { + return _lastNeighborhood; +} + +void GameStateManager::setLastNeighborhood(const NeighborhoodID neighborhood) { + _lastNeighborhood = neighborhood; +} + +RoomID GameStateManager::getLastRoom() { + return _lastRoom; +} + +void GameStateManager::setLastRoom(const RoomID room) { + _lastRoom = room; +} + +DirectionConstant GameStateManager::getLastDirection() { + return _lastDirection; +} + +void GameStateManager::setLastDirection(const DirectionConstant direction) { + _lastDirection = direction; +} + +RoomViewID GameStateManager::getLastRoomAndView() { + return MakeRoomView(_lastRoom, _lastDirection); +} + +void GameStateManager::getOpenDoorLocation(RoomID &room, DirectionConstant &direction) { + room = _openDoorRoom; + direction = _openDoorDirection; +} + +void GameStateManager::setOpenDoorLocation(const RoomID room, const DirectionConstant direction) { + _openDoorRoom = room; + _openDoorDirection = direction; +} + +RoomID GameStateManager::getOpenDoorRoom() { + return _openDoorRoom; +} + +void GameStateManager::setOpenDoorRoom(const RoomID room) { + _openDoorRoom = room; +} + +DirectionConstant GameStateManager::getOpenDoorDirection() { + return _openDoorDirection; +} + +void GameStateManager::setOpenDoorDirection(const DirectionConstant direction) { + _openDoorDirection = direction; +} + +RoomViewID GameStateManager::getDoorOpenRoomAndView() { + return MakeRoomView(_openDoorRoom, _openDoorDirection); +} + +bool GameStateManager::isCurrentDoorOpen() { + return _openDoorRoom == _currentRoom && _openDoorDirection == _currentDirection; +} + +GameScoreType GameStateManager::getCaldoriaTSAScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringSawINNFlag)) + result += kSawINNScore; + if (_scoringFlags.getFlag(kScoringTookShowerFlag)) + result += kTookShowerScore; + if (_scoringFlags.getFlag(kScoringFixedHairFlag)) + result += kFixedHairScore; + if (_scoringFlags.getFlag(kScoringGotKeyCardFlag)) + result += kGotKeyCardScore; + if (_scoringFlags.getFlag(kScoringReadPaperFlag)) + result += kReadPaperScore; + if (_scoringFlags.getFlag(kScoringLookThroughTelescopeFlag)) + result += kLookThroughTelescopeScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag)) + result += kSawCaldoriaKioskScore; + if (_scoringFlags.getFlag(kScoringGoToTSAFlag)) + result += kGoToTSAScore; + if (_scoringFlags.getFlag(kScoringEnterTSAFlag)) + result += kEnterTSAScore; + if (_scoringFlags.getFlag(kScoringSawBust1Flag)) + result += kSawBust1Score; + if (_scoringFlags.getFlag(kScoringSawBust2Flag)) + result += kSawBust2Score; + if (_scoringFlags.getFlag(kScoringSawBust3Flag)) + result += kSawBust3Score; + if (_scoringFlags.getFlag(kScoringSawBust4Flag)) + result += kSawBust4Score; + if (_scoringFlags.getFlag(kScoringSawBust5Flag)) + result += kSawBust5Score; + if (_scoringFlags.getFlag(kScoringSawBust6Flag)) + result += kSawBust6Score; + if (_scoringFlags.getFlag(kScoringSawTheoryFlag)) + result += kSawTheoryScore; + if (_scoringFlags.getFlag(kScoringSawBackgroundFlag)) + result += kSawBackgroundScore; + if (_scoringFlags.getFlag(kScoringSawProcedureFlag)) + result += kSawProcedureScore; + if (_scoringFlags.getFlag(kScoringGotJourneymanKeyFlag)) + result += kGotJourneymanKeyScore; + if (_scoringFlags.getFlag(kScoringGotPegasusBiochipFlag)) + result += kGotPegasusBiochipScore; + if (_scoringFlags.getFlag(kScoringGotBiosuitFlag)) + result += kGotBiosuitScore; + if (_scoringFlags.getFlag(kScoringGoToPrehistoricFlag)) + result += kGoToPrehistoricScore; + if (_scoringFlags.getFlag(kScoringPutLogInReaderFlag)) + result += kPutLogInReaderScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag)) + result += kSawCaldoriaNormalScore; + if (_scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag)) + result += kSawCaldoriaAlteredScore; + if (_scoringFlags.getFlag(kScoringSawNoradNormalFlag)) + result += kSawNoradNormalScore; + if (_scoringFlags.getFlag(kScoringSawNoradAlteredFlag)) + result += kSawNoradAlteredScore; + if (_scoringFlags.getFlag(kScoringSawMarsNormalFlag)) + result += kSawMarsNormalScore; + if (_scoringFlags.getFlag(kScoringSawMarsAlteredFlag)) + result += kSawMarsAlteredScore; + if (_scoringFlags.getFlag(kScoringSawWSCNormalFlag)) + result += kSawWSCNormalScore; + if (_scoringFlags.getFlag(kScoringSawWSCAlteredFlag)) + result += kSawWSCAlteredScore; + if (_scoringFlags.getFlag(kScoringWentToReadyRoom2Flag)) + result += kWentToReadyRoom2Score; + if (_scoringFlags.getFlag(kScoringWentAfterSinclairFlag)) + result += kWentAfterSinclairScore; + if (_scoringFlags.getFlag(kScoringUsedCardBombFlag)) + result += kUsedCardBombScore; + if (_scoringFlags.getFlag(kScoringShieldedCardBombFlag)) + result += kShieldedCardBombScore; + if (_scoringFlags.getFlag(kScoringStunnedSinclairFlag)) + result += kStunnedSinclairScore; + if (_scoringFlags.getFlag(kScoringDisarmedNukeFlag)) + result += kDisarmedNukeScore; + + return result; +} + +GameScoreType GameStateManager::getPrehistoricScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringThrewBreakerFlag)) + result += kThrewBreakerScore; + if (_scoringFlags.getFlag(kScoringExtendedBridgeFlag)) + result += kExtendedBridgeScore; + if (_scoringFlags.getFlag(kScoringGotHistoricalLogFlag)) + result += kGotHistoricalLogScore; + if (_scoringFlags.getFlag(kScoringFinishedPrehistoricFlag)) + result += kFinishedPrehistoricScore; + + return result; +} + +GameScoreType GameStateManager::getMarsScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringThrownByRobotFlag)) + result += kThrownByRobotScore; + if (_scoringFlags.getFlag(kScoringGotMarsCardFlag)) + result += kGotMarsCardScore; + if (_scoringFlags.getFlag(kScoringSawMarsKioskFlag)) + result += kSawMarsKioskScore; + if (_scoringFlags.getFlag(kScoringSawTransportMapFlag)) + result += kSawTransportMapScore; + if (_scoringFlags.getFlag(kScoringGotCrowBarFlag)) + result += kGotCrowBarScore; + if (_scoringFlags.getFlag(kScoringTurnedOnTransportFlag)) + result += kTurnedOnTransportScore; + if (_scoringFlags.getFlag(kScoringGotOxygenMaskFlag)) + result += kGotOxygenMaskScore; + if (_scoringFlags.getFlag(kScoringAvoidedRobotFlag)) + result += kAvoidedRobotScore; + if (_scoringFlags.getFlag(kScoringActivatedPlatformFlag)) + result += kActivatedPlatformScore; + if (_scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag)) + result += kUsedLiquidNitrogenScore; + if (_scoringFlags.getFlag(kScoringUsedCrowBarFlag)) + result += kUsedCrowBarScore; + if (_scoringFlags.getFlag(kScoringFoundCardBombFlag)) + result += kFoundCardBombScore; + if (_scoringFlags.getFlag(kScoringDisarmedCardBombFlag)) + result += kDisarmedCardBombScore; + if (_scoringFlags.getFlag(kScoringGotCardBombFlag)) + result += kGotCardBombScore; + if (_scoringFlags.getFlag(kScoringThreadedMazeFlag)) + result += kThreadedMazeScore; + if (_scoringFlags.getFlag(kScoringThreadedGearRoomFlag)) + result += kThreadedGearRoomScore; + if (_scoringFlags.getFlag(kScoringEnteredShuttleFlag)) + result += kEnteredShuttleScore; + if (_scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag)) + result += kEnteredLaunchTubeScore; + if (_scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag)) + result += kStoppedRobotsShuttleScore; + if (_scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag)) + result += kGotMarsOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedMarsFlag)) + result += kFinishedMarsScore; + + return result; +} + +GameScoreType GameStateManager::getNoradScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringSawSecurityMonitorFlag)) + result += kSawSecurityMonitorScore; + if (_scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag)) + result += kFilledOxygenCanisterScore; + if (_scoringFlags.getFlag(kScoringFilledArgonCanisterFlag)) + result += kFilledArgonCanisterScore; + if (_scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag)) + result += kSawUnconsciousOperatorScore; + if (_scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag)) + result += kWentThroughPressureDoorScore; + if (_scoringFlags.getFlag(kScoringPreppedSubFlag)) + result += kPreppedSubScore; + if (_scoringFlags.getFlag(kScoringEnteredSubFlag)) + result += kEnteredSubScore; + if (_scoringFlags.getFlag(kScoringExitedSubFlag)) + result += kExitedSubScore; + if (_scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag)) + result += kSawRobotAt54NorthScore; + if (_scoringFlags.getFlag(kScoringPlayedWithClawFlag)) + result += kPlayedWithClawScore; + if (_scoringFlags.getFlag(kScoringUsedRetinalChipFlag)) + result += kUsedRetinalChipScore; + if (_scoringFlags.getFlag(kScoringFinishedGlobeGameFlag)) + result += kFinishedGlobeGameScore; + if (_scoringFlags.getFlag(kScoringStoppedNoradRobotFlag)) + result += kStoppedNoradRobotScore; + if (_scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag)) + result += kGotNoradOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedNoradFlag)) + result += kFinishedNoradScore; + + return result; +} + +GameScoreType GameStateManager::getWSCScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringRemovedDartFlag)) + result += kRemovedDartScore; + if (_scoringFlags.getFlag(kScoringAnalyzedDartFlag)) + result += kAnalyzedDartScore; + if (_scoringFlags.getFlag(kScoringBuiltAntidoteFlag)) + result += kBuiltAntidoteScore; + if (_scoringFlags.getFlag(kScoringGotSinclairKeyFlag)) + result += kGotSinclairKeyScore; + if (_scoringFlags.getFlag(kScoringGotArgonCanisterFlag)) + result += kGotArgonCanisterScore; + if (_scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag)) + result += kGotNitrogenCanisterScore; + if (_scoringFlags.getFlag(kScoringPlayedWithMessagesFlag)) + result += kPlayedWithMessagesScore; + if (_scoringFlags.getFlag(kScoringSawMorphExperimentFlag)) + result += kSawMorphExperimentScore; + if (_scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag)) + result += kEnteredSinclairOfficeScore; + if (_scoringFlags.getFlag(kScoringSawBrochureFlag)) + result += kSawBrochureScore; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry1Flag)) + result += kSawSinclairEntry1Score; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry2Flag)) + result += kSawSinclairEntry2Score; + if (_scoringFlags.getFlag(kScoringSawSinclairEntry3Flag)) + result += kSawSinclairEntry3Score; + if (_scoringFlags.getFlag(kScoringSawWSCDirectoryFlag)) + result += kSawWSCDirectoryScore; + if (_scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag)) + result += kUsedCrowBarInWSCScore; + if (_scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag)) + result += kFinishedPlasmaDodgeScore; + if (_scoringFlags.getFlag(kScoringOpenedCatwalkFlag)) + result += kOpenedCatwalkScore; + if (_scoringFlags.getFlag(kScoringStoppedWSCRobotFlag)) + result += kStoppedWSCRobotScore; + if (_scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag)) + result += kGotWSCOpMemChipScore; + if (_scoringFlags.getFlag(kScoringFinishedWSCFlag)) + result += kFinishedWSCScore; + + return result; +} + +GameScoreType GameStateManager::getGandhiScore() { + GameScoreType result = 0; + + if (_scoringFlags.getFlag(kScoringMarsGandhiFlag)) + result += kMarsGandhiScore; + if (_scoringFlags.getFlag(kScoringNoradGandhiFlag)) + result += kNoradGandhiScore; + if (_scoringFlags.getFlag(kScoringWSCGandhiFlag)) + result += kWSCGandhiScore; + + return result; +} + +GameScoreType GameStateManager::getTotalScore() { + return getCaldoriaTSAScore() + + getPrehistoricScore() + + getMarsScore() + + getNoradScore() + + getWSCScore() + + getGandhiScore(); +} + +///////////////////////////////////////////// +// +// Caldoria data + +void GameStateManager::writeCaldoriaState(Common::WriteStream *stream) { + _caldoriaFlags.writeToStream(stream); + stream->writeUint32BE(_caldoriaFuseTimeLimit); +} + +void GameStateManager::readCaldoriaState(Common::ReadStream *stream) { + _caldoriaFlags.readFromStream(stream); + _caldoriaFuseTimeLimit = stream->readUint32BE(); +} + +void GameStateManager::resetCaldoriaState() { + _caldoriaFlags.clearAllFlags(); + _caldoriaFuseTimeLimit = 0; +} + +///////////////////////////////////////////// +// +// TSA data + +void GameStateManager::writeTSAState(Common::WriteStream *stream) { + _TSAFlags.writeToStream(stream); + stream->writeUint32BE(_TSARipTimerTime); + stream->writeUint32BE(_TSAFuseTimeLimit); + stream->writeByte(_TSAState); + stream->writeByte(_T0BMonitorMode); + stream->writeUint32BE(_T0BMonitorStart); +} + +void GameStateManager::readTSAState(Common::ReadStream *stream) { + _TSAFlags.readFromStream(stream); + _TSARipTimerTime = stream->readUint32BE(); + _TSAFuseTimeLimit = stream->readUint32BE(); + _TSAState = stream->readByte(); + _T0BMonitorMode = stream->readByte(); + _T0BMonitorStart = stream->readUint32BE(); +} + +void GameStateManager::resetTSAState() { + _TSAFlags.clearAllFlags(); + _TSAState = 0; + _T0BMonitorMode = 0; + _T0BMonitorStart = 0; + _TSARipTimerTime = 0; + _TSAFuseTimeLimit = kTSAUncreatedTimeLimit; +} + +///////////////////////////////////////////// +// +// Prehistoric data + +void GameStateManager::writePrehistoricState(Common::WriteStream *stream) { + _prehistoricFlags.writeToStream(stream); +} + +void GameStateManager::readPrehistoricState(Common::ReadStream *stream) { + _prehistoricFlags.readFromStream(stream); +} + +void GameStateManager::resetPrehistoricState() { + _prehistoricFlags.clearAllFlags(); +} + +///////////////////////////////////////////// +// +// Norad data + +void GameStateManager::writeNoradState(Common::WriteStream *stream) { + _noradFlags.writeToStream(stream); + stream->writeUint16BE(_noradSubRoomPressure); + stream->writeByte(_noradSubPrepState); +} + +void GameStateManager::readNoradState(Common::ReadStream *stream) { + _noradFlags.readFromStream(stream); + _noradSubRoomPressure = stream->readUint16BE(); + _noradSubPrepState = (NoradSubPrepState)stream->readByte(); +} + +void GameStateManager::resetNoradState() { + _noradFlags.clearAllFlags(); + _noradSubRoomPressure = 9; + _noradSubPrepState = kSubNotPrepped; +} + +///////////////////////////////////////////// +// +// Mars data + +void GameStateManager::writeMarsState(Common::WriteStream *stream) { + _marsFlags.writeToStream(stream); +} + +void GameStateManager::readMarsState(Common::ReadStream *stream) { + _marsFlags.readFromStream(stream); +} + +void GameStateManager::resetMarsState() { + _marsFlags.clearAllFlags(); +} + +///////////////////////////////////////////// +// +// WSC data + +void GameStateManager::writeWSCState(Common::WriteStream *stream) { + _WSCFlags.writeToStream(stream); +} + +void GameStateManager::readWSCState(Common::ReadStream *stream) { + _WSCFlags.readFromStream(stream); +} + +void GameStateManager::resetWSCState() { + _WSCFlags.clearAllFlags(); +} + +void GameStateManager::setScoringSawINN(const bool flag) { + _scoringFlags.setFlag(kScoringSawINNFlag, flag); +} + +void GameStateManager::setScoringTookShower(const bool flag) { + _scoringFlags.setFlag(kScoringTookShowerFlag, flag); +} + +void GameStateManager::setScoringFixedHair(const bool flag) { + _scoringFlags.setFlag(kScoringFixedHairFlag, flag); +} + +void GameStateManager::setScoringGotKeyCard(const bool flag) { + _scoringFlags.setFlag(kScoringGotKeyCardFlag, flag); +} + +void GameStateManager::setScoringReadPaper(const bool flag) { + _scoringFlags.setFlag(kScoringReadPaperFlag, flag); +} + +void GameStateManager::setScoringLookThroughTelescope(const bool flag) { + _scoringFlags.setFlag(kScoringLookThroughTelescopeFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaKiosk(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaKioskFlag, flag); +} + +void GameStateManager::setScoringGoToTSA(const bool flag) { + _scoringFlags.setFlag(kScoringGoToTSAFlag, flag); +} + +void GameStateManager::setScoringEnterTSA(const bool flag) { + _scoringFlags.setFlag(kScoringEnterTSAFlag, flag); +} + +void GameStateManager::setScoringSawBust1(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust1Flag, flag); +} + +void GameStateManager::setScoringSawBust2(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust2Flag, flag); +} + +void GameStateManager::setScoringSawBust3(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust3Flag, flag); +} + +void GameStateManager::setScoringSawBust4(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust4Flag, flag); +} + +void GameStateManager::setScoringSawBust5(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust5Flag, flag); +} + +void GameStateManager::setScoringSawBust6(const bool flag) { + _scoringFlags.setFlag(kScoringSawBust6Flag, flag); +} + +void GameStateManager::setScoringSawTheory(const bool flag) { + _scoringFlags.setFlag(kScoringSawTheoryFlag, flag); +} + +void GameStateManager::setScoringSawBackground(const bool flag) { + _scoringFlags.setFlag(kScoringSawBackgroundFlag, flag); +} + +void GameStateManager::setScoringSawProcedure(const bool flag) { + _scoringFlags.setFlag(kScoringSawProcedureFlag, flag); +} + +void GameStateManager::setScoringGotJourneymanKey(const bool flag) { + _scoringFlags.setFlag(kScoringGotJourneymanKeyFlag, flag); +} + +void GameStateManager::setScoringGotPegasusBiochip(const bool flag) { + _scoringFlags.setFlag(kScoringGotPegasusBiochipFlag, flag); +} + +void GameStateManager::setScoringGotBiosuit(const bool flag) { + _scoringFlags.setFlag(kScoringGotBiosuitFlag, flag); +} + +void GameStateManager::setScoringGoToPrehistoric(const bool flag) { + _scoringFlags.setFlag(kScoringGoToPrehistoricFlag, flag); +} + +void GameStateManager::setScoringPutLogInReader(const bool flag) { + _scoringFlags.setFlag(kScoringPutLogInReaderFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaNormalFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawCaldoriaAlteredFlag, flag); +} + +void GameStateManager::setScoringSawNoradNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawNoradNormalFlag, flag); +} + +void GameStateManager::setScoringSawNoradAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawNoradAlteredFlag, flag); +} + +void GameStateManager::setScoringSawMarsNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsNormalFlag, flag); +} + +void GameStateManager::setScoringSawMarsAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsAlteredFlag, flag); +} + +void GameStateManager::setScoringSawWSCNormal(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCNormalFlag, flag); +} + +void GameStateManager::setScoringSawWSCAltered(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCAlteredFlag, flag); +} + +void GameStateManager::setScoringWentToReadyRoom2(const bool flag) { + _scoringFlags.setFlag(kScoringWentToReadyRoom2Flag, flag); +} + +void GameStateManager::setScoringWentAfterSinclair(const bool flag) { + _scoringFlags.setFlag(kScoringWentAfterSinclairFlag, flag); +} + +void GameStateManager::setScoringUsedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCardBombFlag, flag); +} + +void GameStateManager::setScoringShieldedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringShieldedCardBombFlag, flag); +} + +void GameStateManager::setScoringStunnedSinclair(const bool flag) { + _scoringFlags.setFlag(kScoringStunnedSinclairFlag, flag); +} + +void GameStateManager::setScoringDisarmedNuke(const bool flag) { + _scoringFlags.setFlag(kScoringDisarmedNukeFlag, flag); +} + +void GameStateManager::setScoringThrewBreaker(const bool flag) { + _scoringFlags.setFlag(kScoringThrewBreakerFlag, flag); +} + +void GameStateManager::setScoringExtendedBridge(const bool flag) { + _scoringFlags.setFlag(kScoringExtendedBridgeFlag, flag); +} + +void GameStateManager::setScoringGotHistoricalLog(const bool flag) { + _scoringFlags.setFlag(kScoringGotHistoricalLogFlag, flag); +} + +void GameStateManager::setScoringFinishedPrehistoric(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedPrehistoricFlag, flag); +} + +void GameStateManager::setScoringThrownByRobot(const bool flag) { + _scoringFlags.setFlag(kScoringThrownByRobotFlag, flag); +} + +void GameStateManager::setScoringGotMarsCard(const bool flag) { + _scoringFlags.setFlag(kScoringGotMarsCardFlag, flag); +} + +void GameStateManager::setScoringSawMarsKiosk(const bool flag) { + _scoringFlags.setFlag(kScoringSawMarsKioskFlag, flag); +} + +void GameStateManager::setScoringSawTransportMap(const bool flag) { + _scoringFlags.setFlag(kScoringSawTransportMapFlag, flag); +} + +void GameStateManager::setScoringGotCrowBar(const bool flag) { + _scoringFlags.setFlag(kScoringGotCrowBarFlag, flag); +} + +void GameStateManager::setScoringTurnedOnTransport(const bool flag) { + _scoringFlags.setFlag(kScoringTurnedOnTransportFlag, flag); +} + +void GameStateManager::setScoringGotOxygenMask(const bool flag) { + _scoringFlags.setFlag(kScoringGotOxygenMaskFlag, flag); +} + +void GameStateManager::setScoringAvoidedRobot(const bool flag) { + _scoringFlags.setFlag(kScoringAvoidedRobotFlag, flag); +} + +void GameStateManager::setScoringActivatedPlatform(const bool flag) { + _scoringFlags.setFlag(kScoringActivatedPlatformFlag, flag); +} + +void GameStateManager::setScoringUsedLiquidNitrogen(const bool flag) { + _scoringFlags.setFlag(kScoringUsedLiquidNitrogenFlag, flag); +} + +void GameStateManager::setScoringUsedCrowBar(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCrowBarFlag, flag); +} + +void GameStateManager::setScoringFoundCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringFoundCardBombFlag, flag); +} + +void GameStateManager::setScoringDisarmedCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringDisarmedCardBombFlag, flag); +} + +void GameStateManager::setScoringGotCardBomb(const bool flag) { + _scoringFlags.setFlag(kScoringGotCardBombFlag, flag); +} + +void GameStateManager::setScoringThreadedMaze(const bool flag) { + _scoringFlags.setFlag(kScoringThreadedMazeFlag, flag); +} + +void GameStateManager::setScoringThreadedGearRoom(const bool flag) { + _scoringFlags.setFlag(kScoringThreadedGearRoomFlag, flag); +} + +void GameStateManager::setScoringEnteredShuttle(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredShuttleFlag, flag); +} + +void GameStateManager::setScoringEnteredLaunchTube(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredLaunchTubeFlag, flag); +} + +void GameStateManager::setScoringStoppedRobotsShuttle(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedRobotsShuttleFlag, flag); +} + +void GameStateManager::setScoringGotMarsOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotMarsOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedMars(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedMarsFlag, flag); +} + +void GameStateManager::setScoringSawSecurityMonitor(const bool flag) { + _scoringFlags.setFlag(kScoringSawSecurityMonitorFlag, flag); +} + +void GameStateManager::setScoringFilledOxygenCanister(const bool flag) { + _scoringFlags.setFlag(kScoringFilledOxygenCanisterFlag, flag); +} + +void GameStateManager::setScoringFilledArgonCanister(const bool flag) { + _scoringFlags.setFlag(kScoringFilledArgonCanisterFlag, flag); +} + +void GameStateManager::setScoringSawUnconsciousOperator(const bool flag) { + _scoringFlags.setFlag(kScoringSawUnconsciousOperatorFlag, flag); +} + +void GameStateManager::setScoringWentThroughPressureDoor(const bool flag) { + _scoringFlags.setFlag(kScoringWentThroughPressureDoorFlag, flag); +} + +void GameStateManager::setScoringPreppedSub(const bool flag) { + _scoringFlags.setFlag(kScoringPreppedSubFlag, flag); +} + +void GameStateManager::setScoringEnteredSub(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredSubFlag, flag); +} + +void GameStateManager::setScoringExitedSub(const bool flag) { + _scoringFlags.setFlag(kScoringExitedSubFlag, flag); +} + +void GameStateManager::setScoringSawRobotAt54North(const bool flag) { + _scoringFlags.setFlag(kScoringSawRobotAt54NorthFlag, flag); +} + +void GameStateManager::setScoringPlayedWithClaw(const bool flag) { + _scoringFlags.setFlag(kScoringPlayedWithClawFlag, flag); +} + +void GameStateManager::setScoringUsedRetinalChip(const bool flag) { + _scoringFlags.setFlag(kScoringUsedRetinalChipFlag, flag); +} + +void GameStateManager::setScoringFinishedGlobeGame(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedGlobeGameFlag, flag); +} + +void GameStateManager::setScoringStoppedNoradRobot(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedNoradRobotFlag, flag); +} + +void GameStateManager::setScoringGotNoradOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotNoradOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedNorad(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedNoradFlag, flag); +} + +void GameStateManager::setScoringRemovedDart(const bool flag) { + _scoringFlags.setFlag(kScoringRemovedDartFlag, flag); +} + +void GameStateManager::setScoringAnalyzedDart(const bool flag) { + _scoringFlags.setFlag(kScoringAnalyzedDartFlag, flag); +} + +void GameStateManager::setScoringBuiltAntidote(const bool flag) { + _scoringFlags.setFlag(kScoringBuiltAntidoteFlag, flag); +} + +void GameStateManager::setScoringGotSinclairKey(const bool flag) { + _scoringFlags.setFlag(kScoringGotSinclairKeyFlag, flag); +} + +void GameStateManager::setScoringGotArgonCanister(const bool flag) { + _scoringFlags.setFlag(kScoringGotArgonCanisterFlag, flag); +} + +void GameStateManager::setScoringGotNitrogenCanister(const bool flag) { + _scoringFlags.setFlag(kScoringGotNitrogenCanisterFlag, flag); +} + +void GameStateManager::setScoringPlayedWithMessages(const bool flag) { + _scoringFlags.setFlag(kScoringPlayedWithMessagesFlag, flag); +} + +void GameStateManager::setScoringSawMorphExperiment(const bool flag) { + _scoringFlags.setFlag(kScoringSawMorphExperimentFlag, flag); +} + +void GameStateManager::setScoringEnteredSinclairOffice(const bool flag) { + _scoringFlags.setFlag(kScoringEnteredSinclairOfficeFlag, flag); +} + +void GameStateManager::setScoringSawBrochure(const bool flag) { + _scoringFlags.setFlag(kScoringSawBrochureFlag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry1(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry1Flag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry2(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry2Flag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry3(const bool flag) { + _scoringFlags.setFlag(kScoringSawSinclairEntry3Flag, flag); +} + +void GameStateManager::setScoringSawWSCDirectory(const bool flag) { + _scoringFlags.setFlag(kScoringSawWSCDirectoryFlag, flag); +} + +void GameStateManager::setScoringUsedCrowBarInWSC(const bool flag) { + _scoringFlags.setFlag(kScoringUsedCrowBarInWSCFlag, flag); +} + +void GameStateManager::setScoringFinishedPlasmaDodge(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedPlasmaDodgeFlag, flag); +} + +void GameStateManager::setScoringOpenedCatwalk(const bool flag) { + _scoringFlags.setFlag(kScoringOpenedCatwalkFlag, flag); +} + +void GameStateManager::setScoringStoppedWSCRobot(const bool flag) { + _scoringFlags.setFlag(kScoringStoppedWSCRobotFlag, flag); +} + +void GameStateManager::setScoringGotWSCOpMemChip(const bool flag) { + _scoringFlags.setFlag(kScoringGotWSCOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedWSC(const bool flag) { + _scoringFlags.setFlag(kScoringFinishedWSCFlag, flag); +} + +void GameStateManager::setScoringMarsGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringMarsGandhiFlag, flag); +} + +void GameStateManager::setScoringNoradGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringNoradGandhiFlag, flag); +} + +void GameStateManager::setScoringWSCGandhi(const bool flag) { + _scoringFlags.setFlag(kScoringWSCGandhiFlag, flag); +} + +bool GameStateManager::getScoringSawINN() { + return _scoringFlags.getFlag(kScoringSawINNFlag); +} + +bool GameStateManager::getScoringTookShower() { + return _scoringFlags.getFlag(kScoringTookShowerFlag); +} + +bool GameStateManager::getScoringFixedHair() { + return _scoringFlags.getFlag(kScoringFixedHairFlag); +} + +bool GameStateManager::getScoringGotKeyCard() { + return _scoringFlags.getFlag(kScoringGotKeyCardFlag); +} + +bool GameStateManager::getScoringReadPaper() { + return _scoringFlags.getFlag(kScoringReadPaperFlag); +} + +bool GameStateManager::getScoringLookThroughTelescope() { + return _scoringFlags.getFlag(kScoringLookThroughTelescopeFlag); +} + +bool GameStateManager::getScoringSawCaldoriaKiosk() { + return _scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag); +} + +bool GameStateManager::getScoringGoToTSA() { + return _scoringFlags.getFlag(kScoringGoToTSAFlag); +} + +bool GameStateManager::getScoringEnterTSA() { + return _scoringFlags.getFlag(kScoringEnterTSAFlag); +} + +bool GameStateManager::getScoringSawBust1() { + return _scoringFlags.getFlag(kScoringSawBust1Flag); +} + +bool GameStateManager::getScoringSawBust2() { + return _scoringFlags.getFlag(kScoringSawBust2Flag); +} + +bool GameStateManager::getScoringSawBust3() { + return _scoringFlags.getFlag(kScoringSawBust3Flag); +} + +bool GameStateManager::getScoringSawBust4() { + return _scoringFlags.getFlag(kScoringSawBust4Flag); +} + +bool GameStateManager::getScoringSawBust5() { + return _scoringFlags.getFlag(kScoringSawBust5Flag); +} + +bool GameStateManager::getScoringSawBust6() { + return _scoringFlags.getFlag(kScoringSawBust6Flag); +} + +bool GameStateManager::getScoringSawTheory() { + return _scoringFlags.getFlag(kScoringSawTheoryFlag); +} + +bool GameStateManager::getScoringSawBackground() { + return _scoringFlags.getFlag(kScoringSawBackgroundFlag); +} + +bool GameStateManager::getScoringSawProcedure() { + return _scoringFlags.getFlag(kScoringSawProcedureFlag); +} + +bool GameStateManager::getScoringGotJourneymanKey() { + return _scoringFlags.getFlag(kScoringGotJourneymanKeyFlag); +} + +bool GameStateManager::getScoringGotPegasusBiochip() { + return _scoringFlags.getFlag(kScoringGotPegasusBiochipFlag); +} + +bool GameStateManager::getScoringGotBiosuit() { + return _scoringFlags.getFlag(kScoringGotBiosuitFlag); +} + +bool GameStateManager::getScoringGoToPrehistoric() { + return _scoringFlags.getFlag(kScoringGoToPrehistoricFlag); +} + +bool GameStateManager::getScoringPutLogInReader() { + return _scoringFlags.getFlag(kScoringPutLogInReaderFlag); +} + +bool GameStateManager::getScoringSawCaldoriaNormal() { + return _scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag); +} + +bool GameStateManager::getScoringSawCaldoriaAltered() { + return _scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag); +} + +bool GameStateManager::getScoringSawNoradNormal() { + return _scoringFlags.getFlag(kScoringSawNoradNormalFlag); +} + +bool GameStateManager::getScoringSawNoradAltered() { + return _scoringFlags.getFlag(kScoringSawNoradAlteredFlag); +} + +bool GameStateManager::getScoringSawMarsNormal() { + return _scoringFlags.getFlag(kScoringSawMarsNormalFlag); +} + +bool GameStateManager::getScoringSawMarsAltered() { + return _scoringFlags.getFlag(kScoringSawMarsAlteredFlag); +} + +bool GameStateManager::getScoringSawWSCNormal() { + return _scoringFlags.getFlag(kScoringSawWSCNormalFlag); +} + +bool GameStateManager::getScoringSawWSCAltered() { + return _scoringFlags.getFlag(kScoringSawWSCAlteredFlag); +} + +bool GameStateManager::getScoringWentToReadyRoom2() { + return _scoringFlags.getFlag(kScoringWentToReadyRoom2Flag); +} + +bool GameStateManager::getScoringWentAfterSinclair() { + return _scoringFlags.getFlag(kScoringWentAfterSinclairFlag); +} + +bool GameStateManager::getScoringUsedCardBomb() { + return _scoringFlags.getFlag(kScoringUsedCardBombFlag); +} + +bool GameStateManager::getScoringShieldedCardBomb() { + return _scoringFlags.getFlag(kScoringShieldedCardBombFlag); +} + +bool GameStateManager::getScoringStunnedSinclair() { + return _scoringFlags.getFlag(kScoringStunnedSinclairFlag); +} + +bool GameStateManager::getScoringDisarmedNuke() { + return _scoringFlags.getFlag(kScoringDisarmedNukeFlag); +} + +bool GameStateManager::getScoringThrewBreaker() { + return _scoringFlags.getFlag(kScoringThrewBreakerFlag); +} + +bool GameStateManager::getScoringExtendedBridge() { + return _scoringFlags.getFlag(kScoringExtendedBridgeFlag); +} + +bool GameStateManager::getScoringGotHistoricalLog() { + return _scoringFlags.getFlag(kScoringGotHistoricalLogFlag); +} + +bool GameStateManager::getScoringFinishedPrehistoric() { + return _scoringFlags.getFlag(kScoringFinishedPrehistoricFlag); +} + +bool GameStateManager::getScoringThrownByRobot() { + return _scoringFlags.getFlag(kScoringThrownByRobotFlag); +} + +bool GameStateManager::getScoringGotMarsCard() { + return _scoringFlags.getFlag(kScoringGotMarsCardFlag); +} + +bool GameStateManager::getScoringSawMarsKiosk() { + return _scoringFlags.getFlag(kScoringSawMarsKioskFlag); +} + +bool GameStateManager::getScoringSawTransportMap() { + return _scoringFlags.getFlag(kScoringSawTransportMapFlag); +} + +bool GameStateManager::getScoringGotCrowBar() { + return _scoringFlags.getFlag(kScoringGotCrowBarFlag); +} + +bool GameStateManager::getScoringTurnedOnTransport() { + return _scoringFlags.getFlag(kScoringTurnedOnTransportFlag); +} + +bool GameStateManager::getScoringGotOxygenMask() { + return _scoringFlags.getFlag(kScoringGotOxygenMaskFlag); +} + +bool GameStateManager::getScoringAvoidedRobot() { + return _scoringFlags.getFlag(kScoringAvoidedRobotFlag); +} + +bool GameStateManager::getScoringActivatedPlatform() { + return _scoringFlags.getFlag(kScoringActivatedPlatformFlag); +} + +bool GameStateManager::getScoringUsedLiquidNitrogen() { + return _scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag); +} + +bool GameStateManager::getScoringUsedCrowBar() { + return _scoringFlags.getFlag(kScoringUsedCrowBarFlag); +} + +bool GameStateManager::getScoringFoundCardBomb() { + return _scoringFlags.getFlag(kScoringFoundCardBombFlag); +} + +bool GameStateManager::getScoringDisarmedCardBomb() { + return _scoringFlags.getFlag(kScoringDisarmedCardBombFlag); +} + +bool GameStateManager::getScoringGotCardBomb() { + return _scoringFlags.getFlag(kScoringGotCardBombFlag); +} + +bool GameStateManager::getScoringThreadedMaze() { + return _scoringFlags.getFlag(kScoringThreadedMazeFlag); +} + +bool GameStateManager::getScoringThreadedGearRoom() { + return _scoringFlags.getFlag(kScoringThreadedGearRoomFlag); +} + +bool GameStateManager::getScoringEnteredShuttle() { + return _scoringFlags.getFlag(kScoringEnteredShuttleFlag); +} + +bool GameStateManager::getScoringEnteredLaunchTube() { + return _scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag); +} + +bool GameStateManager::getScoringStoppedRobotsShuttle() { + return _scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag); +} + +bool GameStateManager::getScoringGotMarsOpMemChip() { + return _scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedMars() { + return _scoringFlags.getFlag(kScoringFinishedMarsFlag); +} + +bool GameStateManager::getScoringSawSecurityMonitor() { + return _scoringFlags.getFlag(kScoringSawSecurityMonitorFlag); +} + +bool GameStateManager::getScoringFilledOxygenCanister() { + return _scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag); +} + +bool GameStateManager::getScoringFilledArgonCanister() { + return _scoringFlags.getFlag(kScoringFilledArgonCanisterFlag); +} + +bool GameStateManager::getScoringSawUnconsciousOperator() { + return _scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag); +} + +bool GameStateManager::getScoringWentThroughPressureDoor() { + return _scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag); +} + +bool GameStateManager::getScoringPreppedSub() { + return _scoringFlags.getFlag(kScoringPreppedSubFlag); +} + +bool GameStateManager::getScoringEnteredSub() { + return _scoringFlags.getFlag(kScoringEnteredSubFlag); +} + +bool GameStateManager::getScoringExitedSub() { + return _scoringFlags.getFlag(kScoringExitedSubFlag); +} + +bool GameStateManager::getScoringSawRobotAt54North() { + return _scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag); +} + +bool GameStateManager::getScoringPlayedWithClaw() { + return _scoringFlags.getFlag(kScoringPlayedWithClawFlag); +} + +bool GameStateManager::getScoringUsedRetinalChip() { + return _scoringFlags.getFlag(kScoringUsedRetinalChipFlag); +} + +bool GameStateManager::getScoringFinishedGlobeGame() { + return _scoringFlags.getFlag(kScoringFinishedGlobeGameFlag); +} + +bool GameStateManager::getScoringStoppedNoradRobot() { + return _scoringFlags.getFlag(kScoringStoppedNoradRobotFlag); +} + +bool GameStateManager::getScoringGotNoradOpMemChip() { + return _scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedNorad() { + return _scoringFlags.getFlag(kScoringFinishedNoradFlag); +} + +bool GameStateManager::getScoringRemovedDart() { + return _scoringFlags.getFlag(kScoringRemovedDartFlag); +} + +bool GameStateManager::getScoringAnalyzedDart() { + return _scoringFlags.getFlag(kScoringAnalyzedDartFlag); +} + +bool GameStateManager::getScoringBuiltAntidote() { + return _scoringFlags.getFlag(kScoringBuiltAntidoteFlag); +} + +bool GameStateManager::getScoringGotSinclairKey() { + return _scoringFlags.getFlag(kScoringGotSinclairKeyFlag); +} + +bool GameStateManager::getScoringGotArgonCanister() { + return _scoringFlags.getFlag(kScoringGotArgonCanisterFlag); +} + +bool GameStateManager::getScoringGotNitrogenCanister() { + return _scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag); +} + +bool GameStateManager::getScoringPlayedWithMessages() { + return _scoringFlags.getFlag(kScoringPlayedWithMessagesFlag); +} + +bool GameStateManager::getScoringSawMorphExperiment() { + return _scoringFlags.getFlag(kScoringSawMorphExperimentFlag); +} + +bool GameStateManager::getScoringEnteredSinclairOffice() { + return _scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag); +} + +bool GameStateManager::getScoringSawBrochure() { + return _scoringFlags.getFlag(kScoringSawBrochureFlag); +} + +bool GameStateManager::getScoringSawSinclairEntry1() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry1Flag); +} + +bool GameStateManager::getScoringSawSinclairEntry2() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry2Flag); +} + +bool GameStateManager::getScoringSawSinclairEntry3() { + return _scoringFlags.getFlag(kScoringSawSinclairEntry3Flag); +} + +bool GameStateManager::getScoringSawWSCDirectory() { + return _scoringFlags.getFlag(kScoringSawWSCDirectoryFlag); +} + +bool GameStateManager::getScoringUsedCrowBarInWSC() { + return _scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag); +} + +bool GameStateManager::getScoringFinishedPlasmaDodge() { + return _scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag); +} + +bool GameStateManager::getScoringOpenedCatwalk() { + return _scoringFlags.getFlag(kScoringOpenedCatwalkFlag); +} + +bool GameStateManager::getScoringStoppedWSCRobot() { + return _scoringFlags.getFlag(kScoringStoppedWSCRobotFlag); +} + +bool GameStateManager::getScoringGotWSCOpMemChip() { + return _scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedWSC() { + return _scoringFlags.getFlag(kScoringFinishedWSCFlag); +} + +bool GameStateManager::getScoringMarsGandhi() { + return _scoringFlags.getFlag(kScoringMarsGandhiFlag); +} + +bool GameStateManager::getScoringNoradGandhi() { + return _scoringFlags.getFlag(kScoringNoradGandhiFlag); +} + +bool GameStateManager::getScoringWSCGandhi() { + return _scoringFlags.getFlag(kScoringWSCGandhiFlag); +} + +void GameStateManager::setWalkthroughMode(bool value) { + _globalFlags.setFlag(kGlobalWalkthroughFlag, value); +} + +bool GameStateManager::getWalkthroughMode() { + return _globalFlags.getFlag(kGlobalWalkthroughFlag); +} + +void GameStateManager::setShieldOn(bool value) { + _globalFlags.setFlag(kGlobalShieldOnFlag, value); +} + +bool GameStateManager::getShieldOn() { + return _globalFlags.getFlag(kGlobalShieldOnFlag); +} + +void GameStateManager::setEasterEgg(bool value) { + _globalFlags.setFlag(kGlobalEasterEggFlag, value); +} + +bool GameStateManager::getEasterEgg() { + return _globalFlags.getFlag(kGlobalEasterEggFlag); +} + +void GameStateManager::setBeenToWSC(bool value) { + _globalFlags.setFlag(kGlobalBeenToWSCFlag, value); +} + +bool GameStateManager::getBeenToWSC() { + return _globalFlags.getFlag(kGlobalBeenToWSCFlag); +} + +void GameStateManager::setBeenToMars(bool value) { + _globalFlags.setFlag(kGlobalBeenToMarsFlag, value); +} + +bool GameStateManager::getBeenToMars() { + return _globalFlags.getFlag(kGlobalBeenToMarsFlag); +} + +void GameStateManager::setBeenToNorad(bool value) { + _globalFlags.setFlag(kGlobalBeenToNoradFlag, value); +} + +bool GameStateManager::getBeenToNorad() { + return _globalFlags.getFlag(kGlobalBeenToNoradFlag); +} + +void GameStateManager::setWSCFinished(bool value) { + _globalFlags.setFlag(kGlobalWSCFinishedFlag, value); +} + +bool GameStateManager::getWSCFinished() { + return _globalFlags.getFlag(kGlobalWSCFinishedFlag); +} + +void GameStateManager::setMarsFinished(bool value) { + _globalFlags.setFlag(kGlobalMarsFinishedFlag, value); +} + +bool GameStateManager::getMarsFinished() { + return _globalFlags.getFlag(kGlobalMarsFinishedFlag); +} + +void GameStateManager::setNoradFinished(bool value) { + _globalFlags.setFlag(kGlobalNoradFinishedFlag, value); +} + +bool GameStateManager::getNoradFinished() { + return _globalFlags.getFlag(kGlobalNoradFinishedFlag); +} + +bool GameStateManager::allTimeZonesFinished() { + return getWSCFinished() && getMarsFinished() && getNoradFinished(); +} + +void GameStateManager::setTakenItemID(ItemID id, bool value) { + _itemTakenFlags.setFlag(id, value); +} + +bool GameStateManager::isTakenItemID(ItemID id) { + return _itemTakenFlags.getFlag(id); +} + +void GameStateManager::setTakenItem(Item *item, bool value) { + setTakenItemID(item->getObjectID(), value); +} + +bool GameStateManager::isTakenItem(Item *item) { + return isTakenItemID(item->getObjectID()); +} + +void GameStateManager::setCaldoriaFuseTimeLimit(const TimeValue timeLimit) { + _caldoriaFuseTimeLimit = timeLimit; +} + +TimeValue GameStateManager::getCaldoriaFuseTimeLimit() { + return _caldoriaFuseTimeLimit; +} + +void GameStateManager::setCaldoriaSeenPullback(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenPullbackFlag, value); +} + +bool GameStateManager::getCaldoriaSeenPullback() { + return _caldoriaFlags.getFlag(kCaldoriaSeenPullbackFlag); +} + +void GameStateManager::setCaldoriaMadeOJ(bool value) { + _caldoriaFlags.setFlag(kCaldoriaMadeOJFlag, value); +} + +bool GameStateManager::getCaldoriaMadeOJ() { + return _caldoriaFlags.getFlag(kCaldoriaMadeOJFlag); +} + +void GameStateManager::setCaldoriaWokenUp(bool value) { + _caldoriaFlags.setFlag(kCaldoriaWokenUpFlag, value); +} + +bool GameStateManager::getCaldoriaWokenUp() { + return _caldoriaFlags.getFlag(kCaldoriaWokenUpFlag); +} + +void GameStateManager::setCaldoriaDidRecalibration(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDidRecalibrationFlag, value); +} + +bool GameStateManager::getCaldoriaDidRecalibration() { + return _caldoriaFlags.getFlag(kCaldoriaDidRecalibrationFlag); +} + +void GameStateManager::setCaldoriaSeenSinclairInElevator(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenSinclairInElevatorFlag, value); +} + +bool GameStateManager::getCaldoriaSeenSinclairInElevator() { + return _caldoriaFlags.getFlag(kCaldoriaSeenSinclairInElevatorFlag); +} + +void GameStateManager::setCaldoriaINNAnnouncing(bool value) { + _caldoriaFlags.setFlag(kCaldoriaINNAnnouncingFlag, value); +} + +bool GameStateManager::getCaldoriaINNAnnouncing() { + return _caldoriaFlags.getFlag(kCaldoriaINNAnnouncingFlag); +} + +void GameStateManager::setCaldoriaSeenINN(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenINNFlag, value); +} + +bool GameStateManager::getCaldoriaSeenINN() { + return _caldoriaFlags.getFlag(kCaldoriaSeenINNFlag); +} + +void GameStateManager::setCaldoriaSeenMessages(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSeenMessagesFlag, value); +} + +bool GameStateManager::getCaldoriaSeenMessages() { + return _caldoriaFlags.getFlag(kCaldoriaSeenMessagesFlag); +} + +void GameStateManager::setCaldoriaSinclairShot(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSinclairShotFlag, value); +} + +bool GameStateManager::getCaldoriaSinclairShot() { + return _caldoriaFlags.getFlag(kCaldoriaSinclairShotFlag); +} + +void GameStateManager::setCaldoriaBombDisarmed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaBombDisarmedFlag, value); +} + +bool GameStateManager::getCaldoriaBombDisarmed() { + return _caldoriaFlags.getFlag(kCaldoriaBombDisarmedFlag); +} + +void GameStateManager::setCaldoriaRoofDoorOpen(bool value) { + _caldoriaFlags.setFlag(kCaldoriaRoofDoorOpenFlag, value); +} + +bool GameStateManager::getCaldoriaRoofDoorOpen() { + return _caldoriaFlags.getFlag(kCaldoriaRoofDoorOpenFlag); +} + +void GameStateManager::setCaldoriaDoneHygiene(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDoneHygieneFlag, value); +} + +bool GameStateManager::getCaldoriaDoneHygiene() { + return _caldoriaFlags.getFlag(kCaldoriaDoneHygieneFlag); +} + +void GameStateManager::setCaldoriaSawVoiceAnalysis(bool value) { + _caldoriaFlags.setFlag(kCaldoriaSawVoiceAnalysisFlag, value); +} + +bool GameStateManager::getCaldoriaSawVoiceAnalysis() { + return _caldoriaFlags.getFlag(kCaldoriaSawVoiceAnalysisFlag); +} + +void GameStateManager::setCaldoriaDoorBombed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaDoorBombedFlag, value); +} + +bool GameStateManager::getCaldoriaDoorBombed() { + return _caldoriaFlags.getFlag(kCaldoriaDoorBombedFlag); +} + +void GameStateManager::setCaldoriaGunAimed(bool value) { + _caldoriaFlags.setFlag(kCaldoriaGunAimedFlag, value); +} + +bool GameStateManager::getCaldoriaGunAimed() { + return _caldoriaFlags.getFlag(kCaldoriaGunAimedFlag); +} + +void GameStateManager::setRipTimerTime(TimeValue limit) { + _TSARipTimerTime = limit; +} + +TimeValue GameStateManager::getRipTimerTime() { + return _TSARipTimerTime; +} + +void GameStateManager::setTSAFuseTimeLimit(TimeValue limit) { + _TSAFuseTimeLimit = limit; +} + +TimeValue GameStateManager::getTSAFuseTimeLimit() { + return _TSAFuseTimeLimit; +} + +void GameStateManager::setTSAState(byte state) { + _TSAState = state; +} + +byte GameStateManager::getTSAState() { + return _TSAState; +} + +void GameStateManager::setT0BMonitorMode(byte mode) { + _T0BMonitorMode = mode; +} + +byte GameStateManager::getT0BMonitorMode() { + return _T0BMonitorMode; +} + +void GameStateManager::setT0BMonitorStart(TimeValue start) { + _T0BMonitorStart = start; +} + +TimeValue GameStateManager::getT0BMonitorStart() { + return _T0BMonitorStart; +} + +void GameStateManager::setTSAIDedAtDoor(bool value) { + _TSAFlags.setFlag(kTSAIDedAtDoorFlag, value); +} + +bool GameStateManager::getTSAIDedAtDoor() { + return _TSAFlags.getFlag(kTSAIDedAtDoorFlag); +} + +void GameStateManager::setTSA0BZoomedIn(bool value) { + _TSAFlags.setFlag(kTSA0BZoomedInFlag, value); +} + +bool GameStateManager::getTSA0BZoomedIn() { + return _TSAFlags.getFlag(kTSA0BZoomedInFlag); +} + +void GameStateManager::setTSAFrontDoorUnlockedOutside(bool value) { + _TSAFlags.setFlag(kTSAFrontDoorUnlockedOutsideFlag, value); +} + +bool GameStateManager::getTSAFrontDoorUnlockedOutside() { + return _TSAFlags.getFlag(kTSAFrontDoorUnlockedOutsideFlag); +} + +void GameStateManager::setTSAFrontDoorUnlockedInside(bool value) { + _TSAFlags.setFlag(kTSAFrontDoorUnlockedInsideFlag, value); +} + +bool GameStateManager::getTSAFrontDoorUnlockedInside() { + return _TSAFlags.getFlag(kTSAFrontDoorUnlockedInsideFlag); +} + +void GameStateManager::setTSASeenRobotGreeting(bool value) { + _TSAFlags.setFlag(kTSASeenRobotGreetingFlag, value); +} + +bool GameStateManager::getTSASeenRobotGreeting() { + return _TSAFlags.getFlag(kTSASeenRobotGreetingFlag); +} + +void GameStateManager::setTSASeenTheory(bool value) { + _TSAFlags.setFlag(kTSASeenTheoryFlag, value); +} + +bool GameStateManager::getTSASeenTheory() { + return _TSAFlags.getFlag(kTSASeenTheoryFlag); +} + +void GameStateManager::setTSASeenBackground(bool value) { + _TSAFlags.setFlag(kTSASeenBackgroundFlag, value); +} + +bool GameStateManager::getTSASeenBackground() { + return _TSAFlags.getFlag(kTSASeenBackgroundFlag); +} + +void GameStateManager::setTSASeenProcedure(bool value) { + _TSAFlags.setFlag(kTSASeenProcedureFlag, value); +} + +bool GameStateManager::getTSASeenProcedure() { + return _TSAFlags.getFlag(kTSASeenProcedureFlag); +} + +void GameStateManager::setTSASeenAgent3AtDoor(bool value) { + _TSAFlags.setFlag(kTSASeenAgent3AtDoorFlag, value); +} + +bool GameStateManager::getTSASeenAgent3AtDoor() { + return _TSAFlags.getFlag(kTSASeenAgent3AtDoorFlag); +} + +void GameStateManager::setTSACommandCenterLocked(bool value) { + _TSAFlags.setFlag(kTSACommandCenterLockedFlag, value); +} + +bool GameStateManager::getTSACommandCenterLocked() { + return _TSAFlags.getFlag(kTSACommandCenterLockedFlag); +} + +void GameStateManager::setTSASeenCaldoriaNormal(bool value) { + _TSAFlags.setFlag(kTSASeenCaldoriaNormalFlag, value); +} + +bool GameStateManager::getTSASeenCaldoriaNormal() { + return _TSAFlags.getFlag(kTSASeenCaldoriaNormalFlag); +} + +void GameStateManager::setTSASeenCaldoriaAltered(bool value) { + _TSAFlags.setFlag(kTSASeenCaldoriaAlteredFlag, value); +} + +bool GameStateManager::getTSASeenCaldoriaAltered() { + return _TSAFlags.getFlag(kTSASeenCaldoriaAlteredFlag); +} + +void GameStateManager::setTSASeenNoradNormal(bool value) { + _TSAFlags.setFlag(kTSASeenNoradNormalFlag, value); +} + +bool GameStateManager::getTSASeenNoradNormal() { + return _TSAFlags.getFlag(kTSASeenNoradNormalFlag); +} + +void GameStateManager::setTSASeenNoradAltered(bool value) { + _TSAFlags.setFlag(kTSASeenNoradAlteredFlag, value); +} + +bool GameStateManager::getTSASeenNoradAltered() { + return _TSAFlags.getFlag(kTSASeenNoradAlteredFlag); +} + +void GameStateManager::setTSASeenMarsNormal(bool value) { + _TSAFlags.setFlag(kTSASeenMarsNormalFlag, value); +} + +bool GameStateManager::getTSASeenMarsNormal() { + return _TSAFlags.getFlag(kTSASeenMarsNormalFlag); +} + +void GameStateManager::setTSASeenMarsAltered(bool value) { + _TSAFlags.setFlag(kTSASeenMarsAlteredFlag, value); +} + +bool GameStateManager::getTSASeenMarsAltered() { + return _TSAFlags.getFlag(kTSASeenMarsAlteredFlag); +} + +void GameStateManager::setTSASeenWSCNormal(bool value) { + _TSAFlags.setFlag(kTSASeenWSCNormalFlag, value); +} + +bool GameStateManager::getTSASeenWSCNormal() { + return _TSAFlags.getFlag(kTSASeenWSCNormalFlag); +} + +void GameStateManager::setTSASeenWSCAltered(bool value) { + _TSAFlags.setFlag(kTSASeenWSCAlteredFlag, value); +} + +bool GameStateManager::getTSASeenWSCAltered() { + return _TSAFlags.getFlag(kTSASeenWSCAlteredFlag); +} + +void GameStateManager::setTSABiosuitOn(bool value) { + _TSAFlags.setFlag(kTSABiosuitOnFlag, value); +} + +bool GameStateManager::getTSABiosuitOn() { + return _TSAFlags.getFlag(kTSABiosuitOnFlag); +} + +void GameStateManager::setPrehistoricTriedToExtendBridge(bool value) { + _prehistoricFlags.setFlag(kPrehistoricTriedToExtendBridgeFlag, value); +} + +bool GameStateManager::getPrehistoricTriedToExtendBridge() { + return _prehistoricFlags.getFlag(kPrehistoricTriedToExtendBridgeFlag); +} + +void GameStateManager::setPrehistoricSeenTimeStream(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenTimeStreamFlag, value); +} + +bool GameStateManager::getPrehistoricSeenTimeStream() { + return _prehistoricFlags.getFlag(kPrehistoricSeenTimeStreamFlag); +} + +void GameStateManager::setPrehistoricSeenFlyer1(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenFlyer1Flag, value); +} + +bool GameStateManager::getPrehistoricSeenFlyer1() { + return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer1Flag); +} + +void GameStateManager::setPrehistoricSeenFlyer2(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenFlyer2Flag, value); +} + +bool GameStateManager::getPrehistoricSeenFlyer2() { + return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer2Flag); +} + +void GameStateManager::setPrehistoricSeenBridgeZoom(bool value) { + _prehistoricFlags.setFlag(kPrehistoricSeenBridgeZoomFlag, value); +} + +bool GameStateManager::getPrehistoricSeenBridgeZoom() { + return _prehistoricFlags.getFlag(kPrehistoricSeenBridgeZoomFlag); +} + +void GameStateManager::setPrehistoricBreakerThrown(bool value) { + _prehistoricFlags.setFlag(kPrehistoricBreakerThrownFlag, value); +} + +bool GameStateManager::getPrehistoricBreakerThrown() { + return _prehistoricFlags.getFlag(kPrehistoricBreakerThrownFlag); +} + +void GameStateManager::setNoradSeenTimeStream(bool value) { + _noradFlags.setFlag(kNoradSeenTimeStreamFlag, value); +} + +bool GameStateManager::getNoradSeenTimeStream() { + return _noradFlags.getFlag(kNoradSeenTimeStreamFlag); +} + +void GameStateManager::setNoradGassed(bool value) { + _noradFlags.setFlag(kNoradGassedFlag, value); +} + +bool GameStateManager::getNoradGassed() { + return _noradFlags.getFlag(kNoradGassedFlag); +} + +void GameStateManager::setNoradFillingStationOn(bool value) { + _noradFlags.setFlag(kNoradFillingStationOnFlag, value); +} + +bool GameStateManager::getNoradFillingStationOn() { + return _noradFlags.getFlag(kNoradFillingStationOnFlag); +} + +void GameStateManager::setNoradN22MessagePlayed(bool value) { + _noradFlags.setFlag(kNoradN22MessagePlayedFlag, value); +} + +bool GameStateManager::getNoradN22MessagePlayed() { + return _noradFlags.getFlag(kNoradN22MessagePlayedFlag); +} + +void GameStateManager::setNoradPlayedGlobeGame(bool value) { + _noradFlags.setFlag(kNoradPlayedGlobeGameFlag, value); +} + +bool GameStateManager::getNoradPlayedGlobeGame() { + return _noradFlags.getFlag(kNoradPlayedGlobeGameFlag); +} + +void GameStateManager::setNoradBeatRobotWithClaw(bool value) { + _noradFlags.setFlag(kNoradBeatRobotWithClawFlag, value); +} + +bool GameStateManager::getNoradBeatRobotWithClaw() { + return _noradFlags.getFlag(kNoradBeatRobotWithClawFlag); +} + +void GameStateManager::setNoradBeatRobotWithDoor(bool value) { + _noradFlags.setFlag(kNoradBeatRobotWithDoorFlag, value); +} + +bool GameStateManager::getNoradBeatRobotWithDoor() { + return _noradFlags.getFlag(kNoradBeatRobotWithDoorFlag); +} + +void GameStateManager::setNoradRetScanGood(bool value) { + _noradFlags.setFlag(kNoradRetScanGoodFlag, value); +} + +bool GameStateManager::getNoradRetScanGood() { + return _noradFlags.getFlag(kNoradRetScanGoodFlag); +} + +void GameStateManager::setNoradWaitingForLaser(bool value) { + _noradFlags.setFlag(kNoradWaitingForLaserFlag, value); +} + +bool GameStateManager::getNoradWaitingForLaser() { + return _noradFlags.getFlag(kNoradWaitingForLaserFlag); +} + +void GameStateManager::setNoradSubRoomPressure(uint16 pressure) { + _noradSubRoomPressure = pressure; +} + +uint16 GameStateManager::getNoradSubRoomPressure() { + return _noradSubRoomPressure; +} + +void GameStateManager::setNoradSubPrepState(NoradSubPrepState state) { + _noradSubPrepState = state; +} + +NoradSubPrepState GameStateManager::getNoradSubPrepState() { + return _noradSubPrepState; +} + +void GameStateManager::setNoradArrivedFromSub(bool value) { + _noradFlags.setFlag(kNoradArrivedFromSubFlag, value); +} + +bool GameStateManager::getNoradArrivedFromSub() { + return _noradFlags.getFlag(kNoradArrivedFromSubFlag); +} + +void GameStateManager::setMarsSeenTimeStream(bool value) { + _marsFlags.setFlag(kMarsSeenTimeStreamFlag, value); +} + +bool GameStateManager::getMarsSeenTimeStream() { + return _marsFlags.getFlag(kMarsSeenTimeStreamFlag); +} + +void GameStateManager::setMarsHeardUpperPodMessage(bool value) { + _marsFlags.setFlag(kMarsHeardUpperPodMessageFlag, value); +} + +bool GameStateManager::getMarsHeardUpperPodMessage() { + return _marsFlags.getFlag(kMarsHeardUpperPodMessageFlag); +} + +void GameStateManager::setMarsRobotThrownPlayer(bool value) { + _marsFlags.setFlag(kMarsRobotThrownPlayerFlag, value); +} + +bool GameStateManager::getMarsRobotThrownPlayer() { + return _marsFlags.getFlag(kMarsRobotThrownPlayerFlag); +} + +void GameStateManager::setMarsHeardCheckInMessage(bool value) { + _marsFlags.setFlag(kMarsHeardCheckInMessageFlag, value); +} + +bool GameStateManager::getMarsHeardCheckInMessage() { + return _marsFlags.getFlag(kMarsHeardCheckInMessageFlag); +} + +void GameStateManager::setMarsPodAtUpperPlatform(bool value) { + _marsFlags.setFlag(kMarsPodAtUpperPlatformFlag, value); +} + +bool GameStateManager::getMarsPodAtUpperPlatform() { + return _marsFlags.getFlag(kMarsPodAtUpperPlatformFlag); +} + +void GameStateManager::setMarsSeenThermalScan(bool value) { + _marsFlags.setFlag(kMarsSeenThermalScanFlag, value); +} + +bool GameStateManager::getMarsSeenThermalScan() { + return _marsFlags.getFlag(kMarsSeenThermalScanFlag); +} + +void GameStateManager::setMarsArrivedBelow(bool value) { + _marsFlags.setFlag(kMarsArrivedBelowFlag, value); +} + +bool GameStateManager::getMarsArrivedBelow() { + return _marsFlags.getFlag(kMarsArrivedBelowFlag); +} + +void GameStateManager::setMarsSeenRobotAtReactor(bool value) { + _marsFlags.setFlag(kMarsSeenRobotAtReactorFlag, value); +} + +bool GameStateManager::getMarsSeenRobotAtReactor() { + return _marsFlags.getFlag(kMarsSeenRobotAtReactorFlag); +} + +void GameStateManager::setMarsAvoidedReactorRobot(bool value) { + _marsFlags.setFlag(kMarsAvoidedReactorRobotFlag, value); +} + +bool GameStateManager::getMarsAvoidedReactorRobot() { + return _marsFlags.getFlag(kMarsAvoidedReactorRobotFlag); +} + +void GameStateManager::setMarsSecurityDown(bool value) { + _marsFlags.setFlag(kMarsSecurityDownFlag, value); +} + +bool GameStateManager::getMarsSecurityDown() { + return _marsFlags.getFlag(kMarsSecurityDownFlag); +} + +void GameStateManager::setMarsInAirlock(bool value) { + _marsFlags.setFlag(kMarsInAirlockFlag, value); +} + +bool GameStateManager::getMarsInAirlock() { + return _marsFlags.getFlag(kMarsInAirlockFlag); +} + +void GameStateManager::setMarsAirlockOpen(bool value) { + _marsFlags.setFlag(kMarsAirlockOpenFlag, value); +} + +bool GameStateManager::getMarsAirlockOpen() { + return _marsFlags.getFlag(kMarsAirlockOpenFlag); +} + +void GameStateManager::setMarsMaskOnFiller(bool value) { + _marsFlags.setFlag(kMarsMaskOnFillerFlag, value); +} + +bool GameStateManager::getMarsMaskOnFiller() { + return _marsFlags.getFlag(kMarsMaskOnFillerFlag); +} + +void GameStateManager::setMarsLockFrozen(bool value) { + _marsFlags.setFlag(kMarsLockFrozenFlag, value); +} + +bool GameStateManager::getMarsLockFrozen() { + return _marsFlags.getFlag(kMarsLockFrozenFlag); +} + +void GameStateManager::setMarsLockBroken(bool value) { + _marsFlags.setFlag(kMarsLockBrokenFlag, value); +} + +bool GameStateManager::getMarsLockBroken() { + return _marsFlags.getFlag(kMarsLockBrokenFlag); +} + +void GameStateManager::setMarsMazeDoorPair1(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair1Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair1() { + return _marsFlags.getFlag(kMarsMazeDoorPair1Flag); +} + +void GameStateManager::setMarsMazeDoorPair2(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair2Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair2() { + return _marsFlags.getFlag(kMarsMazeDoorPair2Flag); +} + +void GameStateManager::setMarsMazeDoorPair3(bool value) { + _marsFlags.setFlag(kMarsMazeDoorPair3Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair3() { + return _marsFlags.getFlag(kMarsMazeDoorPair3Flag); +} + +void GameStateManager::setMarsSawRobotLeave(bool value) { + _marsFlags.setFlag(kMarsSawRobotLeaveFlag, value); +} + +bool GameStateManager::getMarsSawRobotLeave() { + return _marsFlags.getFlag(kMarsSawRobotLeaveFlag); +} + +void GameStateManager::setMarsHitRobotWithCannon(bool flag) { + _marsFlags.setFlag(kMarsHitRobotWithCannonFlag, flag); +} + +bool GameStateManager::getMarsHitRobotWithCannon() { + return _marsFlags.getFlag(kMarsHitRobotWithCannonFlag); +} + +void GameStateManager::setMarsReadyForShuttleTransport(bool value) { + _marsFlags.setFlag(kMarsReadyForShuttleTransportFlag, value); +} + +bool GameStateManager::getMarsReadyForShuttleTransport() { + return _marsFlags.getFlag(kMarsReadyForShuttleTransportFlag); +} + +void GameStateManager::setMarsFinishedCanyonChase(bool flag) { + _marsFlags.setFlag(kMarsFinishedCanyonChaseFlag, flag); +} + +bool GameStateManager::getMarsFinishedCanyonChase() { + return _marsFlags.getFlag(kMarsFinishedCanyonChaseFlag); +} + +void GameStateManager::setMarsThreadedMaze(bool flag) { + _marsFlags.setFlag(kMarsThreadedMazeFlag, flag); +} + +bool GameStateManager::getMarsThreadedMaze() { + return _marsFlags.getFlag(kMarsThreadedMazeFlag); +} + +void GameStateManager::setWSCSeenTimeStream(bool value) { + _WSCFlags.setFlag(kWSCSeenTimeStreamFlag, value); +} + +bool GameStateManager::getWSCSeenTimeStream() { + return _WSCFlags.getFlag(kWSCSeenTimeStreamFlag); +} + +void GameStateManager::setWSCPoisoned(bool value) { + _WSCFlags.setFlag(kWSCPoisonedFlag, value); +} + +bool GameStateManager::getWSCPoisoned() { + return _WSCFlags.getFlag(kWSCPoisonedFlag); +} + +void GameStateManager::setWSCAnsweredAboutDart(bool value) { + _WSCFlags.setFlag(kWSCAnsweredAboutDartFlag, value); +} + +bool GameStateManager::getWSCAnsweredAboutDart() { + return _WSCFlags.getFlag(kWSCAnsweredAboutDartFlag); +} + +void GameStateManager::setWSCRemovedDart(bool value) { + _WSCFlags.setFlag(kWSCRemovedDartFlag, value); +} + +bool GameStateManager::getWSCRemovedDart() { + return _WSCFlags.getFlag(kWSCRemovedDartFlag); +} + +void GameStateManager::setWSCAnalyzerOn(bool value) { + _WSCFlags.setFlag(kWSCAnalyzerOnFlag, value); +} + +bool GameStateManager::getWSCAnalyzerOn() { + return _WSCFlags.getFlag(kWSCAnalyzerOnFlag); +} + +void GameStateManager::setWSCDartInAnalyzer(bool value) { + _WSCFlags.setFlag(kWSCDartInAnalyzerFlag, value); +} + +bool GameStateManager::getWSCDartInAnalyzer() { + return _WSCFlags.getFlag(kWSCDartInAnalyzerFlag); +} + +void GameStateManager::setWSCAnalyzedDart(bool value) { + _WSCFlags.setFlag(kWSCAnalyzedDartFlag, value); +} + +bool GameStateManager::getWSCAnalyzedDart() { + return _WSCFlags.getFlag(kWSCAnalyzedDartFlag); +} + +void GameStateManager::setWSCSawMorph(bool value) { + _WSCFlags.setFlag(kWSCSawMorphFlag, value); +} + +bool GameStateManager::getWSCSawMorph() { + return _WSCFlags.getFlag(kWSCSawMorphFlag); +} + +void GameStateManager::setWSCDesignedAntidote(bool value) { + _WSCFlags.setFlag(kWSCDesignedAntidoteFlag, value); +} + +bool GameStateManager::getWSCDesignedAntidote() { + return _WSCFlags.getFlag(kWSCDesignedAntidoteFlag); +} + +void GameStateManager::setWSCPickedUpAntidote(bool value) { + _WSCFlags.setFlag(kWSCPickedUpAntidoteFlag, value); +} + +bool GameStateManager::getWSCPickedUpAntidote() { + return _WSCFlags.getFlag(kWSCPickedUpAntidoteFlag); +} + +void GameStateManager::setWSCOfficeMessagesOpen(bool value) { + _WSCFlags.setFlag(kWSCOfficeMessagesOpenFlag, value); +} + +bool GameStateManager::getWSCOfficeMessagesOpen() { + return _WSCFlags.getFlag(kWSCOfficeMessagesOpenFlag); +} + +void GameStateManager::setWSCSeenNerd(bool value) { + _WSCFlags.setFlag(kWSCSeenNerdFlag, value); +} + +bool GameStateManager::getWSCSeenNerd() { + return _WSCFlags.getFlag(kWSCSeenNerdFlag); +} + +void GameStateManager::setWSCHeardPage1(bool value) { + _WSCFlags.setFlag(kWSCHeardPage1Flag, value); +} + +bool GameStateManager::getWSCHeardPage1() { + return _WSCFlags.getFlag(kWSCHeardPage1Flag); +} + +void GameStateManager::setWSCHeardPage2(bool value) { + _WSCFlags.setFlag(kWSCHeardPage2Flag, value); +} + +bool GameStateManager::getWSCHeardPage2() { + return _WSCFlags.getFlag(kWSCHeardPage2Flag); +} + +void GameStateManager::setWSCHeardCheckIn(bool value) { + _WSCFlags.setFlag(kWSCHeardCheckInFlag, value); +} + +bool GameStateManager::getWSCHeardCheckIn() { + return _WSCFlags.getFlag(kWSCHeardCheckInFlag); +} + +void GameStateManager::setWSCDidPlasmaDodge(bool value) { + _WSCFlags.setFlag(kWSCDidPlasmaDodgeFlag, value); +} + +bool GameStateManager::getWSCDidPlasmaDodge() { + return _WSCFlags.getFlag(kWSCDidPlasmaDodgeFlag); +} + +void GameStateManager::setWSCSeenSinclairLecture(bool value) { + _WSCFlags.setFlag(kWSCSeenSinclairLectureFlag, value); +} + +bool GameStateManager::getWSCSeenSinclairLecture() { + return _WSCFlags.getFlag(kWSCSeenSinclairLectureFlag); +} + +void GameStateManager::setWSCBeenAtWSC93(bool value) { + _WSCFlags.setFlag(kWSCBeenAtWSC93Flag, value); +} + +bool GameStateManager::getWSCBeenAtWSC93() { + return _WSCFlags.getFlag(kWSCBeenAtWSC93Flag); +} + +void GameStateManager::setWSCCatwalkDark(bool value) { + _WSCFlags.setFlag(kWSCCatwalkDarkFlag, value); +} + +bool GameStateManager::getWSCCatwalkDark() { + return _WSCFlags.getFlag(kWSCCatwalkDarkFlag); +} + +void GameStateManager::setWSCRobotDead(bool value) { + _WSCFlags.setFlag(kWSCRobotDeadFlag, value); +} + +bool GameStateManager::getWSCRobotDead() { + return _WSCFlags.getFlag(kWSCRobotDeadFlag); +} + +void GameStateManager::setWSCRobotGone(bool value) { + _WSCFlags.setFlag(kWSCRobotGoneFlag, value); +} + +bool GameStateManager::getWSCRobotGone() { + return _WSCFlags.getFlag(kWSCRobotGoneFlag); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/gamestate.h b/engines/pegasus/gamestate.h new file mode 100644 index 0000000000..dd47bd6e51 --- /dev/null +++ b/engines/pegasus/gamestate.h @@ -0,0 +1,886 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_GAMESTATE_H +#define PEGASUS_GAMESTATE_H + +#include "common/singleton.h" +#include "common/util.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" +#include "pegasus/items/item.h" + +namespace Common { + class Error; + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +// The only things saved in here are things which get written out to a saved game file... + +enum { + kGlobalWalkthroughFlag, + kGlobalShieldOnFlag, + kGlobalEasterEggFlag, + kGlobalBeenToWSCFlag, + kGlobalBeenToMarsFlag, + kGlobalBeenToNoradFlag, + kGlobalWSCFinishedFlag, + kGlobalMarsFinishedFlag, + kGlobalNoradFinishedFlag, + kNumGlobalFlags +}; + +enum { + kScoringSawINNFlag, + kScoringTookShowerFlag, + kScoringFixedHairFlag, + kScoringGotKeyCardFlag, + kScoringReadPaperFlag, + kScoringLookThroughTelescopeFlag, + kScoringSawCaldoriaKioskFlag, + kScoringGoToTSAFlag, + kScoringEnterTSAFlag, + kScoringSawBust1Flag, + kScoringSawBust2Flag, + kScoringSawBust3Flag, + kScoringSawBust4Flag, + kScoringSawBust5Flag, + kScoringSawBust6Flag, + kScoringSawTheoryFlag, + kScoringSawBackgroundFlag, + kScoringSawProcedureFlag, + kScoringGotJourneymanKeyFlag, + kScoringGotPegasusBiochipFlag, + kScoringGotBiosuitFlag, + kScoringGoToPrehistoricFlag, + kScoringPutLogInReaderFlag, + kScoringSawCaldoriaNormalFlag, + kScoringSawCaldoriaAlteredFlag, + kScoringSawNoradNormalFlag, + kScoringSawNoradAlteredFlag, + kScoringSawMarsNormalFlag, + kScoringSawMarsAlteredFlag, + kScoringSawWSCNormalFlag, + kScoringSawWSCAlteredFlag, + kScoringWentToReadyRoom2Flag, + kScoringWentAfterSinclairFlag, + kScoringUsedCardBombFlag, + kScoringShieldedCardBombFlag, + kScoringStunnedSinclairFlag, + kScoringDisarmedNukeFlag, + + kScoringThrewBreakerFlag, + kScoringExtendedBridgeFlag, + kScoringGotHistoricalLogFlag, + kScoringFinishedPrehistoricFlag, + + kScoringThrownByRobotFlag, + kScoringGotMarsCardFlag, + kScoringSawMarsKioskFlag, + kScoringSawTransportMapFlag, + kScoringGotCrowBarFlag, + kScoringTurnedOnTransportFlag, + kScoringGotOxygenMaskFlag, + kScoringAvoidedRobotFlag, + kScoringActivatedPlatformFlag, + kScoringUsedLiquidNitrogenFlag, + kScoringUsedCrowBarFlag, + kScoringFoundCardBombFlag, + kScoringDisarmedCardBombFlag, + kScoringGotCardBombFlag, + kScoringThreadedMazeFlag, + kScoringThreadedGearRoomFlag, + kScoringEnteredShuttleFlag, + kScoringEnteredLaunchTubeFlag, + kScoringStoppedRobotsShuttleFlag, + kScoringGotMarsOpMemChipFlag, + kScoringFinishedMarsFlag, + + kScoringSawSecurityMonitorFlag, + kScoringFilledOxygenCanisterFlag, + kScoringFilledArgonCanisterFlag, + kScoringSawUnconsciousOperatorFlag, + kScoringWentThroughPressureDoorFlag, + kScoringPreppedSubFlag, + kScoringEnteredSubFlag, + kScoringExitedSubFlag, + kScoringSawRobotAt54NorthFlag, + kScoringPlayedWithClawFlag, + kScoringUsedRetinalChipFlag, + kScoringFinishedGlobeGameFlag, + kScoringStoppedNoradRobotFlag, + kScoringGotNoradOpMemChipFlag, + kScoringFinishedNoradFlag, + + kScoringRemovedDartFlag, + kScoringAnalyzedDartFlag, + kScoringBuiltAntidoteFlag, + kScoringGotSinclairKeyFlag, + kScoringGotArgonCanisterFlag, + kScoringGotNitrogenCanisterFlag, + kScoringPlayedWithMessagesFlag, + kScoringSawMorphExperimentFlag, + kScoringEnteredSinclairOfficeFlag, + kScoringSawBrochureFlag, + kScoringSawSinclairEntry1Flag, + kScoringSawSinclairEntry2Flag, + kScoringSawSinclairEntry3Flag, + kScoringSawWSCDirectoryFlag, + kScoringUsedCrowBarInWSCFlag, + kScoringFinishedPlasmaDodgeFlag, + kScoringOpenedCatwalkFlag, + kScoringStoppedWSCRobotFlag, + kScoringGotWSCOpMemChipFlag, + kScoringFinishedWSCFlag, + + kScoringMarsGandhiFlag, + kScoringNoradGandhiFlag, + kScoringWSCGandhiFlag, + + kNumScoringFlags +}; + +enum { + kCaldoriaSeenPullbackFlag, + kCaldoriaMadeOJFlag, + kCaldoriaWokenUpFlag, + kCaldoriaDidRecalibrationFlag, + kCaldoriaSeenSinclairInElevatorFlag, + kCaldoriaINNAnnouncingFlag, + kCaldoriaSeenINNFlag, + kCaldoriaSeenMessagesFlag, + kCaldoriaSinclairShotFlag, + kCaldoriaBombDisarmedFlag, + kCaldoriaRoofDoorOpenFlag, + kCaldoriaDoneHygieneFlag, + kCaldoriaSawVoiceAnalysisFlag, + kCaldoriaDoorBombedFlag, + kCaldoriaGunAimedFlag, + kNumCaldoriaFlags +}; + +enum { + kCaldoriaNoFuseRunning, + kCaldoriaDoorBombFuseRunning, + kCaldoriaSinclairFuseRunning +}; + +enum { + kTSAIDedAtDoorFlag, + kTSA0BZoomedInFlag, + kTSAFrontDoorUnlockedOutsideFlag, + kTSAFrontDoorUnlockedInsideFlag, + kTSASeenRobotGreetingFlag, + kTSASeenTheoryFlag, + kTSASeenBackgroundFlag, + kTSASeenProcedureFlag, + kTSASeenAgent3AtDoorFlag, + kTSACommandCenterLockedFlag, + kTSASeenCaldoriaNormalFlag, + kTSASeenCaldoriaAlteredFlag, + kTSASeenNoradNormalFlag, + kTSASeenNoradAlteredFlag, + kTSASeenMarsNormalFlag, + kTSASeenMarsAlteredFlag, + kTSASeenWSCNormalFlag, + kTSASeenWSCAlteredFlag, + kTSABiosuitOnFlag, + kNumTSAFlags +}; + +enum { + kPrehistoricTriedToExtendBridgeFlag, + kPrehistoricSeenTimeStreamFlag, + kPrehistoricSeenFlyer1Flag, + kPrehistoricSeenFlyer2Flag, + kPrehistoricSeenBridgeZoomFlag, + kPrehistoricBreakerThrownFlag, + kNumPrehistoricFlags +}; + +enum { + kNoradSeenTimeStreamFlag, + kNoradGassedFlag, + kNoradFillingStationOnFlag, + kNoradN22MessagePlayedFlag, + kNoradArrivedFromSubFlag, + kNoradWaitingForLaserFlag, + kNoradRetScanGoodFlag, + kNoradPlayedGlobeGameFlag, + kNoradBeatRobotWithClawFlag, + kNoradBeatRobotWithDoorFlag, + kNumNoradFlags +}; + +enum { + kMarsSeenTimeStreamFlag, + kMarsHeardUpperPodMessageFlag, + kMarsRobotThrownPlayerFlag, + kMarsHeardCheckInMessageFlag, + kMarsPodAtUpperPlatformFlag, + kMarsSeenThermalScanFlag, + kMarsArrivedBelowFlag, + kMarsSeenRobotAtReactorFlag, + kMarsAvoidedReactorRobotFlag, + kMarsInAirlockFlag, + kMarsAirlockOpenFlag, + kMarsMaskOnFillerFlag, + kMarsLockFrozenFlag, + kMarsLockBrokenFlag, + kMarsMazeDoorPair1Flag, + kMarsMazeDoorPair2Flag, + kMarsMazeDoorPair3Flag, + kMarsSawRobotLeaveFlag, + kMarsSecurityDownFlag, + kMarsHitRobotWithCannonFlag, + kMarsReadyForShuttleTransportFlag, + kMarsFinishedCanyonChaseFlag, + kMarsThreadedMazeFlag, + kNumMarsFlags +}; + +enum { + kWSCSeenTimeStreamFlag, + kWSCPoisonedFlag, + kWSCAnsweredAboutDartFlag, + kWSCRemovedDartFlag, + kWSCAnalyzerOnFlag, + kWSCDartInAnalyzerFlag, + kWSCAnalyzedDartFlag, + kWSCSawMorphFlag, + kWSCDesignedAntidoteFlag, + kWSCPickedUpAntidoteFlag, + kWSCOfficeMessagesOpenFlag, + kWSCSeenNerdFlag, + kWSCHeardPage1Flag, + kWSCHeardPage2Flag, + kWSCHeardCheckInFlag, + kWSCDidPlasmaDodgeFlag, + kWSCSeenSinclairLectureFlag, + kWSCBeenAtWSC93Flag, + kWSCCatwalkDarkFlag, + kWSCRobotDeadFlag, + kWSCRobotGoneFlag, + kNumWSCFlags +}; + +class GameStateManager : public Common::Singleton<GameStateManager> { +public: + GameStateManager() { resetGameState(); } + + // Base game state + Common::Error writeGameState(Common::WriteStream *stream); + Common::Error readGameState(Common::ReadStream *stream); + + void resetGameState(); + + void getCurrentLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); + void setCurrentLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + + NeighborhoodID getCurrentNeighborhood(); + void setCurrentNeighborhood(const NeighborhoodID neighborhood); + RoomID getCurrentRoom(); + void setCurrentRoom(const RoomID room); + DirectionConstant getCurrentDirection(); + void setCurrentDirection(const DirectionConstant direction); + + RoomViewID getCurrentRoomAndView(); + + void getNextLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); + void setNextLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + + NeighborhoodID getNextNeighborhood(); + void setNextNeighborhood(const NeighborhoodID neighborhood); + RoomID getNextRoom(); + void setNextRoom(const RoomID room); + DirectionConstant getNextDirection(); + void setNextDirection(const DirectionConstant direction); + + void getLastLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); + void setLastLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + + NeighborhoodID getLastNeighborhood(); + void setLastNeighborhood(const NeighborhoodID neighborhood); + RoomID getLastRoom(); + void setLastRoom(const RoomID room); + DirectionConstant getLastDirection(); + void setLastDirection(const DirectionConstant direction); + + RoomViewID getLastRoomAndView(); + + void getOpenDoorLocation(RoomID &room, DirectionConstant &direction); + void setOpenDoorLocation(const RoomID room, const DirectionConstant direction); + RoomID getOpenDoorRoom(); + void setOpenDoorRoom(const RoomID room); + DirectionConstant getOpenDoorDirection(); + void setOpenDoorDirection(const DirectionConstant direction); + + RoomViewID getDoorOpenRoomAndView(); + + bool isCurrentDoorOpen(); + + // Pegasus Prime + + // Scoring... + // Scoring "Set" functions. + // Caldoria/TSA scoring + void setScoringSawINN(const bool = true); + void setScoringTookShower(const bool = true); + void setScoringFixedHair(const bool = true); + void setScoringGotKeyCard(const bool = true); + void setScoringReadPaper(const bool = true); + void setScoringLookThroughTelescope(const bool = true); + void setScoringSawCaldoriaKiosk(const bool = true); + void setScoringGoToTSA(const bool = true); + void setScoringEnterTSA(const bool = true); + void setScoringSawBust1(const bool = true); + void setScoringSawBust2(const bool = true); + void setScoringSawBust3(const bool = true); + void setScoringSawBust4(const bool = true); + void setScoringSawBust5(const bool = true); + void setScoringSawBust6(const bool = true); + void setScoringSawTheory(const bool = true); + void setScoringSawBackground(const bool = true); + void setScoringSawProcedure(const bool = true); + void setScoringGotJourneymanKey(const bool = true); + void setScoringGotPegasusBiochip(const bool = true); + void setScoringGotBiosuit(const bool = true); + void setScoringGoToPrehistoric(const bool = true); + void setScoringPutLogInReader(const bool = true); + void setScoringSawCaldoriaNormal(const bool = true); + void setScoringSawCaldoriaAltered(const bool = true); + void setScoringSawNoradNormal(const bool = true); + void setScoringSawNoradAltered(const bool = true); + void setScoringSawMarsNormal(const bool = true); + void setScoringSawMarsAltered(const bool = true); + void setScoringSawWSCNormal(const bool = true); + void setScoringSawWSCAltered(const bool = true); + void setScoringWentToReadyRoom2(const bool = true); + void setScoringWentAfterSinclair(const bool = true); + void setScoringUsedCardBomb(const bool = true); + void setScoringShieldedCardBomb(const bool = true); + void setScoringStunnedSinclair(const bool = true); + void setScoringDisarmedNuke(const bool = true); + + // Prehistoric scoring + void setScoringThrewBreaker(const bool = true); + void setScoringExtendedBridge(const bool = true); + void setScoringGotHistoricalLog(const bool = true); + void setScoringFinishedPrehistoric(const bool = true); + + // Mars scoring + void setScoringThrownByRobot(const bool = true); + void setScoringGotMarsCard(const bool = true); + void setScoringSawMarsKiosk(const bool = true); + void setScoringSawTransportMap(const bool = true); + void setScoringGotCrowBar(const bool = true); + void setScoringTurnedOnTransport(const bool = true); + void setScoringGotOxygenMask(const bool = true); + void setScoringAvoidedRobot(const bool = true); + void setScoringActivatedPlatform(const bool = true); + void setScoringUsedLiquidNitrogen(const bool = true); + void setScoringUsedCrowBar(const bool = true); + void setScoringFoundCardBomb(const bool = true); + void setScoringDisarmedCardBomb(const bool = true); + void setScoringGotCardBomb(const bool = true); + void setScoringThreadedMaze(const bool = true); + void setScoringThreadedGearRoom(const bool = true); + void setScoringEnteredShuttle(const bool = true); + void setScoringEnteredLaunchTube(const bool = true); + void setScoringStoppedRobotsShuttle(const bool = true); + void setScoringGotMarsOpMemChip(const bool = true); + void setScoringFinishedMars(const bool = true); + + // Norad scoring + void setScoringSawSecurityMonitor(const bool = true); + void setScoringFilledOxygenCanister(const bool = true); + void setScoringFilledArgonCanister(const bool = true); + void setScoringSawUnconsciousOperator(const bool = true); + void setScoringWentThroughPressureDoor(const bool = true); + void setScoringPreppedSub(const bool = true); + void setScoringEnteredSub(const bool = true); + void setScoringExitedSub(const bool = true); + void setScoringSawRobotAt54North(const bool = true); + void setScoringPlayedWithClaw(const bool = true); + void setScoringUsedRetinalChip(const bool = true); + void setScoringFinishedGlobeGame(const bool = true); + void setScoringStoppedNoradRobot(const bool = true); + void setScoringGotNoradOpMemChip(const bool = true); + void setScoringFinishedNorad(const bool = true); + + // WSC scoring + void setScoringRemovedDart(const bool = true); + void setScoringAnalyzedDart(const bool = true); + void setScoringBuiltAntidote(const bool = true); + void setScoringGotSinclairKey(const bool = true); + void setScoringGotArgonCanister(const bool = true); + void setScoringGotNitrogenCanister(const bool = true); + void setScoringPlayedWithMessages(const bool = true); + void setScoringSawMorphExperiment(const bool = true); + void setScoringEnteredSinclairOffice(const bool = true); + void setScoringSawBrochure(const bool = true); + void setScoringSawSinclairEntry1(const bool = true); + void setScoringSawSinclairEntry2(const bool = true); + void setScoringSawSinclairEntry3(const bool = true); + void setScoringSawWSCDirectory(const bool = true); + void setScoringUsedCrowBarInWSC(const bool = true); + void setScoringFinishedPlasmaDodge(const bool = true); + void setScoringOpenedCatwalk(const bool = true); + void setScoringStoppedWSCRobot(const bool = true); + void setScoringGotWSCOpMemChip(const bool = true); + void setScoringFinishedWSC(const bool = true); + + // Gandhi scoring + void setScoringMarsGandhi(const bool = true); + void setScoringNoradGandhi(const bool = true); + void setScoringWSCGandhi(const bool = true); + + // Scoring "Get" functions. + bool getScoringSawINN(); + bool getScoringTookShower(); + bool getScoringFixedHair(); + bool getScoringGotKeyCard(); + bool getScoringReadPaper(); + bool getScoringLookThroughTelescope(); + bool getScoringSawCaldoriaKiosk(); + bool getScoringGoToTSA(); + bool getScoringEnterTSA(); + bool getScoringSawBust1(); + bool getScoringSawBust2(); + bool getScoringSawBust3(); + bool getScoringSawBust4(); + bool getScoringSawBust5(); + bool getScoringSawBust6(); + bool getScoringSawTheory(); + bool getScoringSawBackground(); + bool getScoringSawProcedure(); + bool getScoringGotJourneymanKey(); + bool getScoringGotPegasusBiochip(); + bool getScoringGotBiosuit(); + bool getScoringGoToPrehistoric(); + bool getScoringPutLogInReader(); + bool getScoringSawCaldoriaNormal(); + bool getScoringSawCaldoriaAltered(); + bool getScoringSawNoradNormal(); + bool getScoringSawNoradAltered(); + bool getScoringSawMarsNormal(); + bool getScoringSawMarsAltered(); + bool getScoringSawWSCNormal(); + bool getScoringSawWSCAltered(); + bool getScoringWentToReadyRoom2(); + bool getScoringWentAfterSinclair(); + bool getScoringUsedCardBomb(); + bool getScoringShieldedCardBomb(); + bool getScoringStunnedSinclair(); + bool getScoringDisarmedNuke(); + bool getScoringThrewBreaker(); + bool getScoringExtendedBridge(); + bool getScoringGotHistoricalLog(); + bool getScoringFinishedPrehistoric(); + bool getScoringThrownByRobot(); + bool getScoringGotMarsCard(); + bool getScoringSawMarsKiosk(); + bool getScoringSawTransportMap(); + bool getScoringGotCrowBar(); + bool getScoringTurnedOnTransport(); + bool getScoringGotOxygenMask(); + bool getScoringAvoidedRobot(); + bool getScoringActivatedPlatform(); + bool getScoringUsedLiquidNitrogen(); + bool getScoringUsedCrowBar(); + bool getScoringFoundCardBomb(); + bool getScoringDisarmedCardBomb(); + bool getScoringGotCardBomb(); + bool getScoringThreadedMaze(); + bool getScoringThreadedGearRoom(); + bool getScoringEnteredShuttle(); + bool getScoringEnteredLaunchTube(); + bool getScoringStoppedRobotsShuttle(); + bool getScoringGotMarsOpMemChip(); + bool getScoringFinishedMars(); + bool getScoringSawSecurityMonitor(); + bool getScoringFilledOxygenCanister(); + bool getScoringFilledArgonCanister(); + bool getScoringSawUnconsciousOperator(); + bool getScoringWentThroughPressureDoor(); + bool getScoringPreppedSub(); + bool getScoringEnteredSub(); + bool getScoringExitedSub(); + bool getScoringSawRobotAt54North(); + bool getScoringPlayedWithClaw(); + bool getScoringUsedRetinalChip(); + bool getScoringFinishedGlobeGame(); + bool getScoringStoppedNoradRobot(); + bool getScoringGotNoradOpMemChip(); + bool getScoringFinishedNorad(); + bool getScoringRemovedDart(); + bool getScoringAnalyzedDart(); + bool getScoringBuiltAntidote(); + bool getScoringGotSinclairKey(); + bool getScoringGotArgonCanister(); + bool getScoringGotNitrogenCanister(); + bool getScoringPlayedWithMessages(); + bool getScoringSawMorphExperiment(); + bool getScoringEnteredSinclairOffice(); + bool getScoringSawBrochure(); + bool getScoringSawSinclairEntry1(); + bool getScoringSawSinclairEntry2(); + bool getScoringSawSinclairEntry3(); + bool getScoringSawWSCDirectory(); + bool getScoringUsedCrowBarInWSC(); + bool getScoringFinishedPlasmaDodge(); + bool getScoringOpenedCatwalk(); + bool getScoringStoppedWSCRobot(); + bool getScoringGotWSCOpMemChip(); + bool getScoringFinishedWSC(); + bool getScoringMarsGandhi(); + bool getScoringNoradGandhi(); + bool getScoringWSCGandhi(); + + GameScoreType getCaldoriaTSAScore(); + GameScoreType getPrehistoricScore(); + GameScoreType getMarsScore(); + GameScoreType getNoradScore(); + GameScoreType getWSCScore(); + GameScoreType getGandhiScore(); + GameScoreType getTotalScore(); + + void writeCaldoriaState(Common::WriteStream *stream); + void readCaldoriaState(Common::ReadStream *stream); + void resetCaldoriaState(); + + void writeTSAState(Common::WriteStream *stream); + void readTSAState(Common::ReadStream *stream); + void resetTSAState(); + + void writePrehistoricState(Common::WriteStream *stream); + void readPrehistoricState(Common::ReadStream *stream); + void resetPrehistoricState(); + + void writeNoradState(Common::WriteStream *stream); + void readNoradState(Common::ReadStream *stream); + void resetNoradState(); + + void writeMarsState(Common::WriteStream *stream); + void readMarsState(Common::ReadStream *stream); + void resetMarsState(); + + void writeWSCState(Common::WriteStream *stream); + void readWSCState(Common::ReadStream *stream); + void resetWSCState(); + + // Globals. + void setWalkthroughMode(bool); + bool getWalkthroughMode(); + void setShieldOn(bool); + bool getShieldOn(); + void setEasterEgg(bool); + bool getEasterEgg(); + void setBeenToWSC(bool value); + bool getBeenToWSC(); + void setBeenToMars(bool value); + bool getBeenToMars(); + void setBeenToNorad(bool value); + bool getBeenToNorad(); + void setWSCFinished(bool); + bool getWSCFinished(); + void setMarsFinished(bool); + bool getMarsFinished(); + void setNoradFinished(bool); + bool getNoradFinished(); + bool allTimeZonesFinished(); + void setTakenItemID(ItemID, bool); + bool isTakenItemID(ItemID); + void setTakenItem(Item *, bool); + bool isTakenItem(Item *); + + // Caldoria + void setCaldoriaFuseTimeLimit(const TimeValue); + TimeValue getCaldoriaFuseTimeLimit(); + void setCaldoriaSeenPullback(bool); + bool getCaldoriaSeenPullback(); + void setCaldoriaMadeOJ(bool); + bool getCaldoriaMadeOJ(); + void setCaldoriaWokenUp(bool); + bool getCaldoriaWokenUp(); + void setCaldoriaDidRecalibration(bool); + bool getCaldoriaDidRecalibration(); + void setCaldoriaSeenSinclairInElevator(bool); + bool getCaldoriaSeenSinclairInElevator(); + void setCaldoriaINNAnnouncing(bool); + bool getCaldoriaINNAnnouncing(); + void setCaldoriaSeenINN(bool); + bool getCaldoriaSeenINN(); + void setCaldoriaSeenMessages(bool); + bool getCaldoriaSeenMessages(); + void setCaldoriaSinclairShot(bool); + bool getCaldoriaSinclairShot(); + void setCaldoriaBombDisarmed(bool); + bool getCaldoriaBombDisarmed(); + void setCaldoriaRoofDoorOpen(bool); + bool getCaldoriaRoofDoorOpen(); + void setCaldoriaDoneHygiene(bool); + bool getCaldoriaDoneHygiene(); + void setCaldoriaSawVoiceAnalysis(bool); + bool getCaldoriaSawVoiceAnalysis(); + void setCaldoriaDoorBombed(bool); + bool getCaldoriaDoorBombed(); + void setCaldoriaGunAimed(bool); + bool getCaldoriaGunAimed(); + + // TSA + void setRipTimerTime(TimeValue); + TimeValue getRipTimerTime(); + void setTSAFuseTimeLimit(TimeValue); + TimeValue getTSAFuseTimeLimit(); + void setT0BMonitorMode(byte); + byte getT0BMonitorMode(); + void setTSAState(byte); + byte getTSAState(); + void setT0BMonitorStart(TimeValue); + TimeValue getT0BMonitorStart(); + void setTSAIDedAtDoor(bool); + bool getTSAIDedAtDoor(); + void setTSA0BZoomedIn(bool); + bool getTSA0BZoomedIn(); + void setTSAFrontDoorUnlockedOutside(bool); + bool getTSAFrontDoorUnlockedOutside(); + void setTSAFrontDoorUnlockedInside(bool); + bool getTSAFrontDoorUnlockedInside(); + void setTSASeenRobotGreeting(bool); + bool getTSASeenRobotGreeting(); + void setTSASeenTheory(bool); + bool getTSASeenTheory(); + void setTSASeenBackground(bool); + bool getTSASeenBackground(); + void setTSASeenProcedure(bool); + bool getTSASeenProcedure(); + void setTSASeenAgent3AtDoor(bool); + bool getTSASeenAgent3AtDoor(); + void setTSACommandCenterLocked(bool); + bool getTSACommandCenterLocked(); + void setTSASeenCaldoriaNormal(bool); + bool getTSASeenCaldoriaNormal(); + void setTSASeenCaldoriaAltered(bool); + bool getTSASeenCaldoriaAltered(); + void setTSASeenNoradNormal(bool); + bool getTSASeenNoradNormal(); + void setTSASeenNoradAltered(bool); + bool getTSASeenNoradAltered(); + void setTSASeenMarsNormal(bool); + bool getTSASeenMarsNormal(); + void setTSASeenMarsAltered(bool); + bool getTSASeenMarsAltered(); + void setTSASeenWSCNormal(bool); + bool getTSASeenWSCNormal(); + void setTSASeenWSCAltered(bool); + bool getTSASeenWSCAltered(); + void setTSABiosuitOn(bool); + bool getTSABiosuitOn(); + + // Prehistoric + void setPrehistoricTriedToExtendBridge(bool); + bool getPrehistoricTriedToExtendBridge(); + void setPrehistoricSeenTimeStream(bool); + bool getPrehistoricSeenTimeStream(); + void setPrehistoricSeenFlyer1(bool); + bool getPrehistoricSeenFlyer1(); + void setPrehistoricSeenFlyer2(bool); + bool getPrehistoricSeenFlyer2(); + void setPrehistoricSeenBridgeZoom(bool); + bool getPrehistoricSeenBridgeZoom(); + void setPrehistoricBreakerThrown(bool); + bool getPrehistoricBreakerThrown(); + + // Norad + void setNoradSeenTimeStream(bool); + bool getNoradSeenTimeStream(); + void setNoradGassed(bool); + bool getNoradGassed(); + void setNoradFillingStationOn(bool); + bool getNoradFillingStationOn(); + void setNoradN22MessagePlayed(bool); + bool getNoradN22MessagePlayed(); + void setNoradPlayedGlobeGame(bool); + bool getNoradPlayedGlobeGame(); + void setNoradBeatRobotWithClaw(bool); + bool getNoradBeatRobotWithClaw(); + void setNoradBeatRobotWithDoor(bool); + bool getNoradBeatRobotWithDoor(); + void setNoradRetScanGood(bool); + bool getNoradRetScanGood(); + void setNoradWaitingForLaser(bool); + bool getNoradWaitingForLaser(); + void setNoradSubRoomPressure(uint16); + uint16 getNoradSubRoomPressure(); + void setNoradSubPrepState(NoradSubPrepState); + NoradSubPrepState getNoradSubPrepState(); + void setNoradArrivedFromSub(bool); + bool getNoradArrivedFromSub(); + + // Mars + void setMarsSeenTimeStream(bool); + bool getMarsSeenTimeStream(); + void setMarsHeardUpperPodMessage(bool); + bool getMarsHeardUpperPodMessage(); + void setMarsRobotThrownPlayer(bool); + bool getMarsRobotThrownPlayer(); + void setMarsHeardCheckInMessage(bool); + bool getMarsHeardCheckInMessage(); + void setMarsPodAtUpperPlatform(bool); + bool getMarsPodAtUpperPlatform(); + void setMarsSeenThermalScan(bool); + bool getMarsSeenThermalScan(); + void setMarsArrivedBelow(bool); + bool getMarsArrivedBelow(); + void setMarsSeenRobotAtReactor(bool); + bool getMarsSeenRobotAtReactor(); + void setMarsAvoidedReactorRobot(bool); + bool getMarsAvoidedReactorRobot(); + void setMarsInAirlock(bool); + bool getMarsInAirlock(); + void setMarsAirlockOpen(bool); + bool getMarsAirlockOpen(); + void setMarsMaskOnFiller(bool); + bool getMarsMaskOnFiller(); + void setMarsLockFrozen(bool); + bool getMarsLockFrozen(); + void setMarsLockBroken(bool); + bool getMarsLockBroken(); + void setMarsMazeDoorPair1(bool); + bool getMarsMazeDoorPair1(); + void setMarsMazeDoorPair2(bool); + bool getMarsMazeDoorPair2(); + void setMarsMazeDoorPair3(bool); + bool getMarsMazeDoorPair3(); + void setMarsSawRobotLeave(bool); + bool getMarsSawRobotLeave(); + void setMarsSecurityDown(bool); + bool getMarsSecurityDown(); + void setMarsFinishedCanyonChase(bool); + bool getMarsFinishedCanyonChase(); + void setMarsThreadedMaze(bool); + bool getMarsThreadedMaze(); + void setMarsHitRobotWithCannon(bool); + bool getMarsHitRobotWithCannon(); + void setMarsReadyForShuttleTransport(bool); + bool getMarsReadyForShuttleTransport(); + + // WSC + void setWSCSeenTimeStream(bool); + bool getWSCSeenTimeStream(); + void setWSCPoisoned(bool); + bool getWSCPoisoned(); + void setWSCAnsweredAboutDart(bool); + bool getWSCAnsweredAboutDart(); + void setWSCRemovedDart(bool); + bool getWSCRemovedDart(); + void setWSCAnalyzerOn(bool); + bool getWSCAnalyzerOn(); + void setWSCDartInAnalyzer(bool); + bool getWSCDartInAnalyzer(); + void setWSCAnalyzedDart(bool); + bool getWSCAnalyzedDart(); + void setWSCSawMorph(bool); + bool getWSCSawMorph(); + void setWSCDesignedAntidote(bool); + bool getWSCDesignedAntidote(); + void setWSCPickedUpAntidote(bool); + bool getWSCPickedUpAntidote(); + void setWSCOfficeMessagesOpen(bool); + bool getWSCOfficeMessagesOpen(); + void setWSCSeenNerd(bool); + bool getWSCSeenNerd(); + void setWSCHeardPage1(bool); + bool getWSCHeardPage1(); + void setWSCHeardPage2(bool); + bool getWSCHeardPage2(); + void setWSCHeardCheckIn(bool); + bool getWSCHeardCheckIn(); + void setWSCDidPlasmaDodge(bool); + bool getWSCDidPlasmaDodge(); + void setWSCSeenSinclairLecture(bool); + bool getWSCSeenSinclairLecture(); + void setWSCBeenAtWSC93(bool); + bool getWSCBeenAtWSC93(); + void setWSCCatwalkDark(bool); + bool getWSCCatwalkDark(); + void setWSCRobotDead(bool); + bool getWSCRobotDead(); + void setWSCRobotGone(bool); + bool getWSCRobotGone(); + +protected: + friend class Common::Singleton<SingletonBaseType>; + +private: + // Base + NeighborhoodID _currentNeighborhood; + RoomID _currentRoom; + DirectionConstant _currentDirection; + NeighborhoodID _nexNeighborhoodID; + RoomID _nextRoomID; + DirectionConstant _nextDirection; + NeighborhoodID _lastNeighborhood; + RoomID _lastRoom; + DirectionConstant _lastDirection; + RoomID _openDoorRoom; + DirectionConstant _openDoorDirection; + + // Pegasus Prime + FlagsArray<byte, kNumGlobalFlags> _globalFlags; + FlagsArray<byte, kNumScoringFlags> _scoringFlags; + FlagsArray<uint32, kNumItems> _itemTakenFlags; + + FlagsArray<byte, kNumCaldoriaFlags> _caldoriaFlags; + TimeValue _caldoriaFuseTimeLimit; + + TimeValue _TSARipTimerTime; + TimeValue _TSAFuseTimeLimit; + byte _TSAState; + byte _T0BMonitorMode; + TimeValue _T0BMonitorStart; + FlagsArray<byte, kNumTSAFlags> _TSAFlags; + + FlagsArray<byte, kNumPrehistoricFlags> _prehistoricFlags; + + FlagsArray<byte, kNumNoradFlags> _noradFlags; + uint16 _noradSubRoomPressure; + NoradSubPrepState _noradSubPrepState; + + FlagsArray<byte, kNumMarsFlags> _marsFlags; + + FlagsArray<byte, kNumWSCFlags> _WSCFlags; +}; + +} // End of namespace Pegasus + +#define GameState (::Pegasus::GameStateManager::instance()) + +#endif diff --git a/engines/pegasus/graphics.cpp b/engines/pegasus/graphics.cpp new file mode 100644 index 0000000000..8dbd678809 --- /dev/null +++ b/engines/pegasus/graphics.cpp @@ -0,0 +1,346 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "common/file.h" +#include "common/textconsole.h" +#include "engines/util.h" + +#include "pegasus/elements.h" +#include "pegasus/graphics.h" +#include "pegasus/transition.h" + +namespace Pegasus { + +GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) { + initGraphics(640, 480, true, NULL); + + if (_vm->_system->getScreenFormat().bytesPerPixel == 1) + error("No true color mode available"); + + _backLayer = kMinAvailableOrder; + _frontLayer = kMaxAvailableOrder; + _firstDisplayElement = _lastDisplayElement = 0; + _workArea.create(640, 480, _vm->_system->getScreenFormat()); + _modifiedScreen = false; + _curSurface = &_workArea; + _erase = false; + _updatesEnabled = true; + _screenFader = new ScreenFader(); +} + +GraphicsManager::~GraphicsManager() { + _workArea.free(); + delete _screenFader; +} + +void GraphicsManager::invalRect(const Common::Rect &rect) { + // We're using a simpler algorithm for dirty rect handling than the original + // The original was way too overcomplicated for what we need here now. + + if (_dirtyRect.width() == 0 || _dirtyRect.height() == 0) { + // We have no dirty rect, so this is now our dirty rect + _dirtyRect = rect; + } else { + // Expand our dirty rect to include rect + _dirtyRect.extend(rect); + } + + // Sanity check: clip our rect to the screen + _dirtyRect.right = MIN<int>(640, _dirtyRect.right); + _dirtyRect.bottom = MIN<int>(480, _dirtyRect.bottom); +} + +void GraphicsManager::addDisplayElement(DisplayElement *newElement) { + newElement->_elementOrder = CLIP<int>(newElement->_elementOrder, kMinAvailableOrder, kMaxAvailableOrder); + + if (_firstDisplayElement) { + DisplayElement *runner = _firstDisplayElement; + DisplayElement *lastRunner = 0; + + // Search for first element whose display order is greater than + // the new element's and add the new element just before it. + while (runner) { + if (newElement->_elementOrder < runner->_elementOrder) { + if (lastRunner) { + lastRunner->_nextElement = newElement; + newElement->_nextElement = runner; + } else { + newElement->_nextElement = _firstDisplayElement; + _firstDisplayElement = newElement; + } + break; + } + lastRunner = runner; + runner = runner->_nextElement; + } + + // If got here and runner == NULL, we ran through the whole list without + // inserting, so add at the end. + if (!runner) { + _lastDisplayElement->_nextElement = newElement; + _lastDisplayElement = newElement; + } + } else { + _firstDisplayElement = newElement; + _lastDisplayElement = newElement; + } + + newElement->_elementIsDisplaying = true; +} + +void GraphicsManager::removeDisplayElement(DisplayElement *oldElement) { + if (!_firstDisplayElement) + return; + + if (oldElement == _firstDisplayElement) { + if (oldElement == _lastDisplayElement) { + _firstDisplayElement = 0; + _lastDisplayElement = 0; + } else { + _firstDisplayElement = oldElement->_nextElement; + } + + invalRect(oldElement->_bounds); + } else { + // Scan list for element. + // If we get here, we know that the list has at least one item, and it + // is not the first item, so we can skip it. + DisplayElement *runner = _firstDisplayElement->_nextElement; + DisplayElement *lastRunner = _firstDisplayElement; + + while (runner) { + if (runner == oldElement) { + lastRunner->_nextElement = runner->_nextElement; + + if (oldElement == _lastDisplayElement) + _lastDisplayElement = lastRunner; + + invalRect(oldElement->_bounds); + break; + } + + lastRunner = runner; + runner = runner->_nextElement; + } + } + + oldElement->_nextElement = 0; + oldElement->_elementIsDisplaying = false; +} + +void GraphicsManager::updateDisplay() { + bool screenDirty = false; + + if (!_dirtyRect.isEmpty()) { + // Fill the dirty area with black if erase mode is enabled + if (_erase) + _workArea.fillRect(_dirtyRect, _workArea.format.RGBToColor(0, 0, 0)); + + for (DisplayElement *runner = _firstDisplayElement; runner != 0; runner = runner->_nextElement) { + Common::Rect bounds; + runner->getBounds(bounds); + + // TODO: Better logic; it does a bit more work than it probably needs to + // but it should work fine for now. + if (bounds.intersects(_dirtyRect) && runner->validToDraw(_backLayer, _frontLayer)) { + runner->draw(bounds); + screenDirty = true; + } + } + + // Copy only the dirty rect to the screen + if (screenDirty) + g_system->copyRectToScreen((byte *)_workArea.getBasePtr(_dirtyRect.left, _dirtyRect.top), _workArea.pitch, _dirtyRect.left, _dirtyRect.top, _dirtyRect.width(), _dirtyRect.height()); + + // Clear the dirty rect + _dirtyRect = Common::Rect(); + } + + if (_updatesEnabled && (screenDirty || _modifiedScreen)) + g_system->updateScreen(); + + _modifiedScreen = false; +} + +void GraphicsManager::clearScreen() { + Graphics::Surface *screen = g_system->lockScreen(); + screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0)); + g_system->unlockScreen(); + _modifiedScreen = true; +} + +DisplayElement *GraphicsManager::findDisplayElement(const DisplayElementID id) { + DisplayElement *runner = _firstDisplayElement; + + while (runner) { + if (runner->getObjectID() == id) + return runner; + runner = runner->_nextElement; + } + + return 0; +} + +void GraphicsManager::doFadeOutSync(const TimeValue time, const TimeScale scale, bool isBlack) { + _updatesEnabled = false; + _screenFader->doFadeOutSync(time, scale, isBlack); +} + +void GraphicsManager::doFadeInSync(const TimeValue time, const TimeScale scale, bool isBlack) { + _screenFader->doFadeInSync(time, scale, isBlack); + _updatesEnabled = true; +} + +void GraphicsManager::markCursorAsDirty() { + _modifiedScreen = true; +} + +void GraphicsManager::newShakePoint(int32 index1, int32 index2, int32 maxRadius) { + int32 index3 = (index1 + index2) >> 1; + + if (maxRadius == 0) { + _shakeOffsets[index3].x = ((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1); + _shakeOffsets[index3].y = ((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1); + } else { + double angle = (int32)(_vm->getRandomNumber(360 - 1) * 3.1415926535 / 180); + int32 radius = maxRadius; + _shakeOffsets[index3].x = (int32)(((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1) + + cos(angle) / 2 * radius); + _shakeOffsets[index3].y = (int32)(((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1) + + sin(angle) * radius); + } + + if (index1 < index3 - 1) + newShakePoint(index1, index3, maxRadius * 2 / 3); + + if (index3 < index2 - 1) + newShakePoint(index3, index2, maxRadius * 2 / 3); +} + +void GraphicsManager::shakeTheWorld(TimeValue duration, TimeScale scale) { + if (duration == 0 || scale == 0) + return; + + _shakeOffsets[0].x = 0; + _shakeOffsets[0].y = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) / 4].x = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) / 4].y = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) / 2].x = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) / 2].y = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].x = 0; + _shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].y = 0; + _shakeOffsets[kMaxShakeOffsets - 1].x = 0; + _shakeOffsets[kMaxShakeOffsets - 1].y = 0; + + newShakePoint(0, (kMaxShakeOffsets - 1) / 4, 8); + newShakePoint((kMaxShakeOffsets - 1) / 4, (kMaxShakeOffsets - 1) / 2, 6); + newShakePoint((kMaxShakeOffsets - 1) / 2, (kMaxShakeOffsets - 1) * 3 / 4, 4); + newShakePoint((kMaxShakeOffsets - 1) * 3 / 4, kMaxShakeOffsets - 1, 3); + + Common::Point lastOffset(0, 0); + + // Store the current screen for later use + Graphics::Surface oldScreen; + Graphics::Surface *curScreen = g_system->lockScreen(); + oldScreen.copyFrom(*curScreen); + g_system->unlockScreen(); + + // Convert to millis + duration = duration * 1000 / scale; + + uint32 startTime = g_system->getMillis(); + + while (g_system->getMillis() < startTime + duration) { + Common::Point thisOffset = _shakeOffsets[(g_system->getMillis() - startTime) * (kMaxShakeOffsets - 1) / duration]; + if (thisOffset != lastOffset) { + // Fill the screen with black + Graphics::Surface *screen = g_system->lockScreen(); + screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0)); + g_system->unlockScreen(); + + // Calculate the src/dst offsets and the width/height + int32 srcOffsetX, dstOffsetX, width; + + if (thisOffset.x > 0) { + srcOffsetX = 0; + dstOffsetX = thisOffset.x; + width = 640 - dstOffsetX; + } else { + srcOffsetX = -thisOffset.x; + dstOffsetX = 0; + width = 640 - srcOffsetX; + } + + int32 srcOffsetY, dstOffsetY, height; + + if (thisOffset.y > 0) { + srcOffsetY = 0; + dstOffsetY = thisOffset.y; + height = 480 - dstOffsetY; + } else { + srcOffsetY = -thisOffset.y; + dstOffsetY = 0; + height = 480 - srcOffsetY; + } + + // Now copy to the screen + g_system->copyRectToScreen((byte *)oldScreen.getBasePtr(srcOffsetX, srcOffsetY), oldScreen.pitch, + dstOffsetX, dstOffsetY, width, height); + g_system->updateScreen(); + + lastOffset = thisOffset; + } + + g_system->delayMillis(10); + } + + if (lastOffset.x != 0 || lastOffset.y != 0) { + g_system->copyRectToScreen((byte *)oldScreen.pixels, oldScreen.pitch, 0, 0, 640, 480); + g_system->updateScreen(); + } + + oldScreen.free(); +} + +void GraphicsManager::enableErase() { + _erase = true; +} + +void GraphicsManager::disableErase() { + _erase = false; +} + +void GraphicsManager::enableUpdates() { + _updatesEnabled = true; + _screenFader->setFaderValue(100); +} + +void GraphicsManager::disableUpdates() { + _updatesEnabled = false; + _screenFader->setFaderValue(0); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/graphics.h b/engines/pegasus/graphics.h new file mode 100644 index 0000000000..fcdf1c9e78 --- /dev/null +++ b/engines/pegasus/graphics.h @@ -0,0 +1,95 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_GRAPHICS_H +#define PEGASUS_GRAPHICS_H + +#include "common/rect.h" +#include "common/str.h" +#include "common/system.h" +#include "graphics/surface.h" + +#include "pegasus/constants.h" +#include "pegasus/pegasus.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class Cursor; +class DisplayElement; +class PegasusEngine; +class ScreenFader; + +class GraphicsManager { +friend class Cursor; +public: + GraphicsManager(PegasusEngine *vm); + ~GraphicsManager(); + + void addDisplayElement(DisplayElement *element); + void removeDisplayElement(DisplayElement *element); + void invalRect(const Common::Rect &rect); + DisplayOrder getBackOfActiveLayer() const { return _backLayer; } + DisplayOrder getFrontOfActiveLayer() const { return _frontLayer; } + void updateDisplay(); + Graphics::Surface *getCurSurface() { return _curSurface; } + void setCurSurface(Graphics::Surface *surface) { _curSurface = surface; } + Graphics::Surface *getWorkArea() { return &_workArea; } + void clearScreen(); + DisplayElement *findDisplayElement(const DisplayElementID id); + void shakeTheWorld(TimeValue time, TimeScale scale); + void enableErase(); + void disableErase(); + void enableUpdates(); + void disableUpdates(); + + // These default to black + void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true); + void doFadeInSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true); + +protected: + void markCursorAsDirty(); + +private: + PegasusEngine *_vm; + + bool _modifiedScreen, _erase; + Common::Rect _dirtyRect; + DisplayOrder _backLayer, _frontLayer; + DisplayElement *_firstDisplayElement, *_lastDisplayElement; + Graphics::Surface _workArea, *_curSurface; + + // Shake Shake Shake! + static const int kMaxShakeOffsets = 17; + Common::Point _shakeOffsets[kMaxShakeOffsets]; + void newShakePoint(int32 index1, int32 index2, int32 maxRadius); + + bool _updatesEnabled; + ScreenFader *_screenFader; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/hotspot.cpp b/engines/pegasus/hotspot.cpp new file mode 100644 index 0000000000..d8b07a94f4 --- /dev/null +++ b/engines/pegasus/hotspot.cpp @@ -0,0 +1,325 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "pegasus/hotspot.h" + +namespace Pegasus { + +Region::Region(Common::ReadStream *stream) { + uint16 length = stream->readUint16BE(); + + assert(length >= 10); + + _bounds.top = stream->readUint16BE(); + _bounds.left = stream->readUint16BE(); + _bounds.bottom = stream->readUint16BE(); + _bounds.right = stream->readUint16BE(); + + _bounds.debugPrint(0, "Bounds:"); + + if (length == 10) + return; + + length -= 10; + + while (length > 0) { + Vector v; + v.y = stream->readUint16BE(); + length -= 2; + + if (v.y == 0x7fff) + break; + + debug(0, "y: %d", v.y); + + // Normalize y to _bounds + v.y -= _bounds.top; + + while (length > 0) { + Run run; + run.start = stream->readUint16BE(); + length -= 2; + + if (run.start == 0x7fff) + break; + + run.end = stream->readUint16BE(); + length -= 2; + + debug(0, "\t[%d, %d)", run.start, run.end); + + // Normalize to _bounds + run.start -= _bounds.left; + run.end -= _bounds.left; + + v.push_back(run); + } + + _vectors.push_back(v); + } +} + +Region::Region(const Common::Rect &rect) { + _bounds = rect; +} + +bool Region::pointInRegion(const Common::Point &point) const { + if (!_bounds.contains(point)) + return false; + + bool pixelActive = false; + + // Normalize the points to _bounds + uint16 x = point.x - _bounds.left; + uint16 y = point.y - _bounds.top; + + for (Common::List<Vector>::const_iterator v = _vectors.begin(); v != _vectors.end(); v++) { + if (v->y > y) + return pixelActive; + + for (Vector::const_iterator run = v->begin(); run != v->end(); run++) { + if (x >= run->start && x < run->end) { + pixelActive = !pixelActive; + break; + } + } + } + + // the case if the region is just a rect + return true; +} + +void Region::moveTo(CoordType h, CoordType v) { + _bounds.moveTo(h, v); +} + +void Region::moveTo(const Common::Point &point) { + _bounds.moveTo(point); +} + +void Region::translate(CoordType h, CoordType v) { + _bounds.translate(h, v); +} + +void Region::translate(const Common::Point &point) { + _bounds.translate(point.x, point.y); +} + +void Region::getCenter(CoordType &h, CoordType &v) const { + h = (_bounds.left + _bounds.right) / 2; + v = (_bounds.top + _bounds.bottom) / 2; +} + +void Region::getCenter(Common::Point &point) const { + getCenter(point.x, point.y); +} + +Hotspot::Hotspot(const HotSpotID id) : IDObject(id) { + _spotFlags = kNoHotSpotFlags; + _spotActive = false; +} + +Hotspot::~Hotspot() { +} + +void Hotspot::setArea(const Common::Rect &area) { + _spotArea = Region(area); +} + +void Hotspot::setArea(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) { + _spotArea = Region(Common::Rect(left, top, right, bottom)); +} + +void Hotspot::getBoundingBox(Common::Rect &r) const { + r = _spotArea.getBoundingBox(); +} + +void Hotspot::getCenter(Common::Point &pt) const { + _spotArea.getCenter(pt); +} + +void Hotspot::getCenter(CoordType &h, CoordType &v) const { + _spotArea.getCenter(h, v); +} + +void Hotspot::setActive() { + _spotActive = true; +} + +void Hotspot::setInactive() { + _spotActive = false; +} + +void Hotspot::setHotspotFlags(const HotSpotFlags flags) { + _spotFlags = flags; +} + +void Hotspot::setMaskedHotspotFlags(const HotSpotFlags flags, const HotSpotFlags mask) { + _spotFlags = (_spotFlags & ~mask) | flags; +} + +bool Hotspot::isSpotActive() const { + return _spotActive; +} + +void Hotspot::moveSpotTo(const CoordType h, const CoordType v) { + _spotArea.moveTo(h, v); +} + +void Hotspot::moveSpotTo(const Common::Point pt) { + _spotArea.moveTo(pt); +} + +void Hotspot::moveSpot(const CoordType h, const CoordType v) { + _spotArea.translate(h, v); +} + +void Hotspot::moveSpot(const Common::Point pt) { + _spotArea.translate(pt.x, pt.y); +} + +bool Hotspot::pointInSpot(const Common::Point where) const { + return _spotActive && _spotArea.pointInRegion(where); +} + +HotSpotFlags Hotspot::getHotspotFlags() const { + return _spotFlags; +} + +HotspotList::HotspotList() { +} + +HotspotList::~HotspotList() { + // TODO: Should this call deleteHotspots()? +} + +void HotspotList::deleteHotspots() { + for (HotspotIterator it = begin(); it != end(); it++) + delete *it; + + clear(); +} + +Hotspot *HotspotList::findHotspot(const Common::Point where) { + for (HotspotIterator it = begin(); it != end(); it++) + if ((*it)->pointInSpot(where)) + return *it; + + return 0; +} + +HotSpotID HotspotList::findHotspotID(const Common::Point where) { + Hotspot *hotspot = findHotspot(where); + return hotspot ? hotspot->getObjectID() : kNoHotSpotID; +} + +Hotspot *HotspotList::findHotspotByID(const HotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) + if ((*it)->getObjectID() == id) + return *it; + + return 0; +} + +Hotspot *HotspotList::findHotspotByMask(const HotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (((*it)->getHotspotFlags() & flags) == flags) + return *it; + + return 0; +} + +void HotspotList::activateMaskedHotspots(const HotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (flags == kNoHotSpotFlags || ((*it)->getHotspotFlags() & flags) != 0) + (*it)->setActive(); +} + +void HotspotList::deactivateAllHotspots() { + for (HotspotIterator it = begin(); it != end(); it++) + (*it)->setInactive(); +} + +void HotspotList::deactivateMaskedHotspots(const HotSpotFlags flags) { + for (HotspotIterator it = begin(); it != end(); it++) + if (((*it)->getHotspotFlags() & flags) != 0) + (*it)->setInactive(); +} + +void HotspotList::activateOneHotspot(const HotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + (*it)->setActive(); + return; + } + } +} + +void HotspotList::deactivateOneHotspot(const HotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + (*it)->setInactive(); + return; + } + } +} + +void HotspotList::removeOneHotspot(const HotSpotID id) { + for (HotspotIterator it = begin(); it != end(); it++) { + if ((*it)->getObjectID() == id) { + erase(it); + return; + } + } +} + +void HotspotList::removeMaskedHotspots(const HotSpotFlags flags) { + if (flags != kNoHotSpotFlags) { + for (HotspotIterator it = begin(); it != end(); ) { + if (((*it)->getHotspotFlags() & flags) != 0) + it = erase(it); + else + it++; + } + } else { + clear(); + } +} + +void HotspotList::setHotspotRect(const HotSpotID id, const Common::Rect &r) { + Hotspot *hotspot = findHotspotByID(id); + if (hotspot) + hotspot->setArea(r); +} + +void HotspotList::getHotspotRect(const HotSpotID id, Common::Rect &r) { + Hotspot *hotspot = findHotspotByID(id); + if (hotspot) + hotspot->getBoundingBox(r); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/hotspot.h b/engines/pegasus/hotspot.h new file mode 100644 index 0000000000..64d3fb19f9 --- /dev/null +++ b/engines/pegasus/hotspot.h @@ -0,0 +1,152 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_HOTSPOT_H +#define PEGASUS_HOTSPOT_H + +#include "common/list.h" +#include "common/rect.h" + +#include "pegasus/constants.h" +#include "pegasus/types.h" +#include "pegasus/util.h" + +/* + + Hot spots combine a pixel area, an ID value and an active flag. + + A point is considered in a hot spot if the point is in the hot spot's pixel area and + the active flag is set. + + In addition, hot spots have a 32 bit word of bit flags for filtering use. + +*/ + +namespace Common { + class ReadStream; +} + +namespace Pegasus { + +// Our implementation of QuickDraw regions +class Region { +public: + Region() {} + Region(Common::ReadStream *stream); + Region(const Common::Rect &rect); + + Common::Rect getBoundingBox() const { return _bounds; } + + bool pointInRegion(const Common::Point &point) const; + + void moveTo(CoordType h, CoordType v); + void moveTo(const Common::Point &point); + void translate(CoordType h, CoordType v); + void translate(const Common::Point &point); + void getCenter(CoordType &h, CoordType &v) const; + void getCenter(Common::Point &point) const; + +private: + Common::Rect _bounds; + + struct Run { + uint16 start, end; + }; + + class Vector : public Common::List<Run> { + public: + uint16 y; + }; + + Common::List<Vector> _vectors; +}; + +class Hotspot : public IDObject { +public: + Hotspot(const HotSpotID); + virtual ~Hotspot(); + + void setArea(const Region ®ion) { _spotArea = region; } + void setArea(const Common::Rect &); + void setArea(const CoordType, const CoordType, const CoordType, const CoordType); + void getBoundingBox(Common::Rect &) const; + void getArea(Region &) const; + void getCenter(Common::Point&) const; + void getCenter(CoordType&, CoordType&) const; + + void moveSpotTo(const CoordType, const CoordType); + void moveSpotTo(const Common::Point); + void moveSpot(const CoordType, const CoordType); + void moveSpot(const Common::Point); + + bool pointInSpot(const Common::Point) const; + + void setActive(); + void setInactive(); + bool isSpotActive() const; + + HotSpotFlags getHotspotFlags() const; + void setHotspotFlags(const HotSpotFlags); + void setMaskedHotspotFlags(const HotSpotFlags flags, const HotSpotFlags mask); + +protected: + Region _spotArea; + HotSpotFlags _spotFlags; + bool _spotActive; +}; + +class HotspotList : public Common::List<Hotspot *> { +public: + HotspotList(); + virtual ~HotspotList(); + + void deleteHotspots(); + + Hotspot *findHotspot(const Common::Point); + HotSpotID findHotspotID(const Common::Point); + Hotspot *findHotspotByID(const HotSpotID); + Hotspot *findHotspotByMask(const HotSpotFlags); + + void activateMaskedHotspots(const HotSpotFlags = kNoHotSpotFlags); + void deactivateAllHotspots(); + void deactivateMaskedHotspots(const HotSpotFlags); + + void activateOneHotspot(const HotSpotID); + void deactivateOneHotspot(const HotSpotID); + + void removeOneHotspot(const HotSpotID); + void removeMaskedHotspots(const HotSpotFlags = kNoHotSpotFlags); + + void setHotspotRect(const HotSpotID, const Common::Rect&); + void getHotspotRect(const HotSpotID, Common::Rect&); +}; + +typedef HotspotList::iterator HotspotIterator; + +#define g_allHotspots (((PegasusEngine *)g_engine)->getAllHotspots()) + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/input.cpp b/engines/pegasus/input.cpp new file mode 100644 index 0000000000..b74e4a4c45 --- /dev/null +++ b/engines/pegasus/input.cpp @@ -0,0 +1,373 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "common/system.h" + +#include "pegasus/cursor.h" +#include "pegasus/input.h" +#include "pegasus/pegasus.h" + +namespace Common { +DECLARE_SINGLETON(Pegasus::InputDeviceManager); +} + +namespace Pegasus { + +InputDeviceManager::InputDeviceManager() { + // Set all keys to "not down" + _keyMap[Common::KEYCODE_UP] = false; + _keyMap[Common::KEYCODE_KP8] = false; + _keyMap[Common::KEYCODE_LEFT] = false; + _keyMap[Common::KEYCODE_KP4] = false; + _keyMap[Common::KEYCODE_DOWN] = false; + _keyMap[Common::KEYCODE_KP5] = false; + _keyMap[Common::KEYCODE_RIGHT] = false; + _keyMap[Common::KEYCODE_KP6] = false; + _keyMap[Common::KEYCODE_RETURN] = false; + _keyMap[Common::KEYCODE_SPACE] = false; + _keyMap[Common::KEYCODE_t] = false; + _keyMap[Common::KEYCODE_KP_EQUALS] = false; + _keyMap[Common::KEYCODE_i] = false; + _keyMap[Common::KEYCODE_KP_DIVIDE] = false; + _keyMap[Common::KEYCODE_q] = false; + _keyMap[Common::KEYCODE_ESCAPE] = false; + _keyMap[Common::KEYCODE_p] = false; + _keyMap[Common::KEYCODE_TILDE] = false; + _keyMap[Common::KEYCODE_BACKQUOTE] = false; + _keyMap[Common::KEYCODE_NUMLOCK] = false; + _keyMap[Common::KEYCODE_BACKSPACE] = false; + _keyMap[Common::KEYCODE_KP_MULTIPLY] = false; + _keyMap[Common::KEYCODE_LALT] = false; + _keyMap[Common::KEYCODE_RALT] = false; + _keyMap[Common::KEYCODE_e] = false; + _keyMap[Common::KEYCODE_KP_ENTER] = false; + + g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 2, false); + _lastRawBits = kAllUpBits; + _consoleRequested = false; +} + +InputDeviceManager::~InputDeviceManager() { + g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this); +} + +void InputDeviceManager::getInput(Input &input, const InputBits filter) { + // Poll for events, but ignore them! + // We'll pick them up in notifyEvent() + // We do that so that any pollEvent() call can update the variables + // (ie. if one uses enter to access the restore menu, we never receive + // the key up event, which leads to bad things) + // This is to closely emulate what the GetKeys() function did on Mac OS + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) + ; + + // Now create the bitfield + InputBits currentBits = 0; + + if (_keyMap[Common::KEYCODE_UP] || _keyMap[Common::KEYCODE_KP8]) + currentBits |= (kRawButtonDown << kUpButtonShift); + + if (_keyMap[Common::KEYCODE_DOWN] || _keyMap[Common::KEYCODE_KP5]) + currentBits |= (kRawButtonDown << kDownButtonShift); + + if (_keyMap[Common::KEYCODE_LEFT] || _keyMap[Common::KEYCODE_KP4]) + currentBits |= (kRawButtonDown << kLeftButtonShift); + + if (_keyMap[Common::KEYCODE_RIGHT] || _keyMap[Common::KEYCODE_KP6]) + currentBits |= (kRawButtonDown << kRightButtonShift); + + if (_keyMap[Common::KEYCODE_SPACE] || _keyMap[Common::KEYCODE_RETURN] || _keyMap[Common::KEYCODE_KP_ENTER]) + currentBits |= (kRawButtonDown << kTwoButtonShift); + + if (_keyMap[Common::KEYCODE_t] || _keyMap[Common::KEYCODE_KP_EQUALS]) + currentBits |= (kRawButtonDown << kThreeButtonShift); + + if (_keyMap[Common::KEYCODE_i] || _keyMap[Common::KEYCODE_KP_DIVIDE]) + currentBits |= (kRawButtonDown << kFourButtonShift); + + if (_keyMap[Common::KEYCODE_q]) + currentBits |= (kRawButtonDown << kMod1ButtonShift); + + if (_keyMap[Common::KEYCODE_ESCAPE] || _keyMap[Common::KEYCODE_p]) + currentBits |= (kRawButtonDown << kMod3ButtonShift); + + if (_keyMap[Common::KEYCODE_TILDE] || _keyMap[Common::KEYCODE_BACKQUOTE] || _keyMap[Common::KEYCODE_NUMLOCK]) + currentBits |= (kRawButtonDown << kLeftFireButtonShift); + + if (_keyMap[Common::KEYCODE_BACKSPACE] || _keyMap[Common::KEYCODE_KP_MULTIPLY]) + currentBits |= (kRawButtonDown << kRightFireButtonShift); + + // Update mouse button state + // Note that we don't use EVENT_LBUTTONUP/EVENT_LBUTTONDOWN because + // they do not show if the button is being held down. We're treating + // both mouse buttons as the same for ease of use. + if (g_system->getEventManager()->getButtonState() != 0) + currentBits |= (kRawButtonDown << kTwoButtonShift); + + // Update the mouse position too + input.setInputLocation(g_system->getEventManager()->getMousePos()); + + // Set the outgoing bits + InputBits filteredBits = currentBits & filter; + input.setInputBits((filteredBits & kAllButtonDownBits) | (filteredBits & _lastRawBits & kAllAutoBits)); + + // Update the last bits + _lastRawBits = currentBits; + + // Set the console to be requested or not + input.setConsoleRequested(_consoleRequested); + + // WORKAROUND: The original had this in currentBits, but then + // pressing alt would count as an event (and mess up someone + // trying to do alt+enter or something). Since it's only used + // as an easter egg, I'm just going to handle it as a separate + // bool value. + // WORKAROUND x2: I'm also accepting 'e' here since an + // alt+click is often intercepted by the OS. 'e' is used as the + // easter egg key in Buried in Time and Legacy of Time. + input.setAltDown(_keyMap[Common::KEYCODE_LALT] || _keyMap[Common::KEYCODE_RALT] || _keyMap[Common::KEYCODE_e]); +} + +// Wait until the input device stops returning input allowed by filter... +void InputDeviceManager::waitInput(const InputBits filter) { + if (filter != 0) { + for (;;) { + Input input; + getInput(input, filter); + if (!input.anyInput()) + break; + } + } +} + +bool InputDeviceManager::notifyEvent(const Common::Event &event) { + // We're mapping from ScummVM events to pegasus events, which + // are based on pippin events. + _consoleRequested = false; + + switch (event.type) { + case Common::EVENT_KEYDOWN: + switch (event.kbd.keycode) { + case Common::KEYCODE_d: + if (event.kbd.flags & Common::KBD_CTRL) // Console! + _consoleRequested = true; + break; + case Common::KEYCODE_s: + // We support meta where available and control elsewhere + if (event.kbd.flags & (Common::KBD_CTRL|Common::KBD_META)) + ((PegasusEngine *)g_engine)->requestSave(); + break; + case Common::KEYCODE_o: // o for open (original) + case Common::KEYCODE_l: // l for load (ScummVM terminology) + // We support meta where available and control elsewhere + if (event.kbd.flags & (Common::KBD_CTRL|Common::KBD_META)) + ((PegasusEngine *)g_engine)->requestLoad(); + break; + default: + // Otherwise, set the key to down if we have it + if (_keyMap.contains(event.kbd.keycode)) + _keyMap[event.kbd.keycode] = true; + break; + } + break; + case Common::EVENT_KEYUP: + // Set the key to up if we have it + if (_keyMap.contains(event.kbd.keycode)) + _keyMap[event.kbd.keycode] = false; + break; + default: + break; + } + + return false; +} + +int operator==(const Input &arg1, const Input &arg2) { + return arg1._inputState == arg2._inputState; +} + +int operator!=(const Input &arg1, const Input &arg2) { + return !operator==(arg1, arg2); +} + +InputHandler *InputHandler::_inputHandler = 0; +bool InputHandler::_invalHotspots = false; +InputBits InputHandler::_lastFilter = kFilterNoInput; + +InputHandler *InputHandler::setInputHandler(InputHandler *currentHandler) { + InputHandler *result = 0; + + if (_inputHandler != currentHandler && (!_inputHandler || _inputHandler->releaseInputFocus())) { + result = _inputHandler; + _inputHandler = currentHandler; + if (_inputHandler) + _inputHandler->grabInputFocus(); + } + + return result; +} + +void InputHandler::pollForInput() { + if (_inputHandler) { + Input input; + Hotspot *cursorSpot = 0; + + InputHandler::getInput(input, cursorSpot); + if (_inputHandler->isClickInput(input, cursorSpot)) + _inputHandler->clickInHotspot(input, cursorSpot); + else + _inputHandler->handleInput(input, cursorSpot); + } +} + +void InputHandler::getInput(Input &input, Hotspot *&cursorSpot) { + Cursor *cursor = ((PegasusEngine *)g_engine)->_cursor; + + if (_inputHandler) + _lastFilter = _inputHandler->getInputFilter(); + else + _lastFilter = kFilterAllInput; + + InputDevice.getInput(input, _lastFilter); + + if (_inputHandler && _inputHandler->wantsCursor() && (_lastFilter & _inputHandler->getClickFilter()) != 0) { + if (cursor->isVisible()) { + g_allHotspots.deactivateAllHotspots(); + _inputHandler->activateHotspots(); + + Common::Point cursorLocation; + cursor->getCursorLocation(cursorLocation); + cursorSpot = g_allHotspots.findHotspot(cursorLocation); + + if (_inputHandler) + _inputHandler->updateCursor(cursorLocation, cursorSpot); + } else { + cursor->hideUntilMoved(); + } + } else { + cursor->hide(); + } +} + +void InputHandler::readInputDevice(Input &input) { + InputDevice.getInput(input, kFilterAllInput); +} + +InputHandler::InputHandler(InputHandler *nextHandler) { + _nextHandler = nextHandler; + allowInput(true); +} + +InputHandler::~InputHandler() { + if (_inputHandler == this) + setInputHandler(_nextHandler); +} + +void InputHandler::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->handleInput(input, cursorSpot); +} + +void InputHandler::clickInHotspot(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->clickInHotspot(input, cursorSpot); +} + +bool InputHandler::isClickInput(const Input &input, const Hotspot *cursorSpot) { + if (_nextHandler) + return _nextHandler->isClickInput(input, cursorSpot); + + return false; +} + +void InputHandler::activateHotspots() { + if (_nextHandler) + _nextHandler->activateHotspots(); +} + +InputBits InputHandler::getInputFilter() { + if (_allowInput) { + if (_nextHandler) + return _nextHandler->getInputFilter(); + else + return kFilterAllInput; + } + + return kFilterNoInput; +} + +InputBits InputHandler::getClickFilter() { + if (_allowInput && _nextHandler) + return _nextHandler->getClickFilter(); + + return kFilterNoInput; +} + +void InputHandler::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) { + if (_nextHandler) + _nextHandler->updateCursor(cursorLocation, cursorSpot); +} + +bool InputHandler::wantsCursor() { + if (_allowInput) { + if (_nextHandler) + return _nextHandler->wantsCursor(); + else + return true; + } + + return false; +} + +Tracker *Tracker::_currentTracker = 0; + +void Tracker::handleInput(const Input &input, const Hotspot *) { + if (stopTrackingInput(input)) + stopTracking(input); + else if (isTracking()) + continueTracking(input); +} + +void Tracker::startTracking(const Input &) { + if (!isTracking()) { + _savedHandler = InputHandler::setInputHandler(this); + _currentTracker = this; + } +} + +void Tracker::stopTracking(const Input &) { + if (isTracking()) { + _currentTracker = NULL; + InputHandler::setInputHandler(_savedHandler); + } +} + +bool Tracker::isClickInput(const Input &input, const Hotspot *hotspot) { + return !isTracking() && InputHandler::isClickInput(input, hotspot); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/input.h b/engines/pegasus/input.h new file mode 100644 index 0000000000..3e938fa42a --- /dev/null +++ b/engines/pegasus/input.h @@ -0,0 +1,500 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_INPUT_H +#define PEGASUS_INPUT_H + +#include "common/events.h" +#include "common/hashmap.h" +#include "common/rect.h" +#include "common/singleton.h" + +#include "pegasus/constants.h" +#include "pegasus/types.h" + +namespace Pegasus { + +class Hotspot; +class Input; + +class InputDeviceManager : public Common::Singleton<InputDeviceManager>, public Common::EventObserver { +public: + InputDeviceManager(); + ~InputDeviceManager(); + + bool notifyEvent(const Common::Event &event); + + void getInput(Input &, const InputBits); + + void waitInput(const InputBits); + +protected: + friend class Common::Singleton<SingletonBaseType>; + + // Keep track of which keys are down (= true) or not + Common::HashMap<uint, bool> _keyMap; + InputBits _lastRawBits; + bool _consoleRequested; +}; + +enum { + kButtonDownBit = 0, + kAutoButtonBit = 1, + kBitsPerButton = 2, + + kButtonDownMask = 1 << kButtonDownBit, + kAutoButtonMask = 1 << kAutoButtonBit, + + kButtonStateBits = kButtonDownMask | kAutoButtonMask, + + kRawButtonUp = 0, + kRawButtonDown = kButtonDownMask | kAutoButtonMask, + + kButtonUp = 0, + kButtonDown = kButtonDownMask, + kButtonAutoUp = kAutoButtonMask, + kButtonAutoDown = kButtonDownMask | kAutoButtonMask +}; + +enum { + kUpButtonNum = 0, + kLeftButtonNum = 1, + kDownButtonNum = 2, + kRightButtonNum = 3, + kLeftFireButtonNum = 4, + kRightFireButtonNum = 5, + kOneButtonNum = 6, + kTwoButtonNum = 7, + kThreeButtonNum = 8, + kFourButtonNum = 9, + kMod1ButtonNum = 10, + kMod2ButtonNum = 11, + kMod3ButtonNum = 12 +}; + +enum { + kUpButtonShift = kUpButtonNum * kBitsPerButton, + kLeftButtonShift = kLeftButtonNum * kBitsPerButton, + kDownButtonShift = kDownButtonNum * kBitsPerButton, + kRightButtonShift = kRightButtonNum * kBitsPerButton, + kLeftFireButtonShift = kLeftFireButtonNum * kBitsPerButton, + kRightFireButtonShift = kRightFireButtonNum * kBitsPerButton, + kOneButtonShift = kOneButtonNum * kBitsPerButton, + kTwoButtonShift = kTwoButtonNum * kBitsPerButton, + kThreeButtonShift = kThreeButtonNum * kBitsPerButton, + kFourButtonShift = kFourButtonNum * kBitsPerButton, + kMod1ButtonShift = kMod1ButtonNum * kBitsPerButton, + kMod2ButtonShift = kMod2ButtonNum * kBitsPerButton, + kMod3ButtonShift = kMod3ButtonNum * kBitsPerButton +}; + +enum { + kAllUpBits = (kButtonUp << kUpButtonShift) | + (kButtonUp << kLeftButtonShift) | + (kButtonUp << kDownButtonShift) | + (kButtonUp << kRightButtonShift) | + (kButtonUp << kLeftFireButtonShift) | + (kButtonUp << kRightFireButtonShift) | + (kButtonUp << kOneButtonShift) | + (kButtonUp << kTwoButtonShift) | + (kButtonUp << kThreeButtonShift) | + (kButtonUp << kFourButtonShift) | + (kButtonUp << kMod1ButtonShift) | + (kButtonUp << kMod2ButtonShift) | + (kButtonUp << kMod3ButtonShift), + kDirectionBits = (kButtonDownMask << kUpButtonShift) | + (kButtonDownMask << kLeftButtonShift) | + (kButtonDownMask << kDownButtonShift) | + (kButtonDownMask << kRightButtonShift), + kButtonBits = (kButtonDownMask << kLeftFireButtonShift) | + (kButtonDownMask << kRightFireButtonShift) | + (kButtonDownMask << kOneButtonShift) | + (kButtonDownMask << kTwoButtonShift) | + (kButtonDownMask << kThreeButtonShift) | + (kButtonDownMask << kFourButtonShift) | + (kButtonDownMask << kMod1ButtonShift) | + (kButtonDownMask << kMod2ButtonShift) | + (kButtonDownMask << kMod3ButtonShift), + kAllButtonDownBits = kDirectionBits | kButtonBits, + kAllAutoBits = (kAutoButtonMask << kUpButtonShift) | + (kAutoButtonMask << kLeftButtonShift) | + (kAutoButtonMask << kDownButtonShift) | + (kAutoButtonMask << kRightButtonShift) | + (kAutoButtonMask << kLeftFireButtonShift) | + (kAutoButtonMask << kRightFireButtonShift) | + (kAutoButtonMask << kOneButtonShift) | + (kAutoButtonMask << kTwoButtonShift) | + (kAutoButtonMask << kThreeButtonShift) | + (kAutoButtonMask << kFourButtonShift) | + (kAutoButtonMask << kMod1ButtonShift) | + (kAutoButtonMask << kMod2ButtonShift) | + (kAutoButtonMask << kMod3ButtonShift), + + kFilterUpButton = kButtonDownMask << kUpButtonShift, + kFilterUpAuto = kAutoButtonMask << kUpButtonShift, + kFilterUpButtonAny = kFilterUpButton | kFilterUpAuto, + kFilterLeftButton = kButtonDownMask << kLeftButtonShift, + kFilterLeftAuto = kAutoButtonMask << kLeftButtonShift, + kFilterLeftButtonAny = kFilterLeftButton | kFilterLeftAuto, + kFilterDownButton = kButtonDownMask << kDownButtonShift, + kFilterDownAuto = kAutoButtonMask << kDownButtonShift, + kFilterDownButtonAny = kFilterDownButton | kFilterDownAuto, + kFilterRightButton = kButtonDownMask << kRightButtonShift, + kFilterRightAuto = kAutoButtonMask << kRightButtonShift, + kFilterRightButtonAny = kFilterRightButton | kFilterRightAuto, + kFilterLeftFireButton = kButtonDownMask << kLeftFireButtonShift, + kFilterLeftFireAuto = kAutoButtonMask << kLeftFireButtonShift, + kFilterLeftFireButtonAny = kFilterLeftFireButton | kFilterLeftFireAuto, + kFilterRightFireButton = kButtonDownMask << kRightFireButtonShift, + kFilterRightFireAuto = kAutoButtonMask << kRightFireButtonShift, + kFilterRightFireButtonAny = kFilterRightFireButton | kFilterRightFireAuto, + kFilterOneButton = kButtonDownMask << kOneButtonShift, + kFilterOneAuto = kAutoButtonMask << kOneButtonShift, + kFilterOneButtonAny = kFilterOneButton | kFilterOneAuto, + kFilterTwoButton = kButtonDownMask << kTwoButtonShift, + kFilterTwoAuto = kAutoButtonMask << kTwoButtonShift, + kFilterTwoButtonAny = kFilterTwoButton | kFilterTwoAuto, + kFilterThreeButton = kButtonDownMask << kThreeButtonShift, + kFilterThreeAuto = kAutoButtonMask << kThreeButtonShift, + kFilterThreeButtonAny = kFilterThreeButton | kFilterThreeAuto, + kFilterFourButton = kButtonDownMask << kFourButtonShift, + kFilterFourAuto = kAutoButtonMask << kFourButtonShift, + kFilterFourButtonAny = kFilterFourButton | kFilterFourAuto, + kFilterMod1Button = kButtonDownMask << kMod1ButtonShift, + kFilterMod1Auto = kAutoButtonMask << kMod1ButtonShift, + kFilterMod1ButtonAny = kFilterMod1Button | kFilterMod1Auto, + kFilterMod2Button = kButtonDownMask << kMod2ButtonShift, + kFilterMod2Auto = kAutoButtonMask << kMod2ButtonShift, + kFilterMod2ButtonAny = kFilterMod2Button | kFilterMod2Auto, + kFilterMod3Button = kButtonDownMask << kMod3ButtonShift, + kFilterMod3Auto = kAutoButtonMask << kMod3ButtonShift, + kFilterMod3ButtonAny = kFilterMod3Button | kFilterMod3Auto, + + kFilterNoInput = 0, + kFilterAllInput = kFilterUpButton | + kFilterUpAuto | + kFilterLeftButton | + kFilterLeftAuto | + kFilterDownButton | + kFilterDownAuto | + kFilterRightButton | + kFilterRightAuto | + kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto | + kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto | + kFilterMod1Button | + kFilterMod1Auto | + kFilterMod2Button | + kFilterMod2Auto | + kFilterMod3Button | + kFilterMod3Auto, + + kFilterAllDirections = kFilterUpButton | + kFilterUpAuto | + kFilterLeftButton | + kFilterLeftAuto | + kFilterDownButton | + kFilterDownAuto | + kFilterRightButton | + kFilterRightAuto, + + kFilterButtons = kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto, + + kFilterFireButtons = kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto, + + kFilterAllButtons = kFilterLeftFireButton | + kFilterLeftFireAuto | + kFilterRightFireButton | + kFilterRightFireAuto | + kFilterOneButton | + kFilterOneAuto | + kFilterTwoButton | + kFilterTwoAuto | + kFilterThreeButton | + kFilterThreeAuto | + kFilterFourButton | + kFilterFourAuto | + kFilterMod1Button | + kFilterMod1Auto | + kFilterMod2Button | + kFilterMod2Auto | + kFilterMod3Button | + kFilterMod3Auto, + + kFilterAllInputNoAuto = kFilterUpButton | + kFilterLeftButton | + kFilterDownButton | + kFilterRightButton | + kFilterLeftFireButton | + kFilterRightFireButton | + kFilterOneButton | + kFilterTwoButton | + kFilterThreeButton | + kFilterFourButton | + kFilterMod1Button | + kFilterMod2Button | + kFilterMod3Button +}; + +static const InputBits kHintInterruption = kFilterAllInputNoAuto; +static const InputBits kWarningInterruption = kFilterNoInput; +static const InputBits kOpticalInterruption = kFilterAllInputNoAuto; + +/* + + Buttons are defined as: + up, left, down, right direction buttons. + fireLeft, fireRight: fire buttons. + mod1, mod2, mod3: modifier buttons, similar to shift, control, etc. + a, b, c, d: general purpose buttons. + + button state is held as bits in a long word, two bits per button. + + Filter bits: + for each button, two bits are assigned for filtering. If bit 0 is set, the + corresponding button is available for "button down" input. If bit 1 is set, + the corresponding button is available for "auto down" input. Note that bit + 1 is meaningful only if bit 0 is set. + +*/ + +class Input { +friend int operator==(const Input &, const Input &); +friend int operator!=(const Input &, const Input &); +friend class InputDeviceManager; + +public: + Input() { clearInput(); } + + bool upButtonDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonDown << kUpButtonShift); } + bool upButtonAutoDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonAutoDown << kUpButtonShift); } + bool upButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kUpButtonShift)) != 0; } + + bool leftButtonDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonDown << kLeftButtonShift); } + bool leftButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonAutoDown << kLeftButtonShift); } + bool leftButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftButtonShift)) != 0; } + + bool downButtonDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonDown << kDownButtonShift); } + bool downButtonAutoDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonAutoDown << kDownButtonShift); } + bool downButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kDownButtonShift)) != 0; } + + bool rightButtonDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonDown << kRightButtonShift); } + bool rightButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonAutoDown << kRightButtonShift); } + bool rightButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightButtonShift)) != 0; } + + bool leftFireButtonDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonDown << kLeftFireButtonShift); } + bool leftFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonAutoDown << kLeftFireButtonShift); } + bool leftFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftFireButtonShift)) != 0; } + + bool rightFireButtonDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonDown << kRightFireButtonShift); } + bool rightFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonAutoDown << kRightFireButtonShift); } + bool rightFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightFireButtonShift)) != 0; } + + bool oneButtonDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonDown << kOneButtonShift); } + bool oneButtonAutoDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonAutoDown << kOneButtonShift); } + bool oneButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kOneButtonShift)) != 0; } + + bool twoButtonDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonDown << kTwoButtonShift); } + bool twoButtonAutoDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonAutoDown << kTwoButtonShift); } + bool twoButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kTwoButtonShift)) != 0; } + + bool threeButtonDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonDown << kThreeButtonShift); } + bool threeButtonAutoDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonAutoDown << kThreeButtonShift); } + bool threeButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kThreeButtonShift)) != 0; } + + bool fourButtonDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonDown << kFourButtonShift); } + bool fourButtonAutoDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonAutoDown << kFourButtonShift); } + bool fourButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kFourButtonShift)) != 0; } + + bool mod1ButtonDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonDown << kMod1ButtonShift); } + bool mod1ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonAutoDown << kMod1ButtonShift); } + bool mod1ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod1ButtonShift)) != 0; } + + bool mod2ButtonDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonDown << kMod2ButtonShift); } + bool mod2ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonAutoDown << kMod2ButtonShift); } + bool mod2ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod2ButtonShift)) != 0; } + + bool mod3ButtonDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonDown << kMod3ButtonShift); } + bool mod3ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonAutoDown << kMod3ButtonShift); } + bool mod3ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod3ButtonShift)) != 0; } + + bool allAutoInput() const { return (_inputState & kAllAutoBits) != 0; } + bool anyDirectionInput() const { return (_inputState & kDirectionBits) != 0; } + bool anyButtonInput() const { return (_inputState & kButtonBits) != 0; } + bool anyInput() const { return _inputState != 0; } + + void getInputLocation(Common::Point &where) const { where = _inputLocation; } + + bool anyInputBitSet(const InputBits bits) const { return (_inputState & bits) != 0; } + + bool isAltDown() const { return _altDown; } + bool isConsoleRequested() const { return _consoleRequested; } + + void clearInput() { + _inputState = kAllUpBits; + _inputLocation.x = 0; + _inputLocation.y = 0; + _consoleRequested = false; + _altDown = false; + } + +protected: + void setInputBits(const InputBits state) { _inputState = state; } + void setInputLocation(const Common::Point &where) { _inputLocation = where; } + void setConsoleRequested(bool consoleRequested) { _consoleRequested = consoleRequested; } + void setAltDown(bool altDown) { _altDown = altDown; } + + InputBits _inputState; + Common::Point _inputLocation; + bool _consoleRequested; + bool _altDown; +}; + +class InputHandler { +public: + static InputHandler *setInputHandler(InputHandler*); + static InputHandler *getCurrentHandler() { return _inputHandler; } + static void pollForInput(); + static void getInput(Input&, Hotspot*&); + static void readInputDevice(Input&); + static void invalHotspots() { _invalHotspots = true; } + static InputBits getCurrentFilter() { return _lastFilter; } + + InputHandler(InputHandler*); + virtual ~InputHandler(); + + virtual void setNextHandler(InputHandler *nextHandler) { _nextHandler = nextHandler; } + virtual InputHandler *getNextHandler() { return _nextHandler; } + + virtual void handleInput(const Input &, const Hotspot *); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void activateHotspots(); + virtual void updateCursor(const Common::Point, const Hotspot *); + virtual bool isClickInput(const Input &, const Hotspot *); + virtual bool wantsCursor(); + + virtual bool releaseInputFocus() { return true; } + virtual void grabInputFocus() {} + + // This returns bits set for what kinds of input to accept. + virtual InputBits getInputFilter(); + + // This returns bits defining what input constitutes a "click." + virtual InputBits getClickFilter(); + + virtual void allowInput(const bool allow) { _allowInput = allow; } + +protected: + static InputHandler *_inputHandler; + static bool _invalHotspots; + static InputBits _lastFilter; + + InputHandler *_nextHandler; + bool _allowInput; +}; + + +/* + + Tracker implements "dragging". A Tracker can receive a startTracking message, + which causes it to be the current tracker, as well as setting it up as the current + input handler. In addition, only one tracker can be tracking at a time, and no + other handler can be set up as the current handler until the track finishes. By + default, there is no next input handler for a Tracker, but this behavior can be + overridden if desired. + +*/ + +class Tracker : public InputHandler { +public: + Tracker() : InputHandler(0) {} + virtual ~Tracker() {} + + virtual void handleInput(const Input &, const Hotspot *); + virtual bool stopTrackingInput(const Input &) { return false; } + + virtual void startTracking(const Input &); + virtual void stopTracking(const Input &); + virtual void continueTracking(const Input &) {} + + bool isTracking() { return this == _currentTracker; } + bool isClickInput(const Input &, const Hotspot *); + + bool releaseInputFocus() { return !isTracking(); } + +protected: + static Tracker *_currentTracker; + + InputHandler *_savedHandler; +}; + +class JMPPPInput { +public: + static bool isMenuButtonPressInput(const Input &input) { return input.twoButtonDown(); } + + static InputBits getClickInputFilter() { return kFilterTwoButton; } + static bool isClickInput(const Input &input) { return input.twoButtonDown(); } + static bool isDraggingInput(const Input &input) { return input.twoButtonAnyDown(); } + static bool isPressingInput(const Input &input) { return input.twoButtonAnyDown(); } + + static bool isRaiseInventoryInput(const Input &input) { return input.leftFireButtonDown(); } + static bool isRaiseBiochipsInput(const Input &input) { return input.rightFireButtonDown(); } + static InputBits getItemPanelsInputFilter() { return kFilterLeftFireButton | kFilterRightFireButton; } + + static bool isToggleAIMiddleInput(const Input &input) { return input.threeButtonDown(); } + + static bool isToggleInfoInput(const Input &input) { return input.fourButtonDown(); } + + // Hmmmmm.... + static bool isEasterEggModifierInput(const Input &input) { return input.isAltDown(); } + + static bool isTogglePauseInput(const Input &input) { return input.mod3ButtonDown(); } +}; + +} // End of namespace Pegasus + +#define InputDevice (::Pegasus::InputDeviceManager::instance()) + +#endif diff --git a/engines/pegasus/interaction.h b/engines/pegasus/interaction.h new file mode 100644 index 0000000000..293ee6be83 --- /dev/null +++ b/engines/pegasus/interaction.h @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_INTERACTION_H +#define PEGASUS_INTERACTION_H + +#include "pegasus/input.h" +#include "pegasus/util.h" + +namespace Pegasus { + +static const InteractionID kNoInteractionID = -1; + +class Neighborhood; + +class GameInteraction : public IDObject, public InputHandler { +public: + GameInteraction(const InteractionID id, Neighborhood *nextHandler) : IDObject(id), InputHandler((InputHandler *)nextHandler) { + _isInteracting = false; + _savedHandler = 0; + _owner = nextHandler; + } + + // If the interaction is open (_isInteracting == true), it's too late to do anything + // about it here. + virtual ~GameInteraction() {} + + // startInteraction and stopInteraction are called by the outside world to + // start and stop the interaction sequence. + // isInteracting returns a bool indicating whether or not the interaction + // is going. + void startInteraction() { + if (!isInteracting()) { + openInteraction(); + initInteraction(); + _isInteracting = true; + _savedHandler = InputHandler::setInputHandler(this); + } + } + void stopInteraction() { + if (isInteracting()) { + closeInteraction(); + _isInteracting = false; + if (InputHandler::_inputHandler == this) + InputHandler::setInputHandler(_savedHandler); + } + } + void startOverInteraction() { + if (isInteracting()) + resetInteraction(); + } + bool isInteracting() const { return _isInteracting; } + Neighborhood *getOwner() const { return _owner; } + + virtual Common::String getBriefingMovie() { return ""; } + virtual Common::String getEnvScanMovie() { return ""; } + virtual long getNumHints() { return 0; } + virtual Common::String getHintMovie(uint) { return ""; } + virtual bool canSolve() { return false; } + + virtual void setSoundFXLevel(const uint16) {} + virtual void setAmbienceLevel(const uint16) {} + + virtual void doSolve() {} + +protected: + // Subclasses override openInteraction and closeInteraction to perform + // specific initialization and cleanup. Override resetInteraction to + // "start the interaction over." resetInteraction is called only when + // the interaction is already open. + // These functions are only called in pairs, never two opens or closes + // in a row. + virtual void openInteraction() {} + virtual void initInteraction() {} + virtual void closeInteraction() {} + virtual void resetInteraction() {} + + InputHandler *_savedHandler; + Neighborhood *_owner; + +private: + // Private so that only StartInteraction and StopInteraction can touch it. + bool _isInteracting; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/interface.cpp b/engines/pegasus/interface.cpp new file mode 100644 index 0000000000..d9d3865192 --- /dev/null +++ b/engines/pegasus/interface.cpp @@ -0,0 +1,667 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/compass.h" +#include "pegasus/energymonitor.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +Interface *g_interface = 0; + +Interface::Interface() : InputHandler(0), _interfaceNotification(kInterfaceNotificationID, (NotificationManager *)((PegasusEngine *)g_engine)), + _currentItemSpot(kCurrentItemSpotID), _currentBiochipSpot(kCurrentBiochipSpotID), + _background1(kInterface1ID), _background2(kInterface2ID), _background3(kInterface3ID), + _background4(kInterface4ID), _datePicture(kDateID), _inventoryPush(kInventoryPushID), + _inventoryLid(kInventoryLidID, kNoDisplayElement), + _inventoryPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getItemsInventory()), + _biochipPush(kBiochipPushID), _biochipLid(kBiochipLidID, kNoDisplayElement), + _biochipPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getBiochipsInventory()) { + g_energyMonitor = 0; + _previousHandler = 0; + _inventoryRaised = false; + _biochipRaised = false; + _playingEndMessage = false; + g_interface = this; +} + +Interface::~Interface() { + throwAwayInterface(); + g_interface = 0; +} + +void Interface::throwAwayInterface() { + g_allHotspots.removeOneHotspot(kCurrentItemSpotID); + g_allHotspots.removeOneHotspot(kCurrentBiochipSpotID); + + throwAwayBackground(); + throwAwayDateMonitor(); + throwAwayEnergyMonitor(); + throwAwayAIArea(); + throwAwayCompass(); + throwAwayNotifications(); + throwAwayInventoryPanel(); + throwAwayBiochipPanel(); +} + +void Interface::validateBackground() { + if (!_background1.isSurfaceValid()) { + _background1.initFromPICTFile("Images/Interface/3DInterface Left"); + _background2.initFromPICTFile("Images/Interface/3DInterface Top"); + _background3.initFromPICTFile("Images/Interface/3DInterface Right"); + _background4.initFromPICTFile("Images/Interface/3DInterface Bottom"); + + _background1.setDisplayOrder(kBackground1Order); + _background1.startDisplaying(); + _background1.moveElementTo(kBackground1Left, kBackground1Top); + + _background2.setDisplayOrder(kBackground2Order); + _background2.startDisplaying(); + _background2.moveElementTo(kBackground2Left, kBackground2Top); + + _background3.setDisplayOrder(kBackground2Order); + _background3.startDisplaying(); + _background3.moveElementTo(kBackground3Left, kBackground3Top); + + _background4.setDisplayOrder(kBackground4Order); + _background4.startDisplaying(); + _background4.moveElementTo(kBackground4Left, kBackground4Top); + + _background1.show(); + _background2.show(); + _background3.show(); + _background4.show(); + } +} + +void Interface::throwAwayBackground() { + _background1.stopDisplaying(); + _background1.deallocateSurface(); + _background2.stopDisplaying(); + _background2.deallocateSurface(); + _background3.stopDisplaying(); + _background3.deallocateSurface(); + _background4.stopDisplaying(); + _background4.deallocateSurface(); +} + +void Interface::validateDateMonitor() { + if (!_datePicture.isSurfaceValid()) { + _datePicture.setDisplayOrder(kDateOrder); + _datePicture.startDisplaying(); + _datePicture.moveElementTo(kDateLeft, kDateTop); + _datePicture.show(); + } +} + +void Interface::throwAwayDateMonitor() { + _datePicture.stopDisplaying(); + _datePicture.deallocateSurface(); +} + +void Interface::setDate(const uint16 dateResID) { + validateDateMonitor(); + _datePicture.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, dateResID); + _datePicture.triggerRedraw(); +} + +void Interface::validateCompass() { + if (!g_compass) { + new Compass(); + g_compass->initCompass(); + g_compass->setDisplayOrder(kCompassOrder); + g_compass->startDisplaying(); + g_compass->moveElementTo(kCompassLeft, kCompassTop); + g_compass->show(); + } +} + +void Interface::throwAwayCompass() { + delete g_compass; +} + +void Interface::validateNotifications() { + _interfaceNotification.notifyMe(this, kInterfaceNotificationFlags, kInterfaceNotificationFlags); + _inventoryLidCallBack.setNotification(&_interfaceNotification); + _inventoryPushCallBack.setNotification(&_interfaceNotification); + _biochipLidCallBack.setNotification(&_interfaceNotification); + _biochipPushCallBack.setNotification(&_interfaceNotification); +} + +void Interface::throwAwayNotifications() { + _interfaceNotification.cancelNotification(this); +} + +void Interface::validateAIArea() { + if (!g_AIArea) { + new AIArea((InputHandler *)((PegasusEngine *)g_engine)); + if (g_AIArea) + g_AIArea->initAIArea(); + } +} + +void Interface::throwAwayAIArea() { + delete g_AIArea; +} + +void Interface::validateInventoryPanel() { + if (!_inventoryPanel.isSurfaceValid()) { + _inventoryPanel.initInventoryImage(&_inventoryPush); + _inventoryPanel.moveElementTo(kInventoryPushLeft, kInventoryPushTop); + _inventoryPush.setSlideDirection(kSlideUpMask); + _inventoryPush.setInAndOutElements(&_inventoryPanel, 0); + _inventoryPush.setDisplayOrder(kInventoryPushOrder); + _inventoryPush.startDisplaying(); + + _inventoryLid.useFileName("Images/Lids/Inventory Lid Sequence"); + _inventoryLid.useTransparent(true); + _inventoryLid.openFrameSequence(); + _inventoryLid.moveElementTo(kInventoryLidLeft, kInventoryLidTop); + _inventoryLid.setDisplayOrder(kInventoryLidOrder); + _inventoryLid.startDisplaying(); + + _inventoryPushCallBack.initCallBack(&_inventoryPush, kCallBackAtExtremes); + _inventoryLidCallBack.initCallBack(&_inventoryLid, kCallBackAtExtremes); + + _inventoryUp = false; + _inventoryRaised = false; + + Item *item = getCurrentInventoryItem(); + if (item) + item->select(); + } +} + +void Interface::throwAwayInventoryPanel() { + _inventoryPanel.stopDisplaying(); + _inventoryPanel.throwAwayInventoryImage(); + _inventoryPush.stopDisplaying(); + _inventoryLid.stopDisplaying(); + _inventoryLid.closeFrameSequence(); + _inventoryPushCallBack.releaseCallBack(); + _inventoryLidCallBack.releaseCallBack(); + + Item *item = getCurrentInventoryItem(); + if (item) + item->deselect(); + + _inventoryUp = false; + _inventoryRaised = false; +} + +void Interface::validateBiochipPanel() { + if (!_biochipPanel.isSurfaceValid()) { + _biochipPanel.initInventoryImage(&_biochipPush); + _biochipPanel.moveElementTo(kBiochipPushLeft, kBiochipPushTop); + _biochipPush.setSlideDirection(kSlideUpMask); + _biochipPush.setInAndOutElements(&_biochipPanel, 0); + _biochipPush.setDisplayOrder(kBiochipPushOrder); + _biochipPush.startDisplaying(); + + _biochipLid.useFileName("Images/Lids/Biochip Lid Sequence"); + _biochipLid.useTransparent(true); + _biochipLid.openFrameSequence(); + _biochipLid.moveElementTo(kBiochipLidLeft, kBiochipLidTop); + _biochipLid.setDisplayOrder(kBiochipLidOrder); + _biochipLid.startDisplaying(); + + _biochipPushCallBack.initCallBack(&_biochipPush, kCallBackAtExtremes); + _biochipLidCallBack.initCallBack(&_biochipLid, kCallBackAtExtremes); + + _biochipUp = false; + _biochipRaised = false; + + Item *item = getCurrentBiochip(); + if (item) + item->select(); + } +} + +void Interface::throwAwayBiochipPanel() { + _biochipPanel.stopDisplaying(); + _biochipPanel.throwAwayInventoryImage(); + _biochipPush.stopDisplaying(); + _biochipLid.stopDisplaying(); + _biochipLid.closeFrameSequence(); + _biochipPushCallBack.releaseCallBack(); + _biochipLidCallBack.releaseCallBack(); + + Item *item = getCurrentBiochip(); + if (item) + item->deselect(); + + _biochipUp = false; + _biochipRaised = false; +} + +void Interface::validateEnergyMonitor() { + if (!g_energyMonitor) + new EnergyMonitor(); +} + +void Interface::throwAwayEnergyMonitor() { + delete g_energyMonitor; +} + +void Interface::createInterface() { + validateBackground(); + validateDateMonitor(); + validateCompass(); + validateNotifications(); + validateAIArea(); + validateBiochipPanel(); + validateInventoryPanel(); + validateEnergyMonitor(); + + if (!g_allHotspots.findHotspotByID(kCurrentItemSpotID)) { + _currentItemSpot.setArea(Common::Rect(76, 334, 172, 430)); + _currentItemSpot.setHotspotFlags(kShellSpotFlag); + _currentItemSpot.setActive(); + g_allHotspots.push_back(&_currentItemSpot); + } + + if (!g_allHotspots.findHotspotByID(kCurrentBiochipSpotID)) { + _currentBiochipSpot.setArea(Common::Rect(364, 334, 460, 430)); + _currentBiochipSpot.setHotspotFlags(kShellSpotFlag); + _currentBiochipSpot.setActive(); + g_allHotspots.push_back(&_currentBiochipSpot); + } +} + +InventoryResult Interface::addInventoryItem(InventoryItem *item) { + return _inventoryPanel.addInventoryItem(item); +} + +InventoryResult Interface::removeInventoryItem(InventoryItem *item) { + return _inventoryPanel.removeInventoryItem(item); +} + +void Interface::removeAllItemsFromInventory() { + _inventoryPanel.removeAllItems(); +} + +InventoryItem *Interface::getCurrentInventoryItem() { + return (InventoryItem *)_inventoryPanel.getCurrentItem(); +} + +void Interface::setCurrentInventoryItem(InventoryItem *item) { + setCurrentInventoryItemID(item->getObjectID()); +} + +void Interface::setCurrentInventoryItemID(ItemID id) { + _inventoryPanel.setCurrentItemID(id); +} + +InventoryResult Interface::addBiochip(BiochipItem *item) { + return _biochipPanel.addInventoryItem(item); +} + +void Interface::removeAllItemsFromBiochips() { + _biochipPanel.removeAllItems(); +} + +BiochipItem *Interface::getCurrentBiochip() { + return (BiochipItem *)_biochipPanel.getCurrentItem(); +} + +void Interface::setCurrentBiochip(BiochipItem *item) { + setCurrentBiochipID(item->getObjectID()); +} + +void Interface::setCurrentBiochipID(ItemID id) { + _biochipPanel.setCurrentItemID(id); +} + +void Interface::receiveNotification(Notification *notification, const NotificationFlags flags) { + if (notification == &_interfaceNotification) { + switch (flags) { + case kInventoryLidOpenFlag: + inventoryLidOpen(true); + break; + case kInventoryLidClosedFlag: + inventoryLidClosed(); + break; + case kInventoryDrawerUpFlag: + inventoryDrawerUp(); + break; + case kInventoryDrawerDownFlag: + inventoryDrawerDown(true); + break; + case kBiochipLidOpenFlag: + biochipLidOpen(true); + break; + case kBiochipLidClosedFlag: + biochipLidClosed(); + break; + case kBiochipDrawerUpFlag: + biochipDrawerUp(); + break; + case kBiochipDrawerDownFlag: + biochipDrawerDown(true); + break; + } + } +} + +void Interface::raiseInventoryDrawer(const bool doCallBacks) { + if (!_biochipUp) + _previousHandler = InputHandler::getCurrentHandler(); + + InputHandler::setInputHandler(&_inventoryPanel); + _inventoryUp = true; + _inventoryPanel.activateInventoryPicture(); + + if (doCallBacks) { + _inventoryLidCallBack.setCallBackFlag(kInventoryLidOpenFlag); + _inventoryLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _inventoryLid.show(); + _inventoryPush.show(); + _inventoryLid.start(); +} + +void Interface::playEndMessage() { + raiseInventoryDrawerForMessage(); + _playingEndMessage = true; + _inventoryPanel.playEndMessage(&_inventoryPush); + lowerInventoryDrawerForMessage(); + _playingEndMessage = false; +} + +void Interface::raiseInventoryDrawerForMessage() { + _inventoryPanel.disableLooping(); + raiseInventoryDrawerSync(); +} + +void Interface::lowerInventoryDrawerForMessage() { + lowerInventoryDrawerSync(); +} + +void Interface::inventoryLidOpen(const bool doCallBacks) { + _inventoryLid.stop(); + + if (doCallBacks) { + _inventoryPushCallBack.setCallBackFlag(kInventoryDrawerUpFlag); + _inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000); + _inventoryPush.startFader(moveSpec); +} + +void Interface::inventoryDrawerUp() { + _inventoryPush.stopFader(); + _inventoryPanel.panelUp(); + _inventoryRaised = true; +} + +bool Interface::isInventoryUp() { + return _inventoryRaised; +} + +bool Interface::isInventoryDown() { + return !_inventoryUp; +} + +void Interface::lowerInventoryDrawer(const bool doCallBacks) { + if (_inventoryRaised) { + _inventoryRaised = false; + + if (!_playingEndMessage) + _inventoryPanel.deactivateInventoryPicture(); + + if (doCallBacks) { + _inventoryPushCallBack.setCallBackFlag(kInventoryDrawerDownFlag); + _inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 15, 0); + _inventoryPush.startFader(moveSpec); + } +} + +void Interface::inventoryDrawerDown(const bool doCallBacks) { + _inventoryPush.stopFader(); + + if (doCallBacks) { + _inventoryLidCallBack.setCallBackFlag(kInventoryLidClosedFlag); + _inventoryLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + } + + _inventoryLid.setRate(-1); +} + +void Interface::inventoryLidClosed() { + _inventoryLid.stop(); + + if (!_biochipUp) + InputHandler::setInputHandler(_previousHandler); + + _inventoryLid.hide(); + _inventoryPush.hide(); + _inventoryUp = false; +} + +void Interface::raiseBiochipDrawer(const bool doCallBacks) { + if (!_inventoryUp) + _previousHandler = InputHandler::getCurrentHandler(); + + InputHandler::setInputHandler(&_biochipPanel); + _biochipUp = true; + _biochipPanel.activateInventoryPicture(); + + if (doCallBacks) { + _biochipLidCallBack.setCallBackFlag(kBiochipLidOpenFlag); + _biochipLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _biochipLid.show(); + _biochipPush.show(); + _biochipLid.start(); +} + +void Interface::biochipLidOpen(const bool doCallBacks) { + _biochipLid.stop(); + + if (doCallBacks) { + _biochipPushCallBack.setCallBackFlag(kBiochipDrawerUpFlag); + _biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 9, 1000); + _biochipPush.startFader(moveSpec); +} + +void Interface::biochipDrawerUp() { + _biochipPush.stopFader(); + _biochipPanel.panelUp(); + _biochipRaised = true; +} + +void Interface::lowerBiochipDrawer(const bool doCallBacks) { + if (_biochipRaised) { + _biochipRaised = false; + _biochipPanel.deactivateInventoryPicture(); + + if (doCallBacks) { + _biochipPushCallBack.setCallBackFlag(kBiochipDrawerDownFlag); + _biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 9, 0); + _biochipPush.startFader(moveSpec); + } +} + +void Interface::biochipDrawerDown(const bool doCallBacks) { + _biochipPush.stopFader(); + + if (doCallBacks) { + _biochipLidCallBack.setCallBackFlag(kBiochipLidClosedFlag); + _biochipLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + } + + _biochipLid.setRate(-1); +} + +void Interface::biochipLidClosed() { + _biochipLid.stop(); + + if (!_inventoryUp) + InputHandler::setInputHandler(_previousHandler); + + _biochipLid.hide(); + _biochipPush.hide(); + _biochipUp = false; +} + +void Interface::calibrateCompass() { + uint32 currentValue = g_compass->getFaderValue(); + FaderMoveSpec compassMove; + compassMove.makeTwoKnotFaderSpec(15, 0, currentValue, 30, currentValue + 360); + + g_compass->startFader(compassMove); + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + while (g_compass->isFading()) { + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + g_compass->setFaderValue(currentValue); +} + +void Interface::calibrateEnergyBar() { + g_energyMonitor->calibrateEnergyBar(); +} + +void Interface::raiseInventoryDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + raiseInventoryDrawer(false); + + while (_inventoryLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryLidOpen(false); + + while (_inventoryPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryDrawerUp(); +} + +void Interface::lowerInventoryDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lowerInventoryDrawer(false); + + while (_inventoryPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryDrawerDown(false); + + while (_inventoryLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + inventoryLidClosed(); +} + +void Interface::raiseBiochipDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + raiseBiochipDrawer(false); + + while (_biochipLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipLidOpen(false); + + while (_biochipPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipDrawerUp(); +} + +void Interface::lowerBiochipDrawerSync() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lowerBiochipDrawer(false); + + while (_biochipPush.isFading()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipDrawerDown(false); + + while (_biochipLid.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + vm->refreshDisplay(); + biochipLidClosed(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/interface.h b/engines/pegasus/interface.h new file mode 100644 index 0000000000..a65d9a595a --- /dev/null +++ b/engines/pegasus/interface.h @@ -0,0 +1,148 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_INTERFACE_H +#define PEGASUS_INTERFACE_H + +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" +#include "pegasus/transition.h" +#include "pegasus/items/inventorypicture.h" + +namespace Pegasus { + +class BiochipItem; +class InventoryItem; + +class Interface : public InputHandler, public NotificationReceiver { +public: + Interface(); + virtual ~Interface(); + + void createInterface(); + + // Recalibration functions... + void calibrateCompass(); + void calibrateEnergyBar(); + void raiseInventoryDrawerSync(); + void lowerInventoryDrawerSync(); + void raiseBiochipDrawerSync(); + void lowerBiochipDrawerSync(); + + void raiseInventoryDrawer(const bool doCallBacks = true); + void raiseBiochipDrawer(const bool doCallBacks = true); + void lowerInventoryDrawer(const bool doCallBacks = true); + void lowerBiochipDrawer(const bool doCallBacks = true); + + void raiseInventoryDrawerForMessage(); + void lowerInventoryDrawerForMessage(); + bool isInventoryUp(); + bool isInventoryDown(); + + InventoryResult addInventoryItem(InventoryItem *); + InventoryResult removeInventoryItem(InventoryItem *); + void removeAllItemsFromInventory(); + InventoryItem *getCurrentInventoryItem(); + void setCurrentInventoryItem(InventoryItem *); + void setCurrentInventoryItemID(ItemID); + InventoryResult addBiochip(BiochipItem *); + void removeAllItemsFromBiochips(); + BiochipItem *getCurrentBiochip(); + void setCurrentBiochip(BiochipItem *); + void setCurrentBiochipID(ItemID); + + void setDate(const uint16); + + void playEndMessage(); + + void throwAwayInterface(); + +protected: + void validateBackground(); + void validateDateMonitor(); + void validateCompass(); + void validateNotifications(); + void validateAIArea(); + void validateInventoryPanel(); + void validateBiochipPanel(); + void validateEnergyMonitor(); + + void throwAwayBackground(); + void throwAwayDateMonitor(); + void throwAwayCompass(); + void throwAwayNotifications(); + void throwAwayAIArea(); + void throwAwayInventoryPanel(); + void throwAwayBiochipPanel(); + void throwAwayEnergyMonitor(); + + void receiveNotification(Notification *, const NotificationFlags); + void inventoryLidOpen(const bool doCallBacks); + void inventoryLidClosed(); + void inventoryDrawerUp(); + void inventoryDrawerDown(const bool doCallBacks); + void biochipLidOpen(const bool doCallBacks); + void biochipLidClosed(); + void biochipDrawerUp(); + void biochipDrawerDown(const bool doCallBacks); + + Picture _background1; + Picture _background2; + Picture _background3; + Picture _background4; + + Picture _datePicture; + + InputHandler *_previousHandler; + + Push _inventoryPush; + SpriteSequence _inventoryLid; + NotificationCallBack _inventoryPushCallBack; + NotificationCallBack _inventoryLidCallBack; + InventoryItemsPicture _inventoryPanel; + bool _inventoryUp, _inventoryRaised; + + Push _biochipPush; + SpriteSequence _biochipLid; + NotificationCallBack _biochipPushCallBack; + NotificationCallBack _biochipLidCallBack; + BiochipPicture _biochipPanel; + bool _biochipUp, _biochipRaised; + + Hotspot _currentItemSpot; + Hotspot _currentBiochipSpot; + + Notification _interfaceNotification; + + bool _playingEndMessage; +}; + +extern Interface *g_interface; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/autodragger.cpp b/engines/pegasus/items/autodragger.cpp new file mode 100644 index 0000000000..40bad14a89 --- /dev/null +++ b/engines/pegasus/items/autodragger.cpp @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/elements.h" +#include "pegasus/items/autodragger.h" + +namespace Pegasus { + +AutoDragger::AutoDragger() { + _draggingElement = NULL; + _lastTime = 0; + initCallBack(this, kCallBackAtExtremes); +} + +void AutoDragger::autoDrag(DisplayElement *dragElement, const Common::Point &startPoint, const Common::Point &stopPoint, + TimeValue dragTime, TimeScale dragScale) { + _draggingElement = dragElement; + + if (_draggingElement) { + _startLocation = startPoint; + _stopLocation = stopPoint; + _lastTime = 0; + _done = false; + _draggingElement->moveElementTo(_startLocation.x, _startLocation.y); + setScale(dragScale); + setSegment(0, dragTime); + setTime(0); + scheduleCallBack(kTriggerAtStop, 0, 0); + startIdling(); + start(); + } else { + stopDragging(); + } +} + +void AutoDragger::stopDragging() { + cancelCallBack(); + stopIdling(); + _draggingElement = 0; + _startLocation = Common::Point(); + _stopLocation = Common::Point(); + _lastTime = 0; + _done = true; +} + +bool AutoDragger::isDragging() { + return isIdling(); +} + +void AutoDragger::useIdleTime() { + TimeValue thisTime = getTime(); + + if (thisTime != _lastTime) { + int32 offsetX = (_stopLocation.x - _startLocation.x) * (int32)thisTime / (int32)getDuration(); + int32 offsetY = (_stopLocation.y - _startLocation.y) * (int32)thisTime / (int32)getDuration(); + _draggingElement->moveElementTo(_startLocation.x + offsetX, _startLocation.y + offsetY); + _lastTime = thisTime; + } + + if (_done) + stopDragging(); +} + +void AutoDragger::callBack() { + if (isIdling()) + _done = true; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/autodragger.h b/engines/pegasus/items/autodragger.h new file mode 100644 index 0000000000..6783fdf9a3 --- /dev/null +++ b/engines/pegasus/items/autodragger.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_AUTODRAGGER_H +#define PEGASUS_ITEMS_AUTODRAGGER_H + +#include "pegasus/timers.h" + +namespace Pegasus { + +class DisplayElement; + +class AutoDragger : private Idler, private TimeBase, private TimeBaseCallBack { +public: + AutoDragger(); + virtual ~AutoDragger() {} + + void autoDrag(DisplayElement *, const Common::Point &, const Common::Point &, TimeValue, TimeScale); + bool isDragging(); + void stopDragging(); + +protected: + void useIdleTime(); + void callBack(); + + DisplayElement *_draggingElement; + Common::Point _startLocation, _stopLocation; + TimeValue _lastTime; + bool _done; +}; + +} // End of namespace Pegasus + +#endif + diff --git a/engines/pegasus/items/biochips/aichip.cpp b/engines/pegasus/items/biochips/aichip.cpp new file mode 100644 index 0000000000..cbcfc363e8 --- /dev/null +++ b/engines/pegasus/items/biochips/aichip.cpp @@ -0,0 +1,279 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// indexed by [number of hints][number of solves (0, 1, or 2)][which button to highlight] +static const ItemState s_highlightState[4][3][7] = { + { + {kAI000, -1, -1, -1, -1, kAI005, kAI006}, + {kAI010, -1, -1, -1, -1, kAI015, kAI016}, + {kAI020, -1, -1, -1, kAI024, -1, -1} + }, + { + {kAI100, kAI101, -1, -1, -1, kAI105, kAI106}, + {kAI110, kAI111, -1, -1, -1, kAI115, kAI116}, + {kAI120, kAI121, -1, -1, kAI124, kAI125, kAI126} + }, + { + {kAI200, kAI201, kAI202, -1, -1, kAI205, kAI206}, + {kAI210, kAI211, kAI212, -1, -1, kAI215, kAI216}, + {kAI220, kAI221, kAI222, -1, kAI224, kAI225, kAI226} + }, + { + {kAI300, kAI301, kAI302, kAI303, -1, kAI305, kAI306}, + {kAI310, kAI311, kAI312, kAI313, -1, kAI315, kAI316}, + {kAI320, kAI321, kAI322, kAI323, kAI324, kAI325, kAI326} + } +}; + +AIChip *g_AIChip = 0; + +AIChip::AIChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _briefingSpot(kAIBriefingSpotID), _scanSpot(kAIScanSpotID), + _hint1Spot(kAIHint1SpotID), _hint2Spot(kAIHint2SpotID), _hint3Spot(kAIHint3SpotID), _solveSpot(kAISolveSpotID) { + _briefingSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 10 + 81, kAIMiddleAreaTop + 27 + 31)); + _briefingSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_briefingSpot); + + _scanSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 100, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 100 + 81, kAIMiddleAreaTop + 27 + 31)); + _scanSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_scanSpot); + + _hint1Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 70, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 70 + 21, kAIMiddleAreaTop + 67 + 21)); + _hint1Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint1Spot); + + _hint2Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 91, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 91 + 20, kAIMiddleAreaTop + 67 + 21)); + _hint2Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint2Spot); + + _hint3Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 111, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 111 + 20, kAIMiddleAreaTop + 67 + 21)); + _hint3Spot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_hint3Spot); + + _solveSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 131, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 131 + 50, kAIMiddleAreaTop + 67 + 21)); + _solveSpot.setHotspotFlags(kAIBiochipSpotFlag); + g_allHotspots.push_back(&_solveSpot); + + _playingMovie = false; + setItemState(kAI000); + + g_AIChip = this; +} + +AIChip::~AIChip() { + g_AIChip = NULL; + + g_allHotspots.removeOneHotspot(kAIBriefingSpotID); + g_allHotspots.removeOneHotspot(kAIScanSpotID); + g_allHotspots.removeOneHotspot(kAIHint1SpotID); + g_allHotspots.removeOneHotspot(kAIHint2SpotID); + g_allHotspots.removeOneHotspot(kAIHint3SpotID); + g_allHotspots.removeOneHotspot(kAISolveSpotID); +} + +void AIChip::select() { + BiochipItem::select(); + setUpAIChip(); +} + +void AIChip::takeSharedArea() { + setUpAIChip(); +} + +void AIChip::setUpAIChip() { + if (!_playingMovie) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + setItemState(s_highlightState[vm->getNumHints()][numSolves][0]); + } +} + +// Only does something when there are hints or solves available. +void AIChip::setUpAIChipRude() { + if (!_playingMovie) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + uint numHints = vm->getNumHints(); + if (numSolves == 2 || numHints != 0) + setItemState(s_highlightState[numHints][numSolves][0]); + } +} + +void AIChip::activateAIHotspots() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + _briefingSpot.setActive(); + _scanSpot.setActive(); + + switch (vm->getNumHints()) { + case 3: + _hint3Spot.setActive(); + // fall through + case 2: + _hint2Spot.setActive(); + // fall through + case 1: + _hint1Spot.setActive(); + break; + } + + if (GameState.getWalkthroughMode() && vm->canSolve()) + _solveSpot.setActive(); +} + +void AIChip::showBriefingClicked() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + ItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIBriefingSpotID - kAIHint1SpotID + 1]; + if (newState != -1) + setItemState(newState); +} + +void AIChip::showEnvScanClicked() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + ItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIScanSpotID - kAIHint1SpotID + 1]; + + if (newState != -1) + setItemState(newState); +} + +void AIChip::clearClicked() { + _playingMovie = false; + setUpAIChip(); +} + +void AIChip::clickInAIHotspot(HotSpotID id) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::String movieName; + + switch (id) { + case kAIBriefingSpotID: + movieName = vm->getBriefingMovie(); + break; + case kAIScanSpotID: + movieName = vm->getEnvScanMovie(); + break; + case kAIHint1SpotID: + movieName = vm->getHintMovie(1); + break; + case kAIHint2SpotID: + movieName = vm->getHintMovie(2); + break; + case kAIHint3SpotID: + movieName = vm->getHintMovie(3); + break; + case kAISolveSpotID: + g_neighborhood->doSolve(); + break; + } + + ItemState state = getItemState(); + + if (!movieName.empty()) { + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + ItemState newState = s_highlightState[vm->getNumHints()][numSolves][id - kAIHint1SpotID + 1]; + + if (newState != -1) + setItemState(newState); + + if (g_AIArea) { + vm->prepareForAIHint(movieName); + g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kHintInterruption); + vm->cleanUpAfterAIHint(movieName); + } + + if (newState != -1) + setItemState(state); + + _playingMovie = false; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/aichip.h b/engines/pegasus/items/biochips/aichip.h new file mode 100644 index 0000000000..7a33953612 --- /dev/null +++ b/engines/pegasus/items/biochips/aichip.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_AICHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_AICHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class AIChip : public BiochipItem { +public: + AIChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~AIChip(); + + void select(); + + void setUpAIChip(); + + // Called to set up the AI chip when the AI chip is the current chip but does not + // own the center area. + void setUpAIChipRude(); + void activateAIHotspots(); + void clickInAIHotspot(HotSpotID); + + void takeSharedArea(); + + void showBriefingClicked(); + void showEnvScanClicked(); + void clearClicked(); + +protected: + Hotspot _briefingSpot; + Hotspot _scanSpot; + Hotspot _hint1Spot; + Hotspot _hint2Spot; + Hotspot _hint3Spot; + Hotspot _solveSpot; + bool _playingMovie; +}; + +extern AIChip *g_AIChip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/biochipitem.cpp b/engines/pegasus/items/biochips/biochipitem.cpp new file mode 100644 index 0000000000..5686948937 --- /dev/null +++ b/engines/pegasus/items/biochips/biochipitem.cpp @@ -0,0 +1,95 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include "common/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +BiochipItem::BiochipItem(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + Item(id, neighborhood, room, direction) { + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *biochipInfo = vm->_resFork->getResource(MKTAG('B', 'i', 'o', 'I'), kItemBaseResID + id); + if (biochipInfo) { + _biochipInfoPanelTime = biochipInfo->readUint32BE(); + delete biochipInfo; + } else { + _biochipInfoPanelTime = 0; + } + + Common::SeekableReadStream *rightInfo = vm->_resFork->getResource(MKTAG('R', 'g', 'h', 't'), kItemBaseResID + id); + if (!rightInfo) + error("Could not find right info for biochip %d", id); + + _rightAreaInfo = readItemState(rightInfo); + delete rightInfo; + + setItemState(kNormalItem); +} + +BiochipItem::~BiochipItem() { + delete[] _rightAreaInfo.entries; +} + +ItemType BiochipItem::getItemType() { + return kBiochipItemType; +} + +TimeValue BiochipItem::getRightAreaTime() const { + if (!_rightAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + ItemState state; + + findItemStateEntryByState(_rightAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_rightAreaInfo, 0, state, time); + + return time; +} + +// Must affect images in right area. +void BiochipItem::select() { + Item::select(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, getRightAreaTime()); +} + +void BiochipItem::deselect() { + Item::deselect(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, 0xffffffff); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/biochipitem.h b/engines/pegasus/items/biochips/biochipitem.h new file mode 100644 index 0000000000..2039e80c6f --- /dev/null +++ b/engines/pegasus/items/biochips/biochipitem.h @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H +#define PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H + +#include "pegasus/items/item.h" + +namespace Pegasus { + +class BiochipItem : public Item { +public: + BiochipItem(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~BiochipItem(); + + virtual ItemType getItemType(); + + TimeValue getPanelTime() const { return _biochipInfoPanelTime; } + TimeValue getRightAreaTime() const; + + // Must affect images in right area. + virtual void select(); + virtual void deselect(); + +protected: + TimeValue _biochipInfoPanelTime; + ItemStateInfo _rightAreaInfo; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/mapchip.cpp b/engines/pegasus/items/biochips/mapchip.cpp new file mode 100644 index 0000000000..69050d5193 --- /dev/null +++ b/engines/pegasus/items/biochips/mapchip.cpp @@ -0,0 +1,106 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +MapChip *g_map = 0; + +MapChip::MapChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction) { + g_map = this; + setItemState(kMapUnavailable); +} + +MapChip::~MapChip() { + g_map = 0; +} + +void MapChip::writeToStream(Common::WriteStream *stream) { + return _image.writeToStream(stream); +} + +void MapChip::readFromStream(Common::ReadStream *stream) { + return _image.readFromStream(stream); +} + +void MapChip::select() { + BiochipItem::select(); + moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); + _image.show(); +} + +void MapChip::takeSharedArea() { + _image.show(); +} + +void MapChip::giveUpSharedArea() { + _image.hide(); +} + +void MapChip::deselect() { + BiochipItem::deselect(); + _image.unloadImage(); +} + +void MapChip::moveToMapLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant dir) { + AirQuality airQuality; + + if (g_neighborhood) + airQuality = g_neighborhood->getAirQuality(room); + else + airQuality = kAirQualityGood; + + switch (neighborhood) { + case kMarsID: + if (airQuality == kAirQualityVacuum) { + if (room >= kMars35 && room <= kMars39) { + setItemState(kMapEngaged); + if (isSelected() && g_AIArea && g_AIArea->getMiddleAreaOwner() == kBiochipSignature) + _image.loadGearRoomIfNecessary(); + } else { + setItemState(kMapEngaged); + if (isSelected() && g_AIArea && g_AIArea->getMiddleAreaOwner() == kBiochipSignature) + _image.loadMazeIfNecessary(); + } + + _image.moveToMapLocation(neighborhood, room, dir); + } else { + _image.unloadImage(); + setItemState(kMapUnavailable); + } + break; + default: + _image.unloadImage(); + setItemState(kMapUnavailable); + break; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/mapchip.h b/engines/pegasus/items/biochips/mapchip.h new file mode 100644 index 0000000000..6690090aa4 --- /dev/null +++ b/engines/pegasus/items/biochips/mapchip.h @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_MAPCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_MAPCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/mapimage.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class MapChip : public BiochipItem { +public: + MapChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~MapChip(); + + void select(); + void deselect(); + void takeSharedArea(); + void giveUpSharedArea(); + + void moveToMapLocation(const NeighborhoodID, const RoomID, const DirectionConstant); + + void writeToStream(Common::WriteStream *); + void readFromStream(Common::ReadStream *); + + bool beenToMaze() { return _image.anyFlagSet(); } + +protected: + MapImage _image; +}; + +extern MapChip *g_map; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/mapimage.cpp b/engines/pegasus/items/biochips/mapimage.cpp new file mode 100644 index 0000000000..9f4170d063 --- /dev/null +++ b/engines/pegasus/items/biochips/mapimage.cpp @@ -0,0 +1,443 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/biochips/mapimage.h" + +namespace Pegasus { + +#define FLAG_TO_INDEX(flag) ((flag) >> 2) +#define INDEX_TO_FLAG(index) ((index) << 2) + +#define ROOM_TO_INDEX(room) \ + (((room) >= kMars35 && (room) <= kMars39) ? ((room) - kMars35) : \ + (((room) == kMars60) ? (kMars39 - kMars35 + 1) : \ + ((room) - kMarsMaze004 + kMars39 - kMars35 + 2))) + +#define INDEX_TO_ROOM(index) \ + (((index) <= ROOM_TO_INDEX(kMars39)) ? \ + (((index) - ROOM_TO_INDEX(kMars35)) + kMars35) : \ + ((index) <= ROOM_TO_INDEX(kMars60,)) ? kMars60 : \ + ((((index) - ROOM_TO_INDEX(kMarsMaze004))) + kMarsMaze004)) + +#define ROOM_TO_FLAG(room, dir) (INDEX_TO_FLAG(ROOM_TO_INDEX(room)) | (dir)) + +#define FLAG_TO_ROOM(flag) (INDEX_TO_ROOM(FLAG_TO_INDEX(flag))) + +#define FLAG_TO_DIRECTION(flag) ((flag) & 3) + +static const int kGearRoomFlagLow = ROOM_TO_FLAG(kMars35, kNorth); +static const int kGearRoomFlagHigh = ROOM_TO_FLAG(kMars39, kWest); + +static const int kMazeFlagLow = ROOM_TO_FLAG(kMars60, kNorth); +static const int kMazeFlagHigh = ROOM_TO_FLAG(kMarsMaze200, kWest); + +static const CoordType kGearRoomScreenOffsetX = 49; +static const CoordType kGearRoomScreenOffsetY = 47; + +static const CoordType kGearRoomGridOriginX = 1; +static const CoordType kGearRoomGridOriginY = 4; + +static const CoordType kMazeScreenOffsetX = 16; +static const CoordType kMazeScreenOffsetY = 20; + +static const CoordType kMazeGridOriginX = 6; +static const CoordType kMazeGridOriginY = 1; + +static const CoordType kGridWidth = 4; +static const CoordType kGridHeight = 4; + +static const uint16 kMapOfMazePICTID = 906; +static const uint16 kMapOfGearRoomPICTID = 907; + +static const int s_mapCoords[MapImage::kNumMappingRooms][2] = { + /* kMars35 */ { 0, 0 }, + /* kMars36 */ { 1, 0 }, + /* kMars37 */ { 2, 0 }, + /* kMars38 */ { 3, 0 }, + /* kMars39 */ { 4, 0 }, + /* kMars60 */ { 19, 9 }, + /* kMarsMaze004 */ { 18, 9 }, + /* kMarsMaze005 */ { 18, 10 }, + /* kMarsMaze006 */ { 17, 10 }, + /* kMarsMaze007 */ { 16, 10 }, + /* kMarsMaze008 */ { 15, 10 }, + /* kMarsMaze009 */ { 14, 10 }, + /* kMarsMaze010 */ { 14, 9 }, + /* kMarsMaze011 */ { 14, 8 }, + /* kMarsMaze012 */ { 14, 7 }, + /* kMarsMaze015 */ { 16, 7 }, + /* kMarsMaze016 */ { 14, 11 }, + /* kMarsMaze017 */ { 14, 12 }, + /* kMarsMaze018 */ { 15, 12 }, + /* kMarsMaze019 */ { 16, 12 }, + /* kMarsMaze020 */ { 16, 13 }, + /* kMarsMaze021 */ { 16, 14 }, + /* kMarsMaze022 */ { 16, 15 }, + /* kMarsMaze023 */ { 17, 15 }, + /* kMarsMaze024 */ { 18, 15 }, + /* kMarsMaze025 */ { 18, 14 }, + /* kMarsMaze026 */ { 18, 13 }, + /* kMarsMaze027 */ { 18, 12 }, + /* kMarsMaze028 */ { 18, 11 }, + /* kMarsMaze031 */ { 19, 14 }, + /* kMarsMaze032 */ { 20, 14 }, + /* kMarsMaze033 */ { 20, 13 }, + /* kMarsMaze034 */ { 20, 12 }, + /* kMarsMaze035 */ { 20, 11 }, + /* kMarsMaze036 */ { 21, 11 }, + /* kMarsMaze037 */ { 15, 15 }, + /* kMarsMaze038 */ { 14, 15 }, + /* kMarsMaze039 */ { 13, 15 }, + /* kMarsMaze042 */ { 10, 15 }, + /* kMarsMaze043 */ { 9, 15 }, + /* kMarsMaze044 */ { 8, 15 }, + /* kMarsMaze045 */ { 7, 15 }, + /* kMarsMaze046 */ { 6, 15 }, + /* kMarsMaze047 */ { 5, 15 }, + /* kMarsMaze049 */ { 13, 14 }, + /* kMarsMaze050 */ { 12, 14 }, + /* kMarsMaze051 */ { 11, 14 }, + /* kMarsMaze052 */ { 10, 14 }, + /* kMarsMaze053 */ { 10, 13 }, + /* kMarsMaze054 */ { 9, 13 }, + /* kMarsMaze055 */ { 8, 13 }, + /* kMarsMaze056 */ { 8, 12 }, + /* kMarsMaze057 */ { 7, 12 }, + /* kMarsMaze058 */ { 12, 13 }, + /* kMarsMaze059 */ { 12, 12 }, + /* kMarsMaze060 */ { 12, 11 }, + /* kMarsMaze061 */ { 12, 10 }, + /* kMarsMaze063 */ { 12, 9 }, + /* kMarsMaze064 */ { 12, 8 }, + /* kMarsMaze065 */ { 12, 7 }, + /* kMarsMaze066 */ { 13, 7 }, + /* kMarsMaze067 */ { 15, 7 }, + /* kMarsMaze068 */ { 17, 7 }, + /* kMarsMaze069 */ { 18, 7 }, + /* kMarsMaze070 */ { 19, 7 }, + /* kMarsMaze071 */ { 20, 7 }, + /* kMarsMaze072 */ { 20, 6 }, + /* kMarsMaze074 */ { 20, 5 }, + /* kMarsMaze076 */ { 20, 4 }, + /* kMarsMaze078 */ { 20, 3 }, + /* kMarsMaze079 */ { 20, 2 }, + /* kMarsMaze081 */ { 20, 2 }, + /* kMarsMaze083 */ { 20, 0 }, + /* kMarsMaze084 */ { 19, 0 }, + /* kMarsMaze085 */ { 18, 0 }, + /* kMarsMaze086 */ { 17, 0 }, + /* kMarsMaze087 */ { 16, 0 }, + /* kMarsMaze088 */ { 15, 0 }, + /* kMarsMaze089 */ { 14, 0 }, + /* kMarsMaze090 */ { 13, 0 }, + /* kMarsMaze091 */ { 12, 0 }, + /* kMarsMaze092 */ { 11, 0 }, + /* kMarsMaze093 */ { 10, 0 }, + /* kMarsMaze098 */ { 10, 1 }, + /* kMarsMaze099 */ { 8, 2 }, + /* kMarsMaze100 */ { 9, 2 }, + /* kMarsMaze101 */ { 10, 2 }, + /* kMarsMaze104 */ { 13, 2 }, + /* kMarsMaze105 */ { 13, 3 }, + /* kMarsMaze106 */ { 13, 4 }, + /* kMarsMaze107 */ { 13, 5 }, + /* kMarsMaze108 */ { 14, 5 }, + /* kMarsMaze111 */ { 15, 5 }, + /* kMarsMaze113 */ { 16, 5 }, + /* kMarsMaze114 */ { 17, 5 }, + /* kMarsMaze115 */ { 18, 5 }, + /* kMarsMaze116 */ { 18, 4 }, + /* kMarsMaze117 */ { 18, 3 }, + /* kMarsMaze118 */ { 19, 3 }, + /* kMarsMaze119 */ { 18, 2 }, + /* kMarsMaze120 */ { 17, 2 }, + /* kMarsMaze121 */ { 16, 2 }, + /* kMarsMaze122 */ { 15, 2 }, + /* kMarsMaze123 */ { 15, 1 }, + /* kMarsMaze124 */ { 12, 4 }, + /* kMarsMaze125 */ { 11, 4 }, + /* kMarsMaze126 */ { 10, 4 }, + /* kMarsMaze127 */ { 10, 5 }, + /* kMarsMaze128 */ { 10, 6 }, + /* kMarsMaze129 */ { 9, 6 }, + /* kMarsMaze130 */ { 8, 6 }, + /* kMarsMaze131 */ { 7, 6 }, + /* kMarsMaze132 */ { 7, 7 }, + /* kMarsMaze133 */ { 7, 8 }, + /* kMarsMaze136 */ { 7, 11 }, + /* kMarsMaze137 */ { 6, 11 }, + /* kMarsMaze138 */ { 5, 11 }, + /* kMarsMaze139 */ { 5, 12 }, + /* kMarsMaze140 */ { 4, 12 }, + /* kMarsMaze141 */ { 5, 13 }, + /* kMarsMaze142 */ { 5, 14 }, + /* kMarsMaze143 */ { 4, 14 }, + /* kMarsMaze144 */ { 3, 14 }, + /* kMarsMaze145 */ { 3, 13 }, + /* kMarsMaze146 */ { 2, 13 }, + /* kMarsMaze147 */ { 1, 13 }, + /* kMarsMaze148 */ { 1, 14 }, + /* kMarsMaze149 */ { 1, 15 }, + /* kMarsMaze152 */ { 1, 12 }, + /* kMarsMaze153 */ { 1, 11 }, + /* kMarsMaze154 */ { 1, 10 }, + /* kMarsMaze155 */ { 1, 9 }, + /* kMarsMaze156 */ { 1, 8 }, + /* kMarsMaze157 */ { 2, 10 }, + /* kMarsMaze159 */ { 2, 8 }, + /* kMarsMaze160 */ { 2, 7 }, + /* kMarsMaze161 */ { 2, 6 }, + /* kMarsMaze162 */ { 3, 10 }, + /* kMarsMaze163 */ { 3, 9 }, + /* kMarsMaze164 */ { 3, 8 }, + /* kMarsMaze165 */ { 4, 8 }, + /* kMarsMaze166 */ { 5, 8 }, + /* kMarsMaze167 */ { 6, 8 }, + /* kMarsMaze168 */ { 3, 6 }, + /* kMarsMaze169 */ { 4, 6 }, + /* kMarsMaze170 */ { 5, 6 }, + /* kMarsMaze171 */ { 5, 5 }, + /* kMarsMaze172 */ { 5, 4 }, + /* kMarsMaze173 */ { 4, 4 }, + /* kMarsMaze174 */ { 3, 4 }, + /* kMarsMaze175 */ { 3, 5 }, + /* kMarsMaze177 */ { 8, 4 }, + /* kMarsMaze178 */ { 8, 3 }, + /* kMarsMaze179 */ { 7, 4 }, + /* kMarsMaze180 */ { 6, 4 }, + /* kMarsMaze181 */ { 6, 3 }, + /* kMarsMaze182 */ { 6, 2 }, + /* kMarsMaze183 */ { 6, 1 }, + /* kMarsMaze184 */ { 6, 0 }, + /* kMarsMaze187 */ { 3, 0 }, + /* kMarsMaze188 */ { 2, 0 }, + /* kMarsMaze189 */ { 1, 0 }, + /* kMarsMaze190 */ { 1, 1 }, + /* kMarsMaze191 */ { 1, 2 }, + /* kMarsMaze192 */ { 5, 2 }, + /* kMarsMaze193 */ { 4, 2 }, + /* kMarsMaze194 */ { 3, 2 }, + /* kMarsMaze195 */ { 3, 1 }, + /* kMarsMaze198 */ { 1, 3 }, + /* kMarsMaze199 */ { 1, 4 }, + /* kMarsMaze200 */ { 0, 4 } +}; + +MapImage::MapImage() : DisplayElement(kNoDisplayElement) { + _whichArea = kMapNoArea; + setBounds(kAIMiddleAreaLeft, kAIMiddleAreaTop, kAIMiddleAreaLeft + kAIMiddleAreaWidth, kAIMiddleAreaTop + kAIMiddleAreaHeight); + setDisplayOrder(kAIMiddleAreaOrder + 10); + startDisplaying(); + + _darkGreen = g_system->getScreenFormat().RGBToColor(64, 150, 10); + _lightGreen = g_system->getScreenFormat().RGBToColor(102, 239, 0); +} + +void MapImage::writeToStream(Common::WriteStream *stream) { + _mappedRooms.writeToStream(stream); +} + +void MapImage::readFromStream(Common::ReadStream *stream) { + _mappedRooms.readFromStream(stream); +} + +void MapImage::loadGearRoomIfNecessary() { + if (_whichArea != kMapGearRoom) { + _mapImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMapOfGearRoomPICTID); + + Common::Rect bounds; + _mapImage.getSurfaceBounds(bounds); + _mapMask.allocateSurface(bounds); + _whichArea = kMapGearRoom; + + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + gfx->setCurSurface(_mapMask.getSurface()); + + gfx->getCurSurface()->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + + for (int i = kGearRoomFlagLow; i <= kGearRoomFlagHigh; i++) + if (_mappedRooms.getFlag(i)) + addFlagToMask(i); + + gfx->setCurSurface(gfx->getWorkArea()); + show(); + } +} + +void MapImage::loadMazeIfNecessary() { + if (_whichArea != kMapMaze) { + _mapImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMapOfMazePICTID); + + Common::Rect bounds; + _mapImage.getSurfaceBounds(bounds); + _mapMask.allocateSurface(bounds); + _whichArea = kMapMaze; + + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + gfx->setCurSurface(_mapMask.getSurface()); + + gfx->getCurSurface()->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + + for (int i = kMazeFlagLow; i <= kMazeFlagHigh; i++) + if (_mappedRooms.getFlag(i)) + addFlagToMask(i); + + gfx->setCurSurface(gfx->getWorkArea()); + show(); + } +} + +void MapImage::unloadImage() { + _mapImage.deallocateSurface(); + _mapMask.deallocateSurface(); + hide(); + _whichArea = kMapNoArea; +} + +void MapImage::moveToMapLocation(const NeighborhoodID, const RoomID room, const DirectionConstant dir) { + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + + int flag = ROOM_TO_FLAG(room, dir); + + if (!_mappedRooms.getFlag(flag)) { + _mappedRooms.setFlag(flag, true); + + if (_mapMask.isSurfaceValid()) { + gfx->setCurSurface(_mapMask.getSurface()); + addFlagToMask(flag); + gfx->setCurSurface(gfx->getWorkArea()); + } + } + + if (isDisplaying()) + triggerRedraw(); +} + +void MapImage::addFlagToMask(const int flag) { + Common::Rect r1; + getRevealedRects(flag, r1); + ((PegasusEngine *)g_engine)->_gfx->getCurSurface()->fillRect(r1, g_system->getScreenFormat().RGBToColor(0, 0, 0)); +} + +// This function can even be sensitive to open doors. +// clone2727 notices that it's not, though +void MapImage::getRevealedRects(const uint32 flag, Common::Rect &r1) { + CoordType gridX, gridY; + + switch (_whichArea) { + case kMapMaze: + gridX = kMazeGridOriginX; + gridY = kMazeGridOriginY; + break; + case kMapGearRoom: + gridX = kGearRoomGridOriginX; + gridY = kGearRoomGridOriginY; + break; + default: + return; + } + + int index = FLAG_TO_INDEX(flag); + gridX += s_mapCoords[index][0] * kGridWidth; + gridY += s_mapCoords[index][1] * kGridHeight; + + r1 = Common::Rect(gridX - 1, gridY - 1, gridX + kGridWidth + 1, gridY + kGridHeight + 1); +} + +void MapImage::drawPlayer() { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + + CoordType gridX, gridY; + + switch (_whichArea) { + case kMapMaze: + gridX = _bounds.left + kMazeScreenOffsetX + kMazeGridOriginX; + gridY = _bounds.top + kMazeScreenOffsetY + kMazeGridOriginY; + break; + case kMapGearRoom: + gridX = _bounds.left + kGearRoomScreenOffsetX + kGearRoomGridOriginX; + gridY = _bounds.top + kGearRoomScreenOffsetY + kGearRoomGridOriginY; + break; + default: + return; + } + + int index = ROOM_TO_INDEX(GameState.getCurrentRoom()); + gridX += s_mapCoords[index][0] * kGridWidth; + gridY += s_mapCoords[index][1] * kGridHeight; + + // This was intended to make little arrows + switch (GameState.getCurrentDirection()) { + case kNorth: + screen->drawLine(gridX + 1, gridY, gridX + 2, gridY, _darkGreen); + screen->drawLine(gridX, gridY + 1, gridX + 3, gridY + 1, _darkGreen); + screen->drawLine(gridX + 1, gridY + 1, gridX + 2, gridY + 1, _lightGreen); + screen->drawLine(gridX, gridY + 2, gridX + 3, gridY + 2, _lightGreen); + break; + case kSouth: + screen->drawLine(gridX + 1, gridY + 3, gridX + 2, gridY + 3, _darkGreen); + screen->drawLine(gridX, gridY + 2, gridX + 3, gridY + 2, _darkGreen); + screen->drawLine(gridX + 1, gridY + 2, gridX + 2, gridY + 2, _lightGreen); + screen->drawLine(gridX, gridY + 1, gridX + 3, gridY + 1, _lightGreen); + break; + case kEast: + screen->drawLine(gridX + 3, gridY + 1, gridX + 3, gridY + 2, _darkGreen); + screen->drawLine(gridX + 2, gridY, gridX + 2, gridY + 3, _darkGreen); + screen->drawLine(gridX + 2, gridY + 1, gridX + 2, gridY + 2, _lightGreen); + screen->drawLine(gridX + 1, gridY, gridX + 1, gridY + 3, _lightGreen); + break; + case kWest: + screen->drawLine(gridX, gridY + 1, gridX, gridY + 2, _darkGreen); + screen->drawLine(gridX + 1, gridY, gridX + 1, gridY + 3, _darkGreen); + screen->drawLine(gridX + 1, gridY + 1, gridX + 1, gridY + 2, _lightGreen); + screen->drawLine(gridX + 2, gridY, gridX + 2, gridY + 3, _lightGreen); + break; + } +} + +void MapImage::draw(const Common::Rect &) { + Common::Rect r1; + _mapImage.getSurfaceBounds(r1); + + Common::Rect r2 = r1; + switch (_whichArea) { + case kMapMaze: + r2.moveTo(_bounds.left + kMazeScreenOffsetX, _bounds.top + kMazeScreenOffsetY); + break; + case kMapGearRoom: + r2.moveTo(_bounds.left + kGearRoomScreenOffsetX, _bounds.top + kGearRoomScreenOffsetY); + break; + default: + return; + } + + _mapImage.copyToCurrentPortMasked(r1, r2, &_mapMask); + + drawPlayer(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/mapimage.h b/engines/pegasus/items/biochips/mapimage.h new file mode 100644 index 0000000000..49ad9945ee --- /dev/null +++ b/engines/pegasus/items/biochips/mapimage.h @@ -0,0 +1,84 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_MAPIMAGE_H +#define PEGASUS_ITEMS_BIOCHIPS_MAPIMAGE_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" +#include "pegasus/neighborhood/mars/constants.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class MapImage : public DisplayElement { +public: + MapImage(); + virtual ~MapImage() {} + + void writeToStream(Common::WriteStream *); + void readFromStream(Common::ReadStream *); + + void loadGearRoomIfNecessary(); + void loadMazeIfNecessary(); + void unloadImage(); + void moveToMapLocation(const NeighborhoodID, const RoomID, const DirectionConstant); + + void draw(const Common::Rect &); + + bool anyFlagSet() { return _mappedRooms.anyFlagSet(); } + + static const uint32 kNumMappingRooms = (kMars39 - kMars35 + 1) + (kMars60 - kMars60 + 1) + + (kMarsMaze200 - kMarsMaze004 + 1); + static const uint32 kNumMappingFlags = kNumMappingRooms * 4; + +protected: + enum MapArea { + kMapNoArea, + kMapMaze, + kMapGearRoom + }; + + void addFlagToMask(const int flag); + void getRevealedRects(const uint32, Common::Rect &); + void drawPlayer(); + + MapArea _whichArea; + + FlagsArray<byte, kNumMappingFlags> _mappedRooms; + + uint32 _darkGreen, _lightGreen; + + Surface _mapImage, _mapMask; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/opticalchip.cpp b/engines/pegasus/items/biochips/opticalchip.cpp new file mode 100644 index 0000000000..7b8858edae --- /dev/null +++ b/engines/pegasus/items/biochips/opticalchip.cpp @@ -0,0 +1,190 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" + +namespace Pegasus { + +OpticalChip *g_opticalChip = 0; + +OpticalChip::OpticalChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _ariesHotspot(kAriesSpotID), _mercuryHotspot(kMercurySpotID), + _poseidonHotspot(kPoseidonSpotID) { + _ariesHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 27 + 20)); + _ariesHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_ariesHotspot); + + _mercuryHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 47, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 47 + 20)); + _mercuryHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_mercuryHotspot); + + _poseidonHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 67 + 20)); + _poseidonHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); + g_allHotspots.push_back(&_poseidonHotspot); + + setItemState(kOptical000); + + g_opticalChip = this; +} + +OpticalChip::~OpticalChip() { + g_allHotspots.removeOneHotspot(kAriesSpotID); + g_allHotspots.removeOneHotspot(kMercurySpotID); + g_allHotspots.removeOneHotspot(kPoseidonSpotID); +} + +void OpticalChip::writeToStream(Common::WriteStream *stream) { + BiochipItem::writeToStream(stream); + _opticalFlags.writeToStream(stream); +} + +void OpticalChip::readFromStream(Common::ReadStream *stream) { + BiochipItem::readFromStream(stream); + _opticalFlags.readFromStream(stream); +} + +void OpticalChip::addAries() { + _opticalFlags.setFlag(kOpticalAriesExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::addMercury() { + _opticalFlags.setFlag(kOpticalMercuryExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::addPoseidon() { + _opticalFlags.setFlag(kOpticalPoseidonExposed, true); + setUpOpticalChip(); +} + +void OpticalChip::setUpOpticalChip() { + if (_opticalFlags.getFlag(kOpticalAriesExposed)) { + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical111); + else + setItemState(kOptical011); + } else { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical101); + else + setItemState(kOptical001); + } + } else { + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical110); + else + setItemState(kOptical010); + } else { + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + setItemState(kOptical100); + else + setItemState(kOptical000); + } + } +} + +void OpticalChip::activateOpticalHotspots() { + if (_opticalFlags.getFlag(kOpticalAriesExposed)) + _ariesHotspot.setActive(); + if (_opticalFlags.getFlag(kOpticalMercuryExposed)) + _mercuryHotspot.setActive(); + if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) + _poseidonHotspot.setActive(); +} + +void OpticalChip::clickInOpticalHotspot(HotSpotID id) { + playOpMemMovie(id); +} + +void OpticalChip::playOpMemMovie(HotSpotID id) { + Common::String movieName; + switch (id) { + case kAriesSpotID: + movieName = "Images/AI/Globals/OMAI"; + break; + case kMercurySpotID: + movieName = "Images/AI/Globals/OMMI"; + break; + case kPoseidonSpotID: + movieName = "Images/AI/Globals/OMPI"; + break; + } + + ItemState state = getItemState(), newState; + switch (state) { + case kOptical001: + newState = kOptical002; + break; + case kOptical010: + newState = kOptical020; + break; + case kOptical011: + if (id == kAriesSpotID) + newState = kOptical012; + else + newState = kOptical021; + break; + case kOptical100: + newState = kOptical200; + break; + case kOptical101: + if (id == kAriesSpotID) + newState = kOptical102; + else + newState = kOptical201; + break; + case kOptical110: + if (id == kMercurySpotID) + newState = kOptical120; + else + newState = kOptical210; + break; + case kOptical111: + if (id == kAriesSpotID) + newState = kOptical112; + else if (id == kMercurySpotID) + newState = kOptical121; + else + newState = kOptical211; + break; + case kOptical000: // Can never happen. + default: + error("Invalid optical chip state"); + } + + setItemState(newState); + + if (g_AIArea) + g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kOpticalInterruption); + + setItemState(state); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/opticalchip.h b/engines/pegasus/items/biochips/opticalchip.h new file mode 100644 index 0000000000..2f66f73d3a --- /dev/null +++ b/engines/pegasus/items/biochips/opticalchip.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/util.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class OpticalChip : public BiochipItem { +public: + OpticalChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~OpticalChip(); + + virtual void writeToStream(Common::WriteStream *); + virtual void readFromStream(Common::ReadStream *); + + void addAries(); + void addMercury(); + void addPoseidon(); + + void activateOpticalHotspots(); + void clickInOpticalHotspot(HotSpotID); + void playOpMemMovie(HotSpotID); + +protected: + enum { + kOpticalAriesExposed, + kOpticalMercuryExposed, + kOpticalPoseidonExposed, + kNumOpticalChipFlags + }; + + void setUpOpticalChip(); + + FlagsArray<byte, kNumOpticalChipFlags> _opticalFlags; + Hotspot _ariesHotspot; + Hotspot _mercuryHotspot; + Hotspot _poseidonHotspot; +}; + +extern OpticalChip *g_opticalChip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/pegasuschip.cpp b/engines/pegasus/items/biochips/pegasuschip.cpp new file mode 100644 index 0000000000..fa551fce30 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.cpp @@ -0,0 +1,198 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +PegasusChip::PegasusChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction), _recallSpot(kPegasusRecallSpotID) { + _recallSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 116, kAIMiddleAreaTop + 63, kAIMiddleAreaLeft + 184, kAIMiddleAreaTop + 91)); + _recallSpot.setHotspotFlags(kPegasusBiochipSpotFlag); + g_allHotspots.push_back(&_recallSpot); + setItemState(kPegasusTSA00); +} + +PegasusChip::~PegasusChip() { + g_allHotspots.removeOneHotspot(kPegasusRecallSpotID); +} + +void PegasusChip::select() { + BiochipItem::select(); + setUpPegasusChip(); +} + +void PegasusChip::setUpPegasusChip() { + switch (GameState.getCurrentNeighborhood()) { + case kCaldoriaID: + setItemState(kPegasusCaldoria); + break; + case kFullTSAID: + case kFinalTSAID: + case kTinyTSAID: + setItemState(kPegasusTSA10); + break; + case kPrehistoricID: + if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) + setItemState(kPegasusPrehistoric00); + else + setItemState(kPegasusPrehistoric10); + break; + case kMarsID: + if (GameState.getMarsFinished()) + setItemState(kPegasusMars00); + else + setItemState(kPegasusMars10); + break; + case kWSCID: + if (GameState.getWSCFinished()) + setItemState(kPegasusWSC00); + else + setItemState(kPegasusWSC10); + break; + case kNoradAlphaID: + case kNoradDeltaID: + if (GameState.getNoradFinished()) + setItemState(kPegasusNorad00); + else + setItemState(kPegasusNorad10); + break; + } +} + +// Only does something if the chip should be announcing that the time zone is finished... +void PegasusChip::setUpPegasusChipRude() { + switch (GameState.getCurrentNeighborhood()) { + case kPrehistoricID: + if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) + setItemState(kPegasusPrehistoric00); + break; + case kMarsID: + if (GameState.getMarsFinished()) + setItemState(kPegasusMars00); + break; + case kWSCID: + if (GameState.getWSCFinished()) + setItemState(kPegasusWSC00); + break; + case kNoradAlphaID: + case kNoradDeltaID: + if (GameState.getNoradFinished()) + setItemState(kPegasusNorad00); + break; + } +} + +void PegasusChip::activatePegasusHotspots() { + switch (GameState.getCurrentNeighborhood()) { + case kPrehistoricID: + // WORKAROUND: Don't allow the player to recall if they don't have + // the historical log. Otherwise, gameplay is broken when returning + // to the TSA. + if (!((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) + return; + // fall through + case kMarsID: + case kWSCID: + case kNoradAlphaID: + case kNoradDeltaID: + _recallSpot.setActive(); + break; + } +} + +void PegasusChip::clickInPegasusHotspot() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + ItemState thisState = getItemState(); + ItemState hiliteState; + + switch (thisState) { + case kPegasusPrehistoric00: + hiliteState = kPegasusPrehistoric01; + break; + case kPegasusPrehistoric10: + hiliteState = kPegasusPrehistoric11; + break; + case kPegasusMars00: + hiliteState = kPegasusMars01; + break; + case kPegasusMars10: + hiliteState = kPegasusMars11; + break; + case kPegasusNorad00: + hiliteState = kPegasusNorad01; + break; + case kPegasusNorad10: + hiliteState = kPegasusNorad11; + break; + case kPegasusWSC00: + hiliteState = kPegasusWSC01; + break; + case kPegasusWSC10: + hiliteState = kPegasusWSC11; + break; + default: + error("Invalid pegasus chip state"); + } + + // WORKAROUND: The original called setItemState() here. However, + // since we're overriding select() to call setUpPegasusChip(), + // the highlighted frame is never displayed! So, we're manually + // setting the state and selecting the item. Also of note is that + // setItemState() for this class is effectively useless since it + // always gets overriden in the select() function. The only reason + // that this doesn't end in infinite recursion is because setItemState() + // has a check against the current state to make sure you don't call + // select() again. </rant> + _itemState = hiliteState; + BiochipItem::select(); + + uint32 time = g_system->getMillis(); + while (g_system->getMillis() < time + 500) { + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + setItemState(thisState); + + if (!((Neighborhood *)g_neighborhood)->okayToJump()) + return; + + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + if (GameState.getTSAState() == kPlayerWentToPrehistoric || GameState.allTimeZonesFinished()) + vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth); + else + vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/pegasuschip.h b/engines/pegasus/items/biochips/pegasuschip.h new file mode 100644 index 0000000000..7597424821 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class PegasusChip : public BiochipItem { +public: + PegasusChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~PegasusChip(); + + void select(); + + void setUpPegasusChip(); + + // Called to set up the Pegasus chip when the Pegasus chip is the current chip but does not + // own the center area. + void setUpPegasusChipRude(); + void activatePegasusHotspots(); + void clickInPegasusHotspot(); + +protected: + Hotspot _recallSpot; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/retscanchip.cpp b/engines/pegasus/items/biochips/retscanchip.cpp new file mode 100644 index 0000000000..84b74a63d2 --- /dev/null +++ b/engines/pegasus/items/biochips/retscanchip.cpp @@ -0,0 +1,49 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/retscanchip.h" + +namespace Pegasus { + +RetScanChip::RetScanChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction) { +} + +void RetScanChip::searchForLaser() { + ItemExtraEntry entry; + findItemExtra(kRetinalScanSearching, entry); + + if (g_AIArea) + g_AIArea->playAIAreaSequence(kBiochipSignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); + + findItemExtra(kRetinalScanActivated, entry); + if (g_AIArea) + g_AIArea->playAIAreaSequence(kBiochipSignature, kRightAreaSignature, entry.extraStart, entry.extraStop); + + setItemState(kRetinalSimulating); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/retscanchip.h b/engines/pegasus/items/biochips/retscanchip.h new file mode 100644 index 0000000000..153e6cd071 --- /dev/null +++ b/engines/pegasus/items/biochips/retscanchip.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class RetScanChip : public BiochipItem { +public: + RetScanChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~RetScanChip() {} + + void searchForLaser(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/shieldchip.cpp b/engines/pegasus/items/biochips/shieldchip.cpp new file mode 100644 index 0000000000..58cbfcc4ec --- /dev/null +++ b/engines/pegasus/items/biochips/shieldchip.cpp @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +ShieldChip *g_shield = 0; + +ShieldChip::ShieldChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + BiochipItem(id, neighborhood, room, direction) { + g_shield = this; +} + +void ShieldChip::select() { + BiochipItem::select(); + GameState.setShieldOn(true); + if (g_neighborhood) + g_neighborhood->shieldOn(); +} + +void ShieldChip::deselect() { + BiochipItem::deselect(); + GameState.setShieldOn(false); + if (g_neighborhood) + g_neighborhood->shieldOff(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/shieldchip.h b/engines/pegasus/items/biochips/shieldchip.h new file mode 100644 index 0000000000..69c6369236 --- /dev/null +++ b/engines/pegasus/items/biochips/shieldchip.h @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class ShieldChip : public BiochipItem { +public: + ShieldChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~ShieldChip() {} + + void select(); + void deselect(); +}; + +extern ShieldChip *g_shield; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory.cpp b/engines/pegasus/items/inventory.cpp new file mode 100644 index 0000000000..57923b105d --- /dev/null +++ b/engines/pegasus/items/inventory.cpp @@ -0,0 +1,175 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/constants.h" +#include "pegasus/items/item.h" +#include "pegasus/items/inventory.h" + +namespace Pegasus { + +Inventory::Inventory() { + _weightLimit = 100; + _ownerID = kNoActorID; + _referenceCount = 0; +} + +Inventory::~Inventory() { +} + +void Inventory::setWeightLimit(WeightType limit) { + _weightLimit = limit; + // *** What to do if the new weight limit is greater than the current weight? +} + +WeightType Inventory::getWeight() { + WeightType result = 0; + + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) + result += (*it)->getItemWeight(); + + return result; +} + +// If the item already belongs, just return kInventoryOK. +InventoryResult Inventory::addItem(Item *item) { + if (itemInInventory(item)) + return kInventoryOK; + + if (getWeight() + item->getItemWeight() > _weightLimit) + return kTooMuchWeight; + + _inventoryList.push_back(item); + item->setItemOwner(_ownerID); + + ++_referenceCount; + return kInventoryOK; +} + +InventoryResult Inventory::removeItem(Item *item) { + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) { + if (*it == item) { + _inventoryList.erase(it); + item->setItemOwner(kNoActorID); + + ++_referenceCount; + return kInventoryOK; + } + } + + return kItemNotInInventory; +} + +InventoryResult Inventory::removeItem(ItemID id) { + Item *item = findItemByID(id); + + if (item) { + _inventoryList.remove(item); + item->setItemOwner(kNoActorID); + + ++_referenceCount; + return kInventoryOK; + } + + return kItemNotInInventory; +} + +void Inventory::removeAllItems() { + _inventoryList.clear(); + ++_referenceCount; +} + +bool Inventory::itemInInventory(Item *item) { + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) + if (*it == item) + return true; + + return false; +} + +bool Inventory::itemInInventory(ItemID id) { + return findItemByID(id) != NULL; +} + +Item *Inventory::getItemAt(int32 index) { + int32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if (i == index) + return *it; + + return 0; +} + +ItemID Inventory::getItemIDAt(int32 index) { + Item *item = getItemAt(index); + + if (item) + return item->getObjectID(); + + return kNoItemID; +} + +Item *Inventory::findItemByID(ItemID id) { + return _inventoryList.findItemByID(id); +} + +// Return -1 if not found. + +int32 Inventory::findIndexOf(Item *item) { + uint32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if (*it == item) + return i; + + return -1; +} + +// Return -1 if not found. + +int32 Inventory::findIndexOf(ItemID id) { + uint32 i = 0; + for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) + if ((*it)->getObjectID() == id) + return i; + + return -1; +} + +WeightType Inventory::getWeightLimit() { + return _weightLimit; +} + +int32 Inventory::getNumItems() { + return _inventoryList.size(); +} + +void Inventory::setOwnerID(const ActorID id) { + _ownerID = id; +} + +ActorID Inventory::getOwnerID() const { + return _ownerID; +} + +} // End of namespae Pegasus diff --git a/engines/pegasus/items/inventory.h b/engines/pegasus/items/inventory.h new file mode 100644 index 0000000000..796ec49556 --- /dev/null +++ b/engines/pegasus/items/inventory.h @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_H +#define PEGASUS_ITEMS_INVENTORY_H + +#include "pegasus/types.h" +#include "pegasus/items/itemlist.h" + +namespace Pegasus { + +class Item; + +// Inventories have a "current item". This item is the default item the player can +// use. In a text adventure system, the current item would be "it", as in +// "Hit the troll with it," where "it" would refer to some weapon which is the current +// item. In a graphic adventure, the current item would be the item the user selects +// to use with the mouse or other pointing device. + +class Inventory { +public: + Inventory(); + virtual ~Inventory(); + + WeightType getWeightLimit(); + void setWeightLimit(WeightType limit); + WeightType getWeight(); + + virtual InventoryResult addItem(Item *item); + virtual InventoryResult removeItem(Item *item); + virtual InventoryResult removeItem(ItemID id); + virtual bool itemInInventory(Item *item); + virtual bool itemInInventory(ItemID id); + virtual Item *getItemAt(int32 index); + virtual ItemID getItemIDAt(int32 index); + virtual Item *findItemByID(ItemID id); + virtual int32 findIndexOf(Item *item); + virtual int32 findIndexOf(ItemID id); + int32 getNumItems(); + virtual void removeAllItems(); + + void setOwnerID(const ActorID id); + ActorID getOwnerID() const; + + uint32 getReferenceCount() { return _referenceCount; } + +protected: + WeightType _weightLimit; + ActorID _ownerID; + ItemList _inventoryList; + +private: + uint32 _referenceCount; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/airmask.cpp b/engines/pegasus/items/inventory/airmask.cpp new file mode 100644 index 0000000000..c65dd36102 --- /dev/null +++ b/engines/pegasus/items/inventory/airmask.cpp @@ -0,0 +1,249 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +AirMask *g_airMask = 0; + +// Based on full == 100, which is scale used by GetAirLeft(). +static const TimeValue kOxygenLowThreshold = 25; + +void AirMask::airMaskTimerExpired() { + if (g_neighborhood) + g_neighborhood->checkAirMask(); +} + +AirMask::AirMask(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction), _toggleSpot(kAirMaskToggleSpotID) { + g_airMask = this; + _toggleSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 17, kAIMiddleAreaLeft + 110, kAIMiddleAreaTop + 57)); + _toggleSpot.setHotspotFlags(kAirMaskSpotFlag); + g_allHotspots.push_back(&_toggleSpot); + setItemState(kAirMaskEmptyOff); + _oxygenTimer.primeFuse(0); + _oxygenTimer.setFunctor(new Common::Functor0Mem<void, AirMask>(this, &AirMask::airMaskTimerExpired)); +} + +AirMask::~AirMask() { + g_allHotspots.removeOneHotspot(kAirMaskToggleSpotID); + g_airMask = 0; +} + +void AirMask::writeToStream(Common::WriteStream *stream) { + InventoryItem::writeToStream(stream); + stream->writeUint32BE(_oxygenTimer.getTimeRemaining()); +} + +void AirMask::readFromStream(Common::ReadStream *stream) { + _oxygenTimer.stopFuse(); + InventoryItem::readFromStream(stream); + _oxygenTimer.primeFuse(stream->readUint32BE()); +} + +void AirMask::putMaskOn() { + AirQuality airQuality; + + if (g_neighborhood) + airQuality = g_neighborhood->getAirQuality(GameState.getCurrentRoom()); + else + airQuality = kAirQualityGood; + + uint airLevel = getAirLeft(); + ItemState newState = getItemState(); + ItemState oldState = newState; + + if (airLevel == 0) { + newState = kAirMaskEmptyFilter; + } else if (airLevel <= kOxygenLowThreshold) { + if (airQuality == kAirQualityVacuum) + newState = kAirMaskLowOn; + else + newState = kAirMaskLowFilter; + } else { + if (airQuality == kAirQualityVacuum) + newState = kAirMaskFullOn; + else + newState = kAirMaskFullFilter; + } + + if (newState != oldState) + setItemState(newState); +} + +void AirMask::takeMaskOff() { + uint airLevel = getAirLeft(); + ItemState newState = getItemState(); + ItemState oldState = newState; + + if (airLevel == 0) + newState = kAirMaskEmptyOff; + else if (airLevel <= kOxygenLowThreshold) + newState = kAirMaskLowOff; + else + newState = kAirMaskFullOff; + + if (newState != oldState) + setItemState(newState); +} + +void AirMask::toggleItemState() { + if (isAirMaskInUse()) + takeMaskOff(); + else + putMaskOn(); +} + +void AirMask::airQualityChanged() { + if (isAirMaskInUse()) + putMaskOn(); + else + takeMaskOff(); +} + +void AirMask::setItemState(const ItemState newState) { + if (newState != getItemState()) { + InventoryItem::setItemState(newState); + + switch (newState) { + case kAirMaskFullOn: + case kAirMaskLowOn: + if (!_oxygenTimer.isFuseLit()) { + _oxygenTimer.lightFuse(); + startIdling(); + } + break; + default: + if (_oxygenTimer.isFuseLit()) { + _oxygenTimer.stopFuse(); + stopIdling(); + } + break; + } + + if (g_neighborhood) + g_neighborhood->checkAirMask(); + + g_AIArea->checkMiddleArea(); + } +} + +void AirMask::useIdleTime() { + if (getAirLeft() == 0) + setItemState(kAirMaskEmptyOff); + else if (getAirLeft() <= kOxygenLowThreshold) + setItemState(kAirMaskLowOn); +} + +void AirMask::refillAirMask() { + switch (getItemState()) { + case kAirMaskEmptyOff: + case kAirMaskLowOff: + setItemState(kAirMaskFullOff); + break; + case kAirMaskEmptyFilter: + case kAirMaskLowFilter: + setItemState(kAirMaskFullFilter); + break; + case kAirMaskLowOn: + setItemState(kAirMaskFullOn); + break; + } + + if (_oxygenTimer.isFuseLit()) { + _oxygenTimer.stopFuse(); + _oxygenTimer.primeFuse(kOxyMaskFullTime); + _oxygenTimer.lightFuse(); + } else { + _oxygenTimer.primeFuse(kOxyMaskFullTime); + } +} + +// Doesn't return 0 until the timer is actually at 0. +uint AirMask::getAirLeft() { + return CLIP<int>(((_oxygenTimer.getTimeRemaining() * 100) + kOxyMaskFullTime - 1) / kOxyMaskFullTime, 0, 100); +} + +bool AirMask::isAirMaskInUse() { + switch (getItemState()) { + case kAirMaskEmptyOff: + case kAirMaskLowOff: + case kAirMaskFullOff: + return false; + break; + default: + return true; + break; + } +} + +bool AirMask::isAirMaskOn() { + switch (getItemState()) { + case kAirMaskLowOn: + case kAirMaskFullOn: + return true; + break; + default: + return false; + break; + } +} + +bool AirMask::isAirFilterOn() { + switch (getItemState()) { + case kAirMaskEmptyFilter: + case kAirMaskLowFilter: + case kAirMaskFullFilter: + return true; + break; + default: + return false; + break; + } +} + +void AirMask::addedToInventory() { + GameState.setMarsMaskOnFiller(false); +} + +void AirMask::removedFromInventory() { + if (isAirMaskInUse()) + toggleItemState(); +} + +void AirMask::activateAirMaskHotspots() { + _toggleSpot.setActive(); +} + +void AirMask::clickInAirMaskHotspot() { + toggleItemState(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/airmask.h b/engines/pegasus/items/inventory/airmask.h new file mode 100644 index 0000000000..6a2d708a6c --- /dev/null +++ b/engines/pegasus/items/inventory/airmask.h @@ -0,0 +1,76 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_AIRMASK_H +#define PEGASUS_ITEMS_INVENTORY_AIRMASK_H + +#include "pegasus/hotspot.h" +#include "pegasus/timers.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class AirMask : public InventoryItem, private Idler { +public: + AirMask(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~AirMask(); + + virtual void writeToStream(Common::WriteStream *); + virtual void readFromStream(Common::ReadStream *); + + virtual void setItemState(const ItemState); + void putMaskOn(); + void takeMaskOff(); + void toggleItemState(); + void airQualityChanged(); + + bool isAirMaskInUse(); + bool isAirMaskOn(); + bool isAirFilterOn(); + + void refillAirMask(); + + // Returns a percentage + uint getAirLeft(); + + void activateAirMaskHotspots(); + void clickInAirMaskHotspot(); + +protected: + void airMaskTimerExpired(); + + virtual void removedFromInventory(); + virtual void addedToInventory(); + void useIdleTime(); + + Hotspot _toggleSpot; + FuseFunction _oxygenTimer; +}; + +extern AirMask *g_airMask; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/gascanister.cpp b/engines/pegasus/items/inventory/gascanister.cpp new file mode 100644 index 0000000000..bf63cc6542 --- /dev/null +++ b/engines/pegasus/items/inventory/gascanister.cpp @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/gascanister.h" + +namespace Pegasus { + +GasCanister::GasCanister(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction) { +} + +void GasCanister::select() { + InventoryItem::select(); + takeSharedArea(); +} + +void GasCanister::takeSharedArea() { + ItemExtraEntry entry; + findItemExtra(kGasCanLoop, entry); + g_AIArea->loopAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/gascanister.h b/engines/pegasus/items/inventory/gascanister.h new file mode 100644 index 0000000000..7d4d8193f5 --- /dev/null +++ b/engines/pegasus/items/inventory/gascanister.h @@ -0,0 +1,44 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_GASCANISTER_H +#define PEGASUS_ITEMS_INVENTORY_GASCANISTER_H + +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class GasCanister : public InventoryItem { +public: + GasCanister(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~GasCanister() {} + + void select(); + void takeSharedArea(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/inventoryitem.cpp b/engines/pegasus/items/inventory/inventoryitem.cpp new file mode 100644 index 0000000000..4399708879 --- /dev/null +++ b/engines/pegasus/items/inventory/inventoryitem.cpp @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +InventoryItem::InventoryItem(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + Item(id, neighborhood, room, direction) { + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *leftInfo = vm->_resFork->getResource(MKTAG('L', 'e', 'f', 't'), kItemBaseResID + id); + if (leftInfo) { + _leftAreaInfo = readItemState(leftInfo); + delete leftInfo; + } else { + _leftAreaInfo.numEntries = 0; + _leftAreaInfo.entries = 0; + } + + Common::SeekableReadStream *inventoryInfo = vm->_resFork->getResource(MKTAG('I', 'n', 'v', 'I'), kItemBaseResID + id); + if (inventoryInfo) { + _inventoryInfo.panelStart = inventoryInfo->readUint32BE(); + _inventoryInfo.panelStop = inventoryInfo->readUint32BE(); + delete inventoryInfo; + } else { + _inventoryInfo.panelStart = _inventoryInfo.panelStop = 0; + } + + _itemAnimationTime = 0; +} + +InventoryItem::~InventoryItem() { + delete[] _leftAreaInfo.entries; +} + +ItemType InventoryItem::getItemType() { + return kInventoryItemType; +} + +TimeValue InventoryItem::getLeftAreaTime() const { + if (!_leftAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + ItemState state; + + findItemStateEntryByState(_leftAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_leftAreaInfo, 0, state, time); + + return time; +} + +void InventoryItem::setAnimationTime(const TimeValue time) { + _itemAnimationTime = time; +} + +TimeValue InventoryItem::getAnimationTime() const { + return _itemAnimationTime; +} + +// Must affect images in left area. +void InventoryItem::select() { + Item::select(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, getLeftAreaTime()); +} + +void InventoryItem::deselect() { + Item::deselect(); + + if (g_AIArea) + g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, 0xffffffff); +} + +void InventoryItem::getPanelTimes(TimeValue &start, TimeValue &stop) const { + start = _inventoryInfo.panelStart; + stop = _inventoryInfo.panelStop; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/inventoryitem.h b/engines/pegasus/items/inventory/inventoryitem.h new file mode 100644 index 0000000000..9d78113014 --- /dev/null +++ b/engines/pegasus/items/inventory/inventoryitem.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H +#define PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H + +#include "pegasus/items/item.h" + +namespace Pegasus { + +// JMPInventoryInfo contains the resource data used by InventoryItems. + +struct JMPInventoryInfo { + TimeValue panelStart; + TimeValue panelStop; +}; + +class InventoryItem : public Item { +public: + InventoryItem(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~InventoryItem(); + + virtual ItemType getItemType(); + + void getPanelTimes(TimeValue &, TimeValue &) const; + TimeValue getLeftAreaTime() const; + + void setAnimationTime(const TimeValue); + TimeValue getAnimationTime() const; + + virtual void toggleItemState() {} + + // Must affect images in left area. + virtual void select(); + virtual void deselect(); + +protected: + JMPInventoryInfo _inventoryInfo; + ItemStateInfo _leftAreaInfo; + TimeValue _itemAnimationTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/keycard.cpp b/engines/pegasus/items/inventory/keycard.cpp new file mode 100644 index 0000000000..c818b6675b --- /dev/null +++ b/engines/pegasus/items/inventory/keycard.cpp @@ -0,0 +1,59 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/items/inventory/keycard.h" + +namespace Pegasus { + +KeyCard::KeyCard(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : + InventoryItem(id, neighborhood, room, direction) { + setItemState(kFlashlightOff); +} + +void KeyCard::toggleItemState() { + if (getItemState() == kFlashlightOff) + setItemState(kFlashlightOn); + else + setItemState(kFlashlightOff); +} + +void KeyCard::setItemState(const ItemState newState) { + if (newState != getItemState()) { + InventoryItem::setItemState(newState); + ((PegasusEngine *)g_engine)->checkFlashlight(); + } +} + +bool KeyCard::isFlashlightOn() { + return getItemState() == kFlashlightOn; +} + +void KeyCard::removedFromInventory() { + if (isFlashlightOn()) + setItemState(kFlashlightOff); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/keycard.h b/engines/pegasus/items/inventory/keycard.h new file mode 100644 index 0000000000..846f40e6e5 --- /dev/null +++ b/engines/pegasus/items/inventory/keycard.h @@ -0,0 +1,48 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_KEYCARD_H +#define PEGASUS_ITEMS_INVENTORY_KEYCARD_H + +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class KeyCard : public InventoryItem { +public: + KeyCard(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + virtual ~KeyCard() {} + + virtual void toggleItemState(); + virtual void setItemState(const ItemState); + bool isFlashlightOn(); + +protected: + virtual void removedFromInventory(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventorypicture.cpp b/engines/pegasus/items/inventorypicture.cpp new file mode 100644 index 0000000000..fc812faae2 --- /dev/null +++ b/engines/pegasus/items/inventorypicture.cpp @@ -0,0 +1,370 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/transition.h" +#include "pegasus/items/inventorypicture.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +InventoryPicture::InventoryPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InputHandler(nextHandler), Picture(id), _panelMovie(kNoDisplayElement){ + _inventory = inventory; + _lastReferenceCount = 0xffffffff; + + if (_inventory->getNumItems() > 0) { + _currentItemIndex = 0; + _currentItem = (Item *)_inventory->getItemAt(0); + } else { + _currentItemIndex = -1; + _currentItem = 0; + } + + _active = false; + _shouldDrawHighlight = true; + _itemsPerRow = 1; + _numberOfRows = 1; + _itemWidth = 0; + _itemHeight = 0; + _itemX = 0; + _itemY = 0; +} + +void InventoryPicture::initInventoryImage(Transition *transition) { + initFromPICTFile(_pictName, true); + _panelMovie.shareSurface(this); + _panelMovie.initFromMovieFile(_movieName); + _panelMovie.getBounds(_highlightBounds); + _panelMovie.setTriggeredElement(transition); + _highlightImage.initFromPICTFile(_highlightName, true); +} + +void InventoryPicture::throwAwayInventoryImage() { + if (isSurfaceValid()) { + _panelMovie.releaseMovie(); + _highlightImage.deallocateSurface(); + deallocateSurface(); + } +} + +void InventoryPicture::getItemXY(uint32 index, CoordType &x, CoordType &y) { + x = (index % _itemsPerRow) * _itemWidth + _itemX; + y = (index / _itemsPerRow) * _itemHeight + _itemY; +} + +void InventoryPicture::drawItemHighlight(const Common::Rect &r) { + if (_highlightImage.isSurfaceValid()) { + Common::Rect r2 = _highlightBounds; + Common::Rect bounds; + getBounds(bounds); + + r2.translate(bounds.left, bounds.top); + r2 = r2.findIntersectingRect(r); + if (!r2.isEmpty()) { + Common::Rect r1 = r2; + r1.translate(-bounds.left - _highlightBounds.left, -bounds.top - _highlightBounds.top); + _highlightImage.drawImage(r1, r2); + } + } +} + +void InventoryPicture::draw(const Common::Rect &r) { + Picture::draw(r); + if (_inventory->getNumItems() != 0 && _shouldDrawHighlight) + drawItemHighlight(r); +} + +// Assumes index >= 0. +void InventoryPicture::setCurrentItemIndex(int32 index) { + if (index >= _inventory->getNumItems()) + index = _inventory->getNumItems() - 1; + + Item *currentItem = 0; + if (index >= 0) + currentItem = (Item *)_inventory->getItemAt(index); + + if (currentItem != _currentItem) { + if (_currentItem) { + if (_currentItem->isSelected()) + _currentItem->deselect(); + + if (_active) + unhighlightCurrentItem(); + } + + _currentItemIndex = index; + _currentItem = currentItem; + if (_currentItem) { + _currentItem->select(); + + if (_active) + highlightCurrentItem(); + } + + if (_active) + triggerRedraw(); + } +} + +void InventoryPicture::setCurrentItemID(ItemID id) { + int32 index = _inventory->findIndexOf(id); + if (index >= 0) + setCurrentItemIndex(index); +} + +InventoryResult InventoryPicture::addInventoryItem(Item *item) { + InventoryResult result = _inventory->addItem(item); + + if (result == kInventoryOK) + setCurrentItemIndex(_inventory->findIndexOf(item)); + + return result; +} + +InventoryResult InventoryPicture::removeInventoryItem(Item *item) { + InventoryResult result = _inventory->removeItem(item); + + if (result == kInventoryOK) + setCurrentItemIndex(getCurrentItemIndex()); + + return result; +} + +void InventoryPicture::removeAllItems() { + _inventory->removeAllItems(); + setCurrentItemIndex(0); +} + +bool InventoryPicture::itemInInventory(Item *item) { + return _inventory->itemInInventory(item); +} + +bool InventoryPicture::itemInInventory(const ItemID id) { + return _inventory->itemInInventory(id); +} + +void InventoryPicture::panelUp() { + allowInput(true); +} + +// Must ensure that the picture matches the _inventory member variable. +void InventoryPicture::activateInventoryPicture() { + if (_active) + return; + + allowInput(false); + + if (_lastReferenceCount != _inventory->getReferenceCount()) { + uint32 numItems = _inventory->getNumItems(); + + CoordType x, y; + getItemXY(0, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.show(); + + for (uint32 i = 0; i < numItems; i++) { + Item *item = (Item *)_inventory->getItemAt(i); + if (item == _currentItem) + item->select(); + + getItemXY(i, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.setTime(getItemPanelTime(item)); + _panelMovie.redrawMovieWorld(); + } + + uint32 numSlots = _itemsPerRow * _numberOfRows; + + for (uint32 i = numItems; i < numSlots; i++) { + getItemXY(i, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.setTime(0); + _panelMovie.redrawMovieWorld(); + } + + _lastReferenceCount = _inventory->getReferenceCount(); + } + + show(); // *** Do we really need this? + if (_currentItem) + highlightCurrentItem(); + + _active = true; +} + +void InventoryPicture::deactivateInventoryPicture() { + if (!_active) + return; + + _active = false; + allowInput(false); + _panelMovie.hide(); + hide(); + + if (_inventory->getNumItems() != 0) + if (!_currentItem->isActive()) + _currentItem->activate(); +} + +void InventoryPicture::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_active) { + if (input.upButtonDown()) { + if (_currentItemIndex - _itemsPerRow >= 0) + setCurrentItemIndex(_currentItemIndex - _itemsPerRow); + } else if (input.downButtonDown()) { + if (_currentItemIndex + _itemsPerRow < _inventory->getNumItems()) + setCurrentItemIndex(_currentItemIndex + _itemsPerRow); + } else if (input.leftButtonDown()) { + if ((_currentItemIndex % _itemsPerRow) != 0) + setCurrentItemIndex(_currentItemIndex - 1); + } else if (input.rightButtonDown()) { + if (((_currentItemIndex + 1) % _itemsPerRow) != 0 && _currentItemIndex + 1 < _inventory->getNumItems()) + setCurrentItemIndex(_currentItemIndex + 1); + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void InventoryPicture::highlightCurrentItem() { + CoordType x, y; + getItemXY(_currentItemIndex, x, y); + _highlightBounds.moveTo(x, y); +} + +InventoryItemsPicture::InventoryItemsPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InventoryPicture(id, nextHandler, inventory) { + _pictName = "Images/Items/Inventory/Inventory Panel"; + _movieName = "Images/Items/Inventory/Inventory Panel Movie"; + _highlightName = "Images/Items/Inventory/Inventory Hilite"; + + _itemsPerRow = 3; + _numberOfRows = 3; + _itemWidth = 88; + _itemHeight = 64; + _itemX = 8; + _itemY = 26; + _isLooping = true; +} + +void InventoryItemsPicture::loopCurrentItem() { + if (_isLooping) { + CoordType x, y; + getItemXY(_currentItemIndex, x, y); + _panelMovie.moveMovieBoxTo(x, y); + _highlightBounds.moveTo(x, y); + + TimeValue start, stop; + ((InventoryItem *)_currentItem)->getPanelTimes(start, stop); + _panelMovie.stop(); + _panelMovie.setFlags(0); + _panelMovie.setSegment(start, stop); + _panelMovie.setFlags(kLoopTimeBase); + _panelMovie.setTime(((InventoryItem *)_currentItem)->getAnimationTime()); + _panelMovie.start(); + } +} + +void InventoryItemsPicture::highlightCurrentItem() { + InventoryPicture::highlightCurrentItem(); + loopCurrentItem(); +} + +void InventoryItemsPicture::unhighlightCurrentItem() { + InventoryPicture::unhighlightCurrentItem(); + _panelMovie.stop(); + _panelMovie.setFlags(0); + ((InventoryItem *)_currentItem)->setAnimationTime(_panelMovie.getTime()); +} + +TimeValue InventoryItemsPicture::getItemPanelTime(Item *item) { + TimeValue start, stop; + ((InventoryItem *)item)->getPanelTimes(start, stop); + ((InventoryItem *)item)->setAnimationTime(start); + return start; +} + +void InventoryItemsPicture::deactivateInventoryPicture() { + if (_active) { + InventoryPicture::deactivateInventoryPicture(); + _panelMovie.stop(); + _panelMovie.setFlags(0); + _panelMovie.setSegment(0, _panelMovie.getDuration()); + _isLooping = true; + } +} + +void InventoryItemsPicture::playEndMessage(DisplayElement *pushElement) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Movie endMessage(0); + + _shouldDrawHighlight = false; + endMessage.shareSurface(this); + endMessage.initFromMovieFile("Images/Caldoria/A56 Congrats"); + endMessage.moveMovieBoxTo(kFinalMessageLeft - kInventoryPushLeft, kFinalMessageTop - kInventoryPushTop); + endMessage.setTriggeredElement(pushElement); + endMessage.start(); + + while (endMessage.isRunning()) { + vm->checkCallBacks(); + vm->refreshDisplay(); + g_system->delayMillis(10); + } + + endMessage.stop(); +} + +BiochipPicture::BiochipPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : + InventoryPicture(id, nextHandler, inventory) { + _pictName = "Images/Items/Biochips/Biochip Panel"; + _movieName = "Images/Items/Biochips/Biochip Panel Movie"; + _highlightName = "Images/Items/Biochips/BioChip Hilite"; + + _itemsPerRow = 4; + _numberOfRows = 2; + _itemWidth = 46; + _itemHeight = 46; + _itemX = 4; + _itemY = 24; +} + +void BiochipPicture::unhighlightCurrentItem() { + InventoryPicture::unhighlightCurrentItem(); + CoordType x, y; + getItemXY(_currentItemIndex, x, y); + _panelMovie.show(); + _panelMovie.moveMovieBoxTo(x, y); + _panelMovie.setTime(getItemPanelTime(_currentItem)); + _panelMovie.redrawMovieWorld(); +} + +TimeValue BiochipPicture::getItemPanelTime(Item *item) { + return ((BiochipItem *)item)->getPanelTime(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventorypicture.h b/engines/pegasus/items/inventorypicture.h new file mode 100644 index 0000000000..88a4a4ba75 --- /dev/null +++ b/engines/pegasus/items/inventorypicture.h @@ -0,0 +1,125 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORYPICTURE_H +#define PEGASUS_ITEMS_INVENTORYPICTURE_H + +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +class Inventory; +class Item; +class Input; +class Transition; + +class InventoryPicture : public InputHandler, public Picture { +public: + InventoryPicture(const DisplayElementID, InputHandler *, Inventory *); + virtual ~InventoryPicture() {} + + void initInventoryImage(Transition *); + void throwAwayInventoryImage(); + + void panelUp(); + void activateInventoryPicture(); + void deactivateInventoryPicture(); + void handleInput(const Input &, const Hotspot *); + bool wantsCursor() { return false; } + + InventoryResult addInventoryItem(Item *); + InventoryResult removeInventoryItem(Item *); + void removeAllItems(); + Item *getCurrentItem() { return _currentItem; } + void setCurrentItemIndex(int32); + void setCurrentItemID(ItemID); + int32 getCurrentItemIndex() { return _currentItemIndex; } + bool itemInInventory(Item *); + bool itemInInventory(const ItemID); + +protected: + void getItemXY(uint32, CoordType &, CoordType &); + void draw(const Common::Rect &); + void drawItemHighlight(const Common::Rect &); + virtual void highlightCurrentItem(); + virtual void unhighlightCurrentItem() {} + virtual TimeValue getItemPanelTime(Item *) = 0; + + Inventory *_inventory; + uint32 _lastReferenceCount; + Frame _highlightImage; + Movie _panelMovie; + int32 _currentItemIndex; + Item *_currentItem; + Common::Rect _highlightBounds; + bool _active, _shouldDrawHighlight; + + Common::String _pictName; + Common::String _movieName; + Common::String _highlightName; + uint16 _itemsPerRow; + uint16 _numberOfRows; + uint16 _itemWidth; + uint16 _itemHeight; + uint16 _itemX; + uint16 _itemY; +}; + +class InventoryItemsPicture : public InventoryPicture { +public: + InventoryItemsPicture(const DisplayElementID, InputHandler *, Inventory *); + virtual ~InventoryItemsPicture() {} + + void deactivateInventoryPicture(); + + void disableLooping() { _isLooping = false; } + + void playEndMessage(DisplayElement *); + +protected: + virtual void highlightCurrentItem(); + virtual void unhighlightCurrentItem(); + virtual TimeValue getItemPanelTime(Item *); + void loopCurrentItem(); + + InputHandler *_previousHandler; + bool _isLooping; +}; + +class BiochipPicture : public InventoryPicture { +public: + BiochipPicture(const DisplayElementID, InputHandler *, Inventory *); + virtual ~BiochipPicture() {} + +protected: + virtual void unhighlightCurrentItem(); + virtual TimeValue getItemPanelTime(Item *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/item.cpp b/engines/pegasus/items/item.cpp new file mode 100644 index 0000000000..8089f2b93d --- /dev/null +++ b/engines/pegasus/items/item.cpp @@ -0,0 +1,314 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/error.h" +#include "common/stream.h" + +#include "pegasus/constants.h" +#include "pegasus/elements.h" +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/item.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +Item::Item(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : IDObject(id) { + _itemNeighborhood = neighborhood; + _itemRoom = room; + _itemDirection = direction; + _itemWeight = 1; + _itemOwnerID = kNoActorID; + _itemState = 0; + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + Common::SeekableReadStream *info = vm->_resFork->getResource(kItemInfoResType, kItemBaseResID + id); + if (info) { + _itemInfo.infoLeftTime = info->readUint32BE(); + _itemInfo.infoRightStart = info->readUint32BE(); + _itemInfo.infoRightStop = info->readUint32BE(); + _itemInfo.dragSpriteNormalID = info->readUint16BE(); + _itemInfo.dragSpriteUsedID = info->readUint16BE(); + + if (vm->isDemo()) { + // Adjust info right times to account for the stuff that was chopped out of the + // info right movies. + // Assumes time scale of 600. + + // Gap times in seconds + static const TimeValue kGap1 = 24; + static const TimeValue kGap2 = 34; + static const TimeValue kGap3 = 4; + static const TimeValue kGap4 = 4; + + static const TimeValue kGapForGroup1 = kGap1; + static const TimeValue kGapForGroup2 = kGapForGroup1 + kGap2; + static const TimeValue kGapForGroup3 = kGapForGroup2 + kGap3; + static const TimeValue kGapForGroup4 = kGapForGroup3 + kGap4; + + switch (id) { + case kHistoricalLog: + case kJourneymanKey: + case kKeyCard: + _itemInfo.infoRightStart -= 600 * kGapForGroup1; + _itemInfo.infoRightStop -= 600 * kGapForGroup1; + break; + case kAIBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup2; + _itemInfo.infoRightStop -= 600 * kGapForGroup2; + break; + case kMapBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup3; + _itemInfo.infoRightStop -= 600 * kGapForGroup3; + break; + case kPegasusBiochip: + _itemInfo.infoRightStart -= 600 * kGapForGroup4; + _itemInfo.infoRightStop -= 600 * kGapForGroup4; + break; + } + } + + delete info; + } else { + memset(&_itemInfo, 0, sizeof(_itemInfo)); + } + + Common::SeekableReadStream *middleAreaInfo = vm->_resFork->getResource(kMiddleAreaInfoResType, kItemBaseResID + id); + if (middleAreaInfo) { + _sharedAreaInfo = readItemState(middleAreaInfo); + delete middleAreaInfo; + } else { + // Only kArgonPickup does not have this + memset(&_sharedAreaInfo, 0, sizeof(_sharedAreaInfo)); + } + + Common::SeekableReadStream *extraInfo = vm->_resFork->getResource(kItemExtraInfoResType, kItemBaseResID + id); + if (!extraInfo) + error("Extra info not found for item %d", id); + + _itemExtras.numEntries = extraInfo->readUint16BE(); + _itemExtras.entries = new ItemExtraEntry[_itemExtras.numEntries]; + for (uint16 i = 0; i < _itemExtras.numEntries; i++) { + _itemExtras.entries[i].extraID = extraInfo->readUint32BE(); + _itemExtras.entries[i].extraArea = extraInfo->readUint16BE(); + _itemExtras.entries[i].extraStart = extraInfo->readUint32BE(); + _itemExtras.entries[i].extraStop = extraInfo->readUint32BE(); + } + + delete extraInfo; + + g_allItems.push_back(this); +} + +Item::~Item() { + delete[] _sharedAreaInfo.entries; + delete[] _itemExtras.entries; +} + +void Item::writeToStream(Common::WriteStream *stream) { + stream->writeUint16BE(_itemNeighborhood); + stream->writeUint16BE(_itemRoom); + stream->writeByte(_itemDirection); + stream->writeUint16BE(_itemOwnerID); + stream->writeUint16BE(_itemState); +} + +void Item::readFromStream(Common::ReadStream *stream) { + _itemNeighborhood = stream->readUint16BE(); + _itemRoom = stream->readUint16BE(); + _itemDirection = stream->readByte(); + _itemOwnerID = stream->readUint16BE(); + _itemState = stream->readUint16BE(); +} + +ActorID Item::getItemOwner() const { + return _itemOwnerID; +} + +void Item::setItemOwner(const ActorID owner) { + _itemOwnerID = owner; + + if (owner == kNoActorID) { + if (isSelected()) + deselect(); + removedFromInventory(); + } else { + addedToInventory(); + } +} + +WeightType Item::getItemWeight() { + return _itemWeight; +} + +ItemState Item::getItemState() const { + return _itemState; +} + +void Item::setItemState(const ItemState state) { + if (state != _itemState) { + _itemState = state; + + if (getItemType() == kInventoryItemType && ((PegasusEngine *)g_engine)->getCurrentInventoryItem() == (InventoryItem *)this) + select(); + else if (getItemType() == kBiochipItemType && ((PegasusEngine *)g_engine)->getCurrentBiochip() == (BiochipItem *)this) + select(); + } +} + +void Item::getItemRoom(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) const { + neighborhood = _itemNeighborhood; + room = _itemRoom; + direction = _itemDirection; +} + +void Item::setItemRoom(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + _itemNeighborhood = neighborhood; + _itemRoom = room; + _itemDirection = direction; + + if (neighborhood == kNoNeighborhoodID) + pickedUp(); + else + dropped(); +} + +NeighborhoodID Item::getItemNeighborhood() const { + return _itemNeighborhood; +} + +TimeValue Item::getSharedAreaTime() const { + if (!_sharedAreaInfo.entries) + return 0xffffffff; + + TimeValue time; + ItemState state; + + findItemStateEntryByState(_sharedAreaInfo, _itemState, time); + if (time == 0xffffffff) + getItemStateEntry(_sharedAreaInfo, 0, state, time); + + return time; +} + +// Must affect images in shared area. +void Item::select() { + _isSelected = true; + + if (g_AIArea) { + if (getItemType() == kInventoryItemType) + g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, getSharedAreaTime()); + else + g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, getSharedAreaTime()); + } +} + +void Item::deselect() { + _isActive = false; + _isSelected = false; + + if (g_AIArea) { + if (getItemType() == kInventoryItemType) + g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, 0xffffffff); + else + g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, 0xffffffff); + } +} + +void Item::getItemStateEntry(ItemStateInfo info, uint32 index, ItemState &state, TimeValue &time) { + if (index < info.numEntries) { + state = info.entries[index].itemState; + time = info.entries[index].itemTime; + } else { + state = kNoItemState; + time = 0xffffffff; + } +} + +void Item::findItemStateEntryByState(ItemStateInfo info, ItemState state, TimeValue &time) { + for (uint16 i = 0; i < info.numEntries; i++) { + if (info.entries[i].itemState == state) { + time = info.entries[i].itemTime; + return; + } + } + + time = 0xffffffff; +} + +ItemStateInfo Item::readItemState(Common::SeekableReadStream *stream) { + ItemStateInfo info; + + info.numEntries = stream->readUint16BE(); + info.entries = new ItemStateEntry[info.numEntries]; + for (uint16 i = 0; i < info.numEntries; i++) { + info.entries[i].itemState = stream->readSint16BE(); + info.entries[i].itemTime = stream->readUint32BE(); + } + + return info; +} + +Sprite *Item::getDragSprite(const DisplayElementID id) const { + PegasusEngine *vm = (PegasusEngine *)g_engine; + Sprite *result = new Sprite(id); + SpriteFrame *frame = new SpriteFrame(); + + frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteNormalID, true); + result->addFrame(frame, 0, 0); + + if (_itemInfo.dragSpriteNormalID != _itemInfo.dragSpriteUsedID) { + frame = new SpriteFrame(); + frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteUsedID, true); + } + + result->addFrame(frame, 0, 0); + result->setCurrentFrameIndex(0); + return result; +} + +TimeValue Item::getInfoLeftTime() const { + return _itemInfo.infoLeftTime; +} + +void Item::getInfoRightTimes(TimeValue &start, TimeValue &stop) const { + start = _itemInfo.infoRightStart; + stop = _itemInfo.infoRightStop; +} + +void Item::findItemExtra(const uint32 extraID, ItemExtraEntry &entry) { + for (uint32 i = 0; i < _itemExtras.numEntries; i++) { + if (_itemExtras.entries[i].extraID == extraID) { + entry = _itemExtras.entries[i]; + return; + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/item.h b/engines/pegasus/items/item.h new file mode 100644 index 0000000000..a1451b2a58 --- /dev/null +++ b/engines/pegasus/items/item.h @@ -0,0 +1,363 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_ITEM_H +#define PEGASUS_ITEMS_ITEM_H + +#include "common/endian.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" + +namespace Common { + class Error; + class ReadStream; + class WriteStream; + class SeekableReadStream; +} + +namespace Pegasus { + +// JMPItemInfo contains resource data used by all Items. + +struct JMPItemInfo { + TimeValue infoLeftTime; + TimeValue infoRightStart; + TimeValue infoRightStop; + uint32 dragSpriteNormalID; + uint32 dragSpriteUsedID; +}; + +// ItemStateEntry contains a single state/TimeValue pair. The TimeValue is +// the time value to set the shared area movie that corresponds with the given +// state of an inventory item. + +struct ItemStateEntry { + ItemState itemState; + TimeValue itemTime; +}; + +struct ItemStateInfo { + uint16 numEntries; // For easy ResEdit access + ItemStateEntry *entries; +}; + +// ItemExtraEntry + +static const short kLeftAreaExtra = 0; +static const short kMiddleAreaExtra = 1; +static const short kRightAreaExtra = 2; + +struct ItemExtraEntry { + uint32 extraID; + uint16 extraArea; + TimeValue extraStart; + TimeValue extraStop; +}; + +struct ItemExtraInfo { + uint16 numEntries; // For easy ResEdit access + ItemExtraEntry *entries; +}; + +// Inventory info resource type and ID: +// Individual inventory items are stored in these resource types. +// Resource ID is item ID + kItemBaseResID. + +static const uint32 kItemInfoResType = MKTAG('I', 't', 'e', 'm'); // JMPItemInfoHandle +static const uint32 kLeftAreaInfoResType = MKTAG('L', 'e', 'f', 't'); // ItemStateInfoHandle +static const uint32 kMiddleAreaInfoResType = MKTAG('M', 'i', 'd', 'l'); // ItemStateInfoHandle +static const uint32 kRightAreaInfoResType = MKTAG('R', 'g', 'h', 't'); // ItemStateInfoHandle +static const uint32 kItemExtraInfoResType = MKTAG('I', 'X', 't', 'r'); // ItemExtraInfoHandle + +static const uint16 kItemBaseResID = 128; + +// Item IDs. + +static const ItemID kAirMask = 7; +static const ItemID kAntidote = 8; +static const ItemID kArgonCanister = 9; +static const ItemID kCardBomb = 10; +static const ItemID kCrowbar = 11; +static const ItemID kGasCanister = 12; +static const ItemID kHistoricalLog = 13; +static const ItemID kJourneymanKey = 14; +static const ItemID kKeyCard = 15; +static const ItemID kMachineGun = 16; +static const ItemID kMarsCard = 17; +static const ItemID kNitrogenCanister = 18; +static const ItemID kOrangeJuiceGlassFull = 19; +static const ItemID kOrangeJuiceGlassEmpty = 20; +static const ItemID kPoisonDart = 21; +static const ItemID kSinclairKey = 22; +static const ItemID kStunGun = 23; +static const ItemID kArgonPickup = 24; + +// Biochips. + +static const ItemID kAIBiochip = 0; +static const ItemID kInterfaceBiochip = 1; +static const ItemID kMapBiochip = 2; +static const ItemID kOpticalBiochip = 3; +static const ItemID kPegasusBiochip = 4; +static const ItemID kRetinalScanBiochip = 5; +static const ItemID kShieldBiochip = 6; + +static const ItemID kNumItems = 25; + +// Item States. + +static const ItemState kAI000 = 0; +static const ItemState kAI005 = 1; +static const ItemState kAI006 = 2; +static const ItemState kAI010 = 3; +static const ItemState kAI015 = 4; +static const ItemState kAI016 = 5; +static const ItemState kAI020 = 6; +static const ItemState kAI024 = 7; +static const ItemState kAI100 = 8; +static const ItemState kAI101 = 9; +static const ItemState kAI105 = 10; +static const ItemState kAI106 = 11; +static const ItemState kAI110 = 12; +static const ItemState kAI111 = 13; +static const ItemState kAI115 = 14; +static const ItemState kAI116 = 15; +static const ItemState kAI120 = 16; +static const ItemState kAI121 = 17; +static const ItemState kAI124 = 18; +static const ItemState kAI125 = 19; +static const ItemState kAI126 = 20; +static const ItemState kAI200 = 21; +static const ItemState kAI201 = 22; +static const ItemState kAI202 = 23; +static const ItemState kAI205 = 24; +static const ItemState kAI206 = 25; +static const ItemState kAI210 = 26; +static const ItemState kAI211 = 27; +static const ItemState kAI212 = 28; +static const ItemState kAI215 = 29; +static const ItemState kAI216 = 30; +static const ItemState kAI220 = 31; +static const ItemState kAI221 = 32; +static const ItemState kAI222 = 33; +static const ItemState kAI224 = 34; +static const ItemState kAI225 = 35; +static const ItemState kAI226 = 36; +static const ItemState kAI300 = 37; +static const ItemState kAI301 = 38; +static const ItemState kAI302 = 39; +static const ItemState kAI303 = 40; +static const ItemState kAI305 = 41; +static const ItemState kAI306 = 42; +static const ItemState kAI310 = 43; +static const ItemState kAI311 = 44; +static const ItemState kAI312 = 45; +static const ItemState kAI313 = 46; +static const ItemState kAI315 = 47; +static const ItemState kAI316 = 48; +static const ItemState kAI320 = 49; +static const ItemState kAI321 = 50; +static const ItemState kAI322 = 51; +static const ItemState kAI323 = 52; +static const ItemState kAI324 = 53; +static const ItemState kAI325 = 54; +static const ItemState kAI326 = 55; +static const ItemState kNormalItem = 56; +static const ItemState kMapUnavailable = 57; +static const ItemState kMapEngaged = 58; +static const ItemState kOptical000 = 59; +static const ItemState kOptical001 = 60; +static const ItemState kOptical002 = 61; +static const ItemState kOptical010 = 62; +static const ItemState kOptical011 = 63; +static const ItemState kOptical012 = 64; +static const ItemState kOptical020 = 65; +static const ItemState kOptical021 = 66; +static const ItemState kOptical100 = 67; +static const ItemState kOptical101 = 68; +static const ItemState kOptical102 = 69; +static const ItemState kOptical110 = 70; +static const ItemState kOptical111 = 71; +static const ItemState kOptical112 = 72; +static const ItemState kOptical120 = 73; +static const ItemState kOptical121 = 74; +static const ItemState kOptical200 = 75; +static const ItemState kOptical201 = 76; +static const ItemState kOptical210 = 77; +static const ItemState kOptical211 = 78; +static const ItemState kPegasusTSA00 = 79; +static const ItemState kPegasusTSA10 = 80; +static const ItemState kPegasusPrehistoric00 = 81; +static const ItemState kPegasusPrehistoric01 = 82; +static const ItemState kPegasusPrehistoric10 = 83; +static const ItemState kPegasusPrehistoric11 = 84; +static const ItemState kPegasusMars00 = 85; +static const ItemState kPegasusMars01 = 86; +static const ItemState kPegasusMars10 = 87; +static const ItemState kPegasusMars11 = 88; +static const ItemState kPegasusNorad00 = 89; +static const ItemState kPegasusNorad01 = 90; +static const ItemState kPegasusNorad10 = 91; +static const ItemState kPegasusNorad11 = 92; +static const ItemState kPegasusWSC00 = 93; +static const ItemState kPegasusWSC01 = 94; +static const ItemState kPegasusWSC10 = 95; +static const ItemState kPegasusWSC11 = 96; +static const ItemState kPegasusCaldoria = 97; +static const ItemState kRetinalSimulating = 98; +static const ItemState kShieldNormal = 99; +static const ItemState kShieldRadiation = 100; +static const ItemState kShieldPlasma = 101; +static const ItemState kShieldCardBomb = 102; +static const ItemState kShieldDraining = 103; +static const ItemState kAirMaskEmptyOff = 104; +static const ItemState kAirMaskEmptyFilter = 105; +static const ItemState kAirMaskLowOff = 106; +static const ItemState kAirMaskLowFilter = 107; +static const ItemState kAirMaskLowOn = 108; +static const ItemState kAirMaskFullOff = 109; +static const ItemState kAirMaskFullFilter = 110; +static const ItemState kAirMaskFullOn = 111; +static const ItemState kArgonEmpty = 112; +static const ItemState kArgonFull = 113; +static const ItemState kFlashlightOff = 114; +static const ItemState kFlashlightOn = 115; +static const ItemState kNitrogenEmpty = 116; +static const ItemState kNitrogenFull = 117; +static const ItemState kFullGlass = 118; + +// Extra IDs. + +static const uint32 kRetinalScanSearching = 0; +static const uint32 kRetinalScanActivated = 1; +static const uint32 kShieldIntro = 2; +static const uint32 kRemoveAirMask = 3; +static const uint32 kRemoveArgon = 4; +static const uint32 kRemoveCrowbar = 5; +static const uint32 kGasCanLoop = 6; +static const uint32 kRemoveJourneymanKey = 7; +static const uint32 kRemoveMarsCard = 8; +static const uint32 kRemoveNitrogen = 9; +static const uint32 kRemoveGlass = 10; +static const uint32 kRemoveDart = 11; +static const uint32 kRemoveSinclairKey = 12; + +enum ItemType { + kInventoryItemType, + kBiochipItemType +}; + +class Sprite; + +/* + + Item is an object which can be picked up and carried around. + Items have + a location + an ID. + weight + an owner (kNoActorID if no one is carrying the Item) + +*/ + +class Item : public IDObject { +public: + Item(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + virtual ~Item(); + + // WriteToStream writes everything EXCEPT the item's ID. + // It is assumed that the calling function will write and read the ID. + virtual void writeToStream(Common::WriteStream *stream); + virtual void readFromStream(Common::ReadStream *stream); + + virtual ActorID getItemOwner() const; + virtual void setItemOwner(const ActorID owner); + + void getItemRoom(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) const; + void setItemRoom(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + NeighborhoodID getItemNeighborhood() const; + + virtual WeightType getItemWeight(); + + virtual void setItemState(const ItemState state); + virtual ItemState getItemState() const; + + virtual ItemType getItemType() = 0; + + TimeValue getInfoLeftTime() const; + void getInfoRightTimes(TimeValue &, TimeValue &) const; + TimeValue getSharedAreaTime() const; + + Sprite *getDragSprite(const DisplayElementID) const; + + /* + select -- called when this item becomes current. Also called when the inventory + panel holding this item is raised and this is the current item. + deselect -- called when this item is no longer current. + activate -- called on the current item when the panel is closed. + */ + // In an override of these three member functions, you must call the inherited + // member functions. + virtual void select(); + virtual void deselect(); + virtual bool isSelected() { return _isSelected; } + + virtual void activate() { _isActive = true; } + virtual bool isActive() { return _isActive; } + virtual void pickedUp() {} + virtual void addedToInventory() {} + virtual void removedFromInventory() {} + virtual void dropped() {} + + // Called when the shared area is taken by another item, but this item is still + // selected. + virtual void giveUpSharedArea() {} + virtual void takeSharedArea() {} + + void findItemExtra(const uint32 extraID, ItemExtraEntry &entry); + +protected: + NeighborhoodID _itemNeighborhood; + RoomID _itemRoom; + DirectionConstant _itemDirection; + ActorID _itemOwnerID; + WeightType _itemWeight; + ItemState _itemState; + + JMPItemInfo _itemInfo; + ItemStateInfo _sharedAreaInfo; + ItemExtraInfo _itemExtras; + bool _isActive; + bool _isSelected; + + static void getItemStateEntry(ItemStateInfo, uint32, ItemState &, TimeValue &); + static void findItemStateEntryByState(ItemStateInfo, ItemState, TimeValue &); + static ItemStateInfo readItemState(Common::SeekableReadStream *stream); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/itemdragger.cpp b/engines/pegasus/items/itemdragger.cpp new file mode 100644 index 0000000000..97fc5a97ac --- /dev/null +++ b/engines/pegasus/items/itemdragger.cpp @@ -0,0 +1,193 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/elements.h" +#include "pegasus/hotspot.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/itemdragger.h" + +namespace Pegasus { + +SpriteDragger::SpriteDragger() { + _draggingSprite = 0; + _limitRect = Common::Rect(-30000, -30000, 30000, 30000); + _slopRect = Common::Rect(-30000, -30000, 30000, 30000); + _dragOffset.x = 0; + _dragOffset.y = 0; + _lastHotspot = 0; +} + +void SpriteDragger::setDragSprite(Sprite *newSprite) { + if (!isTracking()) + _draggingSprite = newSprite; +} + +void SpriteDragger::setDragConstraints(const Common::Rect &limitRect, const Common::Rect &slopRect) { + if (!isTracking()) { + _rawLimitRect = limitRect; + _slopRect = slopRect; + } +} + +void SpriteDragger::getDragConstraints(Common::Rect &limitRect, Common::Rect &slopRect) const { + limitRect = _rawLimitRect; + slopRect = _slopRect; +} + +void SpriteDragger::startTracking(const Input &input) { + if (_draggingSprite) { + Tracker::startTracking(input); + + if (isTracking()) { + input.getInputLocation(_startPoint); + _lastRawPoint = _startRawPoint = _startPoint; + + Common::Rect r; + _draggingSprite->getBounds(r); + _dragOffset.x = _startPoint.x - r.left; + _dragOffset.y = _startPoint.y - r.top; + + _limitRect = _rawLimitRect; + _limitRect.left += _dragOffset.x; + _limitRect.top += _dragOffset.y; + _limitRect.right -= r.width() - _dragOffset.x; + _limitRect.bottom -= r.height() - _dragOffset.y; + pinPointInRect(_limitRect, _startPoint); + + _lastPoint = _startPoint; + if (_startPoint != _startRawPoint) { + Common::Point pt = _startPoint - _dragOffset; + _draggingSprite->moveElementTo(pt.x, pt.y); + } + + _lastHotspot = g_allHotspots.findHotspot(_lastRawPoint); + if (_lastHotspot) + enterHotspot(_lastHotspot); + } + } +} + +void SpriteDragger::continueTracking(const Input &input) { + if (_draggingSprite) { + Common::Point rawPoint; + input.getInputLocation(rawPoint); + + if (!_slopRect.contains(rawPoint)) + rawPoint = _startRawPoint; + + if (rawPoint != _lastRawPoint) { + Common::Point newPoint = rawPoint; + pinPointInRect(_limitRect, newPoint); + newPoint -= _dragOffset; + + if (newPoint != _lastPoint) { + _draggingSprite->moveElementTo(newPoint.x, newPoint.y); + _lastPoint = newPoint; + } + + Hotspot *newHotspot = g_allHotspots.findHotspot(rawPoint); + if (newHotspot != _lastHotspot) { + if (_lastHotspot) + exitHotspot(_lastHotspot); + if (newHotspot) + enterHotspot(newHotspot); + _lastHotspot = newHotspot; + } + + _lastRawPoint = rawPoint; + } + } +} + +void SpriteDragger::pinPointInRect(const Common::Rect &r, Common::Point &pt) { + pt.x = CLIP<int>(pt.x, r.left, r.right - 1); + pt.y = CLIP<int>(pt.y, r.top, r.bottom - 1); +} + +ItemDragger::ItemDragger(PegasusEngine *owner) : _inventoryDropSpot(kInventoryDropSpotID), _biochipDropSpot(kBiochipDropSpotID), + _inventoryHighlight(kInventoryDropHighlightID), _biochipHighlight(kBiochipDropHighlightID) { + _owner = owner; + + Common::Rect r(kInventoryDropLeft, kInventoryDropTop, kInventoryDropRight, kInventoryDropBottom); + _inventoryDropSpot.setArea(r); + _inventoryDropSpot.setHotspotFlags(kDropItemSpotFlag); + g_allHotspots.push_back(&_inventoryDropSpot); + + r = Common::Rect(kBiochipDropLeft, kBiochipDropTop, kBiochipDropRight, kBiochipDropBottom); + _biochipDropSpot.setArea(r); + _biochipDropSpot.setHotspotFlags(kDropBiochipSpotFlag); + g_allHotspots.push_back(&_biochipDropSpot); +} + +void ItemDragger::startTracking(const Input &input) { + _inventoryHighlight.setDisplayOrder(kInventoryHiliteOrder); + _inventoryHighlight.startDisplaying(); + + _biochipHighlight.setDisplayOrder(kBiochipHiliteOrder); + _biochipHighlight.startDisplaying(); + + SpriteDragger::startTracking(input); +} + +void ItemDragger::stopTracking(const Input &input) { + SpriteDragger::stopTracking(input); + _inventoryHighlight.hide(); + _biochipHighlight.hide(); + _inventoryHighlight.stopDisplaying(); + _biochipHighlight.stopDisplaying(); + _owner->dragTerminated(input); +} + +bool ItemDragger::stopTrackingInput(const Input &input) { + return !JMPPPInput::isDraggingInput(input); +} + +void ItemDragger::enterHotspot(Hotspot *spot) { + if (spot->getObjectID() == kInventoryDropSpotID) + _inventoryHighlight.show(); + else if (spot->getObjectID() == kBiochipDropSpotID) + _biochipHighlight.show(); + else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0) + _draggingSprite->setCurrentFrameIndex(1); +} + +void ItemDragger::exitHotspot(Hotspot *spot) { + if (spot->getObjectID() == kInventoryDropSpotID) + _inventoryHighlight.hide(); + else if (spot->getObjectID() == kBiochipDropSpotID) + _biochipHighlight.hide(); + else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0) + _draggingSprite->setCurrentFrameIndex(0); +} + +void ItemDragger::setHighlightBounds() { + uint32 color = g_system->getScreenFormat().RGBToColor(0x48, 0x80, 0xD8); + _inventoryHighlight.setBounds(Common::Rect(76, 334, 172, 430)); + _inventoryHighlight.setHighlightColor(color); + _biochipHighlight.setBounds(Common::Rect(364, 334, 460, 430)); + _biochipHighlight.setHighlightColor(color); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/itemdragger.h b/engines/pegasus/items/itemdragger.h new file mode 100644 index 0000000000..fce953d695 --- /dev/null +++ b/engines/pegasus/items/itemdragger.h @@ -0,0 +1,96 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_ITEMDRAGGER_H +#define PEGASUS_ITEMS_ITEMDRAGGER_H + +#include "pegasus/elements.h" +#include "pegasus/input.h" + +namespace Pegasus { + +// TODO: Merge SpriteDragger and ItemDragger + +class Hotspot; +class Sprite; + +class SpriteDragger : public Tracker { +public: + SpriteDragger(); + virtual ~SpriteDragger() {} + + void setDragSprite(Sprite *); + Sprite *getDragSprite() { return _draggingSprite; } + + void setDragConstraints(const Common::Rect &, const Common::Rect &); + void getDragConstraints(Common::Rect &, Common::Rect &) const; + + void startTracking(const Input &); + void continueTracking(const Input&); + + Hotspot *getLastHotspot() const { return _lastHotspot; } + +protected: + virtual void enterHotspot(Hotspot *) {} + virtual void exitHotspot(Hotspot *) {} + + Sprite *_draggingSprite; + Common::Point _startPoint, _lastPoint, _dragOffset; + Common::Point _startRawPoint, _lastRawPoint; + Common::Rect _rawLimitRect; + Common::Rect _limitRect; + Common::Rect _slopRect; + Hotspot *_lastHotspot; + + // This is a replica of QuickDraw's PinPointInRect function + void pinPointInRect(const Common::Rect &, Common::Point &); +}; + +class PegasusEngine; + +class ItemDragger : public SpriteDragger { +public: + ItemDragger(PegasusEngine *); + virtual ~ItemDragger() {} + + void setHighlightBounds(); + void startTracking(const Input &); + void stopTracking(const Input &); + bool stopTrackingInput(const Input &); + +protected: + virtual void enterHotspot(Hotspot *); + virtual void exitHotspot(Hotspot *); + + PegasusEngine *_owner; + DropHighlight _inventoryHighlight; + Hotspot _inventoryDropSpot; + DropHighlight _biochipHighlight; + Hotspot _biochipDropSpot; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/itemlist.cpp b/engines/pegasus/items/itemlist.cpp new file mode 100644 index 0000000000..ff8cae546b --- /dev/null +++ b/engines/pegasus/items/itemlist.cpp @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/error.h" +#include "common/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/items/item.h" +#include "pegasus/items/itemlist.h" + +namespace Pegasus { + +ItemList::ItemList() { +} + +ItemList::~ItemList() { +} + +void ItemList::writeToStream(Common::WriteStream *stream) { + stream->writeUint32BE(size()); + + for (ItemIterator it = begin(); it != end(); it++) { + stream->writeUint16BE((*it)->getObjectID()); + (*it)->writeToStream(stream); + } +} + +void ItemList::readFromStream(Common::ReadStream *stream) { + uint32 itemCount = stream->readUint32BE(); + + for (uint32 i = 0; i < itemCount; i++) { + ItemID itemID = stream->readUint16BE(); + g_allItems.findItemByID(itemID)->readFromStream(stream); + } +} + +Item *ItemList::findItemByID(const ItemID id) { + for (ItemIterator it = begin(); it != end(); it++) + if ((*it)->getObjectID() == id) + return *it; + + return 0; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/itemlist.h b/engines/pegasus/items/itemlist.h new file mode 100644 index 0000000000..9b59206ab3 --- /dev/null +++ b/engines/pegasus/items/itemlist.h @@ -0,0 +1,59 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_ITEMLIST_H +#define PEGASUS_ITEMS_ITEMLIST_H + +#include "common/list.h" + +#include "pegasus/types.h" + +namespace Common { + class ReadStream; + class WriteStream; +} + +namespace Pegasus { + +class Item; + +class ItemList : public Common::List<Item *> { +public: + ItemList(); + virtual ~ItemList(); + + virtual void writeToStream(Common::WriteStream *stream); + virtual void readFromStream(Common::ReadStream *stream); + + Item *findItemByID(const ItemID id); +}; + +typedef ItemList::iterator ItemIterator; + +#define g_allItems (((PegasusEngine *)g_engine)->getAllItems()) + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/menu.cpp b/engines/pegasus/menu.cpp new file mode 100644 index 0000000000..deaa460188 --- /dev/null +++ b/engines/pegasus/menu.cpp @@ -0,0 +1,1219 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/menu.h" +#include "pegasus/pegasus.h" +#include "pegasus/scoring.h" + +namespace Pegasus { + +GameMenu::GameMenu(const uint32 id) : IDObject(id), InputHandler((InputHandler *)((PegasusEngine *)g_engine)) { + _previousHandler = 0; + _lastCommand = kMenuCmdNoCommand; +} + +void GameMenu::becomeCurrentHandler() { + _previousHandler = InputHandler::setInputHandler(this); +} + +void GameMenu::restorePreviousHandler() { + InputHandler::setInputHandler(_previousHandler); +} + +void GameMenu::drawScore(GameScoreType score, GameScoreType total, const Common::Rect &scoreBounds, Surface *numbers) { + CoordType x = scoreBounds.right; + drawNumber(total, x, scoreBounds.top, numbers); + + x -= 12; + Common::Rect r1(120, 0, 132, 12); // The slash. + Common::Rect r2 = r1; + r2.moveTo(x, scoreBounds.top); + numbers->copyToCurrentPort(r1, r2); + drawNumber(score, x, scoreBounds.top, numbers); +} + +void GameMenu::drawNumber(GameScoreType number, CoordType &x, CoordType y, Surface *numbers) { + Common::Rect r1(0, 0, 12, 12); // Width, height of one digit + Common::Rect r2 = r1; + r2.moveTo(x - 12, y); + + do { + uint16 digit = number % 10; + number /= 10; + r1.moveTo(digit * 12, 0); + numbers->copyToCurrentPort(r1, r2); + r2.translate(-12, 0); + } while (number != 0); + + x = r2.right; +} + +enum { + kMainMenuStartDemo = 0, + kMainMenuCreditsDemo, + kMainMenuQuitDemo, + kFirstSelectionDemo = kMainMenuStartDemo, + kLastSelectionDemo = kMainMenuQuitDemo, + + kMainMenuOverview = 0, + kMainMenuStart, + kMainMenuRestore, + kMainMenuDifficulty, + kMainMenuCredits, + kMainMenuQuit, + kFirstSelection = kMainMenuOverview, + kLastSelection = kMainMenuQuit +}; + +static const CoordType kStartLeftDemo = 44; +static const CoordType kStartTopDemo = 336; + +static const CoordType kStartSelectLeftDemo = 40; +static const CoordType kStartSelectTopDemo = 331; + +static const CoordType kCreditsLeftDemo = 44; +static const CoordType kCreditsTopDemo = 372; + +static const CoordType kCreditsSelectLeftDemo = 40; +static const CoordType kCreditsSelectTopDemo = 367; + +static const CoordType kMainMenuQuitLeftDemo = 32; +static const CoordType kMainMenuQuitTopDemo = 412; + +static const CoordType kMainMenuQuitSelectLeftDemo = 28; +static const CoordType kMainMenuQuitSelectTopDemo = 408; + +static const CoordType kOverviewLeft = 200; +static const CoordType kOverviewTop = 208; + +static const CoordType kOverviewSelectLeft = 152; +static const CoordType kOverviewSelectTop = 204; + +static const CoordType kStartLeft = 212; +static const CoordType kStartTop = 256; + +static const CoordType kStartSelectLeft = 152; +static const CoordType kStartSelectTop = 252; + +static const CoordType kRestoreLeft = 212; +static const CoordType kRestoreTop = 296; + +static const CoordType kRestoreSelectLeft = 152; +static const CoordType kRestoreSelectTop = 292; + +static const CoordType kDifficultyLeft = 320; +static const CoordType kDifficultyTop = 340; + +static const CoordType kDifficultySelectLeft = 152; +static const CoordType kDifficultySelectTop = 336; + +static const CoordType kCreditsLeft = 212; +static const CoordType kCreditsTop = 388; + +static const CoordType kCreditsSelectLeft = 152; +static const CoordType kCreditsSelectTop = 384; + +static const CoordType kMainMenuQuitLeft = 212; +static const CoordType kMainMenuQuitTop = 428; + +static const CoordType kMainMenuQuitSelectLeft = 152; +static const CoordType kMainMenuQuitSelectTop = 424; + +// Never set the current input handler to the MainMenu. +MainMenu::MainMenu() : GameMenu(kMainMenuID), _menuBackground(0), _overviewButton(0), + _restoreButton(0), _adventureButton(0), _walkthroughButton(0), _startButton(0), + _creditsButton(0), _quitButton(0), _largeSelect(0), _smallSelect(0) { + + bool isDemo = ((PegasusEngine *)g_engine)->isDemo(); + + if (isDemo) + _menuBackground.initFromPICTFile("Images/Demo/DemoMenu.pict"); + else + _menuBackground.initFromPICTFile("Images/Main Menu/MainMenu.mac"); + _menuBackground.setDisplayOrder(0); + _menuBackground.startDisplaying(); + _menuBackground.show(); + + if (!isDemo) { + _overviewButton.initFromPICTFile("Images/Main Menu/pbOvervi.pict"); + _overviewButton.setDisplayOrder(1); + _overviewButton.moveElementTo(kOverviewLeft, kOverviewTop); + _overviewButton.startDisplaying(); + + _restoreButton.initFromPICTFile("Images/Main Menu/pbRestor.pict"); + _restoreButton.setDisplayOrder(1); + _restoreButton.moveElementTo(kRestoreLeft, kRestoreTop); + _restoreButton.startDisplaying(); + + _adventureButton.initFromPICTFile("Images/Main Menu/BtnAdv.pict"); + _adventureButton.setDisplayOrder(1); + _adventureButton.moveElementTo(kDifficultyLeft, kDifficultyTop); + _adventureButton.startDisplaying(); + + _walkthroughButton.initFromPICTFile("Images/Main Menu/BtnWlk.pict"); + _walkthroughButton.setDisplayOrder(1); + _walkthroughButton.moveElementTo(kDifficultyLeft, kDifficultyTop); + _walkthroughButton.startDisplaying(); + } + + if (isDemo) + _startButton.initFromPICTFile("Images/Demo/Start.pict"); + else + _startButton.initFromPICTFile("Images/Main Menu/pbStart.pict"); + _startButton.setDisplayOrder(1); + _startButton.moveElementTo(isDemo ? kStartLeftDemo : kStartLeft, isDemo ? kStartTopDemo : kStartTop); + _startButton.startDisplaying(); + + if (isDemo) + _creditsButton.initFromPICTFile("Images/Demo/Credits.pict"); + else + _creditsButton.initFromPICTFile("Images/Main Menu/pbCredit.pict"); + _creditsButton.setDisplayOrder(1); + _creditsButton.moveElementTo(isDemo ? kCreditsLeftDemo : kCreditsLeft, isDemo ? kCreditsTopDemo : kCreditsTop); + _creditsButton.startDisplaying(); + + if (isDemo) + _quitButton.initFromPICTFile("Images/Demo/Quit.pict"); + else + _quitButton.initFromPICTFile("Images/Main Menu/pbQuit.pict"); + _quitButton.setDisplayOrder(1); + _quitButton.moveElementTo(isDemo ? kMainMenuQuitLeftDemo : kMainMenuQuitLeft, isDemo ? kMainMenuQuitTopDemo : kMainMenuQuitTop); + _quitButton.startDisplaying(); + + if (isDemo) + _largeSelect.initFromPICTFile("Images/Demo/SelectL.pict", true); + else + _largeSelect.initFromPICTFile("Images/Main Menu/SelectL.pict", true); + _largeSelect.setDisplayOrder(1); + _largeSelect.startDisplaying(); + + if (isDemo) + _smallSelect.initFromPICTFile("Images/Demo/SelectS.pict", true); + else + _smallSelect.initFromPICTFile("Images/Main Menu/SelectS.pict", true); + _smallSelect.setDisplayOrder(1); + _smallSelect.startDisplaying(); + + _menuSelection = isDemo ? kFirstSelectionDemo : kFirstSelection; + + _adventureMode = true; + + _menuLoop.attachFader(&_menuFader); + _menuLoop.initFromAIFFFile("Sounds/Main Menu.aiff"); + + updateDisplay(); +} + +MainMenu::~MainMenu() { + if (_menuLoop.isPlaying()) + stopMainMenuLoop(); +} + +void MainMenu::startMainMenuLoop() { + FaderMoveSpec spec; + + _menuLoop.loopSound(); + spec.makeTwoKnotFaderSpec(30, 0, 0, 30, 255); + _menuFader.startFaderSync(spec); +} + +void MainMenu::stopMainMenuLoop() { + FaderMoveSpec spec; + + spec.makeTwoKnotFaderSpec(30, 0, 255, 30, 0); + _menuFader.startFaderSync(spec); + _menuLoop.stopSound(); +} + +void MainMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + bool isDemo = vm->isDemo(); + + if (input.upButtonDown()) { + if (_menuSelection > (isDemo ? kFirstSelectionDemo : kFirstSelection)) { + _menuSelection--; + updateDisplay(); + } + } else if (input.downButtonDown()) { + if (_menuSelection < (isDemo ? kLastSelectionDemo : kLastSelection)) { + _menuSelection++; + updateDisplay(); + } + } else if (!isDemo && (input.leftButtonDown() || input.rightButtonDown())) { + if (_menuSelection == kMainMenuDifficulty) { + _adventureMode = !_adventureMode; + updateDisplay(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (isDemo) { + switch (_menuSelection) { + case kMainMenuCreditsDemo: + _creditsButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _creditsButton.hide(); + setLastCommand(kMenuCmdCredits); + break; + case kMainMenuStartDemo: + _startButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _startButton.hide(); + setLastCommand(kMenuCmdStartAdventure); + break; + case kMainMenuQuitDemo: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdQuit); + break; + } + } else { + switch (_menuSelection) { + case kMainMenuOverview: + _overviewButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _overviewButton.hide(); + setLastCommand(kMenuCmdOverview); + break; + case kMainMenuRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdRestore); + break; + case kMainMenuCredits: + _creditsButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _creditsButton.hide(); + setLastCommand(kMenuCmdCredits); + break; + case kMainMenuStart: + _startButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _startButton.hide(); + if (_adventureMode) + setLastCommand(kMenuCmdStartAdventure); + else + setLastCommand(kMenuCmdStartWalkthrough); + break; + case kMainMenuDifficulty: + _adventureMode = !_adventureMode; + updateDisplay(); + break; + case kMainMenuQuit: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdQuit); + break; + } + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void MainMenu::updateDisplay() { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (vm->isDemo()) { + switch (_menuSelection) { + case kMainMenuStartDemo: + _smallSelect.moveElementTo(kStartSelectLeftDemo, kStartSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuCreditsDemo: + _smallSelect.moveElementTo(kCreditsSelectLeftDemo, kCreditsSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuQuitDemo: + _largeSelect.moveElementTo(kMainMenuQuitSelectLeftDemo, kMainMenuQuitSelectTopDemo); + _largeSelect.show(); + _smallSelect.hide(); + break; + } + } else { + switch (_menuSelection) { + case kMainMenuOverview: + _largeSelect.moveElementTo(kOverviewSelectLeft, kOverviewSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kMainMenuRestore: + _smallSelect.moveElementTo(kRestoreSelectLeft, kRestoreSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuDifficulty: + if (_adventureMode) { + _adventureButton.show(); + _walkthroughButton.hide(); + } else { + _walkthroughButton.show(); + _adventureButton.hide(); + } + + _largeSelect.moveElementTo(kDifficultySelectLeft, kDifficultySelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kMainMenuStart: + _smallSelect.moveElementTo(kStartSelectLeft, kStartSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuCredits: + _smallSelect.moveElementTo(kCreditsSelectLeft, kCreditsSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kMainMenuQuit: + _smallSelect.moveElementTo(kMainMenuQuitSelectLeft, kMainMenuQuitSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + } + + vm->resetIntroTimer(); + } +} + +enum { + kCreditsMenuCoreTeam, + kCreditsMenuSupportTeam, + kCreditsMenuOriginalTeam, + kCreditsMenuTalent, + kCreditsMenuOtherTitles, + kCreditsMenuMainMenu, + + kCreditsFirstSelection = kCreditsMenuCoreTeam, + kCreditsLastSelection = kCreditsMenuMainMenu +}; + +static const CoordType kCreditsMovieLeft = 288; +static const CoordType kCreditsMovieTop = 0; + +static const CoordType kCoreTeamSelectLeft = 40; +static const CoordType kCoreTeamSelectTop = 223; + +static const CoordType kSupportTeamSelectLeft = 40; +static const CoordType kSupportTeamSelectTop = 259; + +static const CoordType kOriginalTeamSelectLeft = 40; +static const CoordType kOriginalTeamSelectTop = 295; + +static const CoordType kTalentSelectLeft = 40; +static const CoordType kTalentSelectTop = 331; + +static const CoordType kOtherTitlesSelectLeft = 40; +static const CoordType kOtherTitlesSelectTop = 367; + +static const CoordType kCreditsMainMenuLeft = 32; +static const CoordType kCreditsMainMenuTop = 412; + +static const CoordType kCreditsMainMenuSelectLeft = 30; +static const CoordType kCreditsMainMenuSelectTop = 408; + +static const TimeValue kCoreTeamTime = 0; +static const TimeValue kSupportTeamTime = 1920; +static const TimeValue kOriginalTeamTime = 3000; +static const TimeValue kTalentTime = 4440; +static const TimeValue kOtherTitlesTime = 4680; + +static const TimeValue kFrameIncrement = 120; // Three frames... + +// Never set the current input handler to the CreditsMenu. +CreditsMenu::CreditsMenu() : GameMenu(kCreditsMenuID), _menuBackground(0), _creditsMovie(0), + _mainMenuButton(0), _largeSelect(0), _smallSelect(0) { + + _menuBackground.initFromPICTFile("Images/Credits/CredScrn.pict"); + _menuBackground.setDisplayOrder(0); + _menuBackground.startDisplaying(); + _menuBackground.show(); + + _creditsMovie.initFromMovieFile("Images/Credits/Credits.movie"); + _creditsMovie.setDisplayOrder(1); + _creditsMovie.moveElementTo(kCreditsMovieLeft, kCreditsMovieTop); + _creditsMovie.startDisplaying(); + _creditsMovie.show(); + _creditsMovie.redrawMovieWorld(); + + _mainMenuButton.initFromPICTFile("Images/Credits/MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kCreditsMainMenuLeft, kCreditsMainMenuTop); + _mainMenuButton.startDisplaying(); + + _largeSelect.initFromPICTFile("Images/Credits/SelectL.pict", true); + _largeSelect.setDisplayOrder(2); + _largeSelect.moveElementTo(kCreditsMainMenuSelectLeft, kCreditsMainMenuSelectTop); + _largeSelect.startDisplaying(); + + _smallSelect.initFromPICTFile("Images/Credits/SelectS.pict", true); + _smallSelect.setDisplayOrder(2); + _smallSelect.show(); + _smallSelect.startDisplaying(); + + _menuSelection = -1; + + newMenuSelection(kCreditsMenuCoreTeam); +} + +// Assumes the new selection is never more than one away from the old... +void CreditsMenu::newMenuSelection(const int newSelection) { + if (newSelection != _menuSelection) { + switch (newSelection) { + case kCreditsMenuCoreTeam: + _smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop); + _creditsMovie.setTime(kCoreTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuSupportTeam: + _smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop); + _creditsMovie.setTime(kSupportTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuOriginalTeam: + _smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop); + _creditsMovie.setTime(kOriginalTeamTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuTalent: + _smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop); + _creditsMovie.setTime(kTalentTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuOtherTitles: + _smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _creditsMovie.setTime(kOtherTitlesTime); + _creditsMovie.redrawMovieWorld(); + break; + case kCreditsMenuMainMenu: + _smallSelect.hide(); + _largeSelect.show(); + break; + } + + _menuSelection = newSelection; + } +} + +void CreditsMenu::newMovieTime(const TimeValue newTime) { + if (newTime < kSupportTeamTime) { + _smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop); + _menuSelection = kCreditsMenuCoreTeam; + } else if (newTime < kOriginalTeamTime) { + _smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop); + _menuSelection = kCreditsMenuSupportTeam; + } else if (newTime < kTalentTime) { + _smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop); + _menuSelection = kCreditsMenuOriginalTeam; + } else if (newTime < kOtherTitlesTime) { + _smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _menuSelection = kCreditsMenuTalent; + } else if ((int)newTime == -120) { + // HACK: Avoid getting sent to the bottom button in the default case + return; + } else { + _smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + _menuSelection = kCreditsMenuOtherTitles; + } + + _creditsMovie.setTime(newTime); + _creditsMovie.redrawMovieWorld(); +} + +void CreditsMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (input.upButtonDown()) { + if (_menuSelection > kCreditsFirstSelection) + newMenuSelection(_menuSelection - 1); + } else if (input.downButtonDown()) { + if (_menuSelection < kCreditsLastSelection) + newMenuSelection(_menuSelection + 1); + } else if (input.leftButtonDown()) { + newMovieTime(_creditsMovie.getTime() - kFrameIncrement); + } else if (input.rightButtonDown()) { + newMovieTime(_creditsMovie.getTime() + kFrameIncrement); + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (_menuSelection == kCreditsMenuMainMenu) { + _mainMenuButton.show(); + ((PegasusEngine *)g_engine)->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdCreditsMainMenu); + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +static const CoordType kContinueLeft = 44; +static const CoordType kContinueTop = 336; + +static const CoordType kContinueSelectLeft = 40; +static const CoordType kContinueSelectTopDemo = 331; +static const CoordType kContinueSelectTop = 332; + +static const CoordType kMainMenuLeftDemo = 44; +static const CoordType kMainMenuTopDemo = 372; + +static const CoordType kMainMenuSelectLeftDemo = 40; +static const CoordType kMainMenuSelectTopDemo = 367; + +static const CoordType kQuitLeftDemo = 32; +static const CoordType kQuitTopDemo = 412; + +static const CoordType kQuitSelectLeftDemo = 28; +static const CoordType kQuitSelectTopDemo = 408; + +static const CoordType kRestoreLeftDeath = 44; +static const CoordType kRestoreTopDeath = 372; + +static const CoordType kRestoreSelectLeftDeath = 40; +static const CoordType kRestoreSelectTopDeath = 368; + +static const CoordType kMainMenuLeft = 32; +static const CoordType kMainMenuTop = 412; + +static const CoordType kMainMenuSelectLeft = 28; +static const CoordType kMainMenuSelectTop = 408; + +enum { + kDeathScreenContinueDemo = 0, + kDeathScreenMainMenuDemo, + kDeathScreenQuitDemo, + + kFirstDeathSelectionDemo = kDeathScreenContinueDemo, + kLastDeathSelectionDemo = kDeathScreenQuitDemo, + + kDeathScreenContinue = 0, + kDeathScreenRestore, + kDeathScreenMainMenu, + + kFirstDeathSelection = kDeathScreenContinue, + kLastDeathSelection = kDeathScreenMainMenu +}; + +// Never set the current input handler to the DeathMenu. +DeathMenu::DeathMenu(const DeathReason deathReason) : GameMenu(kDeathMenuID), _deathBackground(0), _continueButton(0), + _mainMenuButton(0), _quitButton(0), _restoreButton(0), _largeSelect(0), _smallSelect(0) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + bool isDemo = vm->isDemo(); + + _playerWon = (deathReason == kPlayerWonGame); + + Common::String prefix = "Images/"; + Common::String imageName; + if (isDemo) { + prefix += "Demo/"; + imageName = prefix; + + switch (deathReason) { + case kDeathFallOffCliff: + imageName += "dPfall"; + break; + case kDeathEatenByDinosaur: + imageName += "dPdino"; + break; + case kDeathStranded: + imageName += "dPstuck"; + break; + default: + imageName += "dPdemowin"; + break; + } + + imageName += ".pict"; + } else { + prefix += "Death Screens/"; + imageName = prefix; + + static const char *fileNames[] = { + "dAunmade", "dAbombed", "dAshot", "dAassass", "dAnuked", + "dTunmade", "dTshot", "dPfall", "dPdino", "dPstuck", + "dNchoke", "dNcaught", "dNcaught", "dNsub", "dNrobot1", + "dNrobot2", "dMfall", "dMcaught", "dMtracks", "dMrobot", + "dMtoast", "dMexplo1", "dMexplo2", "dMchoke1", "dMchoke2", + "dMdroid", "dMfall", "dMgears", "dMshutt1", "dMshutt2", + "dWpoison", "dWcaught", "dWplasma", "dWshot", "dAfinale" + }; + + imageName += fileNames[deathReason - 1]; + imageName += ".pict"; + } + + _deathBackground.initFromPICTFile(imageName); + _deathReason = deathReason; + + if (!isDemo) { + vm->_gfx->setCurSurface(_deathBackground.getSurface()); + drawAllScores(); + vm->_gfx->setCurSurface(vm->_gfx->getWorkArea()); + } + + _deathBackground.setDisplayOrder(0); + _deathBackground.startDisplaying(); + _deathBackground.show(); + + if (isDemo) { + if (_playerWon) // Make credits button... + _continueButton.initFromPICTFile(prefix + "Credits.pict"); + else // Make continue button... + _continueButton.initFromPICTFile(prefix + "Continue.pict"); + + _mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kMainMenuLeftDemo, kMainMenuTopDemo); + _mainMenuButton.startDisplaying(); + + _quitButton.initFromPICTFile(prefix + "Quit.pict"); + _quitButton.setDisplayOrder(1); + _quitButton.moveElementTo(kQuitLeftDemo, kQuitTopDemo); + _quitButton.startDisplaying(); + + _menuSelection = kDeathScreenContinueDemo; + } else { + if (!_playerWon) { + _mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict"); + _mainMenuButton.setDisplayOrder(1); + _mainMenuButton.moveElementTo(kMainMenuLeft, kMainMenuTop); + _mainMenuButton.startDisplaying(); + + _restoreButton.initFromPICTFile(prefix + "Restore.pict"); + _restoreButton.setDisplayOrder(1); + _restoreButton.moveElementTo(kRestoreLeftDeath, kRestoreTopDeath); + _restoreButton.startDisplaying(); + } + + _continueButton.initFromPICTFile(prefix + "Continue.pict"); + + _menuSelection = kDeathScreenContinue; + } + + _smallSelect.initFromPICTFile(prefix + "SelectS.pict", true); + _smallSelect.setDisplayOrder(2); + _smallSelect.startDisplaying(); + + _continueButton.setDisplayOrder(1); + _continueButton.moveElementTo(kContinueLeft, kContinueTop); + _continueButton.startDisplaying(); + + if (isDemo || !_playerWon) { + _largeSelect.initFromPICTFile(prefix + "SelectL.pict", true); + _largeSelect.setDisplayOrder(2); + _largeSelect.startDisplaying(); + } else { + _triumphSound.initFromQuickTime("Sounds/Caldoria/Galactic Triumph"); + _triumphSound.playSound(); + } + + updateDisplay(); +} + +void DeathMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (input.upButtonDown()) { + if (_menuSelection > (vm->isDemo() ? kFirstDeathSelectionDemo : kFirstDeathSelection)) { + _menuSelection--; + updateDisplay(); + } + } else if (input.downButtonDown() && (vm->isDemo() || !_playerWon)) { + if (_menuSelection < (vm->isDemo() ? kLastDeathSelectionDemo : kLastDeathSelection)) { + _menuSelection++; + updateDisplay(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + if (vm->isDemo()) { + switch (_menuSelection) { + case kDeathScreenContinueDemo: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdDeathContinue); + break; + case kDeathScreenQuitDemo: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdDeathQuitDemo); + break; + case kDeathScreenMainMenuDemo: + _mainMenuButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdDeathMainMenuDemo); + break; + } + } else { + switch (_menuSelection) { + case kDeathScreenContinue: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdDeathContinue); + break; + case kDeathScreenRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdDeathRestore); + break; + case kDeathScreenMainMenu: + _mainMenuButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _mainMenuButton.hide(); + setLastCommand(kMenuCmdDeathMainMenu); + break; + } + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void DeathMenu::updateDisplay() { + if (((PegasusEngine *)g_engine)->isDemo()) { + switch (_menuSelection) { + case kDeathScreenContinueDemo: + _smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenQuitDemo: + _largeSelect.moveElementTo(kQuitSelectLeftDemo, kQuitSelectTopDemo); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kDeathScreenMainMenuDemo: + _smallSelect.moveElementTo(kMainMenuSelectLeftDemo, kMainMenuSelectTopDemo); + _smallSelect.show(); + _largeSelect.hide(); + break; + } + } else { + switch (_menuSelection) { + case kDeathScreenContinue: + _smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenRestore: + _smallSelect.moveElementTo(kRestoreSelectLeftDeath, kRestoreSelectTopDeath); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kDeathScreenMainMenu: + _largeSelect.moveElementTo(kMainMenuSelectLeft, kMainMenuSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + } + } +} + +void DeathMenu::drawAllScores() { + Surface numbers; + numbers.getImageFromPICTFile("Images/Death Screens/Numbers.pict"); + + Common::Rect scoreBounds; + GameScoreType caldoriaTotal = 0; + + switch (_deathReason) { + case kDeathCardBomb: + case kDeathShotBySinclair: + case kDeathSinclairShotDelegate: + case kDeathNuclearExplosion: + case kDeathGassedInNorad: + case kDeathWokeUpNorad: + case kDeathArrestedInNorad: + case kDeathSubDestroyed: + case kDeathRobotThroughNoradDoor: + case kDeathRobotSubControlRoom: + case kDeathWrongShuttleLock: + case kDeathArrestedInMars: + case kDeathRunOverByPod: + case kDeathDidntGetOutOfWay: + case kDeathReactorBurn: + case kDeathDidntFindMarsBomb: + case kDeathDidntDisarmMarsBomb: + case kDeathNoMaskInMaze: + case kDeathNoAirInMaze: + case kDeathGroundByMazebot: + case kDeathMissedOreBucket: + case kDeathDidntLeaveBucket: + case kDeathRanIntoCanyonWall: + case kDeathRanIntoSpaceJunk: + case kDeathDidntStopPoison: + case kDeathArrestedInWSC: + case kDeathHitByPlasma: + case kDeathShotOnCatwalk: + case kPlayerWonGame: + caldoriaTotal += kMaxCaldoriaTSAScoreAfter; + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5 + kDeathScreenScoreHeight); + drawScore(GameState.getGandhiScore(), kMaxGandhiScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getWSCScore(), kMaxWSCScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getNoradScore(), kMaxNoradScore, scoreBounds, &numbers); + + scoreBounds.translate(0, kDeathScreenScoreSkipVert); + drawScore(GameState.getMarsScore(), kMaxMarsScore, scoreBounds, &numbers); + // fall through + case kDeathFallOffCliff: + case kDeathEatenByDinosaur: + case kDeathStranded: + case kDeathShotByTSARobots: + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert + kDeathScreenScoreHeight); + drawScore(GameState.getPrehistoricScore(), kMaxPrehistoricScore, scoreBounds, &numbers); + // fall through + case kDeathUncreatedInCaldoria: + case kDeathUncreatedInTSA: + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop, kDeathScreenScoreLeft + kDeathScreenScoreWidth, + kDeathScreenScoreTop + kDeathScreenScoreHeight); + caldoriaTotal += kMaxCaldoriaTSAScoreBefore; + drawScore(GameState.getCaldoriaTSAScore(), caldoriaTotal, scoreBounds, &numbers); + + scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6, + kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6 + kDeathScreenScoreHeight); + + drawScore(GameState.getTotalScore(), kMaxTotalScore, scoreBounds, &numbers); + break; + } +} + +enum { + kPauseMenuSave, + kPauseMenuContinue, + kPauseMenuRestore, + kPauseMenuSoundFX, + kPauseMenuAmbience, + kPauseMenuWalkthru, + kPauseMenuQuitToMainMenu, + + kFirstPauseSelection = kPauseMenuSave, + kLastPauseSelection = kPauseMenuQuitToMainMenu +}; + +static const CoordType kPauseLeft = 194; +static const CoordType kPauseTop = 68; + +static const CoordType kSaveGameLeft = kPauseLeft + 6; +static const CoordType kSaveGameTop = kPauseTop + 56; + +static const CoordType kSaveGameSelectLeft = kPauseLeft - 44; +static const CoordType kSaveGameSelectTop = kPauseTop + 52; + +static const CoordType kPauseContinueLeft = kPauseLeft + 18; +static const CoordType kPauseContinueTop = kPauseTop + 100; + +static const CoordType kPauseContinueSelectLeft = kPauseLeft - 44; +static const CoordType kPauseContinueSelectTop = kPauseTop + 95; + +static const CoordType kPauseRestoreLeft = kPauseLeft + 18; +static const CoordType kPauseRestoreTop = kPauseTop + 136; + +static const CoordType kPauseRestoreSelectLeft = kPauseLeft - 44; +static const CoordType kPauseRestoreSelectTop = kPauseTop + 131; + +static const CoordType kSoundFXLeft = kPauseLeft + 128; +static const CoordType kSoundFXTop = kPauseTop + 187; +static const CoordType kSoundFXRight = kSoundFXLeft + 96; +static const CoordType kSoundFXBottom = kSoundFXTop + 14; + +static const CoordType kSoundFXSelectLeft = kPauseLeft - 44; +static const CoordType kSoundFXSelectTop = kPauseTop + 172; + +static const CoordType kAmbienceLeft = kPauseLeft + 128; +static const CoordType kAmbienceTop = kPauseTop + 227; +static const CoordType kAmbienceRight = kAmbienceLeft + 96; +static const CoordType kAmbienceBottom = kAmbienceTop + 14; + +static const CoordType kAmbienceSelectLeft = kPauseLeft - 44; +static const CoordType kAmbienceSelectTop = kPauseTop + 212; + +static const CoordType kWalkthruLeft = kPauseLeft + 128; +static const CoordType kWalkthruTop = kPauseTop + 264; + +static const CoordType kWalkthruSelectLeft = kPauseLeft - 44; +static const CoordType kWalkthruSelectTop = kPauseTop + 255; + +static const CoordType kQuitLeft = kPauseLeft + 18; +static const CoordType kQuitTop = kPauseTop + 302; + +static const CoordType kQuitSelectLeft = kPauseLeft - 44; +static const CoordType kQuitSelectTop = kPauseTop + 297; + +// These are relative to the pause background. +static const CoordType kPauseScoreLeft = 130; +static const CoordType kPauseScoreTop = 34; +static const CoordType kPauseScoreRight = kPauseScoreLeft + 108; +static const CoordType kPauseScoreBottom = kPauseScoreTop + 12; + +// Never set the current input handler to the CPauseMenu. +PauseMenu::PauseMenu() : GameMenu(kPauseMenuID), _pauseBackground(0), _saveButton(0), _restoreButton(0), + _walkthroughButton(0), _continueButton(0), _soundFXLevel(0), _ambienceLevel(0), _quitButton(0), + _largeSelect(0), _smallSelect(0) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + _pauseBackground.initFromPICTFile("Images/Pause Screen/PausScrn.pict", true); + + if (!vm->isDemo()) { + Surface numbers; + numbers.getImageFromPICTFile("Images/Pause Screen/Numbers.pict"); + vm->_gfx->setCurSurface(_pauseBackground.getSurface()); + drawScore(GameState.getTotalScore(), kMaxTotalScore, + Common::Rect(kPauseScoreLeft, kPauseScoreTop, kPauseScoreRight, kPauseScoreBottom), &numbers); + vm->_gfx->setCurSurface(vm->_gfx->getWorkArea()); + } + + _pauseBackground.setDisplayOrder(kPauseMenuOrder); + _pauseBackground.moveElementTo(kPauseLeft, kPauseTop); + _pauseBackground.startDisplaying(); + _pauseBackground.show(); + + if (!vm->isDemo()) { + _saveButton.initFromPICTFile("Images/Pause Screen/SaveGame.pict"); + _saveButton.setDisplayOrder(kSaveGameOrder); + _saveButton.moveElementTo(kSaveGameLeft, kSaveGameTop); + _saveButton.startDisplaying(); + + _restoreButton.initFromPICTFile("Images/Pause Screen/Restore.pict"); + _restoreButton.setDisplayOrder(kRestoreOrder); + _restoreButton.moveElementTo(kPauseRestoreLeft, kPauseRestoreTop); + _restoreButton.startDisplaying(); + + _walkthroughButton.initFromPICTFile("Images/Pause Screen/Walkthru.pict"); + _walkthroughButton.setDisplayOrder(kWalkthruOrder); + _walkthroughButton.moveElementTo(kWalkthruLeft, kWalkthruTop); + _walkthroughButton.startDisplaying(); + + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + } + + _continueButton.initFromPICTFile("Images/Pause Screen/Continue.pict"); + _continueButton.setDisplayOrder(kContinueOrder); + _continueButton.moveElementTo(kPauseContinueLeft, kPauseContinueTop); + _continueButton.startDisplaying(); + + _soundFXLevel.setDisplayOrder(kSoundFXOrder); + _soundFXLevel.setBounds(Common::Rect(kSoundFXLeft, kSoundFXTop, kSoundFXRight, kSoundFXBottom)); + _soundFXLevel.startDisplaying(); + _soundFXLevel.show(); + _soundFXLevel.setSoundLevel(vm->getSoundFXLevel()); + + _ambienceLevel.setDisplayOrder(kAmbienceOrder); + _ambienceLevel.setBounds(Common::Rect(kAmbienceLeft, kAmbienceTop, kAmbienceRight, kAmbienceBottom)); + _ambienceLevel.startDisplaying(); + _ambienceLevel.show(); + _ambienceLevel.setSoundLevel(vm->getAmbienceLevel()); + + _quitButton.initFromPICTFile("Images/Pause Screen/Quit2MM.pict"); + _quitButton.setDisplayOrder(kQuitToMainMenuOrder); + _quitButton.moveElementTo(kQuitLeft, kQuitTop); + _quitButton.startDisplaying(); + + _largeSelect.initFromPICTFile("Images/Pause Screen/SelectL.pict", true); + _largeSelect.setDisplayOrder(kPauseLargeHiliteOrder); + _largeSelect.startDisplaying(); + + _smallSelect.initFromPICTFile("Images/Pause Screen/SelectS.pict", true); + _smallSelect.setDisplayOrder(kPauseSmallHiliteOrder); + _smallSelect.startDisplaying(); + + _menuSelection = (vm->isDemo()) ? kPauseMenuContinue : kPauseMenuSave; + + updateDisplay(); +} + +void PauseMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + if (input.upButtonDown()) { + if (vm->isDemo()) { + if (_menuSelection > kPauseMenuContinue) { + switch (_menuSelection) { + case kPauseMenuSoundFX: + _menuSelection = kPauseMenuContinue; + break; + case kPauseMenuAmbience: + _menuSelection = kPauseMenuSoundFX; + break; + case kPauseMenuQuitToMainMenu: + _menuSelection = kPauseMenuAmbience; + break; + } + updateDisplay(); + } + } else { + if (_menuSelection > kFirstPauseSelection) { + _menuSelection--; + updateDisplay(); + } + } + } else if (input.downButtonDown()) { + if (vm->isDemo()) { + if (_menuSelection < kPauseMenuQuitToMainMenu) { + switch (_menuSelection) { + case kPauseMenuContinue: + _menuSelection = kPauseMenuSoundFX; + break; + case kPauseMenuSoundFX: + _menuSelection = kPauseMenuAmbience; + break; + case kPauseMenuAmbience: + _menuSelection = kPauseMenuQuitToMainMenu; + break; + } + updateDisplay(); + } + } else { + if (_menuSelection < kLastPauseSelection) { + _menuSelection++; + updateDisplay(); + } + } + } else if (input.leftButtonDown()) { + if (_menuSelection == kPauseMenuSoundFX) { + _soundFXLevel.decrementLevel(); + vm->setSoundFXLevel(_soundFXLevel.getSoundLevel()); + } else if (_menuSelection == kPauseMenuAmbience) { + _ambienceLevel.decrementLevel(); + vm->setAmbienceLevel(_ambienceLevel.getSoundLevel()); + } else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) { + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + } + } else if (input.rightButtonDown()) { + if (_menuSelection == kPauseMenuSoundFX) { + _soundFXLevel.incrementLevel(); + vm->setSoundFXLevel(_soundFXLevel.getSoundLevel()); + } else if (_menuSelection == kPauseMenuAmbience) { + _ambienceLevel.incrementLevel(); + vm->setAmbienceLevel(_ambienceLevel.getSoundLevel()); + } else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) { + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + } + } else if (JMPPPInput::isMenuButtonPressInput(input)) { + switch (_menuSelection) { + case kPauseMenuSave: + _saveButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _saveButton.hide(); + setLastCommand(kMenuCmdPauseSave); + break; + case kPauseMenuRestore: + _restoreButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _restoreButton.hide(); + setLastCommand(kMenuCmdPauseRestore); + break; + case kPauseMenuContinue: + _continueButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _continueButton.hide(); + setLastCommand(kMenuCmdPauseContinue); + break; + case kPauseMenuWalkthru: + GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); + if (GameState.getWalkthroughMode()) + _walkthroughButton.show(); + else + _walkthroughButton.hide(); + break; + case kPauseMenuQuitToMainMenu: + _quitButton.show(); + vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); + _quitButton.hide(); + setLastCommand(kMenuCmdPauseQuit); + break; + } + } + + InputHandler::handleInput(input, cursorSpot); +} + +void PauseMenu::updateDisplay() { + switch (_menuSelection) { + case kPauseMenuSave: + _largeSelect.moveElementTo(kSaveGameSelectLeft, kSaveGameSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuContinue: + _smallSelect.moveElementTo(kPauseContinueSelectLeft, kPauseContinueSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kPauseMenuRestore: + _smallSelect.moveElementTo(kPauseRestoreSelectLeft, kPauseRestoreSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + case kPauseMenuSoundFX: + _largeSelect.moveElementTo(kSoundFXSelectLeft, kSoundFXSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuAmbience: + _largeSelect.moveElementTo(kAmbienceSelectLeft, kAmbienceSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuWalkthru: + _largeSelect.moveElementTo(kWalkthruSelectLeft, kWalkthruSelectTop); + _largeSelect.show(); + _smallSelect.hide(); + break; + case kPauseMenuQuitToMainMenu: + _smallSelect.moveElementTo(kQuitSelectLeft, kQuitSelectTop); + _smallSelect.show(); + _largeSelect.hide(); + break; + } + + ((PegasusEngine *)g_engine)->resetIntroTimer(); +} + + +} // End of namespace Pegasus diff --git a/engines/pegasus/menu.h b/engines/pegasus/menu.h new file mode 100644 index 0000000000..288b846093 --- /dev/null +++ b/engines/pegasus/menu.h @@ -0,0 +1,171 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_MENU_H +#define PEGASUS_MENU_H + +#include "pegasus/constants.h" +#include "pegasus/fader.h" +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/sound.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class GameMenu : public IDObject, public InputHandler { +public: + GameMenu(const uint32); + virtual ~GameMenu() {} + + virtual void becomeCurrentHandler(); + virtual void restorePreviousHandler(); + + GameMenuCommand getLastCommand() { return _lastCommand; } + void clearLastCommand() { _lastCommand = kMenuCmdNoCommand; } + +protected: + void setLastCommand(const GameMenuCommand command) { _lastCommand = command; } + + InputHandler *_previousHandler; + GameMenuCommand _lastCommand; + + void drawScore(GameScoreType, GameScoreType, const Common::Rect &, Surface *); + +private: + void drawNumber(GameScoreType, CoordType &, CoordType, Surface *); +}; + +class Hotspot; + +class MainMenu : public GameMenu { +public: + MainMenu(); + virtual ~MainMenu(); + + virtual void handleInput(const Input &input, const Hotspot *); + void startMainMenuLoop(); + void stopMainMenuLoop(); + +protected: + void updateDisplay(); + + uint32 _menuSelection; + + // Full and Demo + Picture _menuBackground; + Picture _startButton; + Picture _creditsButton; + Picture _quitButton; + Picture _largeSelect; + Picture _smallSelect; + + // Full only + bool _adventureMode; + Picture _overviewButton; + Picture _restoreButton; + Picture _adventureButton; + Picture _walkthroughButton; + + Sound _menuLoop; + SoundFader _menuFader; +}; + +class CreditsMenu : public GameMenu { +public: + CreditsMenu(); + virtual ~CreditsMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + +protected: + void newMenuSelection(const int); + void newMovieTime(const TimeValue); + + int _menuSelection; + Picture _menuBackground; + Movie _creditsMovie; + Picture _mainMenuButton; + Picture _largeSelect; + Picture _smallSelect; +}; + +class DeathMenu : public GameMenu { +public: + DeathMenu(const DeathReason); + virtual ~DeathMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + + bool playerWon() { return _playerWon; } + +protected: + void drawAllScores(); + + void updateDisplay(); + + bool _playerWon; + int _menuSelection; + DeathReason _deathReason; + + Picture _deathBackground; + Picture _continueButton; + Picture _restoreButton; + Picture _mainMenuButton; + Picture _quitButton; + + Picture _largeSelect; + Picture _smallSelect; + + Sound _triumphSound; +}; + +class PauseMenu : public GameMenu { +public: + PauseMenu(); + virtual ~PauseMenu() {} + + virtual void handleInput(const Input &input, const Hotspot *); + +protected: + void updateDisplay(); + + uint32 _menuSelection; + Picture _pauseBackground; + Picture _saveButton; + Picture _restoreButton; + Picture _walkthroughButton; + Picture _continueButton; + SoundLevel _soundFXLevel; + SoundLevel _ambienceLevel; + Picture _quitButton; + Picture _largeSelect; + Picture _smallSelect; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk new file mode 100644 index 0000000000..cb44a04171 --- /dev/null +++ b/engines/pegasus/module.mk @@ -0,0 +1,100 @@ +MODULE := engines/pegasus + +MODULE_OBJS = \ + compass.o \ + console.o \ + cursor.o \ + detection.o \ + elements.o \ + energymonitor.o \ + fader.o \ + gamestate.o \ + graphics.o \ + hotspot.o \ + input.o \ + interface.o \ + menu.o \ + movie.o \ + notification.o \ + pegasus.o \ + sound.o \ + surface.o \ + timers.o \ + transition.o \ + util.o \ + ai/ai_action.o \ + ai/ai_area.o \ + ai/ai_condition.o \ + ai/ai_rule.o \ + items/autodragger.o \ + items/inventory.o \ + items/inventorypicture.o \ + items/item.o \ + items/itemdragger.o \ + items/itemlist.o \ + items/biochips/aichip.o \ + items/biochips/biochipitem.o \ + items/biochips/mapchip.o \ + items/biochips/mapimage.o \ + items/biochips/opticalchip.o \ + items/biochips/pegasuschip.o \ + items/biochips/retscanchip.o \ + items/biochips/shieldchip.o \ + items/inventory/airmask.o \ + items/inventory/gascanister.o \ + items/inventory/inventoryitem.o \ + items/inventory/keycard.o \ + neighborhood/door.o \ + neighborhood/exit.o \ + neighborhood/extra.o \ + neighborhood/hotspotinfo.o \ + neighborhood/neighborhood.o \ + neighborhood/spot.o \ + neighborhood/turn.o \ + neighborhood/view.o \ + neighborhood/zoom.o \ + neighborhood/caldoria/caldoria.o \ + neighborhood/caldoria/caldoria4dsystem.o \ + neighborhood/caldoria/caldoriabomb.o \ + neighborhood/caldoria/caldoriamessages.o \ + neighborhood/caldoria/caldoriamirror.o \ + neighborhood/mars/energybeam.o \ + neighborhood/mars/gravitoncannon.o \ + neighborhood/mars/hermite.o \ + neighborhood/mars/mars.o \ + neighborhood/mars/planetmover.o \ + neighborhood/mars/reactor.o \ + neighborhood/mars/robotship.o \ + neighborhood/mars/shuttleenergymeter.o \ + neighborhood/mars/shuttlehud.o \ + neighborhood/mars/shuttleweapon.o \ + neighborhood/mars/spacechase3d.o \ + neighborhood/mars/spacejunk.o \ + neighborhood/mars/tractorbeam.o \ + neighborhood/norad/norad.o \ + neighborhood/norad/noradelevator.o \ + neighborhood/norad/pressuredoor.o \ + neighborhood/norad/pressuretracker.o \ + neighborhood/norad/subcontrolroom.o \ + neighborhood/norad/subplatform.o \ + neighborhood/norad/alpha/ecrmonitor.o \ + neighborhood/norad/alpha/fillingstation.o \ + neighborhood/norad/alpha/noradalpha.o \ + neighborhood/norad/alpha/panorama.o \ + neighborhood/norad/alpha/panoramascroll.o \ + neighborhood/norad/delta/globegame.o \ + neighborhood/norad/delta/noraddelta.o \ + neighborhood/prehistoric/prehistoric.o \ + neighborhood/tsa/fulltsa.o \ + neighborhood/tsa/tinytsa.o \ + neighborhood/wsc/moleculebin.o \ + neighborhood/wsc/wsc.o + + +# This module can be built as a plugin +ifeq ($(ENABLE_PEGASUS), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/pegasus/movie.cpp b/engines/pegasus/movie.cpp new file mode 100644 index 0000000000..75c287c7a6 --- /dev/null +++ b/engines/pegasus/movie.cpp @@ -0,0 +1,280 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "graphics/surface.h" +#include "video/qt_decoder.h" +#include "video/video_decoder.h" + +#include "pegasus/movie.h" + +namespace Pegasus { + +Movie::Movie(const DisplayElementID id) : Animation(id) { + _video = 0; + setScale(600); +} + +Movie::~Movie() { + releaseMovie(); +} + +// *** Make sure this will stop displaying the movie. + +void Movie::releaseMovie() { + if (_video) { + delete _video; + _video = 0; + disposeAllCallBacks(); + deallocateSurface(); + } + + setBounds(Common::Rect(0, 0, 0, 0)); +} + +void Movie::initFromMovieFile(const Common::String &fileName, bool transparent) { + _transparent = transparent; + + releaseMovie(); + _video = new Video::QuickTimeDecoder(); + if (!_video->loadFile(fileName)) { + // Replace any colon with an underscore, since only Mac OS X + // supports that. See PegasusEngine::detectOpeningClosingDirectory() + // for more info. + Common::String newName(fileName); + if (newName.contains(':')) + for (uint i = 0; i < newName.size(); i++) + if (newName[i] == ':') + newName.setChar('_', i); + + if (!_video->loadFile(newName)) + error("Could not load video '%s'", fileName.c_str()); + } + + Common::Rect bounds(0, 0, _video->getWidth(), _video->getHeight()); + sizeElement(_video->getWidth(), _video->getHeight()); + _movieBox = bounds; + + if (!isSurfaceValid()) + allocateSurface(bounds); + + setStart(0, getScale()); + TimeBase::setStop(_video->getDuration().convertToFramerate(getScale()).totalNumberOfFrames(), getScale()); +} + +void Movie::redrawMovieWorld() { + if (_video && _video->needsUpdate()) { + const Graphics::Surface *frame = _video->decodeNextFrame(); + + if (!frame) + return; + + // Make sure we have a surface in the current pixel format + Graphics::Surface *convertedFrame = 0; + + if (frame->format != g_system->getScreenFormat()) { + convertedFrame = frame->convertTo(g_system->getScreenFormat()); + frame = convertedFrame; + } + + // Copy to the surface using _movieBox + uint16 width = MIN<int>(frame->w, _movieBox.width()); + uint16 height = MIN<int>(frame->h, _movieBox.height()); + + for (uint16 y = 0; y < height; y++) + memcpy((byte *)_surface->getBasePtr(_movieBox.left, _movieBox.top + y), (const byte *)frame->getBasePtr(0, y), width * frame->format.bytesPerPixel); + + if (convertedFrame) { + convertedFrame->free(); + delete convertedFrame; + } + + triggerRedraw(); + } +} + +void Movie::draw(const Common::Rect &r) { + Common::Rect worldBounds = _movieBox; + Common::Rect elementBounds; + getBounds(elementBounds); + + worldBounds.moveTo(elementBounds.left, elementBounds.top); + Common::Rect r1 = r.findIntersectingRect(worldBounds); + + Common::Rect r2 = r1; + r2.translate(_movieBox.left - elementBounds.left, _movieBox.top - elementBounds.top); + drawImage(r2, r1); +} + +void Movie::moveMovieBoxTo(const CoordType h, const CoordType v) { + _movieBox.moveTo(h, v); +} + +void Movie::setStop(const TimeValue stopTime, const TimeScale scale) { + TimeBase::setStop(stopTime, scale); + + if (_video) + _video->setEndTime(Audio::Timestamp(0, _stopTime, _stopScale)); +} + +void Movie::setVolume(uint16 volume) { + if (_video) + _video->setVolume(MIN<uint>(volume, 0xFF)); +} + +void Movie::setTime(const TimeValue time, const TimeScale scale) { + if (_video) { + // Don't go past the ends of the movie + Common::Rational timeFrac = Common::Rational(time, ((scale == 0) ? getScale() : scale)); + + if (timeFrac < Common::Rational(_startTime, _startScale)) + timeFrac = Common::Rational(_startTime, _startScale); + else if (timeFrac >= Common::Rational(_stopTime, _stopScale)) + return; + + _video->seek(Audio::Timestamp(0, timeFrac.getNumerator(), timeFrac.getDenominator())); + _time = timeFrac; + _lastMillis = 0; + } +} + +void Movie::setRate(const Common::Rational rate) { + if (rate != 1 && rate != 0) { + warning("Cannot set movie rate"); + start(); + return; + } + + TimeBase::setRate(rate); +} + +void Movie::start() { + if (_video) + _video->start(); + + TimeBase::start(); +} + +void Movie::stop() { + if (_video) + _video->stop(); + + TimeBase::stop(); +} + +void Movie::resume() { + if (_paused) { + if (_video) + _video->pauseVideo(false); + + _paused = false; + } +} + +void Movie::pause() { + if (isRunning() && !_paused) { + if (_video) + _video->pauseVideo(true); + + _paused = true; + _pauseStart = g_system->getMillis(); + } +} + +TimeValue Movie::getDuration(const TimeScale scale) const { + // Unlike TimeBase::getDuration(), this returns the whole duration of the movie + // The original source has a TODO to make this behave like TimeBase::getDuration(), + // but the problem is that too much code requires this function to behave this way... + + if (_video) + return _video->getDuration().convertToFramerate(((scale == 0) ? getScale() : scale)).totalNumberOfFrames(); + + return 0; +} + +void Movie::updateTime() { + // The reason why we overrode TimeBase's updateTime(): + // Again, avoiding timers and handling it here + if (_video && _video->isPlaying() && !_video->isPaused()) { + redrawMovieWorld(); + + uint32 startTime = _startTime * getScale() / _startScale; + uint32 stopTime = _stopTime * getScale() / _stopScale; + uint32 actualTime = CLIP<int>(_video->getTime() * getScale() / 1000, startTime, stopTime); + + // HACK: Due to the inaccuracy of the time, we won't actually allow us to hit + // the stop time unless we've actually reached the end of the segment. + if (actualTime == stopTime && !_video->endOfVideo()) + actualTime--; + + _time = Common::Rational(actualTime, getScale()); + } +} + +GlowingMovie::GlowingMovie(const DisplayElementID id) : Movie(id) { + _glowing = false; +} + +void GlowingMovie::draw(const Common::Rect &r) { + // Make sure the rectangles are clipped properly, OR guarantee that _bounds will + // never fall off the screen. + if (_glowing) { + Common::Rect bounds; + getBounds(bounds); + + copyToCurrentPortTransparentGlow(_movieBox, bounds); + } else { + Movie::draw(r); + } +} + +void GlowingMovie::setBounds(const Common::Rect &r) { + Common::Rect bounds; + getBounds(bounds); + + if (r != bounds) { + // Avoid Movie::setBounds. + // clone2727 asks why, but goes along with it + Animation::setBounds(r); + } +} + +ScalingMovie::ScalingMovie(const DisplayElementID id) : GlowingMovie(id) { +} + +void ScalingMovie::draw(const Common::Rect &) { + // Make sure the rectangles are clipped properly, OR guarantee that _bounds will + // never fall off the screen. + + Common::Rect bounds; + getBounds(bounds); + + if (_glowing) + scaleTransparentCopyGlow(_movieBox, bounds); + else + scaleTransparentCopy(_movieBox, bounds); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/movie.h b/engines/pegasus/movie.h new file mode 100644 index 0000000000..9b9cc2bdbe --- /dev/null +++ b/engines/pegasus/movie.h @@ -0,0 +1,105 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_MOVIE_H +#define PEGASUS_MOVIE_H + +#include "common/str.h" + +#include "pegasus/elements.h" +#include "pegasus/surface.h" + +namespace Video { +class VideoDecoder; +} + +namespace Pegasus { + +class Movie : public Animation, public PixelImage { +public: + Movie(const DisplayElementID); + virtual ~Movie(); + + virtual void initFromMovieFile(const Common::String &fileName, bool transparent = false); + + bool isMovieValid() { return _video != 0; } + + virtual void releaseMovie(); + + virtual void draw(const Common::Rect &); + virtual void redrawMovieWorld(); + + virtual void setTime(const TimeValue, const TimeScale = 0); + + virtual void setRate(const Common::Rational); + + virtual void start(); + virtual void stop(); + virtual void resume(); + virtual void pause(); + + virtual void moveMovieBoxTo(const CoordType, const CoordType); + + virtual void setStop(const TimeValue, const TimeScale = 0); + + virtual TimeValue getDuration(const TimeScale = 0) const; + + // *** HACK ALERT + Video::VideoDecoder *getMovie() { return _video; } + void setVolume(uint16); + +protected: + void updateTime(); + + Video::VideoDecoder *_video; + Common::Rect _movieBox; +}; + +class GlowingMovie : public Movie { +public: + GlowingMovie(DisplayElementID); + virtual ~GlowingMovie() {} + + virtual void draw(const Common::Rect &); + + void setBounds(const Common::Rect &); + + void setGlowing(const bool glowing) { _glowing = glowing; } + +protected: + bool _glowing; +}; + +class ScalingMovie : public GlowingMovie { +public: + ScalingMovie(DisplayElementID); + virtual ~ScalingMovie() {} + + virtual void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.cpp b/engines/pegasus/neighborhood/caldoria/caldoria.cpp new file mode 100644 index 0000000000..140e6e8093 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria.cpp @@ -0,0 +1,1962 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "video/qt_decoder.h" + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h" +#include "pegasus/neighborhood/caldoria/caldoriabomb.h" +#include "pegasus/neighborhood/caldoria/caldoriamessages.h" +#include "pegasus/neighborhood/caldoria/caldoriamirror.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" + +namespace Pegasus { + +static const int16 kVidPhoneAngle = 30; +static const int16 kReplicatorAngle = 50; +static const int16 kDrawersAngle = -30; +static const int16 kCaldoria53Angle = 45; +static const int16 kCaldoria55Angle = -45; + +static const TimeValue kSinclairInterruptionTime1 = 2955; +static const TimeValue kSinclairInterruptionTime2 = 6835; +static const TimeValue kSinclairInterruptionTime3 = 9835; +static const TimeValue kSinclairInterruptionTime4 = 12555; + +static const InputBits kPullbackInterruptFilter = kFilterAllInput; +static const InputBits kRecalibrationInterruptFilter = kFilterAllInput; + +static const TimeValue kCaldoriaReplicatorIntroIn = 4933; +static const TimeValue kCaldoriaReplicatorIntroOut = 6557; + +static const TimeValue kCaldoriaReplicatorWrongChoiceIn = 6557; +static const TimeValue kCaldoriaReplicatorWrongChoiceOut = 8586; + +static const TimeValue kCaldoriaReplicatorOJChoiceIn = 8586; +static const TimeValue kCaldoriaReplicatorOJChoiceOut = 11687; + +static const TimeValue kCaldoriaMessagesIntroIn = 11687; +static const TimeValue kCaldoriaMessagesIntroOut = 13641; + +static const TimeValue kCaldoriaFirstMessageIn = 13641; +static const TimeValue kCaldoriaFirstMessageOut = 14203; + +static const TimeValue kCaldoriaSecondMessageIn = 14203; +static const TimeValue kCaldoriaSecondMessageOut = 14750; + +static const TimeValue kCaldoriaDoorCloseIn = 14750; +static const TimeValue kCaldoriaDoorCloseOut = 15472; + +static const TimeValue kCaldoriaElevatorCloseIn = 15472; +static const TimeValue kCaldoriaElevatorCloseOut = 16336; + +static const TimeValue kCaldoriaShowerCloseIn = 16336; +static const TimeValue kCaldoriaShowerCloseOut = 17101; + +static const TimeValue kCaldoriaGTDoorCloseIn = 17101; +static const TimeValue kCaldoriaGTDoorCloseOut = 18523; + +static const TimeValue kCaldoriaNobodyHomeIn = 18523; +static const TimeValue kCaldoriaNobodyHomeOut = 21469; + +static const TimeValue kCaldoriaNoOtherFloorIn = 21469; +static const TimeValue kCaldoriaNoOtherFloorOut = 28013; + +static const TimeValue kCaldoria4DInstructionsIn = 28013; +static const TimeValue kCaldoria4DInstructionsOut = 29730; + +static const TimeValue kCaldoriaDrinkOJIn = 33910; +static const TimeValue kCaldoriaDrinkOJOut = 35846; + +static const TimeValue kCaldoriaNoOtherDestinationIn = 35846; +static const TimeValue kCaldoriaNoOtherDestinationOut = 37877; + +static const TimeValue kCaldoriaUhghIn = 37877; +static const TimeValue kCaldoriaUhghOut = 38025; + +static const TimeValue kCaldoriaSinclairShootsOSIn = 38025; +static const TimeValue kCaldoriaSinclairShootsOSOut = 40649; + +static const TimeValue kCaldoriaScreamingAfterIn = 40649; +static const TimeValue kCaldoriaScreamingAfterOut = 47661; + +static const TimeValue k4FloorTime = 0; + +static const TimeValue k4To1Start = 40; +static const TimeValue k4To1Stop = 7720; + +static const TimeValue k4To5Start = 7720; +static const TimeValue k4To5Stop = 10280; + +static const TimeValue k4To2Time = 10280; + +static const TimeValue k4To3Time = 10320; + +static const TimeValue k1FloorTime = 10360; + +static const TimeValue k1To4Start = 10400; +static const TimeValue k1To4Stop = 18080; + +static const TimeValue k1To5Start = 18080; +static const TimeValue k1To5Stop = 28320; + +static const TimeValue k1To2Time = 28320; + +static const TimeValue k1To3Time = 28360; + +static const TimeValue k5FloorTime = 28400; + +static const TimeValue k5To1Start = 28440; +static const TimeValue k5To1Stop = 38680; + +static const TimeValue k5To4Start = 38680; +static const TimeValue k5To4Stop = 41240; + +static const TimeValue k5To2Time = 41240; + +static const TimeValue k5To3Time = 41280; + +// FuseFunction functions... + +const NotificationFlags kSinclairLoopDoneFlag = kLastNeighborhoodNotificationFlag << 1; + +SinclairCallBack::SinclairCallBack(Caldoria *caldoria) { + _caldoria = caldoria; +} + +void SinclairCallBack::callBack() { + _caldoria->checkInterruptSinclair(); +} + +Caldoria::Caldoria(InputHandler* nextHandler, PegasusEngine *owner) + : Neighborhood(nextHandler, owner, "Caldoria", kCaldoriaID), _sinclairInterrupt(this) { + setIsItemTaken(kKeyCard); + setIsItemTaken(kOrangeJuiceGlassEmpty); + GameState.setTakenItemID(kOrangeJuiceGlassFull, GameState.isTakenItemID(kOrangeJuiceGlassEmpty)); + _zoomOutSpot = 0; + _gunSprite = 0; +} + +Caldoria::~Caldoria() { + _sinclairInterrupt.releaseCallBack(); +} + +void Caldoria::init() { + Neighborhood::init(); + + // We need this notification flag as well. + _neighborhoodNotification.notifyMe(this, kSinclairLoopDoneFlag, kSinclairLoopDoneFlag); + + _sinclairInterrupt.initCallBack(&_navMovie, kCallBackAtTime); + + forceStridingStop(kCaldoria55, kSouth, kAltCaldoriaSinclairDown); + forceStridingStop(kCaldoria50, kNorth, kAltCaldoriaSinclairDown); +} + +void Caldoria::start() { + g_energyMonitor->stopEnergyDraining(); + + if (!GameState.getCaldoriaSeenPullback()) { + _vm->_gfx->doFadeOutSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond); + + g_system->delayMillis(2 * 1000); + + Video::VideoDecoder *pullbackMovie = new Video::QuickTimeDecoder(); + + if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.movie")) + error("Could not load pullback movie"); + + // Draw the first frame so we can fade to it + const Graphics::Surface *frame = pullbackMovie->decodeNextFrame(); + assert(frame); + assert(frame->format == g_system->getScreenFormat()); + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h); + _vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond); + + bool saveAllowed = _vm->swapSaveAllowed(false); + bool openAllowed = _vm->swapLoadAllowed(false); + + bool skipped = false; + Input input; + + pullbackMovie->start(); + + while (!_vm->shouldQuit() && !pullbackMovie->endOfVideo()) { + if (pullbackMovie->needsUpdate()) { + frame = pullbackMovie->decodeNextFrame(); + + if (frame) { + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h); + g_system->updateScreen(); + } + } + + InputDevice.getInput(input, kPullbackInterruptFilter); + if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested()) { + skipped = true; + break; + } + + g_system->delayMillis(10); + } + + delete pullbackMovie; + + if (_vm->shouldQuit()) + return; + + _vm->swapSaveAllowed(saveAllowed); + _vm->swapLoadAllowed(openAllowed); + + ExtraTable::Entry entry; + + if (!skipped) { + _vm->_gfx->doFadeOutSync(kThreeSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false); + g_system->delayMillis(3 * 1000 / 2); + getExtraEntry(kCaldoria00WakeUp1, entry); + _navMovie.setTime(entry.movieStart); + _navMovie.redrawMovieWorld(); + _navMovie.show(); + _vm->refreshDisplay(); + _vm->_gfx->doFadeInSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond, false); + } else { + getExtraEntry(kCaldoria00WakeUp1, entry); + _navMovie.setTime(entry.movieStart); + _navMovie.redrawMovieWorld(); + _navMovie.show(); + } + + GameState.setCaldoriaSeenPullback(true); + } + + Neighborhood::start(); +} + +void Caldoria::flushGameState() { + GameState.setCaldoriaFuseTimeLimit(_utilityFuse.getTimeRemaining()); +} + +class AIBombActiveCondition : public AICondition { +public: + AIBombActiveCondition() {} + + bool fireCondition(); +}; + +// Return true if player is on 53 east and Sinclair is shot. +bool AIBombActiveCondition::fireCondition() { + return GameState.getCurrentRoom() == kCaldoria53 && GameState.getCurrentDirection() == kEast && + GameState.getCaldoriaSinclairShot(); +} + +void Caldoria::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + if (GameState.allTimeZonesFinished()) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X49NB1", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria49, kNorth)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X56EH1", false); + AIBombActiveCondition *activeCondition = new AIBombActiveCondition(); + rule = new AIRule(activeCondition, messageAction); + g_AIArea->addAIRule(rule); + } else { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB2", false); + AITimerCondition *timerCondition = new AITimerCondition(kLateWarning3TimeLimit, 1, true); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria44, kEast)); + AINotCondition *notCondition = new AINotCondition(locCondition); + AIAndCondition *andCondition = new AIAndCondition(timerCondition, notCondition); + AIRule *rule = new AIRule(andCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB1", false); + timerCondition = new AITimerCondition(kLateWarning2TimeLimit, 1, true); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria44, kEast)); + notCondition = new AINotCondition(locCondition); + andCondition = new AIAndCondition(timerCondition, notCondition); + rule = new AIRule(andCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XA44EB", false); + locCondition = new AILocationCondition(3); + locCondition->addLocation(MakeRoomView(kCaldoria01, kNorth)); + locCondition->addLocation(MakeRoomView(kCaldoria01, kEast)); + locCondition->addLocation(MakeRoomView(kCaldoria01, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X42WH1", false); + AICondition *condition = makeLocationAndDoesntHaveItemCondition(kCaldoria44, kEast, kKeyCard); + rule = new AIRule(condition, messageAction); + g_AIArea->addAIRule(rule); + + AIActivateRuleAction *ruleAction = new AIActivateRuleAction(rule); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kCaldoria42, kEast)); + rule = new AIRule(locCondition, ruleAction); + g_AIArea->addAIRule(rule); + } + } +} + +uint16 Caldoria::getDateResID() const { + return kDate2318ID; +} + +TimeValue Caldoria::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry extra; + uint32 extraID = 0xffffffff; + + switch (room) { + case kCaldoria00: + if (direction == kEast && _privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) + extraID = k4DEnvironOpenView; + break; + case kCaldoriaDrawers: + if (direction == kNorth && _privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) { + if (GameState.isTakenItemID(kKeyCard)) + extraID = kRightDrawerOpenViewNoKeys; + else + extraID = kRightDrawerOpenViewWithKeys; + } + break; + case kCaldoria16: + if (direction == kSouth && GameState.getCaldoriaSeenSinclairInElevator()) + extraID = kCaldoria16SouthViewWithElevator; + break; + case kCaldoriaReplicator: + if (GameState.getCaldoriaMadeOJ() && !(GameState.isTakenItemID(kOrangeJuiceGlassEmpty) || GameState.isTakenItemID(kOrangeJuiceGlassFull))) + extraID = kReplicatorNorthViewWithOJ; + break; + case kCaldoriaKiosk: + case kCaldoriaBinoculars: + return 0xffffffff; + case kCaldoria48: + if (direction == kNorth && GameState.getCaldoriaRoofDoorOpen()) + extraID = kCa48NorthExplosion; + break; + } + + if (extraID == 0xffffffff) + return Neighborhood::getViewTime(room, direction); + + getExtraEntry(extraID, extra); + return extra.movieEnd - 1; +} + +void Caldoria::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria13, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen13CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen13CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria14, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen14CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen14CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria18, kWest): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen18CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen18CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria23, kSouth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen23CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen23CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria33, kSouth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen33CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen33CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria36, kNorth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen36CarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen36CarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kNorth): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41NorthCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41NorthCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kEast): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41EastCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41EastCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + case MakeRoomView(kCaldoria41, kWest): + if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41WestCarFlag) && _vm->getRandomBit() == 0) { + _privateFlags.setFlag(kCaldoriaPrivateSeen41WestCarFlag, true); + Neighborhood::startSpotOnceOnly(startTime, stopTime); + } + break; + default: + Neighborhood::startSpotOnceOnly(startTime, stopTime); + break; + } +} + +void Caldoria::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { + Neighborhood::findSpotEntry(room, direction, flags, entry); + + switch (room) { + case kCaldoria00: + if (direction == kEast && (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN())) + entry.clear(); + break; + case kCaldoriaVidPhone: + if (direction == kNorth && GameState.getCaldoriaSeenMessages()) + entry.clear(); + break; + case kCaldoria44: + if (direction == kEast && GameState.getLastRoom() != kCaldoria42) + entry.clear(); + break; + } +} + +void Caldoria::startExitMovie(const ExitTable::Entry &exitEntry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria05: + case kCaldoria07: + if (GameState.getCurrentDirection() == kWest) + closeCroppedMovie(); + // fall through + case kCaldoria11: + if (GameState.getCurrentDirection() == kEast) + closeCroppedMovie(); + break; + case kCaldoria13: + case kCaldoria14: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + } + + Neighborhood::startExitMovie(exitEntry); +} + +void Caldoria::startZoomMovie(const ZoomTable::Entry &zoomEntry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria12: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + } + + Neighborhood::startZoomMovie(zoomEntry); +} + +void Caldoria::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { + if (GameState.getCurrentRoom() == kCaldoria27 || GameState.getCurrentRoom() == kCaldoria28 || GameState.getCurrentRoom() == kCaldoria45) + // Must be opening elevator door. + closeCroppedMovie(); + + if (GameState.getCurrentRoom() == kCaldoria44 && GameState.getLastRoom() != kCaldoria42) + startExtraSequence(kArriveAtCaldoriaFromTSA, kDoorOpenCompletedFlag, false); + else + Neighborhood::startDoorOpenMovie(startTime, stopTime); +} + +void Caldoria::startTurnPush(const TurnDirection turnDirection, const TimeValue newViewTime, const DirectionConstant destDirection) { + switch (GameState.getCurrentRoom()) { + case kCaldoria05: + case kCaldoria07: + if (GameState.getCurrentDirection() == kWest) + closeCroppedMovie(); + break; + case kCaldoria11: + if (GameState.getCurrentDirection() == kEast) + closeCroppedMovie(); + break; + case kCaldoria12: + case kCaldoria13: + case kCaldoria14: + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (GameState.getCurrentDirection() == kNorth) + closeCroppedMovie(); + break; + case kCaldoria48: + if (_croppedMovie.isSurfaceValid()) + closeCroppedMovie(); + break; + } + + Neighborhood::startTurnPush(turnDirection, newViewTime, destDirection); +} + +void Caldoria::bumpIntoWall() { + requestSpotSound(kCaldoriaUhghIn, kCaldoriaUhghOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +void Caldoria::closeDoorOffScreen(const RoomID room, const DirectionConstant direction) { + switch (room) { + case kCaldoria08: + if (direction == kNorth) + playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut); + else + playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut); + break; + case kCaldoria09: + playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut); + break; + case kCaldoria16: + case kCaldoria38: + case kCaldoria46: + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + playSpotSoundSync(kCaldoriaElevatorCloseIn, kCaldoriaElevatorCloseOut); + break; + case kCaldoria44: + case kCaldoria42: + if (GameState.getCurrentRoom() == kCaldoria42) + playSpotSoundSync(kCaldoriaGTDoorCloseIn, kCaldoriaGTDoorCloseOut); + break; + default: + playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut); + break; + } +} + +int16 Caldoria::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 result = Neighborhood::getStaticCompassAngle(room, dir); + + switch (room) { + case kCaldoriaVidPhone: + result += kVidPhoneAngle; + break; + case kCaldoriaReplicator: + result += kReplicatorAngle; + break; + case kCaldoriaDrawers: + result += kDrawersAngle; + break; + case kCaldoria53: + result += kCaldoria53Angle; + break; + case kCaldoria55: + result += kCaldoria55Angle; + break; + } + + return result; +} + +void Caldoria::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kCaldoria08, kNorth): + case MakeRoomView(kCaldoria09, kSouth): + compassMove.insertFaderKnot((exitEntry.movieStart + exitEntry.movieEnd) >> 1, compassMove.getNthKnotValue(0) + 30); + break; + case MakeRoomView(kCaldoria10, kEast): + compassMove.insertFaderKnot(exitEntry.movieStart + 4 * kCaldoriaFrameDuration, 90); + compassMove.insertFaderKnot(exitEntry.movieStart + 19 * kCaldoriaFrameDuration, -90); + break; + case MakeRoomView(kCaldoria42, kWest): + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, -90, exitEntry.movieEnd, 90); + compassMove.insertFaderKnot(exitEntry.movieStart + 3 * kCaldoriaFrameDuration, -90); + compassMove.insertFaderKnot(exitEntry.movieStart + 33 * kCaldoriaFrameDuration, 90); + break; + case MakeRoomView(kCaldoria54, kEast): + if (getCurrentAlternate() != kAltCaldoriaSinclairDown) { + compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kCaldoriaFrameDuration, 135); + compassMove.insertFaderKnot(exitEntry.movieEnd, 135); + } + break; + case MakeRoomView(kCaldoria55, kNorth): + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, 315, exitEntry.movieEnd, 270); + break; + } +} + +void Caldoria::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) { + Neighborhood::getZoomCompassMove(zoomEntry, compassMove); + + switch (zoomEntry.hotspot) { + case kCaBathroomToiletSpotID: + compassMove.insertFaderKnot(zoomEntry.movieStart + 4 * kCaldoriaFrameDuration, 90); + compassMove.insertFaderKnot(zoomEntry.movieStart + 19 * kCaldoriaFrameDuration, -90); + compassMove.insertFaderKnot(zoomEntry.movieEnd, -90); + break; + } +} + +void Caldoria::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kCaldoria00WakeUp1: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 180); + compassMove.insertFaderKnot(entry.movieStart + 1000, 90); + compassMove.insertFaderKnot(entry.movieStart + 1640, 120); + compassMove.insertFaderKnot(entry.movieStart + 2240, 135); + compassMove.insertFaderKnot(entry.movieStart + 2640, 180); + break; + case kCaldoria00WakeUp2: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 180, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + 560, 90); + break; + case kCaldoria56BombStage1: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 10); + compassMove.insertFaderKnot(entry.movieStart + 31 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 49 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 66 * kCaldoriaFrameDuration, 10); + break; + case kCaldoria56BombStage7: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 10, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + 131 * kCaldoriaFrameDuration, 10); + compassMove.insertFaderKnot(entry.movieStart + 148 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieStart + 165 * kCaldoriaFrameDuration, 60); + compassMove.insertFaderKnot(entry.movieEnd - 5 * kCaldoriaFrameDuration, 90); + break; + default: + Neighborhood::getExtraCompassMove(entry, compassMove); + break; + } +} + +void Caldoria::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + if (room == kCaldoria00 && GameState.getCaldoriaWokenUp()) + loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4); + else if (room >= kCaldoria01 && room <= kCaldoria14) + loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4); + else if (room == kCaldoria27 || room == kCaldoria28 || room == kCaldoria45) + loadLoopSound1("Sounds/Caldoria/Elevator Loop.AIFF", 0x100 / 5); + else if (room == kCaldoria44) + loadLoopSound1("Sounds/Caldoria/TSA Hum Loop.AIFF"); + else if (room >= kCaldoria15 && room <= kCaldoria48) + loadLoopSound1("Sounds/Caldoria/Industrial Nuage.aiff", 2 * 0x100 / 3); + else if (room >= kCaldoria49 && room <= kCaldoria56) + loadLoopSound1("Sounds/Caldoria/A50NLB00.22K.AIFF", 0x100 / 4); +} + +void Caldoria::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kCaldoria06, kSouth): + case MakeRoomView(kCaldoria13, kNorth): + case MakeRoomView(kCaldoria16, kSouth): + case MakeRoomView(kCaldoria38, kEast): + case MakeRoomView(kCaldoria38, kWest): + case MakeRoomView(kCaldoria40, kNorth): + case MakeRoomView(kCaldoria44, kEast): + case MakeRoomView(kCaldoria48, kNorth): + case MakeRoomView(kCaldoria49, kNorth): + makeContinuePoint(); + break; + } +} + +void Caldoria::spotCompleted() { + Neighborhood::spotCompleted(); + if (GameState.getCurrentRoom() == kCaldoriaBinoculars) + startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput); +} + +void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) { + switch (room) { + case kCaldoria56: + if (!GameState.getCaldoriaGunAimed()) + // Fall through... + case kCaldoria49: + case kCaldoria50: + case kCaldoria51: + case kCaldoria52: + case kCaldoria53: + case kCaldoria54: + case kCaldoria55: + if (GameState.getCaldoriaSinclairShot()) + setCurrentAlternate(kAltCaldoriaSinclairDown); + break; + } + + Neighborhood::arriveAt(room, direction); + Input dummy; + + switch (room) { + case kCaldoria00: + arriveAtCaldoria00(); + break; + case kCaldoria05: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop); + break; + case kCaldoria07: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop); + break; + case kCaldoria09: + _lastExtra = 0xffffffff; + break; + case kCaldoriaToilet: + GameState.setScoringReadPaper(true); + break; + case kCaldoriaReplicator: + setCurrentActivation(kActivateReplicatorReady); + requestSpotSound(kCaldoriaReplicatorIntroIn, kCaldoriaReplicatorIntroOut, kFilterNoInput, 0); + break; + case kCaldoria11: + setCurrentAlternate(kAltCaldoriaNormal); + if (direction == kEast && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop); + break; + case kCaldoria12: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop); + break; + case kCaldoriaDrawers: + setCurrentActivation(kActivateDrawersClosed); + break; + case kCaldoria13: + GameState.setCaldoriaINNAnnouncing(true); + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop); + break; + case kCaldoria14: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop); + break; + case kCaldoria08: + if (direction == kWest) + setCurrentActivation(kActivateMirrorReady); + // Fall through... + case kCaldoria15: + GameState.setCaldoriaINNAnnouncing(true); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (GameState.getCurrentDirection() == kNorth) + openDoor(); + break; + case kCaldoriaBinoculars: + GameState.setScoringLookThroughTelescope(true); + break; + case kCaldoriaKiosk: + GameState.setScoringSawCaldoriaKiosk(true); + startExtraSequenceSync(kCaldoriaKioskVideo, kFilterAllInput); + downButton(dummy); + break; + case kCaldoria44: + arriveAtCaldoria44(); + break; + case kCaldoria49: + arriveAtCaldoria49(); + break; + case kCaldoria53: + if (direction == kEast && !GameState.getCaldoriaSinclairShot()) + zoomToSinclair(); + break; + case kCaldoria50: + if (direction == kNorth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria54: + if (direction == kSouth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria56: + arriveAtCaldoria56(); + break; + case kCaldoriaDeathRoom: + arriveAtCaldoriaDeath(); + break; + } + + checkSinclairShootsOS(); + setUpRoofTop(); +} + +void Caldoria::doAIRecalibration() { + GameState.setCaldoriaDidRecalibration(true); + + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB1", true, kRecalibrationInterruptFilter)) + return; + + g_interface->calibrateEnergyBar(); + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB4", true, kRecalibrationInterruptFilter)) + return; + + g_interface->raiseInventoryDrawerSync(); + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB6", true, kRecalibrationInterruptFilter)) { + g_interface->lowerInventoryDrawerSync(); + return; + } + + g_interface->lowerInventoryDrawerSync(); + g_interface->raiseBiochipDrawerSync(); + + if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB5", true, kRecalibrationInterruptFilter)) { + g_interface->lowerBiochipDrawerSync(); + return; + } + + g_interface->lowerBiochipDrawerSync(); + + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB8", false, kRecalibrationInterruptFilter); +} + +void Caldoria::arriveAtCaldoria00() { + if (GameState.getCurrentDirection() == kEast) { + if (GameState.getCaldoriaWokenUp()) { + if (!GameState.getCaldoriaDidRecalibration()) + doAIRecalibration(); + setCurrentActivation(kActivate4DClosed); + } else { + // Good morning, sleeping beauty + ExtraTable::Entry extra; + getExtraEntry(kCaldoria00WakeUp1, extra); + + if (_navMovie.getTime() != extra.movieStart) { + _navMovie.setTime(extra.movieStart); + _navMovie.redrawMovieWorld(); + } + + startExtraSequenceSync(kCaldoria00WakeUp1, kFilterNoInput); + GameState.setCaldoriaWokenUp(true); + playCroppedMovieOnce("Images/Caldoria/VidPhone.movie", kCaldoriaVidPhoneLeft, kCaldoriaVidPhoneTop, kFilterAllInput); + startExtraSequence(kCaldoria00WakeUp2, kExtraCompletedFlag, kFilterNoInput); + } + } +} + +bool Caldoria::wantsCursor() { + return GameState.getCaldoriaDidRecalibration(); +} + +void Caldoria::arriveAtCaldoria44() { + if (GameState.getLastNeighborhood() != kCaldoriaID) { + openDoor(); + } else { + setCurrentActivation(kActivateReadyForCard); + loopExtraSequence(kCaldoriaTransporterArrowLoop, 0); + } +} + +void Caldoria::arriveAtCaldoria49() { + if (GameState.getLastRoom() == kCaldoria48) + setCurrentAlternate(kAltCaldoriaNormal); + + // Need to force the loop to play. + if (GameState.getCurrentDirection() == kNorth) { + GameState.setCaldoriaFuseTimeLimit(kSinclairShootsTimeLimit); + startExtraSequence(kCa49NorthVoiceAnalysis, kExtraCompletedFlag, kFilterNoInput); + } +} + +void Caldoria::arriveAtCaldoria56() { + if (!GameState.getCaldoriaBombDisarmed()) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, true); + + if (GameState.getCurrentDirection() == kNorth) { + turnRight(); + } else if (GameState.getCurrentDirection() == kSouth) { + turnLeft(); + } else if (GameState.getCurrentDirection() == kEast) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false); + newInteraction(kCaldoriaBombInteractionID); + } + } +} + +void Caldoria::arriveAtCaldoriaDeath() { + if (GameState.getLastRoom() == kCaldoria49) { + if (GameState.getCaldoriaSinclairShot()) { + die(kDeathNuclearExplosion); + } else { + playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut); + playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut); + die(kDeathSinclairShotDelegate); + } + } else { + die(kDeathShotBySinclair); + } +} + +void Caldoria::setUpRoofTop() { + switch (GameState.getCurrentRoom()) { + case kCaldoria48: + if (GameState.getCurrentDirection() == kNorth) { + if (GameState.getCaldoriaRoofDoorOpen()) { + setCurrentAlternate(kAltCaldoriaRoofDoorBlown); + } else if (GameState.getCaldoriaDoorBombed()) { + // Long enough for AI hints...? + _utilityFuse.primeFuse(kCardBombCountDownTime); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::doorBombTimerExpired)); + _utilityFuse.lightFuse(); + + loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop); + } else { + setCurrentActivation(kActivateRoofSlotEmpty); + } + } + break; + case kCaldoria56: + if (GameState.getCurrentDirection() == kEast && GameState.getCaldoriaGunAimed()) + startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false); + else + // Fall through... + case kCaldoria49: + case kCaldoria50: + case kCaldoria51: + case kCaldoria52: + case kCaldoria53: + case kCaldoria54: + case kCaldoria55: + if (!GameState.getCaldoriaSinclairShot()) { + if (GameState.getCaldoriaSawVoiceAnalysis() && !_utilityFuse.isFuseLit()) { + _utilityFuse.primeFuse(GameState.getCaldoriaFuseTimeLimit()); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::sinclairTimerExpired)); + _utilityFuse.lightFuse(); + } + } else { + setCurrentAlternate(kAltCaldoriaSinclairDown); + } + break; + } +} + +void Caldoria::downButton(const Input &input) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria01, kEast): + GameState.setCaldoriaWokenUp(true); + startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::downButton(input); + break; + } +} + +void Caldoria::turnTo(const DirectionConstant direction) { + Neighborhood::turnTo(direction); + + switch (GameState.getCurrentRoom()) { + case kCaldoria00: + if (direction == kEast) + setCurrentActivation(kActivate4DClosed); + break; + case kCaldoria01: + if (direction == kEast) { + GameState.setCaldoriaWokenUp(true); + startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCaldoria05: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop); + break; + case kCaldoria07: + if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) + loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop); + break; + case kCaldoria08: + if (direction == kWest) + setCurrentActivation(kActivateMirrorReady); + break; + case kCaldoria09: + _lastExtra = 0xffffffff; + break; + case kCaldoria11: + if (direction == kEast && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop); + break; + case kCaldoria12: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop); + break; + case kCaldoria13: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop); + break; + case kCaldoria14: + if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) + loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + if (direction == kNorth) + openElevatorMovie(); + else + closeCroppedMovie(); + break; + case kCaldoria48: + if (direction == kNorth && !GameState.getCaldoriaDoorBombed()) + setCurrentActivation(kActivateRoofSlotEmpty); + break; + case kCaldoria53: + if (GameState.getCurrentDirection() == kEast && !GameState.getCaldoriaSinclairShot()) + zoomToSinclair(); + break; + case kCaldoria50: + if (direction == kNorth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria54: + if (direction == kSouth && !GameState.getCaldoriaSinclairShot()) + setUpSinclairLoops(); + break; + case kCaldoria56: + if (_privateFlags.getFlag(kCaldoriaPrivateZoomingToBombFlag)) { + _privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false); + newInteraction(kCaldoriaBombInteractionID); + } else if (GameState.getCaldoriaBombDisarmed()) { + _vm->playEndMessage(); + } + break; + } + + checkSinclairShootsOS(); +} + +void Caldoria::zoomTo(const Hotspot *zoomOutSpot) { + // Need to set _zoomOutSpot here because we may come through + // this function another way, say by pressing the down arrow, + // that doesn't involve the ClickInHotSpot function. + _zoomOutSpot = zoomOutSpot; + + if (zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) { + if (_privateFlags.getFlag(kCaloriaPrivateLeftDrawerOpenFlag)) { + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false); + startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput); + } else if (_privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) { + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + if (GameState.isTakenItemID(kKeyCard)) + startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, false); + else + startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, false); + } else { + Neighborhood::zoomTo(zoomOutSpot); + } + } else { + Neighborhood::zoomTo(zoomOutSpot); + } +} + +void Caldoria::setUpSinclairLoops() { + _navMovie.stop(); + scheduleNavCallBack(kSinclairLoopDoneFlag); + _sinclairLoopCount = 0; + _numSinclairLoops = 2; + _navMovie.start(); +} + +void Caldoria::zoomToSinclair() { + _utilityFuse.stopFuse(); + _privateFlags.setFlag(kCaldoriaPrivateReadyToShootFlag, true); + setCurrentActivation(kActivateZoomedOnSinclair); + + ExtraTable::Entry entry; + getExtraEntry(kCa53EastZoomToSinclair, entry); + _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime1, _navMovie.getScale()); + startExtraSequence(kCa53EastZoomToSinclair, kExtraCompletedFlag, kFilterAllInput); +} + +void Caldoria::receiveNotification(Notification *notification, const NotificationFlags flags) { + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + InventoryItem *item; + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kCaldoria00WakeUp2: + makeContinuePoint(); + // Force ArriveAt to do its thing... + GameState.setCurrentRoom(kNoRoomID); + arriveAt(kCaldoria00, kEast); + break; + case k4DEnvironOpenToINN: + GameState.setCaldoriaSeenINN(true); + GameState.setScoringSawINN(true); + // Fall through to k4DEnvironOpen... + case k4DEnvironOpen: + _privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, true); + setCurrentActivation(kActivate4DOpen); + newInteraction(kCaldoria4DInteractionID); + break; + case kCaldoriaShowerUp: + GameState.setScoringTookShower(true); + GameState.setCaldoriaDoneHygiene(true); + break; + case kLeftDrawerClose: + case kRightDrawerCloseNoKeys: + case kRightDrawerCloseWithKeys: + if (_zoomOutSpot && _zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) { + Input input; + clickInHotspot(input, _zoomOutSpot); + } + break; + case kCreateOrangeJuice: + setCurrentActivation(kActivateOJOnThePad); + requestSpotSound(kCaldoriaReplicatorOJChoiceIn, kCaldoriaReplicatorOJChoiceOut, kFilterNoInput, 0); + break; + case kCaldoria00SitDown: + arriveAt(kCaldoria00, kEast); + break; + case kCaldoria16ElevatorUp: + startExtraSequence(kCaldoria16ElevatorDown, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoria16ElevatorDown: + GameState.setCaldoriaSeenSinclairInElevator(true); + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaFourthToGround: + case kCaldoriaRoofToGround: + arriveAt(kCaldoria28, GameState.getCurrentDirection()); + break; + case kCaldoriaFourthToRoof: + case kCaldoriaGroundToRoof: + arriveAt(kCaldoria45, GameState.getCurrentDirection()); + break; + case kCaldoriaGroundToFourth: + case kCaldoriaRoofToFourth: + arriveAt(kCaldoria27, GameState.getCurrentDirection()); + break; + case kCaGTCardSwipe: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kKeyCard); + _vm->addItemToInventory(item); + setCurrentActivation(kActivateReadyToTransport); + break; + case kCaGTFryTheFly: + case kCaGTGoToTSA: + _vm->jumpToNewEnvironment(kFullTSAID, kTSA00, kNorth); + break; + case kCaGTGoToTokyo: + playDeathExtra(kCaGTArriveAtTokyo, kDeathUncreatedInCaldoria); + break; + case kCaGTGoToBeach: + playDeathExtra(kCaGTArriveAtBeach, kDeathUncreatedInCaldoria); + break; + case kCa48NorthExplosion: + // Current biochip must be the shield if we got here. + _vm->getCurrentBiochip()->setItemState(kShieldNormal); + break; + case kBinocularsZoomInOnShip: + setCurrentActivation(kActivateFocusedOnShip); + break; + case kCa49NorthVoiceAnalysis: + _utilityFuse.primeFuse(kSinclairShootsTimeLimit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::sinclairTimerExpired)); + _utilityFuse.lightFuse(); + GameState.setCaldoriaSawVoiceAnalysis(true); + break; + case kCa53EastZoomToSinclair: + if (GameState.getCaldoriaSinclairShot()) { + delete _gunSprite; + _gunSprite = 0; + startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false); + } else { + playDeathExtra(kCa53EastDeath2, kDeathSinclairShotDelegate); + } + break; + case kCa53EastShootSinclair: + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kStunGun)); + startExtraSequence(kCa53EastZoomOutFromSinclair, kExtraCompletedFlag, false); + GameState.setScoringStunnedSinclair(true); + break; + case kCa53EastZoomOutFromSinclair: + setCurrentAlternate(kAltCaldoriaSinclairDown); + updateViewFrame(); + makeContinuePoint(); + break; + } + } else if ((flags & kSpotSoundCompletedFlag) != 0) { + switch (GameState.getCurrentRoom()) { + case kCaldoria20: + case kCaldoria21: + case kCaldoria26: + case kCaldoria29: + case kCaldoria34: + case kCaldoria35: + updateViewFrame(); + break; + case kCaldoria27: + case kCaldoria28: + case kCaldoria45: + updateElevatorMovie(); + break; + case kCaldoriaReplicator: + emptyOJGlass(); + break; + } + } else if ((flags & kSinclairLoopDoneFlag) != 0) { + if (++_sinclairLoopCount == _numSinclairLoops) { + switch (GameState.getCurrentRoom()) { + case kCaldoria50: + playDeathExtra(kCa50SinclairShoots, kDeathShotBySinclair); + break; + case kCaldoria54: + playDeathExtra(kCa54SouthDeath, kDeathShotBySinclair); + break; + } + } else { + _navMovie.stop(); + scheduleNavCallBack(kSinclairLoopDoneFlag); + _navMovie.start(); + } + } + + g_AIArea->checkMiddleArea(); +} + +InputBits Caldoria::getInputFilter() { + InputBits result = Neighborhood::getInputFilter(); + + switch (GameState.getCurrentRoom()) { + case kCaldoria00: + if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) + result &= ~kFilterAllDirections; + break; + case kCaldoriaBinoculars: + if (getCurrentActivation() == kActivateNotFocusedOnShip) + result &= ~(kFilterDownButton | kFilterDownAuto); + break; + case kCaldoria53: + if (_privateFlags.getFlag(kCaldoriaPrivateReadyToShootFlag) && !GameState.getCaldoriaSinclairShot()) + result &= ~kFilterAllDirections; + break; + case kCaldoria48: + if (GameState.getCaldoriaDoorBombed()) + result &= ~kFilterAllDirections; + } + + return result; +} + +void Caldoria::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (GameState.getCurrentRoom()) { + case kCaldoriaDrawers: + if (getCurrentActivation() == kActivateRightOpen) { + if (GameState.isTakenItemID(kKeyCard)) { + _vm->getAllHotspots().activateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID); + } else { + _vm->getAllHotspots().activateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID); + } + } + case kCaldoriaReplicator: + if (GameState.getCaldoriaMadeOJ()) + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaMakeOJSpotID); + break; + case kCaldoria27: + if (GameState.isCurrentDoorOpen()) { + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator1); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator2); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator3); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator4); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaFourthFloorElevator5); + } + break; + case kCaldoria28: + if (GameState.isCurrentDoorOpen()) { + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator1); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator2); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator3); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator4); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaGroundElevator5); + } + break; + case kCaldoria45: + if (GameState.isCurrentDoorOpen()) { + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator1); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator2); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator3); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator4); + _vm->getAllHotspots().deactivateOneHotspot(kCaldoriaRoofElevator5); + } + break; + } +} + +void Caldoria::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCa4DEnvironOpenSpotID: + if (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN()) { + startExtraSequence(k4DEnvironOpen, kExtraCompletedFlag, kFilterNoInput); + } else { + // This trick depends on the following sequences being in order in the + // world movie: + // k4DEnvironOpenToINN + // k4DINNInterruption + // k4DINNIntro + // k4DINNMarkJohnson + // k4DINNMeganLove + // k4DINNFadeOut + // k4DEnvironOpenFromINN + loadLoopSound1(""); + loadLoopSound2(""); + startExtraLongSequence(k4DEnvironOpenToINN, k4DEnvironOpenFromINN, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCa4DEnvironCloseSpotID: + ((Caldoria4DSystem *)_currentInteraction)->shutDown4DSystem(); + break; + case kCaBathroomMirrorSpotID: + newInteraction(kCaldoriaMirrorInteractionID); + break; + case kCaShowerSpotID: + requestExtraSequence(kCaldoriaShowerTitle, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerButton, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerDown, 0, kFilterNoInput); + requestExtraSequence(kCaldoriaShowerUp, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaLeftDrawerOpenSpotID: + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, true); + setCurrentActivation(kActivateLeftOpen); + startExtraSequence(kLeftDrawerOpen, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaLeftDrawerCloseSpotID: + _privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerOpenSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, true); + setCurrentActivation(kActivateRightOpen); + if (GameState.isTakenItemID(kKeyCard)) + startExtraSequence(kRightDrawerOpenNoKeys, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kRightDrawerOpenWithKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerWithKeysCloseSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRightDrawerNoKeysCloseSpotID: + _privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); + setCurrentActivation(kActivateDrawersClosed); + startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaMakeStickyBunsSpotID: + requestSpotSound(kCaldoriaReplicatorWrongChoiceIn, kCaldoriaReplicatorWrongChoiceOut, kFilterNoInput, 0); + break; + case kCaldoriaMakeOJSpotID: + GameState.setCaldoriaMadeOJ(true); + startExtraSequence(kCreateOrangeJuice, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBedroomVidPhoneActivationSpotID: + newInteraction(kCaldoriaMessagesInteractionID); + break; + case kCaldoriaFourthFloorElevatorSpotID: + if (!GameState.getCaldoriaSeenSinclairInElevator()) { + startExtraSequence(kCaldoria16ElevatorUp, kExtraCompletedFlag, kFilterNoInput); + } else { + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + } + break; + case kCaldoriaGroundElevatorSpotID: + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaRoofElevatorSpotID: + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); + openDoor(); + break; + case kCaldoriaFourthFloorElevator1: + case kCaldoriaFourthFloorElevator2: + case kCaldoriaFourthFloorElevator3: + case kCaldoriaFourthFloorElevator4: + case kCaldoriaFourthFloorElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(4, spot->getObjectID() - kCaldoriaFourthFloorElevator1 + 1); + break; + case kCaldoriaGroundElevator1: + case kCaldoriaGroundElevator2: + case kCaldoriaGroundElevator3: + case kCaldoriaGroundElevator4: + case kCaldoriaGroundElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(1, spot->getObjectID() - kCaldoriaGroundElevator1 + 1); + break; + case kCaldoriaRoofElevator1: + case kCaldoriaRoofElevator2: + case kCaldoriaRoofElevator3: + case kCaldoriaRoofElevator4: + case kCaldoriaRoofElevator5: + // Assumes that elevator hot spots are consecutive. + takeElevator(5, spot->getObjectID() - kCaldoriaRoofElevator1 + 1); + break; + case kCaldoriaGTTokyoSpotID: + startExtraSequence(kCaGTGoToTokyo, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaGTTSASpotID: + GameState.setScoringGoToTSA(true); + startExtraLongSequence(kCaGTFryTheFly, kCaGTGoToTSA, kExtraCompletedFlag, false); + break; + case kCaldoriaGTBeachSpotID: + startExtraSequence(kCaGTGoToBeach, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaGTOtherSpotID: + showExtraView(kCaGTOtherChoice); + playSpotSoundSync(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut); + showExtraView(kCaGTCardSwipe); + break; + case kCaldoriaZoomInOnShipSpotID: + startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoriaRoofDoorSpotID: + startExtraSequence(kCa48NorthRooftopClosed, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaldoria20DoorbellSpotID: + case kCaldoria21DoorbellSpotID: + case kCaldoria26DoorbellSpotID: + case kCaldoria29DoorbellSpotID: + case kCaldoria34DoorbellSpotID: + case kCaldoria35DoorbellSpotID: + clickOnDoorbell(spot->getObjectID()); + break; + default: + Neighborhood::clickInHotspot(input, spot); + break; + } +} + +void Caldoria::clickOnDoorbell(const HotSpotID doorBellSpotID) { + uint32 extra; + ExtraTable::Entry entry; + + switch (doorBellSpotID) { + case kCaldoria20DoorbellSpotID: + extra = kCaldoria20Doorbell; + break; + case kCaldoria21DoorbellSpotID: + extra = kCaldoria21Doorbell; + break; + case kCaldoria26DoorbellSpotID: + extra = kCaldoria26Doorbell; + break; + case kCaldoria29DoorbellSpotID: + extra = kCaldoria29Doorbell; + break; + case kCaldoria34DoorbellSpotID: + extra = kCaldoria34Doorbell; + break; + case kCaldoria35DoorbellSpotID: + extra = kCaldoria35Doorbell; + break; + default: + error("Invalid doorbell hotspot"); + } + + getExtraEntry(extra, entry); + showViewFrame(entry.movieStart); + requestSpotSound(kCaldoriaNobodyHomeIn, kCaldoriaNobodyHomeOut, kFilterNoInput, kSpotSoundCompletedFlag); +} + +CanOpenDoorReason Caldoria::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoom()) { + case kCaldoria16: + case kCaldoria38: + case kCaldoria46: + if (GameState.getCurrentDirection() == kSouth && !_privateFlags.getFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag)) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void Caldoria::doorOpened() { + Neighborhood::doorOpened(); + _privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, false); +} + +GameInteraction *Caldoria::makeInteraction(const InteractionID interactionID) { + switch (interactionID) { + case kCaldoria4DInteractionID: + return new Caldoria4DSystem(this); + case kCaldoriaBombInteractionID: + return new CaldoriaBomb(this, _vm); + case kCaldoriaMessagesInteractionID: + return new CaldoriaMessages(this, kCaldoriaMessagesNotificationID, _vm); + case kCaldoriaMirrorInteractionID: + return new CaldoriaMirror(this); + } + + return 0; +} + +void Caldoria::newInteraction(const InteractionID interactionID) { + Neighborhood::newInteraction(interactionID); + + if (!_currentInteraction) { + if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) { + _privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, false); + setCurrentActivation(kActivate4DClosed); + startExtraSequence(k4DEnvironClose, kExtraCompletedFlag, kFilterNoInput); + } else if (GameState.getCaldoriaBombDisarmed()) { + turnLeft(); + } + } +} + +// Only called when trying to pick up an item and the player can't (because +// the inventory is too full or because the player lets go of the item before +// dropping it into the inventory). +Hotspot *Caldoria::getItemScreenSpot(Item *item, DisplayElement *element) { + HotSpotID destSpotID = kNoHotSpotID; + + switch (item->getObjectID()) { + case kKeyCard: + destSpotID = kCaldoriaKeyCardSpotID; + break; + case kOrangeJuiceGlassEmpty: + case kOrangeJuiceGlassFull: + destSpotID = kCaldoriaOrangeJuiceSpotID; + break; + } + + if (destSpotID == kNoHotSpotID) + return Neighborhood::getItemScreenSpot(item, element); + + return _vm->getAllHotspots().findHotspotByID(destSpotID); +} + +void Caldoria::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kKeyCard: + GameState.setScoringGotKeyCard(true); + break; + case kOrangeJuiceGlassFull: + setCurrentActivation(kActivateReplicatorReady); + requestSpotSound(kCaldoriaDrinkOJIn, kCaldoriaDrinkOJOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case kStunGun: + GameState.setCaldoriaGunAimed(false); + break; + } +} + +void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + switch (item->getObjectID()) { + case kKeyCard: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot->getObjectID() == kCaldoriaGTCardDropSpotID) + startExtraSequence(kCaGTCardSwipe, kExtraCompletedFlag, kFilterNoInput); + break; + case kOrangeJuiceGlassEmpty: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot->getObjectID() == kCaldoriaOrangeJuiceDropSpotID) { + GameState.setCaldoriaMadeOJ(false); + startExtraSequence(kDisposeOrangeJuice, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kCardBomb: + GameState.setCaldoriaDoorBombed(true); + setCurrentActivation(kActivateHotSpotAlways); + Neighborhood::dropItemIntoRoom(item, dropSpot); + // Long enough for AI hints...? + _utilityFuse.primeFuse(kCardBombCountDownTime); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Caldoria>(this, &Caldoria::doorBombTimerExpired)); + _utilityFuse.lightFuse(); + GameState.setCaldoriaFuseTimeLimit(kCardBombCountDownTime); + loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop); + GameState.setScoringUsedCardBomb(true); + break; + case kStunGun: + GameState.setCaldoriaGunAimed(true); + GameState.setCaldoriaSinclairShot(true); + _gunSprite = item->getDragSprite(0); + _gunSprite->setCurrentFrameIndex(1); + _gunSprite->setDisplayOrder(kDragSpriteOrder); + _gunSprite->moveElementTo(kCaldoriaGunSpriteLeft, kCaldoriaGunSpriteTop); + _gunSprite->startDisplaying(); + _gunSprite->show(); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } +} + +void Caldoria::takeElevator(uint startFloor, uint endFloor) { + _croppedMovie.stop(); + _croppedMovie.setSegment(0, _croppedMovie.getDuration()); + + switch (startFloor) { + case 1: + switch (endFloor) { + case 1: + // Do nothing. + break; + case 2: + _croppedMovie.setTime(k1To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k1To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + _croppedMovie.setSegment(k1To4Start, k1To4Stop); + _croppedMovie.setTime(k1To4Start); + startExtraSequence(kCaldoriaGroundToFourth, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 5: + _croppedMovie.setSegment(k1To5Start, k1To5Stop); + _croppedMovie.setTime(k1To5Start); + startExtraSequence(kCaldoriaGroundToRoof, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + } + break; + case 4: + switch (endFloor) { + case 1: + _croppedMovie.setSegment(k4To1Start, k4To1Stop); + _croppedMovie.setTime(k4To1Start); + startExtraSequence(kCaldoriaFourthToGround, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 2: + _croppedMovie.setTime(k4To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k4To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + // Do nothing. + break; + case 5: + _croppedMovie.setSegment(k4To5Start, k4To5Stop); + _croppedMovie.setTime(k4To5Start); + startExtraSequence(kCaldoriaFourthToRoof, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + } + break; + case 5: + switch (endFloor) { + case 1: + _croppedMovie.setSegment(k5To1Start, k5To1Stop); + _croppedMovie.setTime(k5To1Start); + startExtraSequence(kCaldoriaRoofToGround, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 2: + _croppedMovie.setTime(k5To2Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 3: + _croppedMovie.setTime(k5To3Time); + requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 4: + _croppedMovie.setSegment(k5To4Start, k5To4Stop); + _croppedMovie.setTime(k5To4Start); + startExtraSequence(kCaldoriaRoofToFourth, kExtraCompletedFlag, false); + _croppedMovie.start(); + break; + case 5: + // Do nothing. + break; + } + break; + }; +} + +void Caldoria::updateElevatorMovie() { + TimeValue time = 0xffffffff; + + if (GameState.getCurrentDirection() == kNorth) { + switch (GameState.getCurrentRoom()) { + case kCaldoria27: + time = k4FloorTime; + break; + case kCaldoria28: + time = k1FloorTime; + break; + case kCaldoria45: + time = k5FloorTime; + break; + } + } + + _croppedMovie.stop(); + + if (time == 0xffffffff) { + _croppedMovie.hide(); + } else { + _croppedMovie.stop(); + _croppedMovie.setSegment(0, _croppedMovie.getDuration()); + _croppedMovie.setTime(time); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.show(); + + // *** Why do I need this? + // clone2727: "don't ask me!" + _navMovie.redrawMovieWorld(); + } +} + +void Caldoria::openElevatorMovie() { + if (!_croppedMovie.isSurfaceValid()) + openCroppedMovie("Images/Caldoria/Caldoria Elevator.movie", kCaldoriaElevatorLeft, kCaldoriaElevatorTop); + + updateElevatorMovie(); +} + +void Caldoria::emptyOJGlass() { + GameState.setTakenItemID(kOrangeJuiceGlassFull, false); + GameState.setTakenItemID(kOrangeJuiceGlassEmpty, true); + _vm->removeItemFromInventory((InventoryItem *)_vm->getAllItems().findItemByID(kOrangeJuiceGlassFull)); + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kOrangeJuiceGlassEmpty)); +} + +void Caldoria::doorBombTimerExpired() { + closeCroppedMovie(); + + if (GameState.getShieldOn()) { + _vm->getCurrentBiochip()->setItemState(kShieldCardBomb); + setCurrentAlternate(kAltCaldoriaRoofDoorBlown); + startExtraSequence(kCa48NorthExplosion, kExtraCompletedFlag, kFilterNoInput); + GameState.setScoringShieldedCardBomb(true); + GameState.setCaldoriaDoorBombed(false); + GameState.setCaldoriaRoofDoorOpen(true); + } else { + playDeathExtra(kCa48NorthExplosionDeath, kDeathCardBomb); + } +} + +void Caldoria::sinclairTimerExpired() { + _privateFlags.setFlag(kCaldoriaPrivateSinclairTimerExpiredFlag, true); + checkSinclairShootsOS(); +} + +void Caldoria::checkSinclairShootsOS() { + if (_privateFlags.getFlag(kCaldoriaPrivateSinclairTimerExpiredFlag)) + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria49, kNorth): + case MakeRoomView(kCaldoria49, kSouth): + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria49, kWest): + case MakeRoomView(kCaldoria50, kSouth): + case MakeRoomView(kCaldoria50, kEast): + case MakeRoomView(kCaldoria50, kWest): + case MakeRoomView(kCaldoria51, kNorth): + case MakeRoomView(kCaldoria51, kSouth): + case MakeRoomView(kCaldoria51, kWest): + case MakeRoomView(kCaldoria52, kNorth): + case MakeRoomView(kCaldoria52, kSouth): + case MakeRoomView(kCaldoria52, kWest): + case MakeRoomView(kCaldoria53, kNorth): + case MakeRoomView(kCaldoria53, kSouth): + case MakeRoomView(kCaldoria53, kWest): + case MakeRoomView(kCaldoria54, kNorth): + case MakeRoomView(kCaldoria54, kEast): + case MakeRoomView(kCaldoria54, kWest): + playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut); + playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut); + die(kDeathSinclairShotDelegate); + break; + } +} + +void Caldoria::checkInterruptSinclair() { + if (GameState.getCaldoriaSinclairShot()) { + _navMovie.stop(); + _neighborhoodNotification.setNotificationFlags(kExtraCompletedFlag, kExtraCompletedFlag); + g_AIArea->unlockAI(); + } else { + uint32 currentTime = _navMovie.getTime(); + + ExtraTable::Entry entry; + getExtraEntry(kCa53EastZoomToSinclair, entry); + + if (currentTime < entry.movieStart + kSinclairInterruptionTime2) + _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime2, + _navMovie.getScale()); + else if (currentTime < entry.movieStart + kSinclairInterruptionTime3) + _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime3, + _navMovie.getScale()); + else if (currentTime < entry.movieStart + kSinclairInterruptionTime4) + _sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime4, + _navMovie.getScale()); + } +} + +Common::String Caldoria::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) { + if (GameState.allTimeZonesFinished()) + return "Images/AI/Caldoria/XA02"; + + return "Images/AI/Caldoria/XA01"; + } + + return movieName; +} + +Common::String Caldoria::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + RoomID room = GameState.getCurrentRoom(); + + if (room >= kCaldoria00 && room <= kCaldoria14) { + // Inside apartment. + if (GameState.getCaldoriaDoneHygiene()) + return "Images/AI/Caldoria/XAE2"; + + return "Images/AI/Caldoria/XAE1"; + } else if (room >= kCaldoria15 && room <= kCaldoria48) { + // Wandering the halls... + return "Images/AI/Caldoria/XAE3"; + } else { + // Must be the roof. + return "Images/AI/Caldoria/XAEH2"; + } + } + + return movieName; +} + +uint Caldoria::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria44, kEast): + if (!GameState.isTakenItemID(kKeyCard) && GameState.getOpenDoorRoom() == kNoRoomID) + numHints = 1; + break; + case MakeRoomView(kCaldoria48, kNorth): + if (!GameState.getCaldoriaRoofDoorOpen()) { + if (_croppedMovie.isRunning()) // Bomb must be looping. + numHints = 3; + else if (GameState.isTakenItemID(kCardBomb)) + numHints = 1; + } + break; + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria54, kEast): + numHints = 1; + break; + case MakeRoomView(kCaldoria49, kNorth): + numHints = 1; + break; + } + } + + return numHints; +} + +Common::String Caldoria::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kCaldoria44, kEast): + return "Images/AI/Caldoria/X42WH2"; + case MakeRoomView(kCaldoria48, kNorth): + if (_croppedMovie.isRunning()) { // Bomb must be looping. + if (hintNum == 1) + return "Images/AI/Caldoria/X48ND1"; + else if (hintNum == 2) + return "Images/AI/Caldoria/X48ND2"; + else if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Caldoria/X48ND3"; + + // *** Doesn't work yet, need global movies. + break; + } + + return "Images/AI/Globals/XGLOB1A"; + case MakeRoomView(kCaldoria49, kEast): + case MakeRoomView(kCaldoria54, kEast): + return "Images/AI/Caldoria/X49E"; + case MakeRoomView(kCaldoria49, kNorth): + return "Images/AI/Caldoria/X49NB2"; + } + } + + return movieName; +} + +void Caldoria::updateCursor(const Common::Point where, const Hotspot *cursorSpot) { + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kCa4DEnvironCloseSpotID: + _vm->_cursor->setCurrentFrameIndex(2); + return; + case kCaldoriaKioskSpotID: + _vm->_cursor->setCurrentFrameIndex(3); + return; + } + } + + Neighborhood::updateCursor(where, cursorSpot); +} + +Common::String Caldoria::getNavMovieName() { + return "Images/Caldoria/Caldoria.movie"; +} + +Common::String Caldoria::getSoundSpotsName() { + return "Sounds/Caldoria/Caldoria Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.h b/engines/pegasus/neighborhood/caldoria/caldoria.h new file mode 100644 index 0000000000..3d6a155170 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria.h @@ -0,0 +1,523 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +static const TimeScale kCaldoriaMovieScale = 600; +static const TimeScale kCaldoriaFramesPerSecond = 15; +static const TimeScale kCaldoriaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltCaldoriaNormal = 0; +static const AlternateID kAltCaldoriaRoofDoorBlown = 2; +static const AlternateID kAltCaldoriaSinclairDown = 3; + +// Room IDs. + +static const RoomID kCaldoria00 = 1; +static const RoomID kCaldoria01 = 2; +static const RoomID kCaldoria02 = 3; +static const RoomID kCaldoria03 = 4; +static const RoomID kCaldoria04 = 5; +static const RoomID kCaldoria05 = 6; +static const RoomID kCaldoria06 = 7; +static const RoomID kCaldoria07 = 8; +static const RoomID kCaldoria08 = 9; +static const RoomID kCaldoria09 = 10; +static const RoomID kCaldoria10 = 11; +static const RoomID kCaldoriaToilet = 12; +static const RoomID kCaldoria11 = 13; +static const RoomID kCaldoria12 = 14; +static const RoomID kCaldoriaVidPhone = 15; +static const RoomID kCaldoriaReplicator = 16; +static const RoomID kCaldoriaDrawers = 17; +static const RoomID kCaldoria13 = 18; +static const RoomID kCaldoria14 = 19; +static const RoomID kCaldoria15 = 20; +static const RoomID kCaldoria16 = 21; +static const RoomID kCaldoria17 = 22; +static const RoomID kCaldoria18 = 23; +static const RoomID kCaldoria19 = 24; +static const RoomID kCaldoria20 = 25; +static const RoomID kCaldoria21 = 26; +static const RoomID kCaldoria22 = 27; +static const RoomID kCaldoria23 = 28; +static const RoomID kCaldoria24 = 29; +static const RoomID kCaldoria25 = 30; +static const RoomID kCaldoria26 = 31; +static const RoomID kCaldoria27 = 32; +static const RoomID kCaldoria28 = 33; +static const RoomID kCaldoria29 = 34; +static const RoomID kCaldoria30 = 35; +static const RoomID kCaldoria31 = 36; +static const RoomID kCaldoria32 = 37; +static const RoomID kCaldoria33 = 38; +static const RoomID kCaldoria34 = 39; +static const RoomID kCaldoria35 = 40; +static const RoomID kCaldoria36 = 41; +static const RoomID kCaldoria37 = 42; +static const RoomID kCaldoria38 = 43; +static const RoomID kCaldoria39 = 44; +static const RoomID kCaldoria40 = 45; +static const RoomID kCaldoria41 = 46; +static const RoomID kCaldoriaBinoculars = 47; +static const RoomID kCaldoria42 = 48; +static const RoomID kCaldoriaKiosk = 49; +static const RoomID kCaldoria44 = 50; +static const RoomID kCaldoria45 = 51; +static const RoomID kCaldoria46 = 52; +static const RoomID kCaldoria47 = 53; +static const RoomID kCaldoria48 = 54; +static const RoomID kCaldoria49 = 55; +static const RoomID kCaldoria50 = 56; +static const RoomID kCaldoria51 = 57; +static const RoomID kCaldoria52 = 58; +static const RoomID kCaldoria53 = 59; +static const RoomID kCaldoria54 = 60; +static const RoomID kCaldoria55 = 61; +static const RoomID kCaldoria56 = 62; +static const RoomID kCaldoriaDeathRoom = 0; + +// Hot Spot Activation IDs. + +static const HotSpotActivationID kActivate4DClosed = 1; +static const HotSpotActivationID kActivate4DOpen = 2; +static const HotSpotActivationID kActivateMirrorReady = 3; +static const HotSpotActivationID kActivateStylistReady = 4; +static const HotSpotActivationID kActivateReplicatorReady = 5; +static const HotSpotActivationID kActivateOJOnThePad = 6; +static const HotSpotActivationID kActivateDrawersClosed = 7; +static const HotSpotActivationID kActivateRightOpen = 8; +static const HotSpotActivationID kActivateLeftOpen = 9; +static const HotSpotActivationID kActivateFocusedOnShip = 10; +static const HotSpotActivationID kActivateNotFocusedOnShip = 11; +static const HotSpotActivationID kActivateReadyForCard = 12; +static const HotSpotActivationID kActivateReadyToTransport = 13; +static const HotSpotActivationID kActivateRoofSlotEmpty = 14; +static const HotSpotActivationID kActivateZoomedOnSinclair = 15; + +// Hot Spot IDs. + +static const HotSpotID kCa4DEnvironOpenSpotID = 5000; +static const HotSpotID kCa4DEnvironCloseSpotID = 5001; +static const HotSpotID kCa4DVisualSpotID = 5002; +static const HotSpotID kCa4DAudioSpotID = 5003; +static const HotSpotID kCa4DChoice1SpotID = 5004; +static const HotSpotID kCa4DChoice2SpotID = 5005; +static const HotSpotID kCa4DChoice3SpotID = 5006; +static const HotSpotID kCa4DChoice4SpotID = 5007; +static const HotSpotID kCaBathroomMirrorSpotID = 5008; +static const HotSpotID kCaHairStyle1SpotID = 5009; +static const HotSpotID kCaHairStyle2SpotID = 5010; +static const HotSpotID kCaHairStyle3SpotID = 5011; +static const HotSpotID kCaShowerSpotID = 5012; +static const HotSpotID kCaBathroomToiletSpotID = 5013; +static const HotSpotID kCaldoriaVidPhoneSpotID = 5014; +static const HotSpotID kCaldoriaReplicatorSpotID = 5015; +static const HotSpotID kCaldoriaDrawersSpotID = 5016; +static const HotSpotID kCaldoriaVidPhoneOutSpotID = 5017; +static const HotSpotID kCaBedroomVidPhoneActivationSpotID = 5018; +static const HotSpotID kCaldoriaReplicatorOutSpotID = 5019; +static const HotSpotID kCaldoriaMakeOJSpotID = 5020; +static const HotSpotID kCaldoriaMakeStickyBunsSpotID = 5021; +static const HotSpotID kCaldoriaOrangeJuiceSpotID = 5022; +static const HotSpotID kCaldoriaOrangeJuiceDropSpotID = 5023; +static const HotSpotID kCaldoriaDrawersOutSpotID = 5024; +static const HotSpotID kCaldoriaLeftDrawerOpenSpotID = 5025; +static const HotSpotID kCaldoriaRightDrawerOpenSpotID = 5026; +static const HotSpotID kCaldoriaKeyCardSpotID = 5027; +static const HotSpotID kCaldoriaLeftDrawerCloseSpotID = 5028; +static const HotSpotID kCaldoriaRightDrawerWithKeysCloseSpotID = 5029; +static const HotSpotID kCaldoriaRightDrawerNoKeysCloseSpotID = 5030; +static const HotSpotID kCaldoriaFourthFloorElevatorSpotID = 5031; +static const HotSpotID kCaldoria20DoorbellSpotID = 5032; +static const HotSpotID kCaldoria21DoorbellSpotID = 5033; +static const HotSpotID kCaldoria26DoorbellSpotID = 5034; +static const HotSpotID kCaldoriaFourthFloorElevator1 = 5035; +static const HotSpotID kCaldoriaFourthFloorElevator2 = 5036; +static const HotSpotID kCaldoriaFourthFloorElevator3 = 5037; +static const HotSpotID kCaldoriaFourthFloorElevator4 = 5038; +static const HotSpotID kCaldoriaFourthFloorElevator5 = 5039; +static const HotSpotID kCaldoriaGroundElevator1 = 5040; +static const HotSpotID kCaldoriaGroundElevator2 = 5041; +static const HotSpotID kCaldoriaGroundElevator3 = 5042; +static const HotSpotID kCaldoriaGroundElevator4 = 5043; +static const HotSpotID kCaldoriaGroundElevator5 = 5044; +static const HotSpotID kCaldoria29DoorbellSpotID = 5045; +static const HotSpotID kCaldoria34DoorbellSpotID = 5046; +static const HotSpotID kCaldoria35DoorbellSpotID = 5047; +static const HotSpotID kCaldoriaGroundElevatorSpotID = 5048; +static const HotSpotID kCaldoriaBinocularZoomInSpotID = 5049; +static const HotSpotID kCaldoriaBinocularsOutSpotID = 5050; +static const HotSpotID kCaldoriaZoomInOnShipSpotID = 5051; +static const HotSpotID kCaldoriaKioskSpotID = 5052; +static const HotSpotID kCaldoriaKioskOutSpotID = 5053; +static const HotSpotID kCaldoriaKioskInfoSpotID = 5054; +static const HotSpotID kCaldoriaGTCardDropSpotID = 5055; +static const HotSpotID kCaldoriaGTTokyoSpotID = 5056; +static const HotSpotID kCaldoriaGTTSASpotID = 5057; +static const HotSpotID kCaldoriaGTBeachSpotID = 5058; +static const HotSpotID kCaldoriaGTOtherSpotID = 5059; +static const HotSpotID kCaldoriaRoofElevator1 = 5060; +static const HotSpotID kCaldoriaRoofElevator2 = 5061; +static const HotSpotID kCaldoriaRoofElevator3 = 5062; +static const HotSpotID kCaldoriaRoofElevator4 = 5063; +static const HotSpotID kCaldoriaRoofElevator5 = 5064; +static const HotSpotID kCaldoriaRoofElevatorSpotID = 5065; +static const HotSpotID kCaldoriaRoofDoorSpotID = 5066; +static const HotSpotID kCaldoriaRoofCardDropSpotID = 5067; +static const HotSpotID kCaldoria53EastSinclairTargetSpotID = 5068; + +// Extra sequence IDs. + +static const ExtraID kCaldoriaWakeUpView1 = 0; +static const ExtraID kCaldoria00WakeUp1 = 1; +static const ExtraID kCaldoria00WakeUp2 = 2; +static const ExtraID kCaldoria00SitDown = 3; +static const ExtraID k4DEnvironOpenToINN = 4; +static const ExtraID k4DINNInterruption = 5; +static const ExtraID k4DINNIntro = 6; +static const ExtraID k4DINNMarkJohnson = 7; +static const ExtraID k4DINNMeganLove = 8; +static const ExtraID k4DINNFadeOut = 9; +static const ExtraID k4DEnvironOpenFromINN = 10; +static const ExtraID k4DEnvironOpen = 11; +static const ExtraID k4DEnvironOpenView = 12; +static const ExtraID k4DEnvironClose = 13; +static const ExtraID k4DIslandLoop = 14; +static const ExtraID k4DDesertLoop = 15; +static const ExtraID k4DMountainLoop = 16; +static const ExtraID k4DIsland1ToIsland0 = 17; +static const ExtraID k4DIsland2ToIsland0 = 18; +static const ExtraID k4DIsland0ToDesert0 = 19; +static const ExtraID k4DIsland1ToDesert0 = 20; +static const ExtraID k4DIsland2ToDesert0 = 21; +static const ExtraID k4DIsland0ToMountain0 = 22; +static const ExtraID k4DIsland1ToMountain0 = 23; +static const ExtraID k4DIsland2ToMountain0 = 24; +static const ExtraID k4DDesert0ToIsland0 = 25; +static const ExtraID k4DDesert1ToIsland0 = 26; +static const ExtraID k4DDesert2ToIsland0 = 27; +static const ExtraID k4DDesert0ToMountain0 = 28; +static const ExtraID k4DDesert1ToMountain0 = 29; +static const ExtraID k4DDesert2ToMountain0 = 30; +static const ExtraID k4DMountain0ToIsland0 = 31; +static const ExtraID k4DMountain1ToIsland0 = 32; +static const ExtraID k4DMountain2ToIsland0 = 33; +static const ExtraID k4DMountain0ToDesert0 = 34; +static const ExtraID k4DMountain1ToDesert0 = 35; +static const ExtraID k4DMountain2ToDesert0 = 36; +static const ExtraID kCaBathroomGreeting = 37; +static const ExtraID kCaBathroomBodyFat = 38; +static const ExtraID kCaBathroomStylistIntro = 39; +static const ExtraID kCaBathroomRetrothrash = 40; +static const ExtraID kCaBathroomRetrothrashReturn = 41; +static const ExtraID kCaBathroomGeoWave = 42; +static const ExtraID kCaBathroomGeoWaveReturn = 43; +static const ExtraID kCaBathroomAgencyStandard = 44; +static const ExtraID kCaldoriaShowerTitle = 45; +static const ExtraID kCaldoriaShowerButton = 46; +static const ExtraID kCaldoriaShowerDown = 47; +static const ExtraID kCaldoriaShowerUp = 48; +static const ExtraID kCaBedroomVidPhone = 49; +static const ExtraID kCaBedroomMessage1 = 50; +static const ExtraID kCaBedroomMessage2 = 51; +static const ExtraID kCreateOrangeJuice = 52; +static const ExtraID kDisposeOrangeJuice = 53; +static const ExtraID kReplicatorNorthViewWithOJ = 54; +static const ExtraID kLeftDrawerOpen = 55; +static const ExtraID kLeftDrawerClose = 56; +static const ExtraID kRightDrawerOpenWithKeys = 57; +static const ExtraID kRightDrawerCloseWithKeys = 58; +static const ExtraID kRightDrawerOpenNoKeys = 59; +static const ExtraID kRightDrawerCloseNoKeys = 60; +static const ExtraID kRightDrawerOpenViewWithKeys = 61; +static const ExtraID kRightDrawerOpenViewNoKeys = 62; +static const ExtraID kCaldoria16ElevatorUp = 63; +static const ExtraID kCaldoria16ElevatorDown = 64; +static const ExtraID kCaldoria16SouthViewWithElevator = 65; +static const ExtraID kCaldoria20Doorbell = 66; +static const ExtraID kCaldoria21Doorbell = 67; +static const ExtraID kCaldoria26Doorbell = 68; +static const ExtraID kCaldoriaFourthToGround = 69; +static const ExtraID kCaldoriaRoofToFourth = 70; +static const ExtraID kCaldoriaRoofToGround = 71; +static const ExtraID kCaldoriaGroundToFourth = 72; +static const ExtraID kCaldoriaGroundToRoof = 73; +static const ExtraID kCaldoriaFourthToRoof = 74; +static const ExtraID kCaldoria29Doorbell = 75; +static const ExtraID kCaldoria34Doorbell = 76; +static const ExtraID kCaldoria35Doorbell = 77; +static const ExtraID kBinocularsZoomInOnShip = 78; +static const ExtraID kCaldoriaKioskVideo = 79; +static const ExtraID kCaldoriaTransporterArrowLoop = 80; +static const ExtraID kArriveAtCaldoriaFromTSA = 81; +static const ExtraID kCaGTOtherChoice = 82; +static const ExtraID kCaGTCardSwipe = 83; +static const ExtraID kCaGTSelectTSA = 84; +static const ExtraID kCaGTFryTheFly = 85; +static const ExtraID kCaGTGoToTSA = 86; +static const ExtraID kCaGTSelectBeach = 87; +static const ExtraID kCaGTGoToBeach = 88; +static const ExtraID kCaGTArriveAtBeach = 89; +static const ExtraID kCaGTSelectTokyo = 90; +static const ExtraID kCaGTGoToTokyo = 91; +static const ExtraID kCaGTArriveAtTokyo = 92; +static const ExtraID kCa48NorthRooftopClosed = 93; +static const ExtraID kCa48NorthExplosion = 94; +static const ExtraID kCa48NorthExplosionDeath = 95; +static const ExtraID kCa49NorthVoiceAnalysis = 96; +static const ExtraID kCa50SinclairShoots = 97; +static const ExtraID kCa53EastZoomToSinclair = 98; +static const ExtraID kCa53EastDeath2 = 99; +static const ExtraID kCa53EastShootSinclair = 100; +static const ExtraID kCa53EastZoomOutFromSinclair = 101; +static const ExtraID kCa54SouthDeath = 102; +static const ExtraID kCaldoria56BombStage1 = 103; +static const ExtraID kCaldoria56BombStage2 = 104; +static const ExtraID kCaldoria56BombStage3 = 105; +static const ExtraID kCaldoria56BombStage4 = 106; +static const ExtraID kCaldoria56BombStage5 = 107; +static const ExtraID kCaldoria56BombStage6 = 108; +static const ExtraID kCaldoria56BombStage7 = 109; +static const ExtraID kCaldoria56BombExplodes = 110; + +// Caldoria interactions. + +static const InteractionID kCaldoria4DInteractionID = 0; +static const InteractionID kCaldoriaBombInteractionID = 1; +static const InteractionID kCaldoriaMessagesInteractionID = 2; +static const InteractionID kCaldoriaMirrorInteractionID = 3; + +// Caldoria: + +static const DisplayOrder kVidPhoneOrder = kMonitorLayer; +static const DisplayOrder k4DSpritesOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaMessagesOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaElevatorOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaA05LightLoopOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaA07LightLoopOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaBombGridOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaBombTimerOrder = kCaldoriaBombGridOrder + 1; + +///////////////////////////////////////////// +// +// Caldoria + +static const CoordType kCaldoriaVidPhoneLeft = kNavAreaLeft + 105; +static const CoordType kCaldoriaVidPhoneTop = kNavAreaTop + 28; + +static const CoordType kCaldoria4DSpritesLeft = kNavAreaLeft + 10; +static const CoordType kCaldoria4DSpritesTop = kNavAreaTop + 142; + +static const CoordType kCaldoriaMessageLeft = kNavAreaLeft + 202; +static const CoordType kCaldoriaMessageTop = kNavAreaTop + 26; + +static const CoordType kCaldoriaElevatorLeft = kNavAreaLeft + 407; +static const CoordType kCaldoriaElevatorTop = kNavAreaTop + 138; + +static const CoordType kCaldoriaA05LightLoopLeft = kNavAreaLeft + 213; +static const CoordType kCaldoriaA05LightLoopTop = kNavAreaTop + 215; + +static const CoordType kCaldoriaA07LightLoopLeft = kNavAreaLeft + 414; +static const CoordType kCaldoriaA07LightLoopTop = kNavAreaTop + 215; + +static const CoordType kCaldoriaGunSpriteLeft = kNavAreaLeft + 276; +static const CoordType kCaldoriaGunSpriteTop = kNavAreaTop + 115; + +static const CoordType kCaldoria11MessageLoopLeft = kNavAreaLeft + 135; +static const CoordType kCaldoria11MessageLoopTop = kNavAreaTop + 214; + +static const CoordType kCaldoria12MessageLoopLeft = kNavAreaLeft + 209; +static const CoordType kCaldoria12MessageLoopTop = kNavAreaTop + 170; + +static const CoordType kCaldoria13MessageLoopLeft = kNavAreaLeft + 480; +static const CoordType kCaldoria13MessageLoopTop = kNavAreaTop + 191; + +static const CoordType kCaldoria14MessageLoopLeft = kNavAreaLeft + 248; +static const CoordType kCaldoria14MessageLoopTop = kNavAreaTop + 191; + +static const CoordType kCaldoria48CardBombLoopLeft = kNavAreaLeft + 337; +static const CoordType kCaldoria48CardBombLoopTop = kNavAreaTop + 205; + +static const CoordType kCaldoriaBombGridLeft = kNavAreaLeft + 290; +static const CoordType kCaldoriaBombGridTop = kNavAreaTop + 58; + +static const CoordType kCaldoriaBombTimerLeft = kNavAreaLeft + 58; +static const CoordType kCaldoriaBombTimerTop = kNavAreaTop + 204; + +// Caldoria display IDs. + +static const DisplayElementID kCaldoriaVidPhoneID = kNeighborhoodDisplayID; +static const DisplayElementID kCaldoria4DSpritesID = kCaldoriaVidPhoneID + 1; +static const DisplayElementID kCaldoriaMessagesID = kCaldoria4DSpritesID + 1; +static const DisplayElementID kCaldoriaUtilityID = kCaldoriaMessagesID + 1; +static const DisplayElementID kCaldoriaBombGridID = kCaldoriaUtilityID + 1; +static const DisplayElementID kCaldoriaBombTimerID = kCaldoriaBombGridID + 1; + +static const TimeValue kCaldoria4DBlankChoiceIn = 29730; +static const TimeValue kCaldoria4DBlankChoiceOut = 33910; + +class Caldoria; + +class SinclairCallBack : public TimeBaseCallBack { +public: + SinclairCallBack(Caldoria *); + ~SinclairCallBack() {} + +protected: + virtual void callBack(); + + Caldoria *_caldoria; +}; + +class Caldoria : public Neighborhood { +friend class SinclairCallBack; + +public: + Caldoria(InputHandler *, PegasusEngine *); + virtual ~Caldoria(); + + virtual uint16 getDateResID() const; + + void pickedUpItem(Item *); + + virtual GameInteraction *makeInteraction(const InteractionID); + + virtual Common::String getBriefingMovie(); + virtual Common::String getEnvScanMovie(); + virtual uint getNumHints(); + virtual Common::String getHintMovie(uint); + void loadAmbientLoops(); + bool wantsCursor(); + void flushGameState(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + +protected: + enum { + kCaldoriaPrivate4DSystemOpenFlag, + kCaloriaPrivateLeftDrawerOpenFlag, + kCaldoriaPrivateRightDrawerOpenFlag, + kCaldoriaPrivateReadyToShootFlag, + kCaldoriaPrivateZoomingToBombFlag, + kCaldoriaPrivateCanOpenElevatorDoorFlag, + kCaldoriaPrivateSinclairTimerExpiredFlag, + kCaldoriaPrivateSeen13CarFlag, + kCaldoriaPrivateSeen14CarFlag, + kCaldoriaPrivateSeen18CarFlag, + kCaldoriaPrivateSeen23CarFlag, + kCaldoriaPrivateSeen33CarFlag, + kCaldoriaPrivateSeen36CarFlag, + kCaldoriaPrivateSeen41NorthCarFlag, + kCaldoriaPrivateSeen41EastCarFlag, + kCaldoriaPrivateSeen41WestCarFlag, + kNumCaldoriaPrivateFlags + }; + + void init(); + void start(); + + void setUpRoofTop(); + + void setUpAIRules(); + void doAIRecalibration(); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + void startSpotOnceOnly(TimeValue, TimeValue); + void startExitMovie(const ExitTable::Entry &); + void startZoomMovie(const ZoomTable::Entry &); + void startDoorOpenMovie(const TimeValue, const TimeValue); + void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant); + void bumpIntoWall(); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec &); + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + void spotCompleted(); + void arriveAt(const RoomID, const DirectionConstant); + void arriveAtCaldoria00(); + void arriveAtCaldoriaToilet(); + void arriveAtCaldoria44(); + void arriveAtCaldoria49(); + void arriveAtCaldoria56(); + void arriveAtCaldoriaDeath(); + void turnTo(const DirectionConstant); + void zoomTo(const Hotspot *); + void downButton(const Input &); + void receiveNotification(Notification *, const NotificationFlags); + InputBits getInputFilter(); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void newInteraction(const InteractionID); + + void clickOnDoorbell(const HotSpotID); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void dropItemIntoRoom(Item *, Hotspot *); + void takeElevator(uint, uint); + void updateElevatorMovie(); + void openElevatorMovie(); + void emptyOJGlass(); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + void doorBombTimerExpired(); + void sinclairTimerExpired(); + void checkSinclairShootsOS(); + void setUpSinclairLoops(); + void zoomToSinclair(); + void playEndMessage(); + void checkInterruptSinclair(); + + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void doorOpened(); + + void updateCursor(const Common::Point, const Hotspot *); + + FlagsArray<uint16, kNumCaldoriaPrivateFlags> _privateFlags; + + const Hotspot *_zoomOutSpot; + + FuseFunction _utilityFuse; + + long _sinclairLoopCount; + long _numSinclairLoops; + + Sprite *_gunSprite; + + SinclairCallBack _sinclairInterrupt; + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp new file mode 100644 index 0000000000..0494753661 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp @@ -0,0 +1,370 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h" + +namespace Pegasus { + +static const TimeValue kSwitchableSlop = 3 * kCaldoriaFrameDuration; +// Two seconds - some slop +static const TimeValue kSwitchableDuration = kCaldoriaMovieScale * 2 - kSwitchableSlop; +// Twelve frames + some slop +static const TimeValue kNonswitchableDuration = kCaldoriaFrameDuration * 12 + kSwitchableSlop; + +static const TimeValue kSwitchable1Start = 0; +static const TimeValue kSwitchable1Stop = kSwitchable1Start + kSwitchableDuration; + +static const TimeValue kSwitchable2Start = kSwitchable1Stop + kNonswitchableDuration; +static const TimeValue kSwitchable2Stop = kSwitchable2Start + kSwitchableDuration; + +static const TimeValue kSwitchable3Start = kSwitchable2Stop + kNonswitchableDuration; +static const TimeValue kSwitchable3Stop = kSwitchable3Start + kSwitchableDuration; + +static const NotificationFlags kVidPhoneDoneFlag = 1; + +static const TimeValue kRockMusicLoopIn = 0; +static const TimeValue kRockMusicLoopOut = 2088; + +static const TimeValue kOrchestralMusicLoopIn = 2088; +static const TimeValue kOrchestralMusicLoopOut = 4985; + +static const TimeValue kRhythmsMusicLoopIn = 4985; +static const TimeValue kRhythmsMusicLoopOut = 6824; + +static const TimeValue kAcousticMusicLoopIn = 6824; +static const TimeValue kAcousticMusicLoopOut = 9387; + +enum { + k4DVideoMenu, + k4DAudioMenu, + k4DShuttingDown, + + // These constants are the exact frame numbers of the sprite movie. + k4DRockChoice = 0, + k4DOrchestralChoice, + k4DRhythmsChoice, + k4DAcousticChoice, + k4DIslandChoice, + k4DDesertChoice, + k4DMountainChoice, + + k4DFirstVideoChoice = k4DIslandChoice +}; + +static const ExtraID s_transitionExtras0[3][3] = { + { 0xffffffff, k4DIsland0ToDesert0, k4DIsland0ToMountain0 }, + { k4DDesert0ToIsland0, 0xffffffff, k4DDesert0ToMountain0 }, + { k4DMountain0ToIsland0, k4DMountain0ToDesert0, 0xffffffff } +}; + +static const ExtraID s_transitionExtras1[3][3] = { + { 0xffffffff, k4DIsland1ToDesert0, k4DIsland1ToMountain0 }, + { k4DDesert1ToIsland0, 0xffffffff, k4DDesert1ToMountain0 }, + { k4DMountain1ToIsland0, k4DMountain1ToDesert0, 0xffffffff } +}; + +static const ExtraID s_transitionExtras2[3][3] = { + { 0xffffffff, k4DIsland2ToDesert0, k4DIsland2ToMountain0 }, + { k4DDesert2ToIsland0, 0xffffffff, k4DDesert2ToMountain0 }, + { k4DMountain2ToIsland0, k4DMountain2ToDesert0, 0xffffffff } +}; + +static const ExtraID s_shutDownExtras[3][3] = { + { 0xffffffff, k4DIsland1ToIsland0, k4DIsland2ToIsland0 }, + { k4DDesert0ToIsland0, k4DDesert1ToIsland0, k4DDesert2ToIsland0 }, + { k4DMountain0ToIsland0, k4DMountain1ToIsland0, k4DMountain2ToIsland0 } +}; + +Caldoria4DSystem::Caldoria4DSystem(Neighborhood *owner) : GameInteraction(kCaldoria4DInteractionID, owner), + _4DSpritesMovie(kCaldoria4DSpritesID) { + g_AIArea->lockAIOut(); +} + +Caldoria4DSystem::~Caldoria4DSystem() { + g_AIArea->unlockAI(); +} + +void Caldoria4DSystem::openInteraction() { + _whichMenu = k4DVideoMenu; + _videoChoice = k4DIslandChoice; + _audioChoice = k4DRockChoice; + _clickedHotspotID = kNoHotSpotID; + + _4DSpritesMovie.initFromMovieFile("Images/Caldoria/4D Sprites", true); + _4DSpritesMovie.moveElementTo(kCaldoria4DSpritesLeft, kCaldoria4DSpritesTop); + _4DSpritesMovie.setDisplayOrder(k4DSpritesOrder); + _4DSpritesMovie.startDisplaying(); + _4DSpritesMovie.show(); + + _4DSpritesScale = _4DSpritesMovie.getScale(); + + _neighborhoodNotification = _owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + + startIdling(); +} + +void Caldoria4DSystem::loopExtra(const ExtraID extraID) { + ExtraTable::Entry extraEntry; + + _owner->getExtraEntry(extraID, extraEntry); + _loopStart = extraEntry.movieStart; + _owner->loopExtraSequence(extraID); +} + +void Caldoria4DSystem::useIdleTime() { + if (_whichMenu == k4DShuttingDown) { + TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart; + ExtraID extraID; + + if (movieTime < kSwitchable1Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][0]; + else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][1]; + else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) + extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][2]; + else + extraID = 0xffffffff; + + if (extraID != 0xffffffff) { + setSpritesMovie(); + _loopStart = 0; + _owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + } + } else if (_clickedHotspotID != kNoHotSpotID) { + TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart; + ExtraID extraID; + + if (movieTime < kSwitchable1Stop) { + extraID = s_transitionExtras0[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) { + extraID = s_transitionExtras1[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) { + extraID = s_transitionExtras2[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; + _clickedHotspotID = kNoHotSpotID; + } else + extraID = 0xffffffff; + + if (extraID != 0xffffffff) { + switch (extraID) { + case k4DDesert0ToIsland0: + case k4DMountain0ToIsland0: + case k4DDesert1ToIsland0: + case k4DMountain1ToIsland0: + case k4DDesert2ToIsland0: + case k4DMountain2ToIsland0: + _videoChoice = k4DIslandChoice; + break; + case k4DIsland0ToDesert0: + case k4DMountain0ToDesert0: + case k4DIsland1ToDesert0: + case k4DMountain1ToDesert0: + case k4DIsland2ToDesert0: + case k4DMountain2ToDesert0: + _videoChoice = k4DDesertChoice; + break; + case k4DDesert0ToMountain0: + case k4DIsland0ToMountain0: + case k4DIsland1ToMountain0: + case k4DDesert1ToMountain0: + case k4DIsland2ToMountain0: + case k4DDesert2ToMountain0: + _videoChoice = k4DMountainChoice; + break; + } + + setSpritesMovie(); + _loopStart = 0; + _owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + } + } +} + +void Caldoria4DSystem::initInteraction() { + setSpritesMovie(); + + _owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff"); + loopExtra(k4DIslandLoop); +} + +void Caldoria4DSystem::closeInteraction() { + stopIdling(); + _neighborhoodNotification->cancelNotification(this); + _4DSpritesMovie.releaseMovie(); + _owner->loadAmbientLoops(); +} + +void Caldoria4DSystem::setSpritesMovie() { + if (_whichMenu == k4DShuttingDown) + _4DSpritesMovie.setTime(_4DSpritesScale * k4DIslandChoice); + else if (_whichMenu == k4DVideoMenu) + _4DSpritesMovie.setTime(_4DSpritesScale * _videoChoice); + else if (_whichMenu == k4DAudioMenu) + _4DSpritesMovie.setTime(_4DSpritesScale * _audioChoice); + + _4DSpritesMovie.redrawMovieWorld(); +} + +void Caldoria4DSystem::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (input.downButtonAnyDown()) + return; + if (input.anyDirectionInput()) + shutDown4DSystem(); + else + GameInteraction::handleInput(input, cursorSpot); +} + +void Caldoria4DSystem::activateHotspots() { + GameInteraction::activateHotspots(); + if (_whichMenu == k4DAudioMenu) + g_allHotspots.activateOneHotspot(kCa4DChoice4SpotID); +} + +void Caldoria4DSystem::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCa4DVisualSpotID: + if (_whichMenu == k4DAudioMenu) { + _whichMenu = k4DVideoMenu; + setSpritesMovie(); + } + break; + case kCa4DAudioSpotID: + if (_whichMenu == k4DVideoMenu) { + _whichMenu = k4DAudioMenu; + setSpritesMovie(); + } + break; + case kCa4DChoice1SpotID: + if (_whichMenu == k4DVideoMenu) + makeIslandChoice(); + else if (_whichMenu == k4DAudioMenu) + makeRockChoice(); + break; + case kCa4DChoice2SpotID: + if (_whichMenu == k4DVideoMenu) + makeDesertChoice(); + else if (_whichMenu == k4DAudioMenu) + makeOrchestralChoice(); + break; + case kCa4DChoice3SpotID: + if (_whichMenu == k4DVideoMenu) + makeMountainChoice(); + else if (_whichMenu == k4DAudioMenu) + makeRhythmsChoice(); + break; + case kCa4DChoice4SpotID: + if (_whichMenu == k4DAudioMenu) + makeAcousticChoice(); + else + _owner->playSpotSoundSync(kCaldoria4DBlankChoiceIn, kCaldoria4DBlankChoiceOut); + break; + default: + GameInteraction::clickInHotspot(input, spot); + } +} + +void Caldoria4DSystem::receiveNotification(Notification *, const NotificationFlags) { + if (_whichMenu == k4DShuttingDown) { + _owner->requestDeleteCurrentInteraction(); + } else { + uint32 extraID; + + switch (_videoChoice) { + case k4DIslandChoice: + extraID = k4DIslandLoop; + break; + case k4DDesertChoice: + extraID = k4DDesertLoop; + break; + case k4DMountainChoice: + extraID = k4DMountainLoop; + break; + default: + extraID = 0xffffffff; + break; + } + + if (extraID != 0xffffffff) + loopExtra(extraID); + } +} + +void Caldoria4DSystem::makeIslandChoice() { + if (_videoChoice != k4DIslandChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice1SpotID; +} + +void Caldoria4DSystem::makeDesertChoice() { + if (_videoChoice != k4DDesertChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice2SpotID; +} + +void Caldoria4DSystem::makeMountainChoice() { + if (_videoChoice != k4DMountainChoice && _clickedHotspotID == kNoHotSpotID) + _clickedHotspotID = kCa4DChoice3SpotID; +} + +void Caldoria4DSystem::makeRockChoice() { + if (_audioChoice != k4DRockChoice) { + _audioChoice = k4DRockChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff"); + } +} + +void Caldoria4DSystem::makeOrchestralChoice() { + if (_audioChoice != k4DOrchestralChoice) { + _audioChoice = k4DOrchestralChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Orchestral.aiff"); + } +} + +void Caldoria4DSystem::makeRhythmsChoice() { + if (_audioChoice != k4DRhythmsChoice) { + _audioChoice = k4DRhythmsChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Rhythms.aiff"); + } +} + +void Caldoria4DSystem::makeAcousticChoice() { + if (_audioChoice != k4DAcousticChoice) { + _audioChoice = k4DAcousticChoice; + setSpritesMovie(); + _owner->loadLoopSound1("Sounds/Caldoria/Acoustic.aiff"); + } +} + +void Caldoria4DSystem::shutDown4DSystem() { + _whichMenu = k4DShuttingDown; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h new file mode 100644 index 0000000000..1c5fa44b90 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class Caldoria4DSystem : public GameInteraction, private Idler, public NotificationReceiver { +public: + Caldoria4DSystem(Neighborhood *); + virtual ~Caldoria4DSystem(); + + void shutDown4DSystem(); + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void receiveNotification(Notification *, const NotificationFlags); + void setSpritesMovie(); + void makeIslandChoice(); + void makeRockChoice(); + void makeMountainChoice(); + void makeOrchestralChoice(); + void makeDesertChoice(); + void makeRhythmsChoice(); + void makeAcousticChoice(); + + void useIdleTime(); + void loopExtra(const ExtraID); + + Movie _4DSpritesMovie; + TimeScale _4DSpritesScale; + uint _whichMenu; + uint _videoChoice; + uint _audioChoice; + Notification *_neighborhoodNotification; + TimeValue _loopStart; + HotSpotID _clickedHotspotID; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp new file mode 100644 index 0000000000..abf34d3863 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp @@ -0,0 +1,1442 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriabomb.h" + +namespace Pegasus { + +// Bomb game PICTs: + +static const uint16 kYellowBombPICTBaseID = 700; +static const uint16 kRedBombPICTBaseID = 709; +static const uint16 kTimerLeftPICTID = 718; +static const uint16 kTimerRightPICTID = 719; + +static const uint32 kFlashOnTime = 20; +static const uint32 kFlashOffTime = 10; + +static const uint32 kOnTime1 = kFlashOnTime; +static const uint32 kOffTime1 = kOnTime1 + kFlashOffTime; +static const uint32 kOnTime2 = kOffTime1 + kFlashOnTime; +static const uint32 kOffTime2 = kOnTime2 + kFlashOffTime; +static const uint32 kOnTime3 = kOffTime2 + kFlashOnTime; +static const uint32 kOffTime3 = kOnTime3 + kFlashOffTime; +static const uint32 kOnTime4 = kOffTime3 + kFlashOnTime; + +static const HotSpotID kVertextHotSpotBaseID = 10000; + +static const CoordType kVertextHotSpotWidth = 24; +static const CoordType kVertextHotSpotHeight = 24; + +static const NotificationFlags kBombTimerExpiredFlag = 1; + +static const VertexType kBombLevelOne[] = { + 0, 1, 0, 1, 0, // hot vertices first. + 1, 1, 0, 1, 1, + 1, 1, 0, 1, 0, + 1, 1, 0, 1, 1, + 0, 1, 0, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 9, // 9 edges in this level + + kEdgeOneFourth, + 3, + 1, 2, 3, + 0, 0, + + kEdgeOneFourth, + 5, + 5, 6, 7, 8, 9, + 0, 0, 0, 0, + + kEdgeOneFourth, + 4, + 10, 11, 12, 13, + 0, 0, 0, + + kEdgeOneFourth, + 5, + 15, 16, 17, 18, 19, + 0, 0, 0, 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 3, + 5, 10, 15, + 0, 0, + + kEdgeOneHalf, + 5, + 1, 6, 11, 16, 21, + 0, 0, 0, 0, + + kEdgeOneHalf, + 5, + 3, 8, 13, 18, 23, + 0, 0, 0, 0, + + kEdgeOneHalf, + 3, + 9, 14, 19, + 0, 0 +}; + +static const VertexType kBombLevelTwo[] = { + 0, 1, 0, 1, 0, + 1, 1, 1, 0, 1, + 0, 0, 0, 1, 0, + 1, 1, 1, 0, 1, + 0, 1, 0, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 15, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 3, + 17, 13, 9, + 0, 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 2, + 3, 9, + 0, + + kEdgeThreeEighths, + 3, + 7, 13, 19, + 0, 0, + + kEdgeThreeEighths, + 2, + 15, 21, + 0, + + kEdgeOneFourth, + 3, + 1, 2, 3, + 0, 0, + + kEdgeOneFourth, + 4, + 6, 7, 8, 9, + 0, 0, 0, + + kEdgeOneFourth, + 4, + 16, 17, 18, 19, + 0, 0, 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 3, + 5, 10, 15, + 0, 0, + + kEdgeOneHalf, + 2, + 1, 6, + 0, + + kEdgeOneHalf, + 3, + 7, 12, 17, + 0, 0, + + kEdgeOneHalf, + 3, + 9, 14, 19, + 0, 0, + + kEdgeOneHalf, + 2, + 16, 21, + 0 +}; + +static const VertexType kBombLevelThree[] = { + 0, 1, 0, 1, 0, + 1, 1, 1, 1, 1, + 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, + 0, 1, 0, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 22, + + kEdgeThreeSixteenths, + 3, + 15, 12, 9, + 0, 0, + + kEdgeFiveSixteenths, + 3, + 5, 12, 19, + 0, 0, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 2, + 7, 3, + 0, + + kEdgeOneEighth, + 2, + 15, 11, + 0, + + kEdgeOneEighth, + 2, + 21, 17, + 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 2, + 1, 7, + 0, + + kEdgeThreeEighths, + 2, + 3, 9, + 0, + + kEdgeThreeEighths, + 2, + 5, 11, + 0, + + kEdgeThreeEighths, + 2, + 15, 21, + 0, + + kEdgeThreeEighths, + 2, + 17, 23, + 0, + + kEdgeOneFourth, + 3, + 1, 2, 3, + 0, 0, + + kEdgeOneFourth, + 2, + 5, 6, + 0, + + kEdgeOneFourth, + 2, + 8, 9, + 0, + + kEdgeOneFourth, + 2, + 15, 16, + 0, + + kEdgeOneFourth, + 2, + 18, 19, + 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 2, + 1, 6, + 0, + + kEdgeOneHalf, + 2, + 3, 8, + 0, + + kEdgeOneHalf, + 2, + 16, 21, + 0, + + kEdgeOneHalf, + 2, + 18, 23, + 0 +}; + +static const VertexType kBombLevelFour[] = { + 1, 1, 1, 1, 0, + 1, 1, 0, 1, 1, + 1, 0, 1, 0, 1, + 1, 1, 0, 1, 1, + 0, 1, 1, 1, 1, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 19, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 3, + 10, 6, 2, + 0, 0, + + kEdgeOneEighth, + 3, + 16, 12, 8, + 0, 0, + + kEdgeOneEighth, + 3, + 22, 18, 14, + 0, 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 3, + 2, 8, 14, + 0, 0, + + kEdgeThreeEighths, + 3, + 10, 16, 22, + 0, 0, + + kEdgeOneFourth, + 4, + 0, 1, 2, 3, + 0, 0, 0, + + kEdgeOneFourth, + 2, + 5, 6, + 0, + + kEdgeOneFourth, + 2, + 8, 9, + 0, + + kEdgeOneFourth, + 2, + 15, 16, + 0, + + kEdgeOneFourth, + 2, + 18, 19, + 0, + + kEdgeOneFourth, + 4, + 21, 22, 23, 24, + 0, 0, 0, + + kEdgeOneHalf, + 4, + 0, 5, 10, 15, + 0, 0, 0, + + kEdgeOneHalf, + 2, + 1, 6, + 0, + + kEdgeOneHalf, + 2, + 3, 8, + 0, + + kEdgeOneHalf, + 4, + 9, 14, 19, 24, + 0, 0, 0, + + kEdgeOneHalf, + 2, + 16, 21, + 0, + + kEdgeOneHalf, + 2, + 18, 23, + 0 +}; + +static const VertexType kBombLevelFive[] = { + 0, 1, 0, 1, 0, + 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, + 1, 1, 1, 1, 1, + 0, 1, 0, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 19, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 2, + 7, 3, + 0, + + kEdgeOneEighth, + 2, + 13, 9, + 0, + + kEdgeOneEighth, + 2, + 15, 11, + 0, + + kEdgeOneEighth, + 2, + 21, 17, + 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 2, + 1, 7, + 0, + + kEdgeThreeEighths, + 4, + 5, 11, 17, 23, + 0, 0, 0, + + kEdgeThreeEighths, + 3, + 6, 12, 18, + 0, 0, + + kEdgeThreeEighths, + 2, + 13, 19, + 0, + + kEdgeThreeEighths, + 2, + 15, 21, + 0, + + kEdgeOneFourth, + 5, + 5, 6, 7, 8, 9, + 0, 0, 0, 0, + + kEdgeOneFourth, + 3, + 15, 16, 17, + 0, 0, + + kEdgeOneFourth, + 2, + 18, 19, + 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 3, + 5, 10, 15, + 0, 0, + + kEdgeOneHalf, + 2, + 1, 6, + 0, + + kEdgeOneHalf, + 3, + 11, 16, 21, + 0, 0, + + kEdgeOneHalf, + 5, + 3, 8, 13, 18, 23, + 0, 0, 0, 0 +}; + +static const VertexType kBombLevelSix[] = { + 0, 1, 1, 1, 0, + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 25, + + kEdgeOneSixteenth, + 2, + 10, 1, + 0, + + kEdgeOneSixteenth, + 2, + 23, 14, + 0, + + kEdgeSevenSixteenths, + 2, + 3, 14, + 0, + + kEdgeSevenSixteenths, + 2, + 10, 21, + 0, + + kEdgeOneEighth, + 2, + 5, 1, + 0, + + kEdgeOneEighth, + 3, + 10, 6, 2, + 0, 0, + + kEdgeOneEighth, + 2, + 7, 3, + 0, + + kEdgeOneEighth, + 2, + 21, 17, + 0, + + kEdgeOneEighth, + 3, + 22, 18, 14, + 0, 0, + + kEdgeOneEighth, + 2, + 23, 19, + 0, + + kEdgeThreeEighths, + 2, + 1, 7, + 0, + + kEdgeThreeEighths, + 3, + 2, 8, 14, + 0, 0, + + kEdgeThreeEighths, + 2, + 3, 9, + 0, + + kEdgeThreeEighths, + 3, + 10, 16, 22, + 0, 0, + + kEdgeThreeEighths, + 2, + 15, 21, + 0, + + kEdgeThreeEighths, + 2, + 17, 23, + 0, + + kEdgeOneFourth, + 3, + 1, 2, 3, + 0, 0, + + kEdgeOneFourth, + 3, + 6, 7, 8, + 0, 0, + + kEdgeOneFourth, + 3, + 16, 17, 18, + 0, 0, + + kEdgeOneFourth, + 3, + 21, 22, 23, + 0, 0, + + kEdgeOneHalf, + 3, + 5, 10, 15, + 0, 0, + + kEdgeOneHalf, + 3, + 6, 11, 16, + 0, 0, + + kEdgeOneHalf, + 5, + 2, 7, 12, 17, 22, + 0, 0, 0, 0, + + kEdgeOneHalf, + 3, + 8, 13, 18, + 0, 0, + + kEdgeOneHalf, + 3, + 9, 14, 19, + 0, 0 +}; + +static const CoordType kBombGridWidth = 140; +static const CoordType kBombGridHeight = 140; + +static const CoordType kDotOriginX = 0; +static const CoordType kDotOriginY = 0; + +static const CoordType kVertOriginX = 2; +static const CoordType kVertOriginY = 6; + +static const CoordType kHorizOriginX = 6; +static const CoordType kHorizOriginY = 2; + +static const CoordType kDiagOriginX = 6; +static const CoordType kDiagOriginY = 6; + +static const int g_originsX[] = { + kDiagOriginX, + kDiagOriginX, + kDiagOriginX, + kHorizOriginX, + kDiagOriginX, + kDiagOriginX, + kDiagOriginX, + kVertOriginX +}; + +static const int g_originsY[] = { + kDiagOriginY - 64, + kDiagOriginY - 32, + kDiagOriginY - 32, + kHorizOriginY, + kDiagOriginY, + kDiagOriginY, + kDiagOriginY, + kVertOriginY +}; + +struct HotVerticesList { + int numHotVerts; + VertexType hotVerts[25]; +}; + +CoordType vertToX(VertexType vertex) { + return (vertex % 5) * 32; +} + +CoordType vertToY(VertexType vertex) { + return (vertex / 5) * 32; +} + +// This function returns the number of edges in the bomb edge list. +VertexType getNumEdges(BombEdgeList edges) { + return edges[50]; +} + +// These four functions return pointers into the given edge list. + +// getFirstEdge and getNextEdge can be used to iterate across all edges +// in an edge list. These functions can be used to walk all the edges +// in a bomb edge list for drawing. +VertexType *getFirstEdge(BombEdgeList edges) { + return &edges[51]; +} + +VertexType *getNextEdge(VertexType *anEdge) { + return anEdge + *(anEdge + 1) * 2 + 1; +} + +// getVertices returns a pointer to all of the vertices that should are +// hot. These vertices indicate all the vertices that should be drawn in +// the game. +VertexType *getVertices(BombEdgeList edges) { + return &edges[0]; +} + +// getUsedVertices returns a pointer to the "used" vertices area: the +// area that keeps track of which vertices have been set by the +// setVertexUsed used function. +VertexType *getUsedVertices(BombEdgeList edges) { + return &edges[25]; +} + +// Useful for saving. Saving the state of the bomb game is as simple as writing +// out the edge list. +int getEdgeListSize(BombEdgeList edges) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) + anEdge = getNextEdge(anEdge); + + return anEdge - edges + 4; +} + +// Returns true if the given vertex lies on the given edge. +bool vertexOnEdge(VertexType *anEdge, VertexType whichVertex) { + VertexType numVerts = *++anEdge; + + while (numVerts--) + if (*++anEdge == whichVertex) + return true; + + return false; +} + +// Given an edge list and a from vertex, this function constructs a list +// of all vertices that may be clicked on. +// if fromVertex == -1, all vertices are eligible. +// otherwise, only vertices on a line from fromVertex are eligible. +void makeHotVertexList(BombEdgeList edges, VertexType fromVertex, HotVerticesList &hotVertices) { + hotVertices.numHotVerts = 0; + + if (fromVertex == -1) { + for (VertexType i = 0; i < 25; i++) + if (edges[i]) + hotVertices.hotVerts[hotVertices.numHotVerts++] = i; + } else { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + hotVertices.hotVerts[hotVertices.numHotVerts++] = fromVertex; + + while (numEdges--) { + if (vertexOnEdge(anEdge, fromVertex)) { + VertexType *p = anEdge + 1; + VertexType numVerts = *p; + + while (numVerts--) + if (*++p != fromVertex) + hotVertices.hotVerts[hotVertices.numHotVerts++] = *p; + } + + anEdge = getNextEdge(anEdge); + } + } +} + +// Set all edges in the edge list to the value passed in "edgeVal". +// For drawing purposes, 0 can mean don't draw, and 1 and higher can +// represent different colors. +void setAllEdgesUsed(BombEdgeList edges, VertexType used) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) { + VertexType *p1 = anEdge + 1; + VertexType numVerts = *p1; + p1 += numVerts + 1; + + while (--numVerts) + *p1++ = used; + + anEdge = getNextEdge(anEdge); + } + + VertexType *p1 = edges; + VertexType *p2 = getUsedVertices(edges); + + for (VertexType i = 0; i < 25; i++, p1++, p2++) + if (*p1) + *p2 = used; +} + +// Same as setAllEdgesUsed, but only affects edges that are already set +// to a non-zero value. +void setAllUsedEdgesUsed(BombEdgeList edges, VertexType used) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) { + VertexType *p = anEdge + 1; + VertexType numVerts = *p; + p += numVerts + 1; + + while (--numVerts) { + if (*p) + *p = used; + ++p; + } + + anEdge = getNextEdge(anEdge); + } + + VertexType *p = getUsedVertices(edges); + for (VertexType i = 0; i < 25; i++, p++) + if (*p) + *p = used; +} + +// Replace all edges with value "value" with the new value "used". +void replaceUsedEdges(BombEdgeList edges, VertexType value, VertexType used) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) { + VertexType *p = anEdge + 1; + VertexType numVerts = *p; + p += numVerts + 1; + + while (--numVerts) { + if (*p == value) + *p = used; + + p++; + } + + anEdge = getNextEdge(anEdge); + } + + VertexType *p = getUsedVertices(edges); + for (VertexType i = 0; i < 25; i++, p++) + if (*p == value) + *p = used; +} + +// Set a vertex's value to "used". +void setVertexUsed(BombEdgeList edges, VertexType whichVertex, VertexType value) { + *(getUsedVertices(edges) + whichVertex) = value; +} + +// Mark an edge in the given list between the two vertices as "used". This marks +// all inbetween vertices as well, even if the vertex is not marked as a "hot" +// vertex in the hot vertex section. Returns true if doing this operation +// crosses an already marked edge. +bool setEdgeUsed(BombEdgeList edges, VertexType fromVertex, VertexType toVertex) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + bool crossed = false; + + while (numEdges--) { + VertexType *p = anEdge; + VertexType numVerts = *++p; + VertexType *fromPtr = 0; + VertexType *toPtr = 0; + VertexType i = numVerts; + p++; + + while (i--) { + if (*p == fromVertex) + fromPtr = p; + else if (*p == toVertex) + toPtr = p; + + if (fromPtr && toPtr) { + // Found the edge... + if (fromPtr > toPtr) { + p = fromPtr; + fromPtr = toPtr; + toPtr = p; + } + + p = fromPtr + numVerts; + + for (i = toPtr - fromPtr; i > 0; i--, p++) { + ++(*p); + + if (*p == 2) + crossed = true; + } + + VertexType *verts = getVertices(edges); + VertexType *usedVerts = getUsedVertices(edges); + *(usedVerts + *fromPtr) = 1; + + for (p = fromPtr + 1; p != toPtr; p++) + if (*(verts + *p)) + *(usedVerts + *p) = 1; + + *(usedVerts + *toPtr) = 1; + return crossed; + } + + p++; + } + + anEdge = getNextEdge(anEdge); + } + + return false; +} + +// Return true if all edges are used. Can be used to determine when the bomb +// game is over. +bool allEdgesUsed(BombEdgeList edges) { + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + + while (numEdges--) { + VertexType *p = anEdge + 1; + VertexType numVerts = *p; + p += numVerts + 1; + + while (--numVerts) { + if (!*p) + return false; + + ++p; + } + + anEdge = getNextEdge(anEdge); + } + + return true; +} + +BombGrid::BombGrid(const DisplayElementID id) : Picture(id) { + Common::Rect bounds(0, 0, kBombGridWidth, kBombGridHeight); + + allocateSurface(bounds); + setBounds(bounds); + _surface->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + + _transparent = true; + + _yellowDot.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID, true); + _yellowOneSixteenth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 1, true); + _yellowOneEighth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 2, true); + _yellowThreeSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 3, true); + _yellowOneFourth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 4, true); + _yellowFiveSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 5, true); + _yellowThreeEighths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 6, true); + _yellowSevenSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 7, true); + _yellowOneHalf.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 8, true); + + _redDot.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID, true); + _redOneSixteenth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 1, true); + _redOneEighth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 2, true); + _redThreeSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 3, true); + _redOneFourth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 4, true); + _redFiveSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 5, true); + _redThreeEighths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 6, true); + _redSevenSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 7, true); + _redOneHalf.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 8, true); +} + +void BombGrid::drawEdges(BombEdgeList edges) { + GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + gfx->setCurSurface(_surface); + + _surface->fillRect(Common::Rect(0, 0, kBombGridWidth, kBombGridHeight), g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + + Frame *yellowStuff = &_yellowDot; + Frame *redStuff = &_redDot; + VertexType numEdges = getNumEdges(edges); + VertexType *anEdge = getFirstEdge(edges); + VertexType i, *p; + + Common::Rect bounds; + getSurfaceBounds(bounds); + + while (numEdges--) { + p = anEdge; + VertexType edgeDirection = *p++; + VertexType numVerts = *p++; + VertexType numSegs = numVerts - 1; + + for (i = 0; i < numSegs; i++, p++) { + if (*(p + numVerts) > 0 && *(p + numVerts) < 4) { + Frame *drawStuff; + + if (*(p + numVerts) == 2) + drawStuff = redStuff; + else + drawStuff = yellowStuff; + + int x = vertToX(*p) + g_originsX[edgeDirection]; + int y = vertToY(*p) + g_originsY[edgeDirection]; + + Common::Rect r1; + drawStuff[edgeDirection + 1].getSurfaceBounds(r1); + Common::Rect r2 = r1; + r2.moveTo(x, y); + drawStuff[edgeDirection + 1].drawImage(r1, r2); + } + } + + anEdge = getNextEdge(anEdge); + } + + for (i = 0, p = getUsedVertices(edges); i < 25; i++, p++) { + if (*p > 0 && *p < 4) { + Frame *drawStuff; + + if (*p == 2) + drawStuff = redStuff; + else + drawStuff = yellowStuff; + + int x = vertToX(i) + kDotOriginX; + int y = vertToY(i) + kDotOriginY; + + Common::Rect r1; + drawStuff->getSurfaceBounds(r1); + Common::Rect r2 = r1; + r2.moveTo(x, y); + drawStuff->drawImage(r1, r2); + } + } + + triggerRedraw(); + gfx->setCurSurface(gfx->getWorkArea()); +} + +BombTimer::BombTimer(const DisplayElementID id) : IdlerAnimation(id) { + _middle = -1; + _leftImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTimerLeftPICTID); + _rightImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTimerRightPICTID); + + Common::Rect r; + _leftImage.getSurfaceBounds(r); + setBounds(r); +} + +void BombTimer::draw(const Common::Rect &updateRect) { + Common::Rect bounds; + getBounds(bounds); + + Common::Rect r1 = bounds; + r1.right = _middle; + r1 = r1.findIntersectingRect(updateRect); + + if (!r1.isEmpty()) { + Common::Rect r2 = r1; + r2.moveTo(r1.left - bounds.left, r1.top - bounds.top); + _leftImage.copyToCurrentPort(r2, r1); + } + + r1 = bounds; + r1.left = _middle; + r1 = r1.findIntersectingRect(updateRect); + + if (!r1.isEmpty()) { + Common::Rect r2 = r1; + r2.moveTo(r1.left - bounds.left, r1.top - bounds.top); + _rightImage.copyToCurrentPort(r2, r1); + } +} + +void BombTimer::timeChanged(const TimeValue newTime) { + Common::Rect bounds; + getBounds(bounds); + + int newMiddle = bounds.right - bounds.width() * newTime / getDuration(); + if (newMiddle != _middle) { + _middle = newMiddle; + triggerRedraw(); + } +} + +#define CREATE_BOMB_LEVEL(num, data) \ + _bombLevel[num] = new VertexType[sizeof(data)]; \ + memcpy(_bombLevel[num], data, sizeof(data)) + +CaldoriaBomb::CaldoriaBomb(Neighborhood *owner, NotificationManager *manager) : + GameInteraction(kCaldoriaBombInteractionID, owner), _grid(kCaldoriaBombGridID), + _timer(kCaldoriaBombTimerID), _timerNotification(kCaldoriaBombTimerNotificationID, manager) { + CREATE_BOMB_LEVEL(0, kBombLevelOne); + CREATE_BOMB_LEVEL(1, kBombLevelTwo); + CREATE_BOMB_LEVEL(2, kBombLevelThree); + CREATE_BOMB_LEVEL(3, kBombLevelFour); + CREATE_BOMB_LEVEL(4, kBombLevelFive); + CREATE_BOMB_LEVEL(5, kBombLevelSix); + _currentLevel = 0; +} + +#undef CREATE_BOMB_LEVEL + +CaldoriaBomb::~CaldoriaBomb() { + for (int i = 0; i < 6; i++) + delete[] _bombLevel[i]; +} + +void CaldoriaBomb::openInteraction() { + _grid.moveElementTo(kCaldoriaBombGridLeft, kCaldoriaBombGridTop); + _grid.setDisplayOrder(kCaldoriaBombGridOrder); + _grid.startDisplaying(); + + _timer.moveElementTo(kCaldoriaBombTimerLeft, kCaldoriaBombTimerTop); + _timer.setDisplayOrder(kCaldoriaBombTimerOrder); + _timer.startDisplaying(); + _timer.setSegment(0, kTenMinutesPerFifteenTicks, kFifteenTicksPerSecond); + _timer.setTime(0); + + _timerNotification.notifyMe(this, kBombTimerExpiredFlag, kBombTimerExpiredFlag); + _timerCallBack.setNotification(&_timerNotification); + _timerCallBack.initCallBack(&_timer, kCallBackAtExtremes); + _timerCallBack.setCallBackFlag(kBombTimerExpiredFlag); + + Common::Rect r(0, 0, kVertextHotSpotWidth, kVertextHotSpotHeight); + + for (VertexType i = 0; i < 25; i++) { + _vertexHotspot[i] = new Hotspot(i + kVertextHotSpotBaseID); + r.moveTo(vertToX(i) + kCaldoriaBombGridLeft - kVertextHotSpotWidth / 2 + 6, + vertToY(i) + kCaldoriaBombGridTop - kVertextHotSpotHeight / 2 + 6); + _vertexHotspot[i]->setArea(r); + _vertexHotspot[i]->setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + g_allHotspots.push_back(_vertexHotspot[i]); + } + + _neighborhoodNotification = _owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void CaldoriaBomb::initInteraction() { + _owner->loadLoopSound1(""); + _owner->startExtraSequence(kCaldoria56BombStage1, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaBomb::closeInteraction() { + _timer.stop(); + _timer.hide(); + _timer.stopDisplaying(); + _grid.hide(); + _grid.stopDisplaying(); + + // The original did not do this, but we need it here + // Not sure why the original worked without this; probably + // related to the way the List code worked in CodeWarrior. + // If this is not here, the notifications will later attempt + // to remove itself from this receiver causing a very nasty + // crash. + _timerNotification.cancelNotification(this); + _neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaBomb::startBombAmbient(Common::String ambient) { + _owner->loadLoopSound1(ambient); +} + +void CaldoriaBomb::receiveNotification(Notification *notification, const NotificationFlags) { + if (notification == _neighborhoodNotification) { + switch (_owner->getLastExtra()) { + case kCaldoria56BombStage1: + _grid.show(); + _timer.show(); + _timerCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _timer.start(); + _currentLevel = 0; + _lastVertex = -1; + startBombAmbient("Sounds/Caldoria/BmbLoop1.22K.AIFF"); + break; + case kCaldoria56BombStage2: + case kCaldoria56BombStage3: + case kCaldoria56BombStage4: + case kCaldoria56BombStage5: + case kCaldoria56BombStage6: + _grid.show(); + _currentLevel++; + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -1; + startBombAmbient(Common::String::format("Sounds/Caldoria/BmbLoop%d.22K.AIFF", _owner->getLastExtra() - kCaldoria56BombStage1 + 1)); + break; + case kCaldoria56BombStage7: + _owner->requestDeleteCurrentInteraction(); + GameState.setCaldoriaBombDisarmed(true); + GameState.setScoringDisarmedNuke(true); + _owner->loadAmbientLoops(); + break; + } + } else if (notification == &_timerNotification) { + _grid.hide(); + _timer.stop(); + _timer.hide(); + _owner->loadLoopSound1(""); + _owner->playDeathExtra(kCaldoria56BombExplodes, kDeathNuclearExplosion); + } +} + +void CaldoriaBomb::activateHotspots() { + GameInteraction::activateHotspots(); + + if (_currentLevel != -1 && _lastVertex >= -1) { + HotVerticesList hotVertices; + makeHotVertexList(_bombLevel[_currentLevel], _lastVertex, hotVertices); + + for (VertexType i = 0; i < hotVertices.numHotVerts; i++) + g_allHotspots.activateOneHotspot(hotVertices.hotVerts[i] + kVertextHotSpotBaseID); + } +} + +void CaldoriaBomb::clickInHotspot(const Input &input, const Hotspot *hotspot) { + int clickedVertex = (int)hotspot->getObjectID() - (int)kVertextHotSpotBaseID; + + if (clickedVertex >= 0 && clickedVertex < 25) { + if (_lastVertex != -1 && setEdgeUsed(_bombLevel[_currentLevel], _lastVertex, clickedVertex)) { + clickedVertex = -2; + _flashTime = tickCount(); + } else if (allEdgesUsed(_bombLevel[_currentLevel])) { + setVertexUsed(_bombLevel[_currentLevel], clickedVertex, 1); + clickedVertex = -20; + _flashTime = tickCount(); + } else { + setVertexUsed(_bombLevel[_currentLevel], clickedVertex, 2); + } + + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = clickedVertex; + } else { + GameInteraction::clickInHotspot(input, hotspot); + } +} + +InputBits CaldoriaBomb::getInputFilter() { + // Disallow arrow buttons. + return GameInteraction::getInputFilter() & kFilterAllButtons; +} + +void CaldoriaBomb::handleInput(const Input &input, const Hotspot *hotspot) { + GameInteraction::handleInput(input, hotspot); + + switch (_lastVertex) { + case -2: // Flash back to yellow. + if (tickCount() > _flashTime + kOnTime1) { + replaceUsedEdges(_bombLevel[_currentLevel], 2, 3); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -3; + } + break; + case -3: // Flash back to red. + if (tickCount() > _flashTime + kOffTime1) { + replaceUsedEdges(_bombLevel[_currentLevel], 3, 2); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -4; + } + break; + case -4: // Flash all to yellow. + if (tickCount() > _flashTime + kOnTime2) { + setAllUsedEdgesUsed(_bombLevel[_currentLevel], 1); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -5; + } + break; + case -5: // Flash all to red. + if (tickCount() > _flashTime + kOffTime2) { + setAllUsedEdgesUsed(_bombLevel[_currentLevel], 2); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -6; + } + break; + case -6: // Flash all to yellow. + if (tickCount() > _flashTime + kOnTime3) { + setAllUsedEdgesUsed(_bombLevel[_currentLevel], 1); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -7; + } + break; + case -7: // Flash all to red. + if (tickCount() > _flashTime + kOffTime3) { + setAllUsedEdgesUsed(_bombLevel[_currentLevel], 2); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -8; + } + break; + case -8: // Restore to normal. + if (tickCount() > _flashTime + kOnTime4) { + setAllEdgesUsed(_bombLevel[_currentLevel], 0); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -1; + } + break; + + // Flash grid after success. + case -20: // Flash off. + if (tickCount() > _flashTime + kOnTime1) { + setAllEdgesUsed(_bombLevel[_currentLevel], 4); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -21; + } + break; + case -21: // Flash on. + if (tickCount() > _flashTime + kOffTime1) { + setAllEdgesUsed(_bombLevel[_currentLevel], 1); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -22; + } + break; + case -22: // Flash off. + if (tickCount() > _flashTime + kOnTime2) { + setAllEdgesUsed(_bombLevel[_currentLevel], 4); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -23; + } + break; + case -23: // Flash on. + if (tickCount() > _flashTime + kOffTime2) { + setAllEdgesUsed(_bombLevel[_currentLevel], 1); + _grid.drawEdges(_bombLevel[_currentLevel]); + _lastVertex = -24; + } + break; + case -24: + if (tickCount() > _flashTime + kOnTime3) { + _grid.hide(); + _lastVertex = -1; + _owner->loadLoopSound1(""); + + switch (_currentLevel) { + case 0: + _owner->startExtraSequence(kCaldoria56BombStage2, kExtraCompletedFlag, kFilterNoInput); + break; + case 1: + _owner->startExtraSequence(kCaldoria56BombStage3, kExtraCompletedFlag, kFilterNoInput); + break; + case 2: + _owner->startExtraSequence(kCaldoria56BombStage4, kExtraCompletedFlag, kFilterNoInput); + break; + case 3: + _owner->startExtraSequence(kCaldoria56BombStage5, kExtraCompletedFlag, kFilterNoInput); + break; + case 4: + _owner->startExtraSequence(kCaldoria56BombStage6, kExtraCompletedFlag, kFilterNoInput); + break; + case 5: + _timer.stop(); + _grid.hide(); + _timer.hide(); + _owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput); + break; + } + } + break; + } +} + +long CaldoriaBomb::getNumHints() { + return 2; +} + +Common::String CaldoriaBomb::getHintMovie(uint hintNum) { + return (hintNum == 1) ? "Images/AI/Caldoria/X56EH2" : "Images/AI/Caldoria/X56EH3"; +} + +bool CaldoriaBomb::canSolve() { + return true; +} + +void CaldoriaBomb::doSolve() { + _timer.stop(); + _grid.hide(); + _timer.hide(); + _owner->loadLoopSound1(""); + _owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.h b/engines/pegasus/neighborhood/caldoria/caldoriabomb.h new file mode 100644 index 0000000000..5bb39b4122 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.h @@ -0,0 +1,156 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIABOMB_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIABOMB_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +/* + Edge list is arranged as follows: + + all values in the edge list are bytes. + + all vertices are numbers between 0 and 24. x coordinate of vertex is vertex % 5, + and y coordinate is vertex / 5. + + an edge is + a direction code + a number of vertices in the edge + an array of vertices -- all vertices along the edge, whether or not they're + clickable. + an array of bools (bytes) indicating that a portion of the edge is + traversed (and should be drawn). the number of bools is one less than + the number of vertices. + + an edge list is + an array of 25 bools indicating which vertex is clickable. + an array of 25 bools indicating which vertex is used (drawn). + a number of edges + an array of edges. + + a hot vertex list is + a number of vertices + an array of 25 vertices + +*/ + +typedef int8 VertexType; +typedef VertexType *BombEdgeList; + +static const VertexType kEdgeOneSixteenth = 0; +static const VertexType kEdgeOneEighth = 1; +static const VertexType kEdgeThreeSixteenths = 2; +static const VertexType kEdgeOneFourth = 3; +static const VertexType kEdgeFiveSixteenths = 4; +static const VertexType kEdgeThreeEighths = 5; +static const VertexType kEdgeSevenSixteenths = 6; +static const VertexType kEdgeOneHalf = 7; + +class BombTimer : public IdlerAnimation { +public: + BombTimer(const DisplayElementID); + virtual ~BombTimer() {} + + void draw(const Common::Rect &); + +protected: + void timeChanged(const TimeValue); + + int _middle; + Surface _leftImage, _rightImage; +}; + +class BombGrid : public Picture { +public: + BombGrid(const DisplayElementID); + virtual ~BombGrid() {} + + void drawEdges(BombEdgeList); + +protected: + Frame _yellowDot; + Frame _yellowOneSixteenth; + Frame _yellowOneEighth; + Frame _yellowThreeSixteenths; + Frame _yellowOneFourth; + Frame _yellowFiveSixteenths; + Frame _yellowThreeEighths; + Frame _yellowSevenSixteenths; + Frame _yellowOneHalf; + Frame _redDot; + Frame _redOneSixteenth; + Frame _redOneEighth; + Frame _redThreeSixteenths; + Frame _redOneFourth; + Frame _redFiveSixteenths; + Frame _redThreeEighths; + Frame _redSevenSixteenths; + Frame _redOneHalf; +}; + +class Hotspot; + +class CaldoriaBomb : public GameInteraction, public NotificationReceiver { +public: + CaldoriaBomb(Neighborhood *, NotificationManager *); + virtual ~CaldoriaBomb(); + + long getNumHints(); + Common::String getHintMovie(uint); + void doSolve(); + bool canSolve(); + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + void receiveNotification(Notification *, const NotificationFlags); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void handleInput(const Input &, const Hotspot *); + InputBits getInputFilter(); + void startBombAmbient(Common::String); + + Notification *_neighborhoodNotification; + BombGrid _grid; + BombTimer _timer; + BombEdgeList _bombLevel[6]; + int _currentLevel, _flashTime; + Hotspot *_vertexHotspot[25]; + VertexType _lastVertex; + Notification _timerNotification; + NotificationCallBack _timerCallBack; + + TimeValue _readTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp new file mode 100644 index 0000000000..a3ce97d438 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp @@ -0,0 +1,115 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriamessages.h" + +namespace Pegasus { + +static const NotificationFlags kMessageDoneFlag = 1; + +CaldoriaMessages::CaldoriaMessages(Neighborhood *owner, const NotificationID id, NotificationManager *manager) : + GameInteraction(kCaldoriaMessagesInteractionID, owner), Notification(id, manager), _messageMovie(kCaldoriaMessagesID) { +} + +void CaldoriaMessages::openInteraction() { + _neighborhoodNotification = GameInteraction::_owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + _messageCallBack.setNotification(this); + notifyMe(this, kMessageDoneFlag, kMessageDoneFlag); + _messageCallBack.setCallBackFlag(kMessageDoneFlag); + _messageNumber = 1; +} + +void CaldoriaMessages::initInteraction() { + GameInteraction::_owner->startExtraSequence(kCaBedroomVidPhone, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaMessages::closeInteraction() { + cancelNotification(this); + _neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaMessages::receiveNotification(Notification *notification, const NotificationFlags) { + if (notification == _neighborhoodNotification) { + switch (GameInteraction::_owner->getLastExtra()) { + case kCaBedroomVidPhone: + GameInteraction::_owner->showExtraView(kCaBedroomMessage1); + break; + case kCaBedroomMessage1: + play1Message(1); + break; + case kCaBedroomMessage2: + play1Message(2); + break; + } + } else { + _messageCallBack.releaseCallBack(); + _messageMovie.releaseMovie(); + + uint32 extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2; + GameInteraction::_owner->showExtraView(extraID); + allowInput(true); + } +} + +void CaldoriaMessages::clickInHotspot(const Input &input, const Hotspot *spot) { + uint32 extraID; + + switch (spot->getObjectID()) { + case kCaBedroomVidPhoneActivationSpotID: + extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2; + GameInteraction::_owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); + break; + default: + GameInteraction::clickInHotspot(input, spot); + break; + } +} + +void CaldoriaMessages::play1Message(uint messageNumber) { + if (messageNumber == 1) { + _messageMovie.initFromMovieFile("Images/Caldoria/A12NVA.movie"); + _messageNumber = 2; + } else { + _messageMovie.initFromMovieFile("Images/Caldoria/A12NVB.movie"); + _messageNumber = 1; + GameState.setCaldoriaSeenMessages(true); + } + + _messageMovie.moveElementTo(kCaldoriaMessageLeft, kCaldoriaMessageTop); + _messageMovie.setDisplayOrder(kCaldoriaMessagesOrder); + _messageMovie.startDisplaying(); + _messageCallBack.initCallBack(&_messageMovie, kCallBackAtExtremes); + _messageCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + allowInput(false); + _messageMovie.show(); + _messageMovie.redrawMovieWorld(); + _messageMovie.start(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.h b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h new file mode 100644 index 0000000000..955fe10ce9 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H + +#include "pegasus/input.h" +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class CaldoriaMessages : public GameInteraction, public Notification, public NotificationReceiver { +public: + CaldoriaMessages(Neighborhood *, const NotificationID, NotificationManager *); + virtual ~CaldoriaMessages() {} + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + void receiveNotification(Notification *, const NotificationFlags); + void clickInHotspot(const Input &, const Hotspot *); + void play1Message(uint); + + Movie _messageMovie; + NotificationCallBack _messageCallBack; + Notification *_neighborhoodNotification; + uint _messageNumber; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp new file mode 100644 index 0000000000..ff4d1811d0 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp @@ -0,0 +1,135 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriamirror.h" + +namespace Pegasus { + +CaldoriaMirror::CaldoriaMirror(Neighborhood *owner) : GameInteraction(kCaldoriaMirrorInteractionID, owner) { +} + +void CaldoriaMirror::openInteraction() { + _neighborhoodNotification = _owner->getNeighborhoodNotification(); + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void CaldoriaMirror::initInteraction() { + _owner->setCurrentActivation(kActivateMirrorReady); + _owner->startExtraSequence(kCaBathroomGreeting, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaMirror::closeInteraction() { + _neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaMirror::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_owner->getLastExtra() == (uint32)kCaBathroomAgencyStandard || !input.anyDirectionInput()) + GameInteraction::handleInput(input, cursorSpot); +} + +void CaldoriaMirror::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_owner->getLastExtra()) { + case kCaBathroomGreeting: + case kCaBathroomBodyFat: + case kCaBathroomRetrothrash: + case kCaBathroomGeoWave: + g_allHotspots.activateOneHotspot(kCaBathroomMirrorSpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle1SpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle2SpotID); + g_allHotspots.deactivateOneHotspot(kCaHairStyle3SpotID); + break; + case kCaBathroomStylistIntro: + case kCaBathroomRetrothrashReturn: + case kCaBathroomGeoWaveReturn: + g_allHotspots.activateOneHotspot(kCaHairStyle1SpotID); + g_allHotspots.activateOneHotspot(kCaHairStyle2SpotID); + g_allHotspots.activateOneHotspot(kCaHairStyle3SpotID); + g_allHotspots.deactivateOneHotspot(kCaBathroomMirrorSpotID); + break; + } +} + +void CaldoriaMirror::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kCaBathroomMirrorSpotID: + switch (_owner->getLastExtra()) { + case kCaBathroomGreeting: + _owner->startExtraSequence(kCaBathroomBodyFat, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomBodyFat: + _owner->startExtraSequence(kCaBathroomStylistIntro, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomRetrothrash: + _owner->startExtraSequence(kCaBathroomRetrothrashReturn, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaBathroomGeoWave: + _owner->startExtraSequence(kCaBathroomGeoWaveReturn, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kCaHairStyle1SpotID: + _owner->startExtraSequence(kCaBathroomRetrothrash, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaHairStyle2SpotID: + _owner->startExtraSequence(kCaBathroomAgencyStandard, kExtraCompletedFlag, kFilterNoInput); + break; + case kCaHairStyle3SpotID: + _owner->startExtraSequence(kCaBathroomGeoWave, kExtraCompletedFlag, kFilterNoInput); + break; + default: + GameInteraction::clickInHotspot(input, spot); + break; + } +} + +void CaldoriaMirror::receiveNotification(Notification *, const NotificationFlags) { + switch (_owner->getLastExtra()) { + case kCaBathroomRetrothrash: + case kCaBathroomGeoWave: + _owner->setCurrentActivation(kActivateMirrorReady); + break; + case kCaBathroomStylistIntro: + case kCaBathroomRetrothrashReturn: + case kCaBathroomGeoWaveReturn: + _owner->setCurrentActivation(kActivateStylistReady); + break; + case kCaBathroomAgencyStandard: + _owner->setCurrentActivation(kActivateHotSpotAlways); + _owner->requestDeleteCurrentInteraction(); + GameState.setScoringFixedHair(true); + GameState.setCaldoriaDoneHygiene(true); + break; + } + + allowInput(true); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.h b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h new file mode 100644 index 0000000000..1ca47ec774 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +class CaldoriaMirror : public GameInteraction, public NotificationReceiver { +public: + CaldoriaMirror(Neighborhood *); + virtual ~CaldoriaMirror() {} + +protected: + void openInteraction(); + void initInteraction(); + void closeInteraction(); + + void handleInput(const Input &, const Hotspot *); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void receiveNotification(Notification *, const NotificationFlags); + + Notification *_neighborhoodNotification; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/door.cpp b/engines/pegasus/neighborhood/door.cpp new file mode 100644 index 0000000000..f7ec7559fc --- /dev/null +++ b/engines/pegasus/neighborhood/door.cpp @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/door.h" + +namespace Pegasus { + +void DoorTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].flags = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Door[%d]: %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, + _entries[i].flags); + } +} + +void DoorTable::clear() { + _entries.clear(); +} + +DoorTable::Entry DoorTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/door.h b/engines/pegasus/neighborhood/door.h new file mode 100644 index 0000000000..8ea757559a --- /dev/null +++ b/engines/pegasus/neighborhood/door.h @@ -0,0 +1,90 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_DOOR_H +#define PEGASUS_NEIGHBORHOOD_DOOR_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +typedef byte DoorFlags; + +enum { + kDoorPresentBit, // Bit set if there is a door here. + kDoorLockedBit // Bit set if door is locked, clear if unlocked. +}; + +static const DoorFlags kNoDoorFlags = 0; +static const DoorFlags kDoorPresentMask = 1 << kDoorPresentBit; +static const DoorFlags kDoorLockedMask = 1 << kDoorLockedBit; + +class DoorTable { +public: + DoorTable() {} + ~DoorTable() {} + + static uint32 getResTag() { return MKTAG('D', 'o', 'o', 'r'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + flags = kNoDoorFlags; + } + + RoomID room; + DirectionConstant direction; + AlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + DoorFlags flags; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif + diff --git a/engines/pegasus/neighborhood/exit.cpp b/engines/pegasus/neighborhood/exit.cpp new file mode 100644 index 0000000000..f0dfff12d3 --- /dev/null +++ b/engines/pegasus/neighborhood/exit.cpp @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/exit.h" + +namespace Pegasus { + +void ExitTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].exitEnd = stream->readUint32BE(); + _entries[i].exitLoop = stream->readUint32BE(); + _entries[i].exitRoom = stream->readUint16BE(); + _entries[i].exitDirection = stream->readByte(); + stream->readByte(); // alignment + + _entries[i].originalEnd = _entries[i].exitEnd; + + debug(0, "Exit[%d]: %d %d %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, _entries[i].exitEnd, + _entries[i].exitLoop, _entries[i].exitRoom, _entries[i].exitDirection); + } +} + +void ExitTable::clear() { + _entries.clear(); +} + +ExitTable::Entry ExitTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/exit.h b/engines/pegasus/neighborhood/exit.h new file mode 100644 index 0000000000..17150892f9 --- /dev/null +++ b/engines/pegasus/neighborhood/exit.h @@ -0,0 +1,93 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_EXIT_H +#define PEGASUS_NEIGHBORHOOD_EXIT_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ExitTable { +public: + ExitTable() {} + ~ExitTable() {} + + static uint32 getResTag() { return MKTAG('E', 'x', 'i', 't'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + exitEnd = 0xffffffff; + originalEnd = 0xffffffff; + exitLoop = 0xffffffff; + exitRoom = kNoRoomID; + exitDirection = kNoDirection; + } + + RoomID room; + DirectionConstant direction; + AlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + // exitEnd is the end of the optimized run of walks. + TimeValue exitEnd; + TimeValue originalEnd; + // exitLoop is the loop start time of the optimized run of walks if the run + // loops back on itself (so far, only in TSA). + TimeValue exitLoop; + RoomID exitRoom; + DirectionConstant exitDirection; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + + typedef Common::Array<Entry>::iterator iterator; + iterator begin() { return _entries.begin(); } + iterator end() { return _entries.end(); } + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/extra.cpp b/engines/pegasus/neighborhood/extra.cpp new file mode 100644 index 0000000000..b8c4e5b510 --- /dev/null +++ b/engines/pegasus/neighborhood/extra.cpp @@ -0,0 +1,58 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/extra.h" + +namespace Pegasus { + +void ExtraTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].extra = stream->readUint32BE(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + debug(0, "Extra[%d]: %d %d %d", i, _entries[i].extra, _entries[i].movieStart, _entries[i].movieEnd); + } +} + +void ExtraTable::clear() { + _entries.clear(); +} + +ExtraTable::Entry ExtraTable::findEntry(ExtraID extra) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].extra == extra) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/extra.h b/engines/pegasus/neighborhood/extra.h new file mode 100644 index 0000000000..14fcff1009 --- /dev/null +++ b/engines/pegasus/neighborhood/extra.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_EXTRA_H +#define PEGASUS_NEIGHBORHOOD_EXTRA_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ExtraTable { +public: + ExtraTable() {} + ~ExtraTable() {} + + static uint32 getResTag() { return MKTAG('X', 't', 'r', 'a'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { movieStart = 0xffffffff; } + bool isEmpty() { return movieStart == 0xffffffff; } + + ExtraID extra; + TimeValue movieStart; + TimeValue movieEnd; + }; + + Entry findEntry(ExtraID extra); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/hotspotinfo.cpp b/engines/pegasus/neighborhood/hotspotinfo.cpp new file mode 100644 index 0000000000..c7524f3a0f --- /dev/null +++ b/engines/pegasus/neighborhood/hotspotinfo.cpp @@ -0,0 +1,65 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/hotspotinfo.h" + +namespace Pegasus { + +void HotspotInfoTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].hotspot = stream->readUint16BE(); + _entries[i].hotspotActivation = stream->readSByte(); + stream->readByte(); // alignment + _entries[i].hotspotRoom = stream->readUint16BE(); + _entries[i].hotspotDirection = stream->readByte(); + stream->readByte(); // alignment + _entries[i].hotspotExtra = stream->readUint32BE(); + _entries[i].hotspotItem = stream->readUint16BE(); + debug(0, "Hotspot[%d]: %d %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].hotspotActivation, + _entries[i].hotspotRoom, _entries[i].hotspotDirection, _entries[i].hotspotExtra, + _entries[i].hotspotItem); + } +} + +void HotspotInfoTable::clear() { + _entries.clear(); +} + +HotspotInfoTable::Entry HotspotInfoTable::findEntry(HotSpotID hotspot) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].hotspot == hotspot) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/hotspotinfo.h b/engines/pegasus/neighborhood/hotspotinfo.h new file mode 100644 index 0000000000..965f445ba8 --- /dev/null +++ b/engines/pegasus/neighborhood/hotspotinfo.h @@ -0,0 +1,77 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H +#define PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class HotspotInfoTable { +public: + HotspotInfoTable() {} + ~HotspotInfoTable() {} + + static uint32 getResTag() { return MKTAG('H', 'S', 'I', 'n'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { hotspotRoom = kNoRoomID; } + bool isEmpty() { return hotspotRoom == kNoRoomID; } + + HotSpotID hotspot; + HotSpotActivationID hotspotActivation; + // Location hot spot lives in: + RoomID hotspotRoom; + DirectionConstant hotspotDirection; + // Extra to play if this is a "play extra" hot spot. + ExtraID hotspotExtra; + // Item corresponding to this hot spot if it is an item-related hot spot. + ItemID hotspotItem; + }; + + Entry findEntry(HotSpotID hotspot); + + typedef Common::Array<Entry>::iterator iterator; + iterator begin() { return _entries.begin(); } + iterator end() { return _entries.end(); } + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/constants.h b/engines/pegasus/neighborhood/mars/constants.h new file mode 100644 index 0000000000..82a7f03b68 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/constants.h @@ -0,0 +1,941 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_CONSTANTS_H +#define PEGASUS_NEIGHBORHOOD_MARS_CONSTANTS_H + +#include "pegasus/constants.h" + +namespace Pegasus { + +// Element Coordinates + +static const CoordType kUndoHiliteLeft = kNavAreaLeft + 140; +static const CoordType kUndoHiliteTop = kNavAreaTop + 36; + +static const CoordType kCurrentGuessLeft = kNavAreaLeft + 146; +static const CoordType kCurrentGuessTop = kNavAreaTop + 90; + +static const CoordType kReactorChoiceHiliteLeft = kNavAreaLeft + 116; +static const CoordType kReactorChoiceHiliteTop = kNavAreaTop + 158; + +static const CoordType kReactorHistoryLeft = kNavAreaLeft + 302; +static const CoordType kReactorHistoryTop = kNavAreaTop + 39; + +static const CoordType kAnswerLeft = kNavAreaLeft + 304; +static const CoordType kAnswerTop = kNavAreaTop + 180; + +static const CoordType kShuttle1Left = 0; +static const CoordType kShuttle1Top = 0; + +static const CoordType kShuttle2Left = 0; +static const CoordType kShuttle2Top = 96; + +static const CoordType kShuttle3Left = 500; +static const CoordType kShuttle3Top = 96; + +static const CoordType kShuttle4Left = 0; +static const CoordType kShuttle4Top = 320; + +static const CoordType kShuttleWindowLeft = 140; +static const CoordType kShuttleWindowTop = 96; +static const CoordType kShuttleWindowWidth = 360; +static const CoordType kShuttleWindowHeight = 224; + +static const CoordType kShuttleWindowMidH = (kShuttleWindowLeft * 2 + kShuttleWindowWidth) / 2; +static const CoordType kShuttleWindowMidV = (kShuttleWindowTop * 2 + kShuttleWindowHeight) / 2; + +static const CoordType kShuttleLeftLeft = 0; +static const CoordType kShuttleLeftTop = 128; + +static const CoordType kShuttleRightLeft = 506; +static const CoordType kShuttleRightTop = 128; + +static const CoordType kShuttleLowerLeftLeft = 74; +static const CoordType kShuttleLowerLeftTop = 358; + +static const CoordType kShuttleLowerRightLeft = 486; +static const CoordType kShuttleLowerRightTop = 354; + +static const CoordType kShuttleCenterLeft = 260; +static const CoordType kShuttleCenterTop = 336; + +static const CoordType kShuttleUpperLeftLeft = 30; +static const CoordType kShuttleUpperLeftTop = 32; + +static const CoordType kShuttleUpperRightLeft = 506; +static const CoordType kShuttleUpperRightTop = 52; + +static const CoordType kShuttleLeftEnergyLeft = 110; +static const CoordType kShuttleLeftEnergyTop = 186; + +static const CoordType kShuttleRightEnergyLeft = 510; +static const CoordType kShuttleRightEnergyTop = 186; + +static const CoordType kShuttleEnergyLeft = 186; +static const CoordType kShuttleEnergyTop = 60; +static const CoordType kShuttleEnergyWidth = 252; +static const CoordType kShuttleEnergyHeight = 22; + +static const CoordType kPlanetStartLeft = kShuttleWindowLeft; +static const CoordType kPlanetStartTop = kShuttleWindowTop + kShuttleWindowHeight; + +static const CoordType kPlanetStopLeft = kShuttleWindowLeft; +static const CoordType kPlanetStopTop = kShuttleWindowTop + kShuttleWindowHeight - 100; + +static const CoordType kShuttleTractorLeft = kShuttleWindowLeft + 6; +static const CoordType kShuttleTractorTop = kShuttleWindowTop + 56; +static const CoordType kShuttleTractorWidth = 348; +static const CoordType kShuttleTractorHeight = 112; + +static const CoordType kShuttleJunkLeft = kShuttleWindowLeft + 6; +static const CoordType kShuttleJunkTop = kShuttleWindowTop + 6; + +static const DisplayOrder kShuttlePlanetOrder = kInterfaceLayer; +static const DisplayOrder kShuttleAlienShipOrder = kShuttlePlanetOrder + 1; +static const DisplayOrder kShuttleRobotShipOrder = kShuttleAlienShipOrder + 1; +static const DisplayOrder kShuttleTractorBeamMovieOrder = kShuttleRobotShipOrder + 1; +static const DisplayOrder kShuttleWeaponBackOrder = kShuttleTractorBeamMovieOrder + 1; +static const DisplayOrder kShuttleJunkOrder = kShuttleWeaponBackOrder + 1; +static const DisplayOrder kShuttleWeaponFrontOrder = kShuttleJunkOrder + 1; +static const DisplayOrder kShuttleTractorBeamOrder = kShuttleWeaponFrontOrder + 1; +static const DisplayOrder kShuttleHUDOrder = kShuttleTractorBeamOrder + 1; +static const DisplayOrder kShuttleBackgroundOrder = kShuttleHUDOrder + 1; +static const DisplayOrder kShuttleMonitorOrder = kShuttleBackgroundOrder + 1; +static const DisplayOrder kShuttleStatusOrder = kShuttleMonitorOrder + 1; + +static const TimeValue kShuttleSwingStart = 0; +static const TimeValue kShuttleSwingStop = 5 * 600; + +static const TimeValue kCanyonChaseStart = kShuttleSwingStop; +static const TimeValue kCanyonChaseStop = 60 * 600 + 43 * 600 + 14 * 40; + +static const TimeValue kLaunchTubeReachedTime = 60 * 600 + 38 * 600 - kCanyonChaseStart; +static const TimeValue kCanyonChaseFinishedTime = kCanyonChaseStop - kCanyonChaseStart - + kLaunchTubeReachedTime; + +// Left shuttle. + +static const TimeValue kShuttleLeftIntroStart = 0; +static const TimeValue kShuttleLeftIntroStop = 400; + +static const TimeValue kShuttleLeftBlankTime = 400; + +static const TimeValue kShuttleLeftNormalTime = 440; + +static const TimeValue kShuttleLeftAutoTestTime = 480; + +static const TimeValue kShuttleLeftDamagedTime = 520; + +static const TimeValue kShuttleLeftDampingTime = 560; + +static const TimeValue kShuttleLeftGravitonTime = 600; + +static const TimeValue kShuttleLeftTractorTime = 640; + +// Right shuttle. + +static const TimeValue kShuttleRightIntroStart = 0; +static const TimeValue kShuttleRightIntroStop = 400; + +static const TimeValue kShuttleRightDestroyedStart = 400; +static const TimeValue kShuttleRightDestroyedStop = 840; + +static const TimeValue kShuttleRightBlankTime = 840; + +static const TimeValue kShuttleRightNormalTime = 880; + +static const TimeValue kShuttleRightDamagedTime = 920; + +static const TimeValue kShuttleRightTargetLockTime = 960; + +static const TimeValue kShuttleRightGravitonTime = 1000; + +static const TimeValue kShuttleRightOverloadTime = 1040; + +// Lower Left shuttle. + +static const TimeValue kShuttleLowerLeftCollisionTime = 0; + +static const TimeValue kShuttleLowerLeftTubeTime = 40; + +static const TimeValue kShuttleLowerLeftAutopilotTime = 80; + +// Lower Right shuttle. + +static const TimeValue kShuttleLowerRightOffTime = 0; + +static const TimeValue kShuttleLowerRightTrackingTime = 40; + +static const TimeValue kShuttleLowerRightTransportTime = 80; + +static const TimeValue kShuttleLowerRightTransportHiliteTime = 120; + +// Center shuttle. + +static const TimeValue kShuttleCenterBoardingTime = 0; + +static const TimeValue kShuttleCenterCheckTime = 40; + +static const TimeValue kShuttleCenterNavCompTime = 80; + +static const TimeValue kShuttleCenterCommTime = 120; + +static const TimeValue kShuttleCenterWeaponsTime = 160; + +static const TimeValue kShuttleCenterAllSystemsTime = 200; + +static const TimeValue kShuttleCenterSecureLooseTime = 240; + +static const TimeValue kShuttleCenterAutoTestTime = 280; + +static const TimeValue kShuttleCenterLaunchTime = 320; + +static const TimeValue kShuttleCenterEnterTubeTime = 360; + +static const TimeValue kShuttleCenterTargetSightedTime = 400; + +static const TimeValue kShuttleCenterVerifyingTime = 440; + +static const TimeValue kShuttleCenterScanningTime = 480; + +static const TimeValue kShuttleCenterSafeTime = 520; + +// Upper Left shuttle. + +static const TimeValue kShuttleUpperLeftDimTime = 0; + +static const TimeValue kShuttleUpperLeftDampingTime = 40; + +static const TimeValue kShuttleUpperLeftGravitonTime = 80; + +static const TimeValue kShuttleUpperLeftTractorTime = 120; + +// Upper Right shuttle. + +static const TimeValue kShuttleUpperRightLockedTime = 0; + +static const TimeValue kShuttleUpperRightArmedTime = 40; + +static const TimeValue kShuttleUpperRightAlienDestroyedTime = 80; + +static const TimeValue kShuttleUpperRightOverloadTime = 120; + +static const TimeValue kShuttleUpperRightTargetDestroyedTime = 160; + +// Shuttle distance + +static const int kShuttleDistance = 500; + +static const int kJunkMaxDistance = kShuttleDistance; +static const int kJunkMinDistance = 40; + +static const int kEnergyBeamMaxDistance = kShuttleDistance; +static const int kEnergyBeamMinDistance = 40; + +static const int kGravitonMaxDistance = kShuttleDistance; +static const int kGravitonMinDistance = 40; + +static const TimeValue kMarsOxyMaskOnIn = 0; +static const TimeValue kMarsOxyMaskOnOut = 1560; + +static const TimeValue kMarsAirlockButtonBeepIn = 1560; +static const TimeValue kMarsAirlockButtonBeepOut = 1620; + +static const TimeValue kMarsColorMatchingButtonBeepIn = 1620; +static const TimeValue kMarsColorMatchingButtonBeepOut = 1680; + +static const TimeValue kMarsKioskBeepIn = 1680; +static const TimeValue kMarsKioskBeepOut = 1740; + +static const TimeValue kMarsBumpIntoWallIn = 1740; +static const TimeValue kMarsBumpIntoWallOut = 1888; + +static const TimeValue kMarsGantryDoorCloseIn = 1888; +static const TimeValue kMarsGantryDoorCloseOut = 2866; + +static const TimeValue kMarsTransportDoorCloseIn = 2866; +static const TimeValue kMarsTransportDoorCloseOut = 3593; + +static const TimeValue kMarsAirlockPressurizeIn = 3593; +static const TimeValue kMarsAirlockPressurizeOut = 4766; + +static const TimeValue kMarsBigAirlockDoorCloseIn = 4766; +static const TimeValue kMarsBigAirlockDoorCloseOut = 7872; + +static const TimeValue kMarsSmallAirlockDoorCloseIn = 7872; +static const TimeValue kMarsSmallAirlockDoorCloseOut = 10000; + +static const TimeValue kMarsMazeDoorCloseIn = 10000; +static const TimeValue kMarsMazeDoorCloseOut = 10969; + +static const TimeValue kMarsRobotTakesTransportIn = 10969; +static const TimeValue kMarsRobotTakesTransportOut = 12802; + +static const TimeValue kMarsPodDepartedUpperPlatformIn = 12802; +static const TimeValue kMarsPodDepartedUpperPlatformOut = 15783; + +static const TimeValue kMarsPodDepartedLowerPlatformIn = 15783; +static const TimeValue kMarsPodDepartedLowerPlatformOut = 18736; + +static const TimeValue kMarsPodArrivedUpperPlatformIn = 18736; +static const TimeValue kMarsPodArrivedUpperPlatformOut = 21605; + +static const TimeValue kMarsCheckInRequiredIn = 21605; +static const TimeValue kMarsCheckInRequiredOut = 27463; + +static const TimeValue kMarsCantOpenShuttleIn = 27463; +static const TimeValue kMarsCantOpenShuttleOut = 29214; + +static const TimeValue kMarsShuttleLockOverrideIn = 29214; +static const TimeValue kMarsShuttleLockOverrideOut = 30330; + +static const TimeValue kMarsNoShuttleIn = 30330; +static const TimeValue kMarsNoShuttleOut = 31502; + +static const TimeValue kMustBeUnlockedIn = 31502; +static const TimeValue kMustBeUnlockedOut = 33960; + +static const TimeValue kColorMatchBlueIn = 33960; +static const TimeValue kColorMatchBlueOut = 34240; + +static const TimeValue kColorMatchRedIn = 34240; +static const TimeValue kColorMatchRedOut = 34538; + +static const TimeValue kColorMatchGreenIn = 34538; +static const TimeValue kColorMatchGreenOut = 34827; + +static const TimeValue kColorMatchYellowIn = 34827; +static const TimeValue kColorMatchYellowOut = 35162; + +static const TimeValue kColorMatchPurpleIn = 35162; +static const TimeValue kColorMatchPurpleOut = 35426; + +static const TimeValue kColorMatchZeroNodesIn = 35426; +static const TimeValue kColorMatchZeroNodesOut = 36376; + +static const TimeValue kColorMatchOneNodeIn = 36376; +static const TimeValue kColorMatchOneNodeOut = 37209; + +static const TimeValue kColorMatchTwoNodesIn = 37209; +static const TimeValue kColorMatchTwoNodesOut = 37983; + +static const TimeValue kColorMatchThreeNodesIn = 37983; +static const TimeValue kColorMatchThreeNodesOut = 38784; + +static const TimeValue kMarsShuttle1DepartedIn = 38784; +static const TimeValue kMarsShuttle1DepartedOut = 40323; + +static const TimeValue kMarsShuttle2DepartedIn = 40323; +static const TimeValue kMarsShuttle2DepartedOut = 41824; + +static const TimeValue kShuttleCockpitIn = 41824; +static const TimeValue kShuttleCockpitOut = 43126; + +static const TimeValue kShuttleOnboardIn = 43126; +static const TimeValue kShuttleOnboardOut = 44284; + +static const TimeValue kShuttleNavigationIn = 44284; +static const TimeValue kShuttleNavigationOut = 46049; + +static const TimeValue kShuttleCommunicationIn = 46049; +static const TimeValue kShuttleCommunicationOut = 47288; + +static const TimeValue kShuttleAutoTestingIn = 47288; +static const TimeValue kShuttleAutoTestingOut = 48179; + +static const TimeValue kMarsThrusterAutoTestIn = 48179; +static const TimeValue kMarsThrusterAutoTestOut = 49979; + +static const TimeValue kShuttleAllSystemsIn = 49979; +static const TimeValue kShuttleAllSystemsOut = 51065; + +static const TimeValue kShuttleSecureLooseIn = 51065; +static const TimeValue kShuttleSecureLooseOut = 52346; + +static const TimeValue kShuttlePrepareForDropIn = 52346; +static const TimeValue kShuttlePrepareForDropOut = 53216; + +static const TimeValue kShuttleAllClearIn = 53216; +static const TimeValue kShuttleAllClearOut = 54031; + +static const TimeValue kShuttleConfiguringIn = 54031; +static const TimeValue kShuttleConfiguringOut = 54994; + +static const TimeValue kShuttleGeneratingIn = 54994; +static const TimeValue kShuttleGeneratingOut = 56033; + +static const TimeValue kShuttleBreakawayIn = 56033; +static const TimeValue kShuttleBreakawayOut = 57346; + +static const TimeValue kMarsAtmosphericBreakawayIn = 57346; +static const TimeValue kMarsAtmosphericBreakawayOut = 59237; + +static const TimeValue kMarsCockpitChatterIn = 59237; +static const TimeValue kMarsCockpitChatterOut = 70344; + +static const TimeValue kShuttleDamperDescIn = 70344; +static const TimeValue kShuttleDamperDescOut = 73262; + +static const TimeValue kShuttleGravitonDescIn = 73262; +static const TimeValue kShuttleGravitonDescOut = 75296; + +static const TimeValue kShuttleTractorDescIn = 75296; +static const TimeValue kShuttleTractorDescOut = 78381; + +static const TimeValue kShuttleTargetSightedIn = 78381; +static const TimeValue kShuttleTargetSightedOut = 79074; + +static const TimeValue kShuttleAutopilotEngagedIn = 79074; +static const TimeValue kShuttleAutopilotEngagedOut = 80414; + +static const TimeValue kMarsEDBBlastIn = 80414; +static const TimeValue kMarsEDBBlastOut = 80705; + +static const TimeValue kMarsGravitonBlastIn = 80705; +static const TimeValue kMarsGravitonBlastOut = 81199; + +static const TimeValue kMarsJunkCollisionIn = 81199; +static const TimeValue kMarsJunkCollisionOut = 81961; + +static const TimeValue kShuttleGravitonIn = 81961; +static const TimeValue kShuttleGravitonOut = 82587; + +static const TimeValue kShuttleDampingBeamIn = 82587; +static const TimeValue kShuttleDampingBeamOut = 83331; + +static const TimeValue kShuttleTractorBeamIn = 83331; +static const TimeValue kShuttleTractorBeamOut = 83802; + +static const TimeValue kShuttleHullBreachIn = 83802; +static const TimeValue kShuttleHullBreachOut = 84721; + +static const TimeValue kShuttleWingDamageIn = 84721; +static const TimeValue kShuttleWingDamageOut = 85640; + +static const TimeValue kShuttleHullDamageIn = 85640; +static const TimeValue kShuttleHullDamageOut = 86513; + +static const TimeValue kShuttleEnergyTooLowIn = 86513; +static const TimeValue kShuttleEnergyTooLowOut = 87578; + +static const TimeValue kShuttleTractorLimitedIn = 87578; +static const TimeValue kShuttleTractorLimitedOut = 89164; + +static const TimeValue kShuttleCantHoldIn = 89164; +static const TimeValue kShuttleCantHoldOut = 90945; + +static const TimeValue kShuttleBrokeFreeIn = 90945; +static const TimeValue kShuttleBrokeFreeOut = 92322; + +static const TimeValue kShuttleDestroyedIn = 92322; +static const TimeValue kShuttleDestroyedOut = 93189; + +static const TimeValue kShuttleCoordinatesIn = 93189; +static const TimeValue kShuttleCoordinatesOut = 94018; + +static const TimeValue kShuttleScanningIn = 94018; +static const TimeValue kShuttleScanningOut = 94975; + +static const TimeValue kShuttleSafeIn = 94975; +static const TimeValue kShuttleSafeOut = 96176; + +static const TimeValue kShuttleOverloadedIn = 96176; +static const TimeValue kShuttleOverloadedOut = 101308; + +static const TimeScale kMarsMovieScale = 600; +static const TimeScale kMarsFramesPerSecond = 15; +static const TimeScale kMarsFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltMarsNormal = 0; +static const AlternateID kAltMarsPodAtMars34 = 1; +static const AlternateID kAltMarsTookCard = 2; +static const AlternateID kAltMars35AirlockEast = 3; +static const AlternateID kAltMars35AirlockWest = 4; +static const AlternateID kAltMarsPodAtMars45 = 5; +static const AlternateID kAltMarsTookMask = 6; +static const AlternateID kAltMarsMaskOnFiller = 7; +static const AlternateID kAltMars60AirlockEast = 8; +static const AlternateID kAltMars60AirlockWest = 9; + +// Room IDs. + +static const RoomID kMars0A = 0; +static const RoomID kMars00 = 1; +static const RoomID kMars01 = 2; +static const RoomID kMars02 = 3; +static const RoomID kMars03 = 4; +static const RoomID kMars04 = 5; +static const RoomID kMars05 = 6; +static const RoomID kMars06 = 7; +static const RoomID kMars07 = 8; +static const RoomID kMars08 = 9; +static const RoomID kMars09 = 10; +static const RoomID kMars10 = 11; +static const RoomID kMars11 = 12; +static const RoomID kMars12 = 13; +static const RoomID kMars13 = 14; +static const RoomID kMars14 = 15; +static const RoomID kMars15 = 16; +static const RoomID kMars16 = 17; +static const RoomID kMars17 = 18; +static const RoomID kMars18 = 19; +static const RoomID kMars19 = 20; +static const RoomID kMars20 = 21; +static const RoomID kMars21 = 22; +static const RoomID kMars22 = 23; +static const RoomID kMars23 = 24; +static const RoomID kMars24 = 25; +static const RoomID kMars25 = 26; +static const RoomID kMars26 = 27; +static const RoomID kMars27 = 28; +static const RoomID kMars28 = 29; +static const RoomID kMars29 = 30; +static const RoomID kMars30 = 31; +static const RoomID kMars31 = 32; +static const RoomID kMars31South = 33; +static const RoomID kMars32 = 34; +static const RoomID kMars33 = 35; +static const RoomID kMars33North = 36; +static const RoomID kMars34 = 37; +static const RoomID kMars35 = 38; +static const RoomID kMars36 = 39; +static const RoomID kMars37 = 40; +static const RoomID kMars38 = 41; +static const RoomID kMars39 = 42; +static const RoomID kMars41 = 43; +static const RoomID kMars42 = 44; +static const RoomID kMars43 = 45; +static const RoomID kMars44 = 46; +static const RoomID kMars45 = 47; +static const RoomID kMars46 = 48; +static const RoomID kMars47 = 49; +static const RoomID kMars48 = 50; +static const RoomID kMars49 = 51; +static const RoomID kMars50 = 52; +static const RoomID kMars51 = 53; +static const RoomID kMars52 = 54; +static const RoomID kMars54 = 55; +static const RoomID kMars56 = 56; +static const RoomID kMars58 = 57; +static const RoomID kMars60 = 58; +static const RoomID kMarsRobotShuttle = 59; +static const RoomID kMarsMaze004 = 60; +static const RoomID kMarsMaze005 = 61; +static const RoomID kMarsMaze006 = 62; +static const RoomID kMarsMaze007 = 63; +static const RoomID kMarsMaze008 = 64; +static const RoomID kMarsMaze009 = 65; +static const RoomID kMarsMaze010 = 66; +static const RoomID kMarsMaze011 = 67; +static const RoomID kMarsMaze012 = 68; +static const RoomID kMarsMaze015 = 69; +static const RoomID kMarsMaze016 = 70; +static const RoomID kMarsMaze017 = 71; +static const RoomID kMarsMaze018 = 72; +static const RoomID kMarsMaze019 = 73; +static const RoomID kMarsMaze020 = 74; +static const RoomID kMarsMaze021 = 75; +static const RoomID kMarsMaze022 = 76; +static const RoomID kMarsMaze023 = 77; +static const RoomID kMarsMaze024 = 78; +static const RoomID kMarsMaze025 = 79; +static const RoomID kMarsMaze026 = 80; +static const RoomID kMarsMaze027 = 81; +static const RoomID kMarsMaze028 = 82; +static const RoomID kMarsMaze031 = 83; +static const RoomID kMarsMaze032 = 84; +static const RoomID kMarsMaze033 = 85; +static const RoomID kMarsMaze034 = 86; +static const RoomID kMarsMaze035 = 87; +static const RoomID kMarsMaze036 = 88; +static const RoomID kMarsMaze037 = 89; +static const RoomID kMarsMaze038 = 90; +static const RoomID kMarsMaze039 = 91; +static const RoomID kMarsMaze042 = 92; +static const RoomID kMarsMaze043 = 93; +static const RoomID kMarsMaze044 = 94; +static const RoomID kMarsMaze045 = 95; +static const RoomID kMarsMaze046 = 96; +static const RoomID kMarsMaze047 = 97; +static const RoomID kMarsMaze049 = 98; +static const RoomID kMarsMaze050 = 99; +static const RoomID kMarsMaze051 = 100; +static const RoomID kMarsMaze052 = 101; +static const RoomID kMarsMaze053 = 102; +static const RoomID kMarsMaze054 = 103; +static const RoomID kMarsMaze055 = 104; +static const RoomID kMarsMaze056 = 105; +static const RoomID kMarsMaze057 = 106; +static const RoomID kMarsMaze058 = 107; +static const RoomID kMarsMaze059 = 108; +static const RoomID kMarsMaze060 = 109; +static const RoomID kMarsMaze061 = 110; +static const RoomID kMarsMaze063 = 111; +static const RoomID kMarsMaze064 = 112; +static const RoomID kMarsMaze065 = 113; +static const RoomID kMarsMaze066 = 114; +static const RoomID kMarsMaze067 = 115; +static const RoomID kMarsMaze068 = 116; +static const RoomID kMarsMaze069 = 117; +static const RoomID kMarsMaze070 = 118; +static const RoomID kMarsMaze071 = 119; +static const RoomID kMarsMaze072 = 120; +static const RoomID kMarsMaze074 = 121; +static const RoomID kMarsMaze076 = 122; +static const RoomID kMarsMaze078 = 123; +static const RoomID kMarsMaze079 = 124; +static const RoomID kMarsMaze081 = 125; +static const RoomID kMarsMaze083 = 126; +static const RoomID kMarsMaze084 = 127; +static const RoomID kMarsMaze085 = 128; +static const RoomID kMarsMaze086 = 129; +static const RoomID kMarsMaze087 = 130; +static const RoomID kMarsMaze088 = 131; +static const RoomID kMarsMaze089 = 132; +static const RoomID kMarsMaze090 = 133; +static const RoomID kMarsMaze091 = 134; +static const RoomID kMarsMaze092 = 135; +static const RoomID kMarsMaze093 = 136; +static const RoomID kMarsMaze098 = 137; +static const RoomID kMarsMaze099 = 138; +static const RoomID kMarsMaze100 = 139; +static const RoomID kMarsMaze101 = 140; +static const RoomID kMarsMaze104 = 141; +static const RoomID kMarsMaze105 = 142; +static const RoomID kMarsMaze106 = 143; +static const RoomID kMarsMaze107 = 144; +static const RoomID kMarsMaze108 = 145; +static const RoomID kMarsMaze111 = 146; +static const RoomID kMarsMaze113 = 147; +static const RoomID kMarsMaze114 = 148; +static const RoomID kMarsMaze115 = 149; +static const RoomID kMarsMaze116 = 150; +static const RoomID kMarsMaze117 = 151; +static const RoomID kMarsMaze118 = 152; +static const RoomID kMarsMaze119 = 153; +static const RoomID kMarsMaze120 = 154; +static const RoomID kMarsMaze121 = 155; +static const RoomID kMarsMaze122 = 156; +static const RoomID kMarsMaze123 = 157; +static const RoomID kMarsMaze124 = 158; +static const RoomID kMarsMaze125 = 159; +static const RoomID kMarsMaze126 = 160; +static const RoomID kMarsMaze127 = 161; +static const RoomID kMarsMaze128 = 162; +static const RoomID kMarsMaze129 = 163; +static const RoomID kMarsMaze130 = 164; +static const RoomID kMarsMaze131 = 165; +static const RoomID kMarsMaze132 = 166; +static const RoomID kMarsMaze133 = 167; +static const RoomID kMarsMaze136 = 168; +static const RoomID kMarsMaze137 = 169; +static const RoomID kMarsMaze138 = 170; +static const RoomID kMarsMaze139 = 171; +static const RoomID kMarsMaze140 = 172; +static const RoomID kMarsMaze141 = 173; +static const RoomID kMarsMaze142 = 174; +static const RoomID kMarsMaze143 = 175; +static const RoomID kMarsMaze144 = 176; +static const RoomID kMarsMaze145 = 177; +static const RoomID kMarsMaze146 = 178; +static const RoomID kMarsMaze147 = 179; +static const RoomID kMarsMaze148 = 180; +static const RoomID kMarsMaze149 = 181; +static const RoomID kMarsMaze152 = 182; +static const RoomID kMarsMaze153 = 183; +static const RoomID kMarsMaze154 = 184; +static const RoomID kMarsMaze155 = 185; +static const RoomID kMarsMaze156 = 186; +static const RoomID kMarsMaze157 = 187; +static const RoomID kMarsMaze159 = 188; +static const RoomID kMarsMaze160 = 189; +static const RoomID kMarsMaze161 = 190; +static const RoomID kMarsMaze162 = 191; +static const RoomID kMarsMaze163 = 192; +static const RoomID kMarsMaze164 = 193; +static const RoomID kMarsMaze165 = 194; +static const RoomID kMarsMaze166 = 195; +static const RoomID kMarsMaze167 = 196; +static const RoomID kMarsMaze168 = 197; +static const RoomID kMarsMaze169 = 198; +static const RoomID kMarsMaze170 = 199; +static const RoomID kMarsMaze171 = 200; +static const RoomID kMarsMaze172 = 201; +static const RoomID kMarsMaze173 = 202; +static const RoomID kMarsMaze174 = 203; +static const RoomID kMarsMaze175 = 204; +static const RoomID kMarsMaze177 = 205; +static const RoomID kMarsMaze178 = 206; +static const RoomID kMarsMaze179 = 207; +static const RoomID kMarsMaze180 = 208; +static const RoomID kMarsMaze181 = 209; +static const RoomID kMarsMaze182 = 210; +static const RoomID kMarsMaze183 = 211; +static const RoomID kMarsMaze184 = 212; +static const RoomID kMarsMaze187 = 213; +static const RoomID kMarsMaze188 = 214; +static const RoomID kMarsMaze189 = 215; +static const RoomID kMarsMaze190 = 216; +static const RoomID kMarsMaze191 = 217; +static const RoomID kMarsMaze192 = 218; +static const RoomID kMarsMaze193 = 219; +static const RoomID kMarsMaze194 = 220; +static const RoomID kMarsMaze195 = 221; +static const RoomID kMarsMaze198 = 222; +static const RoomID kMarsMaze199 = 223; +static const RoomID kMarsMaze200 = 224; +static const RoomID kMarsDeathRoom = 225; + +// Hot Spot Activation IDs. + +static const HotSpotActivationID kActivationReadyForKiosk = 1; +static const HotSpotActivationID kActivationKioskChoice = 2; +static const HotSpotActivationID kActivationTunnelMapReady = 3; +static const HotSpotActivationID kActivateMarsPodClosed = 4; +static const HotSpotActivationID kActivateMarsPodOpen = 5; +static const HotSpotActivationID kActivateReadyToPressurizeAirlock = 6; +static const HotSpotActivationID kActivateAirlockPressurized = 7; +static const HotSpotActivationID kActivateMaskOnHolder = 8; +static const HotSpotActivationID kActivateMaskOnFiller = 9; +static const HotSpotActivationID kActivateReactorPlatformOut = 10; +static const HotSpotActivationID kActivateReactorPlatformIn = 11; +static const HotSpotActivationID kActivateReactorAskLowerScreen = 12; +static const HotSpotActivationID kActivateReactorReadyForNitrogen = 13; +static const HotSpotActivationID kActivateReactorReadyForCrowBar = 14; +static const HotSpotActivationID kActivateReactorAskOperation = 15; +static const HotSpotActivationID kActivateReactorRanEvaluation = 16; +static const HotSpotActivationID kActivateReactorRanDiagnostics = 17; +static const HotSpotActivationID kActivateReactorAnalyzed = 18; +static const HotSpotActivationID kActivateReactorInstructions = 19; +static const HotSpotActivationID kActivateReactorInGame = 20; +static const HotSpotActivationID kActivateReactorBombSafe = 21; +static const HotSpotActivationID kActivateReactorBombExposed = 22; +static const HotSpotActivationID kActivationRobotHeadClosed = 23; +static const HotSpotActivationID kActivationRobotHeadOpen = 24; + +// Hot Spot IDs. + +static const HotSpotID kMars11NorthKioskSpotID = 5000; +static const HotSpotID kMars11NorthKioskSightsSpotID = 5001; +static const HotSpotID kMars11NorthKioskColonySpotID = 5002; +static const HotSpotID kMars12NorthKioskSpotID = 5003; +static const HotSpotID kMars12NorthKioskSightsSpotID = 5004; +static const HotSpotID kMars12NorthKioskColonySpotID = 5005; +static const HotSpotID kMars31SouthSpotID = 5006; +static const HotSpotID kMars31SouthOutSpotID = 5007; +static const HotSpotID kMars31SouthCardSpotID = 5008; +static const HotSpotID kMars33NorthSpotID = 5009; +static const HotSpotID kMars33NorthOutSpotID = 5010; +static const HotSpotID kMars33NorthMonitorSpotID = 5011; +static const HotSpotID kMars34NorthCardDropSpotID = 5012; +static const HotSpotID kMars34SouthOpenStorageSpotID = 5013; +static const HotSpotID kMars34SouthCloseStorageSpotID = 5014; +static const HotSpotID kMars34SouthCrowbarSpotID = 5015; +static const HotSpotID kMars35EastPressurizeSpotID = 5016; +static const HotSpotID kMars35EastSpinSpotID = 5017; +static const HotSpotID kMars35WestPressurizeSpotID = 5018; +static const HotSpotID kMars35WestSpinSpotID = 5019; +static const HotSpotID kMars45NorthOpenStorageSpotID = 5020; +static const HotSpotID kMars45NorthCloseStorageSpotID = 5021; +static const HotSpotID kMars45NorthCrowbarSpotID = 5022; +static const HotSpotID kAttackRobotHotSpotID = 5023; +static const HotSpotID kMars49AirMaskSpotID = 5024; +static const HotSpotID kMars49AirMaskFilledSpotID = 5025; +static const HotSpotID kMars49AirFillingDropSpotID = 5026; +static const HotSpotID kMars52MoveLeftSpotID = 5027; +static const HotSpotID kMars52MoveRightSpotID = 5028; +static const HotSpotID kMars52ExtractSpotID = 5029; +static const HotSpotID kMars53RetractSpotID = 5030; +static const HotSpotID kMars54MoveLeftSpotID = 5031; +static const HotSpotID kMars54MoveRightSpotID = 5032; +static const HotSpotID kMars54ExtractSpotID = 5033; +static const HotSpotID kMars55RetractSpotID = 5034; +static const HotSpotID kMars56MoveLeftSpotID = 5035; +static const HotSpotID kMars56MoveRightSpotID = 5036; +static const HotSpotID kMars56ExtractSpotID = 5037; +static const HotSpotID kMars57RetractSpotID = 5038; +static const HotSpotID kMars57LowerScreenSpotID = 5039; +static const HotSpotID kMars57Retract2SpotID = 5040; +static const HotSpotID kMars57DropNitrogenSpotID = 5041; +static const HotSpotID kMars57DropCrowBarSpotID = 5042; +static const HotSpotID kMars57CantOpenPanelSpotID = 5043; +static const HotSpotID kMars57ShieldEvaluationSpotID = 5044; +static const HotSpotID kMars57MeasureOutputSpotID = 5045; +static const HotSpotID kMars57RunDiagnosticsSpotID = 5046; +static const HotSpotID kMars57BackToOperationMenuSpotID = 5047; +static const HotSpotID kMars57AnalyzeObjectSpotID = 5048; +static const HotSpotID kMars57RemoveObjectMenuSpotID = 5049; +static const HotSpotID kMars57CircuitLinkSpotID = 5050; +static const HotSpotID kMars57CancelCircuitLinkSpotID = 5051; +static const HotSpotID kMars57GameInstructionsSpotID = 5052; +static const HotSpotID kMars57UndoMoveSpotID = 5053; +static const HotSpotID kMars57RedMoveSpotID = 5054; +static const HotSpotID kMars57YellowMoveSpotID = 5055; +static const HotSpotID kMars57GreenMoveSpotID = 5056; +static const HotSpotID kMars57BlueMoveSpotID = 5057; +static const HotSpotID kMars57PurpleMoveSpotID = 5058; +static const HotSpotID kMars57LowerScreenSafelySpotID = 5059; +static const HotSpotID kMars57GrabBombSpotID = 5060; +static const HotSpotID kMars58MoveLeftSpotID = 5061; +static const HotSpotID kMars58MoveRightSpotID = 5062; +static const HotSpotID kMars58ExtractSpotID = 5063; +static const HotSpotID kMars59RetractSpotID = 5064; +static const HotSpotID kMars60EastPressurizeSpotID = 5065; +static const HotSpotID kMars60EastSpinSpotID = 5066; +static const HotSpotID kMars60WestPressurizeSpotID = 5067; +static const HotSpotID kMars60WestSpinSpotID = 5068; +static const HotSpotID kRobotShuttleOpenHeadSpotID = 5069; +static const HotSpotID kRobotShuttleMapChipSpotID = 5070; +static const HotSpotID kRobotShuttleOpticalChipSpotID = 5071; +static const HotSpotID kRobotShuttleShieldChipSpotID = 5072; + +// Extra sequence IDs. + +static const ExtraID kMarsArrivalFromTSA = 0; +static const ExtraID kMars0AWatchShuttleDepart = 1; +static const ExtraID kRobotThrowsPlayer = 2; +static const ExtraID kMarsInfoKioskIntro = 3; +static const ExtraID kMarsColonyInfo = 4; +static const ExtraID kMarsSightsInfo = 5; +static const ExtraID kRobotOnWayToShuttle = 6; +static const ExtraID kMars31SouthZoomInNoCard = 7; +static const ExtraID kMars31SouthViewNoCard = 8; +static const ExtraID kMars31SouthZoomOutNoCard = 9; +static const ExtraID kMars31SouthZoomViewNoCard = 10; +static const ExtraID kMars33SlideShow1 = 11; +static const ExtraID kMars33SlideShow2 = 12; +static const ExtraID kMars33SlideShow3 = 13; +static const ExtraID kMars33SlideShow4 = 14; +static const ExtraID kMars34SpotOpenWithBar = 15; +static const ExtraID kMars34SpotCloseWithBar = 16; +static const ExtraID kMars34SpotOpenNoBar = 17; +static const ExtraID kMars34SpotCloseNoBar = 18; +static const ExtraID kMars34ViewOpenWithBar = 19; +static const ExtraID kMars34ViewOpenNoBar = 20; +static const ExtraID kMars34NorthPodGreeting = 21; +static const ExtraID kMarsTurnOnPod = 22; +static const ExtraID kMarsTakePodToMars45 = 23; +static const ExtraID kMars35WestSpinAirlockToEast = 24; +static const ExtraID kMars35EastSpinAirlockToWest = 25; +static const ExtraID kMars45SpotOpenWithBar = 26; +static const ExtraID kMars45SpotCloseWithBar = 27; +static const ExtraID kMars45SpotOpenNoBar = 28; +static const ExtraID kMars45SpotCloseNoBar = 29; +static const ExtraID kMars45ViewOpenWithBar = 30; +static const ExtraID kMars45ViewOpenNoBar = 31; +static const ExtraID kMars48RobotApproaches = 32; +static const ExtraID kMars48RobotKillsPlayer = 33; +static const ExtraID kMars48RobotLoops = 34; +static const ExtraID kMars48RobotView = 35; +static const ExtraID kMars48RobotDefends = 36; +static const ExtraID kMars49SouthViewMaskFilling = 37; +static const ExtraID kMars52SpinLeft = 38; +static const ExtraID kMars52SpinRight = 39; +static const ExtraID kMars52Extend = 40; +static const ExtraID kMars53Retract = 41; +static const ExtraID kMars54SpinLeft = 42; +static const ExtraID kMars54SpinRight = 43; +static const ExtraID kMars54Extend = 44; +static const ExtraID kMars55Retract = 45; +static const ExtraID kMars56SpinLeft = 46; +static const ExtraID kMars56SpinRight = 47; +static const ExtraID kMars56ExtendWithBomb = 48; +static const ExtraID kMars56ExtendNoBomb = 49; +static const ExtraID kMars57RetractWithBomb = 50; +static const ExtraID kMars57RetractNoBomb = 51; +static const ExtraID kMars57LowerScreenClosed = 52; +static const ExtraID kMars57CantOpenPanel = 53; +static const ExtraID kMars57FreezeLock = 54; +static const ExtraID kMars57BreakLock = 55; +static const ExtraID kMars57LockFrozenView = 56; +static const ExtraID kMars57ThawLock = 57; +static const ExtraID kMars57OpenPanel = 58; +static const ExtraID kMars57OpenPanelChoices = 59; +static const ExtraID kMars57ShieldEvaluation = 60; +static const ExtraID kMars57MeasureOutput = 61; +static const ExtraID kMars57ShieldOkayLoop = 62; +static const ExtraID kMars57RunDiagnostics = 63; +static const ExtraID kMars57BombExplodes = 64; +static const ExtraID kMars57BombAnalysis = 65; +static const ExtraID kMars57DontLink = 66; +static const ExtraID kMars57CircuitLink = 67; +static const ExtraID kMars57GameLevel1 = 68; +static const ExtraID kMars57GameLevel2 = 69; +static const ExtraID kMars57GameLevel3 = 70; +static const ExtraID kMars57BombExplodesInGame = 71; +static const ExtraID kMars57GameSolved = 72; +static const ExtraID kMars57ExposeBomb = 73; +static const ExtraID kMars57BackToNormal = 74; +static const ExtraID kMars57ViewOpenNoBomb = 75; +static const ExtraID kMars58SpinLeft = 76; +static const ExtraID kMars58SpinRight = 77; +static const ExtraID kMars58Extend = 78; +static const ExtraID kMars59Retract = 79; +static const ExtraID kMars60WestSpinAirlockToEast = 80; +static const ExtraID kMars60EastSpinAirlockToWest = 81; +static const ExtraID kMarsRobotHeadOpen = 82; +static const ExtraID kMarsRobotHeadClose = 83; +static const ExtraID kMarsRobotHead000 = 84; +static const ExtraID kMarsRobotHead001 = 85; +static const ExtraID kMarsRobotHead010 = 86; +static const ExtraID kMarsRobotHead011 = 87; +static const ExtraID kMarsRobotHead100 = 88; +static const ExtraID kMarsRobotHead101 = 89; +static const ExtraID kMarsRobotHead110 = 90; +static const ExtraID kMarsRobotHead111 = 91; +static const ExtraID kMarsMaze007RobotApproach = 92; +static const ExtraID kMarsMaze007RobotLoop = 93; +static const ExtraID kMarsMaze007RobotDeath = 94; +static const ExtraID kMarsMaze015SouthRobotApproach = 95; +static const ExtraID kMarsMaze015SouthRobotLoop = 96; +static const ExtraID kMarsMaze015SouthRobotDeath = 97; +static const ExtraID kMarsMaze101EastRobotApproach = 98; +static const ExtraID kMarsMaze101EastRobotLoop = 99; +static const ExtraID kMarsMaze101EastRobotDeath = 100; +static const ExtraID kMarsMaze104WestLoop = 101; +static const ExtraID kMarsMaze104WestDeath = 102; +static const ExtraID kMarsMaze133SouthApproach = 103; +static const ExtraID kMarsMaze133SouthLoop = 104; +static const ExtraID kMarsMaze133SouthDeath = 105; +static const ExtraID kMarsMaze136NorthApproach = 106; +static const ExtraID kMarsMaze136NorthLoop = 107; +static const ExtraID kMarsMaze136NorthDeath = 108; +static const ExtraID kMarsMaze184WestLoop = 109; +static const ExtraID kMarsMaze184WestDeath = 110; +static const ExtraID kMars200DeathInBucket = 111; + +static const ResIDType kReactorUndoHilitePICTID = 900; + +static const int16 kMars52Compass = 90; +static const int16 kMars54Compass = 180; +static const int16 kMars56Compass = 270; +static const int16 kMars58Compass = 0; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/energybeam.cpp b/engines/pegasus/neighborhood/mars/energybeam.cpp new file mode 100644 index 0000000000..964c8ba381 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/energybeam.cpp @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/energybeam.h" + +namespace Pegasus { + +static const TimeValue kEnergyBeamTime = kOneSecond * kShuttleWeaponScale / 2; + +static const CoordType kEnergyBeamOriginH = kShuttleWindowMidH; +static const CoordType kEnergyBeamOriginV = kShuttleWindowTop + kShuttleWindowHeight; + +static const float kBeamXOrigin = convertScreenHToSpaceX(kEnergyBeamOriginH, kEnergyBeamMinDistance); +static const float kBeamYOrigin = convertScreenVToSpaceY(kEnergyBeamOriginV, kEnergyBeamMinDistance); +static const float kBeamZOrigin = kEnergyBeamMinDistance; + +EnergyBeam::EnergyBeam() { + _weaponDuration = kEnergyBeamTime; + setSegment(0, kEnergyBeamTime); + _weaponOrigin = Point3D(kBeamXOrigin, kBeamYOrigin, kBeamZOrigin); +} + +void EnergyBeam::draw(const Common::Rect &) { + static const int kBeamColorRed1 = 224; + static const int kBeamColorRed2 = 64; + + Graphics::Surface *surface = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + byte red = linearInterp(0, kEnergyBeamTime, _lastTime, kBeamColorRed1, kBeamColorRed2); + uint32 color = surface->format.RGBToColor(red, 0, 0); + + Point3D startPoint; + if (_weaponTime < 0.1) + startPoint = _weaponOrigin; + else + linearInterp(_weaponOrigin, _weaponTarget, _weaponTime - 0.1, startPoint); + + Common::Point lineStart; + project3DTo2D(startPoint, lineStart); + + Common::Point lineEnd; + project3DTo2D(_weaponLocation, lineEnd); + + surface->drawThickLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, 2, 1, color); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/energybeam.h b/engines/pegasus/neighborhood/mars/energybeam.h new file mode 100644 index 0000000000..715ed4b01d --- /dev/null +++ b/engines/pegasus/neighborhood/mars/energybeam.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_ENERGYBEAM_H +#define PEGASUS_NEIGHBORHOOD_MARS_ENERGYBEAM_H + +#include "pegasus/neighborhood/mars/shuttleweapon.h" + +namespace Pegasus { + +class EnergyBeam : public ShuttleWeapon { +public: + EnergyBeam(); + virtual ~EnergyBeam() {} + + void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/gravitoncannon.cpp b/engines/pegasus/neighborhood/mars/gravitoncannon.cpp new file mode 100644 index 0000000000..d04b3d08b2 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/gravitoncannon.cpp @@ -0,0 +1,134 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/gravitoncannon.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const TimeValue kGravitonTime = kOneSecond * kShuttleWeaponScale; + +static const CoordType kGravitonOriginH = kShuttleWindowLeft - 1; +static const CoordType kGravitonOriginV = kShuttleWindowMidV; + +static const float kGravitonXOrigin = convertScreenHToSpaceX(kGravitonOriginH, kGravitonMinDistance); +static const float kGravitonYOrigin = convertScreenVToSpaceY(kGravitonOriginV, kGravitonMinDistance); +static const float kGravitonZOrigin = kGravitonMinDistance; + +// Width of graviton sprite... +static const CoordType kGravitonMaxScreenWidth = 78; +static const CoordType kGravitonMaxScreenHeight = 46; + +static const float kGravitonWidth = convertScreenHToSpaceX(kShuttleWindowMidH + kGravitonMaxScreenWidth / 2, kGravitonMinDistance) + - convertScreenHToSpaceX(kShuttleWindowMidH - kGravitonMaxScreenWidth / 2, kGravitonMinDistance); +static const float kGravitonHeight = convertScreenVToSpaceY(kShuttleWindowMidV - kGravitonMaxScreenHeight / 2, kGravitonMinDistance) + - convertScreenVToSpaceY(kShuttleWindowMidV + kGravitonMaxScreenHeight / 2, kGravitonMinDistance); + +GravitonCannon::GravitonCannon() { + _weaponDuration = kGravitonTime; + setSegment(0, kGravitonTime); + _weaponOrigin = Point3D(kGravitonXOrigin, kGravitonYOrigin, kGravitonZOrigin); + _rightOrigin = Point3D(-kGravitonXOrigin, kGravitonYOrigin, kGravitonZOrigin); +} + +void GravitonCannon::initShuttleWeapon() { + ShuttleWeapon::initShuttleWeapon(); + _gravitonImage.getImageFromPICTFile("Images/Mars/Graviton Cannon"); + _gravitonImage.getSurfaceBounds(_gravitonBounds); +} + +void GravitonCannon::cleanUpShuttleWeapon() { + _gravitonImage.deallocateSurface(); + ShuttleWeapon::cleanUpShuttleWeapon(); +} + +void GravitonCannon::draw(const Common::Rect &) { + // Left graviton... + Point3D pt3D = _weaponLocation; + pt3D.translate(-kGravitonWidth / 2, kGravitonHeight / 2, 0); + Common::Point pt2D; + project3DTo2D(pt3D, pt2D); + Common::Rect gravitonRect; + gravitonRect.left = pt2D.x; + gravitonRect.top = pt2D.y; + + pt3D.translate(kGravitonWidth, -kGravitonHeight, 0); + project3DTo2D(pt3D, pt2D); + gravitonRect.right = pt2D.x; + gravitonRect.bottom = pt2D.y; + + _gravitonImage.scaleTransparentCopy(_gravitonBounds, gravitonRect); + + // Right graviton... + pt3D = _rightLocation; + pt3D.translate(-kGravitonWidth / 2, kGravitonHeight / 2, 0); + project3DTo2D(pt3D, pt2D); + gravitonRect.left = pt2D.x; + gravitonRect.top = pt2D.y; + + pt3D.translate(kGravitonWidth, -kGravitonHeight, 0); + project3DTo2D(pt3D, pt2D); + gravitonRect.right = pt2D.x; + gravitonRect.bottom = pt2D.y; + + _gravitonImage.scaleTransparentCopy(_gravitonBounds, gravitonRect); +} + +void GravitonCannon::updateWeaponPosition() { + ShuttleWeapon::updateWeaponPosition(); + if (_weaponTime != 1.0) + linearInterp(_rightOrigin, _weaponTarget, _weaponTime, _rightLocation); +} + +bool GravitonCannon::collisionWithJunk(Common::Point &impactPoint) { + if (getDisplayOrder() == kShuttleWeaponFrontOrder) { + Point3D junkPosition; + g_spaceJunk->getJunkPosition(junkPosition); + + if (junkPosition.z < _weaponLocation.z) { + setDisplayOrder(kShuttleWeaponBackOrder); + project3DTo2D(_weaponLocation, impactPoint); + + if (g_spaceJunk->pointInJunk(impactPoint)) + return true; + + project3DTo2D(_rightLocation, impactPoint); + return g_spaceJunk->pointInJunk(impactPoint); + } + } + + return false; +} + +void GravitonCannon::hitJunk(Common::Point impactPoint) { + g_spaceJunk->hitByGravitonCannon(impactPoint); +} + +void GravitonCannon::hitShuttle(Common::Point impactPoint) { + g_robotShip->hitByGravitonCannon(impactPoint); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/gravitoncannon.h b/engines/pegasus/neighborhood/mars/gravitoncannon.h new file mode 100644 index 0000000000..b94fd55e5b --- /dev/null +++ b/engines/pegasus/neighborhood/mars/gravitoncannon.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_GRAVITONCANNON_H +#define PEGASUS_NEIGHBORHOOD_MARS_GRAVITONCANNON_H + +#include "pegasus/surface.h" +#include "pegasus/neighborhood/mars/shuttleweapon.h" + +namespace Pegasus { + +class GravitonCannon : public ShuttleWeapon { +public: + GravitonCannon(); + virtual ~GravitonCannon() {} + + void initShuttleWeapon(); + void cleanUpShuttleWeapon(); + + void draw(const Common::Rect &); + +protected: + virtual void updateWeaponPosition(); + virtual bool collisionWithJunk(Common::Point &impactPoint); + virtual void hitJunk(Common::Point impactPoint); + virtual void hitShuttle(Common::Point impactPoint); + + Surface _gravitonImage; + Common::Rect _gravitonBounds; + Point3D _rightOrigin, _rightLocation; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/hermite.cpp b/engines/pegasus/neighborhood/mars/hermite.cpp new file mode 100644 index 0000000000..7f631b369d --- /dev/null +++ b/engines/pegasus/neighborhood/mars/hermite.cpp @@ -0,0 +1,76 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/hermite.h" + +namespace Pegasus { + +CoordType hermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 time, int32 duration) { + float t = (float)time / duration; + float tsq = t * t; + float tcu = t * tsq; + float tcu2 = tcu + tcu; + float tsq2 = tsq + tsq; + float tsq3 = tsq2 + tsq; + return (CoordType)((tcu2 - tsq3 + 1) * p1 + (tsq3 - tcu2) * p4 + (tcu - tsq2 + t) * r1 + (tcu - tsq) * r4); +} + +CoordType dHermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 time, int32 duration) { + float t = (float)time / duration; + float t2 = t + t; + float t4 = t2 + t2; + float t6 = t4 + t2; + float tsq = t * t; + float tsq3 = tsq + tsq + tsq; + float tsq6 = tsq3 + tsq3; + return (CoordType)((tsq6 - t6) * p1 + (t6 - tsq6) * p4 + (tsq3 - t4 + 1) * r1 + (tsq3 - t2) * r4); +} + +void hermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 time, int32 duration, Common::Point &result) { + float t = (float)time / duration; + float tsq = t * t; + float tcu = t * tsq; + float tcu2 = tcu + tcu; + float tsq2 = tsq + tsq; + float tsq3 = tsq2 + tsq; + + result.x = (int16)((tcu2 - tsq3 + 1) * p1.x + (tsq3 - tcu2) * p4.x + (tcu - tsq2 + t) * r1.x + (tcu - tsq) * r4.x); + result.y = (int16)((tcu2 - tsq3 + 1) * p1.y + (tsq3 - tcu2) * p4.y + (tcu - tsq2 + t) * r1.y + (tcu - tsq) * r4.y); +} + +void dHermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 time, int32 duration, Common::Point &result) { + float t = (float)time / duration; + float t2 = t + t; + float t4 = t2 + t2; + float t6 = t4 + t2; + float tsq = t * t; + float tsq3 = tsq + tsq + tsq; + float tsq6 = tsq3 + tsq3; + + result.x = (int16)((tsq6 - t6) * p1.x + (t6 - tsq6) * p4.x + (tsq3 - t4 + 1) * r1.x + (tsq3 - t2) * r4.x); + result.y = (int16)((tsq6 - t6) * p1.y + (t6 - tsq6) * p4.y + (tsq3 - t4 + 1) * r1.y + (tsq3 - t2) * r4.y); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/hermite.h b/engines/pegasus/neighborhood/mars/hermite.h new file mode 100644 index 0000000000..44cb3a5a11 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/hermite.h @@ -0,0 +1,41 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_HERMITE_H +#define PEGASUS_NEIGHBORHOOD_MARS_HERMITE_H + +#include "common/rect.h" +#include "pegasus/types.h" + +namespace Pegasus { + +CoordType hermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 t, int32 duration); +CoordType dHermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 t, int32 duration); +void hermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 t, int32 duration, Common::Point &result); +void dHermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 t, int32 duration, Common::Point &result); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/mars.cpp b/engines/pegasus/neighborhood/mars/mars.cpp new file mode 100644 index 0000000000..34c9e3d0f8 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/mars.cpp @@ -0,0 +1,3735 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "video/qt_decoder.h" + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/mars/mars.h" + +namespace Pegasus { + +// This should really be 22.5. +// Probably no one will know the difference. +static const int16 kMarsShieldPanelOffsetAngle = 22; + +static const CanMoveForwardReason kCantMoveRobotBlocking = kCantMoveLastReason + 1; + +static const NotificationFlags kTimeForCanyonChaseFlag = kLastNeighborhoodNotificationFlag << 1; +static const NotificationFlags kExplosionFinishedFlag = kTimeForCanyonChaseFlag << 1; +static const NotificationFlags kTimeToTransportFlag = kExplosionFinishedFlag << 1; + +static const NotificationFlags kMarsNotificationFlags = kTimeForCanyonChaseFlag | + kExplosionFinishedFlag | + kTimeToTransportFlag; + +static const TimeValue kLittleExplosionStart = 0 * 40; +static const TimeValue kLittleExplosionStop = 24 * 40; + +static const TimeValue kBigExplosionStart = 24 * 40; +static const TimeValue kBigExplosionStop = 62 * 40; + +enum { + kMaze007RobotLoopingEvent, + kMaze015RobotLoopingEvent, + kMaze101RobotLoopingEvent, + kMaze104RobotLoopingEvent, + kMaze133RobotLoopingEvent, + kMaze136RobotLoopingEvent, + kMaze184RobotLoopingEvent +}; + +enum { + kMaze007RobotLoopingTime = (64 + 96) * kMarsFrameDuration, + kMaze015RobotLoopingTime = (64 + 93) * kMarsFrameDuration, + kMaze101RobotLoopingTime = (64 + 45) * kMarsFrameDuration, + kMaze104RobotLoopingTime = 96 * kMarsFrameDuration, + kMaze133RobotLoopingTime = (64 + 96) * kMarsFrameDuration, + kMaze136RobotLoopingTime = (64 + 96) * kMarsFrameDuration, + kMaze184RobotLoopingTime = 96 * kMarsFrameDuration +}; + +// I've made a couple macros for these rects so we don't +// have to globally construct them or whatnot +#define kShuttleEnergyBeamBounds Common::Rect(24, 27, 24 + 112, 27 + 46) +#define kShuttleGravitonBounds Common::Rect(24, 73, 24 + 112, 73 + 30) +#define kShuttleTractorBounds Common::Rect(24, 103, 24 + 112, 103 + 30) +#define kShuttleTransportBounds Common::Rect(484, 353, 89 + 484, 79 + 353) + +void MarsTimerEvent::fire() { + mars->marsTimerExpired(*this); +} + +Mars::Mars(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Mars", kMarsID), + _guessObject(kNoDisplayElement), _undoPict(kNoDisplayElement), _guessHistory(kNoDisplayElement), + _choiceHighlight(kNoDisplayElement), _shuttleInterface1(kNoDisplayElement), _shuttleInterface2(kNoDisplayElement), + _shuttleInterface3(kNoDisplayElement), _shuttleInterface4(kNoDisplayElement), _canyonChaseMovie(kNoDisplayElement), + _leftShuttleMovie(kNoDisplayElement), _rightShuttleMovie(kNoDisplayElement), _lowerLeftShuttleMovie(kNoDisplayElement), + _lowerRightShuttleMovie(kNoDisplayElement), _centerShuttleMovie(kNoDisplayElement), + _upperLeftShuttleMovie(kNoDisplayElement), _upperRightShuttleMovie(kNoDisplayElement), + _leftDamageShuttleMovie(kNoDisplayElement), _rightDamageShuttleMovie(kNoDisplayElement), _explosions(kNoDisplayElement), + _planetMovie(kNoDisplayElement), _junk(kNoDisplayElement), _energyChoiceSpot(kShuttleEnergySpotID), + _gravitonChoiceSpot(kShuttleGravitonSpotID), _tractorChoiceSpot(kShuttleTractorSpotID), + _shuttleViewSpot(kShuttleViewSpotID), _shuttleTransportSpot(kShuttleTransportSpotID) { + _noAirFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::airStageExpired)); + setIsItemTaken(kMarsCard); + setIsItemTaken(kAirMask); + setIsItemTaken(kCrowbar); + setIsItemTaken(kCardBomb); +} + +Mars::~Mars() { + _vm->getAllHotspots().remove(&_energyChoiceSpot); + _vm->getAllHotspots().remove(&_gravitonChoiceSpot); + _vm->getAllHotspots().remove(&_tractorChoiceSpot); + _vm->getAllHotspots().remove(&_shuttleViewSpot); + _vm->getAllHotspots().remove(&_shuttleTransportSpot); +} + +void Mars::init() { + Neighborhood::init(); + + Hotspot *attackSpot = _vm->getAllHotspots().findHotspotByID(kAttackRobotHotSpotID); + attackSpot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag); + _attackingItem = NULL; + + forceStridingStop(kMars08, kNorth, kAltMarsNormal); + + _neighborhoodNotification.notifyMe(this, kMarsNotificationFlags, kMarsNotificationFlags); + + _explosionCallBack.setNotification(&_neighborhoodNotification); + _explosionCallBack.setCallBackFlag(kExplosionFinishedFlag); + + _weaponSelection = kNoWeapon; +} + +void Mars::flushGameState() { + g_energyMonitor->saveCurrentEnergyValue(); +} + +void Mars::start() { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + Neighborhood::start(); +} + +class AirMaskCondition : public AICondition { +public: + AirMaskCondition(const uint32); + + virtual bool fireCondition(); + +protected: + uint32 _airThreshold; + uint32 _lastAirLevel; +}; + +AirMaskCondition::AirMaskCondition(const uint32 airThreshold) { + _airThreshold = airThreshold; + _lastAirLevel = g_airMask->getAirLeft(); +} + +bool AirMaskCondition::fireCondition() { + bool result = g_airMask && g_airMask->isAirMaskOn() && + g_airMask->getAirLeft() <= _airThreshold && _lastAirLevel > _airThreshold; + + _lastAirLevel = g_airMask->getAirLeft(); + return result; +} + +void Mars::setUpAIRules() { + Neighborhood::setUpAIRules(); + + // Don't add these rules if we're going to the robot's shuttle... + if (g_AIArea && !GameState.getMarsReadyForShuttleTransport()) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB1E", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars47, kSouth)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM27NB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars27, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM27NB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars28, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM41ED", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars19, kEast)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars35, kWest)); + rule = new AIRule(locCondition, deactivate); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM41ED", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kMars48, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + AirMaskCondition *airMask50Condition = new AirMaskCondition(50); + messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB1", false); + AIRule *rule50 = new AIRule(airMask50Condition, messageAction); + + AirMaskCondition *airMask25Condition = new AirMaskCondition(25); + AICompoundAction *compound = new AICompoundAction(); + messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB2", false); + compound->addAction(messageAction); + deactivate = new AIDeactivateRuleAction(rule50); + compound->addAction(deactivate); + AIRule *rule25 = new AIRule(airMask25Condition, compound); + + AirMaskCondition *airMask5Condition = new AirMaskCondition(5); + compound = new AICompoundAction; + messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB3", false); + compound->addAction(messageAction); + deactivate = new AIDeactivateRuleAction(rule50); + compound->addAction(deactivate); + deactivate = new AIDeactivateRuleAction(rule25); + compound->addAction(deactivate); + AIRule *rule5 = new AIRule(airMask5Condition, compound); + + g_AIArea->addAIRule(rule5); + g_AIArea->addAIRule(rule25); + g_AIArea->addAIRule(rule50); + + messageAction = new AIPlayMessageAction("Images/AI/Mars/XM51ND", false); + AIDoorOpenedCondition *doorOpen = new AIDoorOpenedCondition(MakeRoomView(kMars51, kEast)); + rule = new AIRule(doorOpen, messageAction); + g_AIArea->addAIRule(rule); + } +} + +uint16 Mars::getDateResID() const { + return kDate2185ID; +} + +TimeValue Mars::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry extra; + SpotTable::Entry spotEntry; + uint32 extraID = 0xffffffff; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kMars0A, kNorth): + if (!GameState.getMarsSeenTimeStream()) { + getExtraEntry(kMarsArrivalFromTSA, extra); + return extra.movieStart; + } + break; + case MakeRoomView(kMars31South, kSouth): + if (GameState.isTakenItemID(kMarsCard)) + extraID = kMars31SouthZoomViewNoCard; + break; + case MakeRoomView(kMars31, kSouth): + if (GameState.isTakenItemID(kMarsCard)) + extraID = kMars31SouthViewNoCard; + break; + case MakeRoomView(kMars34, kSouth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + if (GameState.isTakenItemID(kCrowbar)) + extraID = kMars34ViewOpenNoBar; + else + extraID = kMars34ViewOpenWithBar; + } + break; + case MakeRoomView(kMars36, kSouth): + case MakeRoomView(kMars37, kSouth): + case MakeRoomView(kMars38, kSouth): + findSpotEntry(room, direction, kSpotOnTurnMask | kSpotLoopsMask, spotEntry); + return spotEntry.movieStart; + case MakeRoomView(kMars45, kNorth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + if (GameState.isTakenItemID(kCrowbar)) + extraID = kMars45ViewOpenNoBar; + else + extraID = kMars45ViewOpenWithBar; + } + break; + case MakeRoomView(kMars48, kEast): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + extraID = kMars48RobotView; + break; + case MakeRoomView(kMars56, kEast): + if (_privateFlags.getFlag(kMarsPrivateBombExposedFlag)) { + if (_privateFlags.getFlag(kMarsPrivateDraggingBombFlag)) + extraID = kMars57ViewOpenNoBomb; + else + extraID = kMars57ExposeBomb; + } else if (GameState.getMarsLockBroken()) { + extraID = kMars57OpenPanelChoices; + } else if (GameState.getMarsLockFrozen()) { + extraID = kMars57LockFrozenView; + } + break; + case MakeRoomView(kMarsRobotShuttle, kEast): + if (getCurrentActivation() == kActivationRobotHeadOpen) { + extraID = kMarsRobotHead111; + + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag)) + extraID -= 1; + if (_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) + extraID -= 2; + if (_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag)) + extraID -= 4; + } + break; + } + + if (extraID == 0xffffffff) + return Neighborhood::getViewTime(room, direction); + + getExtraEntry(extraID, extra); + return extra.movieEnd - 1; +} + +void Mars::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) { + Neighborhood::getZoomEntry(spotID, entry); + + uint32 extraID = 0xffffffff; + + switch (spotID) { + case kMars31SouthSpotID: + if (GameState.getCurrentDirection() == kSouth && GameState.isTakenItemID(kMarsCard)) + extraID = kMars31SouthZoomInNoCard; + break; + case kMars31SouthOutSpotID: + if (GameState.getCurrentDirection() == kSouth && GameState.isTakenItemID(kMarsCard)) + extraID = kMars31SouthZoomOutNoCard; + break; + } + + if (extraID != 0xffffffff) { + ExtraTable::Entry extra; + getExtraEntry(extraID, extra); + entry.movieStart = extra.movieStart; + entry.movieEnd = extra.movieEnd; + } +} + +void Mars::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { + Neighborhood::findSpotEntry(room, direction, flags, entry); + + if ((flags & (kSpotOnArrivalMask | kSpotOnTurnMask)) != 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars27, kNorth): + if (GameState.getMarsSeenThermalScan()) + entry.clear(); + else + GameState.setMarsSeenThermalScan(true); + break; + case MakeRoomView(kMars28, kNorth): + if (GameState.getMarsSeenThermalScan()) + entry.clear(); + else + GameState.setMarsSeenThermalScan(true); + break; + } + } +} + +CanMoveForwardReason Mars::canMoveForward(ExitTable::Entry &entry) { + CanMoveForwardReason reason = Neighborhood::canMoveForward(entry); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars48, kEast): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + reason = kCantMoveRobotBlocking; + break; + case MakeRoomView(kMars48, kSouth): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + _utilityFuse.stopFuse(); + break; + } + + return reason; +} + +void Mars::cantMoveThatWay(CanMoveForwardReason reason) { + if (reason == kCantMoveRobotBlocking) { + startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); + loadLoopSound2(""); + } else { + Neighborhood::cantMoveThatWay(reason); + } +} + +void Mars::moveForward() { + if (GameState.getCurrentRoom() == kMars02 || (GameState.getCurrentRoom() >= kMars05 && GameState.getCurrentRoom() <= kMars08)) + loadLoopSound2(""); + + Neighborhood::moveForward(); +} + +void Mars::bumpIntoWall() { + requestSpotSound(kMarsBumpIntoWallIn, kMarsBumpIntoWallOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +CanOpenDoorReason Mars::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars05, kEast): + case MakeRoomView(kMars06, kEast): + case MakeRoomView(kMars07, kEast): + if (!GameState.getMarsSecurityDown()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze037, kWest): + case MakeRoomView(kMarsMaze038, kEast): + if (GameState.getMarsMazeDoorPair1()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze050, kNorth): + case MakeRoomView(kMarsMaze058, kSouth): + if (!GameState.getMarsMazeDoorPair1()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze047, kNorth): + case MakeRoomView(kMarsMaze142, kSouth): + if (GameState.getMarsMazeDoorPair2()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze057, kNorth): + case MakeRoomView(kMarsMaze136, kSouth): + if (!GameState.getMarsMazeDoorPair2()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze120, kWest): + case MakeRoomView(kMarsMaze121, kEast): + if (GameState.getMarsMazeDoorPair3()) + return kCantOpenLocked; + break; + case MakeRoomView(kMarsMaze081, kNorth): + case MakeRoomView(kMarsMaze083, kSouth): + if (!GameState.getMarsMazeDoorPair3()) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void Mars::cantOpenDoor(CanOpenDoorReason reason) { + switch (GameState.getCurrentRoom()) { + case kMars05: + case kMars06: + case kMars07: + playSpotSoundSync(kMarsCantOpenShuttleIn, kMarsCantOpenShuttleOut); + break; + default: + Neighborhood::cantOpenDoor(reason); + break; + } +} + +void Mars::openDoor() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars06, kEast): + case MakeRoomView(kMars07, kEast): + if (GameState.getMarsSecurityDown()) + playSpotSoundSync(kMarsNoShuttleIn, kMarsNoShuttleOut); + break; + case MakeRoomView(kMars47, kSouth): + if (GameState.isTakenItemID(kAirMask)) + setCurrentAlternate(kAltMarsTookMask); + else + setCurrentAlternate(kAltMarsNormal); + break; + case MakeRoomView(kMars48, kNorth): + if (GameState.getMarsPodAtUpperPlatform()) + setCurrentAlternate(kAltMarsNormal); + else + setCurrentAlternate(kAltMarsPodAtMars45); + break; + case MakeRoomView(kMars48, kEast): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { + die(kDeathDidntGetOutOfWay); + return; + } + break; + } + + Neighborhood::openDoor(); +} + +void Mars::doorOpened() { + switch (GameState.getCurrentRoom()) { + case kMars27: + case kMars28: + if (GameState.getCurrentDirection() == kNorth) + _vm->die(kDeathArrestedInMars); + else + Neighborhood::doorOpened(); + break; + case kMars41: + case kMars42: + if (GameState.getCurrentDirection() == kEast) + _vm->die(kDeathWrongShuttleLock); + else + Neighborhood::doorOpened(); + break; + case kMars51: + Neighborhood::doorOpened(); + setUpReactorEnergyDrain(); + + if (g_AIArea) + g_AIArea->checkRules(); + break; + case kMars19: + if (GameState.getCurrentDirection() == kEast) + GameState.setMarsAirlockOpen(true); + + Neighborhood::doorOpened(); + break; + case kMars48: + if (GameState.getCurrentDirection() == kWest) + GameState.setMarsAirlockOpen(true); + + Neighborhood::doorOpened(); + break; + default: + Neighborhood::doorOpened(); + break; + } +} + +void Mars::setUpReactorEnergyDrain() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars51, kEast): + if (GameState.isCurrentDoorOpen()) { + if (g_energyMonitor->getEnergyDrainRate() == kEnergyDrainNormal) { + if (GameState.getShieldOn()) { + g_shield->setItemState(kShieldRadiation); + g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainWithShield); + } else { + g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainNoShield); + } + _vm->setEnergyDeathReason(kDeathReactorBurn); + } + } else { + if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldNormal); + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + } + } + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + if (g_energyMonitor->getEnergyDrainRate() == kEnergyDrainNormal) { + if (GameState.getShieldOn()) { + g_shield->setItemState(kShieldRadiation); + g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainWithShield); + } else { + g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainNoShield); + } + _vm->setEnergyDeathReason(kDeathReactorBurn); + } + break; + default: + if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldNormal); + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + } + break; + } +} + +void Mars::closeDoorOffScreen(const RoomID room, const DirectionConstant direction) { + switch (room) { + case kMars51: + playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldNormal); + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + break; + case kMars05: + case kMars06: + case kMars07: + case kMars13: + case kMars22: + case kMars47: + case kMars52: + playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); + break; + case kMars18: + case kMars32: + playSpotSoundSync(kMarsTransportDoorCloseIn, kMarsTransportDoorCloseOut); + break; + case kMars19: + if (GameState.getCurrentRoom() != kMars35) { + playSpotSoundSync(kMarsBigAirlockDoorCloseIn, kMarsBigAirlockDoorCloseOut); + GameState.setMarsAirlockOpen(false); + } + break; + case kMars36: + if (GameState.getCurrentRoom() != kMars35) + playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); + break; + case kMars48: + if (direction == kWest) { + if (GameState.getCurrentRoom() != kMars60) { + playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); + GameState.setMarsAirlockOpen(false); + } + } else { + playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); + } + break; + case kMars41: + case kMars42: + case kMars43: + if (direction == kWest) + playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); + break; + case kMarsMaze037: + case kMarsMaze038: + case kMarsMaze012: + case kMarsMaze066: + case kMarsMaze050: + case kMarsMaze058: + case kMarsMaze057: + case kMarsMaze136: + case kMarsMaze047: + case kMarsMaze142: + case kMarsMaze133: + case kMarsMaze132: + case kMarsMaze113: + case kMarsMaze114: + case kMarsMaze120: + case kMarsMaze121: + case kMarsMaze081: + case kMarsMaze083: + case kMarsMaze088: + case kMarsMaze089: + case kMarsMaze179: + case kMarsMaze180: + playSpotSoundSync(kMarsMazeDoorCloseIn, kMarsMazeDoorCloseOut); + break; + } +} + +void Mars::checkAirlockDoors() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars19, kWest): + case MakeRoomView(kMars18, kWest): + case MakeRoomView(kMars17, kWest): + case MakeRoomView(kMars16, kWest): + case MakeRoomView(kMars15, kWest): + case MakeRoomView(kMars14, kWest): + case MakeRoomView(kMars12, kWest): + case MakeRoomView(kMars11, kWest): + case MakeRoomView(kMars10, kWest): + if (GameState.getMarsInAirlock()) { + playSpotSoundSync(kMarsBigAirlockDoorCloseIn, kMarsBigAirlockDoorCloseOut); + GameState.setMarsInAirlock(false); + } + break; + case MakeRoomView(kMars36, kEast): + case MakeRoomView(kMars37, kEast): + case MakeRoomView(kMars38, kEast): + case MakeRoomView(kMars39, kEast): + case MakeRoomView(kMars48, kEast): + case MakeRoomView(kMars50, kEast): + case MakeRoomView(kMars51, kEast): + case MakeRoomView(kMars52, kEast): + if (GameState.getMarsInAirlock()) { + playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); + GameState.setMarsInAirlock(false); + } + break; + case MakeRoomView(kMars35, kWest): + case MakeRoomView(kMars35, kEast): + case MakeRoomView(kMars60, kWest): + case MakeRoomView(kMars60, kEast): + GameState.setMarsInAirlock(true); + break; + default: + GameState.setMarsInAirlock(false); + break; + } +} + +int16 Mars::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 angle = Neighborhood::getStaticCompassAngle(room, dir); + + switch (MakeRoomView(room, dir)) { + case MakeRoomView(kMars0A, kNorth): + angle -= 20; + break; + case MakeRoomView(kMars23, kNorth): + case MakeRoomView(kMars23, kSouth): + case MakeRoomView(kMars23, kEast): + case MakeRoomView(kMars23, kWest): + case MakeRoomView(kMars26, kNorth): + case MakeRoomView(kMars26, kSouth): + case MakeRoomView(kMars26, kEast): + case MakeRoomView(kMars26, kWest): + angle += 30; + break; + case MakeRoomView(kMars24, kNorth): + case MakeRoomView(kMars24, kSouth): + case MakeRoomView(kMars24, kEast): + case MakeRoomView(kMars24, kWest): + case MakeRoomView(kMars25, kNorth): + case MakeRoomView(kMars25, kSouth): + case MakeRoomView(kMars25, kEast): + case MakeRoomView(kMars25, kWest): + angle -= 30; + break; + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + angle += 90; + break; + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars56, kWest): + angle += 180; + break; + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + angle -= 90; + break; + } + + return angle; +} + +void Mars::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + if (exitEntry.room == kMars43 && exitEntry.direction == kEast) { + compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kMarsFrameDuration, 90); + compassMove.insertFaderKnot(exitEntry.movieStart + 32 * kMarsFrameDuration, 270); + } else if (exitEntry.room == kMars46 && exitEntry.direction == kWest && exitEntry.altCode != kAltMarsPodAtMars45) { + compassMove.makeTwoKnotFaderSpec(kMarsMovieScale, exitEntry.movieStart, 270, exitEntry.movieEnd, 360); + compassMove.insertFaderKnot(exitEntry.movieStart + 43 * kMarsFrameDuration, 270); + compassMove.insertFaderKnot(exitEntry.movieStart + 58 * kMarsFrameDuration, 360); + } +} + +void Mars::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kMarsTakePodToMars45: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 0, entry.movieEnd, 180); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 3), 30); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 11), 10); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 14), 40); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 16), 30); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 23), 100); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 31), 70); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 34), 100); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 37), 85); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 42), 135); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 44), 125); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 46), 145); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 49), 160); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 51), 180); + break; + case kMars35WestSpinAirlockToEast: + case kMars60WestSpinAirlockToEast: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 270); + compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale, 90); + compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale * 3, 270); + break; + case kMars35EastSpinAirlockToWest: + case kMars60EastSpinAirlockToWest: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 270, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale, 270); + compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale * 3, 90); + break; + case kMars52SpinLeft: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, entry.movieEnd, kMars54Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars54Compass); + break; + case kMars52SpinRight: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, entry.movieEnd, kMars58Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars58Compass); + break; + case kMars52Extend: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, + entry.movieEnd, kMars52Compass + kMarsShieldPanelOffsetAngle); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars52Compass + kMarsShieldPanelOffsetAngle); + break; + case kMars53Retract: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, + kMars52Compass + kMarsShieldPanelOffsetAngle, entry.movieEnd, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass + kMarsShieldPanelOffsetAngle); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars52Compass); + break; + case kMars56ExtendWithBomb: + case kMars56ExtendNoBomb: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, + entry.movieEnd, kMars56Compass - kMarsShieldPanelOffsetAngle); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars56Compass - kMarsShieldPanelOffsetAngle); + break; + case kMars57RetractWithBomb: + case kMars57RetractNoBomb: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, + kMars56Compass - kMarsShieldPanelOffsetAngle, entry.movieEnd, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass - kMarsShieldPanelOffsetAngle); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars56Compass); + break; + case kMars54SpinLeft: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars54Compass, entry.movieEnd, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars54Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars56Compass); + break; + case kMars54SpinRight: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars54Compass, entry.movieEnd, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars54Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars52Compass); + break; + case kMars56SpinLeft: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, + entry.movieEnd, kMars58Compass + 360); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars58Compass + 360); + break; + case kMars56SpinRight: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, entry.movieEnd, kMars54Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars54Compass); + break; + case kMars58SpinLeft: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars58Compass, + entry.movieEnd, kMars52Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars58Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars52Compass); + break; + case kMars58SpinRight: + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, + kMars58Compass + 360, entry.movieEnd, kMars56Compass); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars58Compass + 360); + compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars56Compass); + break; + default: + Neighborhood::getExtraCompassMove(entry, compassMove); + } +} + +void Mars::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + if ((room >= kMars0A && room <= kMars21) || (room >= kMars41 && room <= kMars43)) { + if (GameState.getMarsSeenTimeStream()) + loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF"); + } else if (room >= kMars22 && room <= kMars31South) { + loadLoopSound1("Sounds/Mars/Reception.02.22K.8.AIFF", 0x100 / 4); + } else if (room >= kMars32 && room <= kMars34) { + loadLoopSound1("Sounds/Mars/Pod Room Ambient.22K.8.AIFF"); + } else if (room == kMars35) { + if (getAirQuality(room) == kAirQualityVacuum) + loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF"); + else + loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF", 0x100 / 2); + } else if (room >= kMars36 && room <= kMars39) { + loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF"); + } else if (room >= kMars45 && room <= kMars51) { + loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF"); + } else if (room >= kMars52 && room <= kMars58) { + loadLoopSound1("Sounds/Mars/ReactorLoop.22K.8.AIFF"); + } else if (room == kMars60) { + if (getAirQuality(room) == kAirQualityVacuum) + loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF"); + else + loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF", 0x100 / 2); + } else if (room >= kMarsMaze004 && room <= kMarsMaze200) { + loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF"); + } else if (room == kMarsRobotShuttle) { + loadLoopSound1("Sounds/Mars/Robot Shuttle.22K.8.AIFF"); + } + + if (!_noAirFuse.isFuseLit()) { + switch (room) { + case kMars02: + case kMars05: + case kMars06: + case kMars07: + case kMars08: + loadLoopSound2("Sounds/Mars/Gantry Loop.aiff", 0x100, 0, 0); + break; + // Robot at maze 48 + case kMarsMaze037: + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + else + loadLoopSound2(""); + break; + case kMarsMaze038: + case kMarsMaze039: + case kMarsMaze049: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze050: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze051: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze052: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + break; + case kMarsMaze042: + case kMarsMaze053: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 8); + break; + case kMarsMaze058: + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + else + loadLoopSound2(""); + break; + // Robot at 151 + case kMarsMaze148: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze147: + case kMarsMaze149: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze146: + case kMarsMaze152: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze145: + case kMarsMaze153: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + break; + // Robots at 80 and 82. + case kMarsMaze079: + case kMarsMaze081: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze078: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze083: + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + else + loadLoopSound2(""); + break; + case kMarsMaze118: + case kMarsMaze076: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze074: + case kMarsMaze117: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + break; + // Robot at 94 + case kMarsMaze093: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze091: + case kMarsMaze092: + case kMarsMaze098: + case kMarsMaze101: + case kMarsMaze100: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze090: + case kMarsMaze099: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze089: + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + case kMarsMaze178: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); + break; + // Robot at 197 + case kMarsMaze191: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); + break; + case kMarsMaze190: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); + break; + case kMarsMaze198: + case kMarsMaze189: + loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); + break; + default: + loadLoopSound2(""); + break; + } + } +} + +void Mars::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kMars02, kSouth): + case MakeRoomView(kMars19, kEast): + case MakeRoomView(kMars22, kNorth): + case MakeRoomView(kMars43, kEast): + case MakeRoomView(kMars51, kEast): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars60, kWest): + case MakeRoomView(kMarsMaze004, kWest): + case MakeRoomView(kMarsMaze009, kWest): + case MakeRoomView(kMarsMaze012, kWest): + case MakeRoomView(kMarsMaze037, kWest): + case MakeRoomView(kMarsMaze047, kNorth): + case MakeRoomView(kMarsMaze052, kWest): + case MakeRoomView(kMarsMaze057, kNorth): + case MakeRoomView(kMarsMaze071, kWest): + case MakeRoomView(kMarsMaze081, kNorth): + case MakeRoomView(kMarsMaze088, kWest): + case MakeRoomView(kMarsMaze093, kWest): + case MakeRoomView(kMarsMaze115, kNorth): + case MakeRoomView(kMarsMaze120, kWest): + case MakeRoomView(kMarsMaze126, kEast): + case MakeRoomView(kMarsMaze133, kNorth): + case MakeRoomView(kMarsMaze144, kNorth): + case MakeRoomView(kMarsMaze156, kEast): + case MakeRoomView(kMarsMaze162, kNorth): + case MakeRoomView(kMarsMaze177, kWest): + case MakeRoomView(kMarsMaze180, kNorth): + case MakeRoomView(kMarsMaze187, kWest): + case MakeRoomView(kMarsMaze199, kWest): + makeContinuePoint(); + break; + case MakeRoomView(kMars05, kEast): + case MakeRoomView(kMars06, kEast): + case MakeRoomView(kMars07, kEast): + if (GameState.getMarsSecurityDown()) + makeContinuePoint(); + break; + case MakeRoomView(kMars46, kSouth): + if (!GameState.getMarsSeenRobotAtReactor()) + makeContinuePoint(); + break; + case MakeRoomView(kMars46, kWest): + if (GameState.getMarsAvoidedReactorRobot()) + makeContinuePoint(); + break; + } +} + +void Mars::launchMaze007Robot() { + startExtraLongSequence(kMarsMaze007RobotApproach, kMarsMaze007RobotDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze007RobotLoopingTime, kMarsMovieScale, kMaze007RobotLoopingEvent); +} + +void Mars::launchMaze015Robot() { + startExtraLongSequence(kMarsMaze015SouthRobotApproach, kMarsMaze015SouthRobotDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze015RobotLoopingTime, kMarsMovieScale, kMaze015RobotLoopingEvent); +} + +void Mars::launchMaze101Robot() { + startExtraLongSequence(kMarsMaze101EastRobotApproach, kMarsMaze101EastRobotDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze101RobotLoopingTime, kMarsMovieScale, kMaze101RobotLoopingEvent); +} + +void Mars::launchMaze104Robot() { + startExtraLongSequence(kMarsMaze104WestLoop, kMarsMaze104WestDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze104RobotLoopingTime, kMarsMovieScale, kMaze104RobotLoopingEvent); +} + +void Mars::launchMaze133Robot() { + startExtraLongSequence(kMarsMaze133SouthApproach, kMarsMaze133SouthDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze133RobotLoopingTime, kMarsMovieScale, kMaze133RobotLoopingEvent); +} + +void Mars::launchMaze136Robot() { + startExtraLongSequence(kMarsMaze136NorthApproach, kMarsMaze136NorthDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze136RobotLoopingTime, kMarsMovieScale, kMaze136RobotLoopingEvent); +} + +void Mars::launchMaze184Robot() { + startExtraLongSequence(kMarsMaze184WestLoop, kMarsMaze184WestDeath, kExtraCompletedFlag, kFilterAllInput); + scheduleEvent(kMaze184RobotLoopingTime, kMarsMovieScale, kMaze184RobotLoopingEvent); +} + +void Mars::timerExpired(const uint32 eventType) { + switch (eventType) { + case kMaze007RobotLoopingEvent: + case kMaze015RobotLoopingEvent: + case kMaze101RobotLoopingEvent: + case kMaze104RobotLoopingEvent: + case kMaze133RobotLoopingEvent: + case kMaze136RobotLoopingEvent: + case kMaze184RobotLoopingEvent: + _interruptionFilter = kFilterNoInput; + break; + } +} + +void Mars::arriveAt(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kMars18, kNorth): + if (GameState.getMarsPodAtUpperPlatform()) + setCurrentAlternate(kAltMarsPodAtMars34); + break; + case MakeRoomView(kMars27, kEast): + case MakeRoomView(kMars29, kEast): + if (GameState.isTakenItemID(kMarsCard)) + setCurrentAlternate(kAltMarsTookCard); + else + setCurrentAlternate(kAltMarsNormal); + break; + case MakeRoomView(kMars35, kEast): + case MakeRoomView(kMars35, kWest): + if (GameState.getMarsAirlockOpen()) + setCurrentAlternate(kAltMars35AirlockWest); + else + setCurrentAlternate(kAltMars35AirlockEast); + break; + case MakeRoomView(kMars60, kEast): + case MakeRoomView(kMars60, kWest): + if (GameState.getMarsAirlockOpen()) + setCurrentAlternate(kAltMars60AirlockEast); + else + setCurrentAlternate(kAltMars60AirlockWest); + break; + case MakeRoomView(kMars45, kNorth): + case MakeRoomView(kMars45, kSouth): + case MakeRoomView(kMars45, kEast): + case MakeRoomView(kMars45, kWest): + GameState.setMarsPodAtUpperPlatform(false); + setCurrentAlternate(kAltMarsPodAtMars45); + break; + case MakeRoomView(kMars46, kNorth): + case MakeRoomView(kMars46, kSouth): + case MakeRoomView(kMars46, kEast): + case MakeRoomView(kMars46, kWest): + case MakeRoomView(kMars47, kNorth): + case MakeRoomView(kMars47, kSouth): + case MakeRoomView(kMars47, kEast): + case MakeRoomView(kMars47, kWest): + if (GameState.getMarsPodAtUpperPlatform()) + setCurrentAlternate(kAltMarsNormal); + else + setCurrentAlternate(kAltMarsPodAtMars45); + break; + case MakeRoomView(kMars48, kNorth): + case MakeRoomView(kMars48, kSouth): + case MakeRoomView(kMars48, kEast): + case MakeRoomView(kMars48, kWest): + case MakeRoomView(kMars49, kNorth): + case MakeRoomView(kMars49, kEast): + case MakeRoomView(kMars49, kWest): + if (GameState.isTakenItemID(kAirMask)) + setCurrentAlternate(kAltMarsTookMask); + else + setCurrentAlternate(kAltMarsNormal); + break; + case MakeRoomView(kMars49, kSouth): + if (GameState.getMarsMaskOnFiller()) + setCurrentAlternate(kAltMarsMaskOnFiller); + else if (GameState.isTakenItemID(kAirMask)) + setCurrentAlternate(kAltMarsTookMask); + else + setCurrentAlternate(kAltMarsNormal); + break; + } + + Neighborhood::arriveAt(room, direction); + checkAirlockDoors(); + setUpReactorEnergyDrain(); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kMars0A, kNorth): + if (!GameState.getMarsSeenTimeStream()) + startExtraLongSequence(kMarsArrivalFromTSA, kMars0AWatchShuttleDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kMars07, kSouth): + case MakeRoomView(kMars13, kNorth): + if (!GameState.getMarsHeardCheckInMessage()) { + playSpotSoundSync(kMarsCheckInRequiredIn, kMarsCheckInRequiredOut); + GameState.setMarsHeardCheckInMessage(true); + } + break; + case MakeRoomView(kMars44, kWest): + if (GameState.getMarsReadyForShuttleTransport()) + startUpFromFinishedSpaceChase(); + else if (GameState.getMarsFinishedCanyonChase()) + startUpFromSpaceChase(); + else + _neighborhoodNotification.setNotificationFlags(kTimeForCanyonChaseFlag, kTimeForCanyonChaseFlag); + break; + case MakeRoomView(kMars10, kNorth): + if (!GameState.getMarsRobotThrownPlayer()) + startExtraSequence(kRobotThrowsPlayer, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kMars11, kSouth): + case MakeRoomView(kMars12, kSouth): + setCurrentActivation(kActivationReadyForKiosk); + break; + case MakeRoomView(kMars15, kWest): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) { + playSpotSoundSync(kMarsShuttle2DepartedIn, kMarsShuttle2DepartedOut); + restoreStriding(kMars17, kWest, kAltMarsNormal); + GameState.setMarsSecurityDown(true); + } + break; + case MakeRoomView(kMars17, kNorth): + case MakeRoomView(kMars17, kSouth): + case MakeRoomView(kMars17, kEast): + case MakeRoomView(kMars17, kWest): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) + forceStridingStop(kMars17, kWest, kAltMarsNormal); + + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) { + startExtraSequence(kRobotOnWayToShuttle, kExtraCompletedFlag, kFilterNoInput); + restoreStriding(kMars19, kWest, kAltMarsNormal); + GameState.setMarsSawRobotLeave(true); + } + break; + case MakeRoomView(kMars19, kNorth): + case MakeRoomView(kMars19, kSouth): + case MakeRoomView(kMars19, kWest): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) + forceStridingStop(kMars19, kWest, kAltMarsNormal); + + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) + forceStridingStop(kMars17, kWest, kAltMarsNormal); + break; + case MakeRoomView(kMars19, kEast): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) + forceStridingStop(kMars19, kWest, kAltMarsNormal); + + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) + forceStridingStop(kMars17, kWest, kAltMarsNormal); + break; + case MakeRoomView(kMars32, kNorth): + if (!GameState.getMarsPodAtUpperPlatform()) { + playSpotSoundSync(kMarsPodArrivedUpperPlatformIn, kMarsPodArrivedUpperPlatformOut); + GameState.setMarsPodAtUpperPlatform(true); + } + break; + case MakeRoomView(kMars33North, kNorth): + setCurrentActivation(kActivationTunnelMapReady); + // Fall through... + case MakeRoomView(kMars33, kSouth): + case MakeRoomView(kMars33, kEast): + case MakeRoomView(kMars33, kWest): + case MakeRoomView(kMars32, kSouth): + case MakeRoomView(kMars32, kEast): + case MakeRoomView(kMars32, kWest): + if (!GameState.getMarsPodAtUpperPlatform()) + GameState.setMarsPodAtUpperPlatform(true); + break; + case MakeRoomView(kMars34, kNorth): + startExtraSequence(kMars34NorthPodGreeting, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kMars34, kSouth): + case MakeRoomView(kMars45, kNorth): + setCurrentActivation(kActivateMarsPodClosed); + break; + case MakeRoomView(kMars35, kWest): + if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) + forceStridingStop(kMars19, kWest, kAltMarsNormal); + // Fall through... + case MakeRoomView(kMars60, kEast): + if (!GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + break; + case MakeRoomView(kMars35, kEast): + case MakeRoomView(kMars60, kWest): + if (GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + break; + case MakeRoomView(kMars39, kWest): + if (GameState.getLastRoom() == kMarsMaze200) + GameState.setMarsPodAtUpperPlatform(false); + break; + case MakeRoomView(kMars45, kSouth): + // Set up maze doors here. + // Doing it here makes sure that it will be the same if the player comes + // back out of the maze and goes back in, but will vary if + // the player comes back down to the maze a second time. + GameState.setMarsMazeDoorPair1(_vm->getRandomBit()); + GameState.setMarsMazeDoorPair2(_vm->getRandomBit()); + GameState.setMarsMazeDoorPair3(_vm->getRandomBit()); + GameState.setMarsArrivedBelow(true); + break; + case MakeRoomView(kMars48, kEast): + if (!GameState.getMarsSeenRobotAtReactor()) { + // Preload the looping sound... + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0); + startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput); + } else if (!GameState.getMarsAvoidedReactorRobot()) { + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); + loopExtraSequence(kMars48RobotLoops); + _utilityFuse.primeFuse(kMarsRobotPatienceLimit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting)); + _utilityFuse.lightFuse(); + } + break; + case MakeRoomView(kMars48, kSouth): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); + _utilityFuse.primeFuse(kMarsRobotPatienceLimit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting)); + _utilityFuse.lightFuse(); + } + break; + case MakeRoomView(kMars49, kSouth): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { + playSpotSoundSync(kMarsRobotTakesTransportIn, kMarsRobotTakesTransportOut); + playSpotSoundSync(kMarsPodDepartedLowerPlatformIn, kMarsPodDepartedLowerPlatformOut); + GameState.setMarsAvoidedReactorRobot(true); + GameState.setMarsPodAtUpperPlatform(true); + GameState.setScoringAvoidedRobot(); + } + + if (GameState.isTakenItemID(kAirMask)) + setCurrentActivation(kActivateHotSpotAlways); + else if (GameState.getMarsMaskOnFiller()) + setCurrentActivation(kActivateMaskOnFiller); + else + setCurrentActivation(kActivateMaskOnHolder); + break; + case MakeRoomView(kMars51, kWest): + case MakeRoomView(kMars50, kWest): + case MakeRoomView(kMars48, kWest): + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldNormal); + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + setCurrentActivation(kActivateReactorPlatformOut); + break; + case MakeRoomView(kMars56, kEast): + if (GameState.getMarsLockBroken()) { + setCurrentActivation(kActivateReactorAskOperation); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); + } else if (GameState.getMarsLockFrozen()) { + setCurrentActivation(kActivateReactorReadyForCrowBar); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); + _utilityFuse.primeFuse(kLockFreezeTimeLmit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::lockThawed)); + _utilityFuse.lightFuse(); + } else { + setCurrentActivation(kActivateReactorPlatformOut); + } + break; + case MakeRoomView(kMarsRobotShuttle, kEast): + setCurrentActivation(kActivationRobotHeadClosed); + break; + case MakeRoomView(kMarsMaze007, kNorth): + launchMaze007Robot(); + break; + case MakeRoomView(kMarsMaze015, kSouth): + launchMaze015Robot(); + break; + case MakeRoomView(kMarsMaze101, kEast): + launchMaze101Robot(); + break; + case MakeRoomView(kMarsMaze104, kWest): + launchMaze104Robot(); + break; + case MakeRoomView(kMarsMaze133, kSouth): + launchMaze133Robot(); + break; + case MakeRoomView(kMarsMaze136, kNorth): + launchMaze136Robot(); + break; + case MakeRoomView(kMarsMaze184, kWest): + launchMaze184Robot(); + break; + case MakeRoomView(kMarsMaze199, kSouth): + GameState.setScoringThreadedMaze(); + GameState.setMarsThreadedMaze(true); + break; + case MakeRoomView(kMarsDeathRoom, kNorth): + case MakeRoomView(kMarsDeathRoom, kSouth): + case MakeRoomView(kMarsDeathRoom, kEast): + case MakeRoomView(kMarsDeathRoom, kWest): + switch (GameState.getLastRoom()) { + case kMars39: + die(kDeathDidntLeaveBucket); + break; + case kMars46: + die(kDeathRunOverByPod); + break; + } + break; + } + + checkAirMask(); +} + +void Mars::shieldOn() { + setUpReactorEnergyDrain(); +} + +void Mars::shieldOff() { + setUpReactorEnergyDrain(); +} + +void Mars::turnTo(const DirectionConstant direction) { + switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { + case MakeRoomView(kMars27, kNorth): + case MakeRoomView(kMars27, kSouth): + case MakeRoomView(kMars27, kEast): + case MakeRoomView(kMars29, kNorth): + case MakeRoomView(kMars29, kSouth): + case MakeRoomView(kMars29, kEast): + if (GameState.isTakenItemID(kMarsCard)) + setCurrentAlternate(kAltMarsTookCard); + break; + case MakeRoomView(kMars35, kNorth): + case MakeRoomView(kMars35, kSouth): + case MakeRoomView(kMars60, kNorth): + case MakeRoomView(kMars60, kSouth): + if (getCurrentActivation() == kActivateAirlockPressurized) + playSpotSoundSync(kMarsAirlockPressurizeIn, kMarsAirlockPressurizeOut); + break; + } + + Neighborhood::turnTo(direction); + + switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { + case MakeRoomView(kMars11, kSouth): + case MakeRoomView(kMars12, kSouth): + setCurrentActivation(kActivationReadyForKiosk); + break; + case MakeRoomView(kMars18, kNorth): + if (GameState.getMarsPodAtUpperPlatform()) + setCurrentAlternate(kAltMarsPodAtMars34); + break; + case MakeRoomView(kMars22, kSouth): + if (!GameState.getMarsHeardCheckInMessage()) { + playSpotSoundSync(kMarsCheckInRequiredIn, kMarsCheckInRequiredOut); + GameState.setMarsHeardCheckInMessage(true); + } + break; + case MakeRoomView(kMars34, kSouth): + case MakeRoomView(kMars45, kNorth): + setCurrentActivation(kActivateMarsPodClosed); + break; + case MakeRoomView(kMars34, kNorth): + startExtraSequence(kMars34NorthPodGreeting, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kMars35, kEast): + case MakeRoomView(kMars60, kWest): + if (GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + break; + case MakeRoomView(kMars60, kEast): + if (!GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + break; + case MakeRoomView(kMars35, kWest): + if (!GameState.getMarsAirlockOpen()) + setCurrentActivation(kActivateReadyToPressurizeAirlock); + + // Do this here because this will be called after spinning the airlock after + // going through the gear room. + if (GameState.getMarsThreadedMaze()) + GameState.setScoringThreadedGearRoom(); + break; + case MakeRoomView(kMars48, kNorth): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + die(kDeathDidntGetOutOfWay); + break; + case MakeRoomView(kMars48, kEast): + if (!GameState.getMarsSeenRobotAtReactor()) { + // Preload the looping sound... + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0); + startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput); + } else if (!GameState.getMarsAvoidedReactorRobot()) { + loopExtraSequence(kMars48RobotLoops); + } else if (GameState.isTakenItemID(kAirMask)) { + setCurrentAlternate(kAltMarsTookMask); + } else { + setCurrentAlternate(kAltMarsNormal); + } + break; + case MakeRoomView(kMars48, kWest): + if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) + die(kDeathDidntGetOutOfWay); + else if (GameState.isTakenItemID(kAirMask)) + setCurrentAlternate(kAltMarsTookMask); + else + setCurrentAlternate(kAltMarsNormal); + break; + case MakeRoomView(kMars49, kSouth): + if (GameState.isTakenItemID(kAirMask)) + setCurrentActivation(kActivateHotSpotAlways); + else + setCurrentActivation(kActivateMaskOnHolder); + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + setCurrentActivation(kActivateReactorPlatformOut); + break; + case MakeRoomView(kMarsMaze007, kNorth): + launchMaze007Robot(); + break; + case MakeRoomView(kMarsMaze015, kSouth): + launchMaze015Robot(); + break; + case MakeRoomView(kMarsMaze101, kEast): + launchMaze101Robot(); + break; + case MakeRoomView(kMarsMaze104, kWest): + launchMaze104Robot(); + break; + case MakeRoomView(kMarsMaze133, kSouth): + launchMaze133Robot(); + break; + case MakeRoomView(kMarsMaze136, kNorth): + launchMaze136Robot(); + break; + case MakeRoomView(kMarsMaze184, kWest): + launchMaze184Robot(); + break; + } +} + +void Mars::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { + switch (hotspot->getObjectID()) { + case kMars57RedMoveSpotID: + case kMars57YellowMoveSpotID: + case kMars57GreenMoveSpotID: + if (!_choiceHighlight.choiceHighlighted(hotspot->getObjectID() - kMars57RedMoveSpotID)) + hotspot->setActive(); + break; + case kMars57BlueMoveSpotID: + if (_reactorStage >= 2 && !_choiceHighlight.choiceHighlighted(3)) + hotspot->setActive(); + break; + case kMars57PurpleMoveSpotID: + if (_reactorStage == 3 && !_choiceHighlight.choiceHighlighted(4)) + hotspot->setActive(); + break; + default: + Neighborhood::activateOneHotspot(entry, hotspot); + break; + } +} + +void Mars::activateHotspots() { + InventoryItem *item; + + Neighborhood::activateHotspots(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars48, kEast): + if ((_navMovie.getFlags() & kLoopTimeBase) != 0 && _vm->getDragType() == kDragInventoryUse) + _vm->getAllHotspots().activateOneHotspot(kAttackRobotHotSpotID); + break; + case MakeRoomView(kMars56, kEast): + switch (getCurrentActivation()) { + case kActivateReactorReadyForNitrogen: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kNitrogenCanister); + if (item->getItemState() != kNitrogenFull) + _vm->getAllHotspots().deactivateOneHotspot(kMars57DropNitrogenSpotID); + // Fall through... + case kActivateReactorReadyForCrowBar: + _vm->getAllHotspots().activateOneHotspot(kMars57CantOpenPanelSpotID); + break; + } + break; + case MakeRoomView(kMarsRobotShuttle, kEast): + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleMapChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kRobotShuttleMapChipSpotID); + + if (_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleOpticalChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kRobotShuttleOpticalChipSpotID); + + if (_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kRobotShuttleShieldChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kRobotShuttleShieldChipSpotID); + break; + default: + if (_privateFlags.getFlag(kMarsPrivateInSpaceChaseFlag)) { + if (GameState.getMarsReadyForShuttleTransport()) { + _shuttleTransportSpot.setActive(); + } else { + _energyChoiceSpot.setActive(); + _gravitonChoiceSpot.setActive(); + _tractorChoiceSpot.setActive(); + if (_weaponSelection != kNoWeapon) + _shuttleViewSpot.setActive(); + } + } + break; + } +} + +void Mars::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kMars11NorthKioskSpotID: + case kMars12NorthKioskSpotID: + playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); + Neighborhood::clickInHotspot(input, clickedSpot); + break; + case kMars11NorthKioskSightsSpotID: + case kMars12NorthKioskSightsSpotID: + playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); + if (!startExtraSequenceSync(kMarsSightsInfo, kFilterAllInput)) + showExtraView(kMarsInfoKioskIntro); + break; + case kMars11NorthKioskColonySpotID: + case kMars12NorthKioskColonySpotID: + playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); + if (!startExtraSequenceSync(kMarsColonyInfo, kFilterAllInput)) + showExtraView(kMarsInfoKioskIntro); + break; + case kMars33NorthMonitorSpotID: + switch (_lastExtra) { + case kMars33SlideShow1: + startExtraSequence(kMars33SlideShow2, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars33SlideShow2: + startExtraSequence(kMars33SlideShow3, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars33SlideShow3: + startExtraSequence(kMars33SlideShow4, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars33SlideShow4: + // Should never happen... + default: + startExtraSequence(kMars33SlideShow1, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kMars34SouthOpenStorageSpotID: + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars34SpotOpenNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars34SpotOpenWithBar, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars34SouthCloseStorageSpotID: + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars35WestPressurizeSpotID: + case kMars35EastPressurizeSpotID: + case kMars60WestPressurizeSpotID: + case kMars60EastPressurizeSpotID: + playSpotSoundSync(kMarsAirlockButtonBeepIn, kMarsAirlockButtonBeepOut); + playSpotSoundSync(kMarsAirlockPressurizeIn, kMarsAirlockPressurizeOut); + setCurrentActivation(kActivateAirlockPressurized); + break; + case kMars45NorthOpenStorageSpotID: + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars45SpotOpenNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars45SpotOpenWithBar, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars45NorthCloseStorageSpotID: + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars56ExtractSpotID: + if (GameState.isTakenItemID(kCardBomb)) { + startExtraSequence(kMars56ExtendNoBomb, kExtraCompletedFlag, kFilterNoInput); + setCurrentActivation(kActivateReactorPlatformIn); + } else { + startExtraSequence(kMars56ExtendWithBomb, kExtraCompletedFlag, kFilterNoInput); + setCurrentActivation(kActivateReactorAskLowerScreen); + } + break; + case kMars57UndoMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doUndoOneGuess(); + break; + case kMars57RedMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(0); + break; + case kMars57YellowMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(1); + break; + case kMars57GreenMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(2); + break; + case kMars57BlueMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(3); + break; + case kMars57PurpleMoveSpotID: + playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); + doReactorGuess(4); + break; + case kShuttleEnergySpotID: + case kShuttleGravitonSpotID: + case kShuttleTractorSpotID: + case kShuttleViewSpotID: + case kShuttleTransportSpotID: + spaceChaseClick(input, clickedSpot->getObjectID()); + break; + default: + Neighborhood::clickInHotspot(input, clickedSpot); + break; + } +} + +InputBits Mars::getInputFilter() { + InputBits result = Neighborhood::getInputFilter(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars49, kSouth): + if (GameState.getMarsMaskOnFiller()) + // Can't move when mask is on filler. + result &= ~kFilterAllDirections; + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kEast): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + if (_privateFlags.getFlag(kMarsPrivatePlatformZoomedInFlag)) + // Can't move when platform is extended. + result &= ~kFilterAllDirections; + break; + case MakeRoomView(kMars44, kWest): + if (_canyonChaseMovie.isMovieValid() && _canyonChaseMovie.isRunning()) + result &= ~kFilterAllDirections; + break; + } + + return result; +} + +// Only called when trying to pick up an item and the player can't (because +// the inventory is too full or because the player lets go of the item before +// dropping it into the inventory). +Hotspot *Mars::getItemScreenSpot(Item *item, DisplayElement *element) { + HotSpotID destSpotID; + + switch (item->getObjectID()) { + case kCardBomb: + destSpotID = kMars57GrabBombSpotID; + break; + case kMarsCard: + destSpotID = kMars31SouthCardSpotID; + break; + case kAirMask: + if (GameState.getMarsMaskOnFiller()) + destSpotID = kMars49AirFillingDropSpotID; + else + destSpotID = kMars49AirMaskSpotID; + break; + case kCrowbar: + if (GameState.getCurrentRoom() == kMars34) + destSpotID = kMars34SouthCrowbarSpotID; + else + destSpotID = kMars45NorthCrowbarSpotID; + break; + case kMapBiochip: + destSpotID = kRobotShuttleMapChipSpotID; + break; + case kOpticalBiochip: + destSpotID = kRobotShuttleOpticalChipSpotID; + break; + case kShieldBiochip: + destSpotID = kRobotShuttleShieldChipSpotID; + break; + default: + destSpotID = kNoHotSpotID; + break; + } + + if (destSpotID == kNoHotSpotID) + return Neighborhood::getItemScreenSpot(item, element); + + return _vm->getAllHotspots().findHotspotByID(destSpotID); +} + +void Mars::takeItemFromRoom(Item *item) { + switch (item->getObjectID()) { + case kAirMask: + setCurrentAlternate(kAltMarsTookMask); + break; + case kCardBomb: + _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, true); + break; + case kMapBiochip: + _privateFlags.setFlag(kMarsPrivateGotMapChipFlag, true); + break; + case kShieldBiochip: + _privateFlags.setFlag(kMarsPrivateGotShieldChipFlag, true); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kMarsPrivateGotOpticalChipFlag, true); + break; + } + + Neighborhood::takeItemFromRoom(item); +} + +void Mars::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kAirMask: + setCurrentActivation(kActivateHotSpotAlways); + if (!GameState.getScoringGotOxygenMask()) { + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM48SB", false, kWarningInterruption); + GameState.setScoringGotOxygenMask(); + } + break; + case kCrowbar: + GameState.setScoringGotCrowBar(); + g_AIArea->checkMiddleArea(); + break; + case kMarsCard: + GameState.setScoringGotMarsCard(); + g_AIArea->checkMiddleArea(); + break; + case kCardBomb: + GameState.setScoringGotCardBomb(); + if (GameState.getMarsLockBroken()) { + startExtraSequence(kMars57BackToNormal, kExtraCompletedFlag, kFilterNoInput); + GameState.setMarsLockBroken(false); + } + + _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, false); + break; + case kMapBiochip: + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { + GameState.setMarsFinished(true); + GameState.setScoringMarsGandhi(); + startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kShieldBiochip: + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { + GameState.setMarsFinished(true); + GameState.setScoringMarsGandhi(); + startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kOpticalBiochip: + g_opticalChip->addAries(); + GameState.setScoringGotMarsOpMemChip(); + + if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { + GameState.setMarsFinished(true); + GameState.setScoringMarsGandhi(); + startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); + } + break; + } +} + +void Mars::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + if (dropSpot->getObjectID() == kAttackRobotHotSpotID) { + _attackingItem = (InventoryItem *)item; + startExtraSequence(kMars48RobotDefends, kExtraCompletedFlag, kFilterNoInput); + loadLoopSound2(""); + } else { + switch (item->getObjectID()) { + case kMarsCard: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot && dropSpot->getObjectID() == kMars34NorthCardDropSpotID) + startExtraSequence(kMarsTurnOnPod, kExtraCompletedFlag, kFilterNoInput); + break; + case kNitrogenCanister: + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot && dropSpot->getObjectID() == kMars57DropNitrogenSpotID) + startExtraSequence(kMars57FreezeLock, kExtraCompletedFlag, kFilterNoInput); + break; + case kCrowbar: + _utilityFuse.stopFuse(); + Neighborhood::dropItemIntoRoom(item, dropSpot); + if (dropSpot && dropSpot->getObjectID() == kMars57DropCrowBarSpotID) + startExtraSequence(kMars57BreakLock, kExtraCompletedFlag, kFilterNoInput); + break; + case kAirMask: + if (dropSpot) { + if (dropSpot->getObjectID() == kMars49AirFillingDropSpotID) { + if (!GameState.getMarsMaskOnFiller()) { + Neighborhood::dropItemIntoRoom(item, dropSpot); + startExtraSequence(kMars49SouthViewMaskFilling, kExtraCompletedFlag, kFilterNoInput); + } else { + setCurrentActivation(kActivateMaskOnFiller); + setCurrentAlternate(kAltMarsMaskOnFiller); + Neighborhood::dropItemIntoRoom(item, dropSpot); + } + } else if (dropSpot->getObjectID() == kMars49AirMaskSpotID) { + setCurrentAlternate(kAltMarsNormal); + setCurrentActivation(kActivateMaskOnHolder); + Neighborhood::dropItemIntoRoom(item, dropSpot); + } + } + break; + case kCardBomb: + _privateFlags.setFlag(kMarsPrivateDraggingBombFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kMapBiochip: + _privateFlags.setFlag(kMarsPrivateGotMapChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kShieldBiochip: + _privateFlags.setFlag(kMarsPrivateGotShieldChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kMarsPrivateGotOpticalChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } + } +} + +void Mars::robotTiredOfWaiting() { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kMars48, kEast)) { + if (_attackingItem) { + startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); + loadLoopSound2(""); + } else { + _privateFlags.setFlag(kMarsPrivateRobotTiredOfWaitingFlag, true); + } + } else { + die(kDeathDidntGetOutOfWay); + } +} + +void Mars::turnLeft() { + if (isEventTimerRunning()) + cancelEvent(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars34, kSouth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, true); + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::turnLeft(); + } + break; + case MakeRoomView(kMars45, kNorth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, true); + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::turnLeft(); + } + break; + default: + Neighborhood::turnLeft(); + break; + } +} + +void Mars::turnRight() { + if (isEventTimerRunning()) + cancelEvent(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars34, kSouth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, true); + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::turnRight(); + } + break; + case MakeRoomView(kMars45, kNorth): + if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, true); + if (GameState.isTakenItemID(kCrowbar)) + startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::turnRight(); + } + break; + default: + Neighborhood::turnRight(); + break; + } +} + +void Mars::receiveNotification(Notification *notification, const NotificationFlags flag) { + InventoryItem *item; + + Neighborhood::receiveNotification(notification, flag); + + if ((flag & kExtraCompletedFlag) != 0) { + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kMarsArrivalFromTSA: + GameState.setMarsSeenTimeStream(true); + loadAmbientLoops(); + playSpotSoundSync(kMarsShuttle1DepartedIn, kMarsShuttle1DepartedOut); + makeContinuePoint(); + break; + case kRobotThrowsPlayer: + GameState.setMarsRobotThrownPlayer(true); + GameState.setScoringThrownByRobot(); + restoreStriding(kMars08, kNorth, kAltMarsNormal); + arriveAt(kMars08, kNorth); + if (!GameState.getMarsHeardUpperPodMessage()) { + playSpotSoundSync(kMarsPodDepartedUpperPlatformIn, + kMarsPodDepartedUpperPlatformOut); + GameState.setMarsHeardUpperPodMessage(true); + } + break; + case kMarsInfoKioskIntro: + GameState.setScoringSawMarsKiosk(); + setCurrentActivation(kActivationKioskChoice); + break; + case kMars33SlideShow4: + GameState.setScoringSawTransportMap(); + setCurrentActivation(kActivateHotSpotAlways); + break; + case kMars34SpotOpenNoBar: + case kMars34SpotOpenWithBar: + case kMars45SpotOpenNoBar: + case kMars45SpotOpenWithBar: + _privateFlags.setFlag(kMarsPrivatePodStorageOpenFlag, true); + setCurrentActivation(kActivateMarsPodOpen); + break; + case kMars34SpotCloseNoBar: + case kMars34SpotCloseWithBar: + case kMars45SpotCloseNoBar: + case kMars45SpotCloseWithBar: + _privateFlags.setFlag(kMarsPrivatePodStorageOpenFlag, false); + setCurrentActivation(kActivateMarsPodClosed); + if (_privateFlags.getFlag(kMarsPrivatePodTurnLeftFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, false); + turnLeft(); + } else if (_privateFlags.getFlag(kMarsPrivatePodTurnRightFlag)) { + _privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, false); + turnRight(); + } + break; + case kMarsTurnOnPod: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kMarsCard); + _vm->addItemToInventory(item); + GameState.setScoringTurnedOnTransport(); + loadLoopSound1(""); + loadLoopSound2(""); + startExtraSequence(kMarsTakePodToMars45, kExtraCompletedFlag, kFilterNoInput); + break; + case kMarsTakePodToMars45: + arriveAt(kMars45, kSouth); + break; + case kMars35WestSpinAirlockToEast: + GameState.setMarsAirlockOpen(false); + setCurrentAlternate(kAltMars35AirlockEast); + turnTo(kWest); + setCurrentActivation(kActivateReadyToPressurizeAirlock); + g_airMask->airQualityChanged(); + checkAirMask(); + loadAmbientLoops(); + break; + case kMars35EastSpinAirlockToWest: + GameState.setMarsAirlockOpen(true); + setCurrentAlternate(kAltMars35AirlockWest); + turnTo(kEast); + setCurrentActivation(kActivateReadyToPressurizeAirlock); + g_airMask->airQualityChanged(); + checkAirMask(); + loadAmbientLoops(); + break; + case kMars48RobotApproaches: + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); + GameState.setMarsSeenRobotAtReactor(true); + loopExtraSequence(kMars48RobotLoops); + _utilityFuse.primeFuse(kMarsRobotPatienceLimit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::robotTiredOfWaiting)); + _utilityFuse.lightFuse(); + break; + case kMars48RobotDefends: + _vm->addItemToInventory(_attackingItem); + _attackingItem = 0; + if (_privateFlags.getFlag(kMarsPrivateRobotTiredOfWaitingFlag)) { + startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); + loadLoopSound2("", 0x100, 0, 0); + } else { + loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); + loopExtraSequence(kMars48RobotLoops, kExtraCompletedFlag); + } + break; + case kMars48RobotKillsPlayer: + loadLoopSound2(""); + die(kDeathDidntGetOutOfWay); + break; + case kMars49SouthViewMaskFilling: + setCurrentActivation(kActivateMaskOnFiller); + setCurrentAlternate(kAltMarsMaskOnFiller); + GameState.setMarsMaskOnFiller(true); + break; + case kMars58SpinLeft: + case kMars54SpinRight: + GameState.setScoringActivatedPlatform(); + arriveAt(kMars52, kEast); + break; + case kMars52SpinLeft: + case kMars56SpinRight: + GameState.setScoringActivatedPlatform(); + arriveAt(kMars54, kEast); + break; + case kMars54SpinLeft: + case kMars58SpinRight: + GameState.setScoringActivatedPlatform(); + arriveAt(kMars56, kEast); + break; + case kMars56SpinLeft: + case kMars52SpinRight: + GameState.setScoringActivatedPlatform(); + arriveAt(kMars58, kEast); + break; + case kMars52Extend: + case kMars54Extend: + case kMars56ExtendNoBomb: + case kMars58Extend: + GameState.setScoringActivatedPlatform(); + setCurrentActivation(kActivateReactorPlatformIn); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); + break; + case kMars53Retract: + case kMars55Retract: + case kMars57RetractWithBomb: + case kMars57RetractNoBomb: + case kMars59Retract: + GameState.setScoringActivatedPlatform(); + setCurrentActivation(kActivateReactorPlatformOut); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, false); + break; + case kMars56ExtendWithBomb: + playSpotSoundSync(kMustBeUnlockedIn, kMustBeUnlockedOut); + GameState.setScoringActivatedPlatform(); + _privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); + break; + case kMars57CantOpenPanel: + GameState.setScoringActivatedPlatform(); + setCurrentActivation(kActivateReactorAskLowerScreen); + break; + case kMars57LowerScreenClosed: + case kMars57ThawLock: + setCurrentActivation(kActivateReactorReadyForNitrogen); + GameState.setMarsLockFrozen(false); + break; + case kMars57FreezeLock: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kNitrogenCanister); + item->setItemState(kNitrogenEmpty); + _vm->addItemToInventory(item); + setCurrentActivation(kActivateReactorReadyForCrowBar); + GameState.setScoringUsedLiquidNitrogen(); + GameState.setMarsLockFrozen(true); + showExtraView(kMars57LockFrozenView); + _utilityFuse.primeFuse(kLockFreezeTimeLmit); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::lockThawed)); + _utilityFuse.lightFuse(); + break; + case kMars57BreakLock: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kCrowbar); + _vm->addItemToInventory(item); + GameState.setScoringUsedCrowBar(); + GameState.setMarsLockBroken(true); + GameState.setMarsLockFrozen(false); + startExtraLongSequence(kMars57OpenPanel, kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars57OpenPanel: + case kMars57OpenPanelChoices: + setCurrentActivation(kActivateReactorAskOperation); + break; + case kMars57ShieldEvaluation: + case kMars57MeasureOutput: + setCurrentActivation(kActivateReactorRanEvaluation); + loopExtraSequence(kMars57ShieldOkayLoop); + break; + case kMars57RunDiagnostics: + setCurrentActivation(kActivateReactorRanDiagnostics); + GameState.setScoringFoundCardBomb(); + break; + case kMars57BombExplodes: + case kMars57BombExplodesInGame: + die(kDeathDidntDisarmMarsBomb); + break; + case kMars57BombAnalysis: + setCurrentActivation(kActivateReactorAnalyzed); + break; + case kMars57DontLink: + startExtraSequence(kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); + break; + case kMars57CircuitLink: + setCurrentActivation(kActivateReactorInstructions); + break; + case kMars57GameLevel1: + setUpReactorLevel1(); + break; + case kMars57GameLevel2: + case kMars57GameLevel3: + setUpNextReactorLevel(); + break; + case kMars57GameSolved: + setCurrentActivation(kActivateReactorBombSafe); + break; + case kMars57ExposeBomb: + setCurrentActivation(kActivateReactorBombExposed); + _privateFlags.setFlag(kMarsPrivateBombExposedFlag, true); + break; + case kMars57BackToNormal: + setCurrentActivation(kActivateReactorPlatformIn); + _privateFlags.setFlag(kMarsPrivateBombExposedFlag, false); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM51SW", false, kWarningInterruption); + break; + case kMars60WestSpinAirlockToEast: + GameState.setMarsAirlockOpen(true); + setCurrentAlternate(kAltMars60AirlockEast); + turnTo(kWest); + setCurrentActivation(kActivateReadyToPressurizeAirlock); + g_airMask->airQualityChanged(); + checkAirMask(); + loadAmbientLoops(); + break; + case kMars60EastSpinAirlockToWest: + GameState.setMarsAirlockOpen(false); + setCurrentAlternate(kAltMars60AirlockWest); + turnTo(kEast); + setCurrentActivation(kActivateReadyToPressurizeAirlock); + g_airMask->airQualityChanged(); + checkAirMask(); + loadAmbientLoops(); + break; + case kMarsRobotHeadOpen: + setCurrentActivation(kActivationRobotHeadOpen); + break; + case kMarsRobotHeadClose: + recallToTSASuccess(); + break; + case kMarsMaze007RobotApproach: + case kMarsMaze015SouthRobotApproach: + case kMarsMaze101EastRobotApproach: + case kMarsMaze104WestLoop: + case kMarsMaze133SouthApproach: + case kMarsMaze136NorthApproach: + case kMarsMaze184WestLoop: + die(kDeathGroundByMazebot); + break; + } + } else if ((flag & kTimeForCanyonChaseFlag) != 0) { + doCanyonChase(); + } else if ((flag & kExplosionFinishedFlag) != 0) { + _explosions.stop(); + _explosions.hide(); + if (g_robotShip->isDead()) { + GameState.setMarsFinished(true); + _centerShuttleMovie.hide(); + _upperRightShuttleMovie.show(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightTargetDestroyedTime); + _upperRightShuttleMovie.redrawMovieWorld(); + _rightDamageShuttleMovie.hide(); + playMovieSegment(&_rightShuttleMovie, kShuttleRightDestroyedStart, kShuttleRightDestroyedStop); + playSpotSoundSync(kShuttleDestroyedIn, kShuttleDestroyedOut); + throwAwayMarsShuttle(); + reinstateMonocleInterface(); + recallToTSASuccess(); + } + } else if ((flag & kTimeToTransportFlag) != 0) { + transportToRobotShip(); + } + + if (g_AIArea) + g_AIArea->checkMiddleArea(); +} + +void Mars::spotCompleted() { + Neighborhood::spotCompleted(); + + if (GameState.getCurrentRoom() == kMarsRobotShuttle) + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XN59WD", false, kWarningInterruption); +} + +void Mars::doCanyonChase() { + GameState.setScoringEnteredShuttle(); + setNextHandler(_vm); + throwAwayInterface(); + + _vm->_cursor->hide(); + + // Open the spot sounds movie again... + _spotSounds.initFromQuickTime(getSoundSpotsName()); + _spotSounds.setVolume(_vm->getSoundFXLevel()); + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile("Images/Mars/M44ESA.movie")) + error("Could not load interface->shuttle transition video"); + + video->start(); + + while (!_vm->shouldQuit() && !video->endOfVideo()) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) + _vm->drawScaledFrame(frame, 0, 0); + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) + ; + + g_system->delayMillis(10); + } + + delete video; + + if (_vm->shouldQuit()) + return; + + initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, + kShuttle1Top, true); + initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, + kShuttle2Top, true); + initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, + kShuttle3Top, true); + initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, + kShuttle4Top, true); + + initOneMovie(&_canyonChaseMovie, "Images/Mars/Canyon.movie", + kShuttleMonitorOrder, kShuttleWindowLeft, kShuttleWindowTop, true); + _canyonChaseMovie.setVolume(_vm->getSoundFXLevel()); + + loadLoopSound1("Sounds/Mars/Inside Cockpit.22K.8.AIFF"); + + // Swing shuttle around... + playMovieSegment(&_canyonChaseMovie, kShuttleSwingStart, kShuttleSwingStop); + + initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", + kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); + + initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", + kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); + + initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); + + initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerRightLeft, kShuttleLowerRightTop, false); + + initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", + kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); + + initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); + + initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperRightLeft, kShuttleUpperRightTop, false); + + initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); + + initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); + + _centerShuttleMovie.show(); + _centerShuttleMovie.setTime(kShuttleCenterBoardingTime); + playSpotSoundSync(kShuttleCockpitIn, kShuttleCockpitOut); + + _centerShuttleMovie.setTime(kShuttleCenterCheckTime); + playSpotSoundSync(kShuttleOnboardIn, kShuttleOnboardOut); + + _shuttleEnergyMeter.initShuttleEnergyMeter(); + _shuttleEnergyMeter.powerUpMeter(); + while (_shuttleEnergyMeter.isFading()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + g_system->updateScreen(); + } + + _leftShuttleMovie.show(); + playMovieSegment(&_leftShuttleMovie, kShuttleLeftIntroStart, kShuttleLeftIntroStop); + + _leftShuttleMovie.setTime(kShuttleLeftNormalTime); + _leftShuttleMovie.redrawMovieWorld(); + + _leftDamageShuttleMovie.show(); + playMovieSegment(&_leftDamageShuttleMovie); + + // Take it down a tick initially. This sets the time to the time of the last tick, + // so that subsequence drops will drop it down a tick. + _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getTime() - 40); + _leftDamageShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.show(); + _lowerRightShuttleMovie.setTime(kShuttleLowerRightOffTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + _centerShuttleMovie.setTime(kShuttleCenterNavCompTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleNavigationIn, kShuttleNavigationOut); + + _centerShuttleMovie.setTime(kShuttleCenterCommTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleCommunicationIn, kShuttleCommunicationOut); + + _centerShuttleMovie.setTime(kShuttleCenterAllSystemsTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleAllSystemsIn, kShuttleAllSystemsOut); + + _centerShuttleMovie.setTime(kShuttleCenterSecureLooseTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleSecureLooseIn, kShuttleSecureLooseOut); + + _centerShuttleMovie.setTime(kShuttleCenterAutoTestTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleAutoTestingIn, kShuttleAutoTestingOut); + + _leftShuttleMovie.setTime(kShuttleLeftAutoTestTime); + _leftShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kMarsThrusterAutoTestIn, kMarsThrusterAutoTestOut); + _leftShuttleMovie.setTime(kShuttleLeftNormalTime); + _leftShuttleMovie.redrawMovieWorld(); + + _centerShuttleMovie.setTime(kShuttleCenterLaunchTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttlePrepareForDropIn, kShuttlePrepareForDropOut); + + playSpotSoundSync(kShuttleAllClearIn, kShuttleAllClearOut); + + _centerShuttleMovie.setTime(kShuttleCenterEnterTubeTime); + _centerShuttleMovie.redrawMovieWorld(); + + _lowerLeftShuttleMovie.show(); + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftCollisionTime); + + loadLoopSound1(""); + + _canyonChaseMovie.setSegment(kCanyonChaseStart, kCanyonChaseStop); + _canyonChaseMovie.start(); + + startMarsTimer(kLaunchTubeReachedTime, kMovieTicksPerSecond, kMarsLaunchTubeReached); +} + +void Mars::startUpFromFinishedSpaceChase() { + setNextHandler(_vm); + throwAwayInterface(); + + initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, + kShuttle1Top, true); + initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, + kShuttle2Top, true); + initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, + kShuttle3Top, true); + initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, + kShuttle4Top, true); + + initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", + kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); + + initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", + kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); + + initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); + + initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerRightLeft, kShuttleLowerRightTop, false); + + initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", + kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); + + initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); + + initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperRightLeft, kShuttleUpperRightTop, false); + + initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); + + initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); + + _centerShuttleMovie.show(); + + _shuttleEnergyMeter.initShuttleEnergyMeter(); + _shuttleEnergyMeter.setEnergyValue(kFullShuttleEnergy); + + _leftShuttleMovie.show(); + _leftShuttleMovie.setTime(kShuttleLeftNormalTime); + _leftShuttleMovie.redrawMovieWorld(); + + _leftDamageShuttleMovie.show(); + _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getDuration() - 40); + _leftDamageShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.show(); + + _lowerLeftShuttleMovie.show(); + + loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); + + initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, + kShuttleJunkTop, false); + + initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); + _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + + _energyBeam.initShuttleWeapon(); + _gravitonCannon.initShuttleWeapon(); + + _upperLeftShuttleMovie.show(); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + _rightShuttleMovie.show(); + _rightShuttleMovie.setTime(kShuttleRightIntroStop - 1); + _rightShuttleMovie.redrawMovieWorld(); + + _rightDamageShuttleMovie.show(); + _rightDamageShuttleMovie.setTime(40); + _rightDamageShuttleMovie.redrawMovieWorld(); + + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); + _lowerLeftShuttleMovie.redrawMovieWorld(); + + _shuttleTransportSpot.setArea(kShuttleTransportBounds); + _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleTransportSpot); + + _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + + _upperRightShuttleMovie.show(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightOverloadTime); + _upperRightShuttleMovie.redrawMovieWorld(); + + _centerShuttleMovie.setTime(kShuttleCenterSafeTime); + _centerShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + + initOneMovie(&_canyonChaseMovie, "Images/Mars/M98EAS.movie", kShuttleTractorBeamMovieOrder, + kShuttleWindowLeft, kShuttleWindowTop, true); + _canyonChaseMovie.setTime(_canyonChaseMovie.getDuration()); + _canyonChaseMovie.redrawMovieWorld(); +} + +void Mars::startUpFromSpaceChase() { + setNextHandler(_vm); + throwAwayInterface(); + + // Open the spot sounds movie again... + _spotSounds.initFromQuickTime(getSoundSpotsName()); + _spotSounds.setVolume(_vm->getSoundFXLevel());; + + initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, + kShuttle1Top, true); + initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, + kShuttle2Top, true); + initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, + kShuttle3Top, true); + initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, + kShuttle4Top, true); + + initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", + kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); + + initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", + kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); + + initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); + + initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleLowerRightLeft, kShuttleLowerRightTop, false); + + initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", + kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); + + initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); + + initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, + kShuttleUpperRightLeft, kShuttleUpperRightTop, false); + + initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); + + initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", + kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); + + _centerShuttleMovie.show(); + + _shuttleEnergyMeter.initShuttleEnergyMeter(); + _shuttleEnergyMeter.setEnergyValue(kFullShuttleEnergy); + + _leftShuttleMovie.show(); + _leftShuttleMovie.setTime(kShuttleLeftNormalTime); + _leftShuttleMovie.redrawMovieWorld(); + + _leftDamageShuttleMovie.show(); + _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getDuration() - 40); + _leftDamageShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.show(); + + _lowerLeftShuttleMovie.show(); + + loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); + + initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder, + kPlanetStartLeft, kPlanetStartTop, true); + _planetMovie.setFlags(kLoopTimeBase); + + initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, + kShuttleJunkTop, false); + + initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); + _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + + _energyBeam.initShuttleWeapon(); + _gravitonCannon.initShuttleWeapon(); + + _upperLeftShuttleMovie.show(); + + _robotShip.initRobotShip(); + + _planetMovie.start(); + _planetMover.startMoving(&_planetMovie); + + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + _centerShuttleMovie.setTime(kShuttleCenterTargetSightedTime); + _centerShuttleMovie.redrawMovieWorld(); + + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTrackingTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + + _rightShuttleMovie.show(); + _rightShuttleMovie.setTime(kShuttleRightIntroStop - 1); + _rightShuttleMovie.redrawMovieWorld(); + + _rightDamageShuttleMovie.show(); + _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getDuration() - 40); + _rightDamageShuttleMovie.redrawMovieWorld(); + + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); + _lowerLeftShuttleMovie.redrawMovieWorld(); + + _robotShip.startMoving(); + + _shuttleHUD.initShuttleHUD(); + + _tractorBeam.startDisplaying(); + + _energyChoiceSpot.setArea(kShuttleEnergyBeamBounds); + _energyChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_energyChoiceSpot); + _gravitonChoiceSpot.setArea(kShuttleGravitonBounds); + _gravitonChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_gravitonChoiceSpot); + _tractorChoiceSpot.setArea(kShuttleTractorBounds); + _tractorChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_tractorChoiceSpot); + _shuttleViewSpot.setArea(kShuttleWindowLeft, kShuttleWindowTop, + kShuttleWindowLeft + kShuttleWindowWidth, kShuttleWindowTop + kShuttleWindowHeight); + _shuttleViewSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleViewSpot); + _shuttleTransportSpot.setArea(kShuttleTransportBounds); + _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleTransportSpot); + + _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + + startMarsTimer(kSpaceChaseTimeLimit, kOneTickPerSecond, kMarsSpaceChaseFinished); +} + +void Mars::setSoundFXLevel(const uint16 level) { + Neighborhood::setSoundFXLevel(level); + + if (_canyonChaseMovie.isMovieValid()) + _canyonChaseMovie.setVolume(level); + + if (_explosions.isMovieValid()) + _explosions.setVolume(level); +} + +void Mars::startMarsTimer(TimeValue time, TimeScale scale, MarsTimerCode code) { + _utilityFuse.primeFuse(time, scale); + _marsEvent.mars = this; + _marsEvent.event = code; + _utilityFuse.setFunctor(new Common::Functor0Mem<void, MarsTimerEvent>(&_marsEvent, &MarsTimerEvent::fire)); + _utilityFuse.lightFuse(); +} + +void Mars::marsTimerExpired(MarsTimerEvent &event) { + Common::Rect r; + uint16 x, y; + + switch (event.event) { + case kMarsLaunchTubeReached: + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftTubeTime); + _lowerLeftShuttleMovie.redrawMovieWorld(); + startMarsTimer(kCanyonChaseFinishedTime, kMovieTicksPerSecond, kMarsCanyonChaseFinished); + break; + case kMarsCanyonChaseFinished: + GameState.setScoringEnteredLaunchTube(); + + while (_canyonChaseMovie.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _canyonChaseMovie.stop(); + _canyonChaseMovie.stopDisplaying(); + _canyonChaseMovie.releaseMovie(); + + _vm->_gfx->enableErase(); + + loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); + + playSpotSoundSync(kShuttleConfiguringIn, kShuttleConfiguringOut); + playSpotSoundSync(kShuttleGeneratingIn, kShuttleGeneratingOut); + playSpotSoundSync(kShuttleBreakawayIn, kShuttleBreakawayOut); + playSpotSoundSync(kMarsAtmosphericBreakawayIn, kMarsAtmosphericBreakawayOut); + + initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder, kPlanetStartLeft, kPlanetStartTop, true); + _planetMovie.setFlags(kLoopTimeBase); + + initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, kShuttleJunkTop, false); + + initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); + _explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + + _energyBeam.initShuttleWeapon(); + _gravitonCannon.initShuttleWeapon(); + + _centerShuttleMovie.setTime(kShuttleCenterWeaponsTime); + _centerShuttleMovie.redrawMovieWorld(); + + _upperLeftShuttleMovie.show(); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDampingTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + _robotShip.initRobotShip(); + + _planetMovie.start(); + _planetMover.startMoving(&_planetMovie); + + playSpotSoundSync(kShuttleDamperDescIn, kShuttleDamperDescOut); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftGravitonTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleGravitonDescIn, kShuttleGravitonDescOut); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftTractorTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleTractorDescIn, kShuttleTractorDescOut); + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + + _centerShuttleMovie.setTime(kShuttleCenterTargetSightedTime); + _centerShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleTargetSightedIn, kShuttleTargetSightedOut); + + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTrackingTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + _rightShuttleMovie.show(); + playMovieSegment(&_rightShuttleMovie, kShuttleRightIntroStart, kShuttleRightIntroStop); + + _rightDamageShuttleMovie.show(); + playMovieSegment(&_rightDamageShuttleMovie); + + // Take it down a tick initially. This sets the time to the time of the last tick, + // so that subsequence drops will drop it down a tick. + _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getTime() - 40); + _rightDamageShuttleMovie.redrawMovieWorld(); + + _lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); + _lowerLeftShuttleMovie.redrawMovieWorld(); + playSpotSoundSync(kShuttleAutopilotEngagedIn, kShuttleAutopilotEngagedOut); + + _robotShip.startMoving(); + + _shuttleHUD.initShuttleHUD(); + + _tractorBeam.startDisplaying(); + + _energyChoiceSpot.setArea(kShuttleEnergyBeamBounds); + _energyChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_energyChoiceSpot); + _gravitonChoiceSpot.setArea(kShuttleGravitonBounds); + _gravitonChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_gravitonChoiceSpot); + _tractorChoiceSpot.setArea(kShuttleTractorBounds); + _tractorChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_tractorChoiceSpot); + _shuttleViewSpot.setArea(kShuttleWindowLeft, kShuttleWindowTop, + kShuttleWindowLeft + kShuttleWindowWidth, kShuttleWindowTop + kShuttleWindowHeight); + _shuttleViewSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleViewSpot); + _shuttleTransportSpot.setArea(kShuttleTransportBounds); + _shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); + _vm->getAllHotspots().push_back(&_shuttleTransportSpot); + + _privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + + playSpotSoundSync(kMarsCockpitChatterIn, kMarsCockpitChatterOut); + + GameState.setMarsFinishedCanyonChase(true); + + startMarsTimer(kSpaceChaseTimeLimit, kOneTickPerSecond, kMarsSpaceChaseFinished); + + _vm->_cursor->hideUntilMoved(); + break; + case kMarsSpaceChaseFinished: + // Player failed to stop the robot in time... + _interruptionFilter = kFilterNoInput; + + _rightShuttleMovie.setTime(kShuttleRightTargetLockTime); + _rightShuttleMovie.redrawMovieWorld(); + + _upperRightShuttleMovie.show(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightLockedTime); + _upperRightShuttleMovie.redrawMovieWorld(); + + _rightShuttleMovie.setTime(kShuttleRightGravitonTime); + _rightShuttleMovie.redrawMovieWorld(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightArmedTime); + _upperRightShuttleMovie.redrawMovieWorld(); + + _vm->delayShell(3, 1); + + x = _vm->getRandomNumber(19); + y = _vm->getRandomNumber(19); + + r = Common::Rect(kShuttleWindowMidH - x, kShuttleWindowMidV - y, + kShuttleWindowMidH - x + 20, kShuttleWindowMidV - y + 20); + showBigExplosion(r, kShuttleAlienShipOrder); + + while (_explosions.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + g_system->delayMillis(10); + } + + throwAwayMarsShuttle(); + reinstateMonocleInterface(); + recallToTSAFailure(); + break; + default: + break; + } + + _interruptionFilter = kFilterAllInput; +} + +void Mars::throwAwayMarsShuttle() { + _shuttleInterface1.deallocateSurface(); + _shuttleInterface1.stopDisplaying(); + _shuttleInterface2.deallocateSurface(); + _shuttleInterface2.stopDisplaying(); + _shuttleInterface3.deallocateSurface(); + _shuttleInterface3.stopDisplaying(); + _shuttleInterface4.deallocateSurface(); + _shuttleInterface4.stopDisplaying(); + + _spotSounds.disposeSound(); + + _canyonChaseMovie.releaseMovie(); + _canyonChaseMovie.stopDisplaying(); + _leftShuttleMovie.releaseMovie(); + _leftShuttleMovie.stopDisplaying(); + _rightShuttleMovie.releaseMovie(); + _rightShuttleMovie.stopDisplaying(); + _lowerLeftShuttleMovie.releaseMovie(); + _lowerLeftShuttleMovie.stopDisplaying(); + _lowerRightShuttleMovie.releaseMovie(); + _lowerRightShuttleMovie.stopDisplaying(); + _centerShuttleMovie.releaseMovie(); + _centerShuttleMovie.stopDisplaying(); + _upperLeftShuttleMovie.releaseMovie(); + _upperLeftShuttleMovie.stopDisplaying(); + _upperRightShuttleMovie.releaseMovie(); + _upperRightShuttleMovie.stopDisplaying(); + _leftDamageShuttleMovie.releaseMovie(); + _leftDamageShuttleMovie.stopDisplaying(); + _rightDamageShuttleMovie.releaseMovie(); + _rightDamageShuttleMovie.stopDisplaying(); + + _shuttleEnergyMeter.disposeShuttleEnergyMeter(); + _robotShip.cleanUpRobotShip(); + _shuttleHUD.cleanUpShuttleHUD(); + _tractorBeam.stopDisplaying(); + _junk.releaseMovie(); + _junk.stopDisplaying(); + _energyBeam.cleanUpShuttleWeapon(); + _gravitonCannon.cleanUpShuttleWeapon(); + _vm->getAllHotspots().remove(&_energyChoiceSpot); + _vm->getAllHotspots().remove(&_gravitonChoiceSpot); + _vm->getAllHotspots().remove(&_tractorChoiceSpot); + _vm->getAllHotspots().remove(&_shuttleViewSpot); + _vm->getAllHotspots().remove(&_shuttleTransportSpot); + _explosions.releaseMovie(); + _explosions.stopDisplaying(); + + loadLoopSound1(""); +} + +void Mars::transportToRobotShip() { + throwAwayMarsShuttle(); + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile("Images/Mars/M98EAE.movie")) + error("Could not load shuttle->interface transition video"); + + video->start(); + + while (!_vm->shouldQuit() && !video->endOfVideo()) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) + _vm->drawScaledFrame(frame, 0, 0); + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) + ; + + g_system->delayMillis(10); + } + + delete video; + + if (_vm->shouldQuit()) + return; + + reinstateMonocleInterface(); + + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + + arriveAt(kMarsRobotShuttle, kEast); + + _navMovie.stop(); + _navMovie.setTime(_navMovie.getStart()); + _navMovie.start(); +} + +const int kRobotTooStrong = 1; +const int kTractorTooWeak = 2; +const int kCapturedRobotShip = 3; + +void Mars::spaceChaseClick(const Input &input, const HotSpotID id) { + Common::Point pt; + + switch (id) { + case kShuttleEnergySpotID: + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftDampingTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + _leftShuttleMovie.setTime(kShuttleLeftDampingTime); + _leftShuttleMovie.redrawMovieWorld(); + _shuttleHUD.hide(); + _weaponSelection = kEnergyBeam; + playSpotSoundSync(kShuttleDampingBeamIn, kShuttleDampingBeamOut); + break; + case kShuttleGravitonSpotID: + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftGravitonTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + _leftShuttleMovie.setTime(kShuttleLeftGravitonTime); + _leftShuttleMovie.redrawMovieWorld(); + _shuttleHUD.hide(); + _weaponSelection = kGravitonCannon; + playSpotSoundSync(kShuttleGravitonIn, kShuttleGravitonOut); + break; + case kShuttleTractorSpotID: + _upperLeftShuttleMovie.setTime(kShuttleUpperLeftTractorTime); + _upperLeftShuttleMovie.redrawMovieWorld(); + _leftShuttleMovie.setTime(kShuttleLeftTractorTime); + _leftShuttleMovie.redrawMovieWorld(); + _shuttleHUD.show(); + _weaponSelection = kTractorBeam; + playSpotSoundSync(kShuttleTractorBeamIn, kShuttleTractorBeamOut); + break; + case kShuttleViewSpotID: + switch (_weaponSelection) { + case kEnergyBeam: + if (_shuttleEnergyMeter.getEnergyValue() < kMinDampingEnergy) { + playSpotSoundSync(kShuttleEnergyTooLowIn, kShuttleEnergyTooLowOut); + } else { + if (_energyBeam.canFireWeapon()) { + _shuttleEnergyMeter.dropEnergyValue(kMinDampingEnergy); + input.getInputLocation(pt); + _energyBeam.fireWeapon(pt.x, pt.y); + playSpotSoundSync(kMarsEDBBlastIn, kMarsEDBBlastOut); + } + } + break; + case kGravitonCannon: + if (_shuttleEnergyMeter.getEnergyValue() < kMinGravitonEnergy) { + playSpotSoundSync(kShuttleEnergyTooLowIn, kShuttleEnergyTooLowOut); + } else { + if (_gravitonCannon.canFireWeapon()) { + _shuttleEnergyMeter.dropEnergyValue(kMinGravitonEnergy); + input.getInputLocation(pt); + _gravitonCannon.fireWeapon(pt.x, pt.y); + playSpotSoundSync(kMarsGravitonBlastIn, kMarsGravitonBlastOut); + } + } + break; + case kTractorBeam: + if (_shuttleHUD.isTargetLocked()) { + // play tractor beam sound? + _utilityFuse.stopFuse(); + + _tractorBeam.show(); + + int capture; + if (_rightDamageShuttleMovie.getTime() > 40) { + capture = kRobotTooStrong; + } else if (!_shuttleEnergyMeter.enoughEnergyForTractorBeam()) { + capture = kTractorTooWeak; + } else { + _robotShip.snareByTractorBeam(); + capture = kCapturedRobotShip; + _planetMover.dropPlanetOutOfSight(); + } + + _shuttleEnergyMeter.drainForTractorBeam(); + + while (_shuttleEnergyMeter.isFading()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _shuttleEnergyMeter.setEnergyValue(_shuttleEnergyMeter.getEnergyValue()); + + switch (capture) { + case kRobotTooStrong: + _tractorBeam.hide(); + playSpotSoundSync(kShuttleBrokeFreeIn, kShuttleBrokeFreeOut); + _utilityFuse.lightFuse(); + break; + case kTractorTooWeak: + playSpotSoundSync(kShuttleCantHoldIn, kShuttleCantHoldOut); + _tractorBeam.hide(); + _utilityFuse.lightFuse(); + break; + case kCapturedRobotShip: + _tractorBeam.hide(); + _shuttleHUD.hide(); + _robotShip.cleanUpRobotShip(); + _planetMovie.stop(); + _planetMovie.stopDisplaying(); + _planetMovie.releaseMovie(); + + // Shameless reuse of a variable :P + initOneMovie(&_canyonChaseMovie, "Images/Mars/M98EAS.movie", kShuttleTractorBeamMovieOrder, + kShuttleWindowLeft, kShuttleWindowTop, true); + _canyonChaseMovie.redrawMovieWorld(); + playMovieSegment(&_canyonChaseMovie, 0, _canyonChaseMovie.getDuration()); + + // wait here until any junk clears... + while (_junk.junkFlying()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _upperRightShuttleMovie.show(); + _upperRightShuttleMovie.setTime(kShuttleUpperRightOverloadTime); + _upperRightShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleOverloadedIn, kShuttleOverloadedOut); + _centerShuttleMovie.setTime(kShuttleCenterVerifyingTime); + _centerShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleCoordinatesIn, kShuttleCoordinatesOut); + _centerShuttleMovie.setTime(kShuttleCenterScanningTime); + _centerShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleScanningIn, kShuttleScanningOut); + _centerShuttleMovie.setTime(kShuttleCenterSafeTime); + _centerShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kShuttleSafeIn, kShuttleSafeOut); + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + GameState.setMarsReadyForShuttleTransport(true); + break; + } + } else { + playSpotSoundSync(kShuttleTractorLimitedIn, kShuttleTractorLimitedOut); + } + break; + default: + break; + } + break; + case kShuttleTransportSpotID: + _lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportHiliteTime); + _lowerRightShuttleMovie.redrawMovieWorld(); + _neighborhoodNotification.setNotificationFlags(kTimeToTransportFlag, kTimeToTransportFlag); + break; + } +} + +void Mars::showBigExplosion(const Common::Rect &r, const DisplayOrder order) { + if (_explosions.isMovieValid()) { + _explosions.setDisplayOrder(order); + + Common::Rect r2 = r; + int dx = r.width() / 2; + int dy = r.height() / 2; + r2.left -= dx; + r2.right += dx; + r2.top -= dy; + r2.bottom += dy; + + _explosions.setBounds(r2); + _explosions.show(); + _explosions.stop(); + _explosions.setSegment(kBigExplosionStart, kBigExplosionStop); + _explosions.setTime(kBigExplosionStart); + _explosionCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _explosions.start(); + } +} + +void Mars::showLittleExplosion(const Common::Rect &r, const DisplayOrder order) { + if (_explosions.isMovieValid()) { + _explosions.setDisplayOrder(order); + + Common::Rect r2 = r; + int dx = r.width() / 2; + int dy = r.height() / 2; + r2.left -= dx; + r2.right += dx; + r2.top -= dy; + r2.bottom += dy; + _explosions.setBounds(r2); + + _explosions.show(); + _explosions.stop(); + _explosions.setSegment(kLittleExplosionStart, kLittleExplosionStop); + _explosions.setTime(kLittleExplosionStart); + _explosionCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _explosions.start(); + } +} + +void Mars::hitByJunk() { + _leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getTime() - 40); + _leftDamageShuttleMovie.redrawMovieWorld(); + + playSpotSoundSync(kMarsJunkCollisionIn, kMarsJunkCollisionOut); + + if (_leftDamageShuttleMovie.getTime() == 0) { + die(kDeathRanIntoSpaceJunk); + } else { + TimeValue t = _leftDamageShuttleMovie.getTime() / 40; + + if (t == 1) + playSpotSoundSync(kShuttleHullBreachIn, kShuttleHullBreachOut); + + t = _leftShuttleMovie.getTime(); + _leftShuttleMovie.setTime(kShuttleLeftDamagedTime); + _leftShuttleMovie.redrawMovieWorld(); + _vm->delayShell(1, 3); + _leftShuttleMovie.setTime(t); + _leftShuttleMovie.redrawMovieWorld(); + } +} + +void Mars::setUpNextDropTime() { + _robotShip.setUpNextDropTime(); +} + +void Mars::decreaseRobotShuttleEnergy(const int delta, Common::Point impactPoint) { + _rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getTime() - 40 * delta); + _rightDamageShuttleMovie.redrawMovieWorld(); + + if (_rightDamageShuttleMovie.getTime() == 0) { + Common::Rect r; + _robotShip.getShuttleBounds(r); + int size = MAX(r.width(), r.height()); + r = Common::Rect::center(impactPoint.x, impactPoint.y, size, size); + _robotShip.killRobotShip(); + showBigExplosion(r, kShuttleRobotShipOrder); + } else if (delta > 1) { + Common::Rect r; + _robotShip.getShuttleBounds(r); + int size = MIN(r.width(), r.height()); + r = Common::Rect::center(impactPoint.x, impactPoint.y, size, size); + showLittleExplosion(r, kShuttleWeaponBackOrder); + TimeValue t = _rightShuttleMovie.getTime(); + _rightShuttleMovie.setTime(kShuttleRightDamagedTime); + _rightShuttleMovie.redrawMovieWorld(); + _vm->delayShell(1, 3); + _rightShuttleMovie.setTime(t); + _rightShuttleMovie.redrawMovieWorld(); + } + + if (_rightDamageShuttleMovie.getTime() <= 40) { + GameState.setScoringStoppedRobotsShuttle(); + if (!GameState.getMarsHitRobotWithCannon()) + GameState.setScoringMarsGandhi(); + } +} + +void Mars::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) { + if (cursorSpot && cursorSpot->getObjectID() == kShuttleViewSpotID) { + if (_weaponSelection != kNoWeapon) + _vm->_cursor->setCurrentFrameIndex(6); + else + _vm->_cursor->setCurrentFrameIndex(0); + } else { + Neighborhood::updateCursor(cursorLocation, cursorSpot); + } +} + +AirQuality Mars::getAirQuality(const RoomID room) { + if ((room >= kMars36 && room <= kMars39) || (room >= kMarsMaze004 && room <= kMarsMaze200)) + return kAirQualityVacuum; + if (room == kMars35 && !GameState.getMarsAirlockOpen()) + return kAirQualityVacuum; + if (room == kMars60 && !GameState.getMarsAirlockOpen()) + return kAirQualityVacuum; + + return Neighborhood::getAirQuality(room); +} + +// Start up panting sound if necessary. + +void Mars::checkAirMask() { + Neighborhood::checkAirMask(); + + if (getAirQuality(GameState.getCurrentRoom()) == kAirQualityVacuum) { + if (g_airMask->isAirMaskOn()) { + if (_noAirFuse.isFuseLit()) { + _noAirFuse.stopFuse(); + loadLoopSound2(""); + loadAmbientLoops(); + playSpotSoundSync(kMarsOxyMaskOnIn, kMarsOxyMaskOnOut); + } + } else { + if (!_noAirFuse.isFuseLit()) { + loadLoopSound2("Sounds/Mars/SukWind1.22K.AIFF"); + _noAirFuse.primeFuse(kVacuumSurvivalTimeLimit); + _noAirFuse.lightFuse(); + } + } + } else { + if (_noAirFuse.isFuseLit()) { + _noAirFuse.stopFuse(); + loadLoopSound2(""); + loadAmbientLoops(); + } + } +} + +void Mars::airStageExpired() { + if (((PegasusEngine *)g_engine)->playerHasItemID(kAirMask)) + die(kDeathNoAirInMaze); + else + die(kDeathNoMaskInMaze); +} + +void Mars::lockThawed() { + startExtraSequence(kMars57ThawLock, kExtraCompletedFlag, kFilterNoInput); +} + +void Mars::setUpReactorLevel1() { + _reactorStage = 1; + makeColorSequence(); + _guessObject.initReactorGuess(); + _undoPict.initFromPICTResource(_vm->_resFork, kReactorUndoHilitePICTID); + _undoPict.setDisplayOrder(kMonitorLayer); + _undoPict.moveElementTo(kUndoHiliteLeft, kUndoHiliteTop); + _undoPict.startDisplaying(); + _guessHistory.initReactorHistory(); + _choiceHighlight.initReactorChoiceHighlight(); + setCurrentActivation(kActivateReactorInGame); + _bombFuse.primeFuse(kColorMatchingTimeLimit); + _bombFuse.setFunctor(new Common::Functor0Mem<void, Mars>(this, &Mars::bombExplodesInGame)); + _bombFuse.lightFuse(); +} + +void Mars::setUpNextReactorLevel() { + _guessObject.show(); + _guessHistory.show(); + _guessHistory.clearHistory(); + _choiceHighlight.show(); + _reactorStage++; + makeColorSequence(); +} + +void Mars::makeColorSequence() { + int32 code[5]; + int32 highest = _reactorStage + 2; + + for (int32 i = 0; i < highest; i++) + code[i] = i; + + _vm->shuffleArray(code, highest); + _currentGuess[0] = -1; + _currentGuess[1] = -1; + _currentGuess[2] = -1; + _nextGuess = 0; + _guessObject.setGuess(-1, -1, -1); + _guessHistory.setAnswer(code[0], code[1], code[2]); +} + +void Mars::doUndoOneGuess() { + if (_nextGuess > 0) { + _undoPict.show(); + _vm->delayShell(1, 2); + _undoPict.hide(); + _nextGuess--; + _currentGuess[_nextGuess] = -1; + _guessObject.setGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); + _choiceHighlight.resetHighlight(); + + if (_currentGuess[0] != -1) { + _choiceHighlight.highlightChoice(_currentGuess[0]); + + if (_currentGuess[1] != -1) { + _choiceHighlight.highlightChoice(_currentGuess[1]); + + if (_currentGuess[2] != -1) + _choiceHighlight.highlightChoice(_currentGuess[2]); + } + } + } +} + +void Mars::doReactorGuess(int32 guess) { + _choiceHighlight.highlightChoice(guess); + _currentGuess[_nextGuess] = guess; + _guessObject.setGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); + + switch (guess) { + case 0: + playSpotSoundSync(kColorMatchRedIn, kColorMatchRedOut); + break; + case 1: + playSpotSoundSync(kColorMatchYellowIn, kColorMatchYellowOut); + break; + case 2: + playSpotSoundSync(kColorMatchGreenIn, kColorMatchGreenOut); + break; + case 3: + playSpotSoundSync(kColorMatchBlueIn, kColorMatchBlueOut); + break; + case 4: + playSpotSoundSync(kColorMatchPurpleIn, kColorMatchPurpleOut); + break; + } + + _nextGuess++; + + if (_nextGuess == 3) { + _vm->delayShell(1, 2); + _nextGuess = 0; + _guessHistory.addGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); + + switch (_guessHistory.getCurrentNumCorrect()) { + case 0: + playSpotSoundSync(kColorMatchZeroNodesIn, kColorMatchZeroNodesOut); + break; + case 1: + playSpotSoundSync(kColorMatchOneNodeIn, kColorMatchOneNodeOut); + break; + case 2: + playSpotSoundSync(kColorMatchTwoNodesIn, kColorMatchTwoNodesOut); + break; + case 3: + playSpotSoundSync(kColorMatchThreeNodesIn, kColorMatchThreeNodesOut); + break; + } + + _currentGuess[0] = -1; + _currentGuess[1] = -1; + _currentGuess[2] = -1; + _guessObject.setGuess(-1, -1, -1); + _choiceHighlight.resetHighlight(); + + if (_guessHistory.isSolved()) { + _guessHistory.showAnswer(); + _vm->delayShell(1, 2); + _guessObject.hide(); + _guessHistory.hide(); + _choiceHighlight.hide(); + + switch (_reactorStage) { + case 1: + startExtraSequence(kMars57GameLevel2, kExtraCompletedFlag, kFilterNoInput); + break; + case 2: + startExtraSequence(kMars57GameLevel3, kExtraCompletedFlag, kFilterNoInput); + break; + case 3: + _bombFuse.stopFuse(); + _guessObject.disposeReactorGuess(); + _undoPict.deallocateSurface(); + _guessHistory.disposeReactorHistory(); + _choiceHighlight.disposeReactorChoiceHighlight(); + GameState.setScoringDisarmedCardBomb(); + startExtraSequence(kMars57GameSolved, kExtraCompletedFlag, kFilterNoInput); + break; + } + } else if (_guessHistory.getNumGuesses() >= 5) { + _vm->delayShell(2, 1); + bombExplodesInGame(); + } + } +} + +void Mars::bombExplodesInGame() { + _guessObject.disposeReactorGuess(); + _undoPict.deallocateSurface(); + _guessHistory.disposeReactorHistory(); + _choiceHighlight.disposeReactorChoiceHighlight(); + startExtraSequence(kMars57BombExplodesInGame, kExtraCompletedFlag, kFilterNoInput); +} + +void Mars::didntFindBomb() { + die(kDeathDidntFindMarsBomb); +} + +Common::String Mars::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (!movieName.empty()) + return movieName; + + return "Images/AI/Mars/XM01"; +} + +Common::String Mars::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + RoomID room = GameState.getCurrentRoom(); + + if (room >= kMars0A && room <= kMars21) + return "Images/AI/Mars/XME1"; + else if (room >= kMars22 && room <= kMars31South) + return "Images/AI/Mars/XME2"; + else if (room >= kMars52 && room <= kMars58) + return "Images/AI/Mars/XMREACE"; + + return "Images/AI/Mars/XME3"; + } + + return movieName; +} + +uint Mars::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars27, kNorth): + case MakeRoomView(kMars28, kNorth): + case MakeRoomView(kMars49, kSouth): + numHints = 1; + break; + case MakeRoomView(kMars31, kSouth): + case MakeRoomView(kMars31South, kSouth): + if (!GameState.isTakenItemID(kMarsCard)) + numHints = 1; + break; + case MakeRoomView(kMars34, kNorth): + if (!GameState.isTakenItemID(kMarsCard)) + numHints = 2; + break; + case MakeRoomView(kMars34, kSouth): + case MakeRoomView(kMars45, kNorth): + if (!GameState.isTakenItemID(kCrowbar)) + numHints = 1; + break; + case MakeRoomView(kMars51, kEast): + if (GameState.isCurrentDoorOpen() && !GameState.getShieldOn()) { + if (GameState.isTakenItemID(kShieldBiochip)) + numHints = 1; + else + numHints = 2; + } + break; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + if (!GameState.getShieldOn()) { + if (GameState.isTakenItemID(kShieldBiochip)) + numHints = 1; + else + numHints = 2; + } + break; + case MakeRoomView(kMars56, kEast): + if (getCurrentActivation() == kActivateReactorReadyForNitrogen) { + if ((ExtraID)_lastExtra == kMars57LowerScreenClosed) + numHints = 3; + } else if (getCurrentActivation() == kActivateReactorPlatformOut) { + if (!GameState.getShieldOn()) { + if (GameState.isTakenItemID(kShieldBiochip)) + numHints = 1; + else + numHints = 2; + } + } + break; + } + } + + return numHints; +} + +Common::String Mars::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kMars27, kNorth): + case MakeRoomView(kMars28, kNorth): + return "Images/AI/Globals/XGLOB5C"; + case MakeRoomView(kMars31, kSouth): + case MakeRoomView(kMars31South, kSouth): + case MakeRoomView(kMars34, kSouth): + case MakeRoomView(kMars45, kNorth): + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kMars34, kNorth): + if (hintNum == 1) + return "Images/AI/Globals/XGLOB2C"; + + return "Images/AI/Globals/XGLOB3G"; + case MakeRoomView(kMars49, kSouth): + if (GameState.isTakenItemID(kAirMask)) + return "Images/AI/Globals/XGLOB3E"; + + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kMars51, kEast): + if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Mars/XM52NW"; + + if (hintNum == 1) + return "Images/AI/Globals/XGLOB2D"; + + return "Images/AI/Globals/XGLOB3F"; + case MakeRoomView(kMars52, kNorth): + case MakeRoomView(kMars52, kSouth): + case MakeRoomView(kMars52, kEast): + case MakeRoomView(kMars52, kWest): + case MakeRoomView(kMars54, kNorth): + case MakeRoomView(kMars54, kSouth): + case MakeRoomView(kMars54, kEast): + case MakeRoomView(kMars54, kWest): + case MakeRoomView(kMars56, kNorth): + case MakeRoomView(kMars56, kSouth): + case MakeRoomView(kMars56, kWest): + case MakeRoomView(kMars58, kNorth): + case MakeRoomView(kMars58, kSouth): + case MakeRoomView(kMars58, kEast): + case MakeRoomView(kMars58, kWest): + if (hintNum == 1) { + if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Mars/XM52NW"; + + return "Images/AI/Globals/XGLOB2D"; + } + + return "Images/AI/Globals/XGLOB3F"; + case MakeRoomView(kMars56, kEast): + if (getCurrentActivation() == kActivateReactorReadyForNitrogen) + return Common::String::format("Images/AI/Mars/XM57SD%d", hintNum); + + if (hintNum == 1) { + if (GameState.isTakenItemID(kShieldBiochip)) + return "Images/AI/Mars/XM52NW"; + + return "Images/AI/Globals/XGLOB2D"; + } + + return "Images/AI/Globals/XGLOB3F"; + } + } + + return movieName; +} + +bool Mars::inColorMatchingGame() { + return _guessObject.isDisplaying(); +} + +bool Mars::canSolve() { + return GameState.getCurrentRoomAndView() == MakeRoomView(kMars56, kEast) && (getCurrentActivation() == kActivateReactorReadyForNitrogen || + getCurrentActivation() == kActivateReactorReadyForCrowBar || inColorMatchingGame()); +} + +void Mars::doSolve() { + if (getCurrentActivation() == kActivateReactorReadyForNitrogen || getCurrentActivation() == kActivateReactorReadyForCrowBar) { + _utilityFuse.stopFuse(); + GameState.setMarsLockBroken(true); + GameState.setMarsLockFrozen(false); + startExtraLongSequence(kMars57OpenPanel, kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); + } else if (inColorMatchingGame()) { + _bombFuse.stopFuse(); + _guessObject.disposeReactorGuess(); + _undoPict.deallocateSurface(); + _guessHistory.disposeReactorHistory(); + _choiceHighlight.disposeReactorChoiceHighlight(); + startExtraSequence(kMars57GameSolved, kExtraCompletedFlag, kFilterNoInput); + } +} + +Common::String Mars::getSoundSpotsName() { + return "Sounds/Mars/Mars Spots"; +} + +Common::String Mars::getNavMovieName() { + return "Images/Mars/Mars.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/mars.h b/engines/pegasus/neighborhood/mars/mars.h new file mode 100644 index 0000000000..0859522890 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/mars.h @@ -0,0 +1,238 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_MARS_H +#define PEGASUS_NEIGHBORHOOD_MARS_MARS_H + +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/energybeam.h" +#include "pegasus/neighborhood/mars/gravitoncannon.h" +#include "pegasus/neighborhood/mars/planetmover.h" +#include "pegasus/neighborhood/mars/reactor.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" +#include "pegasus/neighborhood/mars/shuttlehud.h" +#include "pegasus/neighborhood/mars/spacejunk.h" +#include "pegasus/neighborhood/mars/tractorbeam.h" + +namespace Pegasus { + +class InventoryItem; +class Mars; + +enum MarsTimerCode { + kMarsLaunchTubeReached, + kMarsCanyonChaseFinished, + kMarsSpaceChaseFinished // Player ran out of time... +}; + +struct MarsTimerEvent { + Mars *mars; + MarsTimerCode event; + + void fire(); +}; + +enum ShuttleWeaponSelection { + kNoWeapon, + kEnergyBeam, + kGravitonCannon, + kTractorBeam +}; + +class Mars : public Neighborhood { +friend struct MarsTimerEvent; +public: + Mars(InputHandler *, PegasusEngine *); + virtual ~Mars(); + + void flushGameState(); + + virtual uint16 getDateResID() const; + + virtual AirQuality getAirQuality(const RoomID); + + void checkAirMask(); + + void showBigExplosion(const Common::Rect &, const DisplayOrder); + void showLittleExplosion(const Common::Rect &, const DisplayOrder); + void hitByJunk(); + void decreaseRobotShuttleEnergy(const int, Common::Point impactPoint); + void setUpNextDropTime(); + + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + + virtual void shieldOn(); + virtual void shieldOff(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + void setSoundFXLevel(const uint16); + + bool canSolve(); + void doSolve(); + + bool inColorMatchingGame(); + +protected: + enum { + kMarsPrivatePodStorageOpenFlag, + kMarsPrivatePodTurnLeftFlag, + kMarsPrivatePodTurnRightFlag, + kMarsPrivateRobotTiredOfWaitingFlag, + kMarsPrivatePlatformZoomedInFlag, + kMarsPrivateBombExposedFlag, + kMarsPrivateDraggingBombFlag, + kMarsPrivateInSpaceChaseFlag, + kMarsPrivateGotMapChipFlag, + kMarsPrivateGotOpticalChipFlag, + kMarsPrivateGotShieldChipFlag, + kNumMarsPrivateFlags + }; + + void init(); + void start(); + void setUpAIRules(); + void arriveAt(const RoomID, const DirectionConstant); + void takeItemFromRoom(Item *); + void dropItemIntoRoom(Item *, Hotspot *); + void activateHotspots(); + void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); + void clickInHotspot(const Input &, const Hotspot *); + InputBits getInputFilter(); + + TimeValue getViewTime(const RoomID, const DirectionConstant); + void getZoomEntry(const HotSpotID, ZoomTable::Entry &); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void openDoor(); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + void turnTo(const DirectionConstant); + void receiveNotification(Notification *, const NotificationFlags); + void doorOpened(); + void setUpReactorEnergyDrain(); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void lockThawed(); + void robotTiredOfWaiting(); + + void setUpReactorLevel1(); + void setUpNextReactorLevel(); + void makeColorSequence(); + void doUndoOneGuess(); + void doReactorGuess(int32 guess); + void bombExplodesInGame(); + void didntFindBomb(); + CanMoveForwardReason canMoveForward(ExitTable::Entry &); + void cantMoveThatWay(CanMoveForwardReason); + void moveForward(); + void bumpIntoWall(); + void turnLeft(); + void turnRight(); + void airStageExpired(); + void loadAmbientLoops(); + void checkAirlockDoors(); + void pickedUpItem(Item *item); + void cantOpenDoor(CanOpenDoorReason); + void launchMaze007Robot(); + void launchMaze015Robot(); + void launchMaze101Robot(); + void launchMaze104Robot(); + void launchMaze133Robot(); + void launchMaze136Robot(); + void launchMaze184Robot(); + void timerExpired(const uint32); + void spotCompleted(); + + void doCanyonChase(void); + void startMarsTimer(TimeValue, TimeScale, MarsTimerCode); + void marsTimerExpired(MarsTimerEvent &); + void throwAwayMarsShuttle(); + void startUpFromFinishedSpaceChase(); + void startUpFromSpaceChase(); + void transportToRobotShip(); + void spaceChaseClick(const Input &, const HotSpotID); + void updateCursor(const Common::Point, const Hotspot *); + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); + + InventoryItem *_attackingItem; + FuseFunction _bombFuse; + FuseFunction _noAirFuse; + FuseFunction _utilityFuse; + FlagsArray<byte, kNumMarsPrivateFlags> _privateFlags; + uint _reactorStage, _nextGuess; + int32 _currentGuess[3]; + ReactorGuess _guessObject; + Picture _undoPict; + ReactorHistory _guessHistory; + ReactorChoiceHighlight _choiceHighlight; + + Picture _shuttleInterface1; + Picture _shuttleInterface2; + Picture _shuttleInterface3; + Picture _shuttleInterface4; + Movie _canyonChaseMovie; + + MarsTimerEvent _marsEvent; + + Movie _leftShuttleMovie; + Movie _rightShuttleMovie; + Movie _lowerLeftShuttleMovie; + Movie _lowerRightShuttleMovie; + Movie _centerShuttleMovie; + Movie _upperLeftShuttleMovie; + Movie _upperRightShuttleMovie; + Movie _leftDamageShuttleMovie; + Movie _rightDamageShuttleMovie; + ShuttleEnergyMeter _shuttleEnergyMeter; + Movie _planetMovie; + PlanetMover _planetMover; + RobotShip _robotShip; + ShuttleHUD _shuttleHUD; + TractorBeam _tractorBeam; + SpaceJunk _junk; + EnergyBeam _energyBeam; + GravitonCannon _gravitonCannon; + Hotspot _energyChoiceSpot; + Hotspot _gravitonChoiceSpot; + Hotspot _tractorChoiceSpot; + Hotspot _shuttleViewSpot; + Hotspot _shuttleTransportSpot; + ShuttleWeaponSelection _weaponSelection; + ScalingMovie _explosions; + NotificationCallBack _explosionCallBack; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/planetmover.cpp b/engines/pegasus/neighborhood/mars/planetmover.cpp new file mode 100644 index 0000000000..a340120c12 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/planetmover.cpp @@ -0,0 +1,104 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/movie.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/hermite.h" +#include "pegasus/neighborhood/mars/planetmover.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" + +namespace Pegasus { + +static const TimeScale kRovingScale = kTractorBeamScale; +static const TimeValue kRovingTime = kTenSeconds * kRovingScale; +static const TimeValue kRovingSlop = kTwoSeconds * kRovingScale; + +static const CoordType kMaxVelocity = 20; + +PlanetMover::PlanetMover() { + setScale(kRovingScale); + _dropping = false; + _planetMovie = 0; +} + +void PlanetMover::startMoving(Movie *planetMovie) { + _planetMovie = planetMovie; + _p4 = kPlanetStartTop; + _r4 = ((PegasusEngine *)g_engine)->getRandomNumber(kMaxVelocity - 1); + if (_r4 + _p4 < kPlanetStopTop) + _r4 = kPlanetStopTop - _p4; + newDestination(); +} + +void PlanetMover::stopMoving() { + stop(); +} + +void PlanetMover::dropPlanetOutOfSight() { + stop(); + CoordType currentLoc = hermite(_p1, _p4, _r1, _r4, _lastTime, _duration); + CoordType currentV = dHermite(_p1, _p4, _r1, _r4, _lastTime, _duration); + _p1 = currentLoc; + _r1 = currentV; + _p4 = kPlanetStartTop; + _r4 = 0; + _duration = kTractorBeamTime - kTractorBeamScale; + _dropping = true; + setSegment(0, _duration); + setTime(0); + start(); +} + +void PlanetMover::newDestination() { + _p1 = _p4; + _r1 = _r4; + + _p4 = kPlanetStopTop + ((PegasusEngine *)g_engine)->getRandomNumber(kPlanetStartTop - kPlanetStopTop - 1); + _r4 = ((PegasusEngine *)g_engine)->getRandomNumber(kMaxVelocity - 1); + + if (_r4 + _p4 < kPlanetStopTop) + _r4 = kPlanetStopTop - _p4; + + stop(); + _duration = kRovingTime + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingSlop - 1); + setSegment(0, _duration); + setTime(0); + start(); +} + +void PlanetMover::timeChanged(const TimeValue) { + if (_planetMovie) { + _planetMovie->moveElementTo(kPlanetStartLeft, hermite(_p1, _p4, _r1, _r4, _lastTime, _duration)); + if (_lastTime == _duration) { + if (_dropping) + stop(); + else + newDestination(); + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/planetmover.h b/engines/pegasus/neighborhood/mars/planetmover.h new file mode 100644 index 0000000000..2c195387e8 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/planetmover.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_PLANETMOVER_H +#define PEGASUS_NEIGHBORHOOD_MARS_PLANETMOVER_H + +#include "pegasus/timers.h" + +namespace Pegasus { + +class Movie; + +class PlanetMover : IdlerTimeBase { +public: + PlanetMover(); + virtual ~PlanetMover() {} + + void startMoving(Movie *); + void stopMoving(); + + void dropPlanetOutOfSight(); + +protected: + void newDestination(); + virtual void timeChanged(const TimeValue); + + Movie *_planetMovie; + CoordType _p1, _p4, _r1, _r4; + TimeValue _duration; + bool _dropping; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/reactor.cpp b/engines/pegasus/neighborhood/mars/reactor.cpp new file mode 100644 index 0000000000..334fb98879 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/reactor.cpp @@ -0,0 +1,297 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * aint32 with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/reactor.h" + +namespace Pegasus { + +static const CoordType kCurrentGuessWidth = 121; +static const CoordType kCurrentGuessHeight = 23; + +static const CoordType kOneGuessWidth = 25; +static const CoordType kOneGuessHeight = 23; + +static const ResIDType kReactorChoicesPICTID = 905; + +static const CoordType kCurrentGuessLeft = kNavAreaLeft + 146; +static const CoordType kCurrentGuessTop = kNavAreaTop + 90; + +ReactorGuess::ReactorGuess(const DisplayElementID id) : DisplayElement(id) { + setBounds(kCurrentGuessLeft, kCurrentGuessTop, kCurrentGuessLeft + kCurrentGuessWidth, + kCurrentGuessTop + kCurrentGuessHeight); + setDisplayOrder(kMonitorLayer); + _currentGuess[0] = -1; + _currentGuess[1] = -1; + _currentGuess[2] = -1; +} + +void ReactorGuess::initReactorGuess() { + _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorChoicesPICTID); + startDisplaying(); + show(); +} + +void ReactorGuess::disposeReactorGuess() { + stopDisplaying(); + _colors.deallocateSurface(); +} + +void ReactorGuess::setGuess(int32 a, int32 b, int32 c) { + _currentGuess[0] = a; + _currentGuess[1] = b; + _currentGuess[2] = c; + triggerRedraw(); +} + +void ReactorGuess::draw(const Common::Rect &) { + if (_colors.isSurfaceValid()) { + Common::Rect r1(0, 0, kOneGuessWidth, kOneGuessHeight); + Common::Rect r2 = r1; + + for (int i = 0; i < 3; i++) { + if (_currentGuess[i] >= 0) { + r1.moveTo(kOneGuessWidth * _currentGuess[i], 0); + r2.moveTo(kCurrentGuessLeft + 48 * i, kCurrentGuessTop); + _colors.copyToCurrentPortTransparent(r1, r2); + } + } + } +} + +static const CoordType kReactorChoiceHiliteWidth = 166; +static const CoordType kReactorChoiceHiliteHeight = 26; + +static const CoordType kChoiceHiliteLefts[6] = { + 0, + 34, + 34 + 34, + 34 + 34 + 32, + 34 + 34 + 32 + 34, + 34 + 34 + 32 + 34 + 32 +}; + +static const ResIDType kReactorChoiceHilitePICTID = 901; + +static const CoordType kReactorChoiceHiliteLeft = kNavAreaLeft + 116; +static const CoordType kReactorChoiceHiliteTop = kNavAreaTop + 158; + +ReactorChoiceHighlight::ReactorChoiceHighlight(const DisplayElementID id) : DisplayElement(id) { + setBounds(kReactorChoiceHiliteLeft, kReactorChoiceHiliteTop, kReactorChoiceHiliteLeft + kReactorChoiceHiliteWidth, + kReactorChoiceHiliteTop + kReactorChoiceHiliteHeight); + setDisplayOrder(kMonitorLayer); +} + +void ReactorChoiceHighlight::initReactorChoiceHighlight() { + _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorChoiceHilitePICTID); + startDisplaying(); + show(); +} + +void ReactorChoiceHighlight::disposeReactorChoiceHighlight() { + stopDisplaying(); + _colors.deallocateSurface(); +} + +void ReactorChoiceHighlight::draw(const Common::Rect &) { + if (_colors.isSurfaceValid()) { + for (int i = 0; i < 5; ++i) { + if (_choices.getFlag(i)) { + Common::Rect r1(0, 0, kChoiceHiliteLefts[i + 1] - kChoiceHiliteLefts[i], kReactorChoiceHiliteHeight); + Common::Rect r2 = r1; + r1.moveTo(kChoiceHiliteLefts[i], 0); + r2.moveTo(kReactorChoiceHiliteLeft + kChoiceHiliteLefts[i], kReactorChoiceHiliteTop); + _colors.copyToCurrentPort(r1, r2); + } + } + } +} + +static const CoordType kReactorHistoryWidth = 128; +static const CoordType kReactorHistoryHeight = 168; + +static const CoordType kColorWidths[5] = { 24, 25, 25, 26, 27 }; +static const CoordType kColorHeights[5] = { 14, 15, 17, 17, 19}; + +static const CoordType kHistoryLefts[5][3] = { + { 302 + kNavAreaLeft, 329 + kNavAreaLeft, 357 + kNavAreaLeft }, + { 302 + kNavAreaLeft, 331 + kNavAreaLeft, 360 + kNavAreaLeft }, + { 303 + kNavAreaLeft, 333 + kNavAreaLeft, 363 + kNavAreaLeft }, + { 304 + kNavAreaLeft, 335 + kNavAreaLeft, 366 + kNavAreaLeft }, + { 305 + kNavAreaLeft, 337 + kNavAreaLeft, 369 + kNavAreaLeft } +}; + +static const CoordType kHistoryTops[5] = { + 39 + kNavAreaTop, + 61 + kNavAreaTop, + 84 + kNavAreaTop, + 110 + kNavAreaTop, + 137 + kNavAreaTop +}; + +static const CoordType kOneAnswerWidth = 35; +static const CoordType kOneAnswerHeight = 27; + +static const CoordType kDigitWidth = 16; +static const CoordType kDigitHeight = 12; + +static const CoordType kCorrectCountLefts[5] = { + 388 + kNavAreaLeft, + 392 + kNavAreaLeft, + 398 + kNavAreaLeft, + 402 + kNavAreaLeft, + 406 + kNavAreaLeft +}; + +static const CoordType kCorrectCountTops[5] = { + 40 + kNavAreaTop, + 62 + kNavAreaTop, + 86 + kNavAreaTop, + 112 + kNavAreaTop, + 140 + kNavAreaTop +}; + +static const ResIDType kReactorDigitsPICTID = 902; +static const ResIDType kReactorHistoryPICTID = 903; +static const ResIDType kReactorAnswerPICTID = 904; + +static const CoordType kReactorHistoryLeft = kNavAreaLeft + 302; +static const CoordType kReactorHistoryTop = kNavAreaTop + 39; + +static const CoordType kAnswerLeft = kNavAreaLeft + 304; +static const CoordType kAnswerTop = kNavAreaTop + 180; + +ReactorHistory::ReactorHistory(const DisplayElementID id) : DisplayElement(id) { + setBounds(kReactorHistoryLeft, kReactorHistoryTop, kReactorHistoryLeft + kReactorHistoryWidth, + kReactorHistoryTop + kReactorHistoryHeight); + setDisplayOrder(kMonitorLayer); + _numGuesses = 0; + _answer[0] = -1; + _answer[1] = -1; + _answer[2] = -1; + _showAnswer = false; +} + +void ReactorHistory::initReactorHistory() { + _colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorHistoryPICTID); + _digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorDigitsPICTID); + _answerColors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorAnswerPICTID); + startDisplaying(); + show(); +} + +void ReactorHistory::disposeReactorHistory() { + stopDisplaying(); + _colors.deallocateSurface(); +} + +void ReactorHistory::addGuess(int32 a, int32 b, int32 c) { + _history[_numGuesses][0] = a; + _history[_numGuesses][1] = b; + _history[_numGuesses][2] = c; + _numGuesses++; + triggerRedraw(); +} + +void ReactorHistory::clearHistory() { + _numGuesses = 0; + _showAnswer = false; + triggerRedraw(); +} + +void ReactorHistory::setAnswer(int32 a, int32 b, int32 c) { + _answer[0] = a; + _answer[1] = b; + _answer[2] = c; +} + +void ReactorHistory::showAnswer() { + _showAnswer = true; + triggerRedraw(); +} + +bool ReactorHistory::isSolved() { + for (int i = 0; i < _numGuesses; i++) + if (_history[i][0] == _answer[0] && _history[i][1] == _answer[1] && _history[i][2] == _answer[2]) + return true; + + return false; +} + +void ReactorHistory::draw(const Common::Rect &) { + static const CoordType kColorTops[5] = { + 0, + kColorHeights[0], + kColorHeights[0] + kColorHeights[1], + kColorHeights[0] + kColorHeights[1] + kColorHeights[2], + kColorHeights[0] + kColorHeights[1] + kColorHeights[2] + kColorHeights[3], + }; + + if (_colors.isSurfaceValid() && _digits.isSurfaceValid()) { + for (int i = 0; i < _numGuesses; ++i) { + Common::Rect r1(0, 0, kColorWidths[i], kColorHeights[i]); + Common::Rect r2 = r1; + Common::Rect r3(0, 0, kDigitWidth, kDigitHeight); + Common::Rect r4 = r3; + int correct = 0; + + for (int j = 0; j < 3; ++j) { + r1.moveTo(kColorWidths[i] * _history[i][j], kColorTops[i]); + r2.moveTo(kHistoryLefts[i][j], kHistoryTops[i]); + _colors.copyToCurrentPortTransparent(r1, r2); + + if (_history[i][j] == _answer[j]) + correct++; + } + + r3.moveTo(kDigitWidth * correct, 0); + r4.moveTo(kCorrectCountLefts[i], kCorrectCountTops[i]); + _digits.copyToCurrentPort(r3, r4); + } + + if (_showAnswer && _answerColors.isSurfaceValid()) { + Common::Rect r1(0, 0, kOneAnswerWidth, kOneAnswerHeight); + Common::Rect r2 = r1; + + for (int i = 0; i < 3; i++) { + r1.moveTo(kOneAnswerWidth * _answer[i], 0); + r2.moveTo(kAnswerLeft + 34 * i, kAnswerTop); + _answerColors.copyToCurrentPortTransparent(r1, r2); + } + } + } +} + +int32 ReactorHistory::getCurrentNumCorrect() { + int correct = 0; + + for (int i = 0; i < 3; i++) + if (_history[_numGuesses - 1][i] == _answer[i]) + correct++; + + return correct; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/reactor.h b/engines/pegasus/neighborhood/mars/reactor.h new file mode 100644 index 0000000000..86338f8266 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/reactor.h @@ -0,0 +1,108 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_REACTOR_H +#define PEGASUS_NEIGHBORHOOD_MARS_REACTOR_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class ReactorGuess : public DisplayElement { +public: + ReactorGuess(const DisplayElementID); + virtual ~ReactorGuess() {} + + void initReactorGuess(); + void disposeReactorGuess(); + + void setGuess(int32, int32, int32); + + void draw(const Common::Rect &); + +protected: + int32 _currentGuess[3]; + + Surface _colors; +}; + +class ReactorChoiceHighlight : public DisplayElement { +public: + ReactorChoiceHighlight(const DisplayElementID); + virtual ~ReactorChoiceHighlight() {} + + void initReactorChoiceHighlight(); + void disposeReactorChoiceHighlight(); + + void resetHighlight() { + _choices.clearAllFlags(); + triggerRedraw(); + } + + bool choiceHighlighted(uint32 whichChoice) { return _choices.getFlag(whichChoice); } + + void draw(const Common::Rect &); + + void highlightChoice(uint32 whichChoice) { + _choices.setFlag(whichChoice); + triggerRedraw(); + } + +protected: + Surface _colors; + FlagsArray<byte, 5> _choices; +}; + +class ReactorHistory : public DisplayElement { +public: + ReactorHistory(const DisplayElementID); + virtual ~ReactorHistory() {} + + void initReactorHistory(); + void disposeReactorHistory(); + + void draw(const Common::Rect &); + + void addGuess(int32, int32, int32); + int32 getNumGuesses() { return _numGuesses; } + void clearHistory(); + void setAnswer(int32, int32, int32); + void showAnswer(); + bool isSolved(); + int32 getCurrentNumCorrect(); + +protected: + Surface _colors, _digits, _answerColors; + int32 _answer[3]; + int32 _history[5][3]; + int32 _numGuesses; + bool _showAnswer; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/robotship.cpp b/engines/pegasus/neighborhood/mars/robotship.cpp new file mode 100644 index 0000000000..1f4bbc1779 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/robotship.cpp @@ -0,0 +1,267 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/hermite.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const TimeScale kRovingScale = kTractorBeamScale; +static const TimeValue kRovingTime = kSixSeconds * kRovingScale; +static const TimeValue kRovingSlop = kThreeSeconds * kRovingScale; + +static const int kNumSpriteColumns = 15; +static const int kNumSpriteRows = 16; + +static const CoordType kInitialLocationLeft = kShuttleWindowLeft - 50; +static const CoordType kInitialLocationTop = kShuttleWindowTop - 50; +static const CoordType kInitialLocationWidth = kShuttleWindowWidth + 100; +static const CoordType kInitialLocationHeight = kShuttleWindowHeight + 100; + +static const CoordType kVelocityVectorLength = 100; +static const CoordType kVelocityVectorSlop = 50; + +static const CoordType kRovingLeft = kShuttleWindowLeft + 20; +static const CoordType kRovingTop = kShuttleWindowTop + 20; +static const CoordType kRovingWidth = kShuttleWindowMidH - kRovingLeft; +static const CoordType kRovingHeight = kShuttleWindowMidV - kRovingTop; + +RobotShip *g_robotShip = 0; + +RobotShip::RobotShip() : _spritesMovie(kNoDisplayElement) { + g_robotShip = this; + _shipRange = Common::Rect(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, + kShuttleWindowTop + kShuttleWindowHeight); + setScale(kRovingScale); + _currentLocation.x = 0; + _currentLocation.y = 0; + _snaring = false; + _dropJunkFuse.setFunctor(new Common::Functor0Mem<void, RobotShip>(this, &RobotShip::timeToDropJunk)); + _duration = 0xFFFFFFFF; +} + +RobotShip::~RobotShip() { + g_robotShip = 0; +} + +void RobotShip::initRobotShip() { + _spritesMovie.initFromMovieFile("Images/Mars/Ship.movie", true); + _spritesMovie.setDisplayOrder(kShuttleRobotShipOrder); + _spritesMovie.moveElementTo(kPlanetStartLeft, kPlanetStartTop); + _spritesMovie.startDisplaying(); + _spritesMovie.show(); + + Common::Rect r; + _spritesMovie.getBounds(r); + _shipWidth = r.width(); + _shipHeight = r.height(); + _dead = false; +} + +void RobotShip::cleanUpRobotShip() { + _dropJunkFuse.stopFuse(); + _spritesMovie.stopDisplaying(); + _spritesMovie.releaseMovie(); +} + +void RobotShip::startMoving() { + if (((PegasusEngine *)g_engine)->getRandomBit()) { + _p4.x = kInitialLocationLeft + ((PegasusEngine *)g_engine)->getRandomNumber(kInitialLocationWidth - 1); + if (((PegasusEngine *)g_engine)->getRandomBit()) + _p4.y = kInitialLocationTop; + else + _p4.y = kInitialLocationTop + kInitialLocationHeight; + } else { + _p4.y = kInitialLocationTop + ((PegasusEngine *)g_engine)->getRandomNumber(kInitialLocationHeight - 1); + if (((PegasusEngine *)g_engine)->getRandomBit()) + _p4.x = kInitialLocationLeft; + else + _p4.x = kInitialLocationLeft + kInitialLocationWidth; + } + + makeVelocityVector(_p4.x, _p4.y, kShuttleWindowLeft + kShuttleWindowWidth / 2, + kShuttleWindowTop + kShuttleWindowHeight / 2, _r4); + newDestination(); + setUpNextDropTime(); +} + +void RobotShip::killRobotShip() { + cleanUpRobotShip(); + _dead = true; +} + +void RobotShip::setUpNextDropTime() { + if (!isSnared()) { + _dropJunkFuse.primeFuse(kJunkDropBaseTime + ((PegasusEngine *)g_engine)->getRandomNumber(kJunkDropSlopTime)); + _dropJunkFuse.lightFuse(); + } +} + +void RobotShip::timeToDropJunk() { + if (g_spaceJunk) { + CoordType x, y; + _spritesMovie.getCenter(x, y); + g_spaceJunk->launchJunk(((PegasusEngine *)g_engine)->getRandomNumber(24), x, y); + } +} + +void RobotShip::newDestination() { + _p1 = _p4; + _r1 = _r4; + + _p4.x = kRovingLeft + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingWidth - 1); + _p4.y = kRovingTop + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingHeight - 1); + + if (((PegasusEngine *)g_engine)->getRandomNumber(7) < 6) { + if (!sameSign(_p4.x - kShuttleWindowMidH, kShuttleWindowMidH - _p1.x)) { + if (sign(_p4.x - kShuttleWindowMidH) > 0) + _p4.x -= kRovingWidth; + else + _p4.x += kRovingWidth; + } + } + + if (((PegasusEngine *)g_engine)->getRandomNumber(7) < 6) { + if (!sameSign(_p4.y - kShuttleWindowMidV, kShuttleWindowMidV - _p1.y)) { + if (sign(_p4.y - kShuttleWindowMidV) > 0) + _p4.y -= kRovingHeight; + else + _p4.y += kRovingHeight; + } + } + + makeVelocityVector(_p4.x, _p4.y, kShuttleWindowLeft + kShuttleWindowWidth / 2, + kShuttleWindowTop + kShuttleWindowHeight / 2, _r4); + stop(); + _duration = kRovingTime + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingSlop - 1); + setSegment(0, _duration); + setTime(0); + start(); +} + +void RobotShip::moveRobotTo(CoordType x, CoordType y) { + _currentLocation.x = x; + _currentLocation.y = y; + + if (_spritesMovie.isMovieValid()) { + _spritesMovie.moveElementTo(x - (_shipWidth >> 1), y - (_shipHeight >> 1)); + + if (x < _shipRange.left) + x = 0; + else if (x > _shipRange.right - 1) + x = _shipRange.width() - 1; + else + x -= _shipRange.left; + + if (y < _shipRange.top) + y = 0; + else if (y > _shipRange.bottom - 1) + y = _shipRange.height() - 1; + else + y -= _shipRange.top; + + x = kNumSpriteColumns * x / _shipRange.width(); + y = kNumSpriteRows * y / _shipRange.height(); + + _spritesMovie.setTime(40 * (x + y * kNumSpriteColumns)); + _spritesMovie.redrawMovieWorld(); + } +} + +bool RobotShip::pointInShuttle(Common::Point &pt) { + Common::Rect r; + _spritesMovie.getBounds(r); + + int dx = r.width() / 4; + int dy = r.height() / 6; + + r.left += dx; + r.right -= dx; + r.top += dy; + r.bottom -= dy; + + return r.contains(pt); +} + +void RobotShip::hitByEnergyBeam(Common::Point impactPoint) { + ((Mars *)g_neighborhood)->decreaseRobotShuttleEnergy(1, impactPoint); + setGlowing(true); + ((PegasusEngine *)g_engine)->delayShell(1, 3); + setGlowing(false); +} + +void RobotShip::hitByGravitonCannon(Common::Point impactPoint) { + GameState.setMarsHitRobotWithCannon(true); + ((Mars *)g_neighborhood)->decreaseRobotShuttleEnergy(6, impactPoint); +} + +void RobotShip::snareByTractorBeam() { + _dropJunkFuse.stopFuse(); + stop(); + + Common::Point currentV; + dHermite(_p1, _p4, _r1, _r4, _lastTime, _duration, currentV); + + _p1 = _currentLocation; + _r1 = currentV; + _p4.x = kShuttleWindowMidH; + _p4.y = kShuttleWindowMidV; + _r4.x = 0; + _r4.y = 0; + _duration = kTractorBeamTime; + _snaring = true; + setSegment(0, _duration); + setTime(0); + start(); +} + +void RobotShip::timeChanged(const TimeValue) { + Common::Point newLocation; + hermite(_p1, _p4, _r1, _r4, _lastTime, _duration, newLocation); + moveRobotTo(newLocation.x, newLocation.y); + + if (_lastTime == _duration) { + if (_snaring) + stop(); + else + newDestination(); + } +} + +void RobotShip::makeVelocityVector(CoordType x1, CoordType y1, CoordType x2, CoordType y2, Common::Point &vector) { + CoordType length = ((PegasusEngine *)g_engine)->getRandomNumber(kVelocityVectorSlop - 1) + kVelocityVectorLength; + vector.x = x2 - x1; + vector.y = y2 - y1; + float oldLength = sqrt((float)(vector.x * vector.x + vector.y * vector.y)); + vector.x = (int)(vector.x * length / oldLength); + vector.y = (int)(vector.y * length / oldLength); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/robotship.h b/engines/pegasus/neighborhood/mars/robotship.h new file mode 100644 index 0000000000..04be3ea56e --- /dev/null +++ b/engines/pegasus/neighborhood/mars/robotship.h @@ -0,0 +1,84 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_ROBOTSHIP_H +#define PEGASUS_NEIGHBORHOOD_MARS_ROBOTSHIP_H + +#include "pegasus/movie.h" + +namespace Pegasus { + +static const CoordType kShuttleMovieWidth = 114; +static const CoordType kShuttleMovieHeight = 42; + +class RobotShip : IdlerTimeBase { +public: + RobotShip(); + virtual ~RobotShip(); + + void initRobotShip(); + void cleanUpRobotShip(); + + void startMoving(); + + void killRobotShip(); + + bool pointInShuttle(Common::Point&); + + void hitByEnergyBeam(Common::Point impactPoint); + void hitByGravitonCannon(Common::Point impactPoint); + + void getShuttleBounds(Common::Rect &r) { _spritesMovie.getBounds(r); } + + void setGlowing(const bool glowing) { _spritesMovie.setGlowing(glowing); } + + void snareByTractorBeam(); + bool isSnared() { return _snaring && getTime() == _duration; } + + bool isDead() { return _dead; } + + void setUpNextDropTime(); + +protected: + void newDestination(); + void moveRobotTo(CoordType, CoordType); + void timeToDropJunk(); + virtual void timeChanged(const TimeValue); + void makeVelocityVector(CoordType, CoordType, CoordType, CoordType, Common::Point &); + + GlowingMovie _spritesMovie; + Common::Rect _shipRange; + int _shipWidth, _shipHeight; + Common::Point _p1, _p4, _r1, _r4, _currentLocation; + FuseFunction _dropJunkFuse; + TimeValue _duration; + bool _snaring, _dead; +}; + +extern RobotShip *g_robotShip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp b/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp new file mode 100644 index 0000000000..cd08dbae6a --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" + +namespace Pegasus { + +ShuttleEnergyMeter::ShuttleEnergyMeter() : FaderAnimation(kNoDisplayElement) { + setBounds(kShuttleEnergyLeft, kShuttleEnergyTop, kShuttleEnergyLeft + kShuttleEnergyWidth, + kShuttleEnergyTop + kShuttleEnergyHeight); + setDisplayOrder(kShuttleStatusOrder); + setFaderValue(0); +} + +void ShuttleEnergyMeter::initShuttleEnergyMeter() { + _meterImage.getImageFromPICTFile("Images/Mars/Shuttle Energy.pict"); + _lowWarning.getImageFromPICTFile("Images/Mars/Shuttle Low Energy.pict"); + startDisplaying(); + show(); +} + +void ShuttleEnergyMeter::disposeShuttleEnergyMeter() { + stopFader(); + hide(); + stopDisplaying(); + _meterImage.deallocateSurface(); + _lowWarning.deallocateSurface(); +} + +void ShuttleEnergyMeter::draw(const Common::Rect &) { + int32 currentValue = getFaderValue(); + + Common::Rect r1, r2, bounds; + getBounds(bounds); + + if (currentValue < kLowShuttleEnergy) { + _lowWarning.getSurfaceBounds(r1); + r2 = r1; + r2.moveTo(bounds.left, bounds.top); + _lowWarning.copyToCurrentPort(r1, r2); + } + + _meterImage.getSurfaceBounds(r1); + r1.right = r1.left + r1.width() * currentValue / kFullShuttleEnergy; + r2 = r1; + r2.moveTo(bounds.left + 102, bounds.top + 6); + _meterImage.copyToCurrentPort(r1, r2); +} + +void ShuttleEnergyMeter::powerUpMeter() { + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(kThirtyTicksPerSecond, 0, 0, 45, kFullShuttleEnergy); + startFader(moveSpec); +} + +void ShuttleEnergyMeter::setEnergyValue(const int32 value) { + stopFader(); + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(kFifteenTicksPerSecond, value * 3, value, kFullShuttleEnergy * 3, kFullShuttleEnergy); + startFader(moveSpec); +} + +void ShuttleEnergyMeter::drainForTractorBeam() { + stopFader(); + TimeValue startTime = 0, stopTime; + int32 startValue = getFaderValue(), stopValue; + + if (startValue < kTractorBeamEnergy) { + stopTime = startValue * kTractorBeamTime / kTractorBeamEnergy; + stopValue = 0; + } else { + stopTime = kTractorBeamTime; + stopValue = startValue - kTractorBeamEnergy; + } + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(kTractorBeamScale, startTime, startValue, stopTime, stopValue); + startFader(moveSpec); +} + +int32 ShuttleEnergyMeter::getEnergyValue() const { + return getFaderValue(); +} + +void ShuttleEnergyMeter::dropEnergyValue(const int32 delta) { + setEnergyValue(getFaderValue() - delta); +} + +bool ShuttleEnergyMeter::enoughEnergyForTractorBeam() const { + return getEnergyValue() >= kTractorBeamEnergy; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttleenergymeter.h b/engines/pegasus/neighborhood/mars/shuttleenergymeter.h new file mode 100644 index 0000000000..51161e094e --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleenergymeter.h @@ -0,0 +1,73 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEENERGYMETER_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEENERGYMETER_H + +#include "pegasus/fader.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +static const int32 kFullShuttleEnergy = 100; +// Low is 20 percent +static const int32 kLowShuttleEnergy = kFullShuttleEnergy * 20 / 100; + +static const int32 kMinDampingEnergy = 15; +static const int32 kMinGravitonEnergy = 63; + +static const TimeScale kTractorBeamScale = kFifteenTicksPerSecond; +static const TimeValue kTractorBeamTime = kFiveSeconds * kTractorBeamScale; +static const int32 kTractorBeamEnergy = kLowShuttleEnergy; + +class ShuttleEnergyMeter : public FaderAnimation { +public: + ShuttleEnergyMeter(); + ~ShuttleEnergyMeter() {} + + void initShuttleEnergyMeter(); + void disposeShuttleEnergyMeter(); + + void powerUpMeter(); + + void setEnergyValue(const int32); + int32 getEnergyValue() const; + + void dropEnergyValue(const int32); + + void drainForTractorBeam(); + + bool enoughEnergyForTractorBeam() const; + + void draw(const Common::Rect &); + +protected: + Surface _meterImage; + Surface _lowWarning; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.cpp b/engines/pegasus/neighborhood/mars/shuttlehud.cpp new file mode 100644 index 0000000000..11e826278b --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttlehud.cpp @@ -0,0 +1,246 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttlehud.h" + +namespace Pegasus { + +static const CoordType kHUDTargetGridLeft = kShuttleWindowLeft + 16; +static const CoordType kHUDTargetGridTop = kShuttleWindowTop + 8; +static const CoordType kHUDTargetGridWidth = 328; +static const CoordType kHUDTargetGridHeight = 206; + +static const CoordType kHUDRS232Left = kHUDTargetGridLeft + 264; +static const CoordType kHUDRS232Top = kHUDTargetGridTop + 2; + +static const CoordType kHUDLockLeft = kShuttleWindowLeft + 101; +static const CoordType kHUDLockTop = kShuttleWindowTop + 49; +static const CoordType kHUDLockWidth = 145; +static const CoordType kHUDLockHeight = 124; + +static const CoordType kTractorLockWidth = 50; +static const CoordType kTractorLockHeight = 30; + +static const CoordType kTractorLockLeft = kShuttleWindowMidH - kTractorLockWidth / 2; +static const CoordType kTractorLockTop = kShuttleWindowMidV - kTractorLockHeight / 2; +static const CoordType kTractorLockRight = kTractorLockLeft + kTractorLockWidth; +static const CoordType kTractorLockBottom = kTractorLockTop + kTractorLockHeight; + +static const uint16 s_RS232Data[] = { + 0xF0E1, 0xCE70, + 0xF9E1, 0xEF78, + 0x4900, 0x2108, + 0x79C0, 0xE738, + 0x70E1, 0xC770, + 0x5821, 0x0140, + 0x4DE1, 0xEF78, + 0x45C1, 0xEE78 +}; + +static const uint16 s_lockData[] = { + 0xE007, 0xFE1F, 0xF8E0, 0x7000, + 0xE00F, 0xFF3F, 0xFCE0, 0xE000, + 0xE00E, 0x0738, 0x1CE1, 0xC000, + 0xE00E, 0x0738, 0x00FF, 0x8000, + 0xE00E, 0x0738, 0x00FF, 0x0000, + 0xE00E, 0x0738, 0x00E3, 0x8000, + 0xE00E, 0x0738, 0x1CE1, 0xC000, + 0xFFCF, 0xFF3F, 0xFCE0, 0xE000, + 0xFFC7, 0xFE1F, 0xF8E0, 0x7000 +}; + +#define drawHUDLockLine(x1, y1, x2, y2, penX, penY, color) \ + screen->drawThickLine((x1) + kHUDLockLeft, (y1) + kHUDLockTop, \ + (x2) + kHUDLockLeft, (y2) + kHUDLockTop, penX, penY, color) + +#define drawHUDLockArrows(offset, color) \ + drawHUDLockLine(63, 0 + (offset), 68, 5 + (offset), 1, 3, color); \ + drawHUDLockLine(71, 8 + (offset), 77, 14 + (offset), 1, 3, color); \ + drawHUDLockLine(78, 14 + (offset), 84, 8 + (offset), 1, 3, color); \ + drawHUDLockLine(87, 5 + (offset), 92, 0 + (offset), 1, 3, color); \ + drawHUDLockLine(63, 121 - (offset), 68, 116 - (offset), 1, 3, color); \ + drawHUDLockLine(71, 113 - (offset), 77, 107 - (offset), 1, 3, color); \ + drawHUDLockLine(78, 107 - (offset), 84, 113 - (offset), 1, 3, color); \ + drawHUDLockLine(87, 116 - (offset), 92, 121 - (offset), 1, 3, color); \ +\ + drawHUDLockLine(13 + (offset), 47, 18 + (offset), 52, 3, 1, color); \ + drawHUDLockLine(21 + (offset), 55, 27 + (offset), 61, 3, 1, color); \ + drawHUDLockLine(27 + (offset), 62, 21 + (offset), 68, 3, 1, color); \ + drawHUDLockLine(18 + (offset), 71, 13 + (offset), 76, 3, 1, color); \ + drawHUDLockLine(142 - (offset), 47, 137 - (offset), 52, 3, 1, color); \ + drawHUDLockLine(134 - (offset), 55, 128 - (offset), 61, 3, 1, color); \ + drawHUDLockLine(128 - (offset), 62, 134 - (offset), 68, 3, 1, color); \ + drawHUDLockLine(137 - (offset), 71, 142 - (offset), 76, 3, 1, color) + +ShuttleHUD::ShuttleHUD() : DisplayElement(kNoDisplayElement) { + _lightGreen = g_system->getScreenFormat().RGBToColor(0, 204, 0); + _gridDarkGreen = g_system->getScreenFormat().RGBToColor(0, 85, 0); + _lockDarkGreen1 = g_system->getScreenFormat().RGBToColor(0, 68, 0); + _lockDarkGreen2 = g_system->getScreenFormat().RGBToColor(0, 65, 0); + + _targetLocked = false; + setBounds(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, + kShuttleWindowTop + kShuttleWindowHeight); + setDisplayOrder(kShuttleHUDOrder); +} + +void ShuttleHUD::initShuttleHUD() { + startDisplaying(); + startIdling(); +} + +void ShuttleHUD::cleanUpShuttleHUD() { + stopIdling(); + stopDisplaying(); +} + +void ShuttleHUD::showTargetGrid() { + show(); +} + +void ShuttleHUD::hideTargetGrid() { + hide(); + unlockOnTarget(); +} + +void ShuttleHUD::useIdleTime() { + if (isVisible()) { + Common::Rect r; + g_robotShip->getShuttleBounds(r); + if (r.left < kTractorLockRight && r.right > kTractorLockLeft && r.top < kTractorLockBottom && r.bottom > kTractorLockTop) + lockOnTarget(); + else + unlockOnTarget(); + } +} + +void ShuttleHUD::lockOnTarget() { + if (!_targetLocked) { + _targetLocked = true; + triggerRedraw(); + } +} + +void ShuttleHUD::unlockOnTarget() { + if (_targetLocked) { + _targetLocked = false; + triggerRedraw(); + } +} + +void ShuttleHUD::draw(const Common::Rect &) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + for (int y = 0; y < 35; y++) { + Common::Rect r; + + if (y & 1) { + if (y == 17) { + r = Common::Rect(0, 0, 4, 2); + r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 12, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + + r = Common::Rect(0, 0, 6, 2); + r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _lightGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _lightGreen); + + r = Common::Rect(0, 0, 23, 2); + r.moveTo(kHUDTargetGridLeft + 12, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _lightGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 35, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _lightGreen); + } else if (y == 1 || y == 15 || y == 19 || y == 33) { + r = Common::Rect(0, 0, 4, 2); + r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 6, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + + r = Common::Rect(0, 0, 15, 2); + r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 23, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + } else { + r = Common::Rect(0, 0, 4, 2); + r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 6, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + + r = Common::Rect(0, 0, 10, 2); + r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 18, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + } + } else { + r = Common::Rect(0, 0, 2, 2); + r.moveTo(kHUDTargetGridLeft, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 2, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + + r = Common::Rect(0, 0, 4, 2); + r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 12, y * 6 + kHUDTargetGridTop); + screen->fillRect(r, _gridDarkGreen); + } + } + + drawOneBitImageOr(screen, s_RS232Data, 2, Common::Rect(kHUDRS232Left, kHUDRS232Top, + kHUDRS232Left + 29, kHUDRS232Top + 8), _gridDarkGreen); + + if (_targetLocked) { + drawHUDLockArrows(0, _lockDarkGreen2); + drawHUDLockArrows(12, _lockDarkGreen1); + drawHUDLockArrows(24, _lightGreen); + drawOneBitImageOr(screen, s_lockData, 4, Common::Rect(kHUDLockLeft, kHUDLockTop + 115, + kHUDLockLeft + 52, kHUDLockTop + 115 + 9), _lightGreen); + } +} + +void ShuttleHUD::drawOneBitImageOr(Graphics::Surface *screen, const uint16 *data, int pitch, const Common::Rect &bounds, uint32 color) { + for (int y = 0; y < bounds.height(); y++) { + for (int x = 0; x < bounds.width(); x++) { + if ((data[y * pitch + x / 16] & (1 << (15 - (x % 16)))) != 0) { + if (screen->format.bytesPerPixel == 2) + WRITE_UINT16((byte *)screen->getBasePtr(x + bounds.left, y + bounds.top), color); + else + WRITE_UINT32((byte *)screen->getBasePtr(x + bounds.left, y + bounds.top), color); + } + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.h b/engines/pegasus/neighborhood/mars/shuttlehud.h new file mode 100644 index 0000000000..f7dbbaeae8 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttlehud.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEHUD_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEHUD_H + +#include "pegasus/elements.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class ShuttleHUD : public DisplayElement, public Idler { +public: + ShuttleHUD(); + + void showTargetGrid(); + void hideTargetGrid(); + + void initShuttleHUD(); + void cleanUpShuttleHUD(); + + bool isTargetLocked() { return _targetLocked; } + + void draw(const Common::Rect &); + +protected: + void useIdleTime(); + void lockOnTarget(); + void unlockOnTarget(); + void drawOneBitImageOr(Graphics::Surface *, const uint16 *, int, const Common::Rect &, uint32); + + bool _targetLocked; + uint32 _lightGreen, _gridDarkGreen, _lockDarkGreen1, _lockDarkGreen2; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttleweapon.cpp b/engines/pegasus/neighborhood/mars/shuttleweapon.cpp new file mode 100644 index 0000000000..b4c360b280 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleweapon.cpp @@ -0,0 +1,129 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttleweapon.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +ShuttleWeapon::ShuttleWeapon() : IdlerAnimation(kNoDisplayElement) { + setScale(kShuttleWeaponScale); + _weaponDuration = kShuttleWeaponScale * 2; + setSegment(0, _weaponDuration); + setBounds(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, + kShuttleWindowTop + kShuttleWindowHeight); + setDisplayOrder(kShuttleWeaponFrontOrder); +} + +void ShuttleWeapon::initShuttleWeapon() { + startDisplaying(); +} + +void ShuttleWeapon::cleanUpShuttleWeapon() { + stop(); + hide(); + stopDisplaying(); +} + +bool ShuttleWeapon::canFireWeapon() { + return !isRunning(); +} + +void ShuttleWeapon::fireWeapon(const CoordType hStop, const CoordType vStop) { + if (!isRunning()) { + stop(); + setTime(0); + show(); + + Common::Point pt2D(hStop, vStop); + project2DTo3D(pt2D, kShuttleDistance, _weaponTarget); + _weaponTime = 0; + setDisplayOrder(kShuttleWeaponFrontOrder); + start(); + } +} + +void ShuttleWeapon::updateWeaponPosition() { + _weaponTime = (float)_lastTime / _weaponDuration; + linearInterp(_weaponOrigin, _weaponTarget, _weaponTime, _weaponLocation); + + if (_weaponTime == 1.0) { + stop(); + hide(); + } else { + triggerRedraw(); + } +} + +void ShuttleWeapon::timeChanged(const TimeValue) { + updateWeaponPosition(); + + bool hit = false; + Common::Point impactPoint; + + if (g_spaceJunk->isJunkFlying()) { + hit = collisionWithJunk(impactPoint); + if (hit) { + stop(); + hide(); + hitJunk(impactPoint); + } + } + + if (!hit && _weaponTime == 1.0 && collisionWithShuttle(impactPoint)) + hitShuttle(impactPoint); +} + +bool ShuttleWeapon::collisionWithJunk(Common::Point &impactPoint) { + if (getDisplayOrder() == kShuttleWeaponFrontOrder) { + Point3D junkPosition; + g_spaceJunk->getJunkPosition(junkPosition); + + if (junkPosition.z < _weaponLocation.z) { + setDisplayOrder(kShuttleWeaponBackOrder); + project3DTo2D(_weaponLocation, impactPoint); + return g_spaceJunk->pointInJunk(impactPoint); + } + } + + return false; +} + +bool ShuttleWeapon::collisionWithShuttle(Common::Point &impactPoint) { + project3DTo2D(_weaponLocation, impactPoint); + return g_robotShip->pointInShuttle(impactPoint); +} + +void ShuttleWeapon::hitJunk(Common::Point impactPoint) { + g_spaceJunk->hitByEnergyBeam(impactPoint); +} + +void ShuttleWeapon::hitShuttle(Common::Point impactPoint) { + g_robotShip->hitByEnergyBeam(impactPoint); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttleweapon.h b/engines/pegasus/neighborhood/mars/shuttleweapon.h new file mode 100644 index 0000000000..38529c8919 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleweapon.h @@ -0,0 +1,68 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEWEAPON_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEWEAPON_H + +#include "pegasus/elements.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +// Can fire multiple times? +// For now, no... +// clone2727 adds: And now forever + +static const TimeScale kShuttleWeaponScale = kFifteenTicksPerSecond; + +class ShuttleWeapon : public IdlerAnimation { +public: + ShuttleWeapon(); + virtual ~ShuttleWeapon() {} + + virtual void initShuttleWeapon(); + virtual void cleanUpShuttleWeapon(); + + virtual void fireWeapon(const CoordType, const CoordType); + + bool canFireWeapon(); + +protected: + void timeChanged(const TimeValue); + virtual void updateWeaponPosition(); + virtual bool collisionWithJunk(Common::Point &impactPoint); + bool collisionWithShuttle(Common::Point &impactPoint); + virtual void hitJunk(Common::Point impactPoint); + virtual void hitShuttle(Common::Point impactPoint); + + Point3D _weaponOrigin, _weaponTarget; + Point3D _weaponLocation; + float _weaponTime; + TimeValue _weaponDuration; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/spacechase3d.cpp b/engines/pegasus/neighborhood/mars/spacechase3d.cpp new file mode 100644 index 0000000000..05f8233763 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacechase3d.cpp @@ -0,0 +1,106 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +void project3DTo2D(const Point3D &pt3D, Common::Point &pt2D) { + pt2D.x = (int)convertSpaceXToScreenH(pt3D.x, pt3D.z); + pt2D.y = (int)convertSpaceYToScreenV(pt3D.y, pt3D.z); +} + +void project2DTo3D(const Common::Point &pt2D, const float screenDistance, Point3D &pt3D) { + pt3D.x = convertScreenHToSpaceX(pt2D.x, screenDistance); + pt3D.y = convertScreenVToSpaceY(pt2D.y, screenDistance); + pt3D.z = screenDistance; +} + +void linearInterp(const Point3D &pt1, const Point3D &pt2, const float t, Point3D &pt3) { + pt3.x = pt1.x + (pt2.x - pt1.x) * t; + pt3.y = pt1.y + (pt2.y - pt1.y) * t; + pt3.z = pt1.z + (pt2.z - pt1.z) * t; +} + +void linearInterp(const Point3D &pt1, const float x2, const float y2, const float z2, const float t, Point3D &pt3) { + pt3.x = pt1.x + (x2 - pt1.x) * t; + pt3.y = pt1.y + (y2 - pt1.y) * t; + pt3.z = pt1.z + (z2 - pt1.z) * t; +} + +void linearInterp(const float x1, const float y1, const float z1, const Point3D &pt2, const float t, Point3D &pt3) { + pt3.x = x1 + (pt2.x - x1) * t; + pt3.y = y1 + (pt2.y - y1) * t; + pt3.z = z1 + (pt2.z - z1) * t; +} + +void linearInterp(const float x1, const float y1, const float z1, const float x2, const float y2, const float z2, + const float t, Point3D &pt3) { + pt3.x = x1 + (x2 - x1) * t; + pt3.y = y1 + (y2 - y1) * t; + pt3.z = z1 + (z2 - z1) * t; +} + +void linearInterp(const Common::Point &pt1, const Common::Point &pt2, const float t, Common::Point &pt3) { + pt3.x = (int)(pt1.x + (pt2.x - pt1.x) * t); + pt3.y = (int)(pt1.y + (pt2.y - pt1.y) * t); +} + +void linearInterp(const Common::Point &pt1, const float h2, const float v2, const float t, Common::Point &pt3) { + pt3.x = (int)(pt1.x + (h2 - pt1.x) * t); + pt3.y = (int)(pt1.y + (v2 - pt1.y) * t); +} + +void linearInterp(const float h1, const float v1, const Common::Point &pt2, const float t, Common::Point &pt3) { + pt3.x = (int)(h1 + (pt2.x - h1) * t); + pt3.y = (int)(v1 + (pt2.y - v1) * t); +} + +void linearInterp(const float h1, const float v1, const float h2, const float v2, const float t, Common::Point &pt3) { + pt3.x = (int)(h1 + (h2 - h1) * t); + pt3.y = (int)(v1 + (v2 - v1) * t); +} + +float linearInterp(const float arg1, const float arg2, const float t) { + return arg1 + (arg2 - arg1) * t; +} + +bool isNegative(int a) { + return a < 0; +} + +bool isPositive(int a) { + return a > 0; +} + +int sign(int a) { + return isNegative(a) ? -1 : isPositive(a) ? 1 : 0; +} + +bool sameSign(int a, int b) { + return sign(a) == sign(b); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/spacechase3d.h b/engines/pegasus/neighborhood/mars/spacechase3d.h new file mode 100644 index 0000000000..f6815e69bd --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacechase3d.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SPACECHASE3D_H +#define PEGASUS_NEIGHBORHOOD_MARS_SPACECHASE3D_H + +#include "pegasus/neighborhood/mars/constants.h" + +namespace Pegasus { + +// This is approximately right for a field of view of 72 degrees +// (Should be set to the tangent of FOV). +//static const float kTangentFOV = 0.76254; +static const float kTangentFOV = 1.0; + +// Define these as macros and they can be used to define constants... +#define convertSpaceXToScreenH(x, z) \ + ((x) / (z) * (kScreenWidth / (2 * kTangentFOV)) + kShuttleWindowMidH) + +#define convertSpaceYToScreenV(y, z) \ + (kShuttleWindowMidV - (y) / (z) * (kScreenWidth / (2 * kTangentFOV))) + +#define convertScreenHToSpaceX(x, d) \ + (((2.0 * kTangentFOV) / kScreenWidth) * ((float)(x) - kShuttleWindowMidH) * (d)) + +#define convertScreenVToSpaceY(y, d) \ + (((2.0 * kTangentFOV) / kScreenWidth) * ((float)kShuttleWindowMidV - (y)) * (d)) + +struct Point3D { + float x, y, z; + + Point3D() : x(0), y(0), z(0) {} + Point3D(float x1, float y1, float z1) : x(x1), y(y1), z(z1) {} + bool operator==(const Point3D &p) const { return x == p.x && y == p.y && z == p.z; } + bool operator!=(const Point3D &p) const { return x != p.x || y != p.y || z != p.z; } + + void translate(float dx, float dy, float dz) { + x += dx; + y += dy; + z += dz; + } +}; + +static const int kScreenWidth = kShuttleWindowWidth; + +bool isNegative(int a); +bool isPositive(int a); +int sign(int a); +bool sameSign(int a, int b); + +void project3DTo2D(const Point3D &pt3D, Common::Point &pt2D); +void project2DTo3D(const Common::Point &pt2D, const float screenDistance, Point3D &pt3D); + +void linearInterp(const Point3D &pt1, const Point3D &pt2, const float t, Point3D &pt3); +void linearInterp(const Point3D &pt1, const float x2, const float y2, const float z2, const float t, Point3D &pt3); +void linearInterp(const float x1, const float y1, const float z1, const Point3D &pt2, const float t, Point3D &pt3); +void linearInterp(const float x1, const float y1, const float z1, const float x2, + const float y2, const float z2, const float t, Point3D &pt3); + +void linearInterp(const Common::Point &pt1, const Common::Point &pt2, const float t, Common::Point &pt3); +void linearInterp(const Common::Point &pt1, const float h2, const float v2, const float t, Common::Point &pt3); +void linearInterp(const float h1, const float v1, const Common::Point &pt2, const float t, Common::Point &pt3); +void linearInterp(const float h1, const float v1, const float h2, const float v2, const float t, Common::Point &pt3); + +float linearInterp(const float arg1, const float arg2, const float t); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/spacejunk.cpp b/engines/pegasus/neighborhood/mars/spacejunk.cpp new file mode 100644 index 0000000000..3912e659c2 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacejunk.cpp @@ -0,0 +1,212 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const CoordType kMaxBounceSize = 90; +static const CoordType kBounceTargetHRange = 640 - kMaxBounceSize - 2; +static const CoordType kBounceTargetVRange = 480 - kMaxBounceSize - 2; + +static const float kJunkXTarget = 0; +static const float kJunkYTarget = 0; +static const float kJunkZTarget = kJunkMinDistance; + +SpaceJunk *g_spaceJunk = 0; + +SpaceJunk::SpaceJunk(const DisplayElementID id) : ScalingMovie(id) { + _timer.setScale(kJunkTimeScale); + _bouncing = false; + g_spaceJunk = this; +} + +SpaceJunk::~SpaceJunk() { + g_spaceJunk = 0; +} + +void SpaceJunk::launchJunk(int16 whichJunk, CoordType xOrigin, CoordType yOrigin) { + _bouncing = false; + TimeValue startTime = whichJunk * 16 * 40; + TimeValue stopTime = startTime + 16 * 40; + + _launchPoint = Point3D(convertScreenHToSpaceX(xOrigin, kJunkMaxDistance), + convertScreenVToSpaceY(yOrigin, kJunkMaxDistance), kJunkMaxDistance); + startIdling(); + stop(); + setFlags(0); + setSegment(startTime, stopTime); + setFlags(kLoopTimeBase); + setTime(startTime); + start(); + show(); + _timer.stop(); + _timer.setSegment(0, kJunkTravelTime); + _timer.setTime(0); + + // Force it to set up correctly from the get-go + useIdleTime(); + + _timer.start(); +} + +void SpaceJunk::setCenter(const CoordType centerX, const CoordType centerY) { + _center.x = centerX; + _center.y = centerY; + + Common::Rect r; + getBounds(r); + r.moveTo(CLIP<int>(centerX - (r.width() >> 1), 0, 640 - r.width()), CLIP<int>(centerY - (r.height() >> 1), 0, 480 - r.height())); + setBounds(r); +} + +void SpaceJunk::setScaleSize(const CoordType size) { + Common::Rect r; + r.left = _center.x - (size >> 1); + r.top = _center.y - (size >> 1); + r.right = r.left + size; + r.bottom = r.top + size; + setBounds(r); +} + +void SpaceJunk::useIdleTime() { + if (_bouncing) { + TimeValue time = _timer.getTime(); + Common::Point pt; + pt.x = linearInterp(0, _bounceTime, time, _bounceStart.x, _bounceStop.x); + pt.y = linearInterp(0, _bounceTime, time, _bounceStart.y, _bounceStop.y); + CoordType size = linearInterp(0, _bounceTime, time, _bounceSizeStart, _bounceSizeStop); + setCenter(pt.x, pt.y); + setScaleSize(size); + + if (time == _bounceTime) { + stop(); + stopIdling(); + hide(); + ((Mars *)g_neighborhood)->setUpNextDropTime(); + } + } else { + float t = (float)_timer.getTime() / kJunkTravelTime; + linearInterp(_launchPoint, kJunkXTarget, kJunkYTarget, kJunkZTarget, t, _junkPosition); + + Common::Point pt2D; + project3DTo2D(_junkPosition, pt2D); + setCenter(pt2D.x, pt2D.y); + setScaleSize((int)(convertSpaceYToScreenV(_junkPosition.y - kJunkSize / 2, _junkPosition.z) - + convertSpaceYToScreenV(_junkPosition.y + kJunkSize / 2, _junkPosition.z))); + + if (t == 1.0) { + rebound(kCollisionReboundTime); + ((Mars *)g_neighborhood)->hitByJunk(); + } + } +} + +bool SpaceJunk::pointInJunk(const Common::Point &pt) { + Common::Rect r; + getBounds(r); + + int dx = r.width() / 4; + int dy = r.height() / 4; + + r.left += dx; + r.right -= dx; + r.top += dy; + r.top -= dy; + + return r.contains(pt); +} + +void SpaceJunk::rebound(const TimeValue reboundTime) { + Common::Rect bounds; + getBounds(bounds); + + _bounceStart.x = (bounds.left + bounds.right) >> 1; + _bounceStart.y = (bounds.top + bounds.bottom) >> 1; + + PegasusEngine *vm = (PegasusEngine *)g_engine; + + switch (vm->getRandomNumber(3)) { + case 0: + _bounceStop.x = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetHRange - 1); + _bounceStop.y = kMaxBounceSize / 2 + 1; + break; + case 1: + _bounceStop.x = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetHRange - 1); + _bounceStop.y = 480 - kMaxBounceSize / 2 + 1; + break; + case 2: + _bounceStop.x = kMaxBounceSize / 2 + 1; + _bounceStop.y = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetVRange - 1); + break; + case 3: + _bounceStop.x = 640 - kMaxBounceSize / 2 + 1; + _bounceStop.y = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetVRange - 1); + break; + } + + _bounceSizeStart = bounds.width(); + _bounceSizeStop = MIN(_bounceSizeStart, kMaxBounceSize); + + _timer.stop(); + _timer.setSegment(0, reboundTime); + _bounceTime = reboundTime; + _timer.setTime(0); + _timer.start(); + + _bouncing = true; +} + +void SpaceJunk::hitByEnergyBeam(Common::Point) { + rebound(kWeaponReboundTime); + setGlowing(true); + ((PegasusEngine *)g_engine)->delayShell(1, 3); + setGlowing(false); +} + +void SpaceJunk::hitByGravitonCannon(Common::Point impactPoint) { + stop(); + stopIdling(); + hide(); + + Common::Rect r; + getBounds(r); + r = Common::Rect::center(impactPoint.x, impactPoint.y, r.width(), r.height()); + + ((Mars *)g_neighborhood)->showBigExplosion(r, kShuttleJunkOrder); + ((Mars *)g_neighborhood)->setUpNextDropTime(); +} + +void SpaceJunk::getJunkPosition(Point3D &position) { + position = _junkPosition; +} + +bool SpaceJunk::isJunkFlying() { + return isIdling(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/spacejunk.h b/engines/pegasus/neighborhood/mars/spacejunk.h new file mode 100644 index 0000000000..2ec9192513 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacejunk.h @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SPACEJUNK_H +#define PEGASUS_NEIGHBORHOOD_MARS_SPACEJUNK_H + +#include "pegasus/movie.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +static const CoordType kJunkMaxScreenSize = 250; + +static const float kJunkSize = convertScreenVToSpaceY(kShuttleWindowMidV - kJunkMaxScreenSize / 2, kJunkMinDistance) - + convertScreenVToSpaceY(kShuttleWindowMidV + kJunkMaxScreenSize / 2, kJunkMinDistance); + +class SpaceJunk : public ScalingMovie, public Idler { +public: + SpaceJunk(const DisplayElementID); + virtual ~SpaceJunk(); + + void setCenter(const CoordType, const CoordType); + void setScaleSize(const CoordType); + + void useIdleTime(); + + void launchJunk(int16, CoordType, CoordType); + + void getJunkPosition(Point3D &); + bool isJunkFlying(); + + bool pointInJunk(const Common::Point &); + + void hitByEnergyBeam(Common::Point impactPoint); + void hitByGravitonCannon(Common::Point impactPoint); + + bool junkFlying() { return _timer.isRunning(); } + +protected: + void rebound(const TimeValue); + + TimeBase _timer; + Point3D _launchPoint, _junkPosition; + Common::Point _center; + bool _bouncing; + Common::Point _bounceStart, _bounceStop; + CoordType _bounceSizeStart, _bounceSizeStop; + TimeValue _bounceTime; +}; + +extern SpaceJunk *g_spaceJunk; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/tractorbeam.cpp b/engines/pegasus/neighborhood/mars/tractorbeam.cpp new file mode 100644 index 0000000000..d3f9c94328 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/tractorbeam.cpp @@ -0,0 +1,139 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/tractorbeam.h" + +namespace Pegasus { + +TractorBeam::TractorBeam() : DisplayElement(kNoDisplayElement) { + setBounds(kShuttleTractorLeft, kShuttleTractorTop, kShuttleTractorLeft + kShuttleTractorWidth, + kShuttleTractorTop + kShuttleTractorHeight); + setDisplayOrder(kShuttleTractorBeamOrder); + +} + +static const int kHalfWidth = kShuttleTractorWidth >> 1; +static const int kHalfHeight = kShuttleTractorHeight >> 1; + +static const int kW3Vert = kHalfHeight * kHalfHeight * kHalfHeight; +static const int kW3Div2Vert = kW3Vert >> 1; + +static const int kW3Horiz = kHalfWidth * kHalfWidth * kHalfWidth; +static const int kW3Div2Horiz = kW3Horiz >> 1; + +static const int kMaxLevel = 50; + +static const int kAVert = -2 * kMaxLevel; +static const int kBVert = 3 * kMaxLevel * kHalfHeight; + +#define READ_PIXEL(ptr) \ + if (screen->format.bytesPerPixel == 2) \ + color = READ_UINT16(ptr); \ + else \ + color = READ_UINT32(ptr); \ + screen->format.colorToRGB(color, r, g, b) + +#define WRITE_PIXEL(ptr) \ + color = screen->format.RGBToColor(r, g, b); \ + if (screen->format.bytesPerPixel == 2) \ + WRITE_UINT16(ptr, color); \ + else \ + WRITE_UINT32(ptr, color) + +#define DO_BLEND(ptr) \ + READ_PIXEL(ptr); \ + g += (((0xff - g) * blendHoriz) >> 8); \ + b += (((0xff - b) * blendHoriz) >> 8); \ + WRITE_PIXEL(ptr) + +void TractorBeam::draw(const Common::Rect &) { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + // Set up vertical DDA. + int blendVert = 0; + int dVert = 0; + int d1Vert = kAVert + kBVert; + int d2Vert = 6 * kAVert + 2 * kBVert; + int d3Vert = 6 * kAVert; + + byte *rowPtrTop = (byte *)screen->getBasePtr(_bounds.left, _bounds.top); + byte *rowPtrBottom = (byte *)screen->getBasePtr(_bounds.left, _bounds.top + ((kHalfHeight << 1) - 1)); + + for (int y = kHalfHeight; y > 0; y--) { + // Set up horizontal DDA + int A = -2 * blendVert; + int B = 3 * blendVert * kHalfWidth; + int blendHoriz = 0; + int dHoriz = 0; + int d1Horiz = A + B; + int d2Horiz = 6 * A + 2 * B; + int d3Horiz = 6 * A; + + byte *pTopLeft = rowPtrTop; + byte *pTopRight = rowPtrTop + (kHalfWidth * 2 - 1) * screen->format.bytesPerPixel; + byte *pBottomLeft = rowPtrBottom; + byte *pBottomRight = rowPtrBottom + (kHalfWidth * 2 - 1) * screen->format.bytesPerPixel; + + for (int x = kHalfWidth; x > 0; x--) { + byte r, g, b; + uint32 color; + + DO_BLEND(pTopLeft); + DO_BLEND(pTopRight); + DO_BLEND(pBottomLeft); + DO_BLEND(pBottomRight); + + pTopLeft += screen->format.bytesPerPixel; + pBottomLeft += screen->format.bytesPerPixel; + pTopRight -= screen->format.bytesPerPixel; + pBottomRight -= screen->format.bytesPerPixel; + + while (dHoriz > kW3Div2Horiz) { + blendHoriz++; + dHoriz -= kW3Horiz; + } + + dHoriz += d1Horiz; + d1Horiz += d2Horiz; + d2Horiz += d3Horiz; + } + + rowPtrTop += screen->pitch; + rowPtrBottom -= screen->pitch; + + while (dVert > kW3Div2Vert) { + blendVert++; + dVert -= kW3Vert; + } + + dVert += d1Vert; + d1Vert += d2Vert; + d2Vert += d3Vert; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/tractorbeam.h b/engines/pegasus/neighborhood/mars/tractorbeam.h new file mode 100644 index 0000000000..cd87992d11 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/tractorbeam.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_TRACTORBEAM_H +#define PEGASUS_NEIGHBORHOOD_MARS_TRACTORBEAM_H + +#include "pegasus/elements.h" + +namespace Pegasus { + +class TractorBeam : public DisplayElement { +public: + TractorBeam(); + virtual ~TractorBeam() {} + + void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/neighborhood.cpp b/engines/pegasus/neighborhood/neighborhood.cpp new file mode 100644 index 0000000000..07be62c957 --- /dev/null +++ b/engines/pegasus/neighborhood/neighborhood.cpp @@ -0,0 +1,1774 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" + +#include "pegasus/compass.h" +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/graphics.h" +#include "pegasus/input.h" +#include "pegasus/interaction.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +StriderCallBack::StriderCallBack(Neighborhood *neighborhood) { + _neighborhood = neighborhood; +} + +void StriderCallBack::callBack() { + _neighborhood->checkStriding(); +} + +static const TimeValue kStridingSlop = 39; + +Neighborhood *g_neighborhood = 0; + +Neighborhood::Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) + : InputHandler(nextHandler), IDObject(id), _vm(vm), _resName(resName), _navMovie(kNavMovieID), _stridingCallBack(this), + _neighborhoodNotification(kNeighborhoodNotificationID, (NotificationManager *)vm), _pushIn(kNoDisplayElement), + _turnPush(kTurnPushID), _croppedMovie(kCroppedMovieID) { + GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); + _currentAlternate = 0; + _currentActivation = kActivateHotSpotAlways; + _interruptionFilter = kFilterAllInput; + allowInput(true); + resetLastExtra(); + g_neighborhood = this; + _currentInteraction = 0; + _doneWithInteraction = false; + _croppedMovie.setDisplayOrder(kCroppedMovieLayer); +} + +Neighborhood::~Neighborhood() { + for (HotspotIterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) + _vm->getAllHotspots().remove(*it); + + _neighborhoodHotspots.deleteHotspots(); + g_neighborhood = 0; + + loadLoopSound1(""); + loadLoopSound2(""); + newInteraction(kNoInteractionID); + + if (g_AIArea) + g_AIArea->removeAllRules(); +} + +void Neighborhood::init() { + _neighborhoodNotification.notifyMe(this, kNeighborhoodFlags, kNeighborhoodFlags); + _navMovieCallBack.setNotification(&_neighborhoodNotification); + _turnPushCallBack.setNotification(&_neighborhoodNotification); + _delayCallBack.setNotification(&_neighborhoodNotification); + _spotSoundCallBack.setNotification(&_neighborhoodNotification); + + debug(0, "Loading '%s' neighborhood resources", _resName.c_str()); + + Common::SeekableReadStream *stream = _vm->_resFork->getResource(_doorTable.getResTag(), _resName); + if (!stream) + error("Failed to load doors"); + _doorTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_exitTable.getResTag(), _resName); + if (!stream) + error("Failed to load exits"); + _exitTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_extraTable.getResTag(), _resName); + if (!stream) + error("Failed to load extras"); + _extraTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_hotspotInfoTable.getResTag(), _resName); + if (!stream) + error("Failed to load hotspot info"); + _hotspotInfoTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_spotTable.getResTag(), _resName); + if (!stream) + error("Failed to load spots"); + _spotTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_turnTable.getResTag(), _resName); + if (!stream) + error("Failed to load turns"); + _turnTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_viewTable.getResTag(), _resName); + if (!stream) + error("Failed to load views"); + _viewTable.loadFromStream(stream); + delete stream; + + stream = _vm->_resFork->getResource(_zoomTable.getResTag(), _resName); + if (!stream) + error("Failed to load zooms"); + _zoomTable.loadFromStream(stream); + delete stream; + + createNeighborhoodSpots(); + + _navMovie.initFromMovieFile(getNavMovieName()); + _navMovie.setVolume(_vm->getSoundFXLevel()); + + Common::String soundSpotsName = getSoundSpotsName(); + if (soundSpotsName.empty()) { + _spotSounds.disposeSound(); + } else { + _spotSounds.initFromQuickTime(getSoundSpotsName()); + _spotSounds.setVolume(_vm->getSoundFXLevel()); + } + + _navMovie.setDisplayOrder(kNavMovieOrder); + _navMovie.startDisplaying(); + + Common::Rect bounds; + _navMovie.getBounds(bounds); + _pushIn.allocateSurface(bounds); + + _turnPush.setInAndOutElements(&_pushIn, &_navMovie); + _turnPush.setDisplayOrder(kTurnPushOrder); + _turnPush.startDisplaying(); + _navMovieCallBack.initCallBack(&_navMovie, kCallBackAtExtremes); + _stridingCallBack.initCallBack(&_navMovie, kCallBackAtTime); + _turnPushCallBack.initCallBack(&_turnPush, kCallBackAtExtremes); + _delayCallBack.initCallBack(&_delayTimer, kCallBackAtExtremes); + _spotSoundCallBack.initCallBack(&_spotSounds, kCallBackAtExtremes); + + setUpAIRules(); + + if (g_compass) + g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + + _soundLoop1.attachFader(&_loop1Fader); + _soundLoop2.attachFader(&_loop2Fader); + startIdling(); +} + +void Neighborhood::start() { + GameState.setCurrentRoom(GameState.getLastRoom()); + GameState.setCurrentDirection(GameState.getLastDirection()); + arriveAt(GameState.getNextRoom(), GameState.getNextDirection()); +} + +void Neighborhood::receiveNotification(Notification *, const NotificationFlags flags) { + if ((flags & (kNeighborhoodMovieCompletedFlag | kTurnCompletedFlag)) != 0 && g_AIArea) + g_AIArea->unlockAI(); + if (flags & kMoveForwardCompletedFlag) + arriveAt(GameState.getNextRoom(), GameState.getNextDirection()); + if (flags & kTurnCompletedFlag) + turnTo(GameState.getNextDirection()); + if (flags & kSpotCompletedFlag) + spotCompleted(); + if (flags & kDoorOpenCompletedFlag) + doorOpened(); + if (flags & kActionRequestCompletedFlag) + popActionQueue(); + if (flags & kDeathExtraCompletedFlag) + die(_extraDeathReason); +} + +void Neighborhood::arriveAt(const RoomID room, const DirectionConstant direction) { + if (g_map) + g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), room, direction); + + GameState.setCurrentNeighborhood(getObjectID()); + + _currentActivation = kActivateHotSpotAlways; + _interruptionFilter = kFilterAllInput; + + if (room != GameState.getCurrentRoom() || direction != GameState.getCurrentDirection()) { + GameState.setCurrentRoom(room); + GameState.setCurrentDirection(direction); + loadAmbientLoops(); + activateCurrentView(room, direction, kSpotOnArrivalMask); + } else { + loadAmbientLoops(); + showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + } + + if (GameState.getOpenDoorRoom() != kNoRoomID) { + // Arriving always closes a door. + loadAmbientLoops(); + closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection()); + GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); + } + + if (g_compass) + g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + + if (g_AIArea) + g_AIArea->checkMiddleArea(); + + checkContinuePoint(room, direction); +} + +// These functions can be overridden to tweak the exact frames used. + +void Neighborhood::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) { + entry = _exitTable.findEntry(room, direction, _currentAlternate); + + if (entry.isEmpty()) + entry = _exitTable.findEntry(room, direction, kNoAlternateID); +} + +TimeValue Neighborhood::getViewTime(const RoomID room, const DirectionConstant direction) { + if (GameState.getOpenDoorRoom() == room && GameState.getOpenDoorDirection() == direction) { + // If we get here, the door entry for this location must exist. + DoorTable::Entry doorEntry = _doorTable.findEntry(room, direction, _currentAlternate); + + if (doorEntry.isEmpty()) + doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID); + + return doorEntry.movieEnd - 1; + } + + ViewTable::Entry viewEntry = _viewTable.findEntry(room, direction, _currentAlternate); + + if (viewEntry.isEmpty()) + viewEntry = _viewTable.findEntry(room, direction, kNoAlternateID); + + return viewEntry.time; +} + +void Neighborhood::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry) { + doorEntry = _doorTable.findEntry(room, direction, _currentAlternate); + + if (doorEntry.isEmpty()) + doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID); +} + +DirectionConstant Neighborhood::getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turnDirection) { + TurnTable::Entry turnEntry = _turnTable.findEntry(room, direction, turnDirection, _currentAlternate); + + if (turnEntry.isEmpty()) + turnEntry = _turnTable.findEntry(room, direction, turnDirection, kNoAlternateID); + + return turnEntry.endDirection; +} + +void Neighborhood::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { + spotEntry = _spotTable.findEntry(room, direction, flags, _currentAlternate); + + if (spotEntry.isEmpty()) + spotEntry = _spotTable.findEntry(room, direction, flags, kNoAlternateID); +} + +void Neighborhood::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { + zoomEntry = _zoomTable.findEntry(id); +} + +void Neighborhood::getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry) { + hotspotEntry = _hotspotInfoTable.findEntry(id); +} + +void Neighborhood::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { + extraEntry = _extraTable.findEntry(id); +} + +///////////////////////////////////////////// +// +// "Can" functions: Called to see whether or not a user is allowed to do something + +CanMoveForwardReason Neighborhood::canMoveForward(ExitTable::Entry &entry) { + DoorTable::Entry door; + + getExitEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry); + getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), door); + + // Fixed this so that doors that don't lead anywhere can be opened, but not walked + // through. + if (door.flags & kDoorPresentMask) { + if (GameState.isCurrentDoorOpen()) { + if (entry.exitRoom == kNoRoomID) + return kCantMoveBlocked; + else + return kCanMoveForward; + } else if (door.flags & kDoorLockedMask) { + return kCantMoveDoorLocked; + } else { + return kCantMoveDoorClosed; + } + } else if (entry.exitRoom == kNoRoomID) { + return kCantMoveBlocked; + } + + return kCanMoveForward; +} + +CanTurnReason Neighborhood::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) { + nextDir = getTurnEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), turnDirection); + + if (nextDir == kNoDirection) + return kCantTurnNoTurn; + + return kCanTurn; +} + +CanOpenDoorReason Neighborhood::canOpenDoor(DoorTable::Entry &entry) { + getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry); + + if (entry.flags & kDoorPresentMask) { + if (GameState.isCurrentDoorOpen()) + return kCantOpenAlreadyOpen; + + if (entry.flags & kDoorLockedMask) + return kCantOpenLocked; + + return kCanOpenDoor; + } + + return kCantOpenNoDoor; +} + +void Neighborhood::createNeighborhoodSpots() { + Common::SeekableReadStream *hotspotList = _vm->_resFork->getResource(MKTAG('H', 'S', 'L', 's'), _resName); + if (!hotspotList) + error("Could not load neighborhood hotspots"); + + uint32 hotspotCount = hotspotList->readUint32BE(); + + while (hotspotCount--) { + uint16 id = hotspotList->readUint16BE(); + uint32 flags = hotspotList->readUint32BE(); + uint32 rgnSize = hotspotList->readUint32BE(); + + int32 startPos = hotspotList->pos(); + + debug(0, "Hotspot %d:", id); + Region region(hotspotList); + + hotspotList->seek(startPos + rgnSize); + + Hotspot *hotspot = new Hotspot(id); + hotspot->setHotspotFlags(flags); + hotspot->setArea(region); + + _vm->getAllHotspots().push_back(hotspot); + _neighborhoodHotspots.push_back(hotspot); + } + + delete hotspotList; +} + +void Neighborhood::popActionQueue() { + if (!_actionQueue.empty()) { + QueueRequest topRequest = _actionQueue.pop(); + + switch (topRequest.requestType) { + case kNavExtraRequest: + _navMovie.stop(); + break; + case kSpotSoundRequest: + _spotSounds.stopSound(); + break; + case kDelayRequest: + _delayTimer.stop(); + break; + } + + serviceActionQueue(); + } +} + +void Neighborhood::serviceActionQueue() { + if (!_actionQueue.empty()) { + QueueRequest &topRequest = _actionQueue.front(); + + if (!topRequest.playing) { + topRequest.playing = true; + switch (topRequest.requestType) { + case kNavExtraRequest: + startExtraSequence(topRequest.extra, topRequest.flags, topRequest.interruptionFilter); + break; + case kSpotSoundRequest: + _spotSounds.stopSound(); + _spotSounds.playSoundSegment(topRequest.start, topRequest.stop); + _interruptionFilter = topRequest.interruptionFilter; + _spotSoundCallBack.setCallBackFlag(topRequest.flags); + _spotSoundCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + break; + case kDelayRequest: + _delayTimer.stop(); + _delayCallBack.setCallBackFlag(topRequest.flags); + _delayTimer.setSegment(0, topRequest.start, topRequest.stop); + _delayTimer.setTime(0); + _delayCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _interruptionFilter = topRequest.interruptionFilter; + _delayTimer.start(); + break; + } + } + } else { + _interruptionFilter = kFilterAllInput; + } +} + +void Neighborhood::requestAction(const QueueRequestType requestType, const ExtraID extra, const TimeValue in, const TimeValue out, + const InputBits interruptionFilter, const NotificationFlags flags) { + + QueueRequest request; + + request.requestType = requestType; + request.extra = extra; + request.start = in; + request.stop = out; + request.interruptionFilter = interruptionFilter; + request.playing = false; + request.flags = flags | kActionRequestCompletedFlag; + request.notification = &_neighborhoodNotification; + _actionQueue.push(request); + if (_actionQueue.size() == 1) + serviceActionQueue(); +} + +void Neighborhood::requestExtraSequence(const ExtraID whichExtra, const NotificationFlags flags, const InputBits interruptionFilter) { + requestAction(kNavExtraRequest, whichExtra, 0, 0, interruptionFilter, flags); +} + +void Neighborhood::requestSpotSound(const TimeValue in, const TimeValue out, const InputBits interruptionFilter, const NotificationFlags flags) { + requestAction(kSpotSoundRequest, 0xFFFFFFFF, in, out, interruptionFilter, flags); +} + +void Neighborhood::playSpotSoundSync(const TimeValue in, const TimeValue out) { + // Let the action queue play out first... + while (!actionQueueEmpty()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->checkNotifications(); + _vm->_system->delayMillis(10); + } + + _spotSounds.stopSound(); + _spotSounds.playSoundSegment(in, out); + + while (_spotSounds.isPlaying()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } +} + +void Neighborhood::requestDelay(const TimeValue delayDuration, const TimeScale delayScale, const InputBits interruptionFilter, const NotificationFlags flags) { + requestAction(kDelayRequest, 0xFFFFFFFF, delayDuration, delayScale, interruptionFilter, flags); +} + +bool operator==(const QueueRequest &arg1, const QueueRequest &arg2) { + return arg1.requestType == arg2.requestType && arg1.extra == arg2.extra && + arg1.start == arg2.start && arg1.stop == arg2.stop; +} + +bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2) { + return !operator==(arg1, arg2); +} + +Common::String Neighborhood::getBriefingMovie() { + if (_currentInteraction) + return _currentInteraction->getBriefingMovie(); + + return Common::String(); +} + +Common::String Neighborhood::getEnvScanMovie() { + if (_currentInteraction) + return _currentInteraction->getEnvScanMovie(); + + return Common::String(); +} + +uint Neighborhood::getNumHints() { + if (_currentInteraction) + return _currentInteraction->getNumHints(); + + return 0; +} + +Common::String Neighborhood::getHintMovie(uint hintNum) { + if (_currentInteraction) + return _currentInteraction->getHintMovie(hintNum); + + return Common::String(); +} + +bool Neighborhood::canSolve() { + if (_currentInteraction) + return _currentInteraction->canSolve(); + + return false; +} + +void Neighborhood::doSolve() { + if (_currentInteraction) + _currentInteraction->doSolve(); +} + +bool Neighborhood::okayToJump() { + return !_vm->playerHasItemID(kGasCanister) && !_vm->playerHasItemID(kMachineGun); +} + +AirQuality Neighborhood::getAirQuality(const RoomID) { + return kAirQualityGood; +} + +void Neighborhood::checkStriding() { + if (stillMoveForward()) { + ExitTable::Entry nextExit; + getExitEntry(GameState.getNextRoom(), GameState.getNextDirection(), nextExit); + keepStriding(nextExit); + } else { + stopStriding(); + } +} + +bool Neighborhood::stillMoveForward() { + Input input; + + InputHandler::readInputDevice(input); + return input.upButtonAnyDown(); +} + +void Neighborhood::keepStriding(ExitTable::Entry &nextExitEntry) { + FaderMoveSpec compassMove; + + if (g_map) + g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection()); + + if (g_compass) + getExitCompassMove(nextExitEntry, compassMove); + + GameState.setCurrentRoom(GameState.getNextRoom()); + GameState.setCurrentDirection(GameState.getNextDirection()); + GameState.setNextRoom(nextExitEntry.exitRoom); + GameState.setNextDirection(nextExitEntry.exitDirection); + + if (nextExitEntry.movieEnd == nextExitEntry.exitEnd) + scheduleNavCallBack(kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag); + else + scheduleStridingCallBack(nextExitEntry.movieEnd - kStridingSlop, kStrideCompletedFlag); + + if (g_compass) + g_compass->startFader(compassMove); +} + +void Neighborhood::stopStriding() { + _navMovie.stop(); + _neighborhoodNotification.setNotificationFlags(kNeighborhoodMovieCompletedFlag | + kMoveForwardCompletedFlag, kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag); +} + +// Compass support +int16 Neighborhood::getStaticCompassAngle(const RoomID, const DirectionConstant dir) { + // North, south, east, west + static const int16 compassAngles[] = { 0, 180, 90, 270 }; + return compassAngles[dir]; +} + +void Neighborhood::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + int32 startAngle = getStaticCompassAngle(exitEntry.room, exitEntry.direction); + int32 stopAngle = getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection); + + if (startAngle > stopAngle) { + if (stopAngle + 180 < startAngle) + stopAngle += 360; + } else { + if (startAngle + 180 < stopAngle) + startAngle += 360; + } + + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, startAngle, exitEntry.movieEnd, stopAngle); +} + +void Neighborhood::scheduleNavCallBack(NotificationFlags flags) { + _navMovieCallBack.cancelCallBack(); + + if (flags != 0) { + _navMovieCallBack.setCallBackFlag(flags); + _navMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } +} + +void Neighborhood::scheduleStridingCallBack(const TimeValue strideStop, NotificationFlags flags) { + _stridingCallBack.cancelCallBack(); + + if (flags != 0) + _stridingCallBack.scheduleCallBack(kTriggerTimeFwd, strideStop, _navMovie.getScale()); +} + +void Neighborhood::moveNavTo(const CoordType h, const CoordType v) { + CoordType oldH, oldV; + _navMovie.getLocation(oldH, oldV); + + CoordType offH = h - oldH; + CoordType offV = v - oldV; + + _navMovie.moveElementTo(h, v); + _turnPush.moveElementTo(h, v); + + if (offH != 0 || offV != 0) + for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) + if ((*it)->getHotspotFlags() & kNeighborhoodSpotFlag) + (*it)->moveSpot(offH, offV); +} + +void Neighborhood::activateHotspots() { + InputHandler::activateHotspots(); + + for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) { + HotspotInfoTable::Entry entry = *it; + + if (entry.hotspotRoom == GameState.getCurrentRoom() && entry.hotspotDirection == GameState.getCurrentDirection() + && (entry.hotspotActivation == _currentActivation || entry.hotspotActivation == kActivateHotSpotAlways)) { + Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(entry.hotspot); + if (hotspot) + activateOneHotspot(entry, hotspot); + } + } +} + +void Neighborhood::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + HotSpotFlags flags = clickedSpot->getHotspotFlags(); + + if ((flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) != 0) { + ItemID itemID = kNoItemID; + + for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) { + if (it->hotspot == clickedSpot->getObjectID()) { + itemID = it->hotspotItem; + break; + } + } + + if (itemID != kNoItemID) { + Item *draggingItem = _vm->getAllItems().findItemByID(itemID); + + if (draggingItem) { + takeItemFromRoom(draggingItem); + + if ((flags & kPickUpItemSpotFlag) != 0) + _vm->dragItem(input, draggingItem, kDragInventoryPickup); + else + _vm->dragItem(input, draggingItem, kDragBiochipPickup); + } + } + } else { + // Check other flags here? + if ((flags & kZoomSpotFlags) != 0) { + zoomTo(clickedSpot); + } else if ((flags & kPlayExtraSpotFlag) != 0) { + HotspotInfoTable::Entry hotspotEntry; + getHotspotEntry(clickedSpot->getObjectID(), hotspotEntry); + startExtraSequence(hotspotEntry.hotspotExtra, kExtraCompletedFlag, kFilterNoInput); + } else if ((flags & kOpenDoorSpotFlag) != 0) { + openDoor(); + } else { + InputHandler::clickInHotspot(input, clickedSpot); + } + } +} + +void Neighborhood::cantMoveThatWay(CanMoveForwardReason reason) { + switch (reason) { + case kCantMoveDoorClosed: + case kCantMoveDoorLocked: + openDoor(); + break; + case kCantMoveBlocked: + zoomUpOrBump(); + break; + default: + bumpIntoWall(); + break; + } +} + +void Neighborhood::cantOpenDoor(CanOpenDoorReason) { + bumpIntoWall(); +} + +void Neighborhood::turnTo(const DirectionConstant direction) { + if (g_map) + g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), direction); + + // clone2727 says: Is this necessary? + _vm->_gfx->setCurSurface(_navMovie.getSurface()); + _pushIn.copyToCurrentPort(); + _vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea()); + + // Added 2/10/97. Shouldn't this be here? Shouldn't we set the current activation to + // always when turning to a new view? + _currentActivation = kActivateHotSpotAlways; + + _interruptionFilter = kFilterAllInput; + + if (direction != GameState.getCurrentDirection()) { + GameState.setCurrentDirection(direction); + activateCurrentView(GameState.getCurrentRoom(), direction, kSpotOnTurnMask); + } else { + showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + } + + if (GameState.getOpenDoorRoom() != kNoRoomID) { + // Turning always closes a door. + loadAmbientLoops(); + closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection()); + GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); + } + + if (g_AIArea) + g_AIArea->checkMiddleArea(); + + checkContinuePoint(GameState.getCurrentRoom(), direction); + + _vm->_cursor->hideUntilMoved(); +} + +void Neighborhood::spotCompleted() { + _interruptionFilter = kFilterAllInput; + showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); +} + +void Neighborhood::doorOpened() { + _interruptionFilter = kFilterAllInput; + + // 2/23/97 + // Fixes funny bug with doors that are opened by dropping things on them... + setCurrentActivation(kActivateHotSpotAlways); + + GameState.setOpenDoorLocation(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + + SpotTable::Entry entry; + findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask, entry); + + if (entry.dstFlags & kSpotOnDoorOpenMask) { + startSpotOnceOnly(entry.movieStart, entry.movieEnd); + } else { + findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask | kSpotLoopsMask, entry); + + if (entry.dstFlags & kSpotOnDoorOpenMask) + startSpotLoop(entry.movieStart, entry.movieEnd); + } + + loadAmbientLoops(); + + if (g_map) + g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection()); + + if (g_AIArea) + g_AIArea->checkMiddleArea(); +} + +void Neighborhood::moveForward() { + ExitTable::Entry exitEntry; + CanMoveForwardReason moveReason = canMoveForward(exitEntry); + + if (moveReason == kCanMoveForward) + startExitMovie(exitEntry); + else + cantMoveThatWay(moveReason); +} + +void Neighborhood::turn(const TurnDirection turnDirection) { + DirectionConstant nextDir; + CanTurnReason turnReason = canTurn(turnDirection, nextDir); + + if (turnReason == kCanTurn) + startTurnPush(turnDirection, getViewTime(GameState.getCurrentRoom(), nextDir), nextDir); + else + cantTurnThatWay(turnReason); +} + +void Neighborhood::turnLeft() { + turn(kTurnLeft); +} + +void Neighborhood::turnRight() { + turn(kTurnRight); +} + +void Neighborhood::turnUp() { + turn(kTurnUp); +} + +void Neighborhood::turnDown() { + turn(kTurnDown); +} + +void Neighborhood::openDoor() { + DoorTable::Entry door; + CanOpenDoorReason doorReason = canOpenDoor(door); + + if (doorReason == kCanOpenDoor) + startDoorOpenMovie(door.movieStart, door.movieEnd); + else + cantOpenDoor(doorReason); +} + +void Neighborhood::zoomTo(const Hotspot *hotspot) { + ZoomTable::Entry zoomEntry; + getZoomEntry(hotspot->getObjectID(), zoomEntry); + if (!zoomEntry.isEmpty()) + startZoomMovie(zoomEntry); +} + +void Neighborhood::updateViewFrame() { + showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); +} + +void Neighborhood::startSpotLoop(TimeValue startTime, TimeValue stopTime, NotificationFlags flags) { + _turnPush.hide(); + startMovieSequence(startTime, stopTime, flags, true, kFilterAllInput); +} + +void Neighborhood::showViewFrame(TimeValue viewTime) { + if ((int32)viewTime >= 0) { + _turnPush.hide(); + _navMovie.stop(); + _navMovie.setFlags(0); + _navMovie.setSegment(0, _navMovie.getDuration()); + _navMovie.setTime(viewTime); + + Common::Rect pushBounds; + _turnPush.getBounds(pushBounds); + + _navMovie.moveElementTo(pushBounds.left, pushBounds.top); + _navMovie.show(); + _navMovie.redrawMovieWorld(); + } +} + +void Neighborhood::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + + if (entry.movieStart != 0xffffffff) + playExtraMovie(entry, flags, interruptionFilter); +} + +bool Neighborhood::startExtraSequenceSync(const ExtraID extraID, const InputBits interruptionFilter) { + InputDevice.waitInput(interruptionFilter); + return prepareExtraSync(extraID) && waitMovieFinish(&_navMovie, interruptionFilter); +} + +void Neighborhood::loopExtraSequence(const uint32 extraID, NotificationFlags flags) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + + if (entry.movieStart != 0xffffffff) { + _lastExtra = extraID; + startSpotLoop(entry.movieStart, entry.movieEnd, flags); + } +} + +bool Neighborhood::navMoviePlaying() { + return _navMovie.isRunning(); +} + +void Neighborhood::playDeathExtra(ExtraID extra, DeathReason deathReason) { + _extraDeathReason = deathReason; + startExtraSequence(extra, kDeathExtraCompletedFlag, kFilterNoInput); +} + +void Neighborhood::die(const DeathReason deathReason) { + loadLoopSound1(""); + loadLoopSound2(""); + _vm->die(deathReason); +} + +void Neighborhood::setSoundFXLevel(const uint16 fxLevel) { + if (_navMovie.isSurfaceValid()) + _navMovie.setVolume(fxLevel); + if (_spotSounds.isSoundLoaded()) + _spotSounds.setVolume(fxLevel); + if (_currentInteraction) + _currentInteraction->setSoundFXLevel(fxLevel); +} + +void Neighborhood::setAmbienceLevel(const uint16 ambientLevel) { + if (_soundLoop1.isSoundLoaded()) + _loop1Fader.setMasterVolume(_vm->getAmbienceLevel()); + if (_soundLoop2.isSoundLoaded()) + _loop2Fader.setMasterVolume(_vm->getAmbienceLevel()); + if (_currentInteraction) + _currentInteraction->setAmbienceLevel(ambientLevel); +} + +// Force the exit taken from (room, direction, alternate) to come to a stop. +void Neighborhood::forceStridingStop(const RoomID room, const DirectionConstant direction, const AlternateID alternate) { + ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate); + + if (entry.movieStart != 0xffffffff) { + TimeValue strideStop = entry.exitEnd; + TimeValue exitStop = entry.movieEnd; + + if (strideStop != exitStop) { + for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) { + entry = *it; + + if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) { + entry.exitEnd = exitStop; + *it = entry; + } + } + } + } +} + +// Restore the exit taken from (room, direction, alternate) to stride. +void Neighborhood::restoreStriding(const RoomID room, const DirectionConstant direction, const AlternateID alternate) { + ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate); + + if (entry.movieStart != 0xffffffff) { + TimeValue strideStop = entry.exitEnd; + TimeValue exitStop = entry.movieEnd; + + if (strideStop != entry.originalEnd) { + for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) { + entry = *it; + + if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) { + entry.exitEnd = entry.originalEnd; + *it = entry; + } + } + } + } +} + +HotspotInfoTable::Entry *Neighborhood::findHotspotEntry(const HotSpotID id) { + for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) + if (it->hotspot == id) + return &(*it); + + return 0; +} + +void Neighborhood::hideNav() { + _isRunning = _navMovie.isRunning(); + _navMovie.stop(); + _navMovie.hide(); + _turnPush.stopFader(); + _turnPush.hide(); +} + +void Neighborhood::showNav() { + _navMovie.show(); + _turnPush.hide(); + if (_isRunning) + _navMovie.start(); +} + +void Neighborhood::startExitMovie(const ExitTable::Entry &exitEntry) { + FaderMoveSpec compassMove; + + if (g_compass) + getExitCompassMove(exitEntry, compassMove); + + GameState.setNextRoom(exitEntry.exitRoom); + GameState.setNextDirection(exitEntry.exitDirection); + + if (exitEntry.movieEnd == exitEntry.exitEnd) // Just a walk. + startMovieSequence(exitEntry.movieStart, exitEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false); + else // We're stridin'! + startMovieSequence(exitEntry.movieStart, exitEntry.exitEnd, kStrideCompletedFlag, kFilterNoInput, false, exitEntry.movieEnd); + + if (g_compass) + g_compass->startFader(compassMove); +} + +void Neighborhood::startZoomMovie(const ZoomTable::Entry &zoomEntry) { + FaderMoveSpec compassMove; + + if (g_compass) + getZoomCompassMove(zoomEntry, compassMove); + + GameState.setNextRoom(zoomEntry.room); + GameState.setNextDirection(zoomEntry.direction); + + startMovieSequence(zoomEntry.movieStart, zoomEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false); + + if (g_compass) + g_compass->startFader(compassMove); +} + +void Neighborhood::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { + startMovieSequence(startTime, stopTime, kDoorOpenCompletedFlag, kFilterNoInput, false); +} + +void Neighborhood::startTurnPush(const TurnDirection turnDirection, const TimeValue newView, const DirectionConstant nextDir) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + _vm->_cursor->hide(); + + GameState.setNextDirection(nextDir); + + _interruptionFilter = kFilterNoInput; + _turnPush.stopFader(); + + // Set up callback. + _turnPushCallBack.setCallBackFlag(kTurnCompletedFlag); + _turnPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + // Stop nav movie. + _navMovie.stop(); + _navMovie.setFlags(0); + + // Set segment of nav movie to whole movie, so that subsequent initFromMovieFrame + // will work. + _navMovie.setSegment(0, _navMovie.getDuration()); + + _pushIn.initFromMovieFrame(_navMovie.getMovie(), newView); + + _navMovie.hide(); + + switch (turnDirection) { + case kTurnLeft: + _turnPush.setSlideDirection(kSlideRightMask); + break; + case kTurnRight: + _turnPush.setSlideDirection(kSlideLeftMask); + break; + case kTurnUp: + _turnPush.setSlideDirection(kSlideDownMask); + break; + case kTurnDown: + _turnPush.setSlideDirection(kSlideUpMask); + break; + } + + _turnPush.show(); + + FaderMoveSpec moveSpec; + moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000); + _turnPush.startFader(moveSpec); + + if (g_compass) { + _turnPush.pauseFader(); + + int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + int32 stopAngle = getStaticCompassAngle(GameState.getCurrentRoom(), nextDir); + + if (turnDirection == kTurnLeft) { + if (startAngle < stopAngle) + startAngle += 360; + } else { + if (stopAngle < startAngle) + stopAngle += 360; + } + + FaderMoveSpec turnSpec; + _turnPush.getCurrentFaderMove(turnSpec); + + FaderMoveSpec compassMove; + compassMove.makeTwoKnotFaderSpec(turnSpec.getFaderScale(), turnSpec.getNthKnotTime(0), startAngle, turnSpec.getNthKnotTime(1), stopAngle); + g_compass->startFader(compassMove); + } + + _turnPushCallBack.cancelCallBack(); + _turnPush.continueFader(); + + do { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } while (_turnPush.isFading()); + + _turnPush.stopFader(); + _neighborhoodNotification.setNotificationFlags(kTurnCompletedFlag, kTurnCompletedFlag); +} + +void Neighborhood::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) { + FaderMoveSpec compassMove; + + if (g_compass) + getExtraCompassMove(extraEntry, compassMove); + + _lastExtra = extraEntry.extra; + _turnPush.hide(); + startMovieSequence(extraEntry.movieStart, extraEntry.movieEnd, flags, false, interruptionInput); + + if (g_compass) + g_compass->startFader(compassMove); +} + +void Neighborhood::activateCurrentView(const RoomID room, const DirectionConstant direction, SpotFlags flag) { + SpotTable::Entry entry; + findSpotEntry(room, direction, flag, entry); + + if (entry.dstFlags & flag) { + startSpotOnceOnly(entry.movieStart, entry.movieEnd); + } else { + findSpotEntry(room, direction, flag | kSpotLoopsMask, entry); + + if (entry.dstFlags & flag) + startSpotLoop(entry.movieStart, entry.movieEnd); + else + showViewFrame(getViewTime(room, direction)); + } +} + +void Neighborhood::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { + switch (_vm->getDragType()) { + case kDragInventoryUse: + if ((hotspot->getHotspotFlags() & kDropItemSpotFlag) != 0 && + _vm->getDraggingItem()->getObjectID() == entry.hotspotItem) + hotspot->setActive(); + break; + case kDragInventoryPickup: + case kDragBiochipPickup: + // Do nothing -- neighborhoods activate no hot spots in this case... + break; + default: + if ((hotspot->getHotspotFlags() & kPickUpBiochipSpotFlag) != 0) { + Item *item = _vm->getAllItems().findItemByID(entry.hotspotItem); + if (item && item->getItemNeighborhood() == getObjectID()) + hotspot->setActive(); + } else { + HotSpotFlags flags = hotspot->getHotspotFlags(); + + if ((flags & kNeighborhoodSpotFlag) != 0) { + if (flags & kOpenDoorSpotFlag) { + if (!GameState.isCurrentDoorOpen()) + hotspot->setActive(); + } else if ((flags & (kZoomSpotFlags | kClickSpotFlag | kPlayExtraSpotFlag)) != 0) { + hotspot->setActive(); + } else if ((flags & kPickUpItemSpotFlag) != 0) { + // Changed this 2/19/96 + // Should only light up this hot spot if the item's taken flag is not + // set. It's not based on neighborhood ID since that can be reset by the + // destroying process. + + if (!GameState.isTakenItemID(entry.hotspotItem)) + hotspot->setActive(); + } + } + } + break; + } +} + +void Neighborhood::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) { + _turnPush.hide(); + startMovieSequence(startTime, stopTime, kSpotCompletedFlag, kFilterNoInput, false); +} + +void Neighborhood::startMovieSequence(const TimeValue startTime, const TimeValue stopTime, NotificationFlags flags, bool loopSequence, + const InputBits interruptionInput, const TimeValue strideStop) { + if (!loopSequence && g_AIArea) + g_AIArea->lockAIOut(); + + _interruptionFilter = interruptionInput; + + // Stop the movie before doing anything else + _navMovie.stop(); + + Common::Rect pushBounds; + _turnPush.getBounds(pushBounds); + + _navMovie.moveElementTo(pushBounds.left, pushBounds.top); + _navMovie.show(); + _navMovie.setFlags(0); + _navMovie.setSegment(startTime, stopTime); + _navMovie.setTime(startTime); + + if (loopSequence) + _navMovie.setFlags(kLoopTimeBase); + else + flags |= kNeighborhoodMovieCompletedFlag; + + if (strideStop != 0xffffffff) + // Subtract a little slop from the striding stop time to keep from "pumping" at the + // end of a walk. + // 40 is one frame (scale == 600, 15 fps). + scheduleStridingCallBack(strideStop - kStridingSlop, flags); + else + scheduleNavCallBack(flags); + + _navMovie.start(); +} + +void Neighborhood::throwAwayInterface() { + _doorTable.clear(); + _exitTable.clear(); + _extraTable.clear(); + _hotspotInfoTable.clear(); + _spotTable.clear(); + _turnTable.clear(); + _viewTable.clear(); + _zoomTable.clear(); + + _navMovie.stopDisplaying(); + _navMovie.releaseMovie(); + _pushIn.deallocateSurface(); + _turnPush.stopDisplaying(); + _turnPush.setInAndOutElements(0, 0); + _turnPush.disposeAllCallBacks(); + + for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) + _vm->getAllHotspots().remove(*it); + + _neighborhoodHotspots.deleteHotspots(); + _spotSounds.disposeSound(); + _delayTimer.disposeAllCallBacks(); + + if (g_AIArea) { + g_AIArea->saveAIState(); + g_AIArea->removeAllRules(); + } + + if (_currentInteraction) + newInteraction(kNoInteractionID); + + _croppedMovie.releaseMovie(); + + loadLoopSound1(""); + loadLoopSound2(""); + + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->saveCurrentEnergyValue(); + } + + delete g_interface; +} + +bool Neighborhood::prepareExtraSync(const ExtraID extraID) { + ExtraTable::Entry extraEntry; + FaderMoveSpec compassMove; + + if (g_compass) { + getExtraEntry(extraID, extraEntry); + getExtraCompassMove(extraEntry, compassMove); + } + + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + bool result; + + if (entry.movieStart != 0xffffffff) { + _turnPush.hide(); + + // Stop the movie before doing anything else + _navMovie.stop(); + + Common::Rect pushBounds; + _turnPush.getBounds(pushBounds); + _navMovie.moveElementTo(pushBounds.left, pushBounds.top); + + _navMovie.show(); + _navMovie.setFlags(0); + _navMovie.setSegment(entry.movieStart, entry.movieEnd); + _navMovie.setTime(entry.movieStart); + _navMovie.start(); + result = true; + } else { + result = false; + } + + if (result && g_compass) + g_compass->startFader(compassMove); + + return result; +} + +bool Neighborhood::waitMovieFinish(Movie *movie, const InputBits interruptionFilter) { + Input input; + bool result = true; + bool saveAllowed = _vm->swapSaveAllowed(false); + bool openAllowed = _vm->swapLoadAllowed(false); + + while (movie->isRunning()) { + InputDevice.getInput(input, interruptionFilter); + + if (input.anyInput() || _vm->shouldQuit()) { + result = false; + break; + } + + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + movie->stop(); + _vm->swapSaveAllowed(saveAllowed); + _vm->swapLoadAllowed(openAllowed); + + return result; +} + +InputBits Neighborhood::getInputFilter() { + return _interruptionFilter & InputHandler::getInputFilter(); +} + +void Neighborhood::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) { + int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + int32 stopAngle = getStaticCompassAngle(zoomEntry.room, zoomEntry.direction); + + if (startAngle > stopAngle) { + if (stopAngle + 180 < startAngle) + stopAngle += 360; + } else { + if (startAngle + 180 < stopAngle) + startAngle += 360; + } + + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), zoomEntry.movieStart, startAngle, zoomEntry.movieEnd, stopAngle); +} + +void Neighborhood::getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &compassMove) { + compassMove.makeOneKnotFaderSpec(g_compass->getFaderValue()); +} + +void Neighborhood::setUpAIRules() { + // Set up default rules here: + // -- Energy warning rules. + + if (g_AIArea) { + g_AIArea->forceAIUnlocked(); + + if (!_vm->isDemo() && (getObjectID() == kPrehistoricID || getObjectID() == kNoradAlphaID || + getObjectID() == kNoradDeltaID || getObjectID() == kMarsID || getObjectID() == kWSCID)) { + + AIEnergyMonitorCondition *condition50 = new AIEnergyMonitorCondition(kWorriedEnergy); + AIPlayMessageAction *message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4A", false); + AIRule *rule50 = new AIRule(condition50, message); + + AIEnergyMonitorCondition *condition25 = new AIEnergyMonitorCondition(kNervousEnergy); + AICompoundAction *compound = new AICompoundAction(); + message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4B", false); + compound->addAction(message); + AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule50); + compound->addAction(deactivate); + AIRule *rule25 = new AIRule(condition25, compound); + + AIEnergyMonitorCondition *condition5 = new AIEnergyMonitorCondition(kPanicStrickenEnergy); + compound = new AICompoundAction(); + message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4C", false); + compound->addAction(message); + deactivate = new AIDeactivateRuleAction(rule50); + compound->addAction(deactivate); + deactivate = new AIDeactivateRuleAction(rule25); + compound->addAction(deactivate); + AIRule *rule5 = new AIRule(condition5, compound); + + g_AIArea->addAIRule(rule5); + g_AIArea->addAIRule(rule25); + g_AIArea->addAIRule(rule50); + } + } +} + +GameInteraction *Neighborhood::makeInteraction(const InteractionID interactionID) { + if (interactionID == kNoInteractionID) + return 0; + + return new GameInteraction(interactionID, this); +} + +void Neighborhood::newInteraction(const InteractionID interactionID) { + GameInteraction *interaction = makeInteraction(interactionID); + _doneWithInteraction = false; + + if (_currentInteraction) { + _currentInteraction->stopInteraction(); + delete _currentInteraction; + } + + _currentInteraction = interaction; + + if (_currentInteraction) + _currentInteraction->startInteraction(); + + if (g_AIArea) + g_AIArea->checkMiddleArea(); +} + +void Neighborhood::bumpIntoWall() { + _vm->_gfx->shakeTheWorld(15, 30); +} + +void Neighborhood::zoomUpOrBump() { + Hotspot *zoomSpot = 0; + + for (HotspotList::iterator it = _vm->getAllHotspots().begin(); it != _vm->getAllHotspots().end(); it++) { + Hotspot *hotspot = *it; + + if ((hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomInSpotFlag)) == (kNeighborhoodSpotFlag | kZoomInSpotFlag)) { + HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID()); + + if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) { + if (zoomSpot) { + zoomSpot = 0; + break; + } else { + zoomSpot = hotspot; + } + } + } + } + + if (zoomSpot) + zoomTo(zoomSpot); + else + bumpIntoWall(); +} + +void Neighborhood::loadLoopSound1(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) { + FaderMoveSpec faderMove; + + if (!loop1Loaded(soundName)) { + _loop1SoundString = soundName; + + if (_soundLoop1.isSoundLoaded()) { + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeOut, 0); + _loop1Fader.startFaderSync(faderMove); + } + + if (!_loop1SoundString.empty()) { + _soundLoop1.initFromAIFFFile(_loop1SoundString); + _soundLoop1.loopSound(); + _loop1Fader.setMasterVolume(_vm->getAmbienceLevel()); + _loop1Fader.setFaderValue(0); + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume); + _loop1Fader.startFaderSync(faderMove); + } else { + _soundLoop1.disposeSound(); + } + } else if (_loop1Fader.getFaderValue() != volume) { + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeIn, volume); + _loop1Fader.startFaderSync(faderMove); + } +} + +void Neighborhood::loadLoopSound2(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) { + FaderMoveSpec faderMove; + + if (!loop2Loaded(soundName)) { + _loop2SoundString = soundName; + + if (_soundLoop2.isSoundLoaded()) { + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeOut, 0); + _loop2Fader.startFaderSync(faderMove); + } + + if (!_loop2SoundString.empty()) { + _soundLoop2.initFromAIFFFile(_loop2SoundString); + _soundLoop2.loopSound(); + _loop2Fader.setMasterVolume(_vm->getAmbienceLevel()); + _loop2Fader.setFaderValue(0); + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume); + _loop2Fader.startFaderSync(faderMove); + } else { + _soundLoop2.disposeSound(); + } + } else if (_loop2Fader.getFaderValue() != volume) { + faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeIn, volume); + _loop2Fader.startFaderSync(faderMove); + } +} + +void Neighborhood::takeItemFromRoom(Item *item) { + item->setItemRoom(kNoNeighborhoodID, kNoRoomID, kNoDirection); + // Also set the taken item flag. Do this before updating the view frame. + GameState.setTakenItem(item, true); + updateViewFrame(); +} + +void Neighborhood::dropItemIntoRoom(Item *item, Hotspot *) { + item->setItemRoom(getObjectID(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); + // Also set the taken item flag. Do this before updating the view frame. + GameState.setTakenItem(item, false); + updateViewFrame(); +} + +void Neighborhood::makeContinuePoint() { + _vm->makeContinuePoint(); +} + +void Neighborhood::startLoop1Fader(const FaderMoveSpec &faderMove) { + _loop1Fader.startFader(faderMove); +} + +void Neighborhood::startLoop2Fader(const FaderMoveSpec &faderMove) { + _loop2Fader.startFader(faderMove); +} + +// *** Revised 6/13/96 to use the last frame of the extra sequence. +// Necessary for Cinepak buildup. +void Neighborhood::showExtraView(uint32 extraID) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + + if (entry.movieEnd != 0xffffffff) + showViewFrame(entry.movieEnd - 1); +} + +void Neighborhood::startExtraLongSequence(const uint32 firstExtra, const uint32 lastExtra, NotificationFlags flags, + const InputBits interruptionFilter) { + ExtraTable::Entry firstEntry, lastEntry; + getExtraEntry(firstExtra, firstEntry); + + if (firstEntry.movieStart != 0xffffffff) { + getExtraEntry(lastExtra, lastEntry); + _lastExtra = firstExtra; + _turnPush.hide(); + startMovieSequence(firstEntry.movieStart, lastEntry.movieEnd, flags, kFilterNoInput, interruptionFilter); + } +} + +void Neighborhood::openCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) { + if (_croppedMovie.isMovieValid()) + closeCroppedMovie(); + + _croppedMovie.initFromMovieFile(movieName); + _croppedMovie.moveElementTo(left, top); + _croppedMovie.startDisplaying(); + _croppedMovie.show(); +} + +void Neighborhood::loopCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) { + openCroppedMovie(movieName, left, top); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.setFlags(kLoopTimeBase); + _croppedMovie.start(); +} + +void Neighborhood::closeCroppedMovie() { + _croppedMovie.releaseMovie(); +} + +void Neighborhood::playCroppedMovieOnce(const Common::String &movieName, CoordType left, CoordType top, const InputBits interruptionFilter) { + openCroppedMovie(movieName, left, top); + _croppedMovie.redrawMovieWorld(); + _croppedMovie.start(); + + InputBits oldInterruptionFilter = _interruptionFilter; + if (oldInterruptionFilter != kFilterNoInput) + _interruptionFilter = kFilterNoInput; + + bool saveAllowed = _vm->swapSaveAllowed(false); + bool openAllowed = _vm->swapLoadAllowed(false); + + Input input; + while (_croppedMovie.isRunning() && !_vm->shouldQuit()) { + _vm->processShell(); + InputDevice.getInput(input, interruptionFilter); + if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested() || _vm->shouldQuit()) + break; + _vm->_system->delayMillis(10); + } + + if (oldInterruptionFilter != kFilterNoInput) + _interruptionFilter = oldInterruptionFilter; + + closeCroppedMovie(); + _vm->swapSaveAllowed(saveAllowed); + _vm->swapLoadAllowed(openAllowed); +} + +void Neighborhood::playMovieSegment(Movie *movie, TimeValue startTime, TimeValue stopTime) { + TimeValue oldStart, oldStop; + movie->getSegment(oldStart, oldStop); + + if (stopTime == 0xffffffff) + stopTime = movie->getDuration(); + + movie->setSegment(startTime, stopTime); + movie->setTime(startTime); + movie->start(); + + while (movie->isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + movie->stop(); + movie->setSegment(oldStart, oldStop); +} + +void Neighborhood::recallToTSASuccess() { + if (GameState.allTimeZonesFinished()) + _vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth); + else + _vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +void Neighborhood::recallToTSAFailure() { + _vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +void Neighborhood::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (_vm->getGameMode() == kModeNavigation) { + if (input.upButtonAnyDown()) + upButton(input); + else if (input.downButtonAnyDown()) + downButton(input); + else if (input.leftButtonAnyDown()) + leftButton(input); + else if (input.rightButtonAnyDown()) + rightButton(input); + } + + InputHandler::handleInput(input, cursorSpot); +} + +void Neighborhood::setHotspotFlags(const HotSpotID id, const HotSpotFlags flags) { + Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(id); + hotspot->setMaskedHotspotFlags(flags, flags); +} + +void Neighborhood::setIsItemTaken(const ItemID id) { + GameState.setTakenItemID(id, _vm->playerHasItemID(id)); +} + +void Neighborhood::upButton(const Input &) { + moveForward(); +} + +void Neighborhood::leftButton(const Input &) { + turnLeft(); +} + +void Neighborhood::rightButton(const Input &) { + turnRight(); +} + +void Neighborhood::downButton(const Input &) { + if (_inputHandler->wantsCursor()) { + _vm->getAllHotspots().deactivateAllHotspots(); + _inputHandler->activateHotspots(); + + for (HotspotList::iterator it = _vm->getAllHotspots().begin(); it != _vm->getAllHotspots().end(); it++) { + Hotspot *hotspot = *it; + + if (hotspot->isSpotActive() && (hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) == (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) { + HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID()); + + if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) { + Input scratch; + _inputHandler->clickInHotspot(scratch, hotspot); + return; + } + } + } + } +} + +void Neighborhood::initOnePicture(Picture *picture, const Common::String &pictureName, DisplayOrder order, CoordType left, CoordType top, bool show) { + picture->initFromPICTFile(pictureName); + picture->setDisplayOrder(order); + picture->moveElementTo(left, top); + picture->startDisplaying(); + if (show) + picture->show(); +} + +void Neighborhood::initOneMovie(Movie *movie, const Common::String &movieName, DisplayOrder order, CoordType left, CoordType top, bool show) { + movie->initFromMovieFile(movieName); + movie->setDisplayOrder(order); + movie->moveElementTo(left, top); + movie->startDisplaying(); + + if (show) + movie->show(); + + movie->redrawMovieWorld(); +} + +void Neighborhood::reinstateMonocleInterface() { + _vm->_gfx->disableErase(); + + _vm->createInterface(); + + if (g_AIArea) + setNextHandler(g_AIArea); + + init(); + + moveNavTo(kNavAreaLeft, kNavAreaTop); + + if (g_interface) + g_interface->setDate(getDateResID()); + + if (g_AIArea) + g_AIArea->restoreAIState(); +} + +void Neighborhood::useIdleTime() { + if (_doneWithInteraction) { + newInteraction(kNoInteractionID); + loadAmbientLoops(); + } +} + +void Neighborhood::timerFunction() { + timerExpired(getTimerEvent()); +} + +void Neighborhood::scheduleEvent(const TimeValue time, const TimeScale scale, const uint32 eventType) { + _eventTimer.stopFuse(); + _eventTimer.primeFuse(time, scale); + _timerEvent = eventType; + _eventTimer.setFunctor(new Common::Functor0Mem<void, Neighborhood>(this, &Neighborhood::timerFunction)); + _eventTimer.lightFuse(); +} + +void Neighborhood::cancelEvent() { + _eventTimer.stopFuse(); +} + +void Neighborhood::pauseTimer() { + _eventTimer.pauseFuse(); +} + +void Neighborhood::resumeTimer() { + // NOTE: Yes, this function calls pauseFuse! + // Looks like an original game bug, will need + // to investigate how this affects gameplay. + _eventTimer.pauseFuse(); +} + +bool Neighborhood::timerPaused() { + return _eventTimer.isFusePaused(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/neighborhood.h b/engines/pegasus/neighborhood/neighborhood.h new file mode 100644 index 0000000000..3c1c5eac92 --- /dev/null +++ b/engines/pegasus/neighborhood/neighborhood.h @@ -0,0 +1,408 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_H +#define PEGASUS_NEIGHBORHOOD_H + +#include "common/queue.h" +#include "common/str.h" + +#include "pegasus/fader.h" +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/sound.h" +#include "pegasus/timers.h" +#include "pegasus/transition.h" +#include "pegasus/util.h" +#include "pegasus/neighborhood/door.h" +#include "pegasus/neighborhood/exit.h" +#include "pegasus/neighborhood/extra.h" +#include "pegasus/neighborhood/hotspotinfo.h" +#include "pegasus/neighborhood/spot.h" +#include "pegasus/neighborhood/turn.h" +#include "pegasus/neighborhood/view.h" +#include "pegasus/neighborhood/zoom.h" + +namespace Pegasus { + +class PegasusEngine; + +// Pegasus Prime neighborhood id's +static const NeighborhoodID kCaldoriaID = 0; +static const NeighborhoodID kFullTSAID = 1; +static const NeighborhoodID kFinalTSAID = 2; +static const NeighborhoodID kTinyTSAID = 3; +static const NeighborhoodID kPrehistoricID = 4; +static const NeighborhoodID kMarsID = 5; +static const NeighborhoodID kWSCID = 6; +static const NeighborhoodID kNoradAlphaID = 7; +static const NeighborhoodID kNoradDeltaID = 8; +// The sub chase is not really a neighborhood, but we define a constant that is used +// to allow an easy transition out of Norad Alpha. +static const NeighborhoodID kNoradSubChaseID = 1000; + +static const TimeScale kDefaultLoopFadeScale = kThirtyTicksPerSecond; +static const TimeValue kDefaultLoopFadeOut = kHalfSecondPerThirtyTicks; +static const TimeValue kDefaultLoopFadeIn = kHalfSecondPerThirtyTicks; + +enum QueueRequestType { + kNavExtraRequest, + kSpotSoundRequest, + kDelayRequest +}; + +// For delay requests, start is interpreted as the total delay and stop is interpreted +// as the scale the delay is in. +// For extra requests, start and stop are not used. +struct QueueRequest { + QueueRequestType requestType; + ExtraID extra; + TimeValue start, stop; + InputBits interruptionFilter; + bool playing; + NotificationFlags flags; + Notification *notification; +}; + +bool operator==(const QueueRequest &arg1, const QueueRequest &arg2); +bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2); + +class GameInteraction; +class Item; +class Neighborhood; + +class StriderCallBack : public TimeBaseCallBack { +public: + StriderCallBack(Neighborhood *); + virtual ~StriderCallBack() {} + +protected: + virtual void callBack(); + + Neighborhood *_neighborhood; +}; + +typedef Common::Queue<QueueRequest> NeighborhoodActionQueue; + +class Neighborhood : public IDObject, public NotificationReceiver, public InputHandler, public Idler { +friend class StriderCallBack; + +public: + Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id); + virtual ~Neighborhood(); + + virtual void init(); + virtual void start(); + virtual void moveNavTo(const CoordType, const CoordType); + virtual void checkContinuePoint(const RoomID, const DirectionConstant) = 0; + void makeContinuePoint(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual CanMoveForwardReason canMoveForward(ExitTable::Entry &entry); + virtual CanTurnReason canTurn(TurnDirection turn, DirectionConstant &nextDir); + virtual CanOpenDoorReason canOpenDoor(DoorTable::Entry &entry); + + virtual void cantMoveThatWay(CanMoveForwardReason); + virtual void cantTurnThatWay(CanTurnReason) {} + virtual void cantOpenDoor(CanOpenDoorReason); + virtual void arriveAt(const RoomID room, const DirectionConstant direction); + virtual void turnTo(const DirectionConstant); + virtual void spotCompleted(); + virtual void doorOpened(); + virtual void closeDoorOffScreen(const RoomID, const DirectionConstant) {} + + virtual void moveForward(); + virtual void turn(const TurnDirection); + virtual void turnLeft(); + virtual void turnRight(); + virtual void turnUp(); + virtual void turnDown(); + virtual void openDoor(); + virtual void zoomTo(const Hotspot *); + + virtual void updateViewFrame(); + + void requestExtraSequence(const ExtraID, const NotificationFlags, const InputBits interruptionFilter); + void requestSpotSound(const TimeValue, const TimeValue, const InputBits interruptionFilter, const NotificationFlags); + void playSpotSoundSync(const TimeValue in, const TimeValue out); + void requestDelay(const TimeValue, const TimeScale, const InputBits interruptionFilter, const NotificationFlags); + + Notification *getNeighborhoodNotification() { return &_neighborhoodNotification; } + + virtual void getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry); + virtual void startSpotLoop(TimeValue, TimeValue, NotificationFlags = 0); + virtual bool actionQueueEmpty() { return _actionQueue.empty(); } + virtual void showViewFrame(TimeValue); + virtual void findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry); + virtual void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits interruptionFilter); + bool startExtraSequenceSync(const ExtraID, const InputBits); + virtual void loopExtraSequence(const uint32, NotificationFlags = 0); + int32 getLastExtra() const { return _lastExtra; } + virtual void scheduleNavCallBack(NotificationFlags); + + Movie *getNavMovie() { return &_navMovie; } + bool navMoviePlaying(); + + void setCurrentAlternate(const AlternateID alt) { _currentAlternate = alt; } + AlternateID getCurrentAlternate() const { return _currentAlternate; } + + void setCurrentActivation(const HotSpotActivationID a) { _currentActivation = a; } + HotSpotActivationID getCurrentActivation() { return _currentActivation; } + + virtual void playDeathExtra(ExtraID, DeathReason); + virtual void die(const DeathReason); + + virtual void setSoundFXLevel(const uint16); + virtual void setAmbienceLevel(const uint16); + + void forceStridingStop(const RoomID, const DirectionConstant, const AlternateID); + void restoreStriding(const RoomID, const DirectionConstant, const AlternateID); + + HotspotInfoTable::Entry *findHotspotEntry(const HotSpotID); + + Push *getTurnPush() { return &_turnPush; } + Picture *getTurnPushPicture() { return &_pushIn; } + + void hideNav(); + void showNav(); + + virtual void loadAmbientLoops() {} + + virtual void flushGameState() {} + + virtual Common::String getBriefingMovie(); + virtual Common::String getEnvScanMovie(); + virtual uint getNumHints(); + virtual Common::String getHintMovie(uint); + virtual bool canSolve(); + virtual void prepareForAIHint(const Common::String &) {} + virtual void cleanUpAfterAIHint(const Common::String &) {} + virtual void doSolve(); + + virtual bool okayToJump(); + + virtual AirQuality getAirQuality(const RoomID); + virtual void checkAirMask() {} + virtual void checkFlashlight() {} + virtual void shieldOn() {} + virtual void shieldOff() {} + + virtual void loadLoopSound1(const Common::String &, const uint16 volume = 0x100, + const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn, + const TimeScale fadeScale = kDefaultLoopFadeScale); + virtual void loadLoopSound2(const Common::String &, const uint16 volume = 0x100, + const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn, + const TimeScale fadeScale = kDefaultLoopFadeScale); + bool loop1Loaded(const Common::String &soundName) { return _loop1SoundString == soundName; } + bool loop2Loaded(const Common::String &soundName) { return _loop2SoundString == soundName; } + void startLoop1Fader(const FaderMoveSpec &); + void startLoop2Fader(const FaderMoveSpec &); + + virtual void takeItemFromRoom(Item *); + virtual void dropItemIntoRoom(Item *, Hotspot *); + virtual Hotspot *getItemScreenSpot(Item *, DisplayElement *) { return 0; } + + virtual GameInteraction *makeInteraction(const InteractionID); + virtual void requestDeleteCurrentInteraction() { _doneWithInteraction = true; } + + virtual uint16 getDateResID() const = 0; + + virtual void showExtraView(uint32); + virtual void startExtraLongSequence(const uint32, const uint32, NotificationFlags, const InputBits interruptionFilter); + + void openCroppedMovie(const Common::String &, CoordType, CoordType); + void loopCroppedMovie(const Common::String &, CoordType, CoordType); + void closeCroppedMovie(); + void playCroppedMovieOnce(const Common::String &, CoordType, CoordType, const InputBits interruptionFilter = kFilterNoInput); + + void playMovieSegment(Movie *, TimeValue = 0, TimeValue = 0xffffffff); + + virtual void recallToTSASuccess(); + virtual void recallToTSAFailure(); + + virtual void pickedUpItem(Item *) {} + + virtual void handleInput(const Input &, const Hotspot *); +protected: + PegasusEngine *_vm; + Common::String _resName; + + virtual Common::String getSoundSpotsName() = 0; + virtual Common::String getNavMovieName() = 0; + + // Notification function. + virtual void receiveNotification(Notification *, const NotificationFlags); + + // Map info functions. + virtual void getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry); + virtual TimeValue getViewTime(const RoomID room, const DirectionConstant direction); + virtual void getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry); + virtual DirectionConstant getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turn); + virtual void getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry); + virtual void getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry); + + // Nav movie sequences. + virtual void startExitMovie(const ExitTable::Entry &); + virtual void keepStriding(ExitTable::Entry &); + virtual void stopStriding(); + virtual void checkStriding(); + virtual bool stillMoveForward(); + virtual void scheduleStridingCallBack(const TimeValue, NotificationFlags flags); + virtual void startZoomMovie(const ZoomTable::Entry &); + virtual void startDoorOpenMovie(const TimeValue, const TimeValue); + virtual void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant); + virtual void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionFilter); + + virtual void activateCurrentView(const RoomID, const DirectionConstant, SpotFlags); + + virtual void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); + + virtual void startSpotOnceOnly(TimeValue, TimeValue); + + virtual void startMovieSequence(const TimeValue, const TimeValue, NotificationFlags, + bool loopSequence, const InputBits interruptionFilter, const TimeValue strideStop = 0xffffffff); + + virtual void createNeighborhoodSpots(); + + void resetLastExtra() { _lastExtra = -1; } + + virtual void throwAwayInterface(); + + // Action queue stuff + void popActionQueue(); + void serviceActionQueue(); + void requestAction(const QueueRequestType, const ExtraID, const TimeValue, const TimeValue, const InputBits, const NotificationFlags); + + virtual bool prepareExtraSync(const ExtraID); + virtual bool waitMovieFinish(Movie *, const InputBits); + + virtual InputBits getInputFilter(); + + // Misc. + virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant dir); + virtual void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + virtual void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec&); + virtual void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec&); + + virtual void setUpAIRules(); + virtual void setHotspotFlags(const HotSpotID, const HotSpotFlags); + virtual void setIsItemTaken(const ItemID); + + virtual void upButton(const Input &); + virtual void leftButton(const Input &); + virtual void rightButton(const Input &); + virtual void downButton(const Input &); + + void initOnePicture(Picture *, const Common::String &, DisplayOrder, CoordType, CoordType, bool); + void initOneMovie(Movie *, const Common::String &, DisplayOrder, CoordType, CoordType, bool); + + void reinstateMonocleInterface(); + + virtual void newInteraction(const InteractionID); + virtual void useIdleTime(); + virtual void bumpIntoWall(); + virtual void zoomUpOrBump(); + + void scheduleEvent(const TimeValue, const TimeScale, const uint32); + void cancelEvent(); + virtual void timerExpired(const uint32) {} + bool isEventTimerRunning() { return _eventTimer.isFuseLit(); } + uint32 getTimerEvent() { return _timerEvent; } + void timerFunction(); + + void pauseTimer(); + void resumeTimer(); + bool timerPaused(); + + // Navigation Data + DoorTable _doorTable; + ExitTable _exitTable; + ExtraTable _extraTable; + HotspotInfoTable _hotspotInfoTable; + SpotTable _spotTable; + TurnTable _turnTable; + ViewTable _viewTable; + ZoomTable _zoomTable; + AlternateID _currentAlternate; + HotSpotActivationID _currentActivation; + + int32 _lastExtra; + DeathReason _extraDeathReason; + + // Graphics + Movie _navMovie; + Picture _pushIn; + Push _turnPush; + + // Callbacks + Notification _neighborhoodNotification; + NotificationCallBack _navMovieCallBack; + StriderCallBack _stridingCallBack; + NotificationCallBack _turnPushCallBack; + NotificationCallBack _spotSoundCallBack; + NotificationCallBack _delayCallBack; + + // Hotspots + HotspotList _neighborhoodHotspots; + + // Sounds + SoundTimeBase _spotSounds; + + // Action queue + NeighborhoodActionQueue _actionQueue; + TimeBase _delayTimer; + + // Interruptibility... + InputBits _interruptionFilter; + + // Nav hiding (for info support...) + bool _isRunning; + + GameInteraction *_currentInteraction; + bool _doneWithInteraction; + Movie _croppedMovie; + + Sound _soundLoop1; + Common::String _loop1SoundString; + SoundFader _loop1Fader; + + Sound _soundLoop2; + Common::String _loop2SoundString; + SoundFader _loop2Fader; + + // The event timer... + FuseFunction _eventTimer; + uint32 _timerEvent; +}; + +extern Neighborhood *g_neighborhood; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp new file mode 100644 index 0000000000..e2a0267231 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp @@ -0,0 +1,219 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h" + +namespace Pegasus { + +static const NotificationFlags kECRSection1FinishedFlag = 1; +static const NotificationFlags kECRPanFinishedFlag = kECRSection1FinishedFlag << 1; +static const NotificationFlags kECRSection2FinishedFlag = kECRPanFinishedFlag << 1; +static const NotificationFlags kECRNotificationFlags = kECRSection1FinishedFlag | + kECRPanFinishedFlag | + kECRSection2FinishedFlag; + +static const TimeValue kSection1Start = 0; +static const TimeValue kSection1Stop = 25; +static const TimeValue kPanStart = 0; +static const TimeValue kPanStop = 20; +static const TimeValue kSection2Start = 26; +static const TimeValue kSection2Stop = 1000; + +// Seems to be a good value for a 20 second pan. +static const CoordType kPanPixelsPerFrame = 8; + +// Interesting times are in seconds. +static const TimeValue s_ECRInterestingTimes[] = { + 0, 1, 2, 10, 25, 26, 56, 64, 72, 80, 88, 94, 102, 108, 116, 999 +}; + +// Index into s_ECRInterestingTimes of interesting time before security pan. +static const int kBeforePanTime = 3; + +// Index into s_ECRInterestingTimes of interesting time after security pan. +static const int kAfterPanTime = 5; + +NoradAlphaECRMonitor::NoradAlphaECRMonitor(Neighborhood *nextHandler) : GameInteraction(kNoradECRMonitorInteractionID, nextHandler), + _ecrSlideShowNotification(kNoradECRNotificationID, (PegasusEngine *)g_engine), _ecrMovie(kECRSlideShowMovieID), + _ecrPan(kECRPanID) { +} + +void NoradAlphaECRMonitor::receiveNotification(Notification *, const NotificationFlags flags) { + if (flags & kECRSection1FinishedFlag) + ecrSection1Finished(); + else if (flags & kECRPanFinishedFlag) + ecrPanFinished(); + else if (flags & kECRSection2FinishedFlag) + ecrSection2Finished(); +} + +int NoradAlphaECRMonitor::findCurrentInterestingTime() { + TimeValue time = _ecrMovie.getTime(); + TimeScale scale = _ecrMovie.getScale(); + + for (int i = ARRAYSIZE(s_ECRInterestingTimes) - 1; i >= 0; i--) + if (time >= s_ECRInterestingTimes[i] * scale) + return i; + + return 0; +} + +void NoradAlphaECRMonitor::skipToNextInterestingTime() { + if (_ecrMovie.isRunning()) { + int interestingTime = findCurrentInterestingTime(); + _ecrMovie.setTime(s_ECRInterestingTimes[interestingTime + 1] * _ecrMovie.getScale()); + _ecrMovie.redrawMovieWorld(); + } else if (_ecrPan.isRunning()) { + _ecrPanCallBack.cancelCallBack(); + ecrPanFinished(); + } +} + +void NoradAlphaECRMonitor::skipToPreviousInterestingTime() { + if (_ecrPan.isRunning()) { + _ecrPan.stop(); + _ecrPan.stopDisplaying(); + _ecrPanCallBack.cancelCallBack(); + + _ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag); + _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + TimeScale scale = _ecrMovie.getScale(); + _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); + _ecrMovie.setTime(s_ECRInterestingTimes[kBeforePanTime] * scale); + _ecrMovie.start(); + } else { + int interestingTime = findCurrentInterestingTime(); + + if (interestingTime == kAfterPanTime) { + _ecrMovieCallBack.cancelCallBack(); + TimeScale scale = _ecrMovie.getScale(); + _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); + _ecrMovie.setTime(kSection1Stop * scale); + ecrSection1Finished(); + } else if (interestingTime == 0) { + _ecrMovie.setTime(kSection1Start * _ecrMovie.getScale()); + _ecrMovie.redrawMovieWorld(); + } else { + _ecrMovie.setTime(s_ECRInterestingTimes[interestingTime - 1] * _ecrMovie.getScale()); + _ecrMovie.redrawMovieWorld(); + } + } +} + +void NoradAlphaECRMonitor::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (isInteracting()) { + if (input.rightButtonDown()) + skipToNextInterestingTime(); + else if (input.leftButtonDown()) + skipToPreviousInterestingTime(); + else + InputHandler::handleInput(input, cursorSpot); + } else { + InputHandler::handleInput(input, cursorSpot); + } +} + +void NoradAlphaECRMonitor::ecrSection1Finished() { + _ecrMovie.stop(); + _ecrPanCallBack.setNotification(&_ecrSlideShowNotification); + _ecrPanCallBack.initCallBack(&_ecrPan, kCallBackAtExtremes); + _ecrPanCallBack.setCallBackFlag(kECRPanFinishedFlag); + _ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags); + _ecrPanCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _ecrPan.startDisplaying(); + _ecrPan.show(); + + TimeScale scale = _ecrPan.getScale(); + _ecrPan.setSegment(kPanStart * scale, kPanStop * scale); + _ecrPan.setTime(0); + _ecrPan.start(); +} + +void NoradAlphaECRMonitor::ecrPanFinished() { + _ecrPan.stop(); + _ecrPan.stopDisplaying(); + _ecrMovieCallBack.setCallBackFlag(kECRSection2FinishedFlag); + _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + TimeScale scale = _ecrMovie.getScale(); + _ecrMovie.setSegment(kSection2Start * scale, kSection2Stop * scale); + _ecrMovie.start(); +} + +void NoradAlphaECRMonitor::ecrSection2Finished() { + _ecrMovie.stop(); + _ecrMovie.stopDisplaying(); +} + +void NoradAlphaECRMonitor::openInteraction() { + // Initialize the security pan. + _ecrPan.initFromMovieFile("Images/Norad Alpha/Security Pan.pano"); + _ecrPan.initMaskFromPICTFile("Images/Norad Alpha/Security Pan Mask"); + _ecrPan.setBounds(Common::Rect(kECRPanLeft, kECRPanTop, kECRPanRight, kECRPanBottom)); + _ecrPan.setDisplayOrder(kECRPanOrder); + _ecrPan.setScale(15); // 15 fps. + + // Begin the lame ECR slide show. + // clone2727: I didn't say it :P + _ecrMovie.initFromMovieFile("Images/Norad Alpha/ECR Monitor Movie"); + + _ecrMovieCallBack.setNotification(&_ecrSlideShowNotification); + _ecrMovieCallBack.initCallBack(&_ecrMovie, kCallBackAtExtremes); + _ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag); + + _ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags); + _ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + _ecrMovie.moveElementTo(kECRSlideShowLeft, kECRSlideShowTop); + _ecrMovie.setDisplayOrder(kECRMonitorOrder); + _ecrMovie.startDisplaying(); + _ecrMovie.show(); + _ecrMovie.redrawMovieWorld(); + + TimeScale scale = _ecrMovie.getScale(); + _ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); + + _ecrMovie.start(); +} + +void NoradAlphaECRMonitor::closeInteraction() { + _ecrMovieCallBack.releaseCallBack(); + _ecrMovie.stop(); + _ecrMovie.stopDisplaying(); + _ecrMovie.releaseMovie(); + _ecrMovieCallBack.releaseCallBack(); + + _ecrPanCallBack.releaseCallBack(); + _ecrPan.stop(); + _ecrPan.stopDisplaying(); + _ecrPan.releasePanorama(); + _ecrPanCallBack.releaseCallBack(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h new file mode 100644 index 0000000000..9e286ed337 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h @@ -0,0 +1,65 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/neighborhood/norad/alpha/panoramascroll.h" + +namespace Pegasus { + +class NoradAlphaECRMonitor : public GameInteraction, public NotificationReceiver { +public: + NoradAlphaECRMonitor(Neighborhood *); + virtual ~NoradAlphaECRMonitor() {} + + virtual void handleInput(const Input &, const Hotspot *); + +protected: + virtual void openInteraction(); + virtual void closeInteraction(); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + void ecrSection1Finished(); + void ecrPanFinished(); + void ecrSection2Finished(); + + int findCurrentInterestingTime(); + void skipToNextInterestingTime(); + void skipToPreviousInterestingTime(); + + Notification _ecrSlideShowNotification; + Movie _ecrMovie; + NotificationCallBack _ecrMovieCallBack; + PanoramaScroll _ecrPan; + NotificationCallBack _ecrPanCallBack; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp new file mode 100644 index 0000000000..169f75f7d2 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp @@ -0,0 +1,445 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/alpha/fillingstation.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +static const NotificationFlags kFSPowerUpFinishedFlag = 1; +static const NotificationFlags kFSSplashFinishedFlag = kFSPowerUpFinishedFlag << 1; +static const NotificationFlags kFSIntakeWarningFinishedFlag = kFSSplashFinishedFlag << 1; +static const NotificationFlags kFSIntakeHiliteFinishedFlag = kFSIntakeWarningFinishedFlag << 1; +static const NotificationFlags kFSDispenseHiliteFinishedFlag = kFSIntakeHiliteFinishedFlag << 1; +static const NotificationFlags kFSArHiliteFinishedFlag = kFSDispenseHiliteFinishedFlag << 1; +static const NotificationFlags kFSCO2HiliteFinishedFlag = kFSArHiliteFinishedFlag << 1; +static const NotificationFlags kFSHeHiliteFinishedFlag = kFSCO2HiliteFinishedFlag << 1; +static const NotificationFlags kFSOHiliteFinishedFlag = kFSHeHiliteFinishedFlag << 1; +static const NotificationFlags kFSNHiliteFinishedFlag = kFSOHiliteFinishedFlag << 1; + +static const NotificationFlags kFSNotificationFlags = kFSPowerUpFinishedFlag | + kFSSplashFinishedFlag | + kFSIntakeWarningFinishedFlag | + kFSIntakeHiliteFinishedFlag | + kFSDispenseHiliteFinishedFlag | + kFSArHiliteFinishedFlag | + kFSCO2HiliteFinishedFlag | + kFSHeHiliteFinishedFlag | + kFSOHiliteFinishedFlag | + kFSNHiliteFinishedFlag; + +static const int16 kNoState = 0; +static const int16 kMainMenu = 1; +static const int16 kWaitingForAttach = 2; +static const int16 kDispenseMenu = 3; +static const int16 kWaitingForDispense = 4; + +// Dummy itemIDs +static const ItemID kCO2Item = 10000; +static const ItemID kHeItem = 10001; + +// Interactive points. +static const TimeValue kFSPowerUpStartStart = 0; +static const TimeValue kFSPowerUpStartStop = 600; +static const TimeValue kFSSplashStart = 600; +static const TimeValue kFSSplashStop = 7800; +static const TimeValue kFSSplashIntakeStart = 7800; +static const TimeValue kFSSplashIntakeStop = 18600; + +static const TimeValue kFSMainMenu = 18600; +static const TimeValue kFSIntakeHiliteStart = 19200; +static const TimeValue kFSIntakeHiliteStop = 19800; +static const TimeValue kFSDispenseHiliteStart = 19800; +static const TimeValue kFSDispenseHiliteStop = 20400; + +static const TimeValue kFSDispenseMenu = 20400; + +static const TimeValue kFSArHiliteStart = 21000; +static const TimeValue kFSArHiliteStop = 21600; +static const TimeValue kFSArAttach = 21600; +static const TimeValue kFSArFilledStart = 22200; +static const TimeValue kFSArFilledStop = 25200; +static const TimeValue kFSArIncompatibleStart = 25200; +static const TimeValue kFSArIncompatibleStop = 30000; + +static const TimeValue kFSCO2HiliteStart = 30000; +static const TimeValue kFSCO2HiliteStop = 30600; +static const TimeValue kFSCO2Attach = 30600; +static const TimeValue kFSCO2FilledStart = 31200; +static const TimeValue kFSCO2FilledStop = 34200; +static const TimeValue kFSCO2IncompatibleStart = 34200; +static const TimeValue kFSCO2IncompatibleStop = 39000; + +static const TimeValue kFSHeHiliteStart = 39000; +static const TimeValue kFSHeHiliteStop = 39600; +static const TimeValue kFSHeAttach = 39600; +static const TimeValue kFSHeFilledStart = 40200; +static const TimeValue kFSHeFilledStop = 43200; +static const TimeValue kFSHeIncompatibleStart = 43200; +static const TimeValue kFSHeIncompatibleStop = 48000; + +static const TimeValue kFSOHiliteStart = 48000; +static const TimeValue kFSOHiliteStop = 48600; +static const TimeValue kFSOAttach = 48600; +static const TimeValue kFSOFilledStart = 49200; +static const TimeValue kFSOFilledStop = 52200; +static const TimeValue kFSOIncompatibleStart = 52200; +static const TimeValue kFSOIncompatibleStop = 57000; + +static const TimeValue kFSNHiliteStart = 57000; +static const TimeValue kFSNHiliteStop = 57600; +static const TimeValue kFSNAttach = 57600; +static const TimeValue kFSNFilledStart = 58200; +static const TimeValue kFSNFilledStop = 61200; +static const TimeValue kFSNIncompatibleStart = 61200; +static const TimeValue kFSNIncompatibleStop = 66000; + +static const TimeValue kFSIntakeMenu = 66000; +static const TimeValue kFSIntakeInProgressStart = 66600; +static const TimeValue kFSIntakeInProgressStop = 69600; + +NoradAlphaFillingStation::NoradAlphaFillingStation(Neighborhood *owner) : GameInteraction(kNoradFillingStationInteractionID, owner), + _rightSideMovie(kN01RightSideID), _rightSideNotification(kNoradFillingStationNotificationID, ((PegasusEngine *)g_engine)) { + _state = kNoState; +} + +void NoradAlphaFillingStation::openInteraction() { + _rightSideMovie.initFromMovieFile("Images/Norad Alpha/N01W Right Side"); + _rightSideMovie.moveElementTo(kNoradAlpha01RightSideLeft, kNoradAlpha01RightSideTop); + _rightSideMovie.setDisplayOrder(kN01RightSideOrder); + _rightSideMovie.startDisplaying(); + _rightSideCallBack.setNotification(&_rightSideNotification); + _rightSideCallBack.initCallBack(&_rightSideMovie, kCallBackAtExtremes); + _rightSideCallBack.setCallBackFlag(kFSPowerUpFinishedFlag); + _rightSideNotification.notifyMe(this, kFSNotificationFlags, kFSNotificationFlags); + _rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _rightSideMovie.show(); + _rightSideMovie.redrawMovieWorld(); + _rightSideMovie.setSegment(kFSPowerUpStartStart, kFSPowerUpStartStop); +} + +void NoradAlphaFillingStation::initInteraction() { + allowInput(false); + + _rightSideMovie.setRate(2); +} + +void NoradAlphaFillingStation::closeInteraction() { + _rightSideMovie.stop(); + _rightSideMovie.stopDisplaying(); + _rightSideMovie.releaseMovie(); + _rightSideCallBack.releaseCallBack(); + ((NoradAlpha *)getOwner())->turnOffFillingStation(); +} + +void NoradAlphaFillingStation::setStaticState(TimeValue time, int16 state) { + _rightSideMovie.stop(); + _rightSideMovie.setSegment(0, _rightSideMovie.getDuration()); + _rightSideMovie.setTime(time); + _rightSideMovie.redrawMovieWorld(); + _state = state; + allowInput(true); +} + +void NoradAlphaFillingStation::setSegmentState(TimeValue start, TimeValue stop, NotificationFlags flag, int16 state) { + _rightSideMovie.stop(); + _rightSideMovie.setSegment(start, stop); + _rightSideMovie.setTime(start); + _rightSideCallBack.setCallBackFlag(flag); + _rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _state = state; + allowInput(false); + _rightSideMovie.setRate(2); +} + +void NoradAlphaFillingStation::powerUpFinished() { + ((NoradAlpha *)getOwner())->turnOnFillingStation(); + setSegmentState(kFSSplashStart, kFSSplashStop, kFSSplashFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::splashFinished() { + if (GameState.getNoradGassed()) + setSegmentState(kFSSplashIntakeStart, kFSSplashIntakeStop, kFSIntakeWarningFinishedFlag, kNoState); + else + intakeWarningFinished(); +} + +void NoradAlphaFillingStation::intakeWarningFinished() { + setStaticState(kFSMainMenu, kMainMenu); +} + +void NoradAlphaFillingStation::showIntakeInProgress(uint16 numSeconds) { + if (numSeconds == 0) { + setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStop, kFSIntakeWarningFinishedFlag, kNoState); + Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); + + if (item->getObjectID() == kGasCanister) { + GameState.setNoradGassed(true); + ((NoradAlpha *)getOwner())->loadAmbientLoops(); + getOwner()->restoreStriding(kNorad03, kEast, kAltNoradAlphaNormal); + } + } else { + setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStart + _rightSideMovie.getScale() * numSeconds, + kFSIntakeWarningFinishedFlag, kNoState); + } +} + +void NoradAlphaFillingStation::intakeHighlightFinished() { + _rightSideMovie.stop(); + + if (GameState.getNoradGassed()) { + showIntakeInProgress(2); + } else { + Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); + if (item) + showIntakeInProgress(0); + else + setStaticState(kFSIntakeMenu, kWaitingForAttach); + } +} + +void NoradAlphaFillingStation::dispenseHighlightFinished() { + setStaticState(kFSDispenseMenu, kDispenseMenu); +} + +void NoradAlphaFillingStation::dispenseGas() { + Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); + + if (item) { + if (item->getObjectID() != _dispenseItemID) + switch (_dispenseItemID) { + case kArgonCanister: + setSegmentState(kFSArIncompatibleStart, kFSArIncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + case kCO2Item: + setSegmentState(kFSCO2IncompatibleStart, kFSCO2IncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + case kHeItem: + setSegmentState(kFSHeIncompatibleStart, kFSHeIncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + case kAirMask: + setSegmentState(kFSOIncompatibleStart, kFSOIncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + case kNitrogenCanister: + setSegmentState(kFSNIncompatibleStart, kFSNIncompatibleStop, + kFSIntakeWarningFinishedFlag, kNoState); + break; + } + else { + if (_dispenseItemID == kArgonCanister) { + setSegmentState(kFSArFilledStart, kFSArFilledStop, kFSIntakeWarningFinishedFlag, kNoState); + item->setItemState(kArgonFull); + GameState.setScoringFilledArgonCanister(true); + } else if (_dispenseItemID == kAirMask) { + setSegmentState(kFSOFilledStart, kFSOFilledStop, kFSIntakeWarningFinishedFlag, kNoState); + ((AirMask *)item)->refillAirMask(); + GameState.setScoringFilledOxygenCanister(true); + } else if (_dispenseItemID == kNitrogenCanister) { + setSegmentState(kFSNFilledStart, kFSNFilledStop, kFSIntakeWarningFinishedFlag, kNoState); + item->setItemState(kNitrogenFull); + } + } + } else { + switch (_dispenseItemID) { + case kArgonCanister: + setStaticState(kFSArAttach, kWaitingForDispense); + break; + case kCO2Item: + setStaticState(kFSCO2Attach, kWaitingForDispense); + break; + case kHeItem: + setStaticState(kFSHeAttach, kWaitingForDispense); + break; + case kAirMask: + setStaticState(kFSOAttach, kWaitingForDispense); + break; + case kNitrogenCanister: + setStaticState(kFSNAttach, kWaitingForDispense); + break; + } + } +} + +void NoradAlphaFillingStation::ArHighlightFinished() { + _dispenseItemID = kArgonCanister; + dispenseGas(); +} + +void NoradAlphaFillingStation::CO2HighlightFinished() { + _dispenseItemID = kCO2Item; + dispenseGas(); +} + +void NoradAlphaFillingStation::HeHighlightFinished() { + _dispenseItemID = kHeItem; + dispenseGas(); +} + +void NoradAlphaFillingStation::OHighlightFinished() { + _dispenseItemID = kAirMask; + dispenseGas(); +} + +void NoradAlphaFillingStation::NHighlightFinished() { + _dispenseItemID = kNitrogenCanister; + dispenseGas(); +} + +void NoradAlphaFillingStation::receiveNotification(Notification *, const NotificationFlags flags) { + switch (flags) { + case kFSPowerUpFinishedFlag: + powerUpFinished(); + break; + case kFSSplashFinishedFlag: + splashFinished(); + break; + case kFSIntakeWarningFinishedFlag: + intakeWarningFinished(); + break; + case kFSIntakeHiliteFinishedFlag: + intakeHighlightFinished(); + break; + case kFSDispenseHiliteFinishedFlag: + dispenseHighlightFinished(); + break; + case kFSArHiliteFinishedFlag: + ArHighlightFinished(); + break; + case kFSCO2HiliteFinishedFlag: + CO2HighlightFinished(); + break; + case kFSHeHiliteFinishedFlag: + HeHighlightFinished(); + break; + case kFSOHiliteFinishedFlag: + OHighlightFinished(); + break; + case kFSNHiliteFinishedFlag: + NHighlightFinished(); + break; + } +} + +void NoradAlphaFillingStation::handleInput(const Input &input, const Hotspot *cursorSpot) { + InputHandler::handleInput(input, cursorSpot); +} + +void NoradAlphaFillingStation::clickInIntake() { + setSegmentState(kFSIntakeHiliteStart, kFSIntakeHiliteStop, kFSIntakeHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInDispense() { + setSegmentState(kFSDispenseHiliteStart, kFSDispenseHiliteStop, kFSDispenseHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInAr() { + setSegmentState(kFSArHiliteStart, kFSArHiliteStop, kFSArHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInCO2() { + setSegmentState(kFSCO2HiliteStart, kFSCO2HiliteStop, kFSCO2HiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInHe() { + setSegmentState(kFSHeHiliteStart, kFSHeHiliteStop, kFSHeHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInO() { + setSegmentState(kFSOHiliteStart, kFSOHiliteStop, kFSOHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInN() { + setSegmentState(kFSNHiliteStart, kFSNHiliteStop, kFSNHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInHotspot(const Input &input, const Hotspot *spot) { + GameInteraction::clickInHotspot(input, spot); + + switch (spot->getObjectID()) { + case kNorad01IntakeSpotID: + clickInIntake(); + break; + case kNorad01DispenseSpotID: + clickInDispense(); + break; + case kNorad01ArSpotID: + clickInAr(); + break; + case kNorad01CO2SpotID: + clickInCO2(); + break; + case kNorad01HeSpotID: + clickInHe(); + break; + case kNorad01OSpotID: + clickInO(); + break; + case kNorad01NSpotID: + clickInN(); + break; + } +} + +void NoradAlphaFillingStation::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_state) { + case kMainMenu: + g_allHotspots.activateOneHotspot(kNorad01IntakeSpotID); + g_allHotspots.activateOneHotspot(kNorad01DispenseSpotID); + break; + case kDispenseMenu: + g_allHotspots.activateOneHotspot(kNorad01ArSpotID); + g_allHotspots.activateOneHotspot(kNorad01CO2SpotID); + g_allHotspots.activateOneHotspot(kNorad01HeSpotID); + g_allHotspots.activateOneHotspot(kNorad01OSpotID); + g_allHotspots.activateOneHotspot(kNorad01NSpotID); + break; + } +} + +void NoradAlphaFillingStation::newFillingItem(Item *item) { + switch (_state) { + case kWaitingForAttach: + if (item) + showIntakeInProgress(0); + break; + case kWaitingForDispense: + dispenseGas(); + break; + default: + break; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.h b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h new file mode 100644 index 0000000000..eb2088e373 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +class Item; + +class NoradAlphaFillingStation : public GameInteraction, public NotificationReceiver { +public: + NoradAlphaFillingStation(Neighborhood *); + virtual ~NoradAlphaFillingStation() {} + + virtual void handleInput(const Input &, const Hotspot *); + + virtual void clickInHotspot(const Input &, const Hotspot *); + virtual void activateHotspots(); + + void newFillingItem(Item *); + +protected: + void receiveNotification(Notification *, const NotificationFlags); + + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + void powerUpFinished(); + void splashFinished(); + void intakeWarningFinished(); + void intakeHighlightFinished(); + void dispenseHighlightFinished(); + void ArHighlightFinished(); + void CO2HighlightFinished(); + void HeHighlightFinished(); + void OHighlightFinished(); + void NHighlightFinished(); + + void showIntakeInProgress(uint16); + + void clickInIntake(); + void clickInDispense(); + void clickInAr(); + void clickInCO2(); + void clickInHe(); + void clickInO(); + void clickInN(); + + void dispenseGas(); + + void setStaticState(TimeValue, int16); + void setSegmentState(TimeValue, TimeValue, NotificationFlags, int16); + + Movie _rightSideMovie; + Notification _rightSideNotification; + NotificationCallBack _rightSideCallBack; + int16 _state; + ItemID _dispenseItemID; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp new file mode 100644 index 0000000000..e4a5e26473 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp @@ -0,0 +1,763 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h" +#include "pegasus/neighborhood/norad/alpha/fillingstation.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +const uint32 NoradAlpha::_noradAlphaClawExtras[22] = { + kN22ClawFromAToB, + kN22ClawALoop, + kN22ClawAPinch, + kN22ClawACounterclockwise, + kN22ClawAClockwise, + kN22ClawFromBToA, + kN22ClawFromBToC, + kN22ClawFromBToD, + kN22ClawBLoop, + kN22ClawBPinch, + kN22ClawBCounterclockwise, + kN22ClawBClockwise, + kN22ClawFromCToB, + kN22ClawCLoop, + kN22ClawCPinch, + kN22ClawCCounterclockwise, + kN22ClawCClockwise, + kN22ClawFromDToB, + kN22ClawDLoop, + kN22ClawDPinch, + kN22ClawDCounterclockwise, + kN22ClawDClockwise +}; + +NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Alpha", kNoradAlphaID) { + _elevatorUpRoomID = kNorad11South; + _elevatorDownRoomID = kNorad12South; + _elevatorUpSpotID = kNorad12ElevatorUpSpotID; + _elevatorDownSpotID = kNorad11ElevatorDownSpotID; + + _subRoomEntryRoom1 = kNorad10; + _subRoomEntryDir1 = kEast; + _subRoomEntryRoom2 = kNorad21; + _subRoomEntryDir2 = kWest; + _upperPressureDoorRoom = kNorad10East; + _lowerPressureDoorRoom = kNorad21West; + + _upperPressureDoorUpSpotID = kAlphaUpperPressureDoorUpSpotID; + _upperPressureDoorDownSpotID = kAlphaUpperPressureDoorDownSpotID; + _upperPressureDoorAbortSpotID = kNorad10EastOutSpotID; + + _lowerPressureDoorUpSpotID = kAlphaLowerPressureDoorUpSpotID; + _lowerPressureDoorDownSpotID = kAlphaLowerPressureDoorDownSpotID; + _lowerPressureDoorAbortSpotID = kNorad21WestOutSpotID; + + _pressureSoundIn = kPressureDoorIntro1In; + _pressureSoundOut = kPressureDoorIntro1Out; + _equalizeSoundIn = kPressureDoorIntro2In; + _equalizeSoundOut = kPressureDoorIntro2Out; + _accessDeniedIn = kAlphaAccessDeniedIn; + _accessDeniedOut = kAlphaAccessDeniedOut; + + _platformRoom = kNorad19West; + _subControlRoom = kNorad22West; + + _subPrepFailed = false; + + setIsItemTaken(kGasCanister); +} + +void NoradAlpha::init() { + Norad::init(); + + Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasCanisterSpotID); + hotspotEntry->hotspotItem = kGasCanister; + + hotspot = _vm->getAllHotspots().findHotspotByID(kN01ArgonCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01ArgonCanisterSpotID); + hotspotEntry->hotspotItem = kArgonCanister; + + hotspot = _vm->getAllHotspots().findHotspotByID(kN01NitrogenCanisterSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01NitrogenCanisterSpotID); + hotspotEntry->hotspotItem = kNitrogenCanister; + + hotspot = _vm->getAllHotspots().findHotspotByID(kN01AirMaskSpotID); + hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); + hotspotEntry = findHotspotEntry(kN01AirMaskSpotID); + hotspotEntry->hotspotItem = kAirMask; + + hotspot = _vm->getAllHotspots().findHotspotByID(kN01GasOutletSpotID); + hotspot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag); +} + +void NoradAlpha::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + NeighborhoodID itemNeighborhood; + RoomID itemRoom; + DirectionConstant itemDirection; + + Item *item = (Item *)_vm->getAllItems().findItemByID(kGasCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)_vm->getAllItems().findItemByID(kAirMask); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)_vm->getAllItems().findItemByID(kNitrogenCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + if (itemNeighborhood == getObjectID()) { + _fillingStationItem = item; + } else { + item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + if (itemNeighborhood == getObjectID()) + _fillingStationItem = item; + else + _fillingStationItem = 0; + } + } + } + + if (!GameState.getNoradGassed()) + forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal); + + GameState.setNoradArrivedFromSub(false); + Norad::start(); +} + +void NoradAlpha::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN01WD1", false); + AIHasItemCondition *hasGasCanisterCondition = new AIHasItemCondition(kGasCanister); + AIRule *rule = new AIRule(hasGasCanisterCondition, messageAction); + g_AIArea->addAIRule(rule); + } +} + +bool NoradAlpha::okayToJump() { + bool result = Neighborhood::okayToJump(); + + if (!result) + playSpotSoundSync(kAlphaCantTransportIn, kAlphaCantTransportOut); + + return result; +} + +void NoradAlpha::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + if (entry.extra == kNorad19ExitToSub) { + compassMove.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, entry.movieStart, 270 + kSubPlatformCompassAngle, + entry.movieEnd, 90 + 20 + 360); + compassMove.insertFaderKnot(entry.movieStart + 10 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle); + compassMove.insertFaderKnot(entry.movieStart + 29 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20); + compassMove.insertFaderKnot(entry.movieStart + 52 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20); + compassMove.insertFaderKnot(entry.movieStart + 84 * kNoradAlphaFrameDuration, 360 + 90); + compassMove.insertFaderKnot(entry.movieStart + 198 * kNoradAlphaFrameDuration, 360 + 90); + compassMove.insertFaderKnot(entry.movieStart + 270 * kNoradAlphaFrameDuration, 360 + 90 + 15); + compassMove.insertFaderKnot(entry.movieStart + 280 * kNoradAlphaFrameDuration, 360 + 90 + 20); + } else { + Norad::getExtraCompassMove(entry, compassMove); + } +} + +void NoradAlpha::playClawMonitorIntro() { + playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut); +} + +GameInteraction *NoradAlpha::makeInteraction(const InteractionID interactionID) { + switch (interactionID) { + case kNoradECRMonitorInteractionID: + return new NoradAlphaECRMonitor(this); + case kNoradFillingStationInteractionID: + return new NoradAlphaFillingStation(this); + } + + return Norad::makeInteraction(interactionID); +} + +void NoradAlpha::loadAmbientLoops() { + // clone2727 would like to point out that the following comment does not quite + // match the code logic below + +/* + Logic: + + loop sound 1: + if gassed, + play warning loop of some sort + else + play nothing + loop sound 2: + if gassed and not wearing air mask + if in ECR + play breathing water loop + else + play breathing + else + if in ECR + play water loop + if at N07 north + play unmanned loop +*/ + + if (!GameState.getNoradSeenTimeStream()) + return; + + RoomID room = GameState.getCurrentRoom(); + if (GameState.getNoradGassed()) { + if (room >= kNorad11 && room <= kNorad19West) + loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3); + else if (room >= kNorad21 && room <= kNorad22West) + loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3); + else + loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume); + } else { + loadLoopSound1(""); + } + + if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) { + if (room >= kNorad01 && room <= kNorad01West) { + loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume); + } else if (room == kNorad02) { + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume); + else + loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); + } else { + loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); + } + } else { + if (room >= kNorad01 && room <= kNorad01West) { + loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2); + } else if (room == kNorad02) { + if (GameState.isCurrentDoorOpen()) + loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2); + else + loadLoopSound2(""); + } else { + loadLoopSound2(""); + } + } + +} + +void NoradAlpha::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kNorad02, kEast): + case MakeRoomView(kNorad06, kEast): + case MakeRoomView(kNorad11, kEast): + case MakeRoomView(kNorad15, kEast): + case MakeRoomView(kNorad19, kWest): + case MakeRoomView(kNorad21, kSouth): + makeContinuePoint(); + break; + } +} + +void NoradAlpha::arriveAt(const RoomID room, const DirectionConstant direction) { + Norad::arriveAt(room, direction); + + switch (GameState.getCurrentRoom()) { + case kNorad01: + arriveAtNorad01(); + break; + case kNorad01East: + arriveAtNorad01East(); + break; + case kNorad01West: + arriveAtNorad01West(); + break; + case kNorad04: + arriveAtNorad04(); + break; + case kNorad07North: + GameState.setScoringSawUnconsciousOperator(true); + break; + case kNorad11: + GameState.setScoringWentThroughPressureDoor(true); + break; + case kNorad22: + arriveAtNorad22(); + break; + } +} + +void NoradAlpha::arriveAtNorad01() { + if (!GameState.getNoradSeenTimeStream() && GameState.getCurrentDirection() == kSouth) { + GameState.setNoradN22MessagePlayed(false); + requestExtraSequence(kNoradArriveFromTSA, kExtraCompletedFlag, kFilterNoInput); + // You are no match for me, human. + requestExtraSequence(kNorad01RobotTaunt, kExtraCompletedFlag, kFilterNoInput); + } +} + +void NoradAlpha::arriveAtNorad01East() { + GameState.setScoringSawSecurityMonitor(true); + newInteraction(kNoradECRMonitorInteractionID); +} + +void NoradAlpha::arriveAtNorad01West() { + newInteraction(kNoradFillingStationInteractionID); +} + +void NoradAlpha::arriveAtNorad04() { + if (GameState.getCurrentDirection() == kEast && !GameState.getNoradGassed()) + playDeathExtra(kNorad04EastDeath, kDeathWokeUpNorad); +} + +void NoradAlpha::arriveAtNorad22() { + if (!GameState.getNoradN22MessagePlayed() && GameState.getCurrentDirection() == kSouth) { + startExtraSequence(kNorad22SouthIntro, kExtraCompletedFlag, kFilterNoInput); + GameState.setNoradN22MessagePlayed(true); + } +} + +void NoradAlpha::bumpIntoWall() { + requestSpotSound(kAlphaBumpIntoWallIn, kAlphaBumpIntoWallOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +void NoradAlpha::receiveNotification(Notification *notification, const NotificationFlags flags) { + if ((flags & kExtraCompletedFlag) != 0) { + switch (_lastExtra) { + case kNoradArriveFromTSA: + GameState.setNoradSeenTimeStream(true); + loadAmbientLoops(); + break; + case kNorad01RobotTaunt: + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN01SB", false, kWarningInterruption); + _interruptionFilter = kFilterAllInput; + makeContinuePoint(); + break; + } + } + + Norad::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + switch (_lastExtra) { + case kNorad22SouthIntro: + loopExtraSequence(kNorad22SouthReply); + playSpotSoundSync(kN22ReplyIn, kN22ReplyOut); + startExtraSequence(kNorad22SouthFinish, kExtraCompletedFlag, kFilterNoInput); + break; + case kNorad22SouthFinish: + _interruptionFilter = kFilterAllInput; + // Force ArriveAt to do its thing... + GameState.setCurrentRoom(kNorad21); + arriveAt(kNorad22, kSouth); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void NoradAlpha::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) { + Norad::getZoomEntry(spotID, entry); + + ExtraTable::Entry extra; + + if (spotID == kNorad01GasSpotID) { + if (_fillingStationItem) { + if (_fillingStationItem->getObjectID() == kGasCanister) { + getExtraEntry(kNorad01ZoomInWithGasCanister, extra); + entry.movieStart = extra.movieStart; + entry.movieEnd = extra.movieEnd; + } else { + entry.clear(); + } + } + } else if (spotID == kNorad01GasOutSpotID) { + if (_fillingStationItem) { + if (_fillingStationItem->getObjectID() == kGasCanister) { + getExtraEntry(kNorad01ZoomOutWithGasCanister, extra); + entry.movieStart = extra.movieStart; + entry.movieEnd = extra.movieEnd; + } else { + entry.clear(); + } + } + } +} + +TimeValue NoradAlpha::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry entry; + + if (room == kNorad01 && direction == kSouth && !GameState.getNoradSeenTimeStream()) { + getExtraEntry(kNoradArriveFromTSA, entry); + return entry.movieStart; + } + + if (room == kNorad01 && direction == kWest) { + if (!_fillingStationItem) { + return Norad::getViewTime(room, direction); + } else { + getExtraEntry(kN01WGasCanister, entry); + return entry.movieStart; + } + } else if (room == kNorad01West && direction == kWest) { + uint32 extraID = 0xffffffff; + if (_fillingStationItem) { + switch (_fillingStationItem->getObjectID()) { + case kArgonCanister: + if (GameState.getNoradFillingStationOn()) + extraID = kN01WZArgonCanisterLit; + else + extraID = kN01WZArgonCanisterDim; + break; + case kGasCanister: + if (GameState.getNoradFillingStationOn()) + extraID = kN01WZGasCanisterLit; + else + extraID = kN01WZGasCanisterDim; + break; + case kAirMask: + if (GameState.getNoradFillingStationOn()) + extraID = kN01WZAirMaskLit; + else + extraID = kN01WZAirMaskDim; + break; + case kNitrogenCanister: + if (GameState.getNoradFillingStationOn()) + extraID = kN01WZNitrogenCanisterLit; + else + extraID = kN01WZNitrogenCanisterDim; + break; + default: + // Should never happen. + break; + } + } else if (GameState.getNoradFillingStationOn()) { + extraID = kN01WZEmptyLit; + } + + if (extraID == 0xffffffff) { + return Norad::getViewTime(room, direction); + } else { + getExtraEntry(extraID, entry); + return entry.movieStart; + } + } + + return Norad::getViewTime(room, direction); +} + +void NoradAlpha::turnOnFillingStation() { + if (GameState.getCurrentRoom() == kNorad01West && !GameState.getNoradFillingStationOn()) { + GameState.setNoradFillingStationOn(true); + updateViewFrame(); + } +} + +void NoradAlpha::turnOffFillingStation() { + if (GameState.getCurrentRoom() == kNorad01West && GameState.getNoradFillingStationOn()) { + GameState.setNoradFillingStationOn(false); + updateViewFrame(); + } +} + +void NoradAlpha::activateHotspots() { + Norad::activateHotspots(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad01West, kWest): + if (_vm->getDragType() == kDragInventoryUse) { + if (!_fillingStationItem) { + ItemID itemID = _vm->getDraggingItem()->getObjectID(); + if (itemID == kArgonCanister || itemID == kGasCanister || itemID == kAirMask || + itemID == kNitrogenCanister) + _vm->getAllHotspots().activateOneHotspot(kN01GasOutletSpotID); + } + } else { + HotSpotID spotID; + + if (_fillingStationItem) { + switch (_fillingStationItem->getObjectID()) { + case kArgonCanister: + spotID = kN01ArgonCanisterSpotID; + _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID); + break; + case kGasCanister: + spotID = kN01GasCanisterSpotID; + break; + case kAirMask: + spotID = kN01AirMaskSpotID; + _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID); + break; + case kNitrogenCanister: + spotID = kN01NitrogenCanisterSpotID; + _vm->getAllHotspots().deactivateOneHotspot(kNorad01GasOutSpotID); + break; + default: + // Should never happen. + spotID = kNoHotSpotID; + break; + } + _vm->getAllHotspots().activateOneHotspot(spotID); + } + } + break; + case MakeRoomView(kNorad10, kEast): + if (GameState.isCurrentDoorOpen()) + _vm->getAllHotspots().deactivateOneHotspot(kNorad10DoorSpotID); + break; + case MakeRoomView(kNorad21, kWest): + if (GameState.isCurrentDoorOpen()) + _vm->getAllHotspots().deactivateOneHotspot(kNorad21WestSpotID); + break; + } +} + +void NoradAlpha::clickInHotspot(const Input &input, const Hotspot *cursorSpot) { + Norad::clickInHotspot(input, cursorSpot); + + if (_vm->getDragType() == kDragInventoryUse) { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad01West, kWest)) { + Item *item = _vm->getDraggingItem(); + if (item->getObjectID() == kAirMask || item->getObjectID() == kArgonCanister || + item->getObjectID() == kNitrogenCanister || item->getObjectID() == kGasCanister) { + HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasOutletSpotID); + hotspotEntry->hotspotItem = item->getObjectID(); + } + } + } +} + +void NoradAlpha::takeItemFromRoom(Item *item) { + if (GameState.getCurrentRoom() == kNorad01West) { + if (_fillingStationItem == item) { + _fillingStationItem = 0; + GameState.setNoradGassed(false); + loadAmbientLoops(); + ((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(0); + forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal); + } + } + + Norad::takeItemFromRoom(item); +} + +void NoradAlpha::dropItemIntoRoom(Item *item, Hotspot *droppedSpot) { + if (GameState.getCurrentRoom() == kNorad01West) { + if (!_fillingStationItem) { + _fillingStationItem = item; + ((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(item); + } + } + + Norad::dropItemIntoRoom(item, droppedSpot); +} + +void NoradAlpha::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, + HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, + HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) { + outSpotID = kNorad22MonitorOutSpotID; + prepSpotID = kNorad22LaunchPrepSpotID; + clawControlSpotID = kNorad22ClawControlSpotID; + pinchClawSpotID = kNorad22ClawPinchSpotID; + moveClawDownSpotID = kNorad22ClawDownSpotID; + moveClawRightSpotID = kNorad22ClawRightSpotID; + moveClawLeftSpotID = kNorad22ClawLeftSpotID; + moveClawUpSpotID = kNorad22ClawUpSpotID; + clawCCWSpotID = kNorad22ClawCCWSpotID; + clawCWSpotID = kNorad22ClawCWSpotID; + clawPosition = kClawAtD; + clawExtraIDs = _noradAlphaClawExtras; +} + +Hotspot *NoradAlpha::getItemScreenSpot(Item *item, DisplayElement *element) { + switch (item->getObjectID()) { + case kGasCanister: + return _vm->getAllHotspots().findHotspotByID(kN01GasCanisterSpotID); + case kAirMask: + return _vm->getAllHotspots().findHotspotByID(kN01AirMaskSpotID); + case kArgonCanister: + return _vm->getAllHotspots().findHotspotByID(kN01ArgonCanisterSpotID); + case kNitrogenCanister: + return _vm->getAllHotspots().findHotspotByID(kN01NitrogenCanisterSpotID); + } + + return Norad::getItemScreenSpot(item, element); +} + +Common::String NoradAlpha::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + RoomID room = GameState.getCurrentRoom(); + if (room >= kNorad01 && room <= kNorad01West) + return "Images/AI/Norad/XNE1"; + else if ((room >= kNorad02 && room <= kNorad19West)) + return "Images/AI/Norad/XNE2"; + + return "Images/AI/Norad/XNE3"; + } + + return movieName; +} + +uint NoradAlpha::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad01, kNorth): + case MakeRoomView(kNorad01, kSouth): + case MakeRoomView(kNorad01, kEast): + case MakeRoomView(kNorad01, kWest): + case MakeRoomView(kNorad01East, kEast): + case MakeRoomView(kNorad01West, kWest): + if (GameState.getNoradGassed()) { + if (g_airMask->isAirFilterOn()) + numHints = 0; + else + numHints = 3; + } else { + numHints = 2; + } + break; + case MakeRoomView(kNorad19West, kWest): + if (getSubPrepFailed() && GameState.getNoradSubPrepState() != kSubPrepped) + numHints = 1; + break; + case MakeRoomView(kNorad22, kWest): + numHints = 1; + break; + } + } + + return numHints; +} + +Common::String NoradAlpha::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad01, kNorth): + case MakeRoomView(kNorad01, kSouth): + case MakeRoomView(kNorad01, kEast): + case MakeRoomView(kNorad01, kWest): + case MakeRoomView(kNorad01East, kEast): + case MakeRoomView(kNorad01West, kWest): + switch (hintNum) { + case 1: + if (GameState.getNoradGassed()) + return "Images/AI/Norad/XN01SW"; + + return "Images/AI/Norad/XN01WD2"; + case 2: + if (GameState.getNoradGassed()) { + if (_vm->playerHasItemID(kAirMask)) + // Mask must not be on if we get here... + return "Images/AI/Globals/XGLOB1A"; + + return "Images/AI/Globals/XGLOB3D"; + } + + return "Images/AI/Globals/XGLOB5C"; + case 3: + return "Images/AI/Norad/XN01SH"; + } + break; + case MakeRoomView(kNorad19West, kWest): + return "Images/AI/Norad/XN19NH"; + case MakeRoomView(kNorad22, kWest): + return "Images/AI/Globals/XGLOB1C"; + } + } + + return movieName; +} + +void NoradAlpha::closeDoorOffScreen(const RoomID room, const DirectionConstant) { + switch (room) { + case kNorad12: + case kNorad13: + case kNorad18: + case kNorad19: + playSpotSoundSync(kAlphaElevatorDoorCloseIn, kAlphaElevatorDoorCloseOut); + break; + default: + playSpotSoundSync(kAlphaRegDoorCloseIn, kAlphaRegDoorCloseOut); + break; + } +} + +void NoradAlpha::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { + if (room == kNorad01 && direction == kSouth) + spotEntry.clear(); + else + Norad::findSpotEntry(room, direction, flags, spotEntry); +} + +bool NoradAlpha::canSolve() { + return Norad::canSolve() || getHintMovie(1) == "Images/AI/Norad/XN01SW"; +} + +void NoradAlpha::doSolve() { + Norad::doSolve(); + + if (getHintMovie(1) == "Images/AI/Norad/XN01SW") { + _vm->addItemToInventory(g_airMask); + g_airMask->putMaskOn(); + } +} + +Common::String NoradAlpha::getNavMovieName() { + return "Images/Norad Alpha/Norad Alpha.movie"; +} + +Common::String NoradAlpha::getSoundSpotsName() { + return "Sounds/Norad/Norad Alpha Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.h b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h new file mode 100644 index 0000000000..582d6c2bb3 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h @@ -0,0 +1,115 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H + +#include "pegasus/neighborhood/norad/norad.h" + +namespace Pegasus { + +class Item; + +class NoradAlpha : public Norad { +public: + NoradAlpha(InputHandler *, PegasusEngine *); + virtual ~NoradAlpha() {} + + virtual void init(); + void start(); + + virtual bool okayToJump(); + + void playClawMonitorIntro(); + + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + + void turnOnFillingStation(); + void turnOffFillingStation(); + Item *getFillingItem() { return _fillingStationItem; } + bool gasCanisterIntake(); + + virtual void takeItemFromRoom(Item *); + virtual void dropItemIntoRoom(Item *, Hotspot *); + + virtual GameInteraction *makeInteraction(const InteractionID); + + virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, + HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, + HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID, + HotSpotID &clawCWSpotID, uint32 &, const uint32 *&); + + void loadAmbientLoops(); + + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void setUpAIRules(); + + void setSubPrepFailed(bool value) { _subPrepFailed = value; } + bool getSubPrepFailed() { return _subPrepFailed; } + + void closeDoorOffScreen(const RoomID, const DirectionConstant); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + void clickInHotspot(const Input &, const Hotspot *); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool canSolve(); + void doSolve(); + +protected: + static const uint32 _noradAlphaClawExtras[22]; + + virtual void arriveAtNorad01(); + virtual void arriveAtNorad01East(); + virtual void arriveAtNorad01West(); + virtual void arriveAtNorad04(); + virtual void arriveAtNorad22(); + + virtual void arriveAt(const RoomID, const DirectionConstant); + + virtual void getZoomEntry(const HotSpotID, ZoomTable::Entry &); + virtual TimeValue getViewTime(const RoomID, const DirectionConstant); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + virtual void activateHotspots(); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + + void bumpIntoWall(); + + Item *_fillingStationItem; + + bool _subPrepFailed; + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/panorama.cpp b/engines/pegasus/neighborhood/norad/alpha/panorama.cpp new file mode 100644 index 0000000000..5a717a84e7 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panorama.cpp @@ -0,0 +1,239 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/macresman.h" +#include "common/stream.h" +#include "pegasus/neighborhood/norad/alpha/panorama.h" + +namespace Pegasus { + +Panorama::Panorama() : _panoramaMovie(kNoDisplayElement) { + blankFields(); +} + +Panorama::~Panorama() { + releasePanorama(); +} + +void Panorama::blankFields() { + _viewBounds = Common::Rect(); + _drawBounds = Common::Rect(); + _mask = 0; + _panoramaWidth = 0; + _panoramaHeight = 0; + _stripWidth = 0; + _stripLeft = -1; + _stripRight = -1; +} + +void Panorama::releasePanorama() { + if (_panoramaMovie.isMovieValid()) { + _panoramaMovie.releaseMovie(); + _panoramaWorld.deallocateSurface(); + blankFields(); + } +} + +static const uint32 kPanoramaResType = MKTAG('P', 'a', 'n', 'I'); // Panorama Information. +static const uint16 kPanoramaResID = 128; + +void Panorama::initFromMovieFile(const Common::String &fileName) { + // First, we need the resource fork for other reasons -- PanI resource + Common::MacResManager *resFork = new Common::MacResManager(); + if (!resFork->open(fileName) || !resFork->hasResFork()) + error("Could not open the resource fork of '%s'", fileName.c_str()); + + Common::SeekableReadStream *resource = resFork->getResource(kPanoramaResType, kPanoramaResID); + if (!resource) + error("No panorama information in the resource fork of '%s'", fileName.c_str()); + + _panoramaWidth = resource->readUint16BE(); + _panoramaHeight = resource->readUint16BE(); + _stripWidth = resource->readUint16BE(); + + delete resource; + delete resFork; + + // Now we open the movie like normal + _panoramaMovie.initFromMovieFile(fileName); +} + +void Panorama::setMask(Surface *mask) { + _mask = mask; +} + +// If the panorama is not open, do nothing and return. +// Otherwise, set up the view bounds. +void Panorama::setViewBounds(const Common::Rect &newView) { + if (!isPanoramaOpen()) + return; + + if (newView.isEmpty()) + return; + + Common::Rect r = newView; + + if (r.width() > _panoramaWidth) { + r.left = 0; + r.right = _panoramaWidth; + } else { + if (r.right > _panoramaWidth) + r.translate(_panoramaWidth - r.right, 0); + + if (r.left < 0) + r.translate(-r.left, 0); + } + + if (r.height() > _panoramaHeight) { + r.top = 0; + r.bottom = _panoramaHeight; + } else { + if (r.bottom > _panoramaHeight) + r.translate(0, _panoramaHeight - r.bottom); + + if (r.top < 0) + r.translate(0, -r.top); + } + + if (_viewBounds != r) { + CoordType stripLeft = 0; + + if (r.width() != _viewBounds.width() || !_panoramaWorld.isSurfaceValid()) { + _panoramaWorld.deallocateSurface(); + makeNewSurface(r); + } else { + CoordType stripRight; + calcStripRange(r, stripLeft, stripRight); + loadStrips(stripLeft, stripRight); + } + + _viewBounds = r; + _drawBounds = r; + _drawBounds.translate(-stripLeft * _stripWidth, 0); + } +} + +void Panorama::getViewBounds(Common::Rect &r) const { + r = _viewBounds; +} + +void Panorama::getPanoramaBounds(Common::Rect &r) const { + r = Common::Rect(0, 0, _panoramaWidth, _panoramaHeight); +} + +void Panorama::drawPanorama(const Common::Rect &destRect) { + if (_panoramaWorld.isSurfaceValid()) { + if (_mask) + _panoramaWorld.copyToCurrentPortMasked(_drawBounds, destRect, _mask); + else + _panoramaWorld.copyToCurrentPortTransparent(_drawBounds, destRect); + } +} + +// Make a new Surface big enough to show r, which is assumed to be a valid view bounds. +// Assumptions: +// r is a valid view bounds. +// _panoramaWorld is not allocated. +// _panoramaHeight, _stripWidth is correct. +// _panoramaMovie is allocated. +void Panorama::makeNewSurface(const Common::Rect& view) { + CoordType stripLeft, stripRight; + calcStripRange(view, stripLeft, stripRight); + + Common::Rect r(0, 0, (stripRight - stripLeft + 1) * _stripWidth, _panoramaHeight); + _panoramaWorld.allocateSurface(r); + _panoramaMovie.shareSurface(&_panoramaWorld); + loadStrips(stripLeft, stripRight); +} + +// Assumes view is not empty. +void Panorama::calcStripRange(const Common::Rect &view, CoordType &stripLeft, CoordType &stripRight) { + stripLeft = view.left / _stripWidth; + stripRight = (view.left - view.left % _stripWidth + _stripWidth - 1 + view.width()) / _stripWidth; +} + +// Load in all needed strips to put range (stripLeft, stripRight) into the +// panorama's Surface. Try to optimize by saving any pixels already in the Surface. +// Assumptions: +// Surface is allocated and is big enough for maximum range of +// stripLeft and stripRight +void Panorama::loadStrips(CoordType stripLeft, CoordType stripRight) { + if (_stripLeft == -1) { + // Surface has just been allocated. + // Load in all strips. + for (CoordType i = stripLeft; i <= stripRight; i++) + loadOneStrip(i, stripLeft); + + _stripLeft = stripLeft; + _stripRight = stripRight; + } else if (stripLeft != _stripLeft) { + CoordType overlapLeft = MAX(stripLeft, _stripLeft); + CoordType overlapRight = MIN(stripRight, _stripRight); + + if (overlapLeft <= overlapRight) { + Common::Rect r1((overlapLeft - _stripLeft) * _stripWidth, 0, + (overlapRight - _stripLeft + 1) * _stripWidth, _panoramaHeight); + + if (stripLeft < _stripLeft) { + Common::Rect bounds; + _panoramaWorld.getSurfaceBounds(bounds); + _panoramaWorld.getSurface()->move(bounds.right - r1.right, 0, _panoramaHeight); + + for (CoordType i = stripLeft; i < _stripLeft; i++) + loadOneStrip(i, stripLeft); + } else { + _panoramaWorld.getSurface()->move(-r1.left, 0, _panoramaHeight); + + for (CoordType i = _stripRight + 1; i <= stripRight; i++) + loadOneStrip(i, stripLeft); + } + } else { + // No overlap. + // Load everything. + for (CoordType i = stripLeft; i <= stripRight; i++) + loadOneStrip(i, stripLeft); + } + + _stripLeft = stripLeft; + _stripRight = stripRight; + } else if (stripRight > _stripRight) { + // Need to add one or more strips. + for (CoordType i = _stripRight + 1; i <= stripRight; i++) + loadOneStrip(i, _stripLeft); + + _stripRight = stripRight; + } else if (stripRight < _stripRight) { + // Need to chop off one strip. + _stripRight = stripRight; + } +} + +void Panorama::loadOneStrip(CoordType stripToLoad, CoordType leftStrip) { + _panoramaMovie.moveMovieBoxTo((stripToLoad - leftStrip) * _stripWidth, 0); + _panoramaMovie.setTime(stripToLoad, 1); + _panoramaMovie.redrawMovieWorld(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/panorama.h b/engines/pegasus/neighborhood/norad/alpha/panorama.h new file mode 100644 index 0000000000..87c7b3bd4e --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panorama.h @@ -0,0 +1,98 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H + +#include "pegasus/movie.h" + +namespace Pegasus { + +/* + + Panorama implements a wide image using a specially constructed movie file. + The movie holds the image as a series of vertical strips, say 16 or 32 pixels wide. + + The panorama bounds defines the entire panorama. The view bounds represents the + area on the panorama that is kept in memory. + + The panorama bounds is also stored in the movie file; it cannot be changed. The + view bounds must always be a subset of the panorama bounds. + + In actuality, the area kept in memory is at least as wide as the view bounds (but + may be wider to coincide with the width of the movies slices), and is as tall as + the panorama bounds. The view bounds is used by the drawPanorama function to draw + a piece of the panorama to the current screen. + + The panorama movie is built at a time scale of 1, with each strip lasting for one + second, so that strip number corresponds exactly with the time value at which the + strip is stored. + + TO USE: + + Call one initFromMovieFile to open the movie. Then set up a view rect by + calling setViewBounds. Once these two functions have been called, drawPanorama + will draw the panorama. + +*/ + +class Panorama { +public: + Panorama(); + virtual ~Panorama(); + + void initFromMovieFile(const Common::String &); + void releasePanorama(); + bool isPanoramaOpen() { return _panoramaMovie.isMovieValid(); } + + void setViewBounds(const Common::Rect &); + void getViewBounds(Common::Rect &) const; + + void setMask(Surface *); + + void getPanoramaBounds(Common::Rect &) const; + + void drawPanorama(const Common::Rect &); + +protected: + void blankFields(); + void makeNewSurface(const Common::Rect &); + void calcStripRange(const Common::Rect &, CoordType &, CoordType &); + void loadStrips(CoordType, CoordType); + void loadOneStrip(CoordType, CoordType); + + Movie _panoramaMovie; + Surface _panoramaWorld, *_mask; + Common::Rect _viewBounds; + Common::Rect _drawBounds; + CoordType _panoramaWidth, _panoramaHeight; + CoordType _stripWidth; // Pixels per strip. + CoordType _numStrips; + CoordType _stripLeft, _stripRight; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp new file mode 100644 index 0000000000..7865bbb442 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/norad/alpha/panoramascroll.h" + +namespace Pegasus { + +PanoramaScroll::PanoramaScroll(const DisplayElementID id) : IdlerAnimation(id) { + _boundsWidth = 0; + _totalWidth = 0; +} + +void PanoramaScroll::initFromMovieFile(const Common::String &fileName) { + _panorama.initFromMovieFile(fileName); + + Common::Rect r; + _panorama.getPanoramaBounds(r); + _totalWidth = r.width(); +} + +void PanoramaScroll::initMaskFromPICTFile(const Common::String &fileName) { + if (!_panorama.isPanoramaOpen()) + return; + + _mask.getImageFromPICTFile(fileName); + _panorama.setMask(&_mask); +} + +void PanoramaScroll::releasePanorama() { + if (_panorama.isPanoramaOpen()) + _panorama.releasePanorama(); + + _mask.deallocateSurface(); +} + +void PanoramaScroll::setBounds(const Common::Rect &r) { + Animation::setBounds(r); + + _boundsWidth = r.width(); + + Common::Rect r2; + _panorama.getViewBounds(r2); + r2.right = r2.left + _boundsWidth; + r2.bottom = r2.top + r.height(); + _panorama.setViewBounds(r2); +} + +void PanoramaScroll::draw(const Common::Rect &) { + _panorama.drawPanorama(_bounds); +} + +void PanoramaScroll::timeChanged(const TimeValue newTime) { + CoordType leftPixel = (_totalWidth - _boundsWidth) * newTime / getDuration(); + + Common::Rect r; + _panorama.getViewBounds(r); + if (leftPixel != r.left) { + _panorama.getViewBounds(r); + r.moveTo(leftPixel, 0); + _panorama.setViewBounds(r); + triggerRedraw(); + } +} + +void PanoramaScroll::getPanoramaBounds(Common::Rect &r) const { + _panorama.getPanoramaBounds(r); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h new file mode 100644 index 0000000000..6a3e1515e2 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H + +#include "pegasus/neighborhood/norad/alpha/panorama.h" + +namespace Pegasus { + +class PanoramaScroll : public IdlerAnimation { +public: + PanoramaScroll(const DisplayElementID); + virtual ~PanoramaScroll() {} + + void initFromMovieFile(const Common::String &); + void initMaskFromPICTFile(const Common::String &); + + void releasePanorama(); + + bool isPanoramaOpen() { return _panorama.isPanoramaOpen(); } + void getPanoramaBounds(Common::Rect &) const; + + void setBounds(const Common::Rect&); + + void draw(const Common::Rect &); + +protected: + void timeChanged(const TimeValue); + + Panorama _panorama; + Surface _mask; + CoordType _totalWidth, _boundsWidth; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/constants.h b/engines/pegasus/neighborhood/norad/constants.h new file mode 100644 index 0000000000..37c1769309 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/constants.h @@ -0,0 +1,755 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H +#define PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H + +#include "pegasus/constants.h" + +namespace Pegasus { + +// Norad Alpha spot constants + +static const TimeValue kAlphaBumpIntoWallIn = 0; +static const TimeValue kAlphaBumpIntoWallOut = 303; + +static const TimeValue kAlphaAccessDeniedIn = 303; +static const TimeValue kAlphaAccessDeniedOut = 3045; + +static const TimeValue kAlphaRegDoorCloseIn = 3045; +static const TimeValue kAlphaRegDoorCloseOut = 4476; + +static const TimeValue kAlphaElevatorDoorCloseIn = 4476; +static const TimeValue kAlphaElevatorDoorCloseOut = 5071; + +static const TimeValue kAlphaCantTransportIn = 5071; +static const TimeValue kAlphaCantTransportOut = 9348; + +static const TimeValue kAlphaPressureDoorIntro1In = 9348; +static const TimeValue kAlphaPressureDoorIntro1Out = 11061; + +static const TimeValue kAlphaPressureDoorIntro2In = 11061; +static const TimeValue kAlphaPressureDoorIntro2Out = 14098; + +static const TimeValue kN22ReplyIn = 14098; +static const TimeValue kN22ReplyOut = 18442; + +static const TimeValue kAlphaLoadClawIntroIn = 18442; +static const TimeValue kAlphaLoadClawIntroOut = 20698; + +// Norad Delta spot constants + +static const TimeValue kDeltaBumpIntoWallIn = 0; +static const TimeValue kDeltaBumpIntoWallOut = 303; + +static const TimeValue kDeltaAccessDeniedIn = 303; +static const TimeValue kDeltaAccessDeniedOut = 3045; + +static const TimeValue kDeltaRegDoorCloseIn = 3045; +static const TimeValue kDeltaRegDoorCloseOut = 4476; + +static const TimeValue kDeltaElevatorDoorCloseIn = 4476; +static const TimeValue kDeltaElevatorDoorCloseOut = 5071; + +static const TimeValue kPressureDoorIntro1In = 5071; +static const TimeValue kPressureDoorIntro1Out = 6784; + +static const TimeValue kPressureDoorIntro2In = 6784; +static const TimeValue kPressureDoorIntro2Out = 9821; + +static const TimeValue kLoadClawIntroIn = 9821; +static const TimeValue kLoadClawIntroOut = 12077; + +static const TimeValue kHoldForRetinalIn = 12077; +static const TimeValue kHoldForRetinalOut = 14104; + +static const TimeValue kRetinalScanFailedIn = 14104; +static const TimeValue kRetinalScanFailedOut = 17538; + +static const TimeValue kAddisAbabaIn = 17538; +static const TimeValue kAddisAbabaOut = 19263; + +static const TimeValue kBangkokIn = 19263; +static const TimeValue kBangkokOut = 20201; + +static const TimeValue kBonnIn = 20201; +static const TimeValue kBonnOut = 20915; + +static const TimeValue kDublinIn = 20915; +static const TimeValue kDublinOut = 21660; + +static const TimeValue kHonoluluIn = 21660; +static const TimeValue kHonoluluOut = 22498; + +static const TimeValue kMadridIn = 22498; +static const TimeValue kMadridOut = 23474; + +static const TimeValue kReykjavikIn = 23474; +static const TimeValue kReykjavikOut = 24488; + +static const TimeValue kSanAntonioIn = 24488; +static const TimeValue kSanAntonioOut = 25561; + +static const TimeValue kSeoulIn = 25561; +static const TimeValue kSeoulOut = 26461; + +static const TimeValue kSvortalskIn = 26461; +static const TimeValue kSvortalskOut = 27582; + +static const TimeValue kSiloBeepIn = 27582; +static const TimeValue kSiloBeepOut = 27721; + +static const TimeValue kAllSilosDeactivatedIn = 27721; +static const TimeValue kAllSilosDeactivatedOut = 28928; + +static const TimeValue kGlobalLaunchOverrideIn = 28928; +static const TimeValue kGlobalLaunchOverrideOut = 30736; + +static const TimeValue kLaunchSiloSelectedIn = 30736; +static const TimeValue kLaunchSiloSelectedOut = 31660; + +static const TimeValue kLaunchToProceedIn = 31660; +static const TimeValue kLaunchToProceedOut = 32536; + +static const TimeValue kMaximumDeactivationIn = 32536; +static const TimeValue kMaximumDeactivationOut = 34337; + +static const TimeValue kMissileLaunchedIn = 34337; +static const TimeValue kMissileLaunchedOut = 35082; + +static const TimeValue kNewLaunchSiloIn = 35082; +static const TimeValue kNewLaunchSiloOut = 36320; + +static const TimeValue kStrikeAuthorizedIn = 36320; +static const TimeValue kStrikeAuthorizedOut = 37393; + +static const TimeValue kPrimaryTargetIn = 37393; +static const TimeValue kPrimaryTargetOut = 38628; + +static const TimeValue kSiloDeactivatedIn = 38628; +static const TimeValue kSiloDeactivatedOut = 39566; + +static const TimeValue kStrikeCodeRejectedIn = 39566; +static const TimeValue kStrikeCodeRejectedOut = 41056; + +static const TimeValue kToDeactivateIn = 41056; +static const TimeValue kToDeactivateOut = 46494; + +static const TimeValue kTwoMinutesIn = 46494; +static const TimeValue kTwoMinutesOut = 47166; + +static const TimeValue kOneMinuteIn = 47166; +static const TimeValue kOneMinuteOut = 47856; + +static const TimeValue kFiftySecondsIn = 47856; +static const TimeValue kFiftySecondsOut = 48691; + +static const TimeValue kFortySecondsIn = 48691; +static const TimeValue kFortySecondsOut = 49500; + +static const TimeValue kThirtySecondsIn = 49500; +static const TimeValue kThirtySecondsOut = 50362; + +static const TimeValue kTwentySecondsIn = 50362; +static const TimeValue kTwentySecondsOut = 51245; + +static const TimeValue kTenSecondsIn = 51245; +static const TimeValue kTenSecondsOut = 52069; + +static const TimeValue kGiveUpHumanIn = 52069; +static const TimeValue kGiveUpHumanOut = 55023; + +static const TimeValue kIJustBrokeIn = 55023; +static const TimeValue kIJustBrokeOut = 59191; + +static const TimeValue kTheOnlyGoodHumanIn = 59191; +static const TimeValue kTheOnlyGoodHumanOut = 62379; + +static const TimeValue kYouAreRunningIn = 62379; +static const TimeValue kYouAreRunningOut = 64201; + +static const TimeValue kYouCannotPossiblyIn = 64201; +static const TimeValue kYouCannotPossiblyOut = 65740; + +static const TimeValue kYouWillFailIn = 65740; +static const TimeValue kYouWillFailOut = 67217; + +static const CanOpenDoorReason kCantOpenBadPressure = kCantOpenLastReason + 1; + +static const NotificationFlags kAirTimerExpiredFlag = kLastNeighborhoodNotificationFlag << 1; + +static const uint16 kNoradWarningVolume = 0x100 / 3; +static const uint16 kNoradSuckWindVolume = 0x100 / 2; + +static const int16 kElevatorCompassAngle = -40; +static const int16 kSubPlatformCompassAngle = 45; +static const int16 kSubControlCompassAngle = -10; + +// Norad interactions. + +static const InteractionID kNoradGlobeGameInteractionID = 0; +static const InteractionID kNoradECRMonitorInteractionID = 1; +static const InteractionID kNoradFillingStationInteractionID = 2; +static const InteractionID kNoradElevatorInteractionID = 3; +static const InteractionID kNoradPressureDoorInteractionID = 4; +static const InteractionID kNoradSubControlRoomInteractionID = 5; +static const InteractionID kNoradSubPlatformInteractionID = 6; + +///////////////////////////////////////////// +// +// Norad Alpha + +static const CoordType kECRSlideShowLeft = kNavAreaLeft + 78; +static const CoordType kECRSlideShowTop = kNavAreaTop + 1; + +static const CoordType kECRPanLeft = kNavAreaLeft + 78 + 5; +static const CoordType kECRPanTop = kNavAreaTop + 1 + 4; +static const CoordType kECRPanRight = kECRPanLeft + 213; +static const CoordType kECRPanBottom = kECRPanTop + 241; + +static const CoordType kNoradAlphaElevatorControlsLeft = kNavAreaLeft + 332; +static const CoordType kNoradAlphaElevatorControlsTop = kNavAreaTop + 127; + +static const CoordType kNoradAlpha01LeftSideLeft = kNavAreaLeft + 0; +static const CoordType kNoradAlpha01LeftSideTop = kNavAreaTop + 0; + +static const CoordType kNoradAlpha01RightSideLeft = kNavAreaLeft + 240; +static const CoordType kNoradAlpha01RightSideTop = kNavAreaTop + 12; + +static const CoordType kNoradUpperLevelsLeft = kNavAreaLeft + 98; +static const CoordType kNoradUpperLevelsTop = kNavAreaTop + 31; + +static const CoordType kNoradUpperTypeLeft = kNoradUpperLevelsLeft + 114; +static const CoordType kNoradUpperTypeTop = kNoradUpperLevelsTop + 8; + +static const CoordType kNoradUpperUpLeft = kNavAreaLeft + 361; +static const CoordType kNoradUpperUpTop = kNavAreaTop + 32; + +static const CoordType kNoradUpperDownLeft = kNavAreaLeft + 367; +static const CoordType kNoradUpperDownTop = kNavAreaTop + 66; + +static const CoordType kNoradLowerLevelsLeft = kNavAreaLeft + 74; +static const CoordType kNoradLowerLevelsTop = kNavAreaTop + 157; + +static const CoordType kNoradLowerTypeLeft = kNoradLowerLevelsLeft + 144; +static const CoordType kNoradLowerTypeTop = kNoradLowerLevelsTop + 9; + +static const CoordType kNoradLowerUpLeft = kNavAreaLeft + 380; +static const CoordType kNoradLowerUpTop = kNavAreaTop + 164; + +static const CoordType kNoradLowerDownLeft = kNavAreaLeft + 388; +static const CoordType kNoradLowerDownTop = kNavAreaTop + 212; + +static const CoordType kNoradPlatformLeft = kNavAreaLeft + 36; +static const CoordType kNoradPlatformTop = kNavAreaTop + 87; + +static const CoordType kNoradSubControlLeft = kNavAreaLeft + 0; +static const CoordType kNoradSubControlTop = kNavAreaTop + 84; + +static const CoordType kNoradSubControlPinchLeft = kNoradSubControlLeft + 106; +static const CoordType kNoradSubControlPinchTop = kNoradSubControlTop + 86; + +static const CoordType kNoradSubControlDownLeft = kNoradSubControlLeft + 66; +static const CoordType kNoradSubControlDownTop = kNoradSubControlTop + 106; + +static const CoordType kNoradSubControlRightLeft = kNoradSubControlLeft + 83; +static const CoordType kNoradSubControlRightTop = kNoradSubControlTop + 90; + +static const CoordType kNoradSubControlLeftLeft = kNoradSubControlLeft + 56; +static const CoordType kNoradSubControlLeftTop = kNoradSubControlTop + 91; + +static const CoordType kNoradSubControlUpLeft = kNoradSubControlLeft + 66; +static const CoordType kNoradSubControlUpTop = kNoradSubControlTop + 81; + +static const CoordType kNoradSubControlCCWLeft = kNoradSubControlLeft + 29; +static const CoordType kNoradSubControlCCWTop = kNoradSubControlTop + 88; + +static const CoordType kNoradSubControlCWLeft = kNoradSubControlLeft + 0; +static const CoordType kNoradSubControlCWTop = kNoradSubControlTop + 89; + +static const CoordType kNoradClawMonitorLeft = kNavAreaLeft + 288; +static const CoordType kNoradClawMonitorTop = kNavAreaTop + 97; + +static const CoordType kNoradGreenBallAtALeft = kNoradClawMonitorLeft + 179; +static const CoordType kNoradGreenBallAtATop = kNoradClawMonitorTop + 82; + +static const CoordType kNoradGreenBallAtBLeft = kNoradClawMonitorLeft + 130; +static const CoordType kNoradGreenBallAtBTop = kNoradClawMonitorTop + 73; + +static const CoordType kNoradGreenBallAtCLeft = kNoradClawMonitorLeft + 110; +static const CoordType kNoradGreenBallAtCTop = kNoradClawMonitorTop + 26; + +static const CoordType kNoradGreenBallAtDLeft = kNoradClawMonitorLeft + 21; +static const CoordType kNoradGreenBallAtDTop = kNoradClawMonitorTop + 49; + +///////////////////////////////////////////// +// +// Norad Delta + +static const CoordType kGlobeMonitorLeft = kNavAreaLeft + 360; +static const CoordType kGlobeMonitorTop = kNavAreaTop + 144; + +static const CoordType kGlobeLeft = kNavAreaLeft + 172; +static const CoordType kGlobeTop = kNavAreaTop; + +static const CoordType kGlobeCircleLeftLeft = kNavAreaLeft + 186; +static const CoordType kGlobeCircleLeftTop = kNavAreaTop + 41; + +static const CoordType kGlobeCircleRightLeft = kNavAreaLeft + 321; +static const CoordType kGlobeCircleRightTop = kNavAreaTop + 41; + +static const CoordType kGlobeCircleUpLeft = kNavAreaLeft + 220; +static const CoordType kGlobeCircleUpTop = kNavAreaTop + 7; + +static const CoordType kGlobeCircleDownLeft = kNavAreaLeft + 220; +static const CoordType kGlobeCircleDownTop = kNavAreaTop + 142; + +static const CoordType kGlobeUpperLeftHiliteLeft = kNavAreaLeft + 207; +static const CoordType kGlobeUpperLeftHiliteTop = kNavAreaTop + 28; + +static const CoordType kGlobeUpperRightHiliteLeft = kNavAreaLeft + 307; +static const CoordType kGlobeUpperRightHiliteTop = kNavAreaTop + 28; + +static const CoordType kGlobeLowerLeftHiliteLeft = kNavAreaLeft + 207; +static const CoordType kGlobeLowerLeftHiliteTop = kNavAreaTop + 128; + +static const CoordType kGlobeLowerRightHiliteLeft = kNavAreaLeft + 307; +static const CoordType kGlobeLowerRightHiliteTop = kNavAreaTop + 128; + +static const CoordType kGlobeLeftMotionHiliteLeft = kNavAreaLeft + 182; +static const CoordType kGlobeLeftMotionHiliteTop = kNavAreaTop + 60; + +static const CoordType kGlobeRightMotionHiliteLeft = kNavAreaLeft + 331; +static const CoordType kGlobeRightMotionHiliteTop = kNavAreaTop + 60; + +static const CoordType kGlobeUpMotionHiliteLeft = kNavAreaLeft + 239; +static const CoordType kGlobeUpMotionHiliteTop = kNavAreaTop + 3; + +static const CoordType kGlobeDownMotionHiliteLeft = kNavAreaLeft + 239; +static const CoordType kGlobeDownMotionHiliteTop = kNavAreaTop + 152; + +static const CoordType kGlobeUpperNamesLeft = kNavAreaLeft + 368; +static const CoordType kGlobeUpperNamesTop = kNavAreaTop + 188; + +static const CoordType kGlobeLowerNamesLeft = kNavAreaLeft + 368; +static const CoordType kGlobeLowerNamesTop = kNavAreaTop + 212; + +static const CoordType kGlobeCountdownLeft = kNavAreaLeft + 478; +static const CoordType kGlobeCountdownTop = kNavAreaTop + 164; + +// Norad Alpha display IDs. + +static const DisplayElementID kECRSlideShowMovieID = kNeighborhoodDisplayID; +static const DisplayElementID kECRPanID = kECRSlideShowMovieID + 1; +static const DisplayElementID kNoradAlphaDeathMovieID = kECRPanID + 1; +static const DisplayElementID kNoradElevatorControlsID = kNoradAlphaDeathMovieID + 1; +static const DisplayElementID kN01LeftSideID = kNoradElevatorControlsID + 1; +static const DisplayElementID kN01RightSideID = kN01LeftSideID + 1; +static const DisplayElementID kPressureDoorLevelsID = kN01RightSideID + 1; +static const DisplayElementID kPressureDoorTypeID = kPressureDoorLevelsID + 1; +static const DisplayElementID kPressureDoorUpButtonID = kPressureDoorTypeID + 1; +static const DisplayElementID kPressureDoorDownButtonID = kPressureDoorUpButtonID + 1; +static const DisplayElementID kPlatformMonitorID = kPressureDoorDownButtonID + 1; +static const DisplayElementID kSubControlMonitorID = kPlatformMonitorID + 1; +static const DisplayElementID kClawMonitorID = kSubControlMonitorID + 1; +static const DisplayElementID kSubControlPinchID = kClawMonitorID + 1; +static const DisplayElementID kSubControlDownID = kSubControlPinchID + 1; +static const DisplayElementID kSubControlRightID = kSubControlDownID + 1; +static const DisplayElementID kSubControlLeftID = kSubControlRightID + 1; +static const DisplayElementID kSubControlUpID = kSubControlLeftID + 1; +static const DisplayElementID kSubControlCCWID = kSubControlUpID + 1; +static const DisplayElementID kSubControlCWID = kSubControlCCWID + 1; +static const DisplayElementID kClawMonitorGreenBallID = kSubControlCWID + 1; + +// Norad Delta display IDs. + +static const DisplayElementID kGlobeMonitorID = kNeighborhoodDisplayID; +static const DisplayElementID kGlobeMovieID = kGlobeMonitorID + 14; +static const DisplayElementID kGlobeCircleLeftID = kGlobeMovieID + 1; +static const DisplayElementID kGlobeCircleRightID = kGlobeCircleLeftID + 1; +static const DisplayElementID kGlobeCircleUpID = kGlobeCircleRightID + 1; +static const DisplayElementID kGlobeCircleDownID = kGlobeCircleUpID + 1; +static const DisplayElementID kMotionHiliteLeftID = kGlobeCircleDownID + 1; +static const DisplayElementID kMotionHiliteRightID = kMotionHiliteLeftID + 1; +static const DisplayElementID kMotionHiliteUpID = kMotionHiliteRightID + 1; +static const DisplayElementID kMotionHiliteDownID = kMotionHiliteUpID + 1; +static const DisplayElementID kTargetHiliteUpperLeftID = kMotionHiliteDownID + 1; +static const DisplayElementID kTargetHiliteUpperRightID = kTargetHiliteUpperLeftID + 1; +static const DisplayElementID kTargetHiliteLowerLeftID = kTargetHiliteUpperRightID + 1; +static const DisplayElementID kTargetHiliteLowerRightID = kTargetHiliteLowerLeftID + 1; +static const DisplayElementID kGlobeUpperNamesID = kTargetHiliteLowerRightID + 1; +static const DisplayElementID kGlobeLowerNamesID = kGlobeUpperNamesID + 1; +static const DisplayElementID kGlobeCountdownID = kGlobeLowerNamesID + 1; + +// Norad Alpha: + +static const DisplayOrder kECRMonitorOrder = kMonitorLayer; +static const DisplayOrder kECRPanOrder = kECRMonitorOrder + 1; + +static const DisplayOrder kN01LeftSideOrder = kMonitorLayer; +static const DisplayOrder kN01RightSideOrder = kN01LeftSideOrder + 1; + +static const DisplayOrder kElevatorControlsOrder = kMonitorLayer; + +static const DisplayOrder kPressureLevelsOrder = kMonitorLayer; +static const DisplayOrder kPressureTypeOrder = kPressureLevelsOrder + 1; +static const DisplayOrder kPressureUpOrder = kPressureTypeOrder + 1; +static const DisplayOrder kPressureDownOrder = kPressureUpOrder + 1; + +static const DisplayOrder kPlatformOrder = kMonitorLayer; + +static const DisplayOrder kSubControlOrder = kMonitorLayer; +static const DisplayOrder kClawMonitorOrder = kSubControlOrder + 1; +static const DisplayOrder kSubControlPinchOrder = kClawMonitorOrder + 1; +static const DisplayOrder kSubControlDownOrder = kSubControlPinchOrder + 1; +static const DisplayOrder kSubControlRightOrder = kSubControlDownOrder + 1; +static const DisplayOrder kSubControlLeftOrder = kSubControlRightOrder + 1; +static const DisplayOrder kSubControlUpOrder = kSubControlLeftOrder + 1; +static const DisplayOrder kSubControlCCWOrder = kSubControlUpOrder + 1; +static const DisplayOrder kSubControlCWOrder = kSubControlCCWOrder + 1; +static const DisplayOrder kClawMonitorGreenBallOrder = kSubControlCWOrder + 1; + +// Norad Delta: + +static const DisplayOrder kGlobeMonitorLayer = kMonitorLayer; +static const DisplayOrder kGlobeMovieLayer = kGlobeMonitorLayer + 1; +static const DisplayOrder kGlobeCircleLayer = kGlobeMovieLayer + 1; +static const DisplayOrder kGlobeHilitesLayer = kGlobeCircleLayer + 1; +static const DisplayOrder kGlobeUpperNamesLayer = kGlobeHilitesLayer + 1; +static const DisplayOrder kGlobeLowerNamesLayer = kGlobeUpperNamesLayer + 1; +static const DisplayOrder kGlobeCountdownLayer = kGlobeLowerNamesLayer + 1; + +// Norad Alpha Tables + +static const TimeScale kNoradAlphaMovieScale = 600; +static const TimeScale kNoradAlphaFramesPerSecond = 15; +static const TimeScale kNoradAlphaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltNoradAlphaNormal = 0; + +// Room IDs. + +static const RoomID kNorad01 = 0; +static const RoomID kNorad01East = 1; +static const RoomID kNorad01West = 2; +static const RoomID kNorad02 = 3; +static const RoomID kNorad03 = 4; +static const RoomID kNorad04 = 5; +static const RoomID kNorad05 = 6; +static const RoomID kNorad06 = 7; +static const RoomID kNorad07 = 8; +static const RoomID kNorad07North = 9; +static const RoomID kNorad08 = 10; +static const RoomID kNorad09 = 11; +static const RoomID kNorad10 = 12; +static const RoomID kNorad10East = 13; +static const RoomID kNorad11 = 14; +static const RoomID kNorad11South = 15; +static const RoomID kNorad12 = 16; +static const RoomID kNorad12South = 17; +static const RoomID kNorad13 = 18; +static const RoomID kNorad14 = 19; +static const RoomID kNorad15 = 20; +static const RoomID kNorad16 = 21; +static const RoomID kNorad17 = 22; +static const RoomID kNorad18 = 23; +static const RoomID kNorad19 = 24; +static const RoomID kNorad19West = 25; +static const RoomID kNorad21 = 26; +static const RoomID kNorad21West = 27; +static const RoomID kNorad22 = 28; +static const RoomID kNorad22West = 29; + +// Hot Spot Activation IDs. + + +// Hot Spot IDs. + +static const HotSpotID kNorad01ECRSpotID = 5000; +static const HotSpotID kNorad01GasSpotID = 5001; +static const HotSpotID kNorad01ECROutSpotID = 5002; +static const HotSpotID kNorad01GasOutSpotID = 5003; +static const HotSpotID kNorad01MonitorSpotID = 5004; +static const HotSpotID kNorad01IntakeSpotID = 5005; +static const HotSpotID kNorad01DispenseSpotID = 5006; +static const HotSpotID kNorad01ArSpotID = 5007; +static const HotSpotID kNorad01CO2SpotID = 5008; +static const HotSpotID kNorad01HeSpotID = 5009; +static const HotSpotID kNorad01OSpotID = 5010; +static const HotSpotID kNorad01NSpotID = 5011; +static const HotSpotID kN01GasCanisterSpotID = 5012; +static const HotSpotID kN01ArgonCanisterSpotID = 5013; +static const HotSpotID kN01AirMaskSpotID = 5014; +static const HotSpotID kN01NitrogenCanisterSpotID = 5015; +static const HotSpotID kN01GasOutletSpotID = 5016; +static const HotSpotID kNorad07DoorSpotID = 5017; +static const HotSpotID kNorad07DoorOutSpotID = 5018; +static const HotSpotID kNorad10DoorSpotID = 5019; +static const HotSpotID kNorad10EastOutSpotID = 5020; +static const HotSpotID kAlphaUpperPressureDoorUpSpotID = 5021; +static const HotSpotID kAlphaUpperPressureDoorDownSpotID = 5022; +static const HotSpotID kNorad11ElevatorSpotID = 5023; +static const HotSpotID kNorad11ElevatorOutSpotID = 5024; +static const HotSpotID kNorad11ElevatorDownSpotID = 5025; +static const HotSpotID kNorad12ElevatorSpotID = 5026; +static const HotSpotID kNorad12ElevatorOutSpotID = 5027; +static const HotSpotID kNorad12ElevatorUpSpotID = 5028; +static const HotSpotID kNorad19MonitorSpotID = 5029; +static const HotSpotID kNorad19MonitorOutSpotID = 5030; +static const HotSpotID kNorad19ActivateMonitorSpotID = 5031; +static const HotSpotID kNorad21WestSpotID = 5032; +static const HotSpotID kNorad21WestOutSpotID = 5033; +static const HotSpotID kAlphaLowerPressureDoorUpSpotID = 5034; +static const HotSpotID kAlphaLowerPressureDoorDownSpotID = 5035; +static const HotSpotID kNorad22MonitorSpotID = 5036; +static const HotSpotID kNorad22MonitorOutSpotID = 5037; +static const HotSpotID kNorad22LaunchPrepSpotID = 5038; +static const HotSpotID kNorad22ClawControlSpotID = 5039; +static const HotSpotID kNorad22ClawPinchSpotID = 5040; +static const HotSpotID kNorad22ClawDownSpotID = 5041; +static const HotSpotID kNorad22ClawRightSpotID = 5042; +static const HotSpotID kNorad22ClawLeftSpotID = 5043; +static const HotSpotID kNorad22ClawUpSpotID = 5044; +static const HotSpotID kNorad22ClawCCWSpotID = 5045; +static const HotSpotID kNorad22ClawCWSpotID = 5046; + +// Extra sequence IDs. + +static const ExtraID kNoradArriveFromTSA = 0; +static const ExtraID kNorad01RobotTaunt = 1; +static const ExtraID kNorad01ZoomInWithGasCanister = 2; +static const ExtraID kN01WGasCanister = 3; +static const ExtraID kNorad01ZoomOutWithGasCanister = 4; +static const ExtraID kN01WZEmptyLit = 5; +static const ExtraID kN01WZGasCanisterDim = 6; +static const ExtraID kN01WZGasCanisterLit = 7; +static const ExtraID kN01WZArgonCanisterDim = 8; +static const ExtraID kN01WZArgonCanisterLit = 9; +static const ExtraID kN01WZAirMaskDim = 10; +static const ExtraID kN01WZAirMaskLit = 11; +static const ExtraID kN01WZNitrogenCanisterDim = 12; +static const ExtraID kN01WZNitrogenCanisterLit = 13; +static const ExtraID kNorad04EastDeath = 14; +static const ExtraID kNorad19PrepSub = 15; +static const ExtraID kNorad19ExitToSub = 16; +static const ExtraID kNorad22SouthIntro = 17; +static const ExtraID kNorad22SouthReply = 18; +static const ExtraID kNorad22SouthFinish = 19; +static const ExtraID kN22ClawFromAToB = 20; +static const ExtraID kN22ClawALoop = 21; +static const ExtraID kN22ClawAPinch = 22; +static const ExtraID kN22ClawACounterclockwise = 23; +static const ExtraID kN22ClawAClockwise = 24; +static const ExtraID kN22ClawFromBToA = 25; +static const ExtraID kN22ClawFromBToC = 26; +static const ExtraID kN22ClawFromBToD = 27; +static const ExtraID kN22ClawBLoop = 28; +static const ExtraID kN22ClawBPinch = 29; +static const ExtraID kN22ClawBCounterclockwise = 30; +static const ExtraID kN22ClawBClockwise = 31; +static const ExtraID kN22ClawFromCToB = 32; +static const ExtraID kN22ClawCLoop = 33; +static const ExtraID kN22ClawCPinch = 34; +static const ExtraID kN22ClawCCounterclockwise = 35; +static const ExtraID kN22ClawCClockwise = 36; +static const ExtraID kN22ClawFromDToB = 37; +static const ExtraID kN22ClawDLoop = 38; +static const ExtraID kN22ClawDPinch = 39; +static const ExtraID kN22ClawDCounterclockwise = 40; +static const ExtraID kN22ClawDClockwise = 41; + + +// Norad Delta Extra sequence IDs. + +static const ExtraID kArriveFromSubChase = 0; +static const ExtraID kN59ZoomWithRobot = 1; +static const ExtraID kN59RobotApproaches = 2; +static const ExtraID kN59RobotPunchLoop = 3; +static const ExtraID kN59PlayerWins1 = 4; +static const ExtraID kN59PlayerWins2 = 5; +static const ExtraID kN59RobotWins = 6; +static const ExtraID kN59RobotHeadOpens = 7; +static const ExtraID kN59Biochips111 = 8; +static const ExtraID kN59Biochips011 = 9; +static const ExtraID kN59Biochips101 = 10; +static const ExtraID kN59Biochips001 = 11; +static const ExtraID kN59Biochips110 = 12; +static const ExtraID kN59Biochips010 = 13; +static const ExtraID kN59Biochips100 = 14; +static const ExtraID kN59Biochips000 = 15; +static const ExtraID kN59RobotDisappears = 16; +static const ExtraID kN60ClawFromAToB = 17; +static const ExtraID kN60ClawALoop = 18; +static const ExtraID kN60ClawAPinch = 19; +static const ExtraID kN60ClawACounterclockwise = 20; +static const ExtraID kN60ClawAClockwise = 21; +static const ExtraID kN60ClawFromBToA = 22; +static const ExtraID kN60ClawFromBToC = 23; +static const ExtraID kN60ClawFromBToD = 24; +static const ExtraID kN60ClawBLoop = 25; +static const ExtraID kN60ClawBPinch = 26; +static const ExtraID kN60ClawBCounterclockwise = 27; +static const ExtraID kN60ClawBClockwise = 28; +static const ExtraID kN60ClawFromCToB = 29; +static const ExtraID kN60ClawCLoop = 30; +static const ExtraID kN60ClawCPinch = 31; +static const ExtraID kN60ClawCCounterclockwise = 32; +static const ExtraID kN60ClawCClockwise = 33; +static const ExtraID kN60ClawFromDToB = 34; +static const ExtraID kN60ClawDLoop = 35; +static const ExtraID kN60ClawDPinch = 36; +static const ExtraID kN60ClawDCounterclockwise = 37; +static const ExtraID kN60ClawDClockwise = 38; +static const ExtraID kN60RobotApproaches = 39; +static const ExtraID kN60FirstMistake = 40; +static const ExtraID kN60ArmActivated = 41; +static const ExtraID kN60SecondMistake = 42; +static const ExtraID kN60ArmToPositionB = 43; +static const ExtraID kN60ThirdMistake = 44; +static const ExtraID kN60ArmGrabsRobot = 45; +static const ExtraID kN60FourthMistake = 46; +static const ExtraID kN60ArmCarriesRobotToPositionA = 47; +static const ExtraID kN60PlayerFollowsRobotToDoor = 48; +static const ExtraID kN60RobotHeadOpens = 49; +static const ExtraID kN60Biochips111 = 50; +static const ExtraID kN60Biochips011 = 51; +static const ExtraID kN60Biochips101 = 52; +static const ExtraID kN60Biochips001 = 53; +static const ExtraID kN60Biochips110 = 54; +static const ExtraID kN60Biochips010 = 55; +static const ExtraID kN60Biochips100 = 56; +static const ExtraID kN60Biochips000 = 57; +static const ExtraID kN60RobotDisappears = 58; +static const ExtraID kNoradDeltaRetinalScanBad = 59; +static const ExtraID kNoradDeltaRetinalScanGood = 60; +static const ExtraID kN79BrightView = 61; + +// Norad Delta Tables + +static const TimeScale kNoradDeltaMovieScale = 600; +static const TimeScale kNoradDeltaFramesPerSecond = 15; +static const TimeScale kNoradDeltaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltNoradDeltaNormal = 0; + +// Room IDs. + +static const RoomID kNorad41 = 0; +static const RoomID kNorad42 = 1; +static const RoomID kNorad43 = 2; +static const RoomID kNorad44 = 3; +static const RoomID kNorad45 = 4; +static const RoomID kNorad46 = 5; +static const RoomID kNorad47 = 6; +static const RoomID kNorad48 = 7; +static const RoomID kNorad48South = 8; +static const RoomID kNorad49 = 9; +static const RoomID kNorad49South = 10; +static const RoomID kNorad50 = 11; +static const RoomID kNorad50East = 12; +static const RoomID kNorad51 = 13; +static const RoomID kNorad52 = 14; +static const RoomID kNorad53 = 15; +static const RoomID kNorad54 = 16; +static const RoomID kNorad54North = 17; +static const RoomID kNorad55 = 18; +static const RoomID kNorad56 = 19; +static const RoomID kNorad57 = 20; +static const RoomID kNorad58 = 21; +static const RoomID kNorad59 = 22; +static const RoomID kNorad59West = 23; +static const RoomID kNorad60 = 24; +static const RoomID kNorad60West = 25; +static const RoomID kNorad61 = 26; +static const RoomID kNorad62 = 27; +static const RoomID kNorad63 = 28; +static const RoomID kNorad64 = 29; +static const RoomID kNorad65 = 30; +static const RoomID kNorad66 = 31; +static const RoomID kNorad67 = 32; +static const RoomID kNorad68 = 33; +static const RoomID kNorad68West = 34; +static const RoomID kNorad69 = 35; +static const RoomID kNorad78 = 36; +static const RoomID kNorad79 = 37; +static const RoomID kNorad79West = 38; + +// Hot Spot Activation IDs. + + +// Hot Spot IDs. + +static const HotSpotID kNorad48ElevatorSpotID = 5000; +static const HotSpotID kNorad48ElevatorOutSpotID = 5001; +static const HotSpotID kNorad48ElevatorUpSpotID = 5002; +static const HotSpotID kNorad49ElevatorSpotID = 5003; +static const HotSpotID kNorad49ElevatorOutSpotID = 5004; +static const HotSpotID kNorad49ElevatorDownSpotID = 5005; +static const HotSpotID kNorad50DoorSpotID = 5006; +static const HotSpotID kNorad50DoorOutSpotID = 5007; +static const HotSpotID kDeltaUpperPressureDoorUpSpotID = 5008; +static const HotSpotID kDeltaUpperPressureDoorDownSpotID = 5009; +static const HotSpotID kNorad54DoorSpotID = 5010; +static const HotSpotID kNorad54DoorOutSpotID = 5011; +static const HotSpotID kNorad59WestSpotID = 5012; +static const HotSpotID kNorad59WestOutSpotID = 5013; +static const HotSpotID kDeltaLowerPressureDoorUpSpotID = 5014; +static const HotSpotID kDeltaLowerPressureDoorDownSpotID = 5015; +static const HotSpotID kDelta59RobotHeadSpotID = 5016; +static const HotSpotID kDelta59RobotShieldBiochipSpotID = 5017; +static const HotSpotID kDelta59RobotOpMemBiochipSpotID = 5018; +static const HotSpotID kDelta59RobotRetinalBiochipSpotID = 5019; +static const HotSpotID kNorad60MonitorSpotID = 5020; +static const HotSpotID kNorad60MonitorOutSpotID = 5021; +static const HotSpotID kNorad60LaunchPrepSpotID = 5022; +static const HotSpotID kNorad60ClawControlSpotID = 5023; +static const HotSpotID kNorad60ClawPinchSpotID = 5024; +static const HotSpotID kNorad60ClawDownSpotID = 5025; +static const HotSpotID kNorad60ClawRightSpotID = 5026; +static const HotSpotID kNorad60ClawLeftSpotID = 5027; +static const HotSpotID kNorad60ClawUpSpotID = 5028; +static const HotSpotID kNorad60ClawCCWSpotID = 5029; +static const HotSpotID kNorad60ClawCWSpotID = 5030; +static const HotSpotID kDelta60RobotHeadSpotID = 5031; +static const HotSpotID kDelta60RobotShieldBiochipSpotID = 5032; +static const HotSpotID kDelta60RobotOpMemBiochipSpotID = 5033; +static const HotSpotID kDelta60RobotRetinalBiochipSpotID = 5034; +static const HotSpotID kNorad68WestSpotID = 5035; +static const HotSpotID kNorad68WestOutSpotID = 5036; +static const HotSpotID kNorad79WestSpotID = 5037; +static const HotSpotID kNorad79WestOutSpotID = 5038; +static const HotSpotID kNorad79SpinLeftSpotID = 5039; +static const HotSpotID kNorad79SpinRightSpotID = 5040; +static const HotSpotID kNorad79SpinUpSpotID = 5041; +static const HotSpotID kNorad79SpinDownSpotID = 5042; +static const HotSpotID kNorad79SiloAreaSpotID = 5043; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.cpp b/engines/pegasus/neighborhood/norad/delta/globegame.cpp new file mode 100644 index 0000000000..06e40c2b3a --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/globegame.cpp @@ -0,0 +1,1062 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/cursor.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/delta/globegame.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +static const TimeValue kDurationPerFrame = 600 / 15; +static const TimeValue kDurationPerRow = kNumLongSlices * kDurationPerFrame; +static const short kVerticalDuration = 16; + +GlobeTracker::GlobeTracker(Movie *globeMovie, Picture *leftHighlight, Picture *rightHighlight, + Picture *upHighlight, Picture *downHighlight) { + _globeMovie = globeMovie; + _leftHighlight = leftHighlight; + _rightHighlight = rightHighlight; + _upHighlight = upHighlight; + _downHighlight = downHighlight; +} + +void GlobeTracker::setTrackParameters(const Hotspot *trackSpot, GlobeTrackDirection direction) { + _trackSpot = trackSpot; + _trackDirection = direction; + + TimeValue time, newTime, start; + + switch (_trackDirection) { + case kTrackLeft: + time = _globeMovie->getTime(); + + if (((time / kDurationPerRow) & 1) == 0) { + start = (time / kDurationPerRow + 1) * kDurationPerRow; + newTime = start + kDurationPerRow - time % kDurationPerRow; + } else { + start = (time / kDurationPerRow) * kDurationPerRow; + newTime = time; + } + + _globeMovie->setSegment(start, start + kDurationPerRow); + + if (newTime != time) { + _globeMovie->setTime(newTime); + _globeMovie->redrawMovieWorld(); + } + + _globeMovie->setFlags(kLoopTimeBase); + break; + case kTrackRight: + time = _globeMovie->getTime(); + + if (((time / kDurationPerRow) & 1) == 0) { + start = (time / kDurationPerRow) * kDurationPerRow; + newTime = time; + } else { + start = (time / kDurationPerRow - 1) * kDurationPerRow; + newTime = start + kDurationPerRow - time % kDurationPerRow; + } + + _globeMovie->setSegment(start, start + kDurationPerRow); + + if (newTime != time) { + _globeMovie->setTime(newTime); + _globeMovie->redrawMovieWorld(); + } + + _globeMovie->setFlags(kLoopTimeBase); + break; + case kTrackUp: + case kTrackDown: + _globeMovie->setSegment(0, _globeMovie->getDuration()); + _globeMovie->setFlags(0); + break; + } +} + +void GlobeTracker::activateHotspots() { + Tracker::activateHotspots(); + + if (_trackSpot) + g_allHotspots.activateOneHotspot(_trackSpot->getObjectID()); +} + +bool GlobeTracker::stopTrackingInput(const Input &input) { + return !JMPPPInput::isPressingInput(input); +} + +void GlobeTracker::continueTracking(const Input &input) { + Common::Point where; + input.getInputLocation(where); + + if (g_allHotspots.findHotspot(where) == _trackSpot) + trackGlobeMovie(); + else + stopGlobeMovie(); +} + +void GlobeTracker::startTracking(const Input &input) { + Tracker::startTracking(input); + trackGlobeMovie(); +} + +void GlobeTracker::stopTracking(const Input &input) { + Tracker::stopTracking(input); + stopGlobeMovie(); +} + +void GlobeTracker::trackGlobeMovie() { + TimeValue time; + + switch (_trackDirection) { + case kTrackLeft: + if (!_globeMovie->isRunning()) + _globeMovie->start(); + + _leftHighlight->show(); + break; + case kTrackRight: + if (!_globeMovie->isRunning()) + _globeMovie->start(); + + _rightHighlight->show(); + break; + case kTrackUp: + time = _globeMovie->getTime(); + + if (_trackTime == 0) { + _trackTime = tickCount(); + } else if ((int)time - (int)kDurationPerRow * 2 >= 0 && (int)tickCount() >= _trackTime + kVerticalDuration) { + _trackTime = tickCount(); + _globeMovie->setTime(time - kDurationPerRow * 2); + _globeMovie->redrawMovieWorld(); + } + + _upHighlight->show(); + break; + case kTrackDown: + time = _globeMovie->getTime(); + + if (_trackTime == 0) { + _trackTime = tickCount(); + } else if (time + kDurationPerRow * 2 < _globeMovie->getDuration() && (int)tickCount() >= _trackTime + kVerticalDuration) { + _trackTime = tickCount(); + _globeMovie->setTime(time + kDurationPerRow * 2); + _globeMovie->redrawMovieWorld(); + } + + _downHighlight->show(); + break; + } +} + +void GlobeTracker::stopGlobeMovie() { + switch (_trackDirection) { + case kTrackLeft: + _leftHighlight->hide(); + _globeMovie->stop(); + break; + case kTrackRight: + _rightHighlight->hide(); + _globeMovie->stop(); + break; + case kTrackUp: + _upHighlight->hide(); + _trackTime = tickCount() - kVerticalDuration; + break; + case kTrackDown: + _downHighlight->hide(); + _trackTime = tickCount() - kVerticalDuration; + break; + } +} + +// Globe game PICTs: +static const ResIDType kGlobeCircleLeftPICTID = 300; +static const ResIDType kGlobeCircleRightPICTID = 301; +static const ResIDType kGlobeCircleUpPICTID = 302; +static const ResIDType kGlobeCircleDownPICTID = 303; +static const ResIDType kTargetUpperLeftPICTID = 304; +static const ResIDType kTargetUpperRightPICTID = 305; +static const ResIDType kTargetLowerLeftPICTID = 306; +static const ResIDType kTargetLowerRightPICTID = 307; +static const ResIDType kMotionHiliteLeftPICTID = 308; +static const ResIDType kMotionHiliteRightPICTID = 309; +static const ResIDType kMotionHiliteUpPICTID = 310; +static const ResIDType kMotionHiliteDownPICTID = 311; + +static const ResIDType kGlobeCountdownDigitsID = 350; + +static const int kGlobeCountdownWidth = 28; +static const int kGlobeCountdownHeight = 12; +static const int kGlobeCountdownOffset1 = 12; +static const int kGlobeCountdownOffset2 = 20; + +GlobeCountdown::GlobeCountdown(const DisplayElementID id) : IdlerAnimation(id) { + _digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCountdownDigitsID); + + Common::Rect r; + _digits.getSurfaceBounds(r); + _digitOffset = r.width() / 10; + setScale(1); + sizeElement(kGlobeCountdownWidth, kGlobeCountdownHeight); +} + +void GlobeCountdown::setDisplayOrder(const DisplayOrder order) { + IdlerAnimation::setDisplayOrder(order); +} + +void GlobeCountdown::show() { + IdlerAnimation::show(); +} + +void GlobeCountdown::hide() { + IdlerAnimation::hide(); +} + +void GlobeCountdown::moveElementTo(const CoordType x, const CoordType y) { + IdlerAnimation::moveElementTo(x, y); +} + +void GlobeCountdown::setCountdownTime(const int numSeconds) { + stop(); + setSegment(0, numSeconds); + setTime(numSeconds); +} + +void GlobeCountdown::startCountdown() { + setRate(-1); +} + +void GlobeCountdown::stopCountdown() { + stop(); +} + +void GlobeCountdown::draw(const Common::Rect &) { + Common::Rect r1; + _digits.getSurfaceBounds(r1); + r1.right = r1.left + _digitOffset; + Common::Rect r2 = r1; + TimeValue time = getTime(); + + Common::Rect bounds; + getBounds(bounds); + + if (time > 60 * 9 + 59) { + r2.moveTo(bounds.left, bounds.top); + r1.moveTo(9 * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + + r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top); + r1.moveTo(5 * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + + r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top); + r1.moveTo(9 * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + } else { + r2.moveTo(bounds.left, bounds.top); + r1.moveTo((time / 60) * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + + time %= 60; + r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top); + r1.moveTo((time / 10) * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + + r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top); + r1.moveTo((time % 10) * _digitOffset, 0); + _digits.copyToCurrentPort(r1, r2); + } +} + +const int16 GlobeGame::_siloCoords[kNumAllSilos][2] = { + { 60, -151 }, // Anchorage, Alaska + { 6, 39 }, // Addis Ababa, Ethiopia + { -22, 44 }, // Antaro, Madagascar + { 30, -83 }, // Atlanta, Georgia + { -41, 173 }, // Auckland, New Zealand + { 39, -78 }, // Baltimore, Maryland + { 11, 101 }, // Bangkok, Thailand + { 2, -75 }, // Bogota, Colombia + { 46, 4 }, // Bonn, Germany + { 51, -7 }, // Dublin, Ireland + { 28, -1 }, // El Menia, Algeria + { 67, -111 }, // Ellesmere, Canada + { 43, -107 }, // Glasgow, Montana + { 61, -48 }, // Godthab, Greenland + { 19, -157 }, // Honolulu, Hawaii + { 6, 5 }, // Ibadan, Nigeria + { -29, 26 }, // Johannesburg, South Africa + { 46, 92 }, // Kobdo, Mongolia + { -15, -63 }, // La Paz, Bolivia + { -35, -61 }, // La Plata, Argentina + { -9, -76 }, // Lima, Peru + { 38, -4 }, // Madrid, Spain + { -8, -51 }, // Manaus, Brazil + { 13, 120 }, // Manila, Phillipines + { -35, 143 }, // Melbourne, Australia + { 60, -161 }, // Nome, Alaska + { -7, 142 }, // Papua, New Guinea + { -32, 117 }, // Perth, West Australia + { 34, -114 }, // Phoenix, Arizona + { 18, -71 }, // Port-Au-Prince, Haiti + { 42, -121 }, // Portland, Oregon + { 61, -20 }, // Reykjavik, Iceland + { -22, -46 }, // Rio de Janeiro + { 27, -101 }, // San Antonio, Texas + { 34, 126 }, // Seoul, Korea + { 37, -87 }, // Saint Louis, Missouri + { 60, 30 }, // Saint Petersberg, Russia + { 56, 12 }, // Stockholm, Sweden + { 51, 105 }, // Svortalsk, Siberia + { 36, -96 } // Tulsa, Oklahoma +}; + +const int16 GlobeGame::_targetSilo[kNumTargetSilos] = { + 14, 9, 1, 33, 6, 8, 34, 31, 38, 21 +}; + +const short GlobeGame::_timeLimit[kNumTargetSilos] = { + 120, 110, 100, 90, 80, 70, 60, 50, 40, 30 +}; + +const TimeValue GlobeGame::_siloName[kNumTargetSilos][2] = { + { kHonoluluIn, kHonoluluOut }, + { kDublinIn, kDublinOut }, + { kAddisAbabaIn, kAddisAbabaOut }, + { kSanAntonioIn, kSanAntonioOut }, + { kBangkokIn, kBangkokOut }, + { kBonnIn, kBonnOut }, + { kSeoulIn, kSeoulOut }, + { kReykjavikIn, kReykjavikOut }, + { kSvortalskIn, kSvortalskOut }, + { kMadridIn, kMadridOut } +}; + +// From globe room models + +static const GlobeGame::Point3D kCameraLocation = { 0.53f, 4.4f, -0.86f }; +static const GlobeGame::Point3D kGlobeCenter = { -31.5f, 8.0f, 0.0f }; +static const float kGlobeRadius = 8.25f; +static const int16 kDegreesPerLongSlice = 360 / kNumLongSlices; +static const int16 kDegreesPerLatSlice = 25; +static const int16 kLongOrigin = -95; + +// Other constants. + +static const float kTanFieldOfView = 0.7082373180482f; +static const float kPicturePlaneDistance = 10.0f; // Completely arbitrary. +static const int16 kLatError = 2; +static const int16 kLongError = 2; +static const TimeValue kGlobeMovieStartTime = 2 * 2 * kNumLongSlices * 600 / 15; + +static const TimeValue kTimePerGlobeFrame = 40; + +static const NotificationFlags kGlobeSplash1Finished = 1; +static const NotificationFlags kGlobeTimerExpired = kGlobeSplash1Finished << 1; +static const NotificationFlags kMaxDeactivatedFinished = kGlobeTimerExpired << 1; + +static const NotificationFlags kGlobeNotificationFlags = kGlobeSplash1Finished | + kGlobeTimerExpired | + kMaxDeactivatedFinished; + +static const int16 kSplash1End = 4; +static const int16 kSplash2End = 5; +static const int16 kSplash3Start = 8; +static const int16 kSplash3Stop = 9; +static const int16 kSplash4Start = 9; +static const int16 kSplash4Stop = 10; +static const int16 kNewLaunchSiloTime = 10; +static const int16 kSiloDeactivatedTime = 11; +static const int16 kMissileLaunchedTime = 12; +static const int16 kMaxDeactivatedStart = 13; +static const int16 kMaxDeactivatedStop = 23; + +static const int16 kGamePlaying = 1; +static const int16 kGameOver = 2; + +enum { + kGameIntro, + kPlayingRobotIntro, + kPlayingStrikeAuthorized, + kPlayingPrimaryTarget, + kPlayingNewSilo1, + kPlayingNewSilo2, + kPlayingNewSilo3, + kPlayingTime, + kPlayingInstructions, + kWaitingForPlayer, + kSiloDeactivated, + kRobotTaunting, + kDelayingPlayer, + kPlayerWon1, + kPlayerWon2, + kPlayerLost1 +}; + +// TODO: Use ScummVM equivalent +static const float kPI = 3.1415926535f; + +float degreesToRadians(float angle) { + return (angle * kPI) / 180; +} + +float radiansToDegrees(float angle) { + return (angle * 180) / kPI; +} + +GlobeGame::GlobeGame(Neighborhood *handler) : GameInteraction(kNoradGlobeGameInteractionID, handler), + _monitorMovie(kGlobeMonitorID), _globeMovie(kGlobeMovieID), _upperNamesMovie(kGlobeUpperNamesID), + _lowerNamesMovie(kGlobeLowerNamesID), _globeNotification(kNoradGlobeNotificationID, (PegasusEngine *)g_engine), + _globeCircleLeft(kGlobeCircleLeftID), _globeCircleRight(kGlobeCircleRightID), + _globeCircleUp(kGlobeCircleUpID), _globeCircleDown(kGlobeCircleDownID), + _motionHighlightLeft(kMotionHiliteLeftID), _motionHighlightRight(kMotionHiliteRightID), + _motionHighlightUp(kMotionHiliteUpID), _motionHighlightDown(kMotionHiliteDownID), + _targetHighlightUpperLeft(kTargetHiliteUpperLeftID), _targetHighlightUpperRight(kTargetHiliteUpperRightID), + _targetHighlightLowerLeft(kTargetHiliteLowerLeftID), _targetHighlightLowerRight(kTargetHiliteLowerRightID), + _globeTracker(&_globeMovie, &_motionHighlightLeft, &_motionHighlightRight, &_motionHighlightUp, + &_motionHighlightDown), _countdown(kGlobeCountdownID) { + _neighborhoodNotification = handler->getNeighborhoodNotification(); +} + +void GlobeGame::openInteraction() { + _monitorMovie.initFromMovieFile("Images/Norad Delta/N79 Left Monitor"); + _monitorMovie.moveElementTo(kGlobeMonitorLeft, kGlobeMonitorTop); + _monitorMovie.setDisplayOrder(kGlobeMonitorLayer); + _monitorMovie.startDisplaying(); + _monitorMovie.setSegment(0, kSplash1End * _monitorMovie.getScale()); + _monitorMovie.show(); + + _monitorCallBack.setNotification(&_globeNotification); + _monitorCallBack.initCallBack(&_monitorMovie, kCallBackAtExtremes); + _monitorCallBack.setCallBackFlag(kGlobeSplash1Finished); + _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + _upperNamesMovie.initFromMovieFile("Images/Norad Delta/Upper Names"); + _upperNamesMovie.moveElementTo(kGlobeUpperNamesLeft, kGlobeUpperNamesTop); + _upperNamesMovie.setDisplayOrder(kGlobeUpperNamesLayer); + _upperNamesMovie.startDisplaying(); + + _lowerNamesMovie.initFromMovieFile("Images/Norad Delta/Lower Names"); + _lowerNamesMovie.moveElementTo(kGlobeLowerNamesLeft, kGlobeLowerNamesTop); + _lowerNamesMovie.setDisplayOrder(kGlobeLowerNamesLayer); + _lowerNamesMovie.startDisplaying(); + + _globeMovie.initFromMovieFile("Images/Norad Delta/Spinning Globe"); + _globeMovie.moveElementTo(kGlobeLeft, kGlobeTop); + _globeMovie.setDisplayOrder(kGlobeMovieLayer); + _globeMovie.startDisplaying(); + _globeMovie.setTime(kGlobeMovieStartTime); + _globeMovie.redrawMovieWorld(); + + _globeCircleLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleLeftPICTID, true); + _globeCircleLeft.moveElementTo(kGlobeCircleLeftLeft, kGlobeCircleLeftTop); + _globeCircleLeft.setDisplayOrder(kGlobeCircleLayer); + _globeCircleLeft.startDisplaying(); + + _globeCircleRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleRightPICTID, true); + _globeCircleRight.moveElementTo(kGlobeCircleRightLeft, kGlobeCircleRightTop); + _globeCircleRight.setDisplayOrder(kGlobeCircleLayer); + _globeCircleRight.startDisplaying(); + + _globeCircleUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleUpPICTID, true); + _globeCircleUp.moveElementTo(kGlobeCircleUpLeft, kGlobeCircleUpTop); + _globeCircleUp.setDisplayOrder(kGlobeCircleLayer); + _globeCircleUp.startDisplaying(); + + _globeCircleDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleDownPICTID, true); + _globeCircleDown.moveElementTo(kGlobeCircleDownLeft, kGlobeCircleDownTop); + _globeCircleDown.setDisplayOrder(kGlobeCircleLayer); + _globeCircleDown.startDisplaying(); + + _motionHighlightLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteLeftPICTID, true); + _motionHighlightLeft.moveElementTo(kGlobeLeftMotionHiliteLeft, kGlobeLeftMotionHiliteTop); + _motionHighlightLeft.setDisplayOrder(kGlobeHilitesLayer); + _motionHighlightLeft.startDisplaying(); + + _motionHighlightRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteRightPICTID, true); + _motionHighlightRight.moveElementTo(kGlobeRightMotionHiliteLeft, kGlobeRightMotionHiliteTop); + _motionHighlightRight.setDisplayOrder(kGlobeCircleLayer); + _motionHighlightRight.startDisplaying(); + + _motionHighlightUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteUpPICTID, true); + _motionHighlightUp.moveElementTo(kGlobeUpMotionHiliteLeft, kGlobeUpMotionHiliteTop); + _motionHighlightUp.setDisplayOrder(kGlobeHilitesLayer); + _motionHighlightUp.startDisplaying(); + + _motionHighlightDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteDownPICTID, true); + _motionHighlightDown.moveElementTo(kGlobeDownMotionHiliteLeft, kGlobeDownMotionHiliteTop); + _motionHighlightDown.setDisplayOrder(kGlobeHilitesLayer); + _motionHighlightDown.startDisplaying(); + + _targetHighlightUpperLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperLeftPICTID, true); + _targetHighlightUpperLeft.moveElementTo(kGlobeUpperLeftHiliteLeft, kGlobeUpperLeftHiliteTop); + _targetHighlightUpperLeft.setDisplayOrder(kGlobeHilitesLayer); + _targetHighlightUpperLeft.startDisplaying(); + + _targetHighlightUpperRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperRightPICTID, true); + _targetHighlightUpperRight.moveElementTo(kGlobeUpperRightHiliteLeft, kGlobeUpperRightHiliteTop); + _targetHighlightUpperRight.setDisplayOrder(kGlobeHilitesLayer); + _targetHighlightUpperRight.startDisplaying(); + + _targetHighlightLowerLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerLeftPICTID, true); + _targetHighlightLowerLeft.moveElementTo(kGlobeLowerLeftHiliteLeft, kGlobeLowerLeftHiliteTop); + _targetHighlightLowerLeft.setDisplayOrder(kGlobeHilitesLayer); + _targetHighlightLowerLeft.startDisplaying(); + + _targetHighlightLowerRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerRightPICTID, true); + _targetHighlightLowerRight.moveElementTo(kGlobeLowerRightHiliteLeft, kGlobeLowerRightHiliteTop); + _targetHighlightLowerRight.setDisplayOrder(kGlobeHilitesLayer); + _targetHighlightLowerRight.startDisplaying(); + + _countdown.setDisplayOrder(kGlobeCountdownLayer); + _countdown.moveElementTo(kGlobeCountdownLeft, kGlobeCountdownTop); + _countdown.startDisplaying(); + _countdown.setCountdownTime(_timeLimit[0]); + + _countdownCallBack.setNotification(&_globeNotification); + _countdownCallBack.initCallBack(&_countdown, kCallBackAtExtremes); + _countdownCallBack.setCallBackFlag(kGlobeTimerExpired); + _countdownCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + + _globeNotification.notifyMe(this, kGlobeNotificationFlags, kGlobeNotificationFlags); + + _gameState = kGameIntro; + _currentSiloIndex = 0; + _playedInstructions = false; + + _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, kDelayCompletedFlag | kSpotSoundCompletedFlag); +} + +void GlobeGame::initInteraction() { + _monitorMovie.start(); + _monitorMovie.redrawMovieWorld(); +} + +void GlobeGame::closeInteraction() { + _monitorMovie.stop(); + _monitorMovie.stopDisplaying(); + _monitorMovie.releaseMovie(); + _monitorCallBack.releaseCallBack(); + + _globeMovie.stop(); + _globeMovie.stopDisplaying(); + _globeMovie.releaseMovie(); + _globeNotification.cancelNotification(this); + + _upperNamesMovie.stop(); + _upperNamesMovie.stopDisplaying(); + _upperNamesMovie.releaseMovie(); + + _lowerNamesMovie.stop(); + _lowerNamesMovie.stopDisplaying(); + _lowerNamesMovie.releaseMovie(); + + _countdown.hide(); + _countdown.stopDisplaying(); + _countdownCallBack.releaseCallBack(); + + _globeCircleLeft.stopDisplaying(); + _globeCircleLeft.deallocateSurface(); + _globeCircleRight.stopDisplaying(); + _globeCircleRight.deallocateSurface(); + _globeCircleUp.stopDisplaying(); + _globeCircleUp.deallocateSurface(); + _globeCircleDown.stopDisplaying(); + _globeCircleDown.deallocateSurface(); + + _motionHighlightLeft.stopDisplaying(); + _motionHighlightLeft.deallocateSurface(); + _motionHighlightRight.stopDisplaying(); + _motionHighlightRight.deallocateSurface(); + _motionHighlightUp.stopDisplaying(); + _motionHighlightUp.deallocateSurface(); + _motionHighlightDown.stopDisplaying(); + _motionHighlightDown.deallocateSurface(); + + _targetHighlightUpperLeft.stopDisplaying(); + _targetHighlightUpperLeft.deallocateSurface(); + _targetHighlightUpperRight.stopDisplaying(); + _targetHighlightUpperRight.deallocateSurface(); + _targetHighlightLowerLeft.stopDisplaying(); + _targetHighlightLowerLeft.deallocateSurface(); + _targetHighlightLowerRight.stopDisplaying(); + _targetHighlightLowerRight.deallocateSurface(); + + _neighborhoodNotification->cancelNotification(this); +} + +void GlobeGame::receiveNotification(Notification *notification, const NotificationFlags flags) { + TimeScale scale = _monitorMovie.getScale(); + + if (notification == _neighborhoodNotification) { + switch (_gameState) { + case kPlayingRobotIntro: + _monitorMovie.stop(); + _monitorMovie.setSegment(0, _monitorMovie.getDuration()); + _monitorMovie.setTime(kSplash2End * scale - 1); + _monitorMovie.setFlags(0); + + _owner->requestDelay(1, 2, kFilterNoInput, 0); + _owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut, + kFilterNoInput, kSpotSoundCompletedFlag); + _gameState = kPlayingStrikeAuthorized; + break; + case kPlayingStrikeAuthorized: + _monitorMovie.setSegment(kSplash3Start * scale, kSplash3Stop * scale); + _monitorMovie.setTime(kSplash3Start * scale); + _monitorMovie.redrawMovieWorld(); + + _owner->requestDelay(1, 3, kFilterNoInput, 0); + _owner->requestSpotSound(kPrimaryTargetIn, kPrimaryTargetOut, kFilterNoInput, 0); + _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + _monitorMovie.start(); + _gameState = kPlayingPrimaryTarget; + break; + case kPlayingPrimaryTarget: + _monitorMovie.stop(); + _monitorMovie.setSegment(0, _monitorMovie.getDuration()); + _monitorMovie.setTime(kNewLaunchSiloTime * scale); + _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, + kSpotSoundCompletedFlag); + _gameState = kPlayingNewSilo1; + break; + case kPlayingNewSilo1: + _monitorMovie.stop(); + _monitorMovie.setSegment(0, _monitorMovie.getDuration()); + _owner->requestDelay(1, 3, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingNewSilo2; + break; + case kPlayingNewSilo2: + _upperNamesMovie.show(); + _upperNamesMovie.setTime(_currentSiloIndex * _upperNamesMovie.getScale()); + _upperNamesMovie.redrawMovieWorld(); + _monitorMovie.setTime(kSplash4Stop * scale - 1); + _monitorMovie.redrawMovieWorld(); + _owner->requestSpotSound(_siloName[_currentSiloIndex][0], _siloName[_currentSiloIndex][1], kFilterNoInput, 0); + _owner->requestDelay(1, 3, kFilterNoInput, 0); + _owner->requestSpotSound(kLaunchToProceedIn, kLaunchToProceedOut, kFilterNoInput, 0); + _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingNewSilo3; + break; + case kPlayingNewSilo3: + _countdown.stopCountdown(); + _countdown.setCountdownTime(_timeLimit[_currentSiloIndex]); + _countdown.show(); + _gameState = kPlayingTime; + + if (_timeLimit[_currentSiloIndex] >= 120) + _owner->requestSpotSound(kTwoMinutesIn, kTwoMinutesOut, kFilterNoInput, 0); + else if (_timeLimit[_currentSiloIndex] >= 60) + _owner->requestSpotSound(kOneMinuteIn, kOneMinuteOut, kFilterNoInput, 0); + + switch (_timeLimit[_currentSiloIndex] % 60) { + case 0: + _owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + break; + case 10: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kTenSecondsIn, kTenSecondsOut, kFilterNoInput, + kSpotSoundCompletedFlag); + break; + case 20: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kTwentySecondsIn, kTwentySecondsOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 30: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kThirtySecondsIn, kThirtySecondsOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 40: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kFortySecondsIn, kFortySecondsOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 50: + _owner->requestDelay(1, 5, kFilterNoInput, 0); + _owner->requestSpotSound(kFiftySecondsIn, kFiftySecondsOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + } + case kPlayingTime: + _gameState = kPlayingInstructions; + _globeMovie.show(); + _globeCircleLeft.show(); + _globeCircleRight.show(); + _globeCircleUp.show(); + _globeCircleDown.show(); + + if (_playedInstructions) { + receiveNotification(notification, flags); + } else { + _owner->requestSpotSound(kToDeactivateIn, kToDeactivateOut, kFilterNoInput, + kSpotSoundCompletedFlag); + _playedInstructions = true; + } + break; + case kPlayingInstructions: + _gameState = kWaitingForPlayer; + _countdown.startCountdown(); + break; + case kSiloDeactivated: + _gameState = kRobotTaunting; + + switch (_currentSiloIndex) { + case 3: + _owner->requestSpotSound(kYouCannotPossiblyIn, kYouCannotPossiblyOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + case 5: + _owner->requestSpotSound(kYouWillFailIn, kYouWillFailOut, kFilterNoInput, + kSpotSoundCompletedFlag); + break; + case 7: + _owner->requestSpotSound(kGiveUpHumanIn, kGiveUpHumanOut, kFilterNoInput, + kSpotSoundCompletedFlag); + break; + case 9: + _owner->requestSpotSound(kYouAreRunningIn, kYouAreRunningOut, + kFilterNoInput, kSpotSoundCompletedFlag); + break; + default: + _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, + kFilterNoInput, kSpotSoundCompletedFlag); + _monitorMovie.setTime(kNewLaunchSiloTime * scale); + _monitorMovie.redrawMovieWorld(); + _gameState = kPlayingNewSilo1; + break; + } + break; + case kRobotTaunting: + _owner->requestDelay(1, 1, kFilterNoInput, 0); + _owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, kSpotSoundCompletedFlag); + _monitorMovie.setTime(kNewLaunchSiloTime * scale); + _monitorMovie.redrawMovieWorld(); + _gameState = kPlayingNewSilo1; + break; + case kDelayingPlayer: + _gameState = kWaitingForPlayer; + break; + case kPlayerLost1: + _owner->recallToTSAFailure(); + break; + case kPlayerWon2: + ((NoradDelta *)_owner)->finishedGlobeGame(); + _owner->requestDeleteCurrentInteraction(); + break; + default: + break; + } + } else if (notification == &_globeNotification) { + ExtraTable::Entry entry; + + switch (flags) { + case kGlobeSplash1Finished: + _owner->getExtraEntry(kN79BrightView, entry); + _monitorMovie.stop(); + _monitorMovie.setSegment(kSplash1End * scale, kSplash2End * scale); + _monitorMovie.setFlags(kLoopTimeBase); + _monitorMovie.start(); + _owner->showViewFrame(entry.movieStart); + _owner->requestSpotSound(kIJustBrokeIn, kIJustBrokeOut, kFilterNoInput, 0); + _owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingRobotIntro; + break; + case kGlobeTimerExpired: + // Missile launched, player loses. + _owner->requestSpotSound(kMissileLaunchedIn, kMissileLaunchedOut, kFilterNoInput, kSpotSoundCompletedFlag); + _gameState = kPlayerLost1; + break; + case kMaxDeactivatedFinished: + _monitorMovie.stop(); + _monitorMovie.setSegment(0, _monitorMovie.getDuration()); + _owner->requestDelay(1, 2, kFilterNoInput, 0); + _owner->requestSpotSound(kTheOnlyGoodHumanIn, kTheOnlyGoodHumanOut, kFilterNoInput, 0); + _owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayerWon2; + break; + default: + break; + } + } +} + +// Prevent the player from getting up until the game is over. + +void GlobeGame::handleInput(const Input &input, const Hotspot *cursorSpot) { + Common::Point where; + input.getInputLocation(where); + Hotspot *spot = g_allHotspots.findHotspot(where); + + if (((PegasusEngine *)g_engine)->_cursor->isVisible() && spot != 0 && + spot->getObjectID() == kNorad79SiloAreaSpotID && findClickedSilo(input) != -1) { + _targetHighlightUpperLeft.show(); + _targetHighlightUpperRight.show(); + _targetHighlightLowerLeft.show(); + _targetHighlightLowerRight.show(); + } else { + _targetHighlightUpperLeft.hide(); + _targetHighlightUpperRight.hide(); + _targetHighlightLowerLeft.hide(); + _targetHighlightLowerRight.hide(); + } + + // Interrupt certain inputs to prevent player from switching modes. + InputHandler::handleInput(input, cursorSpot); +} + +int16 GlobeGame::findClickedSilo(const Input &input) { + Common::Point screenPoint; + input.getInputLocation(screenPoint); + screenPoint.x -= kNavAreaLeft; + screenPoint.y -= kNavAreaTop; + + Line3D ray; + screenPointTo3DPoint(screenPoint.x, screenPoint.y, ray.pt2); + ray.pt1 = kCameraLocation; + + Point3D globePoint; + if (lineHitsGlobe(ray, globePoint)) { + int16 latOrigin, longOrigin, latitude, longitude; + globeMovieFrameToOrigin(_globeMovie.getTime() / kTimePerGlobeFrame, latOrigin, longOrigin); + globePointToLatLong(globePoint, latOrigin, longOrigin, latitude, longitude); + + for (int16 i = 0; i < kNumAllSilos; i++) + if (_siloCoords[i][0] >= latitude - kLatError && _siloCoords[i][0] <= latitude + kLatError && + _siloCoords[i][1] >= longitude - kLongError && _siloCoords[i][1] <= longitude + kLongError) + return i; + } + + return -1; +} + +void GlobeGame::spinGlobe(const Input &input, const Hotspot *spot, GlobeTrackDirection trackDirection) { + _globeTracker.setTrackParameters(spot, trackDirection); + _globeTracker.startTracking(input); +} + +void GlobeGame::clickGlobe(const Input &input) { + int16 newSilo = findClickedSilo(input); + + if (newSilo != -1) { + _targetHighlightUpperLeft.hide(); + _targetHighlightUpperRight.hide(); + _targetHighlightLowerLeft.hide(); + _targetHighlightLowerRight.hide(); + _lowerNamesMovie.show(); + _lowerNamesMovie.setTime(newSilo * _lowerNamesMovie.getScale()); + _lowerNamesMovie.redrawMovieWorld(); + _owner->requestSpotSound(kSiloBeepIn, kSiloBeepOut, kFilterNoInput, 0); + + if (newSilo == _targetSilo[_currentSiloIndex]) { + _currentSiloIndex++; + _countdown.stopCountdown(); + _owner->requestSpotSound(kSiloDeactivatedIn, kSiloDeactivatedOut, kFilterNoInput, 0); + + if (_currentSiloIndex == kNumTargetSilos) { + // Player won. + _owner->requestDelay(1, 2, kFilterNoInput, 0); + _upperNamesMovie.hide(); + _lowerNamesMovie.hide(); + _countdown.hide(); + _monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(), + kMaxDeactivatedStop * _monitorMovie.getScale()); + _monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale()); + _monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished); + _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _monitorMovie.start(); + _owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, + kFilterNoInput, kSpotSoundCompletedFlag); + _gameState = kPlayerWon1; + } else { + _owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); + _upperNamesMovie.hide(); + _lowerNamesMovie.hide(); + _countdown.hide(); + _monitorMovie.setTime(kSiloDeactivatedTime * _monitorMovie.getScale()); + _monitorMovie.redrawMovieWorld(); + _gameState = kSiloDeactivated; + } + } else { + _owner->requestDelay(5, 1, kFilterNoInput, kDelayCompletedFlag); + _gameState = kDelayingPlayer; + // Play "incorrect" sound? + } + } +} + +void GlobeGame::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kNorad79SpinLeftSpotID: + spinGlobe(input, spot, kTrackLeft); + break; + case kNorad79SpinRightSpotID: + spinGlobe(input, spot, kTrackRight); + break; + case kNorad79SpinUpSpotID: + spinGlobe(input, spot, kTrackUp); + break; + case kNorad79SpinDownSpotID: + spinGlobe(input, spot, kTrackDown); + break; + case kNorad79SiloAreaSpotID: + clickGlobe(input); + break; + default: + GameInteraction::clickInHotspot(input, spot); + break; + } +} + +void GlobeGame::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_gameState) { + case kWaitingForPlayer: + g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID); + g_allHotspots.activateOneHotspot(kNorad79SpinLeftSpotID); + g_allHotspots.activateOneHotspot(kNorad79SpinRightSpotID); + g_allHotspots.activateOneHotspot(kNorad79SpinUpSpotID); + g_allHotspots.activateOneHotspot(kNorad79SpinDownSpotID); + g_allHotspots.activateOneHotspot(kNorad79SiloAreaSpotID); + break; + default: + g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID); + break; + } +} + +void GlobeGame::globeMovieFrameToOrigin(int16 frameNum, int16 &latOrigin, int16 &longOrigin) { + latOrigin = kDegreesPerLatSlice * 2 - (frameNum / (kNumLongSlices * 2)) * kDegreesPerLatSlice; + frameNum %= kNumLongSlices * 2; + + if (frameNum >= kNumLongSlices) + longOrigin = kLongOrigin + (kNumLongSlices * 2 - 1 - frameNum) * kDegreesPerLongSlice; + else + longOrigin = kLongOrigin + frameNum * kDegreesPerLongSlice; + + if (longOrigin > 180) + longOrigin -= 360; +} + +void GlobeGame::globePointToLatLong(const GlobeGame::Point3D &pt, int16 latOrigin, int16 longOrigin, + int16 &latitude, int16 &longitude) { + Point3D scratch = pt; + + // Translate globe center to origin. + scratch.x -= kGlobeCenter.x; + scratch.y -= kGlobeCenter.y; + scratch.z -= kGlobeCenter.z; + + // Rotate around z axis latOrigin degrees to bring equator parallel with XZ plane + float theta = degreesToRadians(latOrigin); + float s = sin(theta); + float c = cos(theta); + float x = scratch.x * c - scratch.y * s; + float y = scratch.y * c + scratch.x * s; + scratch.x = x; + scratch.y = y; + + // Calculate latitude + latitude = (int16)radiansToDegrees(asin(scratch.y / kGlobeRadius)); + + // Rotate around y axis longOrigin degrees to bring longitude 0 to positive X axis + theta = degreesToRadians(longOrigin); + s = sin(theta); + c = cos(theta); + x = scratch.x * c - scratch.z * s; + float z = scratch.z * c + scratch.x * s; + scratch.x = x; + scratch.z = z; + + // Calculate longitude + longitude = (int16)radiansToDegrees(acos(scratch.x / sqrt(scratch.x * scratch.x + scratch.z * scratch.z))); + + if (scratch.z < 0) + longitude = -longitude; +} + +// h, v in [0, 511][0, 255] +// Looking down negative x axis. +void GlobeGame::screenPointTo3DPoint(int16 h, int16 v, GlobeGame::Point3D &pt) { + pt.x = kCameraLocation.x - kPicturePlaneDistance; + pt.y = kCameraLocation.y + (128 - v) * kPicturePlaneDistance * kTanFieldOfView / 256; + pt.z = kCameraLocation.z + (h - 256) * kPicturePlaneDistance * kTanFieldOfView / 256; +} + +// Fundamentals of Three-Dimensional Graphics, by Alan Watt +// pp. 163-164 +bool GlobeGame::lineHitsGlobe(const GlobeGame::Line3D &line, GlobeGame::Point3D &pt) { + float i = line.pt2.x - line.pt1.x; + float j = line.pt2.y - line.pt1.y; + float k = line.pt2.z - line.pt1.z; + float a = i * i + j * j + k * k; + float b = 2 * i * (line.pt1.x - kGlobeCenter.x) + 2 * j * (line.pt1.y - kGlobeCenter.y) + + 2 * k * (line.pt1.z - kGlobeCenter.z); + float c = kGlobeCenter.x * kGlobeCenter.x + kGlobeCenter.y * kGlobeCenter.y + + kGlobeCenter.z * kGlobeCenter.z + line.pt1.x * line.pt1.x + line.pt1.y * line.pt1.y + + line.pt1.z * line.pt1.z + -2 * (kGlobeCenter.x * line.pt1.x + kGlobeCenter.y * line.pt1.y + + kGlobeCenter.z * line.pt1.z) - kGlobeRadius * kGlobeRadius; + + // Solve quadratic equation of a, b, c. + float t = b * b - 4 * a * c; + + if (t >= 0.0f) { + // Return smaller root, which corresponds to closest intersection point. + t = (-b - sqrt(t)) / (2 * a); + pt.x = i * t + line.pt1.x; + pt.y = j * t + line.pt1.y; + pt.z = k * t + line.pt1.z; + return true; + } + + return false; +} + +bool GlobeGame::canSolve() { + return _gameState != kPlayerWon1 && _gameState != kPlayerWon2 && _gameState != kPlayerLost1; +} + +void GlobeGame::doSolve() { + _owner->requestDelay(1, 2, kFilterNoInput, 0); + _upperNamesMovie.hide(); + _lowerNamesMovie.hide(); + _countdown.hide(); + _monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(), kMaxDeactivatedStop * _monitorMovie.getScale()); + _monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale()); + _monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished); + _monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _monitorMovie.start(); + _owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, kFilterNoInput, kSpotSoundCompletedFlag); + _gameState = kPlayerWon1; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.h b/engines/pegasus/neighborhood/norad/delta/globegame.h new file mode 100644 index 0000000000..73ed48866f --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/globegame.h @@ -0,0 +1,169 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H +#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +enum GlobeTrackDirection { + kTrackLeft, + kTrackRight, + kTrackUp, + kTrackDown +}; + +// This class assumes that the globe movie is built at 15 frames per second with a +// time scale of 600, yielding 40 time unit per frame. + +class GlobeTracker : public Tracker { +public: + GlobeTracker(Movie *, Picture *, Picture *, Picture *, Picture *); + virtual ~GlobeTracker() {} + + void setTrackParameters(const Hotspot *, GlobeTrackDirection); + void continueTracking(const Input &); + void startTracking(const Input &); + void stopTracking(const Input &); + void activateHotspots(); + bool stopTrackingInput(const Input &); + +protected: + void trackGlobeMovie(); + void stopGlobeMovie(); + + Movie *_globeMovie; + Picture *_leftHighlight; + Picture *_rightHighlight; + Picture *_upHighlight; + Picture *_downHighlight; + const Hotspot *_trackSpot; + int _trackTime; + GlobeTrackDirection _trackDirection; +}; + +class GlobeCountdown : public IdlerAnimation { +public: + GlobeCountdown(const DisplayElementID); + virtual ~GlobeCountdown() {} + + void setCountdownTime(const int); + void startCountdown(); + void stopCountdown(); + + void setDisplayOrder(const DisplayOrder); + void show(); + void hide(); + void moveElementTo(const CoordType, const CoordType); + + void draw(const Common::Rect &); + +protected: + Surface _digits; + int16 _digitOffset; +}; + +static const int16 kNumAllSilos = 40; +static const int16 kNumTargetSilos = 10; +static const int16 kNumLongSlices = 72; + +class GlobeGame : public GameInteraction, public NotificationReceiver { +public: + GlobeGame(Neighborhood *); + virtual ~GlobeGame() {} + + void handleInput(const Input &, const Hotspot *); + void clickInHotspot(const Input &, const Hotspot *); + void activateHotspots(); + + bool canSolve(); + void doSolve(); + + struct Point3D { + float x, y, z; + }; + + struct Line3D { + Point3D pt1, pt2; + }; + +protected: + // Latitude (-90 - 90) and longitude (-180 - 180) + static const int16 _siloCoords[kNumAllSilos][2]; + + static const int16 _targetSilo[kNumTargetSilos]; + static const int16 _timeLimit[kNumTargetSilos]; + static const TimeValue _siloName[kNumTargetSilos][2]; + + void openInteraction(); + void initInteraction(); + void closeInteraction(); + + void receiveNotification(Notification *, const NotificationFlags); + + void spinGlobe(const Input &, const Hotspot *, GlobeTrackDirection); + void clickGlobe(const Input &); + + int16 findClickedSilo(const Input &); + + void globeMovieFrameToOrigin(int16, int16 &, int16 &); + void globePointToLatLong(const Point3D &, int16, int16, int16 &, int16 &); + void screenPointTo3DPoint(int16, int16, Point3D &); + bool lineHitsGlobe(const Line3D &, Point3D &); + + Movie _monitorMovie; + Movie _globeMovie; + Movie _upperNamesMovie; + Movie _lowerNamesMovie; + Notification _globeNotification; + NotificationCallBack _monitorCallBack; + GlobeTracker _globeTracker; + Picture _globeCircleLeft; + Picture _globeCircleRight; + Picture _globeCircleUp; + Picture _globeCircleDown; + Picture _motionHighlightLeft; + Picture _motionHighlightRight; + Picture _motionHighlightUp; + Picture _motionHighlightDown; + Picture _targetHighlightUpperLeft; + Picture _targetHighlightUpperRight; + Picture _targetHighlightLowerLeft; + Picture _targetHighlightLowerRight; + GlobeCountdown _countdown; + NotificationCallBack _countdownCallBack; + int16 _gameState; + int16 _currentSiloIndex; + Notification *_neighborhoodNotification; + bool _playedInstructions; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp new file mode 100644 index 0000000000..f2ea53ff89 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp @@ -0,0 +1,869 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/retscanchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/delta/globegame.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +const uint32 NoradDelta::_noradDeltaClawExtras[22] = { + kN60ClawFromAToB, + kN60ClawALoop, + kN60ClawAPinch, + kN60ClawACounterclockwise, + kN60ClawAClockwise, + kN60ClawFromBToA, + kN60ClawFromBToC, + kN60ClawFromBToD, + kN60ClawBLoop, + kN60ClawBPinch, + kN60ClawBCounterclockwise, + kN60ClawBClockwise, + kN60ClawFromCToB, + kN60ClawCLoop, + kN60ClawCPinch, + kN60ClawCCounterclockwise, + kN60ClawCClockwise, + kN60ClawFromDToB, + kN60ClawDLoop, + kN60ClawDPinch, + kN60ClawDCounterclockwise, + kN60ClawDClockwise +}; + +NoradDelta::NoradDelta(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Delta", kNoradDeltaID) { + _elevatorUpRoomID = kNorad49South; + _elevatorDownRoomID = kNorad48South; + _elevatorUpSpotID = kNorad48ElevatorUpSpotID; + _elevatorDownSpotID = kNorad49ElevatorDownSpotID; + + // Pressure door stuff. + + _subRoomEntryRoom1 = kNorad50; + _subRoomEntryDir1 = kEast; + _subRoomEntryRoom2 = kNorad59; + _subRoomEntryDir2 = kWest; + _upperPressureDoorRoom = kNorad50East; + _lowerPressureDoorRoom = kNorad59West; + + _upperPressureDoorUpSpotID = kDeltaUpperPressureDoorUpSpotID; + _upperPressureDoorDownSpotID = kDeltaUpperPressureDoorDownSpotID; + _upperPressureDoorAbortSpotID = kNorad50DoorOutSpotID; + + _lowerPressureDoorUpSpotID = kDeltaLowerPressureDoorUpSpotID; + _lowerPressureDoorDownSpotID = kDeltaLowerPressureDoorDownSpotID; + _lowerPressureDoorAbortSpotID = kNorad59WestOutSpotID; + + _pressureSoundIn = kPressureDoorIntro1In; + _pressureSoundOut = kPressureDoorIntro1Out; + _equalizeSoundIn = kPressureDoorIntro2In; + _equalizeSoundOut = kPressureDoorIntro2Out; + _accessDeniedIn = kDeltaAccessDeniedIn; + _accessDeniedOut = kDeltaAccessDeniedOut; + + GameState.setNoradSubPrepState(kSubDamaged); + + _subControlRoom = kNorad60West; +} + +void NoradDelta::init() { + Norad::init(); + + // Little fix for the retinal scan zoom in spot... + Hotspot *hotspot = _vm->getAllHotspots().findHotspotByID(kNorad68WestSpotID); + hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag); + + hotspot = _vm->getAllHotspots().findHotspotByID(kNorad79WestSpotID); + hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag); + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotShieldBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kDelta59RobotShieldBiochipSpotID); + hotspotEntry->hotspotItem = kShieldBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotOpMemBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta59RobotOpMemBiochipSpotID); + hotspotEntry->hotspotItem = kOpticalBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta59RobotRetinalBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta59RobotRetinalBiochipSpotID); + hotspotEntry->hotspotItem = kRetinalScanBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotShieldBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta60RobotShieldBiochipSpotID); + hotspotEntry->hotspotItem = kShieldBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotOpMemBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta60RobotOpMemBiochipSpotID); + hotspotEntry->hotspotItem = kOpticalBiochip; + + hotspot = _vm->getAllHotspots().findHotspotByID(kDelta60RobotRetinalBiochipSpotID); + hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); + hotspotEntry = findHotspotEntry(kDelta60RobotRetinalBiochipSpotID); + hotspotEntry->hotspotItem = kRetinalScanBiochip; +} + +void NoradDelta::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + Norad::start(); +} + +void NoradDelta::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN07NE", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kNorad68, kWest)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + } +} + +void NoradDelta::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kArriveFromSubChase: + compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 20, entry.movieEnd, 90); + compassMove.insertFaderKnot(entry.movieStart + 25 * kNoradDeltaFrameDuration, 20); + compassMove.insertFaderKnot(entry.movieStart + 94 * kNoradDeltaFrameDuration, 45); + compassMove.insertFaderKnot(entry.movieStart + 101 * kNoradDeltaFrameDuration, 45); + compassMove.insertFaderKnot(entry.movieStart + 146 * kNoradDeltaFrameDuration, 90 + 15); + compassMove.insertFaderKnot(entry.movieStart + 189 * kNoradDeltaFrameDuration, 90 + 15); + compassMove.insertFaderKnot(entry.movieStart + 204 * kNoradDeltaFrameDuration, 90 + 30); + compassMove.insertFaderKnot(entry.movieStart + 214 * kNoradDeltaFrameDuration, 90 + 20); + compassMove.insertFaderKnot(entry.movieStart + 222 * kNoradDeltaFrameDuration, 90 + 20); + compassMove.insertFaderKnot(entry.movieStart + 228 * kNoradDeltaFrameDuration, 90 + 10); + compassMove.insertFaderKnot(entry.movieStart + 245 * kNoradDeltaFrameDuration, 90 + 85); + compassMove.insertFaderKnot(entry.movieStart + 262 * kNoradDeltaFrameDuration, 90 + 70); + compassMove.insertFaderKnot(entry.movieStart + 273 * kNoradDeltaFrameDuration, 90 + 80); + compassMove.insertFaderKnot(entry.movieStart + 287 * kNoradDeltaFrameDuration, 90); + break; + case kN60PlayerFollowsRobotToDoor: + compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270 + kSubControlCompassAngle, + entry.movieEnd, 270 - 15); + compassMove.insertFaderKnot(entry.movieStart + 280, 270 + kSubControlCompassAngle); + compassMove.insertFaderKnot(entry.movieStart + 920, 360); + compassMove.insertFaderKnot(entry.movieStart + 1840, 360); + compassMove.insertFaderKnot(entry.movieStart + 2520, 270); + compassMove.insertFaderKnot(entry.movieStart + 3760, 270); + compassMove.insertFaderKnot(entry.movieStart + 4640, 270 + kSubControlCompassAngle); + break; + case kN59PlayerWins2: + compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270, entry.movieEnd, 280); + compassMove.insertFaderKnot(entry.movieEnd - 1000, 270); + default: + Norad::getExtraCompassMove(entry, compassMove); + break; + } +} + +GameInteraction *NoradDelta::makeInteraction(const InteractionID interactionID) { + if (interactionID == kNoradGlobeGameInteractionID) + return new GlobeGame(this); + + return Norad::makeInteraction(interactionID); +} + +void NoradDelta::playClawMonitorIntro() { + playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut); +} + +void NoradDelta::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) { + Norad::getExitEntry(room, direction, entry); + + if (room == kNorad61 && direction == kSouth) + entry.movieStart += kNoradDeltaFrameDuration; +} + +void NoradDelta::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { + Norad::getZoomEntry(id, zoomEntry); + + if (id == kNorad59WestSpotID && GameState.getNoradPlayedGlobeGame()) { + ExtraTable::Entry extraEntry; + getExtraEntry(kN59ZoomWithRobot, extraEntry); + zoomEntry.movieStart = extraEntry.movieStart; + zoomEntry.movieEnd = extraEntry.movieEnd; + } +} + +void NoradDelta::loadAmbientLoops() { +/* + Logic: + + loop sound 1: + if room == kNorad79West + if player globe game + play kNoradGlobeLoop2SoundNum + else + play kNoradRedAlertLoopSoundNum + else if room >= kNorad78 && room <= kNorad79 + play kNoradGlobeLoop2SoundNum + else if gassed, + if room >= kNorad41 && room <= kNorad49South + play kNoradNewSubLoopSoundNum, kNoradWarningVolume + else if room >= kNorad59 && room <= kNorad60West + play kNoradSubControlLoopSoundNum, kNoradWarningVolume + else + play kNoradWarningLoopSoundNum, kNoradWarningVolume + else + play nothing + loop sound 2: + if gassed and not wearing air mask + if room == kNorad54North + play breathing unmanned loop + else + play breathing + else + if room == kNorad54North + play unmanned loop + else + play nothing +*/ + + if (GameState.getNoradArrivedFromSub()) { + RoomID room = GameState.getCurrentRoom(); + + if (room == kNorad79West) { + if (_privateFlags.getFlag(kNoradPrivateFinishedGlobeGameFlag)) + loadLoopSound1("Sounds/Norad/GlobAmb2.22K.AIFF"); + else + loadLoopSound1("Sounds/Norad/RedAlert.22K.AIFF"); + } else if (room >= kNorad78 && room <= kNorad79) { + // clone2727 says: This looks like it should be loadLoopSound1... + loadLoopSound2("Sounds/Norad/RedAlert.22K.AIFF"); + } else if (GameState.getNoradGassed()) { + if (room >= kNorad41 && room <= kNorad49South) + loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3); + else if (room >= kNorad59 && room <= kNorad60West) + loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3); + else + loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume); + } else { + loadLoopSound1(""); + } + + if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) { + if (room == kNorad54North) + loadLoopSound2("Sounds/Norad/Breathing Typing.22K.AIFF", 0x100 / 2); + else + loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); + } else { + if (room == kNorad54North) + loadLoopSound2("Sounds/Norad/N54NAS.22K.AIFF", 0x100 / 2); + else + loadLoopSound2(""); + } + } else { + // Start them off at zero... + if (GameState.getNoradGassed()) + loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", 0, 0, 0); + if (!g_airMask->isAirFilterOn()) + loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", 0, 0, 0); + } +} + +void NoradDelta::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kNorad41, kEast): + case MakeRoomView(kNorad49, kEast): + case MakeRoomView(kNorad49, kWest): + case MakeRoomView(kNorad61, kSouth): + case MakeRoomView(kNorad68, kEast): + case MakeRoomView(kNorad79, kWest): + makeContinuePoint(); + break; + } +} + +void NoradDelta::arriveAt(const RoomID room, const DirectionConstant direction) { + if (room != kNorad68) + GameState.setNoradRetScanGood(false); + + Norad::arriveAt(room, direction); + + FaderMoveSpec loop1Spec, loop2Spec; + ExtraTable::Entry entry; + + switch (room) { + case kNorad41: + if (direction == kEast && !GameState.getNoradArrivedFromSub()) { + GameState.setNoradPlayedGlobeGame(false); + + GameState.setNoradBeatRobotWithClaw(false); + GameState.setNoradBeatRobotWithDoor(false); + GameState.setNoradRetScanGood(false); + + GameState.setScoringExitedSub(true); + + getExtraEntry(kArriveFromSubChase, entry); + + loop1Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd - + entry.movieStart, kNoradWarningVolume); + loop1Spec.insertFaderKnot(7320, 0); + loop1Spec.insertFaderKnot(7880, kNoradWarningVolume); + + loop2Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd - + entry.movieStart, kNoradSuckWindVolume); + loop1Spec.insertFaderKnot(7320, 0); + loop1Spec.insertFaderKnot(7880, kNoradSuckWindVolume); + + startExtraSequence(kArriveFromSubChase, kExtraCompletedFlag, kFilterNoInput); + + startLoop1Fader(loop1Spec); + startLoop2Fader(loop2Spec); + } + break; + case kNorad54North: + GameState.setScoringSawRobotAt54North(true); + break; + case kNorad68: + if (GameState.getNoradRetScanGood()) + openDoor(); + break; + case kNorad68West: + arriveAtNorad68West(); + break; + case kNorad79West: + arriveAtNorad79West(); + break; + default: + break; + } +} + +void NoradDelta::doorOpened() { + Norad::doorOpened(); + GameState.setNoradRetScanGood(false); +} + +void NoradDelta::arriveAtNorad68West() { + playSpotSoundSync(kHoldForRetinalIn, kHoldForRetinalOut); + + BiochipItem *retScan = _vm->getCurrentBiochip(); + + if (retScan != 0 && retScan->getObjectID() == kRetinalScanBiochip) { + ((RetScanChip *)retScan)->searchForLaser(); + succeedRetinalScan(); + } else { + failRetinalScan(); + } +} + +void NoradDelta::arriveAtNorad79West() { + if (!GameState.getNoradPlayedGlobeGame()) + newInteraction(kNoradGlobeGameInteractionID); +} + +void NoradDelta::bumpIntoWall() { + requestSpotSound(kDeltaBumpIntoWallIn, kDeltaBumpIntoWallOut, kFilterNoInput, 0); + Neighborhood::bumpIntoWall(); +} + +void NoradDelta::failRetinalScan() { + startExtraSequence(kNoradDeltaRetinalScanBad, kExtraCompletedFlag, kFilterNoInput); +} + +void NoradDelta::succeedRetinalScan() { + startExtraSequence(kNoradDeltaRetinalScanGood, kExtraCompletedFlag, kFilterNoInput); + GameState.setNoradRetScanGood(true); + GameState.setScoringUsedRetinalChip(true); +} + +void NoradDelta::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &entry) { + Norad::getDoorEntry(room, direction, entry); + + if (room == kNorad68 && direction == kWest && !GameState.getNoradRetScanGood()) + entry.flags = kDoorPresentMask | kDoorLockedMask; +} + +void NoradDelta::finishedGlobeGame() { + GameState.setNoradPlayedGlobeGame(true); + _privateFlags.setFlag(kNoradPrivateFinishedGlobeGameFlag, true); + GameState.setScoringFinishedGlobeGame(true); + loadAmbientLoops(); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN60WD1", false, kWarningInterruption); +} + +bool NoradDelta::playingAgainstRobot() { + return GameState.getNoradPlayedGlobeGame(); +} + +void NoradDelta::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, + HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, + HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) { + outSpotID = kNorad60MonitorOutSpotID; + prepSpotID = kNorad60LaunchPrepSpotID; + clawControlSpotID = kNorad60ClawControlSpotID; + pinchClawSpotID = kNorad60ClawPinchSpotID; + moveClawDownSpotID = kNorad60ClawDownSpotID; + moveClawRightSpotID = kNorad60ClawRightSpotID; + moveClawLeftSpotID = kNorad60ClawLeftSpotID; + moveClawUpSpotID = kNorad60ClawUpSpotID; + clawCCWSpotID = kNorad60ClawCCWSpotID; + clawCWSpotID = kNorad60ClawCWSpotID; + clawPosition = kClawAtC; + clawExtraIDs = _noradDeltaClawExtras; +} + +void NoradDelta::playerBeatRobotWithDoor() { + GameState.setNoradBeatRobotWithDoor(true); + updateViewFrame(); + GameState.setScoringStoppedNoradRobot(true); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption); +} + +void NoradDelta::playerBeatRobotWithClaw() { + GameState.setNoradBeatRobotWithClaw(true); + updateViewFrame(); + GameState.setScoringStoppedNoradRobot(true); + GameState.setScoringNoradGandhi(true); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption); +} + +TimeValue NoradDelta::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry entry; + + if (room == kNorad41 && direction == kSouth && !GameState.getNoradArrivedFromSub()) { + getExtraEntry(kArriveFromSubChase, entry); + return entry.movieStart; + } + + if (GameState.getNoradBeatRobotWithDoor()) { + if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { + uint32 extraID = kN59Biochips111; + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) + extraID += 1; + if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) + extraID += 2; + if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) + extraID += 4; + getExtraEntry(extraID, entry); + return entry.movieStart; + } + + getExtraEntry(kN59RobotHeadOpens, entry); + return entry.movieStart; + } else if (GameState.getNoradBeatRobotWithClaw()) { + if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { + uint32 extraID = kN60Biochips111; + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) + extraID += 1; + if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) + extraID += 2; + if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) + extraID += 4; + getExtraEntry(extraID, entry); + return entry.movieStart; + } + + getExtraEntry(kN60RobotHeadOpens, entry); + return entry.movieStart; + } + + return Norad::getViewTime(room, direction); +} + +void NoradDelta::openDoor() { + if (GameState.getCurrentRoom() == kNorad59 && GameState.getCurrentDirection() == kWest && GameState.getNoradPlayedGlobeGame()) { + Input scratch; + InputHandler::_inputHandler->clickInHotspot(scratch, _vm->getAllHotspots().findHotspotByID(kNorad59WestSpotID)); + } else { + Norad::openDoor(); + } +} + +void NoradDelta::activateHotspots() { + Norad::activateHotspots(); + + if (GameState.getCurrentRoom() == kNorad59West && GameState.getCurrentDirection() == kWest && GameState.getNoradBeatRobotWithDoor()) { + _vm->getAllHotspots().deactivateOneHotspot(kNorad59WestOutSpotID); + + if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { + if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta59RobotShieldBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotShieldBiochipSpotID); + + if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta59RobotOpMemBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotOpMemBiochipSpotID); + + if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta59RobotRetinalBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta59RobotRetinalBiochipSpotID); + } else + _vm->getAllHotspots().activateOneHotspot(kDelta59RobotHeadSpotID); + } else if (GameState.getCurrentRoom() == kNorad60West && GameState.getCurrentDirection() == kWest && + GameState.getNoradBeatRobotWithClaw()) { + _vm->getAllHotspots().deactivateOneHotspot(kNorad60MonitorOutSpotID); + + if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { + if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta60RobotShieldBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotShieldBiochipSpotID); + + if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta60RobotOpMemBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotOpMemBiochipSpotID); + + if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) + _vm->getAllHotspots().activateOneHotspot(kDelta60RobotRetinalBiochipSpotID); + else + _vm->getAllHotspots().deactivateOneHotspot(kDelta60RobotRetinalBiochipSpotID); + } else { + _vm->getAllHotspots().activateOneHotspot(kDelta60RobotHeadSpotID); + } + } else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad50, kEast)) { + if (GameState.isCurrentDoorOpen()) + _vm->getAllHotspots().deactivateOneHotspot(kNorad50DoorSpotID); + } else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad59, kWest)) { + if (GameState.isCurrentDoorOpen()) + _vm->getAllHotspots().deactivateOneHotspot(kNorad59WestSpotID); + } +} + +void NoradDelta::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kDelta59RobotHeadSpotID: + startExtraSequence(kN59RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput); + break; + case kDelta60RobotHeadSpotID: + startExtraSequence(kN60RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Norad::clickInHotspot(input, clickedSpot); + break; + } +} + +void NoradDelta::receiveNotification(Notification *notification, const NotificationFlags flags) { + Norad::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + RetScanChip *retScan; + Input dummy; + + switch (_lastExtra) { + case kArriveFromSubChase: + GameState.setNoradArrivedFromSub(true); + GameState.setCurrentRoom(kNoRoomID); + GameState.setCurrentDirection(kNoDirection); + arriveAt(kNorad41, kEast); + break; + case kN59RobotHeadOpens: + case kN60RobotHeadOpens: + _privateFlags.setFlag(kNoradPrivateRobotHeadOpenFlag, true); + break; + case kNoradDeltaRetinalScanBad: + retScan = (RetScanChip *)_vm->getCurrentBiochip(); + retScan->setItemState(kNormalItem); + playSpotSoundSync(kRetinalScanFailedIn, kRetinalScanFailedOut); + downButton(dummy); + break; + case kNoradDeltaRetinalScanGood: + retScan = (RetScanChip *)_vm->getCurrentBiochip(); + retScan->setItemState(kNormalItem); + downButton(dummy); + break; + case kN59RobotDisappears: + case kN60RobotDisappears: + recallToTSASuccess(); + break; + } + + _interruptionFilter = kFilterAllInput; + } + + g_AIArea->checkMiddleArea(); +} + +void NoradDelta::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kShieldBiochip: + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { + GameState.setNoradFinished(true); + + if (GameState.getCurrentRoom() == kNorad59West) + startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kRetinalScanBiochip: + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { + GameState.setNoradFinished(true); + + if (GameState.getCurrentRoom() == kNorad59West) + startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kOpticalBiochip: + g_opticalChip->addPoseidon(); + GameState.setScoringGotNoradOpMemChip(); + + if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && + _privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { + GameState.setNoradFinished(true); + + if (GameState.getCurrentRoom() == kNorad59West) + startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); + } + break; + } + + Norad::pickedUpItem(item); +} + +void NoradDelta::takeItemFromRoom(Item *item) { + switch (item->getObjectID()) { + case kShieldBiochip: + _privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, true); + break; + case kRetinalScanBiochip: + _privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, true); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, true); + break; + } + + Norad::takeItemFromRoom(item); +} + +void NoradDelta::dropItemIntoRoom(Item *item, Hotspot *hotspot) { + switch (item->getObjectID()) { + case kShieldBiochip: + _privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, false); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, false); + break; + case kRetinalScanBiochip: + _privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, false); + break; + } + + Norad::dropItemIntoRoom(item, hotspot); +} + +Hotspot *NoradDelta::getItemScreenSpot(Item *item, DisplayElement *element) { + HotSpotID id = kNoHotSpotID; + + switch (item->getObjectID()) { + case kShieldBiochip: + if (GameState.getNoradBeatRobotWithDoor()) + id = kDelta59RobotShieldBiochipSpotID; + else + id = kDelta60RobotShieldBiochipSpotID; + break; + case kOpticalBiochip: + if (GameState.getNoradBeatRobotWithDoor()) + id = kDelta59RobotOpMemBiochipSpotID; + else + id = kDelta60RobotOpMemBiochipSpotID; + break; + case kRetinalScanBiochip: + if (GameState.getNoradBeatRobotWithDoor()) + id = kDelta59RobotRetinalBiochipSpotID; + else + id = kDelta60RobotRetinalBiochipSpotID; + break; + } + + if (id != kNoHotSpotID) + return _vm->getAllHotspots().findHotspotByID(id); + + return Norad::getItemScreenSpot(item, element); +} + +Common::String NoradDelta::getEnvScanMovie() { + return "Images/AI/Norad/XNE2"; +} + +uint NoradDelta::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad60, kWest): + if (GameState.getNoradPlayedGlobeGame()) + numHints = 2; + else + numHints = 1; + break; + case MakeRoomView(kNorad59, kNorth): + case MakeRoomView(kNorad59, kSouth): + case MakeRoomView(kNorad59, kEast): + case MakeRoomView(kNorad59, kWest): + case MakeRoomView(kNorad60, kNorth): + case MakeRoomView(kNorad60, kSouth): + case MakeRoomView(kNorad60, kEast): + if (GameState.getNoradPlayedGlobeGame()) + numHints = 2; + break; + case MakeRoomView(kNorad68, kWest): + if (_vm->playerHasItemID(kRetinalScanBiochip)) { + BiochipItem *retScan = _vm->getCurrentBiochip(); + if (retScan == 0 || retScan->getObjectID() != kRetinalScanBiochip) + numHints = 2; + } else if (!GameState.isCurrentDoorOpen()) { + numHints = 2; + } + break; + } + } + + return numHints; +} + +Common::String NoradDelta::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kNorad60, kWest): + if (GameState.getNoradPlayedGlobeGame()) { + if (hintNum == 1) + return "Images/AI/Norad/XN60WD2"; + + return "Images/AI/Norad/XN60WD3"; + } + + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kNorad59, kNorth): + case MakeRoomView(kNorad59, kSouth): + case MakeRoomView(kNorad59, kEast): + case MakeRoomView(kNorad59, kWest): + case MakeRoomView(kNorad60, kNorth): + case MakeRoomView(kNorad60, kSouth): + case MakeRoomView(kNorad60, kEast): + if (hintNum == 1) + return "Images/AI/Norad/XN60WD2"; + + return "Images/AI/Norad/XN60WD3"; + case MakeRoomView(kNorad68, kWest): + if (_vm->playerHasItemID(kRetinalScanBiochip)) { + if (hintNum == 1) + return "Images/AI/Globals/XGLOB1A"; + + return "Images/AI/Globals/XGLOB1C"; + } + + if (hintNum == 1) + return "Images/AI/Globals/XGLOB1B"; + + return "Images/AI/Globals/XGLOB3B"; + } + } + + return movieName; +} + +void NoradDelta::closeDoorOffScreen(const RoomID room, const DirectionConstant) { + switch (room) { + case kNorad47: + case kNorad48: + case kNorad41: + case kNorad42: + playSpotSoundSync(kDeltaElevatorDoorCloseIn, kDeltaElevatorDoorCloseOut); + break; + default: + playSpotSoundSync(kDeltaRegDoorCloseIn, kDeltaRegDoorCloseOut); + break; + } +} + +bool NoradDelta::canSolve() { + if (Norad::canSolve()) + return true; + + if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) { + BiochipItem *biochip = _vm->getCurrentBiochip(); + if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip) + return true; + } + + return false; +} + +void NoradDelta::doSolve() { + Norad::doSolve(); + + if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) { + if (!_vm->playerHasItemID(kRetinalScanBiochip)) + _vm->addItemToBiochips((BiochipItem *)_vm->getAllItems().findItemByID(kRetinalScanBiochip)); + + BiochipItem *biochip = _vm->getCurrentBiochip(); + if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip && g_interface) + g_interface->setCurrentBiochipID(kRetinalScanBiochip); + + Hotspot *spot = _vm->getAllHotspots().findHotspotByID(kNorad68WestSpotID); + Input scratch; + InputHandler::_inputHandler->clickInHotspot(scratch, spot); + } +} + +Common::String NoradDelta::getSoundSpotsName() { + return "Sounds/Norad/Norad Delta Spots"; +} + +Common::String NoradDelta::getNavMovieName() { + return "Images/Norad Delta/Norad Delta.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.h b/engines/pegasus/neighborhood/norad/delta/noraddelta.h new file mode 100644 index 0000000000..11065f2c9d --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.h @@ -0,0 +1,117 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H + +#include "pegasus/neighborhood/norad/norad.h" + +namespace Pegasus { + +class NoradDelta : public Norad { +public: + NoradDelta(InputHandler *, PegasusEngine *); + virtual ~NoradDelta() {} + + void init(); + + void start(); + + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + + void finishedGlobeGame(); + + virtual GameInteraction *makeInteraction(const InteractionID); + + void playClawMonitorIntro(); + + virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, + HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, + HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID, + HotSpotID &clawCWSpotID, uint32 &, const uint32 *&); + + void playerBeatRobotWithClaw(); + void playerBeatRobotWithDoor(); + + void loadAmbientLoops(); + + void setUpAIRules(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool canSolve(); + void doSolve(); + + void doorOpened(); + +protected: + enum { + kNoradPrivateArrivedFromSubFlag, + kNoradPrivateFinishedGlobeGameFlag, + kNoradPrivateRobotHeadOpenFlag, + kNoradPrivateGotShieldChipFlag, + kNoradPrivateGotOpticalChipFlag, + kNoradPrivateGotRetScanChipFlag, + kNumNoradPrivateFlags + }; + + static const uint32 _noradDeltaClawExtras[22]; + + void getExitEntry(const RoomID, const DirectionConstant, ExitTable::Entry &); + void getZoomEntry(const HotSpotID, ZoomTable::Entry &); + virtual void arriveAt(const RoomID, const DirectionConstant); + void arriveAtNorad68West(); + void arriveAtNorad79West(); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void openDoor(); + void activateHotspots(); + void clickInHotspot(const Input &, const Hotspot *); + void receiveNotification(Notification *, const NotificationFlags); + void pickedUpItem(Item *item); + void takeItemFromRoom(Item *item); + void dropItemIntoRoom(Item *item, Hotspot *); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + + virtual bool playingAgainstRobot(); + + void failRetinalScan(); + void succeedRetinalScan(); + void getDoorEntry(const RoomID, const DirectionConstant, DoorTable::Entry &); + + void bumpIntoWall(); + + FlagsArray<byte, kNumNoradPrivateFlags> _privateFlags; + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/norad.cpp b/engines/pegasus/neighborhood/norad/norad.cpp new file mode 100644 index 0000000000..578f062dea --- /dev/null +++ b/engines/pegasus/neighborhood/norad/norad.cpp @@ -0,0 +1,285 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/noradelevator.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/subplatform.h" + +namespace Pegasus { + +const NotificationFlags kDoneWithPressureDoorNotification = 1; + +const NotificationFlags kNoradNotificationFlags = kDoneWithPressureDoorNotification; + +// This class handles everything that Norad Alpha and Delta have in common, such as +// oxygen mask usage, the elevator and the pressure doors. + +Norad::Norad(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) : + Neighborhood(nextHandler, vm, resName, id), _noradNotification(kNoradNotificationID, vm) { + _elevatorUpSpotID = kNoHotSpotID; + _elevatorDownSpotID = kNoHotSpotID; + _elevatorUpRoomID = kNoHotSpotID; + _elevatorDownRoomID = kNoHotSpotID; + + _subRoomEntryRoom1 = kNoRoomID; + _subRoomEntryDir1 = kNoDirection; + _subRoomEntryRoom2 = kNoRoomID; + _subRoomEntryDir2 = kNoDirection; + _upperPressureDoorRoom = kNoRoomID; + _lowerPressureDoorRoom = kNoRoomID; + + _upperPressureDoorUpSpotID = kNoHotSpotID; + _upperPressureDoorDownSpotID = kNoHotSpotID; + _upperPressureDoorAbortSpotID = kNoHotSpotID; + + _lowerPressureDoorUpSpotID = kNoHotSpotID; + _lowerPressureDoorDownSpotID = kNoHotSpotID; + _lowerPressureDoorAbortSpotID = kNoHotSpotID; + + _pressureSoundIn = 0xffffffff; + _pressureSoundOut = 0xffffffff; + _equalizeSoundIn = 0xffffffff; + _equalizeSoundOut = 0xffffffff; + _accessDeniedIn = 0xffffffff; + _accessDeniedOut = 0xffffffff; + + _platformRoom = kNoRoomID; + _subControlRoom = kNoRoomID; + + _doneWithPressureDoor = false; + + _noradNotification.notifyMe(this, kNoradNotificationFlags, kNoradNotificationFlags); +} + +GameInteraction *Norad::makeInteraction(const InteractionID interactionID) { + PressureDoor *pressureDoor; + SubControlRoom *subControl; + + switch (interactionID) { + case kNoradElevatorInteractionID: + return new NoradElevator(this, _elevatorUpRoomID, _elevatorDownRoomID, _elevatorUpSpotID, _elevatorDownSpotID); + case kNoradPressureDoorInteractionID: + if (GameState.getCurrentRoom() == _upperPressureDoorRoom) + pressureDoor = new PressureDoor(this, true, _upperPressureDoorUpSpotID, _upperPressureDoorDownSpotID, + _upperPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut); + else + pressureDoor = new PressureDoor(this, false, _lowerPressureDoorUpSpotID, _lowerPressureDoorDownSpotID, + _lowerPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut); + + if (GameState.getCurrentRoom() == kNorad59West && playingAgainstRobot()) + pressureDoor->playAgainstRobot(); + + return pressureDoor; + case kNoradSubControlRoomInteractionID: + subControl = new SubControlRoom(this); + + if (GameState.getCurrentRoom() == kNorad60West && playingAgainstRobot()) + subControl->playAgainstRobot(); + + return subControl; + case kNoradSubPlatformInteractionID: + return new SubPlatform(this); + default: + return 0; + } +} + +void Norad::flushGameState() { + g_energyMonitor->saveCurrentEnergyValue(); +} + +void Norad::start() { + setUpAirMask(); + Neighborhood::start(); +} + +void Norad::activateHotspots() { + Neighborhood::activateHotspots(); + + RoomID room = GameState.getCurrentRoom(); + if (room == _elevatorUpRoomID) + _neighborhoodHotspots.activateOneHotspot(_elevatorDownSpotID); + else if (room == _elevatorDownRoomID) + _neighborhoodHotspots.activateOneHotspot(_elevatorUpSpotID); +} + +void Norad::arriveAt(const RoomID room, const DirectionConstant direction) { + Neighborhood::arriveAt(room, direction); + + if (GameState.getCurrentRoom() == _elevatorUpRoomID || GameState.getCurrentRoom() == _elevatorDownRoomID) + arriveAtNoradElevator(); + else if (GameState.getCurrentRoom() == _upperPressureDoorRoom) + arriveAtUpperPressureDoorRoom(); + else if (GameState.getCurrentRoom() == _lowerPressureDoorRoom) + arriveAtLowerPressureDoorRoom(); + else if (GameState.getCurrentRoom() == _platformRoom) + arriveAtSubPlatformRoom(); + else if (GameState.getCurrentRoom() == _subControlRoom) + arriveAtSubControlRoom(); + + if (_doneWithPressureDoor) { + _doneWithPressureDoor = false; + openDoor(); + } +} + +void Norad::arriveAtNoradElevator() { + if (_currentInteraction) + _currentInteraction->startOverInteraction(); + else + newInteraction(kNoradElevatorInteractionID); +} + +void Norad::arriveAtUpperPressureDoorRoom() { + newInteraction(kNoradPressureDoorInteractionID); +} + +void Norad::arriveAtLowerPressureDoorRoom() { + newInteraction(kNoradPressureDoorInteractionID); +} + +void Norad::arriveAtSubPlatformRoom() { + newInteraction(kNoradSubPlatformInteractionID); +} + +void Norad::arriveAtSubControlRoom() { + newInteraction(kNoradSubControlRoomInteractionID); +} + +int16 Norad::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 result = Neighborhood::getStaticCompassAngle(room, dir); + + if (room == _elevatorUpRoomID || room == _elevatorDownRoomID) + result += kElevatorCompassAngle; + else if (room == _platformRoom) + result += kSubPlatformCompassAngle; + else if (room == _subControlRoom) + result += kSubControlCompassAngle; + + return result; +} + +CanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) { + if (((GameState.getCurrentRoom() == _subRoomEntryRoom1 && GameState.getCurrentDirection() == _subRoomEntryDir1) || + (GameState.getCurrentRoom() == _subRoomEntryRoom2 && GameState.getCurrentDirection() == _subRoomEntryDir2)) && + GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) + return kCantOpenBadPressure; + + return Neighborhood::canOpenDoor(entry); +} + +void Norad::cantOpenDoor(CanOpenDoorReason reason) { + if (reason == kCantOpenBadPressure) + playSpotSoundSync(_pressureSoundIn, _pressureSoundOut); + else + playSpotSoundSync(_accessDeniedIn, _accessDeniedOut); +} + +void Norad::startExitMovie(const ExitTable::Entry &exitEntry) { + if (GameState.getCurrentRoom() == _elevatorUpRoomID) { + if (exitEntry.exitRoom != _elevatorDownRoomID) + newInteraction(kNoInteractionID); + } else if (GameState.getCurrentRoom() == _elevatorDownRoomID) { + if (exitEntry.exitRoom != _elevatorUpRoomID) + newInteraction(kNoInteractionID); + } else { + newInteraction(kNoInteractionID); + } + + Neighborhood::startExitMovie(exitEntry); +} + +void Norad::startZoomMovie(const ZoomTable::Entry &zoomEntry) { + newInteraction(kNoInteractionID); + Neighborhood::startZoomMovie(zoomEntry); +} + +void Norad::upButton(const Input &input) { + if (GameState.getCurrentRoom() != _elevatorUpRoomID && GameState.getCurrentRoom() != _elevatorDownRoomID) + Neighborhood::upButton(input); +} + +void Norad::setUpAirMask() { + _airMaskCallBack.setNotification(&_neighborhoodNotification); + _airMaskCallBack.initCallBack(&_airMaskTimer, kCallBackAtExtremes); + _airMaskCallBack.setCallBackFlag(kAirTimerExpiredFlag); + _neighborhoodNotification.notifyMe(this, kAirTimerExpiredFlag, kAirTimerExpiredFlag); + _airMaskCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _airMaskTimer.setScale(1); + _airMaskTimer.setSegment(0, kNoradAirMaskTimeLimit); + checkAirMask(); +} + +void Norad::checkAirMask() { + if (g_airMask && g_airMask->isAirFilterOn()) { + _airMaskTimer.stop(); + } else if (GameState.getNoradGassed() && !_airMaskTimer.isRunning()) { + _airMaskTimer.setTime(0); + _airMaskTimer.start(); + } + + loadAmbientLoops(); +} + +void Norad::receiveNotification(Notification *notification, const NotificationFlags flags) { + if (notification == &_neighborhoodNotification && (flags & kAirTimerExpiredFlag) != 0) + ((PegasusEngine *)g_engine)->die(kDeathGassedInNorad); + + Neighborhood::receiveNotification(notification, flags); + + if (notification == &_noradNotification) { + // Must be kDoneWithPressureDoorNotification... + Input scratch; + _doneWithPressureDoor = true; + downButton(scratch); + } +} + +uint16 Norad::getDateResID() const { + return kDate2112ID; +} + +Common::String Norad::getBriefingMovie() { + return "Images/AI/Norad/XNO"; +} + +void Norad::pickedUpItem(Item *item) { + Neighborhood::pickedUpItem(item); + g_AIArea->checkMiddleArea(); +} + +void Norad::doneWithPressureDoor() { + _noradNotification.setNotificationFlags(kDoneWithPressureDoorNotification, kDoneWithPressureDoorNotification); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/norad.h b/engines/pegasus/neighborhood/norad/norad.h new file mode 100644 index 0000000000..3cd77cc54b --- /dev/null +++ b/engines/pegasus/neighborhood/norad/norad.h @@ -0,0 +1,121 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H +#define PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// This is the code common to both Norad Alpha and Norad Delta + +class Norad : public Neighborhood { +public: + Norad(InputHandler *, PegasusEngine *owner, const Common::String &resName, const NeighborhoodID); + virtual ~Norad() {} + + void flushGameState(); + + virtual void start(); + + virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, + HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, + HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, + HotSpotID &moveClawLeftSpotID,HotSpotID &moveClawUpSpotID, + HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &, const uint32 *&) = 0; + void checkAirMask(); + + virtual uint16 getDateResID() const; + + virtual GameInteraction *makeInteraction(const InteractionID); + + Common::String getBriefingMovie(); + + void pickedUpItem(Item *); + + virtual void playClawMonitorIntro() {} + + void doneWithPressureDoor(); + +protected: + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void cantOpenDoor(CanOpenDoorReason); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + virtual void startExitMovie(const ExitTable::Entry &); + void startZoomMovie(const ZoomTable::Entry &); + virtual void upButton(const Input &); + virtual void activateHotspots(); + + virtual void arriveAt(const RoomID, const DirectionConstant); + virtual void arriveAtNoradElevator(); + virtual void arriveAtUpperPressureDoorRoom(); + virtual void arriveAtLowerPressureDoorRoom(); + virtual void arriveAtSubPlatformRoom(); + virtual void arriveAtSubControlRoom(); + void setUpAirMask(); + virtual void receiveNotification(Notification *, const NotificationFlags); + virtual bool playingAgainstRobot() { return false; } + + Notification _noradNotification; + bool _doneWithPressureDoor; + + RoomID _elevatorUpRoomID; + RoomID _elevatorDownRoomID; + HotSpotID _elevatorUpSpotID; + HotSpotID _elevatorDownSpotID; + + TimeBase _airMaskTimer; + NotificationCallBack _airMaskCallBack; + + RoomID _subRoomEntryRoom1; + DirectionConstant _subRoomEntryDir1; + RoomID _subRoomEntryRoom2; + DirectionConstant _subRoomEntryDir2; + RoomID _upperPressureDoorRoom; + RoomID _lowerPressureDoorRoom; + + HotSpotID _upperPressureDoorUpSpotID; + HotSpotID _upperPressureDoorDownSpotID; + HotSpotID _upperPressureDoorAbortSpotID; + + HotSpotID _lowerPressureDoorUpSpotID; + HotSpotID _lowerPressureDoorDownSpotID; + HotSpotID _lowerPressureDoorAbortSpotID; + + TimeValue _pressureSoundIn; + TimeValue _pressureSoundOut; + TimeValue _equalizeSoundIn; + TimeValue _equalizeSoundOut; + TimeValue _accessDeniedIn; + TimeValue _accessDeniedOut; + + RoomID _platformRoom; + RoomID _subControlRoom; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/noradelevator.cpp b/engines/pegasus/neighborhood/norad/noradelevator.cpp new file mode 100644 index 0000000000..1751f4dcb6 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/noradelevator.cpp @@ -0,0 +1,130 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/noradelevator.h" + +namespace Pegasus { + +// Norad elevator PICTs: +static const ResIDType kElevatorLabelID = 200; +static const ResIDType kElevatorButtonsID = 201; +static const ResIDType kElevatorDownOnID = 202; +static const ResIDType kElevatorUpOnID = 203; + +NoradElevator::NoradElevator(Neighborhood *handler, const RoomID upRoom, const RoomID downRoom, + const HotSpotID upHotspot, const HotSpotID downHotspot) : GameInteraction(kNoradElevatorInteractionID, handler), + _elevatorControls(kNoradElevatorControlsID), _elevatorNotification(kNoradElevatorNotificationID, ((PegasusEngine *)g_engine)) { + _timerExpired = false; + _upRoom = upRoom; + _downRoom = downRoom; + _upHotspot = upHotspot; + _downHotspot = downHotspot; +} + +void NoradElevator::openInteraction() { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorLabelID, true); + _elevatorControls.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorButtonsID, true); + _elevatorControls.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorDownOnID, true); + _elevatorControls.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorUpOnID, true); + _elevatorControls.addFrame(frame, 0, 0); + + _elevatorControls.setCurrentFrameIndex(0); + _elevatorControls.setDisplayOrder(kElevatorControlsOrder); + + Common::Rect r; + frame->getSurfaceBounds(r); + r.moveTo(kNoradAlphaElevatorControlsLeft, kNoradAlphaElevatorControlsTop); + + _elevatorControls.setBounds(r); + _elevatorControls.startDisplaying(); + _elevatorControls.show(); +} + +void NoradElevator::initInteraction() { + _elevatorTimer.setScale(2); + _elevatorTimer.setSegment(0, 1); + _elevatorCallBack.initCallBack(&_elevatorTimer, kCallBackAtExtremes); + _elevatorCallBack.setCallBackFlag(1); + _elevatorCallBack.setNotification(&_elevatorNotification); + _elevatorNotification.notifyMe(this, 1, 1); + _elevatorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _elevatorTimer.start(); +} + +void NoradElevator::closeInteraction() { + _elevatorControls.stopDisplaying(); + _elevatorControls.discardFrames(); + _elevatorCallBack.releaseCallBack(); +} + +void NoradElevator::resetInteraction() { + _elevatorControls.setCurrentFrameIndex(1); +} + +void NoradElevator::activateHotspots() { + GameInteraction::activateHotspots(); + + if (_timerExpired) { + if (GameState.getCurrentRoom() == _upRoom) + g_allHotspots.activateOneHotspot(_downHotspot); + else if (GameState.getCurrentRoom() == _downRoom) + g_allHotspots.activateOneHotspot(_upHotspot); + } +} + +void NoradElevator::clickInHotspot(const Input &input, const Hotspot *spot) { + HotSpotID id = spot->getObjectID(); + + if (id == _upHotspot || id == _downHotspot) { + g_neighborhood->moveForward(); + if (id == _downHotspot) + _elevatorControls.setCurrentFrameIndex(2); + else + _elevatorControls.setCurrentFrameIndex(3); + } else { + GameInteraction::clickInHotspot(input, spot); + } +} + +void NoradElevator::receiveNotification(Notification *, const NotificationFlags) { + _elevatorControls.setCurrentFrameIndex(1); + _timerExpired = true; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/noradelevator.h b/engines/pegasus/neighborhood/norad/noradelevator.h new file mode 100644 index 0000000000..24aa488534 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/noradelevator.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class NoradElevator : public GameInteraction, private NotificationReceiver { +public: + NoradElevator(Neighborhood *, const RoomID, const RoomID, const HotSpotID, const HotSpotID); + virtual ~NoradElevator() {} + +protected: + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + virtual void resetInteraction(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + RoomID _upRoom; + RoomID _downRoom; + HotSpotID _upHotspot; + HotSpotID _downHotspot; + Sprite _elevatorControls; + TimeBase _elevatorTimer; + NotificationCallBack _elevatorCallBack; + Notification _elevatorNotification; + bool _timerExpired; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.cpp b/engines/pegasus/neighborhood/norad/pressuredoor.cpp new file mode 100644 index 0000000000..d1378567d3 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuredoor.cpp @@ -0,0 +1,554 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +static const TimeValue kLevelsSplashStart = 0; +static const TimeValue kLevelsSplashStop = 1; +static const TimeValue kPressureBase = 1; + +static const TimeValue kDoorSealedTime = 0; +static const TimeValue kEqualizeTime = 1; +static const TimeValue kMaxPressureLoopStart = 2; +static const TimeValue kMaxPressureLoopStop = 3; +static const TimeValue kOpeningDoorLoopStart = 3; +static const TimeValue kOpeningDoorLoopStop = 4; +static const TimeValue kIncreasingPressureTime = 4; +static const TimeValue kDecreasingPressureTime = 5; +static const TimeValue kCautionLoopStart = 6; +static const TimeValue kCautionLoopStop = 7; + +static const NotificationFlags kSplashFinished = 1; +static const NotificationFlags kPressureDroppingFlag = kSplashFinished << 1; + +static const NotificationFlags kPressureNotificationFlags = kSplashFinished | + kPressureDroppingFlag; + +static const NotificationFlags kDoorJumpsUpFlag = 1; +static const NotificationFlags kDoorJumpsBackFlag = kDoorJumpsUpFlag << 1; +static const NotificationFlags kDoorCrushedFlag = kDoorJumpsBackFlag << 1; + +static const NotificationFlags kUtilityNotificationFlags = kDoorJumpsUpFlag | + kDoorJumpsBackFlag | + kDoorCrushedFlag; + +enum { + kPlayingRobotApproaching, + kRobotPunching, + kRobotComingThrough, + kRobotDying, + kRobotDead +}; + +const short kMaxPunches = 5; + +enum { + kPlayingSplash, + kPlayingPressureMessage, + kPlayingEqualizeMessage, + kWaitingForPlayer, + kPlayingDoneMessage, + kGameOver +}; + +// Pressure values range from 0 to 11. +static const short kMinPressure = 0; +static const short kMaxPressure = 11; + +static const TimeScale kNavTimeScale = 600; +static const TimeValue kNavFrameRate = 15; +static const TimeValue kNavTimePerFrame = kNavTimeScale / kNavFrameRate; + +static const TimeValue kApproachPunchInTime = 122 * kNavTimePerFrame; +static const TimeValue kLoopPunchInTime = 38 * kNavTimePerFrame; +static const TimeValue kPunchThroughTime = 38 * kNavTimePerFrame; + +// Pressure door PICTs: +static const ResIDType kUpperPressureUpOffPICTID = 400; +static const ResIDType kUpperPressureUpOnPICTID = 401; +static const ResIDType kUpperPressureDownOffPICTID = 402; +static const ResIDType kUpperPressureDownOnPICTID = 403; + +static const ResIDType kLowerPressureUpOffPICTID = 404; +static const ResIDType kLowerPressureUpOnPICTID = 405; +static const ResIDType kLowerPressureDownOffPICTID = 406; +static const ResIDType kLowerPressureDownOnPICTID = 407; + +PressureDoor::PressureDoor(Neighborhood *handler, bool isUpperDoor, const HotSpotID upSpotID, const HotSpotID downSpotID, + const HotSpotID outSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, TimeValue equalizeSoundIn, + TimeValue equalizeSoundOut) : GameInteraction(kNoradPressureDoorInteractionID, handler), + _levelsMovie(kPressureDoorLevelsID), _typeMovie(kPressureDoorTypeID), _upButton(kPressureDoorUpButtonID), + _downButton(kPressureDoorDownButtonID), _pressureNotification(kNoradPressureNotificationID, ((PegasusEngine *)g_engine)), + _doorTracker(this), _utilityNotification(kNoradUtilityNotificationID, ((PegasusEngine *)g_engine)) { + _neighborhoodNotification = handler->getNeighborhoodNotification(); + _upHotspotID = upSpotID; + _downHotspotID = downSpotID; + _outHotspotID = outSpotID; + _pressureSoundIn = pressureSoundIn; + _pressureSoundOut = pressureSoundOut; + _equalizeSoundIn = equalizeSoundIn; + _equalizeSoundOut = equalizeSoundOut; + _playingAgainstRobot = false; + _isUpperDoor = isUpperDoor; +} + +void PressureDoor::openInteraction() { + if (_isUpperDoor) { + _levelsMovie.initFromMovieFile("Images/Norad Alpha/Upper Levels Movie"); + _levelsMovie.moveElementTo(kNoradUpperLevelsLeft, kNoradUpperLevelsTop); + } else { + _levelsMovie.initFromMovieFile("Images/Norad Alpha/Lower Levels Movie"); + _levelsMovie.moveElementTo(kNoradLowerLevelsLeft, kNoradLowerLevelsTop); + } + + _levelsScale = _levelsMovie.getScale(); + _levelsMovie.setDisplayOrder(kPressureLevelsOrder); + _levelsMovie.startDisplaying(); + _levelsMovie.setSegment(kLevelsSplashStart * _levelsScale, kLevelsSplashStop * _levelsScale); + _levelsMovie.setTime(kLevelsSplashStart * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _levelsMovie.show(); + + _pressureCallBack.setNotification(&_pressureNotification); + _pressureCallBack.initCallBack(&_levelsMovie, kCallBackAtExtremes); + _pressureCallBack.setCallBackFlag(kSplashFinished); + _pressureCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + _pressureNotification.notifyMe(this, kPressureNotificationFlags, kPressureNotificationFlags); + + if (_isUpperDoor) { + _typeMovie.initFromMovieFile("Images/Norad Alpha/Upper Type Movie"); + _typeMovie.moveElementTo(kNoradUpperTypeLeft, kNoradUpperTypeTop); + } else { + _typeMovie.initFromMovieFile("Images/Norad Alpha/Lower Type Movie"); + _typeMovie.moveElementTo(kNoradLowerTypeLeft, kNoradLowerTypeTop); + } + + _typeScale = _typeMovie.getScale(); + _typeMovie.setDisplayOrder(kPressureTypeOrder); + _typeMovie.startDisplaying(); + _typeMovie.setTime(kDoorSealedTime * _typeScale); + _typeMovie.redrawMovieWorld(); + + SpriteFrame *frame = new SpriteFrame(); + if (_isUpperDoor) + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOffPICTID); + else + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOffPICTID); + _upButton.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + if (_isUpperDoor) + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOnPICTID); + else + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOnPICTID); + _upButton.addFrame(frame, 0, 0); + + _upButton.setCurrentFrameIndex(0); + _upButton.setDisplayOrder(kPressureUpOrder); + + Common::Rect r; + frame->getSurfaceBounds(r); + if (_isUpperDoor) + r.moveTo(kNoradUpperUpLeft, kNoradUpperUpTop); + else + r.moveTo(kNoradLowerUpLeft, kNoradLowerUpTop); + + _upButton.setBounds(r); + _upButton.startDisplaying(); + _upButton.show(); + + frame = new SpriteFrame(); + if (_isUpperDoor) + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOffPICTID); + else + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOffPICTID); + _downButton.addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + if (_isUpperDoor) + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOnPICTID); + else + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOnPICTID); + _downButton.addFrame(frame, 0, 0); + + _downButton.setCurrentFrameIndex(0); + _downButton.setDisplayOrder(kPressureDownOrder); + + frame->getSurfaceBounds(r); + if (_isUpperDoor) + r.moveTo(kNoradUpperDownLeft, kNoradUpperDownTop); + else + r.moveTo(kNoradLowerDownLeft, kNoradLowerDownTop); + + _downButton.setBounds(r); + _downButton.startDisplaying(); + _downButton.show(); + + _utilityCallBack.setNotification(&_utilityNotification); + _utilityCallBack.initCallBack(&_utilityTimer, kCallBackAtTime); + _utilityNotification.notifyMe(this, kUtilityNotificationFlags, kUtilityNotificationFlags); + _utilityTimer.setMasterTimeBase(getOwner()->getNavMovie()); + + if (_playingAgainstRobot) + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag | kDelayCompletedFlag | + kSpotSoundCompletedFlag, kExtraCompletedFlag | kDelayCompletedFlag | kSpotSoundCompletedFlag); + else + _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, + kDelayCompletedFlag | kSpotSoundCompletedFlag); + + _gameState = kPlayingSplash; +} + +void PressureDoor::initInteraction() { + _levelsMovie.start(); + + if (_playingAgainstRobot) { + ExtraTable::Entry entry; + _owner->getExtraEntry(kN59RobotApproaches, entry); + _utilityTimer.setSegment(entry.movieStart, entry.movieEnd); + _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); + _punchInTime = kApproachPunchInTime + entry.movieStart; + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); + _utilityTimer.setTime(entry.movieStart); + _owner->startExtraSequence(kN59RobotApproaches, kExtraCompletedFlag, kFilterAllInput); + _utilityTimer.start(); + _robotState = kPlayingRobotApproaching; + } + + _levelsMovie.redrawMovieWorld(); +} + +void PressureDoor::closeInteraction() { + _pressureNotification.cancelNotification(this); + _pressureCallBack.releaseCallBack(); + _utilityNotification.cancelNotification(this); + _utilityCallBack.releaseCallBack(); + _neighborhoodNotification->cancelNotification(this); +} + +void PressureDoor::playAgainstRobot() { + _playingAgainstRobot = true; +} + +void PressureDoor::receiveNotification(Notification *notification, const NotificationFlags flags) { + Neighborhood *owner = getOwner(); + + if (notification == _neighborhoodNotification) { + if (_playingAgainstRobot && (flags & kExtraCompletedFlag) != 0) { + ExtraTable::Entry entry; + + switch (_robotState) { + case kPlayingRobotApproaching: + _utilityTimer.stop(); + if (GameState.getNoradSubRoomPressure() == kMaxPressure) { + owner->getExtraEntry(kN59PlayerWins1, entry); + _utilityTimer.setSegment(entry.movieStart, entry.movieEnd); + _utilityTimer.setTime(entry.movieStart); + _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); + _punchInTime = kLoopPunchInTime + entry.movieStart; + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); + owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput); + _utilityTimer.start(); + _robotState = kRobotDying; + } else { + owner->getExtraEntry(kN59RobotPunchLoop, entry); + _utilityTimer.setSegment(entry.movieStart, entry.movieEnd); + _utilityTimer.setTime(entry.movieStart); + _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); + _punchInTime = kLoopPunchInTime + entry.movieStart; + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); + owner->startSpotLoop(entry.movieStart, entry.movieEnd, kExtraCompletedFlag); + _utilityTimer.start(); + _robotState = kRobotPunching; + _punchCount = 1; + } + break; + case kRobotPunching: + if (GameState.getNoradSubRoomPressure() == kMaxPressure) { + owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput); + _robotState = kRobotDying; + } else if (++_punchCount >= kMaxPunches) { + _robotState = kRobotComingThrough; + owner->getExtraEntry(kN59RobotWins, entry); + _utilityTimer.stop(); + _utilityTimer.setSegment(entry.movieStart, entry.movieEnd); + _utilityTimer.setTime(entry.movieStart); + _utilityCallBack.cancelCallBack(); + _utilityCallBack.setCallBackFlag(kDoorCrushedFlag); + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, kPunchThroughTime + entry.movieStart, kNavTimeScale); + owner->startExtraSequence(kN59RobotWins, kExtraCompletedFlag, kFilterNoInput); + _utilityTimer.start(); + } else { + _utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); + owner->scheduleNavCallBack(kExtraCompletedFlag); + } + break; + case kRobotComingThrough: + g_system->delayMillis(2 * 1000); + ((PegasusEngine *)g_engine)->die(kDeathRobotThroughNoradDoor); + break; + case kRobotDying: + _robotState = kRobotDead; + _levelsMovie.stop(); + _levelsMovie.setSegment((kNormalSubRoomPressure + kPressureBase) * _levelsScale, + (GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); + _pressureCallBack.setCallBackFlag(kPressureDroppingFlag); + _pressureCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + _typeMovie.stop(); + _typeMovie.setSegment(0, _typeMovie.getDuration()); + _typeMovie.setTime(kDecreasingPressureTime * _typeScale); + _typeMovie.redrawMovieWorld(); + _typeMovie.show(); + _downButton.show(); + _downButton.setCurrentFrameIndex(1); + _gameState = kGameOver; + allowInput(false); + _levelsMovie.setRate(Common::Rational(0x5555, 0x10000) - 1); // Should match door tracker. + break; + case kRobotDead: + allowInput(true); + ((NoradDelta *)owner)->playerBeatRobotWithDoor(); + owner->requestDeleteCurrentInteraction(); + break; + } + } + + if ((flags & (kDelayCompletedFlag | kSpotSoundCompletedFlag)) != 0) { + switch (_gameState) { + case kPlayingPressureMessage: + _typeMovie.setTime(kEqualizeTime * _typeScale); + _typeMovie.redrawMovieWorld(); + owner->requestDelay(1, 5, kFilterNoInput, 0); + owner->requestSpotSound(_equalizeSoundIn, _equalizeSoundOut, kFilterNoInput, 0); + owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingEqualizeMessage; + break; + case kPlayingEqualizeMessage: + _gameState = kWaitingForPlayer; + stopChangingPressure(); + break; + case kPlayingDoneMessage: + _gameState = kWaitingForPlayer; + _typeMovie.stop(); + _typeMovie.setFlags(0); + _typeMovie.hide(); + if (!_playingAgainstRobot) + ((Norad *)_owner)->doneWithPressureDoor(); + break; + } + } + } else if (notification == &_pressureNotification) { + switch (flags) { + case kSplashFinished: + _levelsMovie.stop(); + _levelsMovie.setSegment(0, _levelsMovie.getDuration()); + _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + + if (GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) { + _typeMovie.show(); + owner->requestDelay(1, 5, kFilterNoInput, 0); + owner->requestSpotSound(_pressureSoundIn, _pressureSoundOut, kFilterNoInput, 0); + owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingPressureMessage; + } else { + _gameState = kWaitingForPlayer; + } + break; + case kPressureDroppingFlag: + _levelsMovie.stop(); + _levelsMovie.hide(); + _typeMovie.stop(); + _typeMovie.hide(); + _upButton.hide(); + _downButton.hide(); + owner->startExtraSequence(kN59PlayerWins2, kExtraCompletedFlag, kFilterNoInput); + break; + } + } else if (notification == &_utilityNotification) { + switch (flags) { + case kDoorJumpsUpFlag: + _utilityCallBack.setCallBackFlag(kDoorJumpsBackFlag); + _utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime + kNavTimePerFrame, kNavTimeScale); + _levelsMovie.hide(); + _typePunched = _typeMovie.isVisible(); + if (_typePunched == true) + _typeMovie.hide(); + _upButton.hide(); + _downButton.hide(); + break; + case kDoorJumpsBackFlag: + _levelsMovie.show(); + _upButton.show(); + _downButton.show(); + if (_typePunched) + _typeMovie.show(); + break; + case kDoorCrushedFlag: + _levelsMovie.hide(); + _typeMovie.hide(); + _upButton.hide(); + _downButton.hide(); + break; + } + } +} + +void PressureDoor::activateHotspots() { + GameInteraction::activateHotspots(); + + switch (_gameState) { + case kWaitingForPlayer: + g_allHotspots.activateOneHotspot(_upHotspotID); + g_allHotspots.activateOneHotspot(_downHotspotID); + if (!_playingAgainstRobot) + g_allHotspots.activateOneHotspot(_outHotspotID); + break; + default: + break; + } +} + +void PressureDoor::clickInHotspot(const Input &input, const Hotspot *spot) { + HotSpotID id = spot->getObjectID(); + + if (id == _upHotspotID || id == _downHotspotID) { + if (id == _upHotspotID) + _doorTracker.setTrackParameters(spot, &_upButton); + else + _doorTracker.setTrackParameters(spot, &_downButton); + + _doorTracker.startTracking(input); + } else { + GameInteraction::clickInHotspot(input, spot); + } +} + +void PressureDoor::incrementPressure(const HotSpotID id) { + _typeMovie.stop(); + _typeMovie.setSegment(0, _typeMovie.getDuration()); + _typeMovie.setFlags(0); + + if (id == _upHotspotID) { + if (GameState.getNoradSubRoomPressure() < kMaxPressure) { + GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() + 1); + _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _typeMovie.setTime(kIncreasingPressureTime * _typeScale); + _typeMovie.redrawMovieWorld(); + _typeMovie.show(); + g_AIArea->checkMiddleArea(); + } else { + _typeMovie.hide(); + } + } else if (id == _downHotspotID) { + if (GameState.getNoradSubRoomPressure() > kMinPressure) { + GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() - 1); + _levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _typeMovie.setTime(kDecreasingPressureTime * _typeScale); + _typeMovie.redrawMovieWorld(); + _typeMovie.show(); + g_AIArea->checkMiddleArea(); + } else { + _typeMovie.hide(); + } + } +} + +void PressureDoor::stopChangingPressure() { + Neighborhood *owner; + + switch (GameState.getNoradSubRoomPressure()) { + case 11: + _typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + _typeMovie.start(); + break; + case 10: + _typeMovie.setSegment(kCautionLoopStart * _typeScale, kCautionLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + _typeMovie.start(); + break; + case kNormalSubRoomPressure: + owner = getOwner(); + _typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + _gameState = kPlayingDoneMessage; + owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); + _typeMovie.start(); + break; + default: + _typeMovie.hide(); + break; + } +} + +bool PressureDoor::canSolve() { + if (_playingAgainstRobot) + return GameState.getNoradSubRoomPressure() < 11; + + return GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure; +} + +void PressureDoor::doSolve() { + if (_playingAgainstRobot) { + GameState.setNoradSubRoomPressure(11); + _levelsMovie.setTime((11 + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + _typeMovie.start(); + g_AIArea->checkMiddleArea(); + } else { + GameState.setNoradSubRoomPressure(kNormalSubRoomPressure); + _levelsMovie.setTime((kNormalSubRoomPressure + kPressureBase) * _levelsScale); + _levelsMovie.redrawMovieWorld(); + _typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale); + _typeMovie.setFlags(kLoopTimeBase); + _typeMovie.show(); + Neighborhood *owner = getOwner(); + owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); + _gameState = kPlayingDoneMessage; + _typeMovie.start(); + g_AIArea->checkMiddleArea(); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.h b/engines/pegasus/neighborhood/norad/pressuredoor.h new file mode 100644 index 0000000000..b2018bfcf7 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuredoor.h @@ -0,0 +1,93 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/neighborhood/norad/pressuretracker.h" + +namespace Pegasus { + +static const short kNormalSubRoomPressure = 2; + +class PressureDoor : public GameInteraction, public NotificationReceiver { +public: + PressureDoor(Neighborhood *, bool isUpperDoor, const HotSpotID, const HotSpotID, + const HotSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, + TimeValue equalizeSoundIn, TimeValue equalizeSoundOut); + virtual ~PressureDoor() {} + + void incrementPressure(const HotSpotID); + void stopChangingPressure(); + + void playAgainstRobot(); + + bool canSolve(); + void doSolve(); + +protected: + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + Movie _levelsMovie; + TimeScale _levelsScale; + Movie _typeMovie; + TimeScale _typeScale; + Sprite _upButton; + Sprite _downButton; + Notification _pressureNotification; + NotificationCallBack _pressureCallBack; + Notification *_neighborhoodNotification; + int _gameState; + HotSpotID _upHotspotID; + HotSpotID _downHotspotID; + HotSpotID _outHotspotID; + PressureTracker _doorTracker; + TimeValue _pressureSoundIn; + TimeValue _pressureSoundOut; + TimeValue _equalizeSoundIn; + TimeValue _equalizeSoundOut; + bool _isUpperDoor; + + bool _playingAgainstRobot, _typePunched; + int _robotState, _punchCount; + TimeBase _utilityTimer; + Notification _utilityNotification; + NotificationCallBack _utilityCallBack; + TimeValue _punchInTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.cpp b/engines/pegasus/neighborhood/norad/pressuretracker.cpp new file mode 100644 index 0000000000..5aac19dcbe --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuretracker.cpp @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/hotspot.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/pressuretracker.h" + +namespace Pegasus { + +PressureTracker::PressureTracker(PressureDoor *pressureDoor) { + _pressureDoor = pressureDoor; + _trackSpot = 0; + _trackTime = 0; +} + +void PressureTracker::setTrackParameters(const Hotspot *trackSpot, Sprite *trackButton) { + _trackSpot = trackSpot; + _trackButton = trackButton; + _trackTime = 0; +} + +void PressureTracker::activateHotspots() { + Tracker::activateHotspots(); + + if (_trackSpot) + g_allHotspots.activateOneHotspot(_trackSpot->getObjectID()); +} + +// For click-hold dragging. +bool PressureTracker::stopTrackingInput(const Input &input) { + return !JMPPPInput::isPressingInput(input); +} + +void PressureTracker::continueTracking(const Input &input) { + Common::Point where; + input.getInputLocation(where); + + if (g_allHotspots.findHotspot(where) == _trackSpot) { + trackPressure(); + _trackButton->setCurrentFrameIndex(1); + } else { + _trackButton->setCurrentFrameIndex(0); + } +} + +void PressureTracker::startTracking(const Input &input) { + Tracker::startTracking(input); + trackPressure(); +} + +void PressureTracker::stopTracking(const Input &input) { + _trackButton->setCurrentFrameIndex(0); + _pressureDoor->stopChangingPressure(); + Tracker::stopTracking(input); +} + +void PressureTracker::trackPressure() { + if (g_system->getMillis() - _trackTime > kPressureDoorTrackInterval * 1000 / 60) { + _pressureDoor->incrementPressure(_trackSpot->getObjectID()); + _trackTime = g_system->getMillis(); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.h b/engines/pegasus/neighborhood/norad/pressuretracker.h new file mode 100644 index 0000000000..6ca7252e22 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuretracker.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H +#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H + +#include "pegasus/input.h" + +namespace Pegasus { + +// This class assumes that the globe movie is built at 15 frames per second with a +// time scale of 600, yielding 40 time unit per frame. + +enum PressureTrackDirection { + kTrackPressureUp, + kTrackPressureDown +}; + +static const int kPressureDoorTrackInterval = 45; + +class PressureDoor; +class Sprite; + +class PressureTracker : public Tracker { +public: + PressureTracker(PressureDoor *); + virtual ~PressureTracker() {} + + void setTrackParameters(const Hotspot *, Sprite *); + void continueTracking(const Input &); + void startTracking(const Input &); + void stopTracking(const Input &); + void activateHotspots(); + bool stopTrackingInput(const Input &); + +protected: + void trackPressure(); + + PressureDoor *_pressureDoor; + const Hotspot *_trackSpot; + Sprite *_trackButton; + long _trackTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp new file mode 100644 index 0000000000..d48481e925 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp @@ -0,0 +1,1178 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +// Right Monitor times + +static const TimeValue kAlphaClawSplashStart = 0; +static const TimeValue kAlphaClawSplashStop = 4000; + +static const TimeValue kDeltaClawSplashStart = 4000; +static const TimeValue kDeltaClawSplashStop = 8000; + +static const TimeValue kClawAtATime = 8000; +static const TimeValue kClawAtAPinchedTime = 8600; +static const TimeValue kClawAtATurnedTime = 9200; +static const TimeValue kClawAtAWithRobotPinchedTime = 9800; + +static const TimeValue kClawAtBTime = 10400; +static const TimeValue kClawAtBPinchedTime = 11000; +static const TimeValue kClawAtBTurnedTime = 11600; +static const TimeValue kClawAtBWithRobotTime = 12200; +static const TimeValue kClawAtBWithRobotPinchedTime = 12800; + +static const TimeValue kClawAtCTime = 13400; +static const TimeValue kClawAtCPinchedTime = 14000; +static const TimeValue kClawAtCTurnedTime = 14600; + +static const TimeValue kClawAtDTime = 15200; +static const TimeValue kClawAtDPinchedTime = 15800; +static const TimeValue kClawAtDTurnedTime = 16400; + +static const TimeValue kAToBStart = 17000; +static const TimeValue kAToBStop = 18680; +static const TimeValue kAPinchStart = 18680; +static const TimeValue kAPinchStop = 20200; +static const TimeValue kACCWStart = 20200; +static const TimeValue kACCWStop = 21600; +static const TimeValue kACWStart = 21600; +static const TimeValue kACWStop = 23000; + +static const TimeValue kBToAStart = 23000; +static const TimeValue kBToAStop = 24680; +static const TimeValue kBToCStart = 24680; +static const TimeValue kBToCStop = 26520; +static const TimeValue kBToDStart = 26520; +static const TimeValue kBToDStop = 28320; +static const TimeValue kBPinchStart = 28320; +static const TimeValue kBPinchStop = 29680; +static const TimeValue kBCCWStart = 29680; +static const TimeValue kBCCWStop = 31200; +static const TimeValue kBCWStart = 31200; +static const TimeValue kBCWStop = 32720; + +static const TimeValue kCToBStart = 32720; +static const TimeValue kCToBStop = 34560; +static const TimeValue kCPinchStart = 34560; +static const TimeValue kCPinchStop = 36400; +static const TimeValue kCCCWStart = 36400; +static const TimeValue kCCCWStop = 37840; +static const TimeValue kCCWStart = 37840; +static const TimeValue kCCWStop = 39280; + +static const TimeValue kDToBStart = 39280; +static const TimeValue kDToBStop = 41080; +static const TimeValue kDPinchStart = 41080; +static const TimeValue kDPinchStop = 42600; +static const TimeValue kDCCWStart = 42600; +static const TimeValue kDCCWStop = 44000; +static const TimeValue kDCWStart = 44000; +static const TimeValue kDCWStop = 45400; + +static const TimeValue kRobotApproachStart = 45400; +static const TimeValue kRobotApproachStop = 56800; + +static const TimeValue kCToBWithRobotStart = 56800; +static const TimeValue kCToBWithRobotStop = 58600; + +static const TimeValue kBPinchWithRobotStart = 58600; +static const TimeValue kBPinchWithRobotStop = 60400; +static const TimeValue kBToAWithRobotStart = 60400; +static const TimeValue kBToAWithRobotStop = 62240; + +// As usual, times here are in seconds. + +// Left monitor times. + +static const TimeValue kAlphaSplashStart = 0; +static const TimeValue kAlphaSplashStop = 2; + +static const TimeValue kMainMenuTime = 2; +static const TimeValue kLaunchPrepRolloverTime = 3; +static const TimeValue kLaunchPrepHighlightStart = 4; +static const TimeValue kLaunchPrepHighlightStop = 5; +static const TimeValue kClawControlRolloverTime = 5; +static const TimeValue kClawControlHighlightStart = 6; +static const TimeValue kClawControlHighlightStop = 7; + +static const TimeValue kAlphaLaunchPrepStart = 7; +static const TimeValue kAlphaLaunchPrepStop = 17; + +static const TimeValue kClawMenuStart = 17; +static const TimeValue kClawMenuStop = 18; + +static const TimeValue kClawMenuTime = 18; + +static const TimeValue kDeltaSplashStart = 19; +static const TimeValue kDeltaSplashStop = 21; + +static const TimeValue kDeltaLaunchPrepStart = 21; +static const TimeValue kDeltaLaunchPrepStop = 30; + +// Right monitor times. + +static const NotificationFlags kAlphaSplashFinished = 1; +static const NotificationFlags kAlphaPrepFinished = kAlphaSplashFinished << 1; +static const NotificationFlags kPrepHighlightFinished = kAlphaPrepFinished << 1; +static const NotificationFlags kClawHighlightFinished = kPrepHighlightFinished << 1; +static const NotificationFlags kClawMenuFinished = kClawHighlightFinished << 1; +static const NotificationFlags kDeltaSplashFinished = kClawMenuFinished << 1; +static const NotificationFlags kDeltaPrepFinished = kDeltaSplashFinished << 1; + +static const NotificationFlags kSubControlNotificationFlags = kAlphaSplashFinished | + kAlphaPrepFinished | + kPrepHighlightFinished | + kClawHighlightFinished | + kClawMenuFinished | + kDeltaSplashFinished | + kDeltaPrepFinished; + +static const NotificationFlags kOneSecondOfMoveFinished = 1; + +static const NotificationFlags kGreenBallNotificationFlags = kOneSecondOfMoveFinished; + +enum { + kButtonDimFrame, + kButtonActiveFrame, + kButtonHighlightedFrame +}; + +enum { + kAlphaSplash, + kAlphaMainMenu, + kDeltaSplash, + kDeltaMainMenu, + kClawMenu, + kPlayingHighlight, + kPuttingClawAway +}; + +// The owning neighborhood must provide an array of longs which hold the various +// extra IDs for moving the claw around. In addition, the owner must tell the sub +// control room interaction what position the claw starts out in (which is also the +// position the claw must be in before leaving). + +// Standard array indices: +enum { + kClawFromAToBIndex, + kClawALoopIndex, + kClawAPinchIndex, + kClawACounterclockwiseIndex, + kClawAClockwiseIndex, + kClawFromBToAIndex, + kClawFromBToCIndex, + kClawFromBToDIndex, + kClawBLoopIndex, + kClawBPinchIndex, + kClawBCounterclockwiseIndex, + kClawBClockwiseIndex, + kClawFromCToBIndex, + kClawCLoopIndex, + kClawCPinchIndex, + kClawCCounterclockwiseIndex, + kClawCClockwiseIndex, + kClawFromDToBIndex, + kClawDLoopIndex, + kClawDPinchIndex, + kClawDCounterclockwiseIndex, + kClawDClockwiseIndex +}; + +// Action indices for s_clawStateTable: +// Can also be used as indices into _buttons (except for kNoActionIndex and kLoopActionIndex). +enum { + kNoActionIndex = -1, + kPinchActionIndex = 0, + kMoveDownActionIndex, + kMoveRightActionIndex, + kMoveLeftActionIndex, + kMoveUpActionIndex, + kCCWActionIndex, + kCWActionIndex, + kLoopActionIndex +}; + +/* + _currentAction and _nextAction: + + At any time, _currentAction contains an action index (defined above). The current + action index is what the claw is doing right now. If the player presses a button + before the current action completes, _nextAction saves the new action and input + is disabled. This has the effect of implementing a queue of commands for the claw + that can save at most one extra command. + + The general strategy for using _currentAction and _nextAction are: + -- If the player presses a claw button and _currentAction is kNoActionIndex, + do the command immediately and set _currentAction accordingly. + -- If the player presses a claw button and _currentAction is not kNoActionIndex, + save the appropriate action index in _nextAction. + -- When a command animation completes, set _nextAction to kNoActionIndex, then + check if _nextAction has a command waiting in it. If so, play the appriate + animation, copy _nextAction into _currentAction and set _nextAction to + kNoActionIndex. + -- If the player tries to get up, disable input (including all claw buttons) until + the player rises. Then, if the claw is in its original position, play the + animation of the player rising. + -- If the claw needs to be put back, play the first move required to put the + claw back by setting _currentAction and playing the appropriate animation. + Leave _nextAction alone. When the animation, completes, check to see if the + claw is in its original position or not. If so, complete the player rising + sequence by playing the rising animation. If not, repeat this whole step. + + Using this general strategy allows the use of a single function, + DispatchClawAction, which can both cause the claw to perform a command and saving + the next command in _nextAction. +*/ + +// Array indexed by [claw position] [action] +// array yields an index into the neighborhood's extra id table for claw animation or -1. +static const int s_clawStateTable[4][8] = { + { + kClawAPinchIndex, + kNoActionIndex, + kNoActionIndex, + kClawFromAToBIndex, + kNoActionIndex, + kClawACounterclockwiseIndex, + kClawAClockwiseIndex, + kClawALoopIndex + }, + { + kClawBPinchIndex, + kNoActionIndex, + kClawFromBToAIndex, + kClawFromBToDIndex, + kClawFromBToCIndex, + kClawBCounterclockwiseIndex, + kClawBClockwiseIndex, + kClawBLoopIndex + }, + { + kClawCPinchIndex, + kClawFromCToBIndex, + kNoActionIndex, + kNoActionIndex, + kNoActionIndex, + kClawCCounterclockwiseIndex, + kClawCClockwiseIndex, + kClawCLoopIndex + }, + { + kClawDPinchIndex, + kNoActionIndex, + kClawFromDToBIndex, + kNoActionIndex, + kNoActionIndex, + kClawDCounterclockwiseIndex, + kClawDClockwiseIndex, + kClawDLoopIndex + } +}; + +// Move directions for s_clawMovieTable: +enum { + kMoveClawDown, + kMoveClawRight, + kMoveClawLeft, + kMoveClawUp +}; + +static const int kClawNowhere = -1; + +// Array indexed by [claw position] [move direction] +// array yields new claw position or -1. +static const int s_clawMovieTable[4][4] = { + { + kClawNowhere, + kClawNowhere, + kClawAtB, + kClawNowhere + }, + { + kClawNowhere, + kClawAtA, + kClawAtD, + kClawAtC + }, + { + kClawAtB, + kClawNowhere, + kClawNowhere, + kClawNowhere + }, + { + kClawNowhere, + kClawAtB, + kClawNowhere, + kClawNowhere + } +}; + +// Indexed by claw action index, claw position, plus 0 for start, 1 for stop. +// (Never indexed with kLoopActionIndex.) +static const TimeValue s_clawMonitorTable[7][4][2] = { + { + { kAPinchStart, kAPinchStop }, + { kBPinchStart, kBPinchStop }, + { kCPinchStart, kCPinchStop }, + { kDPinchStart, kDPinchStop } + }, + { + { 0xffffffff, 0xffffffff }, + { 0xffffffff, 0xffffffff }, + { kCToBStart, kCToBStop }, + { 0xffffffff, 0xffffffff } + }, + { + { 0xffffffff, 0xffffffff }, + { kBToAStart, kBToAStop }, + { 0xffffffff, 0xffffffff }, + { kDToBStart, kDToBStop } + }, + { + { kAToBStart, kAToBStop }, + { kBToDStart, kBToDStop }, + { 0xffffffff, 0xffffffff }, + { 0xffffffff, 0xffffffff } + }, + { + { 0xffffffff, 0xffffffff }, + { kBToCStart, kBToCStop }, + { 0xffffffff, 0xffffffff }, + { 0xffffffff, 0xffffffff } + }, + { + { kACCWStart, kACCWStop }, + { kBCCWStart, kBCCWStop }, + { kCCCWStart, kCCCWStop }, + { kDCCWStart, kDCCWStop } + }, + { + { kACWStart, kACWStop }, + { kBCWStart, kBCWStop }, + { kCCWStart, kCCWStop }, + { kDCWStart, kDCWStop } + } +}; + +// Frame indices for the green ball sprite. +enum { + kGreenBallAtA, + kGreenBallAtAWithClaw, + kGreenBallAtAWithClawAndRobot, + kGreenBallAtB, + kGreenBallAtBWithClaw, + kGreenBallAtBWithClawAndRobot, + kGreenBallAtCArmAtA, + kGreenBallAtCArmAtB, + kGreenBallAtCArmAtD, + kGreenBallAtCWithClaw, + kGreenBallAtD, + kGreenBallAtDWithClaw, + kNumClawGreenBalls +}; + +// State constants for _robotState. +enum { + kNoRobot, + kRobotApproaching, + kPunchingOnce, + kPunchingTwice, + kPunchingThrice, + kCarriedToDoor, + kPlayerWon, + kRobotWon +}; + +// Sub Control Room button PICTs: +static const ResIDType kSubControlButtonBaseID = 500; +static const ResIDType kClawMonitorGreenBallBaseID = 600; + +// Constructor +SubControlRoom::SubControlRoom(Neighborhood *handler) : GameInteraction(kNoradSubControlRoomInteractionID, handler), + _subControlMovie(kSubControlMonitorID), _subControlNotification(kSubControlNotificationID, (PegasusEngine *)g_engine), + _clawMonitorMovie(kClawMonitorID), _pinchButton(kSubControlPinchID), _downButton(kSubControlDownID), + _rightButton(kSubControlRightID), _leftButton(kSubControlLeftID), _upButton(kSubControlUpID), + _ccwButton(kSubControlCCWID), _cwButton(kSubControlCWID), _greenBall(kClawMonitorGreenBallID), + _greenBallNotification(kNoradGreenBallNotificationID, (PegasusEngine *)g_engine) { + _neighborhoodNotification = handler->getNeighborhoodNotification(); + _playingAgainstRobot = false; + _robotState = kNoRobot; +} + +void SubControlRoom::playAgainstRobot() { + _playingAgainstRobot = true; +} + +void SubControlRoom::openInteraction() { + _currentAction = kNoActionIndex; + _nextAction = kNoActionIndex; + + Norad *owner = (Norad *)getOwner(); + owner->getClawInfo(_outSpotID, _prepSpotID, _clawControlSpotID, _clawButtonSpotIDs[0], + _clawButtonSpotIDs[1], _clawButtonSpotIDs[2], _clawButtonSpotIDs[3], + _clawButtonSpotIDs[4], _clawButtonSpotIDs[5], _clawButtonSpotIDs[6], + _clawStartPosition, _clawExtraIDs); + + _clawPosition = _clawStartPosition; + _clawNextPosition = _clawPosition; + _subControlMovie.initFromMovieFile("Images/Norad Alpha/N22 Left Monitor Movie"); + _subControlMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + _subControlMovie.moveElementTo(kNoradSubControlLeft, kNoradSubControlTop); + _subControlScale = _subControlMovie.getScale(); + _subControlMovie.setDisplayOrder(kSubControlOrder); + _subControlMovie.startDisplaying(); + _subControlCallBack.setNotification(&_subControlNotification); + _subControlCallBack.initCallBack(&_subControlMovie, kCallBackAtExtremes); + + _clawMonitorMovie.initFromMovieFile("Images/Norad Alpha/N22:N60 Right Monitor"); + _clawMonitorMovie.moveElementTo(kNoradClawMonitorLeft, kNoradClawMonitorTop); + _clawMonitorMovie.setDisplayOrder(kClawMonitorOrder); + _clawMonitorMovie.startDisplaying(); + _clawMonitorCallBack.setNotification(&_subControlNotification); + _clawMonitorCallBack.initCallBack(&_clawMonitorMovie, kCallBackAtExtremes); + + _subControlNotification.notifyMe(this, kSubControlNotificationFlags, kSubControlNotificationFlags); + + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + + _buttons[0] = &_pinchButton; + _buttons[1] = &_downButton; + _buttons[2] = &_rightButton; + _buttons[3] = &_leftButton; + _buttons[4] = &_upButton; + _buttons[5] = &_ccwButton; + _buttons[6] = &_cwButton; + + _pinchButton.setDisplayOrder(kSubControlPinchOrder); + _downButton.setDisplayOrder(kSubControlDownOrder); + _rightButton.setDisplayOrder(kSubControlRightOrder); + _leftButton.setDisplayOrder(kSubControlLeftOrder); + _upButton.setDisplayOrder(kSubControlUpOrder); + _ccwButton.setDisplayOrder(kSubControlCCWOrder); + _cwButton.setDisplayOrder(kSubControlCWOrder); + + for (int i = 0; i < kNumClawButtons; i++) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3, true); + _buttons[i]->addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 1, true); + _buttons[i]->addFrame(frame, 0, 0); + + frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 2, true); + _buttons[i]->addFrame(frame, 0, 0); + + _buttons[i]->setCurrentFrameIndex(0); + _buttons[i]->startDisplaying(); + } + + _pinchButton.moveElementTo(kNoradSubControlPinchLeft, kNoradSubControlPinchTop); + _downButton.moveElementTo(kNoradSubControlDownLeft, kNoradSubControlDownTop); + _rightButton.moveElementTo(kNoradSubControlRightLeft, kNoradSubControlRightTop); + _leftButton.moveElementTo(kNoradSubControlLeftLeft, kNoradSubControlLeftTop); + _upButton.moveElementTo(kNoradSubControlUpLeft, kNoradSubControlUpTop); + _ccwButton.moveElementTo(kNoradSubControlCCWLeft, kNoradSubControlCCWTop); + _cwButton.moveElementTo(kNoradSubControlCWLeft, kNoradSubControlCWTop); + + _greenBall.setDisplayOrder(kClawMonitorGreenBallOrder); + + for (int i = 0; i < kNumClawGreenBalls; i++) { + SpriteFrame *frame = new SpriteFrame(); + frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kClawMonitorGreenBallBaseID + i); + _greenBall.addFrame(frame, 0, 0); + } + + _greenBall.setCurrentFrameIndex(0); + _greenBall.startDisplaying(); + + _greenBallTimer.setScale(owner->getNavMovie()->getScale()); + _greenBallCallBack.setNotification(&_greenBallNotification); + _greenBallCallBack.initCallBack(&_greenBallTimer, kCallBackAtExtremes); + _greenBallCallBack.setCallBackFlag(kOneSecondOfMoveFinished); + _greenBallNotification.notifyMe(this, kGreenBallNotificationFlags, kGreenBallNotificationFlags); + + _subControlMovie.show(); + _clawMonitorMovie.show(); +} + +void SubControlRoom::initInteraction() { + if (GameState.getNoradSubPrepState() == kSubDamaged) { + playControlMonitorSection(kDeltaSplashStart * _subControlScale, kDeltaSplashStop * _subControlScale, + 0, kDeltaSplash, false); + playClawMonitorSection(kDeltaClawSplashStart, kDeltaClawSplashStop, kDeltaSplashFinished, _gameState, false); + } else { + playControlMonitorSection(kAlphaSplashStart * _subControlScale, kAlphaSplashStop * _subControlScale, + 0, kAlphaSplash, false); + playClawMonitorSection(kAlphaClawSplashStart, kAlphaClawSplashStop, kAlphaSplashFinished, _gameState, false); + } + + _subControlMovie.redrawMovieWorld(); + _clawMonitorMovie.redrawMovieWorld(); +} + +void SubControlRoom::closeInteraction() { + _subControlNotification.cancelNotification(this); + _subControlCallBack.releaseCallBack(); + _greenBallNotification.cancelNotification(this); + _greenBallCallBack.releaseCallBack(); + _neighborhoodNotification->cancelNotification(this); +} + +void SubControlRoom::setSoundFXLevel(const uint16 fxLevel) { + _subControlMovie.setVolume(fxLevel); +} + +void SubControlRoom::receiveNotification(Notification *notification, const NotificationFlags flags) { + Norad *owner = (Norad *)getOwner(); + + if (notification == &_subControlNotification) { + switch (flags) { + case kAlphaSplashFinished: + setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true); + break; + case kPrepHighlightFinished: + if (GameState.getNoradSubPrepState() == kSubDamaged) + playControlMonitorSection(kDeltaLaunchPrepStart * _subControlScale, + kDeltaLaunchPrepStop * _subControlScale, kDeltaPrepFinished, _gameState, false); + else + playControlMonitorSection(kAlphaLaunchPrepStart * _subControlScale, + kAlphaLaunchPrepStop * _subControlScale, kAlphaPrepFinished, _gameState, false); + break; + case kAlphaPrepFinished: + GameState.setNoradSubPrepState(kSubPrepped); + GameState.setScoringPreppedSub(true); + setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true); + break; + case kClawHighlightFinished: + playControlMonitorSection(kClawMenuStart * _subControlScale, kClawMenuStop * _subControlScale, + kClawMenuFinished, _gameState, false); + break; + case kClawMenuFinished: + owner->playClawMonitorIntro(); + showButtons(); + setControlMonitorToTime(kClawMenuTime * _subControlScale, kClawMenu, true); + + if (!_playingAgainstRobot) { + updateClawMonitor(); + owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]); + } + break; + case kDeltaSplashFinished: + setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true); + + if (_playingAgainstRobot) { + _robotState = kRobotApproaching; + playClawMonitorSection(kRobotApproachStart, kRobotApproachStop, 0, _gameState, true); + owner->startExtraSequence(kN60RobotApproaches, kExtraCompletedFlag, kFilterAllInput); + } + break; + case kDeltaPrepFinished: + setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true); + break; + } + } else if (notification == &_greenBallNotification) { + if (_robotState == kRobotWon) { + // We are using the green ball notification to hide stuff when the robot comes through + // the glass. + hideEverything(); + } else { + // We are now midway through a move, time to update the claw's position and the green + // ball. + _clawPosition = _clawNextPosition; + updateClawMonitor(); + updateGreenBall(); + } + } else if (notification == _neighborhoodNotification) { + _currentAction = kNoActionIndex; + if (_playingAgainstRobot) { + switch (_robotState) { + case kRobotApproaching: + if (_gameState == kClawMenu) { + _robotState = kPunchingOnce; + dispatchClawAction(kNoActionIndex); + } else { + robotKillsPlayer(kN60FirstMistake, owner); + } + break; + case kPunchingOnce: + if (_nextAction == kMoveDownActionIndex) { + _robotState = kPunchingTwice; + performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); + } else { + robotKillsPlayer(kN60SecondMistake, owner); + } + break; + case kPunchingTwice: + if (_nextAction == kPinchActionIndex) { + _robotState = kPunchingThrice; + performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); + } else { + robotKillsPlayer(kN60ThirdMistake, owner); + } + break; + case kPunchingThrice: + if (_nextAction == kMoveRightActionIndex) { + _robotState = kCarriedToDoor; + performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); + } else { + robotKillsPlayer(kN60FourthMistake, owner); + } + break; + case kCarriedToDoor: + hideEverything(); + _robotState = kPlayerWon; + owner->startExtraSequence(kN60PlayerFollowsRobotToDoor, kExtraCompletedFlag, kFilterAllInput); + break; + case kPlayerWon: + ((NoradDelta *)owner)->playerBeatRobotWithClaw(); + owner->requestDeleteCurrentInteraction(); + break; + case kRobotWon: + g_system->delayMillis(2 * 1000); // 120 ticks + ((PegasusEngine *)g_engine)->die(kDeathRobotSubControlRoom); + break; + } + } else { + if (_gameState == kPuttingClawAway && _nextAction == kNoActionIndex) { + if (_clawPosition == _clawStartPosition) { + Input scratch; + GameInteraction::clickInHotspot(scratch, g_allHotspots.findHotspotByID(_outSpotID)); + } else { + switch (_clawPosition) { + case kClawAtA: + dispatchClawAction(kMoveLeftActionIndex); + break; + case kClawAtB: + if (_clawStartPosition == kClawAtD) // Norad Alpha + dispatchClawAction(kMoveLeftActionIndex); + else if (_clawStartPosition == kClawAtC) // Norad Delta + dispatchClawAction(kMoveUpActionIndex); + break; + case kClawAtC: + dispatchClawAction(kMoveDownActionIndex); + break; + case kClawAtD: + dispatchClawAction(kMoveRightActionIndex); + break; + } + } + } else { + dispatchClawAction(_nextAction); + } + } + } +} + +void SubControlRoom::hideEverything() { + hideButtons(); + _subControlMovie.hide(); + _clawMonitorMovie.hide(); + _greenBall.hide(); +} + +void SubControlRoom::robotKillsPlayer(const uint32 extraID, Neighborhood *owner) { + _robotState = kRobotWon; + owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput); + _greenBallTimer.stop(); + _greenBallTimer.setSegment(0, 32 * _greenBallTimer.getScale() / 15); + _greenBallTimer.setTime(0); + _greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _greenBallTimer.start(); +} + +void SubControlRoom::activateHotspots() { + if (_robotState == kRobotWon || _robotState == kPlayerWon) + return; + + GameInteraction::activateHotspots(); + + switch (_gameState) { + case kAlphaMainMenu: + case kDeltaMainMenu: + g_allHotspots.activateOneHotspot(_prepSpotID); + g_allHotspots.activateOneHotspot(_clawControlSpotID); + break; + case kClawMenu: + // This could be called during a move, so use _clawNextPosition. + if (_playingAgainstRobot) { + g_allHotspots.deactivateOneHotspot(_outSpotID); + if (_robotState != kRobotApproaching && _nextAction == kNoActionIndex) + for (int i = 0; i < kNumClawButtons; i++) + if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex) + g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]); + } else if (_nextAction == kNoActionIndex) { + for (int i = 0; i < kNumClawButtons; i++) + if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex) + g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]); + } + break; + default: + break; + } +} + +void SubControlRoom::showButtons() { + if (_playingAgainstRobot && _robotState == kRobotApproaching) { + for (int i = 0; i < kNumClawButtons; i++) { + _buttons[i]->show(); + _buttons[i]->setCurrentFrameIndex(kButtonDimFrame); + } + } else if (_nextAction != kNoActionIndex) { + for (int i = 0; i < kNumClawButtons; i++) { + _buttons[i]->show(); + if (i == _currentAction || i == _nextAction) + _buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame); + else + _buttons[i]->setCurrentFrameIndex(kButtonDimFrame); + } + } else { + for (int i = 0; i < kNumClawButtons; i++) { + _buttons[i]->show(); + if (i == _currentAction) + _buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame); + else if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex && + _gameState != kPuttingClawAway) // this could be called during a move, so check _clawNextPosition + _buttons[i]->setCurrentFrameIndex(kButtonActiveFrame); + else + _buttons[i]->setCurrentFrameIndex(kButtonDimFrame); + } + } +} + +void SubControlRoom::hideButtons() { + for (int i = 0; i < kNumClawButtons; i++) + _buttons[i]->hide(); +} + +int SubControlRoom::findActionIndex(HotSpotID id) { + for (int i = 0; i < kNumClawButtons; i++) + if (id == _clawButtonSpotIDs[i]) + return i; + + return kNoActionIndex; +} + +void SubControlRoom::clickInHotspot(const Input &input, const Hotspot *spot) { + HotSpotID clickedID = spot->getObjectID(); + int actionIndex = findActionIndex(clickedID); + + if (actionIndex != kNoActionIndex) { + dispatchClawAction(actionIndex); + } else if (clickedID == _prepSpotID) { + playControlMonitorSection(kLaunchPrepHighlightStart * _subControlScale, + kLaunchPrepHighlightStop * _subControlScale, + kPrepHighlightFinished, kPlayingHighlight, false); + } else if (clickedID == _clawControlSpotID) { + playControlMonitorSection(kClawControlHighlightStart * _subControlScale, + kClawControlHighlightStop * _subControlScale, + kClawHighlightFinished, kPlayingHighlight, false); + } else if (clickedID == _outSpotID) { + _gameState = kPuttingClawAway; + + if (_currentAction == kNoActionIndex) { + if (_clawPosition == _clawStartPosition) { + GameInteraction::clickInHotspot(input, spot); + } else { + switch (_clawPosition) { + case kClawAtA: + dispatchClawAction(kMoveLeftActionIndex); + break; + case kClawAtB: + if (_clawStartPosition == kClawAtD) // Norad Alpha + dispatchClawAction(kMoveLeftActionIndex); + else if (_clawStartPosition == kClawAtC) // Norad Delta + dispatchClawAction(kMoveUpActionIndex); + break; + case kClawAtC: + dispatchClawAction(kMoveDownActionIndex); + break; + case kClawAtD: + dispatchClawAction(kMoveRightActionIndex); + break; + } + } + } + } else { + GameInteraction::clickInHotspot(input, spot); + } +} + +void SubControlRoom::dispatchClawAction(const int newAction) { + GameState.setScoringPlayedWithClaw(true); + + Neighborhood *owner = getOwner(); + + if (newAction == kNoActionIndex) { + _currentAction = kNoActionIndex; + _nextAction = kNoActionIndex; + showButtons(); + updateGreenBall(); + + if (_playingAgainstRobot) + owner->startExtraSequence(kN60ArmActivated, kExtraCompletedFlag, kFilterAllInput); + else + owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]); + } else { + if (_currentAction == kNoActionIndex) { + if (_playingAgainstRobot) { + _nextAction = newAction; + showButtons(); + updateGreenBall(); + } else { + performActionImmediately(newAction, _clawExtraIDs[s_clawStateTable[_clawPosition][newAction]], owner); + } + } else if (_nextAction == kNoActionIndex) { + _nextAction = newAction; + showButtons(); + updateGreenBall(); + } + } +} + +void SubControlRoom::performActionImmediately(const int action, const uint32 extraID, Neighborhood *owner) { + _currentAction = action; + _nextAction = kNoActionIndex; + ExtraTable::Entry entry; + + switch (action) { + case kMoveDownActionIndex: + case kMoveRightActionIndex: + case kMoveLeftActionIndex: + case kMoveUpActionIndex: + // Set up green ball callback. + owner->getExtraEntry(extraID, entry); + _greenBallTimer.stop(); + _greenBallTimer.setSegment(entry.movieStart, entry.movieStart + owner->getNavMovie()->getScale()); + _greenBallTimer.setTime(entry.movieStart); + _greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + // Start move. + _greenBallTimer.start(); + break; + } + + if (_playingAgainstRobot) { + switch (_robotState) { + case kPunchingTwice: + owner->startExtraSequence(kN60ArmToPositionB, kExtraCompletedFlag, kFilterAllInput); + break; + case kPunchingThrice: + owner->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput); + break; + case kCarriedToDoor: + owner->startExtraSequence(kN60ArmCarriesRobotToPositionA, kExtraCompletedFlag, kFilterAllInput); + break; + } + } else { + owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput); + } + + switch (action) { + case kMoveDownActionIndex: + _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawDown]; + break; + case kMoveRightActionIndex: + _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawRight]; + break; + case kMoveLeftActionIndex: + _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawLeft]; + break; + case kMoveUpActionIndex: + _clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawUp]; + break; + case kLoopActionIndex: + // Do nothing. + break; + default: + playClawMonitorSection(s_clawMonitorTable[action][_clawPosition][0], + s_clawMonitorTable[action][_clawPosition][1], 0, _gameState, true); + break; + } + + showButtons(); + updateGreenBall(); +} + +void SubControlRoom::setControlMonitorToTime(const TimeValue newTime, const int newState, const bool shouldAllowInput) { + _subControlMovie.stop(); + _subControlMovie.setSegment(0, _subControlMovie.getDuration()); + _subControlMovie.setTime(newTime); + _subControlMovie.redrawMovieWorld(); + _gameState = newState; + allowInput(shouldAllowInput); +} + +void SubControlRoom::playControlMonitorSection(const TimeValue in, const TimeValue out, const NotificationFlags flags, + const int newState, const bool shouldAllowInput) { + _subControlMovie.stop(); + _subControlMovie.setSegment(in, out); + _subControlMovie.setTime(in); + + if (flags != 0) { + _subControlCallBack.setCallBackFlag(flags); + _subControlCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _gameState = newState; + allowInput(shouldAllowInput); + _subControlMovie.start(); +} + +void SubControlRoom::updateClawMonitor() { + switch (_clawPosition) { + case kClawAtA: + setClawMonitorToTime(kClawAtATime); + break; + case kClawAtB: + setClawMonitorToTime(kClawAtBTime); + break; + case kClawAtC: + setClawMonitorToTime(kClawAtCTime); + break; + case kClawAtD: + setClawMonitorToTime(kClawAtDTime); + break; + } +} + +void SubControlRoom::setClawMonitorToTime(const TimeValue newTime) { + _clawMonitorMovie.stop(); + _clawMonitorMovie.setSegment(0, _clawMonitorMovie.getDuration()); + _clawMonitorMovie.setTime(newTime); + _clawMonitorMovie.redrawMovieWorld(); +} + +void SubControlRoom::playClawMonitorSection(const TimeValue in, const TimeValue out, const NotificationFlags flags, + const int newState, const bool shouldAllowInput) { + _clawMonitorMovie.stop(); + _clawMonitorMovie.setSegment(in, out); + _clawMonitorMovie.setTime(in); + + if (flags != 0) { + _clawMonitorCallBack.setCallBackFlag(flags); + _clawMonitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + } + + _gameState = newState; + allowInput(shouldAllowInput); + _clawMonitorMovie.start(); +} + +void SubControlRoom::updateGreenBall() { + switch (_currentAction) { + case kMoveDownActionIndex: + switch (_nextAction) { + case kMoveRightActionIndex: + moveGreenBallToA(); + break; + case kMoveLeftActionIndex: + moveGreenBallToD(); + break; + case kMoveUpActionIndex: + moveGreenBallToC(); + break; + default: + moveGreenBallToB(); + break; + } + break; + case kMoveRightActionIndex: + if (_clawNextPosition == kClawAtA) { + switch (_nextAction) { + case kMoveLeftActionIndex: + moveGreenBallToB(); + break; + default: + moveGreenBallToA(); + break; + } + } else { + switch (_nextAction) { + case kMoveRightActionIndex: + moveGreenBallToA(); + break; + case kMoveLeftActionIndex: + moveGreenBallToD(); + break; + case kMoveUpActionIndex: + moveGreenBallToC(); + break; + default: + moveGreenBallToB(); + break; + } + } + break; + case kMoveLeftActionIndex: + if (_clawNextPosition == kClawAtB) { + switch (_nextAction) { + case kMoveRightActionIndex: + moveGreenBallToA(); + break; + case kMoveLeftActionIndex: + moveGreenBallToD(); + break; + case kMoveUpActionIndex: + moveGreenBallToC(); + break; + default: + moveGreenBallToB(); + break; + } + } else { + switch (_nextAction) { + case kMoveRightActionIndex: + moveGreenBallToB(); + break; + default: + moveGreenBallToD(); + break; + } + } + break; + case kMoveUpActionIndex: + switch (_nextAction) { + case kMoveDownActionIndex: + moveGreenBallToB(); + break; + default: + moveGreenBallToC(); + break; + } + break; + default: + switch (_nextAction) { + case kMoveDownActionIndex: + moveGreenBallToB(); + break; + case kMoveRightActionIndex: + if (_clawPosition == kClawAtB) + moveGreenBallToA(); + else + moveGreenBallToB(); + break; + case kMoveLeftActionIndex: + if (_clawPosition == kClawAtB) + moveGreenBallToD(); + else + moveGreenBallToB(); + break; + case kMoveUpActionIndex: + moveGreenBallToC(); + break; + default: + _greenBall.hide(); + break; + } + break; + } +} + +void SubControlRoom::moveGreenBallToA() { + if (_clawPosition == kClawAtA) { + if (_playingAgainstRobot) + _greenBall.setCurrentFrameIndex(kGreenBallAtAWithClawAndRobot); + else + _greenBall.setCurrentFrameIndex(kGreenBallAtAWithClaw); + } else { + _greenBall.setCurrentFrameIndex(kGreenBallAtA); + } + + _greenBall.moveElementTo(kNoradGreenBallAtALeft, kNoradGreenBallAtATop); + _greenBall.show(); +} + +void SubControlRoom::moveGreenBallToB() { + if (_clawPosition == kClawAtB) { + if (_playingAgainstRobot) + _greenBall.setCurrentFrameIndex(kGreenBallAtBWithClawAndRobot); + else + _greenBall.setCurrentFrameIndex(kGreenBallAtBWithClaw); + } else { + _greenBall.setCurrentFrameIndex(kGreenBallAtB); + } + + _greenBall.moveElementTo(kNoradGreenBallAtBLeft, kNoradGreenBallAtBTop); + _greenBall.show(); +} + +void SubControlRoom::moveGreenBallToC() { + switch (_clawPosition) { + case kClawAtA: + _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtA); + break; + case kClawAtB: + _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtB); + break; + case kClawAtC: + _greenBall.setCurrentFrameIndex(kGreenBallAtCWithClaw); + break; + case kClawAtD: + _greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtD); + break; + } + + _greenBall.moveElementTo(kNoradGreenBallAtCLeft, kNoradGreenBallAtCTop); + _greenBall.show(); +} + +void SubControlRoom::moveGreenBallToD() { + if (_clawPosition == kClawAtD) + _greenBall.setCurrentFrameIndex(kGreenBallAtDWithClaw); + else + _greenBall.setCurrentFrameIndex(kGreenBallAtD); + + _greenBall.moveElementTo(kNoradGreenBallAtDLeft, kNoradGreenBallAtDTop); + _greenBall.show(); +} + +bool SubControlRoom::canSolve() { + return _playingAgainstRobot && _robotState < kCarriedToDoor; +} + +void SubControlRoom::doSolve() { + _robotState = kCarriedToDoor; + hideEverything(); + getOwner()->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput); +} + +InputBits SubControlRoom::getInputFilter() { + if (_playingAgainstRobot) + return GameInteraction::getInputFilter() & ~kFilterDownButtonAny; + + return GameInteraction::getInputFilter(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.h b/engines/pegasus/neighborhood/norad/subcontrolroom.h new file mode 100644 index 0000000000..6ce599db42 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.h @@ -0,0 +1,133 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H +#define PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +static const uint32 kClawAtA = 0; +static const uint32 kClawAtB = 1; +static const uint32 kClawAtC = 2; +static const uint32 kClawAtD = 3; + +static const int kNumClawButtons = 7; + +class Norad; + +class SubControlRoom : public GameInteraction, public NotificationReceiver { +public: + SubControlRoom(Neighborhood *); + virtual ~SubControlRoom() {} + + void playAgainstRobot(); + + virtual void setSoundFXLevel(const uint16); + + bool canSolve(); + void doSolve(); + +protected: + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + void robotKillsPlayer(const uint32, Neighborhood *); + InputBits getInputFilter(); + + int findActionIndex(HotSpotID); + void dispatchClawAction(const int); + void performActionImmediately(const int, const uint32, Neighborhood *); + + void hideEverything(); + void showButtons(); + void hideButtons(); + + void updateGreenBall(); + void moveGreenBallToA(); + void moveGreenBallToB(); + void moveGreenBallToC(); + void moveGreenBallToD(); + + void setControlMonitorToTime(const TimeValue, const int, const bool); + void playControlMonitorSection(const TimeValue, const TimeValue, const NotificationFlags, + const int, const bool); + + void updateClawMonitor(); + void setClawMonitorToTime(const TimeValue); + void playClawMonitorSection(const TimeValue, const TimeValue, const NotificationFlags, + const int, const bool); + + Movie _subControlMovie; + TimeScale _subControlScale; + Notification _subControlNotification; + NotificationCallBack _subControlCallBack; + Movie _clawMonitorMovie; + NotificationCallBack _clawMonitorCallBack; + int _gameState; + uint32 _clawStartPosition; + uint32 _clawPosition; + uint32 _clawNextPosition; + const uint32 *_clawExtraIDs; + + int _currentAction; + int _nextAction; + + Sprite *_buttons[kNumClawButtons]; + Sprite _pinchButton; + Sprite _downButton; + Sprite _rightButton; + Sprite _leftButton; + Sprite _upButton; + Sprite _ccwButton; + Sprite _cwButton; + + Sprite _greenBall; + TimeBase _greenBallTimer; + Notification _greenBallNotification; + NotificationCallBack _greenBallCallBack; + + HotSpotID _outSpotID; + HotSpotID _prepSpotID; + HotSpotID _clawControlSpotID; + HotSpotID _clawButtonSpotIDs[kNumClawButtons]; + + Notification *_neighborhoodNotification; + + bool _playingAgainstRobot; + int _robotState; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/subplatform.cpp b/engines/pegasus/neighborhood/norad/subplatform.cpp new file mode 100644 index 0000000000..97079a9f53 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subplatform.cpp @@ -0,0 +1,205 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/subplatform.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +// As usual, times here are in seconds. + +static const TimeValue kNormalSplashStart = 0; +static const TimeValue kNormalSplashStop = 5; + +static const TimeValue kPrepSubStart = 5; +static const TimeValue kPrepSubStop = 15; + +static const TimeValue kPrepIncompleteStart = 15; +static const TimeValue kPrepIncompleteStop = 19; + +static const TimeValue kDamagedStart = 19; +static const TimeValue kDamagedStop = 28; + +static const NotificationFlags kNormalSplashFinished = 1; +static const NotificationFlags kPrepSubFinished = kNormalSplashFinished << 1; +static const NotificationFlags kPrepIncompleteFinished = kPrepSubFinished << 1; +static const NotificationFlags kDamagedFinished = kPrepIncompleteFinished << 1; + +static const NotificationFlags kPlatformNotificationFlags = kNormalSplashFinished | + kPrepSubFinished | + kPrepIncompleteFinished | + kDamagedFinished; + +static const uint16 kSubPreppedBit = (1 << 0); +static const uint16 kWaitingForPlayerBit = (1 << 1); + +SubPlatform::SubPlatform(Neighborhood *handler) : GameInteraction(kNoradSubPlatformInteractionID, handler), + _platformMovie(kPlatformMonitorID), _platformNotification(kNoradSubPlatformNotificationID, (PegasusEngine *)g_engine) { + _neighborhoodNotification = handler->getNeighborhoodNotification(); +} + +void SubPlatform::openInteraction() { + _stateBits = 0; + + // TODO: These next two lines seem unused? + if (GameState.getNoradSubPrepState() == kSubPrepped) + _stateBits |= kSubPreppedBit; + + _stateBits |= kWaitingForPlayerBit; + _platformMovie.initFromMovieFile("Images/Norad Alpha/Platform Monitor Movie"); + _platformMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); + _platformMovie.moveElementTo(kNoradPlatformLeft, kNoradPlatformTop); + _platformScale = _platformMovie.getScale(); + _platformMovie.setDisplayOrder(kPlatformOrder); + _platformMovie.startDisplaying(); + _platformCallBack.setNotification(&_platformNotification); + _platformCallBack.initCallBack(&_platformMovie, kCallBackAtExtremes); + + _platformNotification.notifyMe(this, kPlatformNotificationFlags, kPlatformNotificationFlags); +} + +void SubPlatform::initInteraction() { + _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void SubPlatform::closeInteraction() { + _platformNotification.cancelNotification(this); + _platformCallBack.releaseCallBack(); + _neighborhoodNotification->cancelNotification(this); +} + +void SubPlatform::setSoundFXLevel(const uint16 fxLevel) { + _platformMovie.setVolume(fxLevel); +} + +void SubPlatform::receiveNotification(Notification *notification, const NotificationFlags flags) { + FaderMoveSpec loop1Spec, loop2Spec; + ExtraTable::Entry entry; + + Norad *owner = (Norad *)getOwner(); + + if (notification == &_platformNotification) { + switch (flags) { + case kNormalSplashFinished: + _platformMovie.stop(); + switch (GameState.getNoradSubPrepState()) { + case kSubNotPrepped: + _platformMovie.setSegment(kPrepIncompleteStart * _platformScale, kPrepIncompleteStop * _platformScale); + _platformMovie.setTime(kPrepIncompleteStart * _platformScale); + _platformCallBack.setCallBackFlag(kPrepIncompleteFinished); + _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _platformMovie.start(); + break; + case kSubPrepped: + _platformMovie.setSegment(kPrepSubStart * _platformScale, kPrepSubStop * _platformScale); + _platformMovie.setTime(kPrepSubStart * _platformScale); + _platformCallBack.setCallBackFlag(kPrepSubFinished); + _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + owner->startExtraSequence(kNorad19PrepSub, 0, kFilterNoInput); + _platformMovie.start(); + break; + case kSubDamaged: + // Shouldn't happen. + break; + } + break; + case kPrepSubFinished: + _platformMovie.stop(); + _platformMovie.stopDisplaying(); + + owner->getExtraEntry(kNorad19ExitToSub, entry); + + loop1Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradWarningVolume, + entry.movieEnd - entry.movieStart, 0); + loop1Spec.insertFaderKnot(4560, kNoradWarningVolume); + loop1Spec.insertFaderKnot(5080, 0); + + loop2Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradSuckWindVolume, + entry.movieEnd - entry.movieStart, 0); + loop1Spec.insertFaderKnot(4560, kNoradSuckWindVolume); + loop1Spec.insertFaderKnot(5080, 0); + + owner->startExtraSequence(kNorad19ExitToSub, kExtraCompletedFlag, kFilterNoInput); + + owner->startLoop1Fader(loop1Spec); + owner->startLoop2Fader(loop2Spec); + break; + case kPrepIncompleteFinished: + ((NoradAlpha *)owner)->setSubPrepFailed(true); + g_AIArea->checkMiddleArea(); + // Fall through... + case kDamagedFinished: + _platformMovie.stop(); + _platformMovie.hide(); + _stateBits |= kWaitingForPlayerBit; + allowInput(true); + break; + } + } else if (notification == _neighborhoodNotification) { + allowInput(true); + ((PegasusEngine *)g_engine)->jumpToNewEnvironment(kNoradSubChaseID, kNoRoomID, kNoDirection); + GameState.setScoringEnteredSub(true); + } +} + +void SubPlatform::activateHotspots() { + if (_stateBits & kWaitingForPlayerBit) + g_allHotspots.activateOneHotspot(kNorad19ActivateMonitorSpotID); + + GameInteraction::activateHotspots(); +} + +void SubPlatform::clickInHotspot(const Input &input, const Hotspot *spot) { + if (spot->getObjectID() == kNorad19ActivateMonitorSpotID) { + if (GameState.getNoradSubPrepState() == kSubDamaged) { + _platformMovie.setSegment(kDamagedStart * _platformScale, kDamagedStop * _platformScale); + _platformMovie.setTime(kDamagedStart * _platformScale); + _platformCallBack.setCallBackFlag(kDamagedFinished); + } else { + _platformMovie.setSegment(kNormalSplashStart * _platformScale, kNormalSplashStop * _platformScale); + _platformMovie.setTime(kNormalSplashStart * _platformScale); + _platformCallBack.setCallBackFlag(kNormalSplashFinished); + } + + _platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + + _platformMovie.show(); + _platformMovie.start(); + _platformMovie.redrawMovieWorld(); + + _stateBits &= ~kWaitingForPlayerBit; + + allowInput(false); + } else { + GameInteraction::clickInHotspot(input, spot); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/subplatform.h b/engines/pegasus/neighborhood/norad/subplatform.h new file mode 100644 index 0000000000..82e86ecae2 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subplatform.h @@ -0,0 +1,63 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H +#define PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class SubPlatform : public GameInteraction, public NotificationReceiver { +public: + SubPlatform(Neighborhood *); + virtual ~SubPlatform() {} + + virtual void setSoundFXLevel(const uint16); + +protected: + virtual void openInteraction(); + virtual void initInteraction(); + virtual void closeInteraction(); + + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual void receiveNotification(Notification *, const NotificationFlags); + + Movie _platformMovie; + TimeScale _platformScale; + Notification _platformNotification; + NotificationCallBack _platformCallBack; + Notification *_neighborhoodNotification; + uint16 _stateBits; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp new file mode 100644 index 0000000000..814d7717de --- /dev/null +++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp @@ -0,0 +1,689 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/compass.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/ai/ai_rule.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" + +namespace Pegasus { + +static const int16 s_prehistoricCompass[kPrehistoric25 + 1][4] = { + { 0, 170, 90, 270 }, // kPrehistoric01 + { 0, 180, 90, 270 }, // kPrehistoric02 + { 10, 180, 90, 270 }, // kPrehistoric03 + { 10, 190, 90, 270 }, // kPrehistoric04 + { 10, 195, 90, 270 }, // kPrehistoric05 + { 20, 210, 90, 270 }, // kPrehistoric06 + { 20, 200, 130, 276 }, // kPrehistoric07 + { 20, 176, 110, 260 }, // kPrehistoric08 + { 20, 200, 100, 270 }, // kPrehistoric09 + { 14, 186, 100, 280 }, // kPrehistoric10 + { 26, 206, 116, 296 }, // kPrehistoric11 + { 60, 226, 140, 320 }, // kPrehistoric12 + { 0, 180, 80, 270 }, // kPrehistoric13 + { 14, 200, 106, 286 }, // kPrehistoric14 + { -10, 174, 80, 260 }, // kPrehistoric15 + { 54, 236, 140, 210 }, // kPrehistoric16 + { -24, 160, 70, 250 }, // kPrehistoric17 + { 26, 206, 140, 296 }, // kPrehistoric18 + { -16, 160, 70, 250 }, // kPrehistoric19 + { -16, 160, 70, 250 }, // kPrehistoric20 + { -10, 160, 90, 250 }, // kPrehistoric21 + { -20, 160, 70, 244 }, // kPrehistoric22 + { -20, 160, 70, 244 }, // kPrehistoric22North + { 60, 234, 150, 330 }, // kPrehistoric23 + { 50, 230, 140, 320 }, // kPrehistoric24 + { 60, 240, 140, 330 } // kPrehistoric25 +}; + +static const TimeValue kPrehistoricFlashlightClickIn = 0; +static const TimeValue kPrehistoricFlashlightClickOut = 138; + +static const TimeValue kPrehistoricBumpIntoWallIn = 138; +static const TimeValue kPrehistoricBumpIntoWallOut = 291; + +static const TimeValue kBridgeRetractIn = 291; +static const TimeValue kBridgeRetractOut = 1499; + +static const TimeValue kPrehistoricWarningTimeLimit = kTenMinutes; + +Prehistoric::Prehistoric(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Prehistoric", kPrehistoricID) { + setIsItemTaken(kHistoricalLog); +} + +uint16 Prehistoric::getDateResID() const { + return kDatePrehistoricID; +} + +void Prehistoric::init() { + Neighborhood::init(); + + // Forces a stop so the flashlight can turn off... + forceStridingStop(kPrehistoric12, kSouth, kNoAlternateID); +} + +void Prehistoric::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + Neighborhood::start(); +} + +class FinishPrehistoricAction : public AIPlayMessageAction { +public: + FinishPrehistoricAction() : AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false) {} + ~FinishPrehistoricAction() {} + + void performAIAction(AIRule *); + +}; + +void FinishPrehistoricAction::performAIAction(AIRule *rule) { + AIPlayMessageAction::performAIAction(rule); + ((PegasusEngine *)g_engine)->die(kPlayerWonGame); +} + +void Prehistoric::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + if (_vm->isDemo()) { + FinishPrehistoricAction *doneAction = new FinishPrehistoricAction(); + AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog); + AIRule *rule = new AIRule(hasLogCondition, doneAction); + g_AIArea->addAIRule(rule); + } else { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP1NB", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric16, kNorth)); + AIRule *rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric01, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric08, kEast)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric25, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP16NB", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kPrehistoric23, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP18NB", false); + AITimerCondition *timerCondition = new AITimerCondition(kPrehistoricWarningTimeLimit, 1, true); + rule = new AIRule(timerCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false); + AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog); + rule = new AIRule(hasLogCondition, messageAction); + g_AIArea->addAIRule(rule); + } + } +} + +TimeValue Prehistoric::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraTable::Entry extra; + uint32 extraID = 0xffffffff; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric02, kSouth): + if (!GameState.getPrehistoricSeenTimeStream()) { + getExtraEntry(kPreArrivalFromTSA, extra); + return extra.movieStart; + } + break; + case MakeRoomView(kPrehistoric25, kEast): + if (_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) { + if (_vm->itemInLocation(kHistoricalLog, kPrehistoricID, kPrehistoric25, kEast)) + extraID = kPre25EastViewWithLog; + else + extraID = kPre25EastViewNoLog; + } + break; + } + + if (extraID == 0xffffffff) + return Neighborhood::getViewTime(room, direction); + + getExtraEntry(extraID, extra); + return extra.movieEnd - 1; +} + + +void Prehistoric::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { + Neighborhood::findSpotEntry(room, direction, flags, entry); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric01, kSouth): + case MakeRoomView(kPrehistoric25, kSouth): + entry.clear(); + break; + case MakeRoomView(kPrehistoric01, kEast): + if (GameState.getPrehistoricSeenFlyer1()) + entry.clear(); + else + GameState.setPrehistoricSeenFlyer1(true); + break; + case MakeRoomView(kPrehistoric08, kEast): + if (GameState.getPrehistoricSeenFlyer2()) + entry.clear(); + else + GameState.setPrehistoricSeenFlyer2(true); + break; + } +} + +int16 Prehistoric::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + if (room == kPrehistoricDeath) + return g_compass->getFaderValue(); + + return s_prehistoricCompass[room][dir]; +} + +void Prehistoric::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + uint32 angle; + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kPrehistoric01, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 2, -10); + break; + case MakeRoomView(kPrehistoric06, kEast): + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4, 95); + compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4 * 1, 100); + break; + case MakeRoomView(kPrehistoric18, kEast): + if (getCurrentAlternate() == kAltPrehistoricBridgeSet) { + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 11, 145); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 26, 145); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 39, 148); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 114, 140); + } else { + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 10, 140); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 16, 145); + compassMove.insertFaderKnot(exitEntry.movieEnd, 145); + } + break; + case MakeRoomView(kPrehistoric23, kWest): + angle = compassMove.getNthKnotValue(0); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 17, angle); + compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 32, angle - 90); + compassMove.insertFaderKnot(exitEntry.movieEnd, angle - 90); + break; + } +} + +void Prehistoric::turnTo(const DirectionConstant newDirection) { + setCurrentAlternate(kAltPrehistoricNormal); + _privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, false); + Neighborhood::turnTo(newDirection); + + Item *keyCard; + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + zoomToVault(); + break; + case MakeRoomView(kPrehistoric18, kNorth): + case MakeRoomView(kPrehistoric18, kSouth): + if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) { + playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut); + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false); + loadAmbientLoops(); + } + // fall through + case MakeRoomView(kPrehistoric25, kEast): + setCurrentActivation(kActivationVaultClosed); + break; + case MakeRoomView(kPrehistoric16, kNorth): + case MakeRoomView(kPrehistoric21, kWest): + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + if (keyCard->getItemState() == kFlashlightOff) { + keyCard->setItemState(kFlashlightOn); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + case MakeRoomView(kPrehistoric16, kEast): + case MakeRoomView(kPrehistoric16, kWest): + case MakeRoomView(kPrehistoric21, kNorth): + case MakeRoomView(kPrehistoric21, kSouth): + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + if (keyCard->getItemState() == kFlashlightOn) { + keyCard->setItemState(kFlashlightOff); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + } +} + +void Prehistoric::zoomToVault() { + if (!GameState.getPrehistoricSeenBridgeZoom()) + startExtraSequence(kPre18EastZoom, kExtraCompletedFlag, kFilterNoInput); +} + +void Prehistoric::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoric08, kEast): + case MakeRoomView(kPrehistoric18, kSouth): + case MakeRoomView(kPrehistoric16, kNorth): + case MakeRoomView(kPrehistoric21, kNorth): + case MakeRoomView(kPrehistoric25, kNorth): + makeContinuePoint(); + break; + } +} + +void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction) { + Item *keyCard; + + if (MakeRoomView(room, direction) == MakeRoomView(kPrehistoric25, kEast) && + _privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) { + _navMovie.stop(); + playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut); + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false); + } + + Neighborhood::arriveAt(room, direction); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kPrehistoricDeath, kNorth): + case MakeRoomView(kPrehistoricDeath, kSouth): + case MakeRoomView(kPrehistoricDeath, kEast): + case MakeRoomView(kPrehistoricDeath, kWest): + if (GameState.getLastRoom() == kPrehistoric23) + die(kDeathEatenByDinosaur); + else + die(kDeathFallOffCliff); + break; + case MakeRoomView(kPrehistoric02, kSouth): + if (!GameState.getPrehistoricSeenTimeStream()) { + GameState.setPrehistoricTriedToExtendBridge(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + startExtraSequence(kPreArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput); + } + break; + case MakeRoomView(kPrehistoric18, kEast): + zoomToVault(); + break; + case MakeRoomView(kPrehistoric16, kNorth): + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + + if (keyCard->getItemState() == kFlashlightOff) { + keyCard->setItemState(kFlashlightOn); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + + if (g_AIArea) + g_AIArea->checkRules(); + break; + case MakeRoomView(kPrehistoric01, kSouth): + case MakeRoomView(kPrehistoric23, kNorth): + if (g_AIArea) + g_AIArea->checkRules(); + break; + case MakeRoomView(kPrehistoric08, kSouth): + case MakeRoomView(kPrehistoric10, kSouth): + case MakeRoomView(kPrehistoric12, kSouth): + case MakeRoomView(kPrehistoric13, kNorth): + case MakeRoomView(kPrehistoric14, kSouth): + case MakeRoomView(kPrehistoric15, kNorth): + case MakeRoomView(kPrehistoric16, kSouth): + case MakeRoomView(kPrehistoric17, kNorth): + case MakeRoomView(kPrehistoric18, kSouth): + case MakeRoomView(kPrehistoric19, kNorth): + case MakeRoomView(kPrehistoric20, kNorth): + case MakeRoomView(kPrehistoric21, kEast): + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + + if (keyCard->getItemState() == kFlashlightOn) { + keyCard->setItemState(kFlashlightOff); + playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); + } + break; + case MakeRoomView(kPrehistoric25, kEast): + setCurrentActivation(kActivationVaultClosed); + break; + } +} + +void Prehistoric::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + switch (room) { + case kPrehistoric02: + // 1/4 volume. + if (GameState.getPrehistoricSeenTimeStream()) + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64); + break; + case kPrehistoric01: + case kPrehistoric03: + case kPrehistoric04: + case kPrehistoric05: + case kPrehistoric06: + case kPrehistoric07: + case kPrehistoric09: + case kPrehistoric11: + case kPrehistoric13: + case kPrehistoric15: + case kPrehistoric17: + case kPrehistoric19: + case kPrehistoric20: + // 1/4 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64); + break; + case kPrehistoric08: + case kPrehistoric10: + case kPrehistoric12: + case kPrehistoric14: + case kPrehistoric16: + case kPrehistoric18: + case kPrehistoric21: + // 3/16 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 48); + break; + case kPrehistoric25: + // 1/8 volume. + loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 32); + break; + case kPrehistoric22: + case kPrehistoric22North: + case kPrehistoric23: + case kPrehistoric24: + case kPrehistoricDeath: + // 0 volume. + loadLoopSound1(""); + break; + } + + switch (room) { + case kPrehistoric02: + case kPrehistoric03: + case kPrehistoric04: + case kPrehistoric05: + case kPrehistoric06: + case kPrehistoric07: + case kPrehistoric08: + case kPrehistoric09: + case kPrehistoric10: + case kPrehistoric11: + case kPrehistoric12: + case kPrehistoric13: + case kPrehistoric14: + case kPrehistoric15: + case kPrehistoric16: + case kPrehistoric17: + case kPrehistoric19: + case kPrehistoric20: + case kPrehistoric21: + case kPrehistoricDeath: + loadLoopSound2(""); + break; + case kPrehistoric01: + case kPrehistoric25: + loadLoopSound2("Sounds/Prehistoric/VolcLoop.22K.AIFF", 64); + break; + case kPrehistoric18: + if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + loadLoopSound2("Sounds/Prehistoric/P18EAL00.22k.AIFF", 0x100, 0, 0); + else + loadLoopSound2(""); + break; + case kPrehistoric23: + case kPrehistoric24: + case kPrehistoric22: + case kPrehistoric22North: + loadLoopSound2("Sounds/Prehistoric/P24NAL00.22k.AIFF", 64); + break; + } +} + +void Prehistoric::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + if (!_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + _vm->getAllHotspots().activateOneHotspot(kPre18EastSpotID); + break; + case MakeRoomView(kPrehistoric22North, kNorth): + _vm->getAllHotspots().activateOneHotspot(kPre22NorthBreakerSpotID); + break; + } +} + +void Prehistoric::clickInHotspot(const Input &input, const Hotspot *spot) { + switch (spot->getObjectID()) { + case kPre18EastSpotID: + if (GameState.getPrehistoricBreakerThrown()) + startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kPre18EastBridgeOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kPre22NorthBreakerSpotID: + startExtraSequence(kPre22ThrowBreaker, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::clickInHotspot(input, spot); + break; + } +} + +void Prehistoric::receiveNotification(Notification *notification, const NotificationFlags flags) { + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kPreArrivalFromTSA: + GameState.setPrehistoricSeenTimeStream(true); + loadAmbientLoops(); + makeContinuePoint(); + break; + case kPre18EastZoom: + startExtraSequence(kPre18EastZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kPre18EastZoomOut: + GameState.setPrehistoricSeenBridgeZoom(true); + break; + case kPre18EastBridgeOn: + _privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, true); + setCurrentAlternate(kAltPrehistoricBridgeSet); + GameState.setPrehistoricTriedToExtendBridge(false); + loadAmbientLoops(); + GameState.setScoringExtendedBridge(true); + break; + case kPre18EastBridgeOut: + GameState.setPrehistoricTriedToExtendBridge(true); + if (g_AIArea) + g_AIArea->checkMiddleArea(); + break; + case kPre22ThrowBreaker: + GameState.setPrehistoricBreakerThrown(true); + GameState.setScoringThrewBreaker(true); + break; + case kPre25EastUnlockingVaultNoLog: + case kPre25EastUnlockingVaultWithLog: + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kJourneymanKey)); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +Common::String Prehistoric::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) + movieName = "Images/AI/Prehistoric/XPE"; + + return movieName; +} + +Common::String Prehistoric::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + if (!_vm->isDemo()) { + switch (GameState.getCurrentRoom()) { + case kPrehistoric16: + case kPrehistoric23: + case kPrehistoric24: + return "Images/AI/Prehistoric/XP7WB"; + } + } + + return "Images/AI/Prehistoric/XP17NB"; + } + + return movieName; +} + +uint Prehistoric::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + if (!GameState.getPrehistoricBreakerThrown() && GameState.getPrehistoricTriedToExtendBridge() && + !_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) + numHints = 1; + break; + case MakeRoomView(kPrehistoric25, kEast): + if (!_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) + numHints = 1; + break; + } + } + + return numHints; +} + +Common::String Prehistoric::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kPrehistoric18, kEast): + return "Images/AI/Prehistoric/XP18WD"; + case MakeRoomView(kPrehistoric25, kEast): + return "Images/AI/Globals/XGLOB1A"; + } + } + + return movieName; +} + +bool Prehistoric::canSolve() { + return GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric18, kEast) && + !GameState.getPrehistoricBreakerThrown() && + GameState.getPrehistoricTriedToExtendBridge() && + !_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag); +} + +void Prehistoric::doSolve() { + GameState.setPrehistoricBreakerThrown(true); + startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput); +} + +Hotspot *Prehistoric::getItemScreenSpot(Item *item, DisplayElement *element) { + if (item->getObjectID() == kHistoricalLog) + return _vm->getAllHotspots().findHotspotByID(kPrehistoricHistoricalLogSpotID); + + return Neighborhood::getItemScreenSpot(item, element); +} + +void Prehistoric::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kHistoricalLog: + GameState.setScoringGotHistoricalLog(true); + break; + } + + Neighborhood::pickedUpItem(item); +} + +void Prehistoric::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + switch (item->getObjectID()) { + case kJourneymanKey: + Neighborhood::dropItemIntoRoom(item, dropSpot); + + if (GameState.isTakenItemID(kHistoricalLog)) + startExtraLongSequence(kPre25EastUnlockingVaultNoLog, kPre25EastVaultOpenNoLog, kExtraCompletedFlag, kFilterNoInput); + else + startExtraLongSequence(kPre25EastUnlockingVaultWithLog, kPre25EastVaultOpenWithLog, kExtraCompletedFlag, kFilterNoInput); + + _privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, true); + setCurrentActivation(kActivationVaultOpen); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } +} + +void Prehistoric::bumpIntoWall() { + requestSpotSound(kPrehistoricBumpIntoWallIn, kPrehistoricBumpIntoWallOut, kFilterAllInput, 0); + Neighborhood::bumpIntoWall(); +} + +Common::String Prehistoric::getNavMovieName() { + return "Images/Prehistoric/Prehistoric.movie"; +} + +Common::String Prehistoric::getSoundSpotsName() { + return "Sounds/Prehistoric/Prehistoric Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.h b/engines/pegasus/neighborhood/prehistoric/prehistoric.h new file mode 100644 index 0000000000..17f9993014 --- /dev/null +++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.h @@ -0,0 +1,158 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_PREHISTORIC_H +#define PEGASUS_NEIGHBORHOOD_PREHISTORIC_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +static const TimeScale kPrehistoricMovieScale = 600; +static const TimeScale kPrehistoricFramesPerSecond = 15; +static const TimeScale kPrehistoricFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltPrehistoricNormal = 0; +static const AlternateID kAltPrehistoricBridgeSet = 1; + +// Room IDs. + +static const RoomID kPrehistoric01 = 0; +static const RoomID kPrehistoric02 = 1; +static const RoomID kPrehistoric03 = 2; +static const RoomID kPrehistoric04 = 3; +static const RoomID kPrehistoric05 = 4; +static const RoomID kPrehistoric06 = 5; +static const RoomID kPrehistoric07 = 6; +static const RoomID kPrehistoric08 = 7; +static const RoomID kPrehistoric09 = 8; +static const RoomID kPrehistoric10 = 9; +static const RoomID kPrehistoric11 = 10; +static const RoomID kPrehistoric12 = 11; +static const RoomID kPrehistoric13 = 12; +static const RoomID kPrehistoric14 = 13; +static const RoomID kPrehistoric15 = 14; +static const RoomID kPrehistoric16 = 15; +static const RoomID kPrehistoric17 = 16; +static const RoomID kPrehistoric18 = 17; +static const RoomID kPrehistoric19 = 18; +static const RoomID kPrehistoric20 = 19; +static const RoomID kPrehistoric21 = 20; +static const RoomID kPrehistoric22 = 21; +static const RoomID kPrehistoric22North = 22; +static const RoomID kPrehistoric23 = 23; +static const RoomID kPrehistoric24 = 24; +static const RoomID kPrehistoric25 = 25; +static const RoomID kPrehistoricDeath = 26; + +// Hot Spot Activation IDs. + +static const HotSpotActivationID kActivationVaultClosed = 1; +static const HotSpotActivationID kActivationVaultOpen = 2; + +// Hot Spot IDs. + +static const HotSpotID kPre18EastSpotID = 5000; +static const HotSpotID kPre22NorthSpotID = 5001; +static const HotSpotID kPre22NorthOutSpotID = 5002; +static const HotSpotID kPre22NorthBreakerSpotID = 5003; +static const HotSpotID kPrehistoricKeyDropSpotID = 5004; +static const HotSpotID kPrehistoricHistoricalLogSpotID = 5005; + +// Extra sequence IDs. + +static const ExtraID kPreArrivalFromTSA = 0; +static const ExtraID kPre18EastBridgeOut = 1; +static const ExtraID kPre18EastBridgeOn = 2; +static const ExtraID kPre18EastZoom = 3; +static const ExtraID kPre18EastZoomOut = 4; +static const ExtraID kPre22ThrowBreaker = 5; +static const ExtraID kPre25EastUnlockingVaultWithLog = 6; +static const ExtraID kPre25EastVaultOpenWithLog = 7; +static const ExtraID kPre25EastViewWithLog = 8; +static const ExtraID kPre25EastUnlockingVaultNoLog = 9; +static const ExtraID kPre25EastVaultOpenNoLog = 10; +static const ExtraID kPre25EastViewNoLog = 11; + +class PegasusEngine; + +class Prehistoric : public Neighborhood { +public: + Prehistoric(InputHandler *, PegasusEngine *); + virtual ~Prehistoric() {} + + virtual uint16 getDateResID() const; + virtual void init(); + + virtual void arriveAt(const RoomID, const DirectionConstant); + virtual void activateHotspots(); + virtual void clickInHotspot(const Input &, const Hotspot *); + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void dropItemIntoRoom(Item *, Hotspot *); + void pickedUpItem(Item *); + + void start(); + + void bumpIntoWall(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool canSolve(); + void doSolve(); + +protected: + enum { + kPrehistoricPrivateVaultOpenFlag, + kPrehistoricPrivateExtendedBridgeFlag, + kNumPrehistoricPrivateFlags + }; + + void setUpAIRules(); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + virtual void receiveNotification(Notification *, const NotificationFlags); + void turnTo(const DirectionConstant); + void zoomToVault(); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + + void loadAmbientLoops(); + + FlagsArray<byte, kNumPrehistoricPrivateFlags> _privateFlags; + + Common::String getNavMovieName(); + Common::String getSoundSpotsName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/spot.cpp b/engines/pegasus/neighborhood/spot.cpp new file mode 100644 index 0000000000..f285bf9bc2 --- /dev/null +++ b/engines/pegasus/neighborhood/spot.cpp @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/spot.h" + +namespace Pegasus { + +void SpotTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].srcFlags = stream->readByte(); + _entries[i].altCode = stream->readByte(); + stream->readByte(); // alignment + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].dstFlags = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Spot[%d]: %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].srcFlags, _entries[i].altCode, _entries[i].movieStart, + _entries[i].movieEnd, _entries[i].dstFlags); + } +} + +void SpotTable::clear() { + _entries.clear(); +} + +// Two SpotTable::Entries are equal if +// In addition to having their rooms, directions and alt codes identical... +// They are both either loops or once only animations AND +// They overlap in at least one of the on arrival, on turn and on door open bits. +SpotTable::Entry SpotTable::findEntry(RoomID room, DirectionConstant direction, SpotFlags srcFlags, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode && (_entries[i].srcFlags & kSpotLoopsMask) == (srcFlags & kSpotLoopsMask) && ((_entries[i].srcFlags & srcFlags) & kSpotTriggers) != 0) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/spot.h b/engines/pegasus/neighborhood/spot.h new file mode 100644 index 0000000000..a985420b7c --- /dev/null +++ b/engines/pegasus/neighborhood/spot.h @@ -0,0 +1,97 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_SPOT_H +#define PEGASUS_NEIGHBORHOOD_SPOT_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +typedef byte SpotFlags; + +enum { + kSpotLoopsBit, // Loop or once only? + kSpotOnArrivalBit, + kSpotOnTurnBit, + kSpotOnDoorOpenBit +}; + +static const SpotFlags kNoSpotFlags = 0; +static const SpotFlags kSpotLoopsMask = 1 << kSpotLoopsBit; +static const SpotFlags kSpotOnArrivalMask = 1 << kSpotOnArrivalBit; +static const SpotFlags kSpotOnTurnMask = 1 << kSpotOnTurnBit; +static const SpotFlags kSpotOnDoorOpenMask = 1 << kSpotOnDoorOpenBit; + +static const SpotFlags kSpotTriggers = kSpotOnArrivalMask | kSpotOnTurnMask | kSpotOnDoorOpenMask; + +class SpotTable { +public: + SpotTable() {} + ~SpotTable() {} + + static uint32 getResTag() { return MKTAG('S', 'p', 'o', 't'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { clear(); } + bool isEmpty() { return movieStart == 0xffffffff; } + void clear() { + room = kNoRoomID; + direction = kNoDirection; + srcFlags = kNoSpotFlags; + altCode = kNoAlternateID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + dstFlags = kNoSpotFlags; + } + + RoomID room; + DirectionConstant direction; + SpotFlags srcFlags; + AlternateID altCode; + TimeValue movieStart; + TimeValue movieEnd; + SpotFlags dstFlags; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, SpotFlags srcFlags, AlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.cpp b/engines/pegasus/neighborhood/tsa/fulltsa.cpp new file mode 100644 index 0000000000..b598841b45 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/fulltsa.cpp @@ -0,0 +1,3023 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +// TSA PICTs: + +static const ResIDType kTBPCloseBoxPICTID = 800; +static const ResIDType kTBPRewindPICTID = 801; +static const ResIDType kUnresolvedPICTID = 802; +static const ResIDType kResolvedPICTID = 803; +static const ResIDType kJumpMenuPICTID = 804; +static const ResIDType kJumpMenuHilitedPICTID = 805; +static const ResIDType kExitPICTID = 806; +static const ResIDType kExitHilitedPICTID = 807; +static const ResIDType kLeftRipPICTID = 808; +static const ResIDType kComparisonCloseBoxPICTID = 809; +static const ResIDType kComparisonLeftRewindPICTID = 810; +static const ResIDType kComparisonRightRewindPICTID = 811; +static const ResIDType kComparisonHiliteNoradPICTID = 812; +static const ResIDType kComparisonHiliteMarsPICTID = 813; +static const ResIDType kComparisonHiliteCaldoriaPICTID = 814; +static const ResIDType kComparisonHiliteWSCPICTID = 815; +static const ResIDType kComparisonChancesNoradPICTID = 816; +static const ResIDType kComparisonChancesMarsPICTID = 817; +static const ResIDType kComparisonChancesCaldoriaPICTID = 818; +static const ResIDType kComparisonChancesWSCPICTID = 819; +static const ResIDType kRedirectionCCRolloverPICTID = 820; +static const ResIDType kRedirectionRRRolloverPICTID = 821; +static const ResIDType kRedirectionFDRolloverPICTID = 822; +static const ResIDType kRedirectionCCDoorPICTID = 823; +static const ResIDType kRedirectionRRDoorPICTID = 824; +static const ResIDType kRedirectionFDDoorPICTID = 825; +static const ResIDType kRedirectionSecuredPICTID = 826; +static const ResIDType kRedirectionNewTargetPICTID = 827; +static const ResIDType kRedirectionClosePICTID = 828; + +static const int16 kCompassShift = 15; + +static const TimeScale kFullTSAMovieScale = 600; +static const TimeScale kFullTSAFramesPerSecond = 15; +static const TimeScale kFullTSAFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltTSANormal = 0; +static const AlternateID kAltTSARobotsAtReadyRoom = 1; +static const AlternateID kAltTSARobotsAtFrontDoor = 2; +static const AlternateID kAltTSARedAlert = 3; + +// Room IDs. +static const RoomID kTSA01 = 1; +static const RoomID kTSA02 = 2; +static const RoomID kTSA03 = 3; +static const RoomID kTSA04 = 4; +static const RoomID kTSA05 = 5; +static const RoomID kTSA0A = 6; +static const RoomID kTSA06 = 7; +static const RoomID kTSA07 = 8; +static const RoomID kTSA08 = 9; +static const RoomID kTSA09 = 10; +static const RoomID kTSA10 = 11; +static const RoomID kTSA11 = 12; +static const RoomID kTSA12 = 13; +static const RoomID kTSA13 = 14; +static const RoomID kTSA14 = 15; +static const RoomID kTSA15 = 16; +static const RoomID kTSA16 = 17; +static const RoomID kTSA17 = 18; +static const RoomID kTSA18 = 19; +static const RoomID kTSA19 = 20; +static const RoomID kTSA0B = 21; +static const RoomID kTSA21Cyan = 22; +static const RoomID kTSA22Cyan = 23; +static const RoomID kTSA23Cyan = 24; +static const RoomID kTSA24Cyan = 25; +static const RoomID kTSA25Cyan = 26; +static const RoomID kTSA21Red = 27; +static const RoomID kTSA23Red = 29; +static const RoomID kTSA24Red = 30; +static const RoomID kTSA25Red = 31; +static const RoomID kTSA26 = 32; +static const RoomID kTSA27 = 33; +static const RoomID kTSA28 = 34; +static const RoomID kTSA29 = 35; +static const RoomID kTSA30 = 36; +static const RoomID kTSA31 = 37; +static const RoomID kTSA32 = 38; +static const RoomID kTSA33 = 39; +static const RoomID kTSA34 = 40; +static const RoomID kTSA35 = 41; +static const RoomID kTSADeathRoom = 43; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivateTSAReadyForCard = 1; +static const HotSpotActivationID kActivateTSAReadyToTransport = 2; +static const HotSpotActivationID kActivateTSARobotsAwake = 3; +static const HotSpotActivationID kActivateTSA0BZoomedOut = 4; +static const HotSpotActivationID kActivateTSA0BZoomedIn = 5; +static const HotSpotActivationID kActivateTSA0BComparisonVideo = 6; +static const HotSpotActivationID kActivationLogReaderOpen = 7; +static const HotSpotActivationID kActivateTSA0BTBPVideo = 8; +static const HotSpotActivationID kActivationDoesntHaveKey = 9; +static const HotSpotActivationID kActivationKeyVaultOpen = 10; +static const HotSpotActivationID kActivationDoesntHaveChips = 11; +static const HotSpotActivationID kActivationChipVaultOpen = 12; +static const HotSpotActivationID kActivationJumpToPrehistoric = 13; +static const HotSpotActivationID kActivationJumpToNorad = 14; +static const HotSpotActivationID kActivationJumpToMars = 15; +static const HotSpotActivationID kActivationJumpToWSC = 16; +static const HotSpotActivationID kActivationReadyToExit = 17; +static const HotSpotActivationID kActivationReadyForJumpMenu = 18; +static const HotSpotActivationID kActivationMainJumpMenu = 19; + +// Hot Spot IDs. +static const HotSpotID kTSAGTCardDropSpotID = 5000; +static const HotSpotID kTSAGTTokyoSpotID = 5001; +static const HotSpotID kTSAGTCaldoriaSpotID = 5002; +static const HotSpotID kTSAGTBeachSpotID = 5003; +static const HotSpotID kTSAGTOtherSpotID = 5004; +static const HotSpotID kTSA02DoorSpotID = 5005; +static const HotSpotID kTSA03EastJimenezSpotID = 5006; +static const HotSpotID kTSA03WestCrenshawSpotID = 5007; +static const HotSpotID kTSA04EastMatsumotoSpotID = 5008; +static const HotSpotID kTSA04WestCastilleSpotID = 5009; +static const HotSpotID kTSA05EastSinclairSpotID = 5010; +static const HotSpotID kTSA05WestWhiteSpotID = 5011; +static const HotSpotID kTSA0AEastSpotID = 5012; +static const HotSpotID kTSA0AWastSpotID = 5013; +static const HotSpotID kTSA0BEastMonitorSpotID = 5014; +static const HotSpotID kTSA0BEastMonitorOutSpotID = 5015; +static const HotSpotID kTSA0BEastCompareNoradSpotID = 5016; +static const HotSpotID kTSA0BEastCompareMarsSpotID = 5017; +static const HotSpotID kTSA0BEastCompareCaldoriaSpotID = 5018; +static const HotSpotID kTSA0BEastCompareWSCSpotID = 5019; +static const HotSpotID kTSA0BEastLeftRewindSpotID = 5020; +static const HotSpotID kTSA0BEastLeftPlaySpotID = 5021; +static const HotSpotID kTSA0BEastRightRewindSpotID = 5022; +static const HotSpotID kTSA0BEastRightPlaySpotID = 5023; +static const HotSpotID kTSA0BEastCloseVideoSpotID = 5024; +static const HotSpotID kTSA0BNorthMonitorSpotID = 5025; +static const HotSpotID kTSA0BNorthMonitorOutSpotID = 5026; +static const HotSpotID kTSA0BNorthHistLogSpotID = 5027; +static const HotSpotID kTSA0BNorthRobotsToCommandCenterSpotID = 5028; +static const HotSpotID kTSA0BNorthRobotsToReadyRoomSpotID = 5029; +static const HotSpotID kTSA0BNorthRobotsToFrontDoorSpotID = 5030; +static const HotSpotID kTSA0BWestMonitorSpotID = 5031; +static const HotSpotID kTSA0BWestMonitorOutSpotID = 5032; +static const HotSpotID kTSA0BWestTheorySpotID = 5033; +static const HotSpotID kTSA0BWestBackgroundSpotID = 5034; +static const HotSpotID kTSA0BWestProcedureSpotID = 5035; +static const HotSpotID kTSA0BWestCloseVideoSpotID = 5036; +static const HotSpotID kTSA0BWestPlayVideoSpotID = 5037; +static const HotSpotID kTSA0BWestRewindVideoSpotID = 5038; +static const HotSpotID kTSA22EastMonitorSpotID = 5039; +static const HotSpotID kTSA22EastKeySpotID = 5040; +static const HotSpotID kTSA23WestMonitorSpotID = 5041; +static const HotSpotID kTSA23WestChipsSpotID = 5042; +static const HotSpotID kTSA34NorthDoorSpotID = 5043; +static const HotSpotID kTSA37NorthJumpToPrehistoricSpotID = 5044; +static const HotSpotID kTSA37NorthJumpToNoradSpotID = 5045; +static const HotSpotID kTSA37NorthCancelNoradSpotID = 5046; +static const HotSpotID kTSA37NorthJumpToMarsSpotID = 5047; +static const HotSpotID kTSA37NorthCancelMarsSpotID = 5048; +static const HotSpotID kTSA37NorthJumpToWSCSpotID = 5049; +static const HotSpotID kTSA37NorthCancelWSCSpotID = 5050; +static const HotSpotID kTSA37NorthExitSpotID = 5051; +static const HotSpotID kTSA37NorthJumpMenuSpotID = 5052; +static const HotSpotID kTSA37NorthNoradMenuSpotID = 5053; +static const HotSpotID kTSA37NorthMarsMenuSpotID = 5054; +static const HotSpotID kTSA37NorthWSCMenuSpotID = 5055; + +// Extra sequence IDs. +static const ExtraID kTSATransporterArrowLoop = 0; +static const ExtraID kTSAArriveFromCaldoria = 1; +static const ExtraID kTSAGTOtherChoice = 2; +static const ExtraID kTSAGTCardSwipe = 3; +static const ExtraID kTSAGTSelectCaldoria = 4; +static const ExtraID kTSAGTGoToCaldoria = 5; +static const ExtraID kTSAGTSelectBeach = 6; +static const ExtraID kTSAGTGoToBeach = 7; +static const ExtraID kTSAGTArriveAtBeach = 8; +static const ExtraID kTSAGTSelectTokyo = 9; +static const ExtraID kTSAGTGoToTokyo = 10; +static const ExtraID kTSAGTArriveAtTokyo = 11; +static const ExtraID kTSA02NorthZoomIn = 12; +static const ExtraID kTSA02NorthTenSecondDoor = 13; +static const ExtraID kTSA02NorthZoomOut = 14; +static const ExtraID kTSA02NorthDoorWithAgent3 = 15; +static const ExtraID kTSA03JimenezZoomIn = 16; +static const ExtraID kTSA03JimenezSpeech = 17; +static const ExtraID kTSA03JimenezZoomOut = 18; +static const ExtraID kTSA03CrenshawZoomIn = 19; +static const ExtraID kTSA03CrenshawSpeech = 20; +static const ExtraID kTSA03CrenshawZoomOut = 21; +static const ExtraID kTSA03SouthRobotDeath = 22; +static const ExtraID kTSA04NorthRobotGreeting = 23; +static const ExtraID kTSA04MatsumotoZoomIn = 24; +static const ExtraID kTSA04MatsumotoSpeech = 25; +static const ExtraID kTSA04MatsumotoZoomOut = 26; +static const ExtraID kTSA04CastilleZoomIn = 27; +static const ExtraID kTSA04CastilleSpeech = 28; +static const ExtraID kTSA04CastilleZoomOut = 29; +static const ExtraID kTSA05SinclairZoomIn = 30; +static const ExtraID kTSA05SinclairSpeech = 31; +static const ExtraID kTSA05SinclairZoomOut = 32; +static const ExtraID kTSA05WhiteZoomIn = 33; +static const ExtraID kTSA05WhiteSpeech = 34; +static const ExtraID kTSA05WhiteZoomOut = 35; +static const ExtraID kTSA0AEastRobot = 36; +static const ExtraID kTSA0AWestRobot = 37; +static const ExtraID kTSA16NorthRobotDeath = 38; +static const ExtraID kTSA0BEastZoomIn = 39; +static const ExtraID kTSA0BEastZoomedView = 40; +static const ExtraID kTSA0BEastZoomOut = 41; +static const ExtraID kTSA0BEastTurnLeft = 42; +static const ExtraID kTSA0BComparisonStartup = 43; +static const ExtraID kTSA0BComparisonView0000 = 44; +static const ExtraID kTSA0BComparisonView0002 = 45; +static const ExtraID kTSA0BComparisonView0020 = 46; +static const ExtraID kTSA0BComparisonView0022 = 47; +static const ExtraID kTSA0BComparisonView0200 = 48; +static const ExtraID kTSA0BComparisonView0202 = 49; +static const ExtraID kTSA0BComparisonView0220 = 50; +static const ExtraID kTSA0BComparisonView0222 = 51; +static const ExtraID kTSA0BComparisonView2000 = 52; +static const ExtraID kTSA0BComparisonView2002 = 53; +static const ExtraID kTSA0BComparisonView2020 = 54; +static const ExtraID kTSA0BComparisonView2022 = 55; +static const ExtraID kTSA0BComparisonView2200 = 56; +static const ExtraID kTSA0BComparisonView2202 = 57; +static const ExtraID kTSA0BComparisonView2220 = 58; +static const ExtraID kTSA0BComparisonView2222 = 59; +static const ExtraID kTSA0BNoradComparisonView = 60; +static const ExtraID kTSA0BNoradUnaltered = 61; +static const ExtraID kTSA0BNoradAltered = 62; +static const ExtraID kTSA0BMarsComparisonView = 63; +static const ExtraID kTSA0BMarsUnaltered = 64; +static const ExtraID kTSA0BMarsAltered = 65; +static const ExtraID kTSA0BWSCComparisonView = 66; +static const ExtraID kTSA0BWSCUnaltered = 67; +static const ExtraID kTSA0BWSCAltered = 68; +static const ExtraID kTSA0BCaldoriaComparisonView = 69; +static const ExtraID kTSA0BCaldoriaUnaltered = 70; +static const ExtraID kTSA0BCaldoriaAltered = 71; +static const ExtraID kTSA0BNorthZoomIn = 72; +static const ExtraID kTSA0BNorthZoomedView = 73; +static const ExtraID kTSA0BNorthZoomOut = 74; +static const ExtraID kTSA0BNorthTurnLeft = 75; +static const ExtraID kTSA0BNorthTurnRight = 76; +static const ExtraID kTSA0BNorthHistLogOpen = 77; +static const ExtraID kTSA0BNorthHistLogClose = 78; +static const ExtraID kTSA0BNorthHistLogCloseWithLog = 79; +static const ExtraID kTSA0BNorthCantChangeHistory = 80; +static const ExtraID kTSA0BNorthYoureBusted = 81; +static const ExtraID kTSA0BNorthFinallyHappened = 82; +static const ExtraID kTSA0BShowRip1 = 83; +static const ExtraID kTSA0BNorthRipView1 = 84; +static const ExtraID kTSA0BShowRip2 = 85; +static const ExtraID kTSA0BShowGuardRobots = 86; +static const ExtraID kTSA0BAIInterruption = 87; +static const ExtraID kTSA0BRobotsToCommandCenter = 88; +static const ExtraID kTSA0BNorthRobotsAtCCView = 89; +static const ExtraID kTSA0BNorthRobotsAtRRView = 90; +static const ExtraID kTSA0BNorthRobotsAtFDView = 91; +static const ExtraID kTSA0BRobotsFromCommandCenterToReadyRoom = 92; +static const ExtraID kTSA0BRobotsFromReadyRoomToCommandCenter = 93; +static const ExtraID kTSA0BRobotsFromCommandCenterToFrontDoor = 94; +static const ExtraID kTSA0BRobotsFromFrontDoorToCommandCenter = 95; +static const ExtraID kTSA0BRobotsFromFrontDoorToReadyRoom = 96; +static const ExtraID kTSA0BRobotsFromReadyRoomToFrontDoor = 97; +static const ExtraID kTSA0BWestZoomIn = 98; +static const ExtraID kTSA0BWestZoomedView = 99; +static const ExtraID kTSA0BWestZoomOut = 100; +static const ExtraID kTSA0BWestTurnRight = 101; +static const ExtraID kTSA0BTBPTheoryHighlight = 102; +static const ExtraID kTSA0BTBPBackgroundHighlight = 103; +static const ExtraID kTSA0BTBPProcedureHighlight = 104; +static const ExtraID kTSA0BTBPTheory = 105; +static const ExtraID kTSA0BTBPBackground = 106; +static const ExtraID kTSA0BTBPProcedure = 107; +static const ExtraID kTSA0BRipAlarmScreen = 108; +static const ExtraID kTSA22RedEastZoomInSequence = 109; +static const ExtraID kTSA22RedEastVaultViewWithKey = 110; +static const ExtraID kTSA22RedEastVaultViewNoKey = 111; +static const ExtraID kTSA23RedWestVaultZoomInSequence = 112; +static const ExtraID kTSA23RedWestVaultViewWithChips = 113; +static const ExtraID kTSA23RedWestVaultViewNoChips = 114; +static const ExtraID kTSA25NorthDeniedNoKey = 115; +static const ExtraID kTSA25NorthDeniedNoChip = 116; +static const ExtraID kTSA25NorthPutOnSuit = 117; +static const ExtraID kTSA25NorthAlreadyHaveSuit = 118; +static const ExtraID kTSA25NorthDescending1 = 119; +static const ExtraID kTSA25NorthDescending2 = 120; +static const ExtraID kTSA37HorseToAI1 = 121; +static const ExtraID kTSA37PegasusAI1 = 122; +static const ExtraID kTSA37AI1ToCommissioner1 = 123; +static const ExtraID kTSA37Commissioner1 = 124; +static const ExtraID kTSA37Commissioner1ToZoom = 125; +static const ExtraID kTSA37ZoomToPrehistoric = 126; +static const ExtraID kTSA37PrehistoricToAI2 = 127; +static const ExtraID kTSA37PegasusAI2 = 128; +static const ExtraID kTSA37AI2ToPrehistoric = 129; +static const ExtraID kTSA37PrehistoricToDepart = 130; +static const ExtraID kTSA37PegasusDepart = 131; +static const ExtraID kTSA37TimeJumpToPegasus = 132; +static const ExtraID kTSA37RecallToDownload = 133; +static const ExtraID kTSA37DownloadToColonel1 = 134; +static const ExtraID kTSA37Colonel1 = 135; +static const ExtraID kTSA37Colonel1ToReviewRequired = 136; +static const ExtraID kTSA37ReviewRequiredToExit = 137; +static const ExtraID kTSA37ExitHilited = 138; +static const ExtraID kTSA37ExitToHorse = 139; +static const ExtraID kTSA37HorseToColonel2 = 140; +static const ExtraID kTSA37Colonel2 = 141; +static const ExtraID kTSA37PegasusAI3 = 142; +static const ExtraID kTSA37AI3ToHorse = 143; +static const ExtraID kTSA37HorseToZoom = 144; +static const ExtraID kTSA37ZoomToMainMenu = 145; +static const ExtraID kTSA37MainMenuToAI4 = 146; +static const ExtraID kTSA37PegasusAI4 = 147; +static const ExtraID kTSA37AI4ToMainMenu = 148; +static const ExtraID kTSA37JumpMenu000 = 149; +static const ExtraID kTSA37JumpMenu001 = 150; +static const ExtraID kTSA37JumpMenu010 = 151; +static const ExtraID kTSA37JumpMenu011 = 152; +static const ExtraID kTSA37JumpMenu100 = 153; +static const ExtraID kTSA37JumpMenu101 = 154; +static const ExtraID kTSA37JumpMenu110 = 155; +static const ExtraID kTSA37JumpMenu111 = 156; +static const ExtraID kTSA37JumpToWSCMenu = 157; +static const ExtraID kTSA37CancelWSC = 158; +static const ExtraID kTSA37JumpToWSC = 159; +static const ExtraID kTSA37WSCToAI5 = 160; +static const ExtraID kTSA37PegasusAI5 = 161; +static const ExtraID kTSA37AI5ToWSC = 162; +static const ExtraID kTSA37WSCToDepart = 163; +static const ExtraID kTSA37JumpToMarsMenu = 164; +static const ExtraID kTSA37CancelMars = 165; +static const ExtraID kTSA37JumpToMars = 166; +static const ExtraID kTSA37MarsToAI6 = 167; +static const ExtraID kTSA37PegasusAI6 = 168; +static const ExtraID kTSA37AI6ToMars = 169; +static const ExtraID kTSA37MarsToDepart = 170; +static const ExtraID kTSA37JumpToNoradMenu = 171; +static const ExtraID kTSA37CancelNorad = 172; +static const ExtraID kTSA37JumpToNorad = 173; +static const ExtraID kTSA37NoradToAI7 = 174; +static const ExtraID kTSA37PegasusAI7 = 175; +static const ExtraID kTSA37AI7ToNorad = 176; +static const ExtraID kTSA37NoradToDepart = 177; +static const ExtraID kTSA37EnvironmentalScan = 178; +static const ExtraID kTSA37DownloadToMainMenu = 179; +static const ExtraID kTSA37DownloadToOpMemReview = 180; +static const ExtraID kTSA37OpMemReviewToMainMenu = 181; +static const ExtraID kTSA37OpMemReviewToAllClear = 182; +static const ExtraID kTSA37AllClearToCongratulations = 183; +static const ExtraID kTSA37Congratulations = 184; +static const ExtraID kTSA37CongratulationsToExit = 185; + +const DisplayOrder kRipTimerOrder = kMonitorLayer; + + +const CoordType kUnresolvedLeft = kNavAreaLeft + 14; +const CoordType kUnresolvedTop = kNavAreaTop + 236; + +const CoordType kResolvedLeft = kNavAreaLeft + 36; +const CoordType kResolvedTop = kNavAreaTop + 236; + +const CoordType kJumpMenuLeft = kNavAreaLeft + 360; +const CoordType kJumpMenuTop = kNavAreaTop + 202; + +const CoordType kJumpMenuHilitedLeft = kNavAreaLeft + 354; +const CoordType kJumpMenuHilitedTop = kNavAreaTop + 196; + +const CoordType kExitLeft = kNavAreaLeft + 360; +const CoordType kExitTop = kNavAreaTop + 216; + +const CoordType kExitHilitedLeft = kNavAreaLeft + 354; +const CoordType kExitHilitedTop = kNavAreaTop + 210; + +const CoordType kRipTimerLeft = kNavAreaLeft + 95; +const CoordType kRipTimerTop = kNavAreaTop + 87; + +const CoordType kTBPCloseLeft = kNavAreaLeft + 30; +const CoordType kTBPCloseTop = kNavAreaTop + 16; + +const CoordType kTBPRewindLeft = kNavAreaLeft + 86; +const CoordType kTBPRewindTop = kNavAreaTop + 218; + +const CoordType kComparisonCloseLeft = kNavAreaLeft + 50; +const CoordType kComparisonCloseTop = kNavAreaTop + 14; + +const CoordType kComparisonLeftRewindLeft = kNavAreaLeft + 96; +const CoordType kComparisonLeftRewindTop = kNavAreaTop + 190; + +const CoordType kComparisonRightRewindLeft = kNavAreaLeft + 282; +const CoordType kComparisonRightRewindTop = kNavAreaTop + 190; + +const CoordType kComparisonHiliteSpriteLeft = kNavAreaLeft + 45; +const CoordType kComparisonHiliteSpriteTop = kNavAreaTop + 65; + +const CoordType kComparisonHiliteNoradLeft = kNavAreaLeft + 45; +const CoordType kComparisonHiliteNoradTop = kNavAreaTop + 65; + +const CoordType kComparisonHiliteMarsLeft = kNavAreaLeft + 45 + 4; +const CoordType kComparisonHiliteMarsTop = kNavAreaTop + 65 + 23; + +const CoordType kComparisonHiliteCaldoriaLeft = kNavAreaLeft + 45 + 7; +const CoordType kComparisonHiliteCaldoriaTop = kNavAreaTop + 65 + 46; + +const CoordType kComparisonHiliteWSCLeft = kNavAreaLeft + 45 + 11; +const CoordType kComparisonHiliteWSCTop = kNavAreaTop + 65 + 68; + +const CoordType kComparisonChancesSpriteLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesSpriteTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesNoradLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesNoradTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesMarsLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesMarsTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesCaldoriaLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesCaldoriaTop = kNavAreaTop + 162 + 1; + +const CoordType kComparisonChancesWSCLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesWSCTop = kNavAreaTop + 162; + +const CoordType kRedirectionSprite1Left = kNavAreaLeft + 58; +const CoordType kRedirectionSprite1Top = kNavAreaTop + 16; + +const CoordType kRedirectionSprite2Left = kNavAreaLeft + 36; +const CoordType kRedirectionSprite2Top = kNavAreaTop + 166; + +const CoordType kRedirectionCCRolloverLeft = kNavAreaLeft + 58; +const CoordType kRedirectionCCRolloverTop = kNavAreaTop + 16; + +const CoordType kRedirectionRRRolloverLeft = kNavAreaLeft + 430; +const CoordType kRedirectionRRRolloverTop = kNavAreaTop + 30; + +const CoordType kRedirectionFDRolloverLeft = kNavAreaLeft + 278; +const CoordType kRedirectionFDRolloverTop = kNavAreaTop + 160; + +const CoordType kRedirectionCCDoorLeft = kNavAreaLeft + 174; +const CoordType kRedirectionCCDoorTop = kNavAreaTop + 36; + +const CoordType kRedirectionRRDoorLeft = kNavAreaLeft + 418; +const CoordType kRedirectionRRDoorTop = kNavAreaTop + 32; + +const CoordType kRedirectionFDDoorLeft = kNavAreaLeft + 298; +const CoordType kRedirectionFDDoorTop = kNavAreaTop + 240; + +const CoordType kRedirectionSecuredLeft = kNavAreaLeft + 36; +const CoordType kRedirectionSecuredTop = kNavAreaTop + 166; + +const CoordType kRedirectionNewTargetLeft = kNavAreaLeft + 36; +const CoordType kRedirectionNewTargetTop = kNavAreaTop + 166; + +const CoordType kRedirectionCloseLeft = kNavAreaLeft + 56; +const CoordType kRedirectionCloseTop = kNavAreaTop + 220; + +static const TimeValue kTSABumpIntoWallIn = 0; +static const TimeValue kTSABumpIntoWallOut = 148; + +static const TimeValue kTSAGTDoorCloseIn = 148; +static const TimeValue kTSAGTDoorCloseOut = 1570; + +static const TimeValue kTSANoOtherDestinationIn = 1570; +static const TimeValue kTSANoOtherDestinationOut = 3601; + +static const TimeValue kTSAEntryDoorCloseIn = 3601; +static const TimeValue kTSAEntryDoorCloseOut = 4200; + +static const TimeValue kTSAInsideDoorCloseIn = 4200; +static const TimeValue kTSAInsideDoorCloseOut = 4800; + +static const TimeValue kTSAVaultCloseIn = 4800; +static const TimeValue kTSAVaultCloseOut = 5388; + +static const TimeValue kTSAPegasusDoorCloseIn = 5388; +static const TimeValue kTSAPegasusDoorCloseOut = 6457; + +static const bool kPegasusUnresolved = false; +static const bool kPegasusResolved = true; +static const bool kPegasusCantExit = false; +static const bool kPegasusCanExit = true; + +// Monitor modes +enum { + kMonitorNeutral = 0, + kMonitorTheory = 1, + kMonitorProcedure = 2, + kMonitorBackground = 3, + kMonitorNoradComparison = 4, + kMonitorMarsComparison = 5, + kMonitorCaldoriaComparison = 6, + kMonitorWSCComparison = 7, + + kRawModeMask = 0x0F, + kPlayingTBPMask = 0x10, + kPlayingLeftComparisonMask = 0x20, + kPlayingRightComparisonMask = 0x40, + + kPlayingAnyMask = kPlayingTBPMask | + kPlayingLeftComparisonMask | + kPlayingRightComparisonMask, + + kMonitorPlayingTheory = kMonitorTheory | kPlayingTBPMask, + kMonitorPlayingProcedure = kMonitorProcedure | kPlayingTBPMask, + kMonitorPlayingBackground = kMonitorBackground | kPlayingTBPMask, + + kMonitorPlayingLeftNoradComparison = kMonitorNoradComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightNoradComparison = kMonitorNoradComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftMarsComparison = kMonitorMarsComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightMarsComparison = kMonitorMarsComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftCaldoriaComparison = kMonitorCaldoriaComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightCaldoriaComparison = kMonitorCaldoriaComparison | + kPlayingRightComparisonMask, + kMonitorPlayingLeftWSCComparison = kMonitorWSCComparison | + kPlayingLeftComparisonMask, + kMonitorPlayingRightWSCComparison = kMonitorWSCComparison | + kPlayingRightComparisonMask +}; + +static const ExtraID s_historicalLogViews[16] = { + kTSA0BComparisonView0000, + kTSA0BComparisonView0002, + kTSA0BComparisonView0020, + kTSA0BComparisonView0022, + kTSA0BComparisonView0200, + kTSA0BComparisonView0202, + kTSA0BComparisonView0220, + kTSA0BComparisonView0222, + kTSA0BComparisonView2000, + kTSA0BComparisonView2002, + kTSA0BComparisonView2020, + kTSA0BComparisonView2022, + kTSA0BComparisonView2200, + kTSA0BComparisonView2202, + kTSA0BComparisonView2220, + kTSA0BComparisonView2222 +}; + +static const int kRedirectionCCRolloverSprite = 0; +static const int kRedirectionRRRolloverSprite = 1; +static const int kRedirectionFDRolloverSprite = 2; +static const int kRedirectionCCDoorSprite = 3; +static const int kRedirectionRRDoorSprite = 4; +static const int kRedirectionFDDoorSprite = 5; +static const int kRedirectionCloseSprite = 6; +static const int kRedirectionSecuredSprite = 0; +static const int kRedirectionNewTargetSprite = 1; + +void RipTimer::initImage() { + _middle = -1; + + _timerImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLeftRipPICTID); + + Common::Rect r; + _timerImage.getSurfaceBounds(r); + setBounds(r); +} + +void RipTimer::releaseImage() { + _timerImage.deallocateSurface(); +} + +void RipTimer::draw(const Common::Rect &updateRect) { + Common::Rect bounds; + getBounds(bounds); + + Common::Rect r1 = bounds; + r1.right = _middle; + r1 = updateRect.findIntersectingRect(r1); + + if (!r1.isEmpty()) { + Common::Rect r2 = r1; + r2.moveTo(r1.left - _bounds.left, r1.top - bounds.top); + _timerImage.copyToCurrentPort(r2, r1); + } +} + +void RipTimer::timeChanged(const TimeValue newTime) { + Common::Rect bounds; + getBounds(bounds); + + CoordType newMiddle = bounds.left + bounds.width() * newTime / getDuration(); + + if (newMiddle != _middle) { + _middle = newMiddle; + triggerRedraw(); + } + + if (newTime == getStop()) + ((PegasusEngine *)g_engine)->die(kDeathUncreatedInTSA); +} + +FullTSA::FullTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Full TSA", kFullTSAID), + _ripTimer(kNoDisplayElement), _sprite1(kNoDisplayElement), _sprite2(kNoDisplayElement), _sprite3(kNoDisplayElement) { + setIsItemTaken(kJourneymanKey); + setIsItemTaken(kPegasusBiochip); + setIsItemTaken(kMapBiochip); +} + +void FullTSA::init() { + Neighborhood::init(); + _ripTimer.setDisplayOrder(kRipTimerOrder); + _ripTimer.startDisplaying(); + + if (!GameState.getTSASeenRobotGreeting()) + forceStridingStop(kTSA03, kNorth, kNoAlternateID); + + _sprite1.setDisplayOrder(kMonitorLayer); + _sprite1.startDisplaying(); + _sprite2.setDisplayOrder(kMonitorLayer); + _sprite2.startDisplaying(); + _sprite3.setDisplayOrder(kMonitorLayer); + _sprite3.startDisplaying(); + + // Fix a mistake in the world builder tables. + HotspotInfoTable::Entry *entry = findHotspotEntry(kTSA23WestChipsSpotID); + entry->hotspotItem = kPegasusBiochip; +} + +void FullTSA::dieUncreatedInTSA() { + die(kDeathUncreatedInTSA); +} + +void FullTSA::start() { + g_energyMonitor->stopEnergyDraining(); + + if (!GameState.getScoringEnterTSA()) { + _utilityFuse.primeFuse(GameState.getTSAFuseTimeLimit()); + _utilityFuse.setFunctor(new Common::Functor0Mem<void, FullTSA>(this, &FullTSA::dieUncreatedInTSA)); + _utilityFuse.lightFuse(); + } else if (GameState.getTSAState() == kTSAPlayerDetectedRip || GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) { + _ripTimer.initImage(); + _ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop); + _ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale); + _ripTimer.setTime(GameState.getRipTimerTime()); + _ripTimer.start(); + } + + Neighborhood::start(); +} + +void FullTSA::flushGameState() { + GameState.setRipTimerTime(_ripTimer.getTime()); + GameState.setTSAFuseTimeLimit(_utilityFuse.getTimeRemaining()); +} + +Common::String FullTSA::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) { + RoomID room = GameState.getCurrentRoom(); + + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + case kTSAPlayerForcedReview: + if (room >= kTSA16 && room <= kTSA0B) + return "Images/AI/TSA/XT01A"; + + return "Images/AI/TSA/XT01"; + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + return "Images/AI/TSA/XT02"; + case kTSAPlayerGotHistoricalLog: + case kTSAPlayerInstalledHistoricalLog: + return "Images/AI/TSA/XT03"; + default: + switch (getCurrentActivation()) { + case kActivationJumpToPrehistoric: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI2, kHintInterruption); + startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToNorad: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI7, kHintInterruption); + startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToMars: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI6, kHintInterruption); + startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput); + g_AIChip->clearClicked(); + break; + case kActivationJumpToWSC: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTSA37PegasusAI5, kHintInterruption); + startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput); + g_AIChip->clearClicked(); + break; + default: + if (GameState.allTimeZonesFinished()) + return "Images/AI/TSA/XT05"; + + return "Images/AI/TSA/XT04"; + } + break; + } + } + + return movieName; +} + +Common::String FullTSA::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + case kTSAPlayerForcedReview: + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + return "Images/AI/TSA/XTE1"; + default: + if (GameState.getCurrentRoom() == kTSA37) { + g_AIChip->showEnvScanClicked(); + startExtraSequenceSync(kTSA37EnvironmentalScan, kHintInterruption); + + switch (getCurrentActivation()) { + case kActivationJumpToPrehistoric: + startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput); + break; + case kActivationJumpToNorad: + startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput); + showExtraView(kTSA37JumpToNoradMenu); + break; + case kActivationJumpToMars: + startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput); + showExtraView(kTSA37JumpToMarsMenu); + break; + case kActivationJumpToWSC: + startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput); + showExtraView(kTSA37JumpToWSCMenu); + break; + default: + startExtraSequenceSync(kTSA37AI4ToMainMenu, kFilterNoInput); + break; + } + + g_AIChip->clearClicked(); + } else if (GameState.allTimeZonesFinished()) { + return "Images/AI/TSA/XTE1"; + } else { + return "Images/AI/TSA/XTE2"; + } + break; + } + } + + return movieName; +} + +uint FullTSA::getNumHints() { + uint numHints = Neighborhood::getNumHints(); + + if (numHints == 0) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getCurrentRoom() == kTSA0B && GameState.getTSA0BZoomedIn()) + numHints = 3; + break; + } + } + + return numHints; +} + +Common::String FullTSA::getHintMovie(uint hintNum) { + Common::String movieName = Neighborhood::getHintMovie(hintNum); + + if (movieName.empty()) + movieName = Common::String::format("Images/AI/TSA/XT20NH%d", hintNum); + + return movieName; +} + +void FullTSA::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + switch (GameState.getTSAState()) { + case kTSAPlayerDetectedRip: + case kTSAPlayerNeedsHistoricalLog: + if ((room >= kTSA16 && room <= kTSA0B) || (room >= kTSA21Cyan && room <= kTSA24Cyan) || (room >= kTSA21Red && room <= kTSA24Red)) + loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 4, 0, 0); + else if (room == kTSA25Cyan || room == kTSA25Red) + loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 6, 0, 0); + else + loadLoopSound1("Sounds/TSA/TSA EchoClaxon.22K.AIFF", 0x100 / 4, 0, 0); + break; + default: + if (room >= kTSA00 && room <= kTSA02) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + else if (room >= kTSA03 && room <= kTSA15) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + else if (room >= kTSA16 && room <= kTSA0B) + loadLoopSound1("Sounds/TSA/T14SAEO1.22K.AIFF"); + else if (room >= kTSA21Cyan && room <= kTSA25Red) + loadLoopSound1("Sounds/TSA/T15SAE01.22K.AIFF"); + else if (room >= kTSA26 && room <= kTSA37) + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); + break; + } +} + +short FullTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 result = Neighborhood::getStaticCompassAngle(room, dir); + + switch (room) { + case kTSA08: + result += kCompassShift; + break; + case kTSA09: + result -= kCompassShift; + break; + case kTSA10: + result += kCompassShift * 2; + break; + case kTSA11: + case kTSA22Cyan: + case kTSA22Red: + result -= kCompassShift * 2; + break; + case kTSA12: + result += kCompassShift * 3; + break; + case kTSA13: + result -= kCompassShift * 3; + break; + case kTSA14: + case kTSA16: + case kTSA17: + case kTSA18: + case kTSA19: + result += kCompassShift * 4; + break; + case kTSA0B: + result += kCompassShift * 4; + + if (dir == kWest) + result += 30; + else if (dir == kEast) + result -= 30; + break; + case kTSA33: + result += kCompassShift * 4; + break; + case kTSA15: + case kTSA21Cyan: + case kTSA24Cyan: + case kTSA25Cyan: + case kTSA21Red: + case kTSA24Red: + case kTSA25Red: + case kTSA26: + case kTSA27: + case kTSA28: + case kTSA29: + case kTSA30: + result -= kCompassShift * 4; + break; + case kTSA23Cyan: + case kTSA23Red: + result -= kCompassShift * 6; + break; + case kTSA32: + result -= kCompassShift * 8; + break; + case kTSA34: + result -= kCompassShift * 12; + break; + case kTSA35: + result += kCompassShift * 8; + break; + case kTSA37: + result -= kCompassShift * 2; + break; + } + + return result; +} + +void FullTSA::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { + case MakeRoomView(kTSA01, kSouth): + compassMove.insertFaderKnot(exitEntry.movieStart, -180); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 3, -180); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 33, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + case MakeRoomView(kTSA11, kEast): + if (getCurrentAlternate() == kAltTSARobotsAtReadyRoom) { + compassMove.makeTwoKnotFaderSpec(kFullTSAMovieScale, exitEntry.movieStart, + getStaticCompassAngle(kTSA11, kEast), exitEntry.movieEnd, + getStaticCompassAngle(kTSA13, kEast)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 13, compassMove.getNthKnotValue(1)); + } + break; + case MakeRoomView(kTSA34, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 48, + getStaticCompassAngle(exitEntry.room, exitEntry.direction)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 68, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + case MakeRoomView(kTSA37, kNorth): + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 38, + getStaticCompassAngle(exitEntry.room, exitEntry.direction)); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 64, + getStaticCompassAngle(exitEntry.room, exitEntry.direction) + kCompassShift * 3 / 2); + compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 105, + getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); + break; + } +} + +void FullTSA::getExtraCompassMove(const ExtraTable::Entry &extraEntry, FaderMoveSpec &compassMove) { + int16 angle; + + switch (extraEntry.extra) { + case kTSA0BEastTurnLeft: + case kTSA0BNorthTurnLeft: + angle =getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle - 60); + break; + case kTSA0BNorthTurnRight: + case kTSA0BWestTurnRight: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle + 60); + break; + case kTSA22RedEastZoomInSequence: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle); + compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 8160, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 9840, angle); + break; + case kTSA23RedWestVaultZoomInSequence: + angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, + extraEntry.movieEnd, angle); + compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 10100, angle - kCompassShift * 2); + compassMove.insertFaderKnot(extraEntry.movieStart + 11880, angle); + break; + default: + Neighborhood::getExtraCompassMove(extraEntry, compassMove); + break; + } +} + +uint16 FullTSA::getDateResID() const { + return kDate2318ID; +} + +TimeValue FullTSA::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraID extraID = 0xffffffff; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + extraID = s_historicalLogViews[getHistoricalLogIndex()]; + break; + default: + extraID = kTSA0BEastZoomedView; + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + extraID = kTSA0BNorthRipView1; + break; + default: + extraID = kTSA0BNorthZoomedView; + break; + } + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) + extraID = kTSA0BWestZoomedView; + break; + case MakeRoomView(kTSA22Red, kEast): + if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) { + if (_vm->itemInLocation(kJourneymanKey, kFullTSAID, kTSA22Red, kEast)) + extraID = kTSA22RedEastVaultViewWithKey; + else + extraID = kTSA22RedEastVaultViewNoKey; + } + break; + case MakeRoomView(kTSA23Red, kWest): + if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) { + if (_vm->itemInLocation(kPegasusBiochip, kFullTSAID, kTSA23Red, kWest)) + extraID = kTSA23RedWestVaultViewWithChips; + else + extraID = kTSA23RedWestVaultViewNoChips; + } + break; + case MakeRoomView(kTSA37, kNorth): + switch (GameState.getTSAState()) { + case kTSAPlayerGotHistoricalLog: + extraID = kTSA37ReviewRequiredToExit; + break; + case kPlayerFinishedWithTSA: + extraID = kTSA37CongratulationsToExit; + break; + default: + extraID = kTSA37AI3ToHorse; + break; + } + break; + } + + if (extraID != 0xffffffff) { + ExtraTable::Entry entry; + getExtraEntry(extraID, entry); + return entry.movieEnd - 1; + } + + return Neighborhood::getViewTime(room, direction); +} + +void FullTSA::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA0B, kNorth): + case MakeRoomView(kTSA0B, kEast): + case MakeRoomView(kTSA0B, kWest): + if (!GameState.getTSA0BZoomedIn()) + Neighborhood::findSpotEntry(room, direction, flags, entry); + break; + default: + Neighborhood::findSpotEntry(room, direction, flags, entry); + break; + } +} + +void FullTSA::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { + Neighborhood::getExtraEntry(id, extraEntry); + + if (id == kTSA0BShowGuardRobots) + extraEntry.movieStart += kFullTSAFrameDuration * 3; +} + +void FullTSA::pickedUpItem(Item *item) { + BiochipItem *biochip; + + switch (item->getObjectID()) { + case kJourneymanKey: + GameState.setScoringGotJourneymanKey(true); + break; + case kPegasusBiochip: + biochip = (BiochipItem *)_vm->getAllItems().findItemByID(kMapBiochip); + _vm->addItemToBiochips(biochip); + GameState.setScoringGotPegasusBiochip(true); + break; + } +} + +void FullTSA::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) { + switch (extraEntry.extra) { + case kTSA0BNorthZoomIn: + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) { + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + requestExtraSequence(kTSA0BNorthHistLogClose, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + } else { + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + } + break; + case kTSA0BNorthZoomOut: + if (_ripTimer.isVisible()) + _ripTimer.hide(); + + shutDownRobotMonitor(); + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + case kTSA0BEastZoomOut: + shutDownComparisonMonitor(); + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + default: + Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); + break; + } +} + +void FullTSA::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA00, kNorth): + if (GameState.getLastNeighborhood() != kFullTSAID) { + startExtraSequence(kTSAArriveFromCaldoria, kDoorOpenCompletedFlag, kFilterNoInput); + return; + } + break; + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAIDedAtDoor()) { + GameState.setTSAIDedAtDoor(true); + requestExtraSequence(kTSA02NorthZoomIn, 0, kFilterNoInput); + requestExtraSequence(kTSA02NorthTenSecondDoor, 0, kFilterNoInput); + + if (GameState.getTSASeenAgent3AtDoor()) { + requestExtraSequence(kTSA02NorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + } else { + GameState.setTSASeenAgent3AtDoor(true); + requestExtraSequence(kTSA02NorthZoomOut, 0, kFilterNoInput); + requestExtraSequence(kTSA02NorthDoorWithAgent3, kDoorOpenCompletedFlag, kFilterNoInput); + } + return; + } + break; + case MakeRoomView(kTSA03, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor) { + playDeathExtra(kTSA03SouthRobotDeath, kDeathShotByTSARobots); + return; + } + break; + case MakeRoomView(kTSA16, kNorth): + if (GameState.getTSAState() == kRobotsAtCommandCenter) { + playDeathExtra(kTSA16NorthRobotDeath, kDeathShotByTSARobots); + return; + } + break; + } + + Neighborhood::startDoorOpenMovie(startTime, stopTime); +} + +InputBits FullTSA::getInputFilter() { + InputBits result = Neighborhood::getInputFilter(); + + switch (GameState.getCurrentRoom()) { + case kTSA0B: + if (GameState.getT0BMonitorMode() != kMonitorNeutral) + // Only allow a click. + result &= JMPPPInput::getClickInputFilter(); + break; + case kTSA37: + // Can't move forward in Pegasus. Only press the exit button. + result &= ~(kFilterUpButton | kFilterUpAuto); + break; + } + + return result; +} + +void FullTSA::turnLeft() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kNorth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) + setCurrentAlternate(kAltTSANormal); + break; + case MakeRoomView(kTSA0B, kNorth): + if (_ripTimer.isVisible()) + _ripTimer.hide(); + releaseSprites(); + break; + case MakeRoomView(kTSA0B, kEast): + shutDownComparisonMonitor(); + break; + } + + Neighborhood::turnLeft(); +} + +void FullTSA::turnRight() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kSouth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) + setCurrentAlternate(kAltTSANormal); + break; + case MakeRoomView(kTSA0B, kNorth): + if (_ripTimer.isVisible()) + _ripTimer.hide(); + releaseSprites(); + break; + case MakeRoomView(kTSA0B, kEast): + shutDownComparisonMonitor(); + break; + } + + Neighborhood::turnRight(); +} + +void FullTSA::openDoor() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA15, kSouth): + if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog || GameState.getTSAState() == kRobotsAtFrontDoor) + setCurrentAlternate(kAltTSARedAlert); + break; + } + + Neighborhood::openDoor(); +} + +CanMoveForwardReason FullTSA::canMoveForward(ExitTable::Entry &entry) { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kTSA25Red, kNorth)) + return kCantMoveBlocked; + + return Neighborhood::canMoveForward(entry); +} + +CanOpenDoorReason FullTSA::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAFrontDoorUnlockedOutside()) + return kCantOpenLocked; + break; + case MakeRoomView(kTSA03, kSouth): + if (!GameState.getTSAFrontDoorUnlockedInside()) + return kCantOpenLocked; + break; + case MakeRoomView(kTSA16, kNorth): + if (GameState.getTSACommandCenterLocked()) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void FullTSA::bumpIntoWall() { + requestSpotSound(kTSABumpIntoWallIn, kTSABumpIntoWallOut, kFilterAllInput, 0); + Neighborhood::bumpIntoWall(); +} + +void FullTSA::downButton(const Input &input) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn() && GameState.getT0BMonitorMode() == kMonitorNeutral) + startExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::downButton(input); + } +} + +void FullTSA::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *spot) { + switch (spot->getObjectID()) { + case kTSA0BEastLeftRewindSpotID: + case kTSA0BEastLeftPlaySpotID: + if (_privateFlags.getFlag(kTSAPrivatePlayingRightComparisonFlag)) + spot->setInactive(); + else + Neighborhood::activateOneHotspot(entry, spot); + break; + case kTSA0BEastRightRewindSpotID: + case kTSA0BEastRightPlaySpotID: + if (_privateFlags.getFlag(kTSAPrivatePlayingLeftComparisonFlag)) + spot->setInactive(); + else + Neighborhood::activateOneHotspot(entry, spot); + break; + default: + Neighborhood::activateOneHotspot(entry, spot); + break; + } +} + +void FullTSA::activateHotspots() { + Neighborhood::activateHotspots(); + + switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) { + case MakeRoomView(kTSA02, kNorth): + if (!GameState.getTSAFrontDoorUnlockedOutside()) + _vm->getAllHotspots().activateOneHotspot(kTSA02DoorSpotID); + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (getCurrentActivation() != kActivateTSA0BComparisonVideo) { + _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareNoradSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareMarsSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareCaldoriaSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BEastCompareWSCSpotID); + } + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToCommandCenterSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToReadyRoomSpotID); + _vm->getAllHotspots().activateOneHotspot(kTSA0BNorthRobotsToFrontDoorSpotID); + break; + } + break; + } +} + +void FullTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kTSAGTOtherSpotID: + showExtraView(kTSAGTOtherChoice); + playSpotSoundSync(kTSANoOtherDestinationIn, kTSANoOtherDestinationOut); + showExtraView(kTSAGTCardSwipe); + break; + case kTSA02DoorSpotID: + GameState.setTSAFrontDoorUnlockedOutside(true); + Neighborhood::clickInHotspot(input, clickedSpot); + break; + case kTSA03EastJimenezSpotID: + startExtraLongSequence(kTSA03JimenezZoomIn, kTSA03JimenezZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA03WestCrenshawSpotID: + startExtraLongSequence(kTSA03CrenshawZoomIn, kTSA03CrenshawZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA04EastMatsumotoSpotID: + startExtraLongSequence(kTSA04MatsumotoZoomIn, kTSA04MatsumotoZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA04WestCastilleSpotID: + startExtraLongSequence(kTSA04CastilleZoomIn, kTSA04CastilleZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA05EastSinclairSpotID: + startExtraLongSequence(kTSA05SinclairZoomIn, kTSA05SinclairZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA05WestWhiteSpotID: + startExtraLongSequence(kTSA05WhiteZoomIn, kTSA05WhiteZoomOut, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA0BEastCompareNoradSpotID: + initializeComparisonMonitor(kMonitorNoradComparison, kTSA0BNoradComparisonView); + break; + case kTSA0BEastCompareMarsSpotID: + initializeComparisonMonitor(kMonitorMarsComparison, kTSA0BMarsComparisonView); + break; + case kTSA0BEastCompareCaldoriaSpotID: + initializeComparisonMonitor(kMonitorCaldoriaComparison, kTSA0BCaldoriaComparisonView); + break; + case kTSA0BEastCompareWSCSpotID: + initializeComparisonMonitor(kMonitorWSCComparison, kTSA0BWSCComparisonView); + break; + case kTSA0BEastCloseVideoSpotID: + _navMovie.stop(); + _sprite3.show(); + _vm->delayShell(1, 2); + _sprite3.hide(); + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + case kTSA0BEastLeftPlaySpotID: + playLeftComparison(); + break; + case kTSA0BEastRightPlaySpotID: + playRightComparison(); + break; + + // Command center + case kTSA0BWestTheorySpotID: + initializeTBPMonitor(kMonitorTheory, kTSA0BTBPTheoryHighlight); + break; + case kTSA0BWestBackgroundSpotID: + initializeTBPMonitor(kMonitorBackground, kTSA0BTBPBackgroundHighlight); + break; + case kTSA0BWestProcedureSpotID: + initializeTBPMonitor(kMonitorProcedure, kTSA0BTBPProcedureHighlight); + break; + case kTSA0BWestCloseVideoSpotID: + _navMovie.stop(); + _sprite2.show(); + _vm->delayShell(1, 2); + _sprite2.hide(); + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case kTSA0BWestPlayVideoSpotID: + playTBPMonitor(); + break; + case kTSA0BEastLeftRewindSpotID: + case kTSA0BEastRightRewindSpotID: + case kTSA0BWestRewindVideoSpotID: + if ((GameState.getT0BMonitorMode() & kPlayingAnyMask) != 0) { + bool playing = _navMovie.isRunning(); + if (playing) + _navMovie.stop(); + + if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID) + _sprite2.show(); + else + _sprite1.show(); + + _vm->delayShell(1, 2); + + if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID) + _sprite2.hide(); + else + _sprite1.hide(); + + _navMovie.setTime(GameState.getT0BMonitorStart()); + + if (playing) { + _navMovie.start(); + } else { + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false); + } + } + break; + case kTSA22EastMonitorSpotID: + requestExtraSequence(kTSA22RedEastZoomInSequence, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA23WestMonitorSpotID: + requestExtraSequence(kTSA23RedWestVaultZoomInSequence, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA0BNorthRobotsToCommandCenterSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionCCDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + // Nothing + break; + case kRobotsAtFrontDoor: + GameState.setTSAState(kRobotsAtCommandCenter); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromFrontDoorToCommandCenter, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtCommandCenter); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToCommandCenter, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kTSA0BNorthRobotsToReadyRoomSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionRRDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtReadyRoom); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToReadyRoom, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + GameState.setTSAState(kRobotsAtReadyRoom); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromFrontDoorToReadyRoom, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtReadyRoom: + // Nothing + break; + } + break; + case kTSA0BNorthRobotsToFrontDoorSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + // Nothing + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + + // Pegasus + case kTSA37NorthJumpToPrehistoricSpotID: + startExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthExitSpotID: + _sprite2.setCurrentFrameIndex(1); + _vm->delayShell(1, 2); + releaseSprites(); + moveForward(); + break; + case kTSA37NorthJumpMenuSpotID: + _sprite2.setCurrentFrameIndex(1); + _vm->delayShell(1, 2); + releaseSprites(); + break; + case kTSA37NorthJumpToNoradSpotID: + GameState.setTSAState(kPlayerOnWayToNorad); + requestExtraSequence(kTSA37JumpToNorad, 0, kFilterNoInput); + + if (!GameState.getBeenToNorad()) { + requestExtraSequence(kTSA37NoradToAI7, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI7, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI7ToNorad, 0, kFilterNoInput); + GameState.setBeenToNorad(true); + } + + requestExtraSequence(kTSA37NoradToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthJumpToMarsSpotID: + GameState.setTSAState(kPlayerOnWayToMars); + requestExtraSequence(kTSA37JumpToMars, 0, kFilterNoInput); + + if (!GameState.getBeenToMars()) { + requestExtraSequence(kTSA37MarsToAI6, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI6, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI6ToMars, 0, kFilterNoInput); + GameState.setBeenToMars(true); + } + + requestExtraSequence(kTSA37MarsToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA37NorthJumpToWSCSpotID: + GameState.setTSAState(kPlayerOnWayToWSC); + requestExtraSequence(kTSA37JumpToWSC, 0, kFilterNoInput); + + if (!GameState.getBeenToWSC()) { + requestExtraSequence(kTSA37WSCToAI5, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusAI5, 0, kFilterNoInput); + requestExtraSequence(kTSA37AI5ToWSC, 0, kFilterNoInput); + GameState.setBeenToWSC(true); + } + + requestExtraSequence(kTSA37WSCToDepart, 0, kFilterNoInput); + requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + break; + default: + Neighborhood::clickInHotspot(input, clickedSpot); + break; + } +} + +void FullTSA::showMainJumpMenu() { + ExtraID jumpMenuView = kTSA37JumpMenu000; + + if (GameState.getNoradFinished()) + jumpMenuView += 4; + if (GameState.getMarsFinished()) + jumpMenuView += 2; + if (GameState.getWSCFinished()) + jumpMenuView += 1; + + showExtraView(jumpMenuView); + setCurrentActivation(kActivationMainJumpMenu); +} + +void FullTSA::playTBPMonitor() { + InputDevice.waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingTBPMask) == 0) { + ExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorTheory: + GameState.setTSASeenTheory(true); + extra = kTSA0BTBPTheory; + GameState.setScoringSawTheory(true); + break; + case kMonitorBackground: + GameState.setTSASeenBackground(true); + extra = kTSA0BTBPBackground; + GameState.setScoringSawBackground(true); + break; + case kMonitorProcedure: + GameState.setTSASeenProcedure(true); + extra = kTSA0BTBPProcedure; + GameState.setScoringSawProcedure(true); + break; + default: + error("Invalid monitor mode"); + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingTBPMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, kExtraCompletedFlag, false, kFilterAllInput); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +void FullTSA::initializeTBPMonitor(const int newMode, const ExtraID highlightExtra) { + GameState.setT0BMonitorMode(newMode); + + if (newMode != kMonitorNeutral) { + showExtraView(highlightExtra); + _vm->delayShell(1, 2); + setCurrentActivation(kActivateTSA0BTBPVideo); + _sprite1.addPICTResourceFrame(kTBPRewindPICTID, false, 0, 0); + _sprite1.moveElementTo(kTBPRewindLeft, kTBPRewindTop); + _sprite1.setCurrentFrameIndex(0); + _sprite2.addPICTResourceFrame(kTBPCloseBoxPICTID, false, 0, 0); + _sprite2.moveElementTo(kTBPCloseLeft, kTBPCloseTop); + _sprite2.setCurrentFrameIndex(0); + playTBPMonitor(); + } else { + if (GameState.getTSAState() == kTSAPlayerForcedReview && GameState.getTSASeenTheory() && + GameState.getTSASeenBackground() && GameState.getTSASeenProcedure()) { + setOffRipAlarm(); + } else { + setCurrentActivation(kActivateTSA0BZoomedIn); + updateViewFrame(); + } + + releaseSprites(); + } + + _interruptionFilter = kFilterAllInput; +} + +void FullTSA::startUpComparisonMonitor() { + releaseSprites(); + + _sprite1.addPICTResourceFrame(kComparisonHiliteNoradPICTID, false, + kComparisonHiliteNoradLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteNoradTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteMarsPICTID, false, + kComparisonHiliteMarsLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteMarsTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteCaldoriaPICTID, false, + kComparisonHiliteCaldoriaLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteCaldoriaTop - kComparisonHiliteSpriteTop); + _sprite1.addPICTResourceFrame(kComparisonHiliteWSCPICTID, false, + kComparisonHiliteWSCLeft - kComparisonHiliteSpriteLeft, + kComparisonHiliteWSCTop - kComparisonHiliteSpriteTop); + + _sprite1.setCurrentFrameIndex(0); + _sprite1.moveElementTo(kComparisonHiliteSpriteLeft, kComparisonHiliteSpriteTop); + + _sprite2.addPICTResourceFrame(kComparisonChancesNoradPICTID, false, + kComparisonChancesNoradLeft - kComparisonChancesSpriteLeft, + kComparisonChancesNoradTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesMarsPICTID, false, + kComparisonChancesMarsLeft - kComparisonChancesSpriteLeft, + kComparisonChancesMarsTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesCaldoriaPICTID, false, + kComparisonChancesCaldoriaLeft - kComparisonChancesSpriteLeft, + kComparisonChancesCaldoriaTop - kComparisonChancesSpriteTop); + _sprite2.addPICTResourceFrame(kComparisonChancesWSCPICTID, false, + kComparisonChancesWSCLeft - kComparisonChancesSpriteLeft, + kComparisonChancesWSCTop - kComparisonChancesSpriteTop); + + _sprite2.setCurrentFrameIndex(0); + _sprite2.moveElementTo(kComparisonChancesSpriteLeft, kComparisonChancesSpriteTop); + updateViewFrame(); +} + +void FullTSA::shutDownComparisonMonitor() { + releaseSprites(); +} + +void FullTSA::initializeComparisonMonitor(const int newMode, const ExtraID comparisonView) { + GameState.setT0BMonitorMode(newMode); + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false); + + if (newMode != kMonitorNeutral) { + shutDownComparisonMonitor(); + setCurrentActivation(kActivateTSA0BComparisonVideo); + _sprite1.addPICTResourceFrame(kComparisonLeftRewindPICTID, false, 0, 0); + _sprite1.moveElementTo(kComparisonLeftRewindLeft, kComparisonLeftRewindTop); + _sprite1.setCurrentFrameIndex(0); + _sprite2.addPICTResourceFrame(kComparisonRightRewindPICTID, false, 0, 0); + _sprite2.moveElementTo(kComparisonRightRewindLeft, kComparisonRightRewindTop); + _sprite2.setCurrentFrameIndex(0); + _sprite3.addPICTResourceFrame(kComparisonCloseBoxPICTID, false, 0, 0); + _sprite3.moveElementTo(kComparisonCloseLeft, kComparisonCloseTop); + _sprite3.setCurrentFrameIndex(0); + showExtraView(comparisonView); + } else { + if (GameState.getTSAState() == kTSAPlayerInstalledHistoricalLog && + GameState.getTSASeenNoradNormal() && + GameState.getTSASeenNoradAltered() && + GameState.getTSASeenMarsNormal() && + GameState.getTSASeenMarsAltered() && + GameState.getTSASeenCaldoriaNormal() && + GameState.getTSASeenCaldoriaAltered() && + GameState.getTSASeenWSCNormal() && + GameState.getTSASeenWSCAltered()) { + GameState.setTSAState(kTSABossSawHistoricalLog); + requestExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BEastTurnLeft, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + } else { + setCurrentActivation(kActivateTSA0BZoomedIn); + releaseSprites(); + startUpComparisonMonitor(); + } + } + + _interruptionFilter = kFilterAllInput; +} + +void FullTSA::playLeftComparison() { + InputDevice.waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingLeftComparisonMask) == 0) { + ExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorNoradComparison: + GameState.setTSASeenNoradAltered(true); + extra = kTSA0BNoradAltered; + GameState.setScoringSawNoradAltered(true); + break; + case kMonitorMarsComparison: + GameState.setTSASeenMarsAltered(true); + extra = kTSA0BMarsAltered; + GameState.setScoringSawMarsAltered(true); + break; + case kMonitorCaldoriaComparison: + GameState.setTSASeenCaldoriaAltered(true); + extra = kTSA0BCaldoriaAltered; + GameState.setScoringSawCaldoriaAltered(true); + break; + case kMonitorWSCComparison: + GameState.setTSASeenWSCAltered(true); + extra = kTSA0BWSCAltered; + GameState.setScoringSawWSCAltered(true); + break; + default: + error("Invalid monitor mode"); + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingLeftComparisonMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + // skip first five frames of movie + // (this is a dissolve that doesn't belong...) + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + _privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag); + + // Allow clicking... + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, + kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter()); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +void FullTSA::playRightComparison() { + InputDevice.waitInput(kFilterAllButtons); + + if ((GameState.getT0BMonitorMode() & kPlayingRightComparisonMask) == 0) { + ExtraID extra; + + switch (GameState.getT0BMonitorMode() & kRawModeMask) { + case kMonitorNoradComparison: + GameState.setTSASeenNoradNormal(true); + extra = kTSA0BNoradUnaltered; + GameState.setScoringSawNoradNormal(true); + break; + case kMonitorMarsComparison: + GameState.setTSASeenMarsNormal(true); + extra = kTSA0BMarsUnaltered; + GameState.setScoringSawMarsNormal(true); + break; + case kMonitorCaldoriaComparison: + GameState.setTSASeenCaldoriaNormal(true); + extra = kTSA0BCaldoriaUnaltered; + GameState.setScoringSawCaldoriaNormal(true); + break; + case kMonitorWSCComparison: + GameState.setTSASeenWSCNormal(true); + extra = kTSA0BWSCUnaltered; + GameState.setScoringSawWSCNormal(true); + break; + default: + error("Invalid monitor mode"); + } + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingRightComparisonMask); + + ExtraTable::Entry entry; + getExtraEntry(extra, entry); + _lastExtra = extra; + + // skip first five frames of movie + // (this is a dissolve that doesn't belong...) + GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); + _privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag); + + // Allow clicking... + startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, + kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter()); + } else if (_navMovie.isRunning()) { + _navMovie.stop(); + } else { + _navMovie.start(); + } +} + +// When this function is called, the player is zoomed up on the center monitor, and the +// TSA state is kTSABossSawHistoricalLog. +void FullTSA::startRobotGame() { + requestExtraSequence(kTSA0BNorthCantChangeHistory, 0, kFilterNoInput); + requestExtraSequence(kTSA0BAIInterruption, 0, kFilterNoInput); + requestExtraSequence(kTSA0BShowGuardRobots, 0, kFilterNoInput); + requestExtraSequence(kTSA0BRobotsToCommandCenter, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::startUpRobotMonitor() { + releaseSprites(); + + _sprite1.addPICTResourceFrame(kRedirectionCCRolloverPICTID, true, + kRedirectionCCRolloverLeft - kRedirectionSprite1Left, + kRedirectionCCRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionRRRolloverPICTID, true, + kRedirectionRRRolloverLeft - kRedirectionSprite1Left, + kRedirectionRRRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionFDRolloverPICTID, false, + kRedirectionFDRolloverLeft - kRedirectionSprite1Left, + kRedirectionFDRolloverTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionCCDoorPICTID, true, + kRedirectionCCDoorLeft - kRedirectionSprite1Left, + kRedirectionCCDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionRRDoorPICTID, true, + kRedirectionRRDoorLeft - kRedirectionSprite1Left, + kRedirectionRRDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionFDDoorPICTID, false, + kRedirectionFDDoorLeft - kRedirectionSprite1Left, + kRedirectionFDDoorTop - kRedirectionSprite1Top); + _sprite1.addPICTResourceFrame(kRedirectionClosePICTID, false, + kRedirectionCloseLeft - kRedirectionSprite1Left, + kRedirectionCloseTop - kRedirectionSprite1Top); + _sprite1.moveElementTo(kRedirectionSprite1Left, kRedirectionSprite1Top); + + _sprite2.addPICTResourceFrame(kRedirectionSecuredPICTID, false, + kRedirectionSecuredLeft - kRedirectionSprite2Left, + kRedirectionSecuredTop - kRedirectionSprite2Top); + _sprite2.addPICTResourceFrame(kRedirectionNewTargetPICTID, false, + kRedirectionNewTargetLeft - kRedirectionSprite2Left, + kRedirectionNewTargetTop - kRedirectionSprite2Top); + _sprite2.moveElementTo(kRedirectionSprite2Left, kRedirectionSprite2Top); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + showExtraView(kTSA0BNorthRobotsAtCCView); + break; + case kRobotsAtFrontDoor: + showExtraView(kTSA0BNorthRobotsAtFDView); + break; + case kRobotsAtReadyRoom: + showExtraView(kTSA0BNorthRobotsAtRRView); + break; + } +} + +void FullTSA::shutDownRobotMonitor() { + releaseSprites(); +} + +// Assume this is called only when zoomed in at T0B west +void FullTSA::setOffRipAlarm() { + GameState.setTSAState(kTSAPlayerDetectedRip); + _ripTimer.initImage(); + _ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop); + _ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale); + _ripTimer.start(); + loadAmbientLoops(); + startExtraSequenceSync(kTSA0BRipAlarmScreen, kFilterNoInput); + _vm->delayShell(2, 1); // Two seconds.. + requestExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BWestTurnRight, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthFinallyHappened, 0, kFilterNoInput); + requestExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSA04, kNorth): + case MakeRoomView(kTSA14, kEast): + case MakeRoomView(kTSA15, kWest): + case MakeRoomView(kTSA16, kNorth): + case MakeRoomView(kTSA16, kSouth): + case MakeRoomView(kTSA21Cyan, kSouth): + case MakeRoomView(kTSA21Red, kSouth): + case MakeRoomView(kTSA26, kNorth): + makeContinuePoint(); + break; + } +} + +void FullTSA::arriveAt(const RoomID room, const DirectionConstant direction) { + checkRobotLocations(room, direction); + Neighborhood::arriveAt(room, direction); + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kTSADeathRoom, kNorth): + case MakeRoomView(kTSADeathRoom, kSouth): + case MakeRoomView(kTSADeathRoom, kEast): + case MakeRoomView(kTSADeathRoom, kWest): + die(kDeathShotByTSARobots); + break; + case MakeRoomView(kTSA00, kNorth): + if (GameState.getLastNeighborhood() != kFullTSAID) { + makeContinuePoint(); + openDoor(); + } else { + setCurrentActivation(kActivateTSAReadyForCard); + loopExtraSequence(kTSATransporterArrowLoop, 0); + } + break; + case MakeRoomView(kTSA03, kNorth): + case MakeRoomView(kTSA05, kNorth): + case MakeRoomView(kTSA0A, kNorth): + case MakeRoomView(kTSA06, kNorth): + case MakeRoomView(kTSA07, kNorth): + if (_utilityFuse.isFuseLit()) + _utilityFuse.stopFuse(); + GameState.setScoringEnterTSA(true); + break; + case MakeRoomView(kTSA04, kNorth): + if (_utilityFuse.isFuseLit()) + _utilityFuse.stopFuse(); + if (!GameState.getTSASeenRobotGreeting()) + startExtraSequence(kTSA04NorthRobotGreeting, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kTSA03, kSouth): + GameState.setTSAFrontDoorUnlockedInside(GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished()); + break; + case MakeRoomView(kTSA0A, kEast): + case MakeRoomView(kTSA0A, kWest): + if (GameState.getTSAState() == kTSAPlayerNotArrived) + setCurrentActivation(kActivateTSARobotsAwake); + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + _ripTimer.show(); + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startUpRobotMonitor(); + break; + } + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + + switch (GameState.getTSAState()) { + case kTSAPlayerNotArrived: + requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthYoureBusted, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BNorthTurnLeft, 0, kFilterNoInput); + requestExtraSequence(kTSA0BWestZoomIn, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSAPlayerGotHistoricalLog: + startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput); + break; + } + } + break; + case MakeRoomView(kTSA0B, kSouth): + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + initializeTBPMonitor(kMonitorNeutral, 0); + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + } + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) { + setCurrentActivation(kActivateTSA0BZoomedIn); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + } + } else { + setCurrentActivation(kActivateTSA0BZoomedOut); + } + break; + case MakeRoomView(kTSA21Red, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor) + GameState.setScoringWentToReadyRoom2(true); + break; + case MakeRoomView(kTSA22Red, kEast): + if (!_vm->playerHasItemID(kJourneymanKey)) + setCurrentActivation(kActivationDoesntHaveKey); + break; + case MakeRoomView(kTSA23Red, kWest): + if (!_vm->playerHasItemID(kPegasusBiochip)) + setCurrentActivation(kActivationDoesntHaveChips); + break; + case MakeRoomView(kTSA25Red, kNorth): + arriveAtTSA25Red(); + break; + case MakeRoomView(kTSA34, kSouth): + if (GameState.getLastRoom() == kTSA37) + closeDoorOffScreen(kTSA37, kNorth); + break; + case MakeRoomView(kTSA37, kNorth): + arriveAtTSA37(); + break; + } +} + +void FullTSA::checkRobotLocations(const RoomID room, const DirectionConstant dir) { + switch (room) { + case kTSA03: + case kTSA04: + case kTSA05: + case kTSA06: + case kTSA0A: + case kTSA07: + case kTSA08: + case kTSA09: + case kTSA10: + case kTSA11: + case kTSA12: + case kTSA13: + case kTSA14: + case kTSA15: + switch (GameState.getTSAState()) { + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + break; + case kTSA16: + if (dir == kNorth) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) { + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption); + _privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true); + } + break; + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + } + break; + } +} + +void FullTSA::arriveAtTSA25Red() { + if (!_vm->playerHasItemID(kJourneymanKey)) + startExtraSequence(kTSA25NorthDeniedNoKey, kExtraCompletedFlag, kFilterNoInput); + else if (!_vm->playerHasItemID(kPegasusBiochip)) + startExtraSequence(kTSA25NorthDeniedNoChip, kExtraCompletedFlag, kFilterNoInput); + else if (GameState.getTSABiosuitOn()) + startExtraSequence(kTSA25NorthAlreadyHaveSuit, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kTSA25NorthPutOnSuit, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::arriveAtTSA37() { + _ripTimer.stop(); + _ripTimer.releaseImage(); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + startExtraLongSequence(kTSA37HorseToAI1, kTSA37AI2ToPrehistoric, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerOnWayToPrehistoric: + setCurrentActivation(kActivationJumpToPrehistoric); + showExtraView(kTSA37AI2ToPrehistoric); + break; + case kTSAPlayerGotHistoricalLog: + initializePegasusButtons(false); + break; + case kPlayerWentToPrehistoric: + case kPlayerOnWayToNorad: + case kPlayerOnWayToMars: + case kPlayerOnWayToWSC: + startExtraSequence(kTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + startExtraLongSequence(kTSA37HorseToColonel2, kTSA37AI4ToMainMenu, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerLockedInPegasus: + showMainJumpMenu(); + break; + case kPlayerFinishedWithTSA: + initializePegasusButtons(true); + break; + } +} + +void FullTSA::turnTo(const DirectionConstant newDirection) { + Neighborhood::turnTo(newDirection); + + switch (MakeRoomView(GameState.getCurrentRoom(), newDirection)) { + case MakeRoomView(kTSA03, kSouth): + if (GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished()) + GameState.setTSAFrontDoorUnlockedInside(true); + else + GameState.setTSAFrontDoorUnlockedInside(false); + break; + case MakeRoomView(kTSA0A, kEast): + case MakeRoomView(kTSA0A, kWest): + setCurrentActivation(kActivateTSARobotsAwake); + break; + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getTSA0BZoomedIn()) + startUpComparisonMonitor(); + break; + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + if (GameState.getTSA0BZoomedIn()) + _ripTimer.show(); + break; + case kTSAPlayerGotHistoricalLog: + if (!GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSAPlayerInstalledHistoricalLog: + if (GameState.getTSA0BZoomedIn()) { + if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) && + (GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) && + (GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) && + (GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) { + GameState.setTSAState(kTSABossSawHistoricalLog); + startRobotGame(); + } + } + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (GameState.getTSA0BZoomedIn()) + startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case MakeRoomView(kTSA0B, kWest): + if (GameState.getTSA0BZoomedIn()) + setCurrentActivation(kActivateTSA0BZoomedIn); + else + setCurrentActivation(kActivateTSA0BZoomedOut); + + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + + if (GameState.getTSA0BZoomedIn()) + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case MakeRoomView(kTSA0B, kSouth): + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case MakeRoomView(kTSA16, kNorth): + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) { + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption); + _privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true); + } + break; + case kRobotsAtFrontDoor: + setCurrentAlternate(kAltTSARobotsAtFrontDoor); + break; + case kRobotsAtReadyRoom: + setCurrentAlternate(kAltTSARobotsAtReadyRoom); + break; + } + break; + case MakeRoomView(kTSA22Red, kEast): + if (!_vm->playerHasItemID(kJourneymanKey)) + setCurrentActivation(kActivationDoesntHaveKey); + break; + case MakeRoomView(kTSA22Red, kNorth): + case MakeRoomView(kTSA22Red, kSouth): + if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) { + playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut); + _privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, false); + } + + setCurrentActivation(kActivateHotSpotAlways); + break; + case MakeRoomView(kTSA23Red, kWest): + if (!_vm->playerHasItemID(kPegasusBiochip)) + setCurrentActivation(kActivationDoesntHaveChips); + break; + case MakeRoomView(kTSA23Red, kNorth): + case MakeRoomView(kTSA23Red, kSouth): + if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) { + playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut); + _privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, false); + } + + setCurrentActivation(kActivateHotSpotAlways); + break; + } + + // Make sure the TBP monitor is forced neutral. + GameState.setT0BMonitorMode(kMonitorNeutral); +} + +void FullTSA::closeDoorOffScreen(const RoomID room, const DirectionConstant) { + switch (room) { + case kTSA00: + case kTSA01: + if (GameState.getCurrentRoom() == kTSA01 || GameState.getCurrentRoom() == kTSA02) + playSpotSoundSync(kTSAGTDoorCloseIn, kTSAGTDoorCloseOut); + break; + case kTSA02: + case kTSA03: + playSpotSoundSync(kTSAEntryDoorCloseIn, kTSAEntryDoorCloseOut); + break; + case kTSA14: + case kTSA15: + case kTSA16: + case kTSA21Cyan: + case kTSA21Red: + playSpotSoundSync(kTSAInsideDoorCloseIn, kTSAInsideDoorCloseOut); + break; + case kTSA34: + case kTSA37: + playSpotSoundSync(kTSAPegasusDoorCloseIn, kTSAPegasusDoorCloseOut); + break; + } +} + +void FullTSA::receiveNotification(Notification *notification, const NotificationFlags flags) { + ExtraID lastExtra = _lastExtra; + + if ((flags & kExtraCompletedFlag) != 0) { + switch (lastExtra) { + case kTSA0BEastTurnLeft: + // Need to check this here because turnTo will call _navMovie.stop, + // so it has to happen before Neighborhood::receiveNotification, + // which may end up starting another sequence... + turnTo(kNorth); + break; + } + } + + Neighborhood::receiveNotification(notification, flags); + + InventoryItem *item; + + if ((flags & kExtraCompletedFlag) != 0) { + // Only allow input if we're not in the middle of series of queue requests. + if (actionQueueEmpty()) + _interruptionFilter = kFilterAllInput; + + switch (lastExtra) { + case kTSAGTCardSwipe: + item = (InventoryItem *)_vm->getAllItems().findItemByID(kKeyCard); + _vm->addItemToInventory(item); + setCurrentActivation(kActivateTSAReadyToTransport); + break; + case kTSAGTGoToCaldoria: + _vm->jumpToNewEnvironment(kCaldoriaID, kCaldoria44, kEast); + + if (GameState.allTimeZonesFinished()) + GameState.setScoringWentAfterSinclair(true); + break; + case kTSAGTGoToTokyo: + case kTSAGTGoToBeach: + if (GameState.allTimeZonesFinished()) + die(kDeathSinclairShotDelegate); + else + die(kDeathUncreatedInTSA); + break; + case kTSA02NorthZoomOut: + openDoor(); + break; + + // Hall of suspects. + case kTSA04NorthRobotGreeting: + GameState.setTSASeenRobotGreeting(true); + restoreStriding(kTSA03, kNorth, kNoAlternateID); + break; + case kTSA03JimenezZoomIn: + GameState.setScoringSawBust1(true); + break; + case kTSA03CrenshawZoomIn: + GameState.setScoringSawBust2(true); + break; + case kTSA04MatsumotoZoomIn: + GameState.setScoringSawBust3(true); + break; + case kTSA04CastilleZoomIn: + GameState.setScoringSawBust4(true); + break; + case kTSA05SinclairZoomIn: + GameState.setScoringSawBust5(true); + break; + case kTSA05WhiteZoomIn: + GameState.setScoringSawBust6(true); + break; + + // Command center + // Historical comparison... + case kTSA0BEastZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startUpComparisonMonitor(); + break; + } + break; + case kTSA0BEastZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + + switch (GameState.getTSAState()) { + case kTSABossSawHistoricalLog: + // Prevent current view from activating. + break; + default: + activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(), + kSpotOnTurnMask); + break; + } + break; + case kTSA0BComparisonStartup: + if ((flags & kActionRequestCompletedFlag) != 0) { + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + GameState.setTSAState(kTSAPlayerInstalledHistoricalLog); + turnTo(kEast); + } + + startUpComparisonMonitor(); + break; + case kTSA0BNoradAltered: + case kTSA0BMarsAltered: + case kTSA0BCaldoriaAltered: + case kTSA0BWSCAltered: + case kTSA0BNoradUnaltered: + case kTSA0BMarsUnaltered: + case kTSA0BCaldoriaUnaltered: + case kTSA0BWSCUnaltered: + initializeComparisonMonitor(kMonitorNeutral, 0); + break; + + // Center monitor. + case kTSA0BNorthZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + + switch (GameState.getTSAState()) { + case kTSAPlayerNeedsHistoricalLog: + startExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSABossSawHistoricalLog: + case kTSAPlayerInstalledHistoricalLog: + if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) && + (GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) && + (GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) && + (GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) { + GameState.setTSAState(kTSABossSawHistoricalLog); + startRobotGame(); + } + break; + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput); + break; + } + break; + case kTSA0BNorthZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + break; + case kTSA0BShowRip1: + GameState.setTSAState(kTSAPlayerNeedsHistoricalLog); + GameState.setTSACommandCenterLocked(false); + + if ((flags & kActionRequestCompletedFlag) != 0) + turnTo(kNorth); + + _ripTimer.show(); + break; + case kTSA0BNorthHistLogOpen: + setCurrentActivation(kActivationLogReaderOpen); + _privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, true); + break; + case kTSA0BRobotsToCommandCenter: + GameState.setTSAState(kRobotsAtCommandCenter); + // Fall through + case kTSA0BShowGuardRobots: + startUpRobotMonitor(); + // Fall through + case kTSA0BRobotsFromCommandCenterToReadyRoom: + case kTSA0BRobotsFromReadyRoomToCommandCenter: + case kTSA0BRobotsFromCommandCenterToFrontDoor: + case kTSA0BRobotsFromFrontDoorToCommandCenter: + case kTSA0BRobotsFromFrontDoorToReadyRoom: + case kTSA0BRobotsFromReadyRoomToFrontDoor: + _sprite2.setCurrentFrameIndex(kRedirectionSecuredSprite); + _sprite2.show(); + break; + + // TBP monitor. + case kTSA0BWestZoomIn: + GameState.setTSA0BZoomedIn(true); + setCurrentActivation(kActivateTSA0BZoomedIn); + + if (GameState.getTSAState() == kTSAPlayerNotArrived) { + turnTo(kWest); + GameState.setTSACommandCenterLocked(true); + GameState.setTSAState(kTSAPlayerForcedReview); + } + + initializeTBPMonitor(kMonitorNeutral, 0); + break; + case kTSA0BWestZoomOut: + GameState.setTSA0BZoomedIn(false); + setCurrentActivation(kActivateTSA0BZoomedOut); + GameState.setT0BMonitorMode(kMonitorNeutral); + + switch (GameState.getTSAState()) { + case kTSAPlayerDetectedRip: + // Keep the current view from activating. + break; + default: + activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(), + kSpotOnTurnMask); + break; + } + break; + case kTSA0BTBPTheory: + case kTSA0BTBPBackground: + case kTSA0BTBPProcedure: + initializeTBPMonitor(kMonitorNeutral, 0); + break; + + // Ready room + case kTSA22RedEastZoomInSequence: + _privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, true); + setCurrentActivation(kActivationKeyVaultOpen); + break; + case kTSA23RedWestVaultZoomInSequence: + _privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, true); + setCurrentActivation(kActivationChipVaultOpen); + break; + case kTSA25NorthPutOnSuit: + GameState.setTSABiosuitOn(true); + GameState.setScoringGotBiosuit(true); + // Fall through... + case kTSA25NorthAlreadyHaveSuit: + requestExtraSequence(kTSA25NorthDescending1, 0, kFilterNoInput); + requestExtraSequence(kTSA25NorthDescending2, kExtraCompletedFlag, kFilterNoInput); + break; + case kTSA25NorthDescending2: + arriveAt(kTSA26, kNorth); + break; + + // Pegasus. + case kTSA37HorseToAI1: + case kTSA37AI2ToPrehistoric: + setCurrentActivation(kActivationJumpToPrehistoric); + GameState.setTSAState(kPlayerOnWayToPrehistoric); + break; + case kTSA37PegasusDepart: + _vm->setLastEnergyValue(kFullEnergy); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToPrehistoric: + _vm->jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth); + GameState.setPrehistoricSeenTimeStream(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + GameState.setScoringGoToPrehistoric(true); + GameState.setTSAState(kPlayerWentToPrehistoric); + break; + case kPlayerOnWayToNorad: + _vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth); + GameState.setNoradSeenTimeStream(false); + GameState.setNoradGassed(true); + GameState.setNoradFillingStationOn(false); + GameState.setNoradN22MessagePlayed(false); + GameState.setNoradPlayedGlobeGame(false); + GameState.setNoradBeatRobotWithClaw(false); + GameState.setNoradBeatRobotWithDoor(false); + GameState.setNoradRetScanGood(false); + GameState.setNoradWaitingForLaser(false); + GameState.setNoradSubRoomPressure(9); + GameState.setNoradSubPrepState(kSubNotPrepped); + break; + case kPlayerOnWayToMars: + _vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth); + GameState.setMarsSeenTimeStream(false); + GameState.setMarsHeardUpperPodMessage(false); + GameState.setMarsRobotThrownPlayer(false); + GameState.setMarsHeardCheckInMessage(false); + GameState.setMarsPodAtUpperPlatform(false); + GameState.setMarsSeenThermalScan(false); + GameState.setMarsArrivedBelow(false); + GameState.setMarsSeenRobotAtReactor(false); + GameState.setMarsAvoidedReactorRobot(false); + GameState.setMarsLockFrozen(false); + GameState.setMarsLockBroken(false); + GameState.setMarsSecurityDown(false); + GameState.setMarsAirlockOpen(false); + GameState.setMarsReadyForShuttleTransport(false); + GameState.setMarsFinishedCanyonChase(false); + GameState.setMarsThreadedMaze(false); + break; + case kPlayerOnWayToWSC: + _vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest); + GameState.setWSCSeenTimeStream(false); + GameState.setWSCPoisoned(false); + GameState.setWSCAnsweredAboutDart(false); + GameState.setWSCRemovedDart(false); + GameState.setWSCAnalyzerOn(false); + GameState.setWSCDartInAnalyzer(false); + GameState.setWSCAnalyzedDart(false); + GameState.setWSCPickedUpAntidote(false); + GameState.setWSCSawMorph(false); + GameState.setWSCDesignedAntidote(false); + GameState.setWSCOfficeMessagesOpen(false); + GameState.setWSCSeenNerd(false); + GameState.setWSCHeardPage1(false); + GameState.setWSCHeardPage2(false); + GameState.setWSCHeardCheckIn(false); + GameState.setWSCDidPlasmaDodge(false); + GameState.setWSCSeenSinclairLecture(false); + GameState.setWSCBeenAtWSC93(false); + GameState.setWSCCatwalkDark(false); + GameState.setWSCRobotDead(false); + GameState.setWSCRobotGone(false); + break; + }; + break; + case kTSA37TimeJumpToPegasus: + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + switch (GameState.getTSAState()) { + case kPlayerWentToPrehistoric: + arriveFromPrehistoric(); + break; + case kPlayerOnWayToNorad: + arriveFromNorad(); + break; + case kPlayerOnWayToMars: + arriveFromMars(); + break; + case kPlayerOnWayToWSC: + arriveFromWSC(); + break; + default: + break; + } + break; + case kTSA37DownloadToOpMemReview: + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + g_opticalChip->playOpMemMovie(kPoseidonSpotID); + break; + case kPlayerOnWayToMars: + g_opticalChip->playOpMemMovie(kAriesSpotID); + break; + case kPlayerOnWayToWSC: + g_opticalChip->playOpMemMovie(kMercurySpotID); + break; + } + + if (GameState.allTimeZonesFinished()) { + requestExtraSequence(kTSA37OpMemReviewToAllClear, 0, kFilterNoInput); + requestExtraSequence(kTSA37AllClearToCongratulations, 0, kFilterNoInput); + requestExtraSequence(kTSA37Congratulations, 0, kFilterNoInput); + requestExtraSequence(kTSA37CongratulationsToExit, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kTSA37RecallToDownload: + case kTSA37ReviewRequiredToExit: + GameState.setTSAState(kTSAPlayerGotHistoricalLog); + initializePegasusButtons(kPegasusUnresolved); + break; + case kTSA37ZoomToMainMenu: + case kTSA37HorseToColonel2: + case kTSA37DownloadToMainMenu: + case kTSA37OpMemReviewToMainMenu: + case kTSA37AI4ToMainMenu: + GameState.setTSAState(kPlayerLockedInPegasus); + showMainJumpMenu(); + makeContinuePoint(); + break; + case kTSA37JumpToNoradMenu: + setCurrentActivation(kActivationJumpToNorad); + break; + case kTSA37JumpToMarsMenu: + setCurrentActivation(kActivationJumpToMars); + break; + case kTSA37JumpToWSCMenu: + setCurrentActivation(kActivationJumpToWSC); + break; + case kTSA37CancelNorad: + case kTSA37CancelMars: + case kTSA37CancelWSC: + showMainJumpMenu(); + break; + case kTSA37CongratulationsToExit: + GameState.setTSAState(kPlayerFinishedWithTSA); + initializePegasusButtons(true); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void FullTSA::arriveFromPrehistoric() { + if (_vm->playerHasItemID(kHistoricalLog)) { + GameState.setScoringFinishedPrehistoric(); + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + requestExtraSequence(kTSA37DownloadToColonel1, 0, kFilterNoInput); + requestExtraSequence(kTSA37Colonel1, 0, kFilterNoInput); + requestExtraSequence(kTSA37Colonel1ToReviewRequired, 0, kFilterNoInput); + requestExtraSequence(kTSA37ReviewRequiredToExit, kExtraCompletedFlag, kFilterNoInput); + } else { + // Make sure rip timer is going... + startExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromNorad() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) { + GameState.setScoringFinishedNorad(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromMars() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) { + GameState.setScoringFinishedMars(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::arriveFromWSC() { + requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) { + GameState.setScoringFinishedWSC(); + requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void FullTSA::initializePegasusButtons(bool resolved) { + if (resolved) { + _sprite1.addPICTResourceFrame(kResolvedPICTID, false, 0, 0); + _sprite1.moveElementTo(kResolvedLeft, kResolvedTop); + } else { + _sprite1.addPICTResourceFrame(kUnresolvedPICTID, false, 0, 0); + _sprite1.moveElementTo(kUnresolvedLeft, kUnresolvedTop); + } + + _sprite1.setCurrentFrameIndex(0); + _sprite1.show(); + + _sprite2.addPICTResourceFrame(kExitPICTID, false, kExitLeft - kExitHilitedLeft, kExitTop - kExitHilitedTop); + _sprite2.addPICTResourceFrame(kExitHilitedPICTID, false, 0, 0); + _sprite2.moveElementTo(kExitHilitedLeft, kExitHilitedTop); + setCurrentActivation(kActivationReadyToExit); + _sprite2.setCurrentFrameIndex(0); + _sprite2.show(); +} + +Hotspot *FullTSA::getItemScreenSpot(Item *item, DisplayElement *element) { + switch (item->getObjectID()) { + case kJourneymanKey: + return _vm->getAllHotspots().findHotspotByID(kTSA22EastKeySpotID); + break; + case kPegasusBiochip: + return _vm->getAllHotspots().findHotspotByID(kTSA23WestChipsSpotID); + break; + } + + return Neighborhood::getItemScreenSpot(item, element); +} + +void FullTSA::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + Neighborhood::dropItemIntoRoom(item, dropSpot); + + switch (item->getObjectID()) { + case kKeyCard: + if (dropSpot->getObjectID() == kTSAGTCardDropSpotID) + startExtraSequence(kTSAGTCardSwipe, kExtraCompletedFlag, kFilterNoInput); + break; + case kHistoricalLog: + if (dropSpot->getObjectID() == kTSA0BNorthHistLogSpotID) { + requestExtraSequence(kTSA0BNorthHistLogCloseWithLog, 0, kFilterNoInput); + requestExtraSequence(kTSA0BNorthTurnRight, 0, kFilterNoInput); + requestExtraSequence(kTSA0BEastZoomIn, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kTSA0BComparisonStartup, kExtraCompletedFlag, kFilterNoInput); + GameState.setScoringPutLogInReader(true); + } + break; + } +} + +uint FullTSA::getHistoricalLogIndex() { + uint index; + + if (GameState.getTSASeenNoradNormal() && GameState.getTSASeenNoradAltered()) + index = 8; + else + index = 0; + + if (GameState.getTSASeenMarsNormal() && GameState.getTSASeenMarsAltered()) + index += 4; + + if (GameState.getTSASeenCaldoriaNormal() && GameState.getTSASeenCaldoriaAltered()) + index += 2; + + if (GameState.getTSASeenWSCNormal() && GameState.getTSASeenWSCAltered()) + index += 1; + + return index; +} + +void FullTSA::handleInput(const Input &input, const Hotspot *cursorSpot) { + switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) { + case MakeRoomView(kTSA0B, kEast): + if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning() && GameState.getT0BMonitorMode() == kMonitorNeutral) { + switch (GameState.getTSAState()) { + case kTSAPlayerInstalledHistoricalLog: + case kTSABossSawHistoricalLog: + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kTSA0BEastCompareNoradSpotID: + _sprite1.setCurrentFrameIndex(0); + _sprite2.setCurrentFrameIndex(0); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareMarsSpotID: + _sprite1.setCurrentFrameIndex(1); + _sprite2.setCurrentFrameIndex(1); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareCaldoriaSpotID: + _sprite1.setCurrentFrameIndex(2); + _sprite2.setCurrentFrameIndex(2); + _sprite1.show(); + _sprite2.show(); + break; + case kTSA0BEastCompareWSCSpotID: + _sprite1.setCurrentFrameIndex(3); + _sprite2.setCurrentFrameIndex(3); + _sprite1.show(); + _sprite2.show(); + break; + default: + _sprite1.hide(); + _sprite2.hide(); + break; + } + } else { + _sprite1.hide(); + _sprite2.hide(); + } + break; + } + } + break; + case MakeRoomView(kTSA0B, kNorth): + if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning()) { + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + case kRobotsAtFrontDoor: + case kRobotsAtReadyRoom: + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kTSA0BNorthRobotsToCommandCenterSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionCCRolloverSprite); + _sprite1.show(); + break; + case kTSA0BNorthRobotsToReadyRoomSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionRRRolloverSprite); + _sprite1.show(); + break; + case kTSA0BNorthRobotsToFrontDoorSpotID: + _sprite1.setCurrentFrameIndex(kRedirectionFDRolloverSprite); + _sprite1.show(); + break; + default: + _sprite1.hide(); + break; + } + } else { + _sprite1.hide(); + } + break; + } + } + break; + } + + Neighborhood::handleInput(input, cursorSpot); +} + +void FullTSA::releaseSprites() { + _sprite1.hide(); + _sprite2.hide(); + _sprite3.hide(); + _sprite1.discardFrames(); + _sprite2.discardFrames(); + _sprite3.discardFrames(); +} + +bool FullTSA::canSolve() { + return GameState.getCurrentRoomAndView() == MakeRoomView(kTSA0B, kNorth) && + GameState.getTSA0BZoomedIn() && + (GameState.getTSAState() == kRobotsAtCommandCenter || + GameState.getTSAState() == kRobotsAtFrontDoor || + GameState.getTSAState() == kRobotsAtReadyRoom); +} + +void FullTSA::doSolve() { + // REROUTING ROBOTS + + _sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite); + _sprite1.show(); + _vm->delayShell(1, 2); + _sprite1.hide(); + + switch (GameState.getTSAState()) { + case kRobotsAtCommandCenter: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + case kRobotsAtFrontDoor: + // Nothing + break; + case kRobotsAtReadyRoom: + GameState.setTSAState(kRobotsAtFrontDoor); + _sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); + startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput); + break; + } +} + +void FullTSA::updateCursor(const Common::Point where, const Hotspot *cursorSpot) { + if (cursorSpot) { + switch (cursorSpot->getObjectID()) { + case kTSA0BEastMonitorSpotID: + case kTSA0BNorthMonitorSpotID: + case kTSA0BWestMonitorSpotID: + case kTSA22EastMonitorSpotID: + case kTSA23WestMonitorSpotID: + _vm->_cursor->setCurrentFrameIndex(1); + return; + case kTSA0BEastMonitorOutSpotID: + case kTSA0BNorthMonitorOutSpotID: + case kTSA0BWestMonitorOutSpotID: + _vm->_cursor->setCurrentFrameIndex(2); + return; + } + } + + Neighborhood::updateCursor(where, cursorSpot); +} + +Common::String FullTSA::getNavMovieName() { + return "Images/TSA/Full TSA.movie"; +} + +Common::String FullTSA::getSoundSpotsName() { + return "Sounds/TSA/TSA Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.h b/engines/pegasus/neighborhood/tsa/fulltsa.h new file mode 100644 index 0000000000..a646d57e6c --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/fulltsa.h @@ -0,0 +1,159 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H +#define PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +class RipTimer : public IdlerAnimation { +public: + RipTimer(const DisplayElementID id) : IdlerAnimation(id) {} + virtual ~RipTimer() {} + + void initImage(); + void releaseImage(); + + void draw(const Common::Rect &); + +protected: + void timeChanged(const TimeValue); + + CoordType _middle; + Surface _timerImage; +}; + +// Room IDs. + +static const RoomID kTSA00 = 0; +static const RoomID kTSA22Red = 28; +static const RoomID kTSA37 = 42; + +class FullTSA : public Neighborhood { +public: + FullTSA(InputHandler *, PegasusEngine *); + virtual ~FullTSA() {} + + virtual void init(); + + void start(); + + virtual uint16 getDateResID() const; + + void flushGameState(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool canSolve(); + void doSolve(); + + void updateCursor(const Common::Point, const Hotspot *); + +protected: + enum { + kTSAPrivateLogReaderOpenFlag, + kTSAPrivateKeyVaultOpenFlag, + kTSAPrivateChipVaultOpenFlag, + kTSAPrivatePlayingLeftComparisonFlag, + kTSAPrivatePlayingRightComparisonFlag, + kTSAPrivateSeenRobotWarningFlag, + kNumTSAPrivateFlags + }; + + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void loadAmbientLoops(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *spot); + virtual void activateHotspots(); + void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); + void dropItemIntoRoom(Item *, Hotspot *); + void downButton(const Input &); + void startDoorOpenMovie(const TimeValue, const TimeValue); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + void turnTo(const DirectionConstant); + CanMoveForwardReason canMoveForward(ExitTable::Entry &); + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void bumpIntoWall(); + void initializeTBPMonitor(const int, const ExtraID); + void playTBPMonitor(); + void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + void openDoor(); + void turnRight(); + void turnLeft(); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionInput); + void handleInput(const Input &, const Hotspot *); + void arriveAtTSA25Red(); + void startUpComparisonMonitor(); + void shutDownComparisonMonitor(); + void initializeComparisonMonitor(const int, const ExtraID); + void playLeftComparison(); + void playRightComparison(); + void startRobotGame(); + void setOffRipAlarm(); + uint getHistoricalLogIndex(); + void startUpRobotMonitor(); + void shutDownRobotMonitor(); + void pickedUpItem(Item *item); + void arriveFromPrehistoric(); + + void arriveFromNorad(); + void arriveFromMars(); + void arriveFromWSC(); + + InputBits getInputFilter(); + void arriveAt(const RoomID, const DirectionConstant); + void initializePegasusButtons(bool); + void releaseSprites(); + void showMainJumpMenu(); + void arriveAtTSA37(); + void receiveNotification(Notification *, const NotificationFlags); + void checkRobotLocations(const RoomID, const DirectionConstant); + void getExtraEntry(const uint32, ExtraTable::Entry &); + + Sprite _sprite1, _sprite2, _sprite3; + FuseFunction _utilityFuse; + RipTimer _ripTimer; + + FlagsArray<byte, kNumTSAPrivateFlags> _privateFlags; + + Common::String getNavMovieName(); + Common::String getSoundSpotsName(); + + void dieUncreatedInTSA(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.cpp b/engines/pegasus/neighborhood/tsa/tinytsa.cpp new file mode 100644 index 0000000000..4f109620c1 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/tinytsa.cpp @@ -0,0 +1,453 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const int16 kCompassShift = 30; + +static const TimeScale kTinyTSAMovieScale = 600; +static const TimeScale kTinyTSAFramesPerSecond = 15; +static const TimeScale kTinyTSAFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltTinyTSANormal = 0; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivationTinyTSAJumpToNorad = 1; +static const HotSpotActivationID kActivationTinyTSAJumpToMars = 2; +static const HotSpotActivationID kActivationTinyTSAJumpToWSC = 3; +static const HotSpotActivationID kActivationTinyTSAReadyForJumpMenu = 4; +static const HotSpotActivationID kActivationTinyTSAMainJumpMenu = 5; + +// Hot Spot IDs. +static const HotSpotID kTinyTSA37NorthJumpToNoradSpotID = 5000; +static const HotSpotID kTinyTSA37NorthCancelNoradSpotID = 5001; +static const HotSpotID kTinyTSA37NorthJumpToMarsSpotID = 5002; +static const HotSpotID kTinyTSA37NorthCancelMarsSpotID = 5003; +static const HotSpotID kTinyTSA37NorthJumpToWSCSpotID = 5004; +static const HotSpotID kTinyTSA37NorthCancelWSCSpotID = 5005; +static const HotSpotID kTinyTSA37NorthJumpMenuSpotID = 5006; +static const HotSpotID kTinyTSA37NorthNoradMenuSpotID = 5007; +static const HotSpotID kTinyTSA37NorthMarsMenuSpotID = 5008; +static const HotSpotID kTinyTSA37NorthWSCMenuSpotID = 5009; + +// Extra sequence IDs. +static const ExtraID kTinyTSA37PegasusDepart = 0; +static const ExtraID kTinyTSA37TimeJumpToPegasus = 1; +static const ExtraID kTinyTSA37RecallToDownload = 2; +static const ExtraID kTinyTSA37ExitHilited = 3; +static const ExtraID kTinyTSA37ExitToHorse = 4; +static const ExtraID kTinyTSA37JumpMenu000 = 5; +static const ExtraID kTinyTSA37JumpMenu001 = 6; +static const ExtraID kTinyTSA37JumpMenu010 = 7; +static const ExtraID kTinyTSA37JumpMenu011 = 8; +static const ExtraID kTinyTSA37JumpMenu100 = 9; +static const ExtraID kTinyTSA37JumpMenu101 = 10; +static const ExtraID kTinyTSA37JumpMenu110 = 11; +static const ExtraID kTinyTSA37JumpMenu111 = 12; +static const ExtraID kTinyTSA37JumpToWSCMenu = 13; +static const ExtraID kTinyTSA37CancelWSC = 14; +static const ExtraID kTinyTSA37JumpToWSC = 15; +static const ExtraID kTinyTSA37WSCToAI5 = 16; +static const ExtraID kTinyTSA37PegasusAI5 = 17; +static const ExtraID kTinyTSA37AI5ToWSC = 18; +static const ExtraID kTinyTSA37WSCToDepart = 19; +static const ExtraID kTinyTSA37JumpToMarsMenu = 20; +static const ExtraID kTinyTSA37CancelMars = 21; +static const ExtraID kTinyTSA37JumpToMars = 22; +static const ExtraID kTinyTSA37MarsToAI6 = 23; +static const ExtraID kTinyTSA37PegasusAI6 = 24; +static const ExtraID kTinyTSA37AI6ToMars = 25; +static const ExtraID kTinyTSA37MarsToDepart = 26; +static const ExtraID kTinyTSA37JumpToNoradMenu = 27; +static const ExtraID kTinyTSA37CancelNorad = 28; +static const ExtraID kTinyTSA37JumpToNorad = 29; +static const ExtraID kTinyTSA37NoradToAI7 = 30; +static const ExtraID kTinyTSA37PegasusAI7 = 31; +static const ExtraID kTinyTSA37AI7ToNorad = 32; +static const ExtraID kTinyTSA37NoradToDepart = 33; +static const ExtraID kTinyTSA37EnvironmentalScan = 34; +static const ExtraID kTinyTSA37DownloadToMainMenu = 35; +static const ExtraID kTinyTSA37DownloadToOpMemReview = 36; +static const ExtraID kTinyTSA37OpMemReviewToMainMenu = 37; + +TinyTSA::TinyTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Tiny TSA", kTinyTSAID) { +} + +void TinyTSA::start() { + g_energyMonitor->stopEnergyDraining(); + Neighborhood::start(); +} + +Common::String TinyTSA::getBriefingMovie() { + Common::String movieName = Neighborhood::getBriefingMovie(); + + if (movieName.empty()) { + switch (getCurrentActivation()) { + case kActivationTinyTSAJumpToNorad: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI7, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + case kActivationTinyTSAJumpToMars: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI6, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + case kActivationTinyTSAJumpToWSC: + g_AIChip->showBriefingClicked(); + startExtraSequenceSync(kTinyTSA37PegasusAI5, kHintInterruption); + startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput); + g_AIChip->clearClicked(); + movieName = ""; + break; + default: + movieName = "Images/AI/TSA/XT04"; + break; + } + } + + return movieName; +} + +Common::String TinyTSA::getEnvScanMovie() { + Common::String movieName = Neighborhood::getEnvScanMovie(); + + if (movieName.empty()) { + g_AIChip->showEnvScanClicked(); + startExtraSequenceSync(kTinyTSA37EnvironmentalScan, kHintInterruption); + + switch (getCurrentActivation()) { + case kActivationTinyTSAJumpToNorad: + startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput); + showExtraView(kTinyTSA37JumpToNoradMenu); + break; + case kActivationTinyTSAJumpToMars: + startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput); + showExtraView(kTinyTSA37JumpToMarsMenu); + break; + case kActivationTinyTSAJumpToWSC: + startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput); + showExtraView(kTinyTSA37JumpToWSCMenu); + break; + default: + showMainJumpMenu(); + break; + } + + g_AIChip->clearClicked(); + } + + return movieName; +} + +void TinyTSA::loadAmbientLoops() { + loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); +} + +int16 TinyTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + return Neighborhood::getStaticCompassAngle(room, dir) - kCompassShift; +} + +uint16 TinyTSA::getDateResID() const { + return kDate2318ID; +} + +InputBits TinyTSA::getInputFilter() { + // Can't move forward... + return Neighborhood::getInputFilter() & ~(kFilterUpButton | kFilterUpAuto); +} + +void TinyTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + if (clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kTinyTSA37NorthJumpMenuSpotID: + // This hotspot isn't accessable from Tiny TSA + warning("jump menu spot"); + return; + case kTinyTSA37NorthJumpToNoradSpotID: + GameState.setTSAState(kPlayerOnWayToNorad); + requestExtraSequence(kTinyTSA37JumpToNorad, 0, kFilterNoInput); + if (!GameState.getBeenToNorad()) { + requestExtraSequence(kTinyTSA37NoradToAI7, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI7, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI7ToNorad, 0, kFilterNoInput); + GameState.setBeenToNorad(true); + } + + requestExtraSequence(kTinyTSA37NoradToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + case kTinyTSA37NorthJumpToMarsSpotID: + GameState.setTSAState(kPlayerOnWayToMars); + requestExtraSequence(kTinyTSA37JumpToMars, 0, kFilterNoInput); + if (!GameState.getBeenToMars()) { + requestExtraSequence(kTinyTSA37MarsToAI6, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI6, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI6ToMars, 0, kFilterNoInput); + GameState.setBeenToMars(true); + } + + requestExtraSequence(kTinyTSA37MarsToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + case kTinyTSA37NorthJumpToWSCSpotID: + GameState.setTSAState(kPlayerOnWayToWSC); + requestExtraSequence(kTinyTSA37JumpToWSC, 0, kFilterNoInput); + if (!GameState.getBeenToWSC()) { + requestExtraSequence(kTinyTSA37WSCToAI5, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusAI5, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37AI5ToWSC, 0, kFilterNoInput); + GameState.setBeenToWSC(true); + } + + requestExtraSequence(kTinyTSA37WSCToDepart, 0, kFilterNoInput); + requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); + return; + } + } + + Neighborhood::clickInHotspot(input, clickedSpot); +} + +void TinyTSA::showMainJumpMenu() { + ExtraID jumpMenuView = kTinyTSA37JumpMenu000; + + if (GameState.getNoradFinished()) + jumpMenuView += 4; + if (GameState.getMarsFinished()) + jumpMenuView += 2; + if (GameState.getWSCFinished()) + jumpMenuView += 1; + + showExtraView(jumpMenuView); + setCurrentActivation(kActivationTinyTSAMainJumpMenu); +} + +void TinyTSA::checkContinuePoint(const RoomID, const DirectionConstant) { + makeContinuePoint(); +} + +void TinyTSA::arriveAt(const RoomID room, const DirectionConstant direction) { + Neighborhood::arriveAt(room, direction); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + case kPlayerOnWayToMars: + case kPlayerOnWayToWSC: + startExtraSequence(kTinyTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput); + break; + case kPlayerLockedInPegasus: + showMainJumpMenu(); + break; + } +} + +void TinyTSA::receiveNotification(Notification *notification, const NotificationFlags flags) { + ExtraID lastExtra = _lastExtra; + + Neighborhood::receiveNotification(notification, flags); + + if ((flags & kExtraCompletedFlag) != 0) { + // Only allow input if we're not in the middle of series of queue requests. + if (actionQueueEmpty()) + _interruptionFilter = kFilterAllInput; + + switch (lastExtra) { + case kTinyTSA37PegasusDepart: + _vm->setLastEnergyValue(kFullEnergy); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + _vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth); + GameState.setNoradSeenTimeStream(false); + GameState.setNoradGassed(true); + GameState.setNoradFillingStationOn(false); + GameState.setNoradN22MessagePlayed(false); + GameState.setNoradPlayedGlobeGame(false); + GameState.setNoradBeatRobotWithClaw(false); + GameState.setNoradBeatRobotWithDoor(false); + GameState.setNoradRetScanGood(false); + GameState.setNoradWaitingForLaser(false); + GameState.setNoradSubRoomPressure(9); + GameState.setNoradSubPrepState(kSubNotPrepped); + break; + case kPlayerOnWayToMars: + _vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth); + GameState.setMarsSeenTimeStream(false); + GameState.setMarsHeardUpperPodMessage(false); + GameState.setMarsRobotThrownPlayer(false); + GameState.setMarsHeardCheckInMessage(false); + GameState.setMarsPodAtUpperPlatform(false); + GameState.setMarsSeenThermalScan(false); + GameState.setMarsArrivedBelow(false); + GameState.setMarsSeenRobotAtReactor(false); + GameState.setMarsAvoidedReactorRobot(false); + GameState.setMarsLockFrozen(false); + GameState.setMarsLockBroken(false); + GameState.setMarsSecurityDown(false); + GameState.setMarsAirlockOpen(false); + GameState.setMarsReadyForShuttleTransport(false); + GameState.setMarsFinishedCanyonChase(false); + GameState.setMarsThreadedMaze(false); + break; + case kPlayerOnWayToWSC: + _vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest); + GameState.setWSCSeenTimeStream(false); + GameState.setWSCPoisoned(false); + GameState.setWSCAnsweredAboutDart(false); + GameState.setWSCDartInAnalyzer(false); + GameState.setWSCRemovedDart(false); + GameState.setWSCAnalyzerOn(false); + GameState.setWSCAnalyzedDart(false); + GameState.setWSCPickedUpAntidote(false); + GameState.setWSCSawMorph(false); + GameState.setWSCDesignedAntidote(false); + GameState.setWSCOfficeMessagesOpen(false); + GameState.setWSCSeenNerd(false); + GameState.setWSCHeardPage1(false); + GameState.setWSCHeardPage2(false); + GameState.setWSCHeardCheckIn(false); + GameState.setWSCDidPlasmaDodge(false); + GameState.setWSCSeenSinclairLecture(false); + GameState.setWSCBeenAtWSC93(false); + GameState.setWSCCatwalkDark(false); + GameState.setWSCRobotDead(false); + GameState.setWSCRobotGone(false); + break; + }; + break; + case kTinyTSA37TimeJumpToPegasus: + if (g_energyMonitor) + g_energyMonitor->stopEnergyDraining(); + + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + arriveFromNorad(); + break; + case kPlayerOnWayToMars: + arriveFromMars(); + break; + case kPlayerOnWayToWSC: + arriveFromWSC(); + break; + default: + break; + } + break; + case kTinyTSA37DownloadToOpMemReview: + switch (GameState.getTSAState()) { + case kPlayerOnWayToNorad: + g_opticalChip->playOpMemMovie(kPoseidonSpotID); + break; + case kPlayerOnWayToMars: + g_opticalChip->playOpMemMovie(kAriesSpotID); + break; + case kPlayerOnWayToWSC: + g_opticalChip->playOpMemMovie(kMercurySpotID); + break; + } + + requestExtraSequence(kTinyTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput); + break; + case kTinyTSA37DownloadToMainMenu: + case kTinyTSA37OpMemReviewToMainMenu: + GameState.setTSAState(kPlayerLockedInPegasus); + showMainJumpMenu(); + makeContinuePoint(); + break; + case kTinyTSA37JumpToNoradMenu: + setCurrentActivation(kActivationTinyTSAJumpToNorad); + break; + case kTinyTSA37JumpToMarsMenu: + setCurrentActivation(kActivationTinyTSAJumpToMars); + break; + case kTinyTSA37JumpToWSCMenu: + setCurrentActivation(kActivationTinyTSAJumpToWSC); + break; + case kTinyTSA37CancelNorad: + case kTinyTSA37CancelMars: + case kTinyTSA37CancelWSC: + showMainJumpMenu(); + break; + } + } + + g_AIArea->checkMiddleArea(); +} + +void TinyTSA::arriveFromNorad() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) { + GameState.setScoringFinishedNorad(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void TinyTSA::arriveFromMars() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) { + GameState.setScoringFinishedMars(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +void TinyTSA::arriveFromWSC() { + requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + + if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) { + GameState.setScoringFinishedWSC(); + requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); + } else { + requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); + } +} + +Common::String TinyTSA::getNavMovieName() { + return "Images/TSA/Tiny TSA.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.h b/engines/pegasus/neighborhood/tsa/tinytsa.h new file mode 100644 index 0000000000..2dc234675d --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/tinytsa.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H +#define PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// Room IDs. + +static const RoomID kTinyTSA37 = 0; + +class TinyTSA : public Neighborhood { +public: + TinyTSA(InputHandler *, PegasusEngine *); + virtual ~TinyTSA() {} + + virtual uint16 getDateResID() const; + + void start(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + +protected: + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + void loadAmbientLoops(); + virtual void clickInHotspot(const Input &, const Hotspot *); + + virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + + void arriveFromNorad(); + void arriveFromMars(); + void arriveFromWSC(); + + InputBits getInputFilter(); + void arriveAt(const RoomID, const DirectionConstant); + void showMainJumpMenu(); + void receiveNotification(Notification *, const NotificationFlags); + + Common::String getNavMovieName(); + Common::String getSoundSpotsName() { return ""; } +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/turn.cpp b/engines/pegasus/neighborhood/turn.cpp new file mode 100644 index 0000000000..1157796f55 --- /dev/null +++ b/engines/pegasus/neighborhood/turn.cpp @@ -0,0 +1,63 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/turn.h" + +namespace Pegasus { + +void TurnTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].turnDirection = stream->readByte(); + _entries[i].altCode = stream->readByte(); + stream->readByte(); // alignment + _entries[i].endDirection = stream->readByte(); + stream->readByte(); // alignment + debug(0, "Turn[%d]: %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].turnDirection, _entries[i].altCode, _entries[i].endDirection); + } +} + +void TurnTable::clear() { + _entries.clear(); +} + +TurnTable::Entry TurnTable::findEntry(RoomID room, DirectionConstant direction, TurnDirection turnDirection, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].turnDirection == turnDirection && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/turn.h b/engines/pegasus/neighborhood/turn.h new file mode 100644 index 0000000000..329b03eddb --- /dev/null +++ b/engines/pegasus/neighborhood/turn.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_TURN_H +#define PEGASUS_NEIGHBORHOOD_TURN_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class TurnTable { +public: + TurnTable() {} + ~TurnTable() {} + + static uint32 getResTag() { return MKTAG('T', 'u', 'r', 'n'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { endDirection = kNoDirection; } + bool isEmpty() { return endDirection == kNoDirection; } + + RoomID room; + DirectionConstant direction; + TurnDirection turnDirection; + AlternateID altCode; + DirectionConstant endDirection; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, TurnDirection turnDirection, AlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/view.cpp b/engines/pegasus/neighborhood/view.cpp new file mode 100644 index 0000000000..4e46f5374e --- /dev/null +++ b/engines/pegasus/neighborhood/view.cpp @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/view.h" + +namespace Pegasus { + +void ViewTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + _entries[i].altCode = stream->readByte(); + _entries[i].time = stream->readUint32BE(); + debug(0, "View[%d]: %d %d %d %d", i, _entries[i].room, _entries[i].direction, + _entries[i].altCode, _entries[i].time); + } +} + +void ViewTable::clear() { + _entries.clear(); +} + +ViewTable::Entry ViewTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) + return _entries[i]; + + return Entry(); +} + +} // End of namespace pegasus diff --git a/engines/pegasus/neighborhood/view.h b/engines/pegasus/neighborhood/view.h new file mode 100644 index 0000000000..3397508b61 --- /dev/null +++ b/engines/pegasus/neighborhood/view.h @@ -0,0 +1,68 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_VIEW_H +#define PEGASUS_NEIGHBORHOOD_VIEW_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ViewTable { +public: + ViewTable() {} + ~ViewTable() {} + + static uint32 getResTag() { return MKTAG('V', 'i', 'e', 'w'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry() { time = 0xffffffff; } + bool isEmpty() { return time == 0xffffffff; } + + RoomID room; + DirectionConstant direction; + AlternateID altCode; + TimeValue time; + }; + + Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/wsc/moleculebin.cpp b/engines/pegasus/neighborhood/wsc/moleculebin.cpp new file mode 100644 index 0000000000..210c0ad313 --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/moleculebin.cpp @@ -0,0 +1,127 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/graphics.h" +#include "pegasus/neighborhood/wsc/moleculebin.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const CoordType kMoleculeBinWidth = 138; +static const CoordType kMoleculeBinHeight = 128; + +static const CoordType kMoleculeWidth = 66; +static const CoordType kMoleculeHeight = 40; + +static const CoordType kMoleculeBinLeft = kNavAreaLeft + 286; +static const CoordType kMoleculeBinTop = kNavAreaLeft + 96; + +// Layouts: + +MoleculeBin::MoleculeBin() : DisplayElement(kNoDisplayElement) { + _highlightColor = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 102); + _selectedMolecule = -1; +} + +void MoleculeBin::initMoleculeBin() { + if (!isDisplaying()) { + for (int i = 0; i < 6; i++) + _binLayout[i] = i; + + resetBin(); + _binImages.getImageFromPICTFile("Images/World Science Center/Molecules"); + setDisplayOrder(kWSCMoleculeBinOrder); + setBounds(kMoleculeBinLeft, kMoleculeBinTop, kMoleculeBinLeft + kMoleculeBinWidth, + kMoleculeBinTop + kMoleculeBinHeight); + startDisplaying(); + show(); + } +} + +void MoleculeBin::cleanUpMoleculeBin() { + if (isDisplaying()) { + stopDisplaying(); + _binImages.deallocateSurface(); + } +} + +void MoleculeBin::setBinLayout(const uint32 *layout) { + for (int i = 0; i < 6; i++) + _binLayout[i] = layout[i]; +} + +void MoleculeBin::highlightMolecule(const uint32 whichMolecule) { + if (!_moleculeFlags.getFlag(whichMolecule)) { + _moleculeFlags.setFlag(whichMolecule, true); + triggerRedraw(); + } +} + +bool MoleculeBin::isMoleculeHighlighted(uint32 whichMolecule) { + return _moleculeFlags.getFlag(whichMolecule); +} + +void MoleculeBin::selectMolecule(const int whichMolecule) { + if (_selectedMolecule != whichMolecule) { + _selectedMolecule = whichMolecule; + triggerRedraw(); + } +} + +void MoleculeBin::resetBin() { + _moleculeFlags.clearAllFlags(); + _selectedMolecule = -1; + triggerRedraw(); +} + +void MoleculeBin::draw(const Common::Rect &) { + Common::Rect r1(0, 0, kMoleculeWidth, kMoleculeHeight); + Common::Rect r2 = r1; + + for (int i = 0; i < 6; i++) { + r1.moveTo(i * (kMoleculeWidth * 2), 0); + + if (_moleculeFlags.getFlag(_binLayout[i])) + r1.translate(kMoleculeWidth, 0); + + r2.moveTo((_binLayout[i] & 1) * (kMoleculeWidth + 2) + _bounds.left + 2, + (_binLayout[i] >> 1) * (kMoleculeHeight + 2) + _bounds.top + 2); + + _binImages.copyToCurrentPort(r1, r2); + } + + if (_selectedMolecule >= 0) { + r2.moveTo((_selectedMolecule & 1) * (kMoleculeWidth + 2) + _bounds.left + 2, + (_selectedMolecule >> 1) * (kMoleculeHeight + 2) + _bounds.top + 2); + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + + screen->frameRect(r2, _highlightColor); + r2.grow(1); + screen->frameRect(r2, _highlightColor); + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/wsc/moleculebin.h b/engines/pegasus/neighborhood/wsc/moleculebin.h new file mode 100644 index 0000000000..3de4b5ed2a --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/moleculebin.h @@ -0,0 +1,72 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_WSC_MOLECULEBIN_H +#define PEGASUS_NEIGHBORHOOD_WSC_MOLECULEBIN_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +enum { + kMolecule1, + kMolecule2, + kMolecule3, + kMolecule4, + kMolecule5, + kMolecule6 +}; + +class MoleculeBin : public DisplayElement { +public: + MoleculeBin(); + virtual ~MoleculeBin() {} + + void initMoleculeBin(); + void cleanUpMoleculeBin(); + + void setBinLayout(const uint32 *); + + void highlightMolecule(const uint32 whichMolecule); + void selectMolecule(const int whichMolecule); + void resetBin(); + + bool isMoleculeHighlighted(uint32); + +protected: + void draw(const Common::Rect &); + + Surface _binImages; + FlagsArray<byte, kMolecule6 + 1> _moleculeFlags; + int _selectedMolecule; + uint32 _binLayout[6]; + uint32 _highlightColor; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/wsc/wsc.cpp b/engines/pegasus/neighborhood/wsc/wsc.cpp new file mode 100644 index 0000000000..50b7774da4 --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/wsc.cpp @@ -0,0 +1,2546 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const CanMoveForwardReason kCantMoveWatchingDiagnosis = kCantMoveLastReason + 1; + +static const CanTurnReason kCantTurnWatchingDiagnosis = kCantTurnLastReason + 1; +static const CanTurnReason kCantTurnWatchingAnalysis = kCantTurnWatchingDiagnosis + 1; +static const CanTurnReason kCantTurnInMoleculeGame = kCantTurnWatchingAnalysis + 1; + +static const TimeScale kMoleculesMovieScale = 600; +static const TimeValue kMoleculeLoopTime = 4 * kMoleculesMovieScale; +static const TimeValue kMoleculeFailTime = 2 * kMoleculesMovieScale; + +enum { + kMoleculeLoop0Time = 0, + kMoleculeFail0Time = kMoleculeLoop0Time + kMoleculeLoopTime, + kMoleculeLoop1Time = kMoleculeFail0Time + kMoleculeFailTime, + kMoleculeFail1Time = kMoleculeLoop1Time + kMoleculeLoopTime, + kMoleculeLoop2Time = kMoleculeFail1Time + kMoleculeFailTime, + kMoleculeFail2Time = kMoleculeLoop2Time + kMoleculeLoopTime, + kMoleculeLoop3Time = kMoleculeFail2Time + kMoleculeFailTime, + kMoleculeFail3Time = kMoleculeLoop3Time + kMoleculeLoopTime, + kMoleculeLoop4Time = kMoleculeFail3Time + kMoleculeFailTime, + kMoleculeFail4Time = kMoleculeLoop4Time + kMoleculeLoopTime, + kMoleculeLoop5Time = kMoleculeFail4Time + kMoleculeFailTime, + kMoleculeFail5Time = kMoleculeLoop5Time + kMoleculeLoopTime, + kMoleculeLoop6Time = kMoleculeFail5Time + kMoleculeFailTime +}; + +static const TimeValue s_moleculeLoopTimes[] = { + kMoleculeLoop0Time, + kMoleculeLoop1Time, + kMoleculeLoop2Time, + kMoleculeLoop3Time, + kMoleculeLoop4Time, + kMoleculeLoop5Time, + kMoleculeLoop6Time +}; + +static const TimeValue s_moleculeFailTimes[] = { + kMoleculeFail0Time, + kMoleculeFail1Time, + kMoleculeFail2Time, + kMoleculeFail3Time, + kMoleculeFail4Time, + kMoleculeFail5Time +}; + +static const int16 kAuditoriumAngleOffset = 5; + +static const int kPlasmaEnergyWithShield = kMaxJMPEnergy * 10 / 100; +static const int kPlasmaEnergyNoShield = kMaxJMPEnergy * 20 / 100; + +static const int kTimerEventPlasmaHit = 0; +static const int kTimerEventPlayerGawkingAtRobot = 1; +static const int kTimerEventPlayerGawkingAtRobot2 = 2; + +static const TimeValue kWSCMolecule1In = 0; +static const TimeValue kWSCMolecule1Out = 937; + +static const TimeValue kWSCMolecule2In = 937; +static const TimeValue kWSCMolecule2Out = 1864; + +static const TimeValue kWSCMolecule3In = 1864; +static const TimeValue kWSCMolecule3Out = 2790; + +static const TimeValue kWSCClick1In = 2790; +static const TimeValue kWSCClick1Out = 2890; + +static const TimeValue kWSCClick2In = 2890; +static const TimeValue kWSCClick2Out = 3059; + +static const TimeValue kWSCClick3In = 3059; +static const TimeValue kWSCClick3Out = 3156; + +static const TimeValue kWSCFlashlightClickIn = 3156; +static const TimeValue kWSCFlashlightClickOut = 3211; + +static const TimeValue kWSCBumpIntoWallIn = 3211; +static const TimeValue kWSCBumpIntoWallOut = 3514; + +static const TimeValue kWSCCantTransportIn = 3514; +static const TimeValue kWSCCantTransportOut = 7791; + +static const TimeValue kHernandezNotHomeIn = 7791; +static const TimeValue kHernandezNotHomeOut = 10199; + +static const TimeValue kWashingtonNotHomeIn = 10199; +static const TimeValue kWashingtonNotHomeOut = 12649; + +static const TimeValue kSullivanNotHomeIn = 12649; +static const TimeValue kSullivanNotHomeOut = 15031; + +static const TimeValue kNakamuraNotHomeIn = 15031; +static const TimeValue kNakamuraNotHomeOut = 17545; + +static const TimeValue kGrailisNotHomeIn = 17545; +static const TimeValue kGrailisNotHomeOut = 19937; + +static const TimeValue kTheriaultNotHomeIn = 19937; +static const TimeValue kTheriaultNotHomeOut = 22395; + +static const TimeValue kGlennerNotHomeIn = 22395; +static const TimeValue kGlennerNotHomeOut = 24770; + +static const TimeValue kSinclairNotHomeIn = 24770; +static const TimeValue kSinclairNotHomeOut = 27328; + +static const TimeValue kWSCLabClosedIn = 27328; +static const TimeValue kWSCLabClosedOut = 28904; + +static const TimeValue kSlidingDoorCloseIn = 28904; +static const TimeValue kSlidingDoorCloseOut = 29295; + +static const TimeValue kSlimyDoorCloseIn = 29295; +static const TimeValue kSlimyDoorCloseOut = 29788; + +static const TimeValue kPaging1In = 29788; +static const TimeValue kPaging1Out = 32501; + +static const TimeValue kPaging2In = 32501; +static const TimeValue kPaging2Out = 34892; + +static const TimeValue kCheckInIn = 34892; +static const TimeValue kCheckInOut = 37789; + +static const TimeValue kDrinkAntidoteIn = 37789; +static const TimeValue kDrinkAntidoteOut = 39725; + +static const TimeScale kWSCMovieScale = 600; +static const TimeScale kWSCFramesPerSecond = 15; +static const TimeScale kWSCFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltWSCNormal = 0; +static const AlternateID kAltWSCTookMachineGun = 1; +static const AlternateID kAltWSCW0ZDoorOpen = 2; +static const AlternateID kAltWSCPeopleAtW19North = 3; + +// Room IDs. +static const RoomID kWSC02 = 1; +static const RoomID kWSC03 = 4; +static const RoomID kWSC04 = 5; +static const RoomID kWSC06 = 6; +static const RoomID kWSC07 = 7; +static const RoomID kWSC08 = 8; +static const RoomID kWSC09 = 9; +static const RoomID kWSC10 = 10; +static const RoomID kWSC11 = 11; +static const RoomID kWSC13 = 12; +static const RoomID kWSC14 = 13; +static const RoomID kWSC15 = 14; +static const RoomID kWSC16 = 15; +static const RoomID kWSC17 = 16; +static const RoomID kWSC18 = 17; +static const RoomID kWSC19 = 18; +static const RoomID kWSC20 = 19; +static const RoomID kWSC21 = 20; +static const RoomID kWSC22 = 21; +static const RoomID kWSC23 = 22; +static const RoomID kWSC24 = 23; +static const RoomID kWSC25 = 24; +static const RoomID kWSC26 = 25; +static const RoomID kWSC27 = 26; +static const RoomID kWSC28 = 27; +static const RoomID kWSC29 = 28; +static const RoomID kWSC31 = 29; +static const RoomID kWSC32 = 30; +static const RoomID kWSC33 = 31; +static const RoomID kWSC34 = 32; +static const RoomID kWSC35 = 33; +static const RoomID kWSC36 = 34; +static const RoomID kWSC37 = 35; +static const RoomID kWSC38 = 36; +static const RoomID kWSC39 = 37; +static const RoomID kWSC40 = 38; +static const RoomID kWSC41 = 39; +static const RoomID kWSC42 = 40; +static const RoomID kWSC43 = 41; +static const RoomID kWSC44 = 42; +static const RoomID kWSC45 = 43; +static const RoomID kWSC46 = 44; +static const RoomID kWSC47 = 45; +static const RoomID kWSC48 = 46; +static const RoomID kWSC49 = 47; +static const RoomID kWSC50 = 48; +static const RoomID kWSC52 = 49; +static const RoomID kWSC53 = 50; +static const RoomID kWSC54 = 51; +static const RoomID kWSC55 = 52; +static const RoomID kWSC56 = 53; +static const RoomID kWSC57 = 54; +static const RoomID kWSC58 = 55; +static const RoomID kWSC60 = 56; +static const RoomID kWSC60East = 57; +static const RoomID kWSC60North = 58; +static const RoomID kWSC61 = 59; +static const RoomID kWSC61South = 60; +static const RoomID kWSC61West = 61; +static const RoomID kWSC63 = 63; +static const RoomID kWSC64 = 64; +static const RoomID kWSC65 = 65; +static const RoomID kWSC65Screen = 66; +static const RoomID kWSC66 = 67; +static const RoomID kWSC67 = 68; +static const RoomID kWSC68 = 69; +static const RoomID kWSC69 = 70; +static const RoomID kWSC70 = 71; +static const RoomID kWSC71 = 72; +static const RoomID kWSC72 = 73; +static const RoomID kWSC73 = 74; +static const RoomID kWSC74 = 75; +static const RoomID kWSC75 = 76; +static const RoomID kWSC0Z = 77; +static const RoomID kWSC76 = 78; +static const RoomID kWSC77 = 79; +static const RoomID kWSC78 = 80; +static const RoomID kWSC79 = 81; +static const RoomID kWSC80 = 82; +static const RoomID kWSC81 = 83; +static const RoomID kWSC82 = 84; +static const RoomID kWSC83 = 85; +static const RoomID kWSC84 = 86; +static const RoomID kWSC85 = 87; +static const RoomID kWSC86 = 88; +static const RoomID kWSC87 = 89; +static const RoomID kWSC88 = 90; +static const RoomID kWSC89 = 91; +static const RoomID kWSC90 = 92; +static const RoomID kWSC91 = 93; +static const RoomID kWSC92 = 94; +static const RoomID kWSC93 = 95; +static const RoomID kWSC94 = 96; +static const RoomID kWSC95 = 97; +static const RoomID kWSC96 = 98; +static const RoomID kWSC97 = 99; +static const RoomID kWSC98 = 100; +static const RoomID kWSCDeathRoom = 101; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivationZoomedInToAnalyzer = 1; +static const HotSpotActivationID kActivationShotByRobot = 2; +static const HotSpotActivationID kActivationWarnedAboutPoison = 3; +static const HotSpotActivationID kActivationMorphScreenOff = 4; +static const HotSpotActivationID kActivationReadyForMorph = 5; +static const HotSpotActivationID kActivationMorphLooping = 6; +static const HotSpotActivationID kActivationMorphInterrupted = 7; +static const HotSpotActivationID kActivationW03NorthOff = 8; +static const HotSpotActivationID kActivationW03NorthReadyForInstructions = 9; +static const HotSpotActivationID kActivationW03NorthSawInstructions = 10; +static const HotSpotActivationID kActivationW03NorthInGame = 11; +static const HotSpotActivationID kActivationReadyForSynthesis = 12; +static const HotSpotActivationID kActivationSynthesizerLooping = 13; +static const HotSpotActivationID kActivationReadyForMap = 14; +static const HotSpotActivationID kActivationSinclairOfficeLocked = 15; +static const HotSpotActivationID kActivationW58SouthDoorLocked = 16; +static const HotSpotActivationID kActivationW61SouthOff = 17; +static const HotSpotActivationID kActivationW61SouthOn = 18; +static const HotSpotActivationID kActivationW61MessagesOff = 19; +static const HotSpotActivationID kActivationW61MessagesOn = 20; +static const HotSpotActivationID kActivationWSCRobotHeadOpen = 21; +static const HotSpotActivationID kActivationRobotTurning = 22; +static const HotSpotActivationID kActivationRobotDead = 23; +static const HotSpotActivationID kActivationRobotGone = 24; + +// Hot Spot IDs. +static const HotSpotID kWSCDropDartSpotID = 5000; +static const HotSpotID kWSCTurnOnAnalyzerSpotID = 5001; +static const HotSpotID kWSCAnalyzerScreenSpotID = 5002; +static const HotSpotID kWSCSpinRobotSpotID = 5003; +static const HotSpotID kWSC01YesSpotID = 5004; +static const HotSpotID kWSC01NoSpotID = 5005; +static const HotSpotID kWSC01AcknowledgeWarningSpotID = 5006; +static const HotSpotID kWSC02SouthMorphSpotID = 5007; +static const HotSpotID kWSC02SouthMessagesSpotID = 5008; +static const HotSpotID kWSC02SouthMorphOutSpotID = 5009; +static const HotSpotID kWSC02ActivateMorphScreenSpotID = 5010; +static const HotSpotID kWSC02SouthStartMorphSpotID = 5011; +static const HotSpotID kWSC02SouthInterruptMorphSpotID = 5012; +static const HotSpotID kWSC02SouthMorphFinishedSpotID = 5013; +static const HotSpotID kWSC02SouthTakeArgonSpotID = 5014; +static const HotSpotID kWSC02SouthMessagesOutSpotID = 5015; +static const HotSpotID kWSC02SouthTakeNitrogenSpotID = 5016; +static const HotSpotID kWSC02SouthPlayMessagesSpotID = 5017; +static const HotSpotID kWSC03NorthActivateScreenSpotID = 5018; +static const HotSpotID kWSC03NorthBuildMoleculeSpotID = 5019; +static const HotSpotID kWSC03NorthProceedSpotID = 5020; +static const HotSpotID kWSC03NorthMolecule1SpotID = 5021; +static const HotSpotID kWSC03NorthMolecule2SpotID = 5022; +static const HotSpotID kWSC03NorthMolecule3SpotID = 5023; +static const HotSpotID kWSC03NorthMolecule4SpotID = 5024; +static const HotSpotID kWSC03NorthMolecule5SpotID = 5025; +static const HotSpotID kWSC03NorthMolecule6SpotID = 5026; +static const HotSpotID kWSC03SouthActivateSynthesizerSpotID = 5027; +static const HotSpotID kWSC03SouthPickUpAntidoteSpotID = 5028; +static const HotSpotID kWSC07SouthMapSpotID = 5029; +static const HotSpotID kW42EastUnlockDoorSpotID = 5030; +static const HotSpotID kW56NorthMapSpotID = 5031; +static const HotSpotID kW58SouthPryDoorSpotID = 5032; +static const HotSpotID kWSC60EastSpotID = 5033; +static const HotSpotID kWSC60NorthSpotID = 5034; +static const HotSpotID kWSC60EastOutSpotID = 5035; +static const HotSpotID kWSC60NorthOutSpotID = 5036; +static const HotSpotID kWSC61EastSpotID = 5037; +static const HotSpotID kWSC61SouthSpotID = 5038; +static const HotSpotID kW61SouthMachineGunSpotID = 5039; +static const HotSpotID kW61SouthDropMachineGunSpotID = 5040; +static const HotSpotID kWSC61WestSpotID = 5041; +static const HotSpotID kWSC61SouthOutSpotID = 5042; +static const HotSpotID kW61SouthActivateSpotID = 5043; +static const HotSpotID kW61SmartAlloysSpotID = 5044; +static const HotSpotID kW61MorphingSpotID = 5045; +static const HotSpotID kW61TimeBendingSpotID = 5046; +static const HotSpotID kWSC61WestOutSpotID = 5047; +static const HotSpotID kW61TurnOnMessagesSpotID = 5048; +static const HotSpotID kW61WhiteMessageSpotID = 5049; +static const HotSpotID kW61WalchekMessageSpotID = 5050; +static const HotSpotID kWSC65SouthScreenSpotID = 5051; +static const HotSpotID kWSC65SouthScreenOutSpotID = 5052; +static const HotSpotID kW98RetinalChipSpotID = 5053; +static const HotSpotID kW98MapChipSpotID = 5054; +static const HotSpotID kW98OpticalChipSpotID = 5055; +static const HotSpotID kW98DropArgonSpotID = 5056; +static const HotSpotID kW98GrabCableSpotID = 5057; +static const HotSpotID kW98OpenRobotSpotID = 5058; +static const HotSpotID kW98StunGunSpotID = 5059; + +// Extra sequence IDs. +static const ExtraID kWSCArrivalFromTSA = 0; +static const ExtraID kWSCShotByRobot = 1; +static const ExtraID kWSCDartScan1 = 2; +static const ExtraID kWSCDartScan2 = 3; +static const ExtraID kWSCDartScanNo = 4; +static const ExtraID kWSCDartScan3 = 5; +static const ExtraID kWSCAnalyzerPowerUp = 6; +static const ExtraID kWSCAnalyzerPowerUpWithDart = 7; +static const ExtraID kWSCDropDartIntoAnalyzer = 8; +static const ExtraID kWSCAnalyzeDart = 9; +static const ExtraID kWSCZoomOutFromAnalyzer = 10; +static const ExtraID kWSCSpinRobot = 11; +static const ExtraID kWSC02MorphZoomNoArgon = 12; +static const ExtraID kWSC02MessagesZoomNoNitrogen = 13; +static const ExtraID kWSC02ZoomOutNoArgon = 14; +static const ExtraID kWSC02TurnOnMorphScreen = 15; +static const ExtraID kWSC02DropToMorphExperiment = 16; +static const ExtraID kWSC02MorphLoop = 17; +static const ExtraID kWSC02MorphInterruption = 18; +static const ExtraID kWSC02MorphFinished = 19; +static const ExtraID kWSC02TurnOffMorphScreen = 20; +static const ExtraID kWSC02SouthViewNoArgon = 21; +static const ExtraID kMessagesMovedToOffice = 22; +static const ExtraID kMessagesOff = 23; +static const ExtraID kMessagesZoomOutNoNitrogen = 24; +static const ExtraID kMessagesMovedToOfficeNoNitrogen = 25; +static const ExtraID kMessagesOffNoNitrogen = 26; +static const ExtraID kMessagesViewNoNitrogen = 27; +static const ExtraID kMessagesViewMachineOnNoNitrogen = 28; +static const ExtraID kW03NorthActivate = 29; +static const ExtraID kW03NorthGetData = 30; +static const ExtraID kW03NorthInstructions = 31; +static const ExtraID kW03NorthPrepMolecule1 = 32; +static const ExtraID kW03NorthPrepMolecule2 = 33; +static const ExtraID kW03NorthPrepMolecule3 = 34; +static const ExtraID kW03NorthFinishSynthesis = 35; +static const ExtraID kW03SouthCreateAntidote = 36; +static const ExtraID kW03SouthAntidoteLoop = 37; +static const ExtraID kW03SouthDeactivate = 38; +static const ExtraID kW03SouthViewNoAntidote = 39; +static const ExtraID kWSC07SouthMap = 40; +static const ExtraID kW17WestPeopleCrossing = 41; +static const ExtraID kW17WestPeopleCrossingView = 42; +static const ExtraID kW21SouthPeopleCrossing = 43; +static const ExtraID kW24SouthPeopleCrossing = 44; +static const ExtraID kW34EastPeopleCrossing = 45; +static const ExtraID kW36WestPeopleCrossing = 46; +static const ExtraID kW38NorthPeopleCrossing = 47; +static const ExtraID kW46SouthPeopleCrossing = 48; +static const ExtraID kW49NorthPeopleCrossing = 49; +static const ExtraID kW49NorthPeopleCrossingView = 50; +static const ExtraID kWSC56SouthMap = 51; +static const ExtraID kNerdAtTheDoor1 = 52; +static const ExtraID kNerdAtTheDoor2 = 53; +static const ExtraID kW61SouthZoomInNoGun = 54; +static const ExtraID kW61Brochure = 55; +static const ExtraID kW61SouthScreenOnWithGun = 56; +static const ExtraID kW61SouthScreenOffWithGun = 57; +static const ExtraID kW61SouthSmartAlloysWithGun = 58; +static const ExtraID kW61SouthMorphingWithGun = 59; +static const ExtraID kW61SouthTimeBendingWithGun = 60; +static const ExtraID kW61SouthZoomOutNoGun = 61; +static const ExtraID kW61SouthScreenOnNoGun = 62; +static const ExtraID kW61SouthScreenOffNoGun = 63; +static const ExtraID kW61SouthSmartAlloysNoGun = 64; +static const ExtraID kW61SouthMorphingNoGun = 65; +static const ExtraID kW61SouthTimeBendingNoGun = 66; +static const ExtraID kW61MessagesOn = 67; +static const ExtraID kW61MessagesOff = 68; +static const ExtraID kW61WhiteMessage = 69; +static const ExtraID kW61WalchekMessage = 70; +static const ExtraID kW61WalchekEasterEgg1 = 71; +static const ExtraID kW62SouthPlasmaRobotAppears = 72; +static const ExtraID kW62ZoomToRobot = 73; +static const ExtraID kW62ZoomOutFromRobot = 74; +static const ExtraID kW62PlasmaDodgeSurvive = 75; +static const ExtraID kW62PlasmaDodgeDie = 76; +static const ExtraID kW65SouthSinclairLecture = 77; +static const ExtraID kW73WestPeopleCrossing = 78; +static const ExtraID kW73WestPeopleCrossingView = 79; +static const ExtraID kW0ZSpottedByWomen = 80; +static const ExtraID kW95RobotShoots = 81; +static const ExtraID kW98MorphsToRobot = 82; +static const ExtraID kW98RobotShoots = 83; +static const ExtraID kW98RobotShocked = 84; +static const ExtraID kW98RobotGassed = 85; +static const ExtraID kW98RobotHeadOpensDark = 86; +static const ExtraID kW98RobotHead000Dark = 87; +static const ExtraID kW98RobotHead001Dark = 88; +static const ExtraID kW98RobotHead010Dark = 89; +static const ExtraID kW98RobotHead011Dark = 90; +static const ExtraID kW98RobotHead100Dark = 91; +static const ExtraID kW98RobotHead101Dark = 92; +static const ExtraID kW98RobotHead110Dark = 93; +static const ExtraID kW98RobotHead111Dark = 94; +static const ExtraID kW98RobotHeadClosesDark = 95; +static const ExtraID kW98WestViewWithGunDark = 96; +static const ExtraID kW98WestViewNoGunDark = 97; +static const ExtraID kW98RobotHeadOpensLight = 98; +static const ExtraID kW98RobotHead000Light = 99; +static const ExtraID kW98RobotHead001Light = 100; +static const ExtraID kW98RobotHead010Light = 101; +static const ExtraID kW98RobotHead011Light = 102; +static const ExtraID kW98RobotHead100Light = 103; +static const ExtraID kW98RobotHead101Light = 104; +static const ExtraID kW98RobotHead110Light = 105; +static const ExtraID kW98RobotHead111Light = 106; +static const ExtraID kW98RobotHeadClosesLight = 107; +static const ExtraID kW98WestViewWithGunLight = 108; +static const ExtraID kW98WestViewNoGunLight = 109; + +static const CoordType kMoleculesMovieLeft = kNavAreaLeft + 112; +static const CoordType kMoleculesMovieTop = kNavAreaTop + 40; + +WSC::WSC(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "WSC", kWSCID), + _moleculesMovie(kNoDisplayElement) { + setIsItemTaken(kArgonCanister); + setIsItemTaken(kSinclairKey); + setIsItemTaken(kNitrogenCanister); + setIsItemTaken(kPoisonDart); + setIsItemTaken(kAntidote); + setIsItemTaken(kMachineGun); + setIsItemTaken(kStunGun); + + GameState.setTakenItemID(kArgonPickup, GameState.isTakenItemID(kArgonCanister) && + GameState.isTakenItemID(kSinclairKey)); +} + +uint16 WSC::getDateResID() const { + return kDate2310ID; +} + +void WSC::init() { + Neighborhood::init(); + + _cachedZoomSpot = 0; + _argonSprite = 0; + + // HACK: Fix the drag item for picking up the Sinclair Key Card + HotspotInfoTable::Entry *entry = findHotspotEntry(kWSC02SouthTakeArgonSpotID); + entry->hotspotItem = kArgonPickup; +} + +void WSC::flushGameState() { + g_energyMonitor->saveCurrentEnergyValue(); +} + +void WSC::start() { + if (g_energyMonitor) { + g_energyMonitor->stopEnergyDraining(); + g_energyMonitor->restoreLastEnergyValue(); + _vm->resetEnergyDeathReason(); + g_energyMonitor->startEnergyDraining(); + } + + if (!GameState.getWSCDidPlasmaDodge()) + forceStridingStop(kWSC58, kSouth, kAltWSCNormal); + + Neighborhood::start(); +} + +class PryDoorMessage : public AIPlayMessageAction { +public: + PryDoorMessage() : AIPlayMessageAction("Images/AI/WSC/XW59SD3", false) {} + +protected: + virtual void performAIAction(AIRule *); +}; + +void PryDoorMessage::performAIAction(AIRule *rule) { + if (((PegasusEngine *)g_engine)->playerHasItemID(kShieldBiochip) + && ((PegasusEngine *)g_engine)->getCurrentBiochip()->getObjectID() != kShieldBiochip) + AIPlayMessageAction::performAIAction(rule); +} + +void WSC::setUpAIRules() { + Neighborhood::setUpAIRules(); + + if (g_AIArea) { + AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/WSC/XW1WB1", false); + AILastExtraCondition *extraCondition = new AILastExtraCondition(kWSCDartScan1); + AIRule *rule = new AIRule(extraCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + AILocationCondition *locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC06, kNorth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC10, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC28, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC49, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC65, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC73, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC79, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/WSC/XW59SD1", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC58, kSouth)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + + PryDoorMessage *pryDoorMessage = new PryDoorMessage(); + AIDoorOpenedCondition *doorCondition = new AIDoorOpenedCondition(MakeRoomView(kWSC58, kSouth)); + rule = new AIRule(doorCondition, pryDoorMessage); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/WSC/XW61E", false); + AIHasItemCondition *itemCondition = new AIHasItemCondition(kMachineGun); + rule = new AIRule(itemCondition, messageAction); + g_AIArea->addAIRule(rule); + + messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB1E", false); + locCondition = new AILocationCondition(1); + locCondition->addLocation(MakeRoomView(kWSC95, kWest)); + rule = new AIRule(locCondition, messageAction); + g_AIArea->addAIRule(rule); + } +} + +Common::String WSC::getBriefingMovie() { + return "Images/AI/WSC/XWO"; +} + +Common::String WSC::getEnvScanMovie() { + RoomID room = GameState.getCurrentRoom(); + + if (room >= kWSC01 && room <= kWSC04) + return "Images/AI/WSC/XWE1"; + else if (room >= kWSC06 && room <= kWSC58) + return "Images/AI/WSC/XWE2"; + else if (room >= kWSC60 && room <= kWSC61West) + return "Images/AI/WSC/XWE3"; + else if (room >= kWSC64 && room <= kWSC98) + return "Images/AI/WSC/XWE4"; + + return "Images/AI/WSC/XWE5"; +} + +uint WSC::getNumHints() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC10, kWest): + case MakeRoomView(kWSC28, kWest): + case MakeRoomView(kWSC49, kWest): + case MakeRoomView(kWSC65, kSouth): + case MakeRoomView(kWSC75, kSouth): + case MakeRoomView(kWSC79, kWest): + return 2; + case MakeRoomView(kWSC02, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return 3; + else if (!GameState.getScoringGotNitrogenCanister() || + !GameState.getScoringGotSinclairKey()) + return 1; + break; + case MakeRoomView(kWSC03, kNorth): + // WORKAROUND: The original game is missing the first two hint movies and + // just plays nothing in its stead. We'll just return that we have one + // hint available. + if (inSynthesizerGame()) + return 1; + + // fall through + case MakeRoomView(kWSC01, kNorth): + case MakeRoomView(kWSC01, kSouth): + case MakeRoomView(kWSC01, kEast): + case MakeRoomView(kWSC01, kWest): + case MakeRoomView(kWSC02, kNorth): + case MakeRoomView(kWSC02, kEast): + case MakeRoomView(kWSC02, kWest): + case MakeRoomView(kWSC02Morph, kNorth): + case MakeRoomView(kWSC02Morph, kEast): + case MakeRoomView(kWSC02Morph, kWest): + case MakeRoomView(kWSC02Messages, kNorth): + case MakeRoomView(kWSC02Messages, kEast): + case MakeRoomView(kWSC02Messages, kWest): + case MakeRoomView(kWSC03, kSouth): + case MakeRoomView(kWSC03, kEast): + case MakeRoomView(kWSC03, kWest): + case MakeRoomView(kWSC04, kNorth): + case MakeRoomView(kWSC04, kSouth): + case MakeRoomView(kWSC04, kEast): + case MakeRoomView(kWSC04, kWest): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return 3; + break; + case MakeRoomView(kWSC02Messages, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return 3; + else if (!GameState.getScoringGotNitrogenCanister()) + return 1; + break; + case MakeRoomView(kWSC02Morph, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return 3; + else if (!GameState.getScoringGotSinclairKey()) + return 1; + break; + case MakeRoomView(kWSC42, kEast): + if (!GameState.isCurrentDoorOpen()) + return 1; + break; + case MakeRoomView(kWSC58, kSouth): + if (GameState.isCurrentDoorOpen()) { + if (GameState.getWSCDidPlasmaDodge()) + return 0; + else + return 1; + } else if (_vm->playerHasItemID(kCrowbar)) + return 2; + + return 3; + case MakeRoomView(kWSC61, kEast): + if (!GameState.getScoringSawBrochure()) + return 1; + break; + case MakeRoomView(kWSC61, kSouth): + if (!GameState.getScoringSawSinclairEntry1() || + !GameState.getScoringSawSinclairEntry2() || + !GameState.getScoringSawSinclairEntry3()) + return 1; + break; + case MakeRoomView(kWSC98, kWest): + if (getCurrentActivation() == kActivationRobotTurning) + return 1; + break; + } + + return 0; +} + +Common::String WSC::getHintMovie(uint hintNum) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC10, kWest): + case MakeRoomView(kWSC28, kWest): + case MakeRoomView(kWSC49, kWest): + case MakeRoomView(kWSC65, kSouth): + case MakeRoomView(kWSC75, kSouth): + case MakeRoomView(kWSC79, kWest): + if (hintNum == 1) + return "Images/AI/Globals/XGLOB5B"; + + return "Images/AI/Globals/XGLOB5C"; + case MakeRoomView(kWSC02, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); + + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kWSC61, kEast): + case MakeRoomView(kWSC61, kSouth): + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kWSC42, kEast): + if (_vm->playerHasItemID(kSinclairKey)) + return "Images/AI/Globals/XGLOB1A"; + + return "Images/AI/Globals/XGLOB2C"; + case MakeRoomView(kWSC58, kSouth): + switch (hintNum) { + case 1: + if (GameState.isCurrentDoorOpen()) { + // Only get here if we haven't done the plasma dodge game... + if (_vm->playerHasItemID(kShieldBiochip)) + return "Images/AI/Globals/XGLOB1A"; + else + return "Images/AI/Globals/XGLOB3F"; + } else if (_vm->playerHasItemID(kCrowbar)) { + return "Images/AI/Globals/XGLOB1A"; + } + + return "Images/AI/Globals/XGLOB1B"; + case 2: + // Only get here if the door is still locked... + if (_vm->playerHasItemID(kCrowbar)) + return "Images/AI/WSC/XW59SD2"; + + return "Images/AI/Globals/XGLOB2D"; + case 3: + // Only get here if the door is still locked and we don't have the + // crowbar... + return "Images/AI/WSC/XW59SD2"; + } + break; + case MakeRoomView(kWSC03, kNorth): + // WORKAROUND: The original game is missing the first two hint movies and + // just plays nothing in its stead. We just make it the first hint. + if (inSynthesizerGame()) + return "Images/AI/WSC/XW03NH3"; + + // fall through + case MakeRoomView(kWSC01, kNorth): + case MakeRoomView(kWSC01, kSouth): + case MakeRoomView(kWSC01, kEast): + case MakeRoomView(kWSC01, kWest): + case MakeRoomView(kWSC02, kNorth): + case MakeRoomView(kWSC02, kEast): + case MakeRoomView(kWSC02, kWest): + case MakeRoomView(kWSC02Morph, kNorth): + case MakeRoomView(kWSC02Morph, kEast): + case MakeRoomView(kWSC02Morph, kWest): + case MakeRoomView(kWSC02Messages, kNorth): + case MakeRoomView(kWSC02Messages, kEast): + case MakeRoomView(kWSC02Messages, kWest): + case MakeRoomView(kWSC03, kSouth): + case MakeRoomView(kWSC03, kEast): + case MakeRoomView(kWSC03, kWest): + case MakeRoomView(kWSC04, kNorth): + case MakeRoomView(kWSC04, kSouth): + case MakeRoomView(kWSC04, kEast): + case MakeRoomView(kWSC04, kWest): + // analyzer hint + return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); + case MakeRoomView(kWSC02Messages, kSouth): + case MakeRoomView(kWSC02Morph, kSouth): + if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && + !_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && + !GameState.getWSCDesignedAntidote()) + // analyzer hint + return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); + + return "Images/AI/Globals/XGLOB1C"; + case MakeRoomView(kWSC98, kWest): + return "Images/AI/WSC/XW98WH2"; + } + + return ""; +} + +void WSC::prepareForAIHint(const Common::String &movieName) { + if (movieName == "Images/AI/WSC/XW98WH2" && isEventTimerRunning()) + pauseTimer(); +} + +void WSC::cleanUpAfterAIHint(const Common::String &movieName) { + if (movieName == "Images/AI/WSC/XW98WH2" && isEventTimerRunning()) + resumeTimer(); +} + +bool WSC::okayToJump() { + if (GameState.getWSCPoisoned()) { + die(kDeathDidntStopPoison); + return false; + } + + bool result = Neighborhood::okayToJump(); + if (!result) + playSpotSoundSync(kWSCCantTransportIn, kWSCCantTransportOut); + + return result; +} + +TimeValue WSC::getViewTime(const RoomID room, const DirectionConstant direction) { + ExtraID viewExtra = 0xffffffff; + ExtraTable::Entry extra; + + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kWSC01, kWest): + if (!GameState.getWSCSeenTimeStream()) { + getExtraEntry(kWSCArrivalFromTSA, extra); + return extra.movieStart; + } else if (GameState.getWSCPoisoned() && !GameState.getWSCAnsweredAboutDart()) { + viewExtra = kWSCDartScan1; + } + break; + case MakeRoomView(kWSC02Morph, kSouth): + if (GameState.isTakenItemID(kArgonPickup) || GameState.isTakenItemID(kArgonCanister)) + viewExtra = kWSC02SouthViewNoArgon; + break; + case MakeRoomView(kWSC02Messages, kSouth): + if (GameState.isTakenItemID(kNitrogenCanister)) { + if (_privateFlags.getFlag(kWSCPrivateLabMessagesOpenFlag)) + viewExtra = kMessagesViewMachineOnNoNitrogen; + else + viewExtra = kMessagesViewNoNitrogen; + } + break; + case MakeRoomView(kWSC03, kSouth): + if (_privateFlags.getFlag(kWSCDraggingAntidoteFlag)) + viewExtra = kW03SouthViewNoAntidote; + break; + case MakeRoomView(kWSC17, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt17WestFlag)) + viewExtra = kW17WestPeopleCrossingView; + break; + case MakeRoomView(kWSC49, kNorth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt49NorthFlag)) + viewExtra = kW49NorthPeopleCrossingView; + break; + case MakeRoomView(kWSC73, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt73WestFlag)) + viewExtra = kW73WestPeopleCrossingView; + break; + case MakeRoomView(kWSC98, kWest): + if (GameState.getWSCRobotDead()) { + if (GameState.getWSCRobotGone()) { + if (GameState.isTakenItemID(kStunGun)) { + if (GameState.getWSCCatwalkDark()) + viewExtra = kW98WestViewNoGunDark; + else + viewExtra = kW98WestViewNoGunLight; + } else { + if (GameState.getWSCCatwalkDark()) + viewExtra = kW98WestViewWithGunDark; + else + viewExtra = kW98WestViewWithGunLight; + } + } else if (_privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) { + if (GameState.getWSCCatwalkDark()) + viewExtra = kW98RobotHead111Dark; + else + viewExtra = kW98RobotHead111Light; + + if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag)) + viewExtra -= 1; + if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) + viewExtra -= 2; + if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) + viewExtra -= 4; + } else if (GameState.getWSCRobotDead()) { + // Should only happen on loading a saved game, so it can take its time. + if (GameState.getWSCCatwalkDark()) + viewExtra = kW98RobotShocked; + else + viewExtra = kW98RobotGassed; + } + } + break; + } + + if (viewExtra != 0xffffffff) { + getExtraEntry(viewExtra, extra); + return extra.movieEnd - 1; + } + + return Neighborhood::getViewTime(room, direction); +} + +void WSC::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kWSC58, kSouth): + case MakeRoomView(kWSC79, kWest): + if ((flags & kSpotOnTurnMask) != 0) { + spotEntry.clear(); + return; + } + break; + } + + Neighborhood::findSpotEntry(room, direction, flags, spotEntry); +} + +void WSC::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { + Neighborhood::getZoomEntry(id, zoomEntry); + + ExtraTable::Entry extra; + ExtraID zoomExtra = 0xffffffff; + + switch (id) { + case kWSC02SouthMessagesSpotID: + if (GameState.isTakenItemID(kNitrogenCanister)) + zoomExtra = kWSC02MessagesZoomNoNitrogen; + break; + case kWSC02SouthMessagesOutSpotID: + if (GameState.isTakenItemID(kNitrogenCanister)) + zoomExtra = kMessagesZoomOutNoNitrogen; + break; + case kWSC02SouthMorphSpotID: + if (GameState.isTakenItemID(kArgonCanister)) + zoomExtra = kWSC02MorphZoomNoArgon; + break; + case kWSC02SouthMorphOutSpotID: + if (GameState.isTakenItemID(kArgonCanister)) + zoomExtra = kWSC02ZoomOutNoArgon; + break; + case kWSC61SouthSpotID: + if (GameState.isTakenItemID(kMachineGun)) + zoomExtra = kW61SouthZoomInNoGun; + break; + case kWSC61SouthOutSpotID: + if (GameState.isTakenItemID(kMachineGun)) + zoomExtra = kW61SouthZoomOutNoGun; + break; + } + + if (zoomExtra != 0xffffffff) { + getExtraEntry(zoomExtra, extra); + zoomEntry.movieStart = extra.movieStart; + zoomEntry.movieEnd = extra.movieEnd; + } +} + +void WSC::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { + switch (id) { + case kWSCZoomOutFromAnalyzer: + Neighborhood::getExtraEntry(kWSCZoomOutFromAnalyzer, extraEntry); + extraEntry.movieEnd = extraEntry.movieStart + 14 * kWSCFrameDuration; + break; + case kW61WalchekMessage: + if (GameState.getEasterEgg()) + Neighborhood::getExtraEntry(kW61WalchekEasterEgg1, extraEntry); + else + Neighborhood::getExtraEntry(id, extraEntry); + break; + case kW61SouthScreenOnWithGun: + if (GameState.isTakenItemID(kMachineGun)) + Neighborhood::getExtraEntry(id, extraEntry); + else + Neighborhood::getExtraEntry(kW61SouthScreenOnNoGun, extraEntry); + break; + case kW61SouthSmartAlloysWithGun: + if (GameState.isTakenItemID(kMachineGun)) + Neighborhood::getExtraEntry(id, extraEntry); + else + Neighborhood::getExtraEntry(kW61SouthSmartAlloysNoGun, extraEntry); + break; + case kW61SouthMorphingWithGun: + if (GameState.isTakenItemID(kMachineGun)) + Neighborhood::getExtraEntry(id, extraEntry); + else + Neighborhood::getExtraEntry(kW61SouthMorphingNoGun, extraEntry); + break; + case kW61SouthTimeBendingWithGun: + if (GameState.isTakenItemID(kMachineGun)) + Neighborhood::getExtraEntry(id, extraEntry); + else + Neighborhood::getExtraEntry(kW61SouthTimeBendingNoGun, extraEntry); + break; + case kW98RobotHeadOpensLight: + if (GameState.getWSCCatwalkDark()) + Neighborhood::getExtraEntry(kW98RobotHeadOpensDark, extraEntry); + else + Neighborhood::getExtraEntry(id, extraEntry); + break; + default: + Neighborhood::getExtraEntry(id, extraEntry); + break; + } +} + +CanMoveForwardReason WSC::canMoveForward(ExitTable::Entry &entry) { + if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC01, kWest) && + getCurrentActivation() != kActivateHotSpotAlways) + return kCantMoveWatchingDiagnosis; + + return Neighborhood::canMoveForward(entry); +} + +// Also add cases here for compound analyzer... +CanTurnReason WSC::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC01, kWest): + if (getCurrentActivation() != kActivateHotSpotAlways) + return kCantTurnWatchingDiagnosis; + break; + case MakeRoomView(kWSC01, kEast): + if (getCurrentActivation() != kActivateHotSpotAlways) + return kCantTurnWatchingAnalysis; + break; + case MakeRoomView(kWSC03, kNorth): + if (_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag)) + return kCantTurnInMoleculeGame; + break; + } + + return Neighborhood::canTurn(turnDirection, nextDir); +} + +CanOpenDoorReason WSC::canOpenDoor(DoorTable::Entry &entry) { + switch (GameState.getCurrentRoom()) { + case kWSC42: + if (!_privateFlags.getFlag(kWSCPrivateSinclairOfficeOpenFlag)) + return kCantOpenLocked; + break; + case kWSC58: + if (!_privateFlags.getFlag(kWSCPrivate58SouthOpenFlag)) + return kCantOpenLocked; + break; + } + + return Neighborhood::canOpenDoor(entry); +} + +void WSC::bumpIntoWall() { + requestSpotSound(kWSCBumpIntoWallIn, kWSCBumpIntoWallOut, kFilterAllInput, 0); + Neighborhood::bumpIntoWall(); +} + +void WSC::closeDoorOffScreen(const RoomID room, const DirectionConstant) { + Item *keyCard; + + switch (room) { + case kWSC58: + case kWSC62: + case kWSC63: + case kWSC64: + case kWSC85: + case kWSC86: + case kWSC88: + case kWSC89: + playSpotSoundSync(kSlidingDoorCloseIn, kSlidingDoorCloseOut); + break; + case kWSC81: + case kWSC82: + case kWSC92: + case kWSC93: + keyCard = _vm->getAllItems().findItemByID(kKeyCard); + if (keyCard->getItemState() == kFlashlightOn && (GameState.getCurrentRoom() == kWSC81 || + GameState.getCurrentRoom() == kWSC93)) { + keyCard->setItemState(kFlashlightOff); + playSpotSoundSync(kWSCFlashlightClickIn, kWSCFlashlightClickOut); + } else if (keyCard->getItemState() == kFlashlightOff && (GameState.getCurrentRoom() == kWSC82 || + GameState.getCurrentRoom() == kWSC92)) { + keyCard->setItemState(kFlashlightOn); + playSpotSoundSync(kWSCFlashlightClickIn, kWSCFlashlightClickOut); + } + + playSpotSoundSync(kSlimyDoorCloseIn, kSlimyDoorCloseOut); + break; + default: + playSpotSoundSync(kSlimyDoorCloseIn, kSlimyDoorCloseOut); + break; + } +} + +void WSC::cantMoveThatWay(CanMoveForwardReason reason) { + if (reason != kCantMoveWatchingDiagnosis) + Neighborhood::cantMoveThatWay(reason); +} + +void WSC::cantOpenDoor(CanOpenDoorReason reason) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC22, kWest): + playSpotSoundSync(kNakamuraNotHomeIn, kNakamuraNotHomeOut); + break; + case MakeRoomView(kWSC23, kEast): + playSpotSoundSync(kHernandezNotHomeIn, kHernandezNotHomeOut); + break; + case MakeRoomView(kWSC26, kWest): + playSpotSoundSync(kGrailisNotHomeIn, kGrailisNotHomeOut); + break; + case MakeRoomView(kWSC27, kEast): + playSpotSoundSync(kWashingtonNotHomeIn, kWashingtonNotHomeOut); + break; + case MakeRoomView(kWSC32, kWest): + playSpotSoundSync(kTheriaultNotHomeIn, kTheriaultNotHomeOut); + break; + case MakeRoomView(kWSC33, kEast): + playSpotSoundSync(kSullivanNotHomeIn, kSullivanNotHomeOut); + break; + case MakeRoomView(kWSC41, kWest): + playSpotSoundSync(kGlennerNotHomeIn, kGlennerNotHomeOut); + break; + case MakeRoomView(kWSC42, kEast): + playSpotSoundSync(kSinclairNotHomeIn, kSinclairNotHomeOut); + break; + case MakeRoomView(kWSC15, kWest): + case MakeRoomView(kWSC25, kWest): + case MakeRoomView(kWSC33, kWest): + case MakeRoomView(kWSC41, kEast): + case MakeRoomView(kWSC46, kWest): + playSpotSoundSync(kWSCLabClosedIn, kWSCLabClosedOut); + break; + default: + Neighborhood::cantOpenDoor(reason); + break; + } +} + +void WSC::doorOpened() { + Neighborhood::doorOpened(); + + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC42, kEast): + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kSinclairKey)); + break; + case MakeRoomView(kWSC58, kSouth): + GameState.setScoringUsedCrowBarInWSC(); + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kCrowbar)); + break; + case MakeRoomView(kWSC06, kNorth): + case MakeRoomView(kWSC79, kWest): + die(kDeathArrestedInWSC); + break; + case MakeRoomView(kWSC60, kWest): + if (_vm->itemInInventory(kMachineGun)) + startExtraSequence(kNerdAtTheDoor2, kExtraCompletedFlag, kFilterNoInput); + else if (!GameState.getWSCSeenNerd()) + startExtraSequence(kNerdAtTheDoor1, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC95, kWest): + GameState.setScoringOpenedCatwalk(); + scheduleEvent(kGawkAtRobotTime, 1, kTimerEventPlayerGawkingAtRobot); + break; + } +} + +void WSC::turnLeft() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC17, kNorth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt17WestFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, true); + break; + case MakeRoomView(kWSC49, kEast): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt49NorthFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, true); + break; + case MakeRoomView(kWSC73, kNorth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt73WestFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, true); + break; + case MakeRoomView(kWSC73, kWest): + if (!GameState.getWSCBeenAtWSC93()) + setCurrentAlternate(kAltWSCW0ZDoorOpen); + break; + case MakeRoomView(kWSC95, kWest): + cancelEvent(); + break; + } + + Neighborhood::turnLeft(); +} + +void WSC::turnRight() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC17, kSouth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt17WestFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, true); + break; + case MakeRoomView(kWSC49, kWest): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt49NorthFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, true); + break; + case MakeRoomView(kWSC73, kSouth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt73WestFlag) && _vm->getRandomNumber(2) == 0) + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, true); + break; + case MakeRoomView(kWSC73, kEast): + if (!GameState.getWSCBeenAtWSC93()) + setCurrentAlternate(kAltWSCW0ZDoorOpen); + break; + case MakeRoomView(kWSC95, kWest): + cancelEvent(); + break; + } + + Neighborhood::turnRight(); +} + +void WSC::moveForward() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC19, kNorth): + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt19NorthFlag)) + setCurrentAlternate(kAltWSCPeopleAtW19North); + break; + case MakeRoomView(kWSC95, kWest): + cancelEvent(); + break; + } + + Neighborhood::moveForward(); +} + +void WSC::zoomTo(const Hotspot *hotspot) { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC02Messages, kSouth): + if (_privateFlags.getFlag(kWSCPrivateLabMessagesOpenFlag)) { + _cachedZoomSpot = hotspot; + if (GameState.isTakenItemID(kNitrogenCanister)) + startExtraSequence(kMessagesOffNoNitrogen, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMessagesOff, kExtraCompletedFlag, kFilterNoInput); + return; + } + break; + case MakeRoomView(kWSC61West, kWest): + if (GameState.getWSCOfficeMessagesOpen()) { + _cachedZoomSpot = hotspot; + startExtraSequence(kW61MessagesOff, kExtraCompletedFlag, kFilterNoInput); + return; + } + break; + case MakeRoomView(kWSC61South, kSouth): + if (_privateFlags.getFlag(kWSCPrivateOfficeLogOpenFlag)) { + _cachedZoomSpot = hotspot; + if (GameState.isTakenItemID(kMachineGun)) + startExtraSequence(kW61SouthScreenOffNoGun, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW61SouthScreenOffWithGun, kExtraCompletedFlag, kFilterNoInput); + return; + } + break; + } + + Neighborhood::zoomTo(hotspot); +} + +void WSC::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) { + if (extraID == kW61Brochure) + loadLoopSound1(""); + + Neighborhood::startExtraSequence(extraID, flags, interruptionFilter); +} + +int16 WSC::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { + int16 angle = Neighborhood::getStaticCompassAngle(room, dir); + + switch (room) { + case kWSC02Messages: + angle -= 50; + break; + case kWSC02Morph: + angle += 5; + break; + case kWSC60East: + angle -= 10; + break; + case kWSC66: + angle -= kAuditoriumAngleOffset; + break; + case kWSC67: + angle += kAuditoriumAngleOffset; + break; + case kWSC68: + angle -= kAuditoriumAngleOffset * 2; + break; + case kWSC69: + angle += kAuditoriumAngleOffset * 2; + break; + case kWSC70: + angle -= kAuditoriumAngleOffset * 3; + break; + case kWSC71: + angle += kAuditoriumAngleOffset * 3; + break; + case kWSC72: + if (dir == kEast || dir == kWest) + angle -= kAuditoriumAngleOffset * 4; + break; + case kWSC73: + if (dir == kEast || dir == kWest) + angle += kAuditoriumAngleOffset * 4; + break; + } + + return angle; +} + +void WSC::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { + Neighborhood::getExitCompassMove(exitEntry, compassMove); + + if (exitEntry.room == kWSC65 && exitEntry.direction == kSouth) { + compassMove.insertFaderKnot(exitEntry.movieStart + 100 * kWSCFrameDuration, 180); + compassMove.insertFaderKnot(exitEntry.movieStart + 108 * kWSCFrameDuration, 150); + compassMove.insertFaderKnot(exitEntry.movieEnd, 150); + } +} + +void WSC::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { + switch (entry.extra) { + case kW61Brochure: + compassMove.insertFaderKnot(entry.movieStart + 15 * kWSCFrameDuration, 85); + compassMove.insertFaderKnot(entry.movieEnd - 15 * kWSCFrameDuration, 85); + compassMove.insertFaderKnot(entry.movieEnd, 90); + break; + default: + Neighborhood::getExtraCompassMove(entry, compassMove); + break; + } +} + +void WSC::loadAmbientLoops() { + RoomID room = GameState.getCurrentRoom(); + + if (room >= kWSC01 && room <= kWSC04) { + if (GameState.getWSCSeenTimeStream()) + loadLoopSound1("Sounds/World Science Center/WLabLoop.22K.AIFF", 0x100 / 2); + } else if ((room >= kWSC06 && room <= kWSC58) || (room >= kWSC62 && room <= kWSC63)) + loadLoopSound1("Sounds/World Science Center/Organic Walls.22K.AIFF", 0x100 / 2); + else if (room >= kWSC82 && room <= kWSC92) + loadLoopSound1("Sounds/World Science Center/Creature Feature.22K.AIFF"); + else if ((room >= kWSC60 && room <= kWSC61West) || (room >= kWSC64 && room <= kWSC81) || + (room >= kWSC93 && room <= kWSC97)) + loadLoopSound1("Sounds/World Science Center/The Other Side.22K.AIFF", 0x100 / 12); + else if (room == kWSC98) + loadLoopSound1("Sounds/World Science Center/WCatLoop.22K.AIFF"); +} + +void WSC::checkContinuePoint(const RoomID room, const DirectionConstant direction) { + switch (MakeRoomView(room, direction)) { + case MakeRoomView(kWSC07, kNorth): + case MakeRoomView(kWSC11, kSouth): + case MakeRoomView(kWSC13, kSouth): + case MakeRoomView(kWSC13, kWest): + case MakeRoomView(kWSC16, kWest): + case MakeRoomView(kWSC17, kEast): + case MakeRoomView(kWSC19, kWest): + case MakeRoomView(kWSC28, kNorth): + case MakeRoomView(kWSC28, kSouth): + case MakeRoomView(kWSC28, kEast): + case MakeRoomView(kWSC28, kWest): + case MakeRoomView(kWSC29, kNorth): + case MakeRoomView(kWSC29, kSouth): + case MakeRoomView(kWSC29, kEast): + case MakeRoomView(kWSC29, kWest): + case MakeRoomView(kWSC40, kEast): + case MakeRoomView(kWSC42, kEast): + case MakeRoomView(kWSC49, kWest): + case MakeRoomView(kWSC49, kNorth): + case MakeRoomView(kWSC50, kNorth): + case MakeRoomView(kWSC55, kEast): + case MakeRoomView(kWSC65, kSouth): + case MakeRoomView(kWSC65, kEast): + case MakeRoomView(kWSC65, kWest): + case MakeRoomView(kWSC72, kEast): + case MakeRoomView(kWSC72, kSouth): + case MakeRoomView(kWSC73, kWest): + case MakeRoomView(kWSC73, kSouth): + case MakeRoomView(kWSC79, kWest): + case MakeRoomView(kWSC81, kEast): + case MakeRoomView(kWSC93, kNorth): + case MakeRoomView(kWSC95, kWest): + makeContinuePoint(); + break; + case MakeRoomView(kWSC58, kSouth): + if (!GameState.getWSCDidPlasmaDodge()) + makeContinuePoint(); + break; + case MakeRoomView(kWSC60, kWest): + if (_vm->playerHasItemID(kMachineGun)) + makeContinuePoint(); + break; + } +} + +void WSC::arriveAt(const RoomID room, const DirectionConstant dir) { + switch (MakeRoomView(room, dir)) { + case MakeRoomView(kWSC60, kNorth): + case MakeRoomView(kWSC60, kSouth): + case MakeRoomView(kWSC60, kEast): + case MakeRoomView(kWSC60, kWest): + case MakeRoomView(kWSC60East, kNorth): + case MakeRoomView(kWSC60East, kSouth): + case MakeRoomView(kWSC60East, kEast): + case MakeRoomView(kWSC60East, kWest): + case MakeRoomView(kWSC60North, kNorth): + case MakeRoomView(kWSC60North, kSouth): + case MakeRoomView(kWSC60North, kEast): + case MakeRoomView(kWSC60North, kWest): + case MakeRoomView(kWSC61, kNorth): + case MakeRoomView(kWSC61, kSouth): + case MakeRoomView(kWSC61, kEast): + case MakeRoomView(kWSC61, kWest): + case MakeRoomView(kWSC61South, kNorth): + case MakeRoomView(kWSC61South, kSouth): + case MakeRoomView(kWSC61South, kEast): + case MakeRoomView(kWSC61South, kWest): + case MakeRoomView(kWSC61West, kNorth): + case MakeRoomView(kWSC61West, kSouth): + case MakeRoomView(kWSC61West, kEast): + case MakeRoomView(kWSC61West, kWest): + if (GameState.isTakenItemID(kMachineGun)) + setCurrentAlternate(kAltWSCTookMachineGun); + else + setCurrentAlternate(kAltWSCNormal); + break; + case MakeRoomView(kWSC73, kSouth): + case MakeRoomView(kWSC75, kNorth): + case MakeRoomView(kWSC75, kSouth): + case MakeRoomView(kWSC75, kEast): + case MakeRoomView(kWSC75, kWest): + if (!GameState.getWSCBeenAtWSC93()) + setCurrentAlternate(kAltWSCW0ZDoorOpen); + break; + } + + Neighborhood::arriveAt(room, dir); + + switch (MakeRoomView(room, dir)) { + case MakeRoomView(kWSC01, kWest): + if (!GameState.getWSCSeenTimeStream()) { + requestExtraSequence(kWSCArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kWSCShotByRobot, 0, kFilterNoInput); + requestExtraSequence(kWSCDartScan1, kExtraCompletedFlag, kFilterNoInput); + } else if (GameState.getWSCPoisoned() && !GameState.getWSCAnsweredAboutDart()) { + setCurrentActivation(kActivationShotByRobot); + } + break; + case MakeRoomView(kWSC01, kEast): + if (GameState.getWSCDartInAnalyzer()) + requestExtraSequence(kWSCDropDartIntoAnalyzer, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC02Morph, kSouth): + setCurrentActivation(kActivationMorphScreenOff); + break; + case MakeRoomView(kWSC03, kNorth): + setCurrentActivation(kActivationW03NorthOff); + break; + case MakeRoomView(kWSC03, kSouth): + if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote()) + setCurrentActivation(kActivationReadyForSynthesis); + break; + case MakeRoomView(kWSC16, kNorth): + if (getCurrentAlternate() == kAltWSCPeopleAtW19North) { + setCurrentAlternate(kAltWSCNormal); + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt19NorthFlag, true); + } + break; + case MakeRoomView(kWSC07, kSouth): + case MakeRoomView(kWSC56, kNorth): + setCurrentActivation(kActivationReadyForMap); + break; + case MakeRoomView(kWSC42, kWest): + setCurrentAlternate(kAltWSCNormal); + break; + case MakeRoomView(kWSC42, kEast): + _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false); + setCurrentActivation(kActivationSinclairOfficeLocked); + break; + case MakeRoomView(kWSC58, kSouth): + setCurrentActivation(kActivationW58SouthDoorLocked); + _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false); + break; + case MakeRoomView(kWSC60, kEast): + GameState.setScoringEnteredSinclairOffice(); + break; + case MakeRoomView(kWSC61West, kWest): + setCurrentActivation(kActivationW61MessagesOff); + break; + case MakeRoomView(kWSC61South, kSouth): + setCurrentActivation(kActivationW61SouthOff); + break; + case MakeRoomView(kWSC62, kSouth): + if (!GameState.getWSCDidPlasmaDodge()) { + g_AIArea->lockAIOut(); + loadLoopSound1("Sounds/World Science Center/Plasma Rock.22K.AIFF"); + requestExtraSequence(kW62SouthPlasmaRobotAppears, 0, kFilterNoInput); + requestExtraSequence(kW62ZoomToRobot, 0, kFilterNoInput); + requestExtraSequence(kW62ZoomOutFromRobot, kExtraCompletedFlag, kFilterNoInput); + } + break; + case MakeRoomView(kWSC65Screen, kSouth): + if (!GameState.getWSCSeenSinclairLecture()) { + GameState.setWSCSeenSinclairLecture(true); + startExtraSequence(kW65SouthSinclairLecture, kExtraCompletedFlag, kFilterAllInput); + } + break; + case MakeRoomView(kWSC66, kWest): + case MakeRoomView(kWSC67, kEast): + if (!GameState.getWSCHeardPage2()) { + playSpotSoundSync(kPaging2In, kPaging2Out); + GameState.setWSCHeardPage2(true); + } + case MakeRoomView(kWSC10, kNorth): + case MakeRoomView(kWSC26, kSouth): + case MakeRoomView(kWSC72, kWest): + case MakeRoomView(kWSC83, kWest): + if (!GameState.getWSCHeardCheckIn()) { + playSpotSoundSync(kCheckInIn, kCheckInOut); + GameState.setWSCHeardCheckIn(true); + } + break; + case MakeRoomView(kWSC0Z, kSouth): + if (getCurrentAlternate() == kAltWSCW0ZDoorOpen) + turnLeft(); + break; + case MakeRoomView(kWSC93, kEast): + GameState.setWSCBeenAtWSC93(true); + break; + case MakeRoomView(kWSC98, kWest): + if (!GameState.getWSCRobotDead()) { + scheduleEvent(kGawkAtRobotTime2, 1, kTimerEventPlayerGawkingAtRobot2); + setCurrentActivation(kActivationRobotTurning); + if (g_AIArea) + g_AIArea->checkMiddleArea(); + } else if (!GameState.getWSCRobotGone()) { + setCurrentActivation(kActivationRobotDead); + } else { + if (GameState.getWSCCatwalkDark()) { + // Change the gun hot spot... + _vm->getAllHotspots().setHotspotRect(kW98StunGunSpotID, Common::Rect(181 + kNavAreaLeft, + 99 + kNavAreaTop,372 + kNavAreaLeft, 149 + kNavAreaTop)); + } + setCurrentActivation(kActivationRobotGone); + } + break; + case MakeRoomView(kWSCDeathRoom, kNorth): + case MakeRoomView(kWSCDeathRoom, kSouth): + case MakeRoomView(kWSCDeathRoom, kEast): + case MakeRoomView(kWSCDeathRoom, kWest): + die(kDeathArrestedInWSC); + break; + } + + checkPeopleCrossing(); + setUpPoison(); +} + +void WSC::turnTo(const DirectionConstant direction) { + Neighborhood::turnTo(direction); + + switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { + case MakeRoomView(kWSC01, kNorth): + case MakeRoomView(kWSC01, kSouth): + GameState.setWSCAnalyzerOn(false); + break; + case MakeRoomView(kWSC03, kNorth): + setCurrentActivation(kActivationW03NorthOff); + break; + case MakeRoomView(kWSC03, kSouth): + if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote()) + setCurrentActivation(kActivationReadyForSynthesis); + break; + case MakeRoomView(kWSC07, kSouth): + case MakeRoomView(kWSC56, kNorth): + setCurrentActivation(kActivationReadyForMap); + break; + case MakeRoomView(kWSC18, kSouth): + case MakeRoomView(kWSC57, kEast): + case MakeRoomView(kWSC75, kEast): + case MakeRoomView(kWSC90, kSouth): + if (!GameState.getWSCHeardCheckIn()) { + playSpotSoundSync(kCheckInIn, kCheckInOut); + GameState.setWSCHeardCheckIn(true); + } + break; + case MakeRoomView(kWSC56, kSouth): + if (!GameState.getWSCHeardPage1()) { + playSpotSoundSync(kPaging1In, kPaging1Out); + GameState.setWSCHeardPage1(true); + } + // clone2727 says: This falls through?!??! WTF? + case MakeRoomView(kWSC42, kEast): + _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false); + setCurrentActivation(kActivationSinclairOfficeLocked); + break; + case MakeRoomView(kWSC58, kSouth): + setCurrentActivation(kActivationW58SouthDoorLocked); + _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false); + break; + case MakeRoomView(kWSC73, kWest): + setCurrentAlternate(kAltWSCNormal); + break; + case MakeRoomView(kWSC0Z, kEast): + if (getCurrentAlternate() == kAltWSCW0ZDoorOpen) + startExtraSequence(kW0ZSpottedByWomen, kExtraCompletedFlag, kFilterNoInput); + break; + } + + checkPeopleCrossing(); +} + +void WSC::receiveNotification(Notification *notification, const NotificationFlags flags) { + int32 currentEnergy; + Item *item; + + if (flags & kExtraCompletedFlag) { + _interruptionFilter = kFilterAllInput; + + switch (_lastExtra) { + case kWSCArrivalFromTSA: + GameState.setWSCSeenTimeStream(true); + loadAmbientLoops(); + break; + case kWSCDartScan1: + setCurrentActivation(kActivationShotByRobot); + GameState.setWSCPoisoned(true); + setUpPoison(); + makeContinuePoint(); + break; + case kWSCDartScan2: + _vm->addItemToInventory((InventoryItem *)_vm->getAllItems().findItemByID(kPoisonDart)); + GameState.setScoringRemovedDart(); + GameState.setWSCRemovedDart(true); + setUpPoison(); + g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XW1WB2", false, kHintInterruption); + // Fall through... + case kWSCDartScanNo: + GameState.setWSCAnsweredAboutDart(true); + startExtraSequence(kWSCDartScan3, kExtraCompletedFlag, kFilterNoInput); + break; + case kWSCDartScan3: + setCurrentActivation(kActivateHotSpotAlways); + break; + case kWSCAnalyzerPowerUp: + case kWSCAnalyzerPowerUpWithDart: + GameState.setWSCAnalyzerOn(true); + break; + case kWSCDropDartIntoAnalyzer: + setCurrentActivation(kActivationZoomedInToAnalyzer); + break; + case kWSCAnalyzeDart: + GameState.setWSCAnalyzedDart(true); + GameState.setScoringAnalyzedDart(); + break; + case kWSCZoomOutFromAnalyzer: + setCurrentActivation(kActivateHotSpotAlways); + GameState.setWSCAnalyzerOn(false); + GameState.setWSCDartInAnalyzer(false); + updateViewFrame(); + break; + case kMessagesMovedToOffice: + case kMessagesMovedToOfficeNoNitrogen: + _privateFlags.setFlag(kWSCPrivateLabMessagesOpenFlag, true); + GameState.setScoringPlayedWithMessages(); + break; + case kMessagesOff: + case kMessagesOffNoNitrogen: + _privateFlags.setFlag(kWSCPrivateLabMessagesOpenFlag, false); + if (_cachedZoomSpot) { + zoomTo(_cachedZoomSpot); + _cachedZoomSpot = 0; + } + break; + case kWSC02TurnOnMorphScreen: + setCurrentActivation(kActivationReadyForMorph); + break; + case kWSC02DropToMorphExperiment: + loopExtraSequence(kWSC02MorphLoop, kExtraCompletedFlag); + setCurrentActivation(kActivationMorphLooping); + break; + case kWSC02MorphLoop: + if (_privateFlags.getFlag(kWSCPrivateInterruptedMorphFlag)) + startExtraSequence(kWSC02MorphInterruption, kExtraCompletedFlag, kFilterNoInput); + else + scheduleNavCallBack(kExtraCompletedFlag); + break; + case kWSC02MorphInterruption: + setCurrentActivation(kActivationMorphInterrupted); + GameState.setScoringSawMorphExperiment(); + break; + case kWSC02TurnOffMorphScreen: + setCurrentActivation(kActivationMorphScreenOff); + GameState.setWSCSawMorph(true); + break; + case kW03NorthActivate: + if (GameState.getWSCAnalyzedDart() && !GameState.getWSCDesignedAntidote()) + startExtraSequence(kW03NorthGetData, kExtraCompletedFlag, kFilterNoInput); + else + setCurrentActivation(kActivateHotSpotAlways); + break; + case kW03NorthGetData: + setCurrentActivation(kActivationW03NorthReadyForInstructions); + break; + case kW03NorthInstructions: + setCurrentActivation(kActivationW03NorthSawInstructions); + break; + case kW03NorthPrepMolecule1: + setUpMoleculeGame(); + break; + case kW03NorthPrepMolecule2: + case kW03NorthPrepMolecule3: + nextMoleculeGameLevel(); + break; + case kW03NorthFinishSynthesis: + setCurrentActivation(kActivateHotSpotAlways); + _privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, false); + GameState.setWSCDesignedAntidote(true); + GameState.setScoringBuiltAntidote(); + break; + case kW03SouthCreateAntidote: + setCurrentActivation(kActivationSynthesizerLooping); + loopExtraSequence(kW03SouthAntidoteLoop); + break; + case kW03SouthDeactivate: + setCurrentActivation(kActivateHotSpotAlways); + break; + case kWSC07SouthMap: + case kWSC56SouthMap: + setCurrentActivation(kActivateHotSpotAlways); + GameState.setScoringSawWSCDirectory(); + break; + case kNerdAtTheDoor1: + GameState.setWSCSeenNerd(true); + break; + case kNerdAtTheDoor2: + die(kDeathArrestedInWSC); + break; + case kW61Brochure: + GameState.setScoringSawBrochure(); + loadAmbientLoops(); + break; + case kW61SouthSmartAlloysWithGun: + case kW61SouthSmartAlloysNoGun: + GameState.setScoringSawSinclairEntry1(); + break; + case kW61SouthMorphingWithGun: + case kW61SouthMorphingNoGun: + GameState.setScoringSawSinclairEntry2(); + break; + case kW61SouthTimeBendingWithGun: + case kW61SouthTimeBendingNoGun: + GameState.setScoringSawSinclairEntry3(); + break; + case kW61MessagesOn: + GameState.setWSCOfficeMessagesOpen(true); + setCurrentActivation(kActivationW61MessagesOn); + break; + case kW61MessagesOff: + GameState.setWSCOfficeMessagesOpen(false); + setCurrentActivation(kActivationW61MessagesOff); + if (_cachedZoomSpot) { + zoomTo(_cachedZoomSpot); + _cachedZoomSpot = 0; + } + break; + case kW61SouthScreenOnWithGun: + case kW61SouthScreenOnNoGun: + _privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, true); + setCurrentActivation(kActivationW61SouthOn); + break; + case kW61SouthScreenOffWithGun: + case kW61SouthScreenOffNoGun: + _privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, false); + setCurrentActivation(kActivationW61SouthOff); + if (_cachedZoomSpot) { + zoomTo(_cachedZoomSpot); + _cachedZoomSpot = 0; + } + break; + case kW62ZoomOutFromRobot: + // Handle action queue before starting new movie sequences. + Neighborhood::receiveNotification(notification, flags); + _energyDrainRate = g_energyMonitor->getEnergyDrainRate(); + g_energyMonitor->setEnergyDrainRate(0); + currentEnergy = g_energyMonitor->getCurrentEnergy(); + _vm->setEnergyDeathReason(kDeathHitByPlasma); + + if (GameState.getShieldOn()) + currentEnergy -= kPlasmaEnergyWithShield; + else + currentEnergy -= kPlasmaEnergyNoShield; + + if (currentEnergy <= 0) + startExtraSequence(kW62PlasmaDodgeDie, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW62PlasmaDodgeSurvive, kExtraCompletedFlag, kFilterNoInput); + + scheduleEvent(kPlasmaImpactTime, kOneTickPerSecond, kTimerEventPlasmaHit); + break; + case kW62PlasmaDodgeDie: + g_energyMonitor->setEnergyValue(0); + break; + case kW62PlasmaDodgeSurvive: + if (GameState.getShieldOn()) { + g_shield->setItemState(kShieldNormal); + g_energyMonitor->drainEnergy(kPlasmaEnergyWithShield); + } else { + g_energyMonitor->drainEnergy(kPlasmaEnergyNoShield); + } + + g_energyMonitor->setEnergyDrainRate(_energyDrainRate); + g_AIArea->unlockAI(); + GameState.setScoringFinishedPlasmaDodge(); + GameState.setWSCDidPlasmaDodge(true); + restoreStriding(kWSC58, kSouth, kAltWSCNormal); + loadAmbientLoops(); + break; + case kW0ZSpottedByWomen: + die(kDeathArrestedInWSC); + break; + case kW17WestPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt17WestFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, false); + break; + case kW21SouthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt21SouthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, true); + break; + case kW24SouthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt24SouthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, true); + break; + case kW34EastPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt34EastFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, true); + break; + case kW36WestPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt36WestFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, true); + break; + case kW38NorthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt38NorthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, true); + break; + case kW46SouthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt46SouthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, true); + break; + case kW49NorthPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt49NorthFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, false); + break; + case kW73WestPeopleCrossing: + _privateFlags.setFlag(kWSCPrivateSeenPeopleAt73WestFlag, true); + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, false); + break; + case kW95RobotShoots: + case kW98RobotShoots: + die(kDeathShotOnCatwalk); + break; + case kW98MorphsToRobot: + if (_argonSprite) { + delete _argonSprite; _argonSprite = 0; + startExtraSequence(kW98RobotGassed, kExtraCompletedFlag, kFilterNoInput); + } else if (_privateFlags.getFlag(kWSCPrivateClickedCatwalkCableFlag)) { + startExtraSequence(kW98RobotShocked, kExtraCompletedFlag, kFilterNoInput); + } else { + startExtraSequence(kW98RobotShoots, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kW98RobotShocked: + GameState.setWSCCatwalkDark(true); + // Change the gun hot spot... + _vm->getAllHotspots().setHotspotRect(kW98StunGunSpotID, Common::Rect(181 + kNavAreaLeft, 99 + kNavAreaTop, + 372 + kNavAreaLeft, 149 + kNavAreaTop)); + setCurrentActivation(kActivationRobotDead); + GameState.setWSCRobotDead(true); + GameState.setScoringStoppedWSCRobot(); + + // Video is not present + //g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption); + break; + case kW98RobotGassed: + item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister); + _vm->addItemToInventory((InventoryItem *)item); + setCurrentActivation(kActivationRobotDead); + GameState.setWSCRobotDead(true); + GameState.setScoringStoppedWSCRobot(); + + // Video is not present + //g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption); + break; + case kW98RobotHeadOpensLight: + case kW98RobotHeadOpensDark: + setCurrentActivation(kActivationWSCRobotHeadOpen); + _privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, true); + break; + case kW98RobotHeadClosesDark: + case kW98RobotHeadClosesLight: + setCurrentActivation(kActivationRobotGone); + _privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, false); + GameState.setWSCRobotGone(true); + break; + } + } + + Neighborhood::receiveNotification(notification, flags); + g_AIArea->checkMiddleArea(); +} + +void WSC::timerExpired(const uint32 event) { + switch (event) { + case kTimerEventPlasmaHit: + if (GameState.getShieldOn()) + g_shield->setItemState(kShieldPlasma); + break; + case kTimerEventPlayerGawkingAtRobot: + startExtraSequence(kW95RobotShoots, kExtraCompletedFlag, kFilterNoInput); + break; + case kTimerEventPlayerGawkingAtRobot2: + startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); + break; + } +} + +void WSC::setUpMoleculeGame() { + _privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, true); + setCurrentActivation(kActivationW03NorthInGame); + initOneMovie(&_moleculesMovie, "Images/World Science Center/Molecules.movie", + kWSCMoleculesMovieOrder, kMoleculesMovieLeft, kMoleculesMovieTop, true); + _moleculesMovie.redrawMovieWorld(); + _moleculeBin.initMoleculeBin(); + _moleculeGameLevel = 0; + nextMoleculeGameLevel(); +} + +void WSC::nextMoleculeGameLevel() { + _moleculeGameLevel++; + + for (byte i = 0; i < 6; ++i) + _levelArray[i] = i; + + _vm->shuffleArray((int32 *)_levelArray, 6); + _moleculeBin.setBinLayout(_levelArray); + startMoleculeGameLevel(); +} + +void WSC::startMoleculeGameLevel() { + _moleculeBin.resetBin(); + _numCorrect = 0; + _moleculesMovie.stop(); + _moleculesMovie.setFlags(0); + _moleculesMovie.setSegment(s_moleculeLoopTimes[0], s_moleculeLoopTimes[0] + kMoleculeLoopTime); + _moleculesMovie.setTime(s_moleculeLoopTimes[0]); + _moleculesMovie.setFlags(kLoopTimeBase); + _moleculesMovie.show(); + + switch (_moleculeGameLevel) { + case 1: + playSpotSoundSync(kWSCMolecule1In, kWSCMolecule1Out); + break; + case 2: + playSpotSoundSync(kWSCMolecule2In, kWSCMolecule2Out); + break; + case 3: + playSpotSoundSync(kWSCMolecule3In, kWSCMolecule3Out); + break; + } + + _moleculesMovie.start(); +} + +void WSC::moleculeGameClick(const HotSpotID id) { + uint32 molecule = id - kWSC03NorthMolecule1SpotID; + + _moleculeBin.highlightMolecule(molecule); + _moleculeBin.selectMolecule(molecule); + + if (molecule == _levelArray[_numCorrect]) { + playSpotSoundSync(kWSCClick2In, kWSCClick2Out); + _numCorrect++; + _moleculesMovie.stop(); + _moleculesMovie.setFlags(0); + + TimeValue time = _moleculesMovie.getTime(); + _moleculesMovie.setSegment(s_moleculeLoopTimes[_numCorrect], s_moleculeLoopTimes[_numCorrect] + kMoleculeLoopTime); + _moleculesMovie.setTime(s_moleculeLoopTimes[_numCorrect] + time - s_moleculeLoopTimes[_numCorrect - 1]); + + if (_numCorrect == 6) { + _moleculesMovie.start(); + + while (_moleculesMovie.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _moleculesMovie.stop(); + _moleculesMovie.hide(); + + switch (_moleculeGameLevel) { + case 1: + startExtraSequence(kW03NorthPrepMolecule2, kExtraCompletedFlag, kFilterNoInput); + break; + case 2: + startExtraSequence(kW03NorthPrepMolecule3, kExtraCompletedFlag, kFilterNoInput); + break; + case 3: + _moleculesMovie.releaseMovie(); + _moleculeBin.cleanUpMoleculeBin(); + requestExtraSequence(kW03NorthFinishSynthesis, kExtraCompletedFlag, kFilterNoInput); + break; + } + } else { + _moleculesMovie.setFlags(kLoopTimeBase); + _moleculesMovie.start(); + } + } else { + // FAIL + playSpotSoundSync(kWSCClick3In, kWSCClick3Out); + + _moleculesMovie.stop(); + _moleculesMovie.setFlags(0); + _moleculesMovie.start(); + + while (_moleculesMovie.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _moleculesMovie.stop(); + _moleculesMovie.setFlags(0); + _moleculesMovie.setSegment(s_moleculeFailTimes[_numCorrect], s_moleculeFailTimes[_numCorrect] + kMoleculeFailTime); + _moleculesMovie.setTime(s_moleculeFailTimes[_numCorrect]); + _moleculesMovie.start(); + + + while (_moleculesMovie.isRunning()) { + _vm->checkCallBacks(); + _vm->refreshDisplay(); + _vm->_system->delayMillis(10); + } + + _moleculesMovie.stop(); + startMoleculeGameLevel(); + } +} + +void WSC::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { + Neighborhood::activateOneHotspot(entry, hotspot); + + Item *argonCanister; + + switch (hotspot->getObjectID()) { + case kWSCTurnOnAnalyzerSpotID: + if (GameState.getWSCAnalyzerOn()) + hotspot->setInactive(); + break; + case kWSC02SouthTakeArgonSpotID: + if (!GameState.getWSCSawMorph() || GameState.isTakenItemID(kArgonCanister)) + hotspot->setInactive(); + break; + case kWSC02ActivateMorphScreenSpotID: + if (GameState.getWSCSawMorph()) + hotspot->setInactive(); + break; + case kWSC03NorthMolecule1SpotID: + case kWSC03NorthMolecule2SpotID: + case kWSC03NorthMolecule3SpotID: + case kWSC03NorthMolecule4SpotID: + case kWSC03NorthMolecule5SpotID: + case kWSC03NorthMolecule6SpotID: + if (_moleculeBin.isMoleculeHighlighted(hotspot->getObjectID() - kWSC03NorthMolecule1SpotID)) + hotspot->setInactive(); + break; + case kWSC03SouthPickUpAntidoteSpotID: + if (getCurrentActivation() == kActivationSynthesizerLooping) + hotspot->setActive(); + break; + case kW98DropArgonSpotID: + argonCanister = _vm->getAllItems().findItemByID(kArgonCanister); + if (argonCanister->getItemState() != kArgonFull) + hotspot->setInactive(); + break; + } +} + +void WSC::activateHotspots() { + Neighborhood::activateHotspots(); + + if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC98, kWest) && _privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) { + if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kW98RetinalChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kW98RetinalChipSpotID); + + if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kW98MapChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kW98MapChipSpotID); + + if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) + _vm->getAllHotspots().deactivateOneHotspot(kW98OpticalChipSpotID); + else + _vm->getAllHotspots().activateOneHotspot(kW98OpticalChipSpotID); + } +} + +void WSC::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + if (JMPPPInput::isEasterEggModifierInput(input)) + GameState.setEasterEgg(true); + + if (clickedSpot) { + switch (clickedSpot->getObjectID()) { + case kWSCAnalyzerScreenSpotID: + requestExtraSequence(kWSCAnalyzeDart, kExtraCompletedFlag, kFilterNoInput); + requestExtraSequence(kWSCZoomOutFromAnalyzer, kExtraCompletedFlag, kFilterNoInput); + break; + case kWSC02SouthPlayMessagesSpotID: + if (GameState.isTakenItemID(kNitrogenCanister)) { + if (_lastExtra == (uint32)kMessagesMovedToOfficeNoNitrogen) + startExtraSequence(kMessagesOffNoNitrogen, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMessagesMovedToOfficeNoNitrogen, kExtraCompletedFlag, kFilterNoInput); + } else { + if (_lastExtra == (uint32)kMessagesMovedToOffice) + startExtraSequence(kMessagesOff, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kMessagesMovedToOffice, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kWSC02SouthInterruptMorphSpotID: + _privateFlags.setFlag(kWSCPrivateInterruptedMorphFlag, true); + break; + case kWSC02SouthMorphFinishedSpotID: + requestExtraSequence(kWSC02MorphFinished, 0, kFilterNoInput); + requestExtraSequence(kWSC02TurnOffMorphScreen, kExtraCompletedFlag, kFilterNoInput); + break; + case kWSC03NorthMolecule1SpotID: + case kWSC03NorthMolecule2SpotID: + case kWSC03NorthMolecule3SpotID: + case kWSC03NorthMolecule4SpotID: + case kWSC03NorthMolecule5SpotID: + case kWSC03NorthMolecule6SpotID: + moleculeGameClick(clickedSpot->getObjectID()); + break; + case kW98GrabCableSpotID: + if (isEventTimerRunning()) { + cancelEvent(); + startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); + } + + _privateFlags.setFlag(kWSCPrivateClickedCatwalkCableFlag, true); + break; + default: + Neighborhood::clickInHotspot(input, clickedSpot); + break; + } + } else { + Neighborhood::clickInHotspot(input, clickedSpot); + } + + GameState.setEasterEgg(false); +} + +void WSC::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { + CoordType h, v; + + switch (item->getObjectID()) { + case kPoisonDart: + Neighborhood::dropItemIntoRoom(item, dropSpot); + GameState.setWSCDartInAnalyzer(true); + if (dropSpot && dropSpot->getObjectID() == kWSCDropDartSpotID) { + if (!GameState.getWSCAnalyzerOn()) + requestExtraSequence(kWSCAnalyzerPowerUpWithDart, kExtraCompletedFlag, kFilterNoInput); + + requestExtraSequence(kWSCDropDartIntoAnalyzer, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kAntidote: + _privateFlags.setFlag(kWSCDraggingAntidoteFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + loopExtraSequence(kW03SouthAntidoteLoop); + break; + case kSinclairKey: + Neighborhood::dropItemIntoRoom(item, dropSpot); + _privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, true); + openDoor(); + break; + case kCrowbar: + Neighborhood::dropItemIntoRoom(item, dropSpot); + _privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, true); + openDoor(); + break; + case kMachineGun: + setCurrentAlternate(kAltWSCNormal); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kArgonCanister: + item->setItemState(kArgonEmpty); + _argonSprite = item->getDragSprite(0); + _argonSprite->setCurrentFrameIndex(1); + _argonSprite->setDisplayOrder(kDragSpriteOrder); + dropSpot->getCenter(h, v); + _argonSprite->centerElementAt(h, v); + _argonSprite->startDisplaying(); + _argonSprite->show(); + + if (isEventTimerRunning()) { + cancelEvent(); + startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); + } + break; + case kRetinalScanBiochip: + _privateFlags.setFlag(kWSCPrivateGotRetScanChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kMapBiochip: + _privateFlags.setFlag(kWSCPrivateGotMapChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kWSCPrivateGotOpticalChipFlag, false); + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + default: + Neighborhood::dropItemIntoRoom(item, dropSpot); + break; + } +} + +void WSC::takeItemFromRoom(Item *item) { + switch (item->getObjectID()) { + case kAntidote: + _privateFlags.setFlag(kWSCDraggingAntidoteFlag, true); + Neighborhood::takeItemFromRoom(item); + break; + case kMachineGun: + setCurrentAlternate(kAltWSCTookMachineGun); + Neighborhood::takeItemFromRoom(item); + break; + case kRetinalScanBiochip: + _privateFlags.setFlag(kWSCPrivateGotRetScanChipFlag, true); + Neighborhood::takeItemFromRoom(item); + break; + case kMapBiochip: + _privateFlags.setFlag(kWSCPrivateGotMapChipFlag, true); + Neighborhood::takeItemFromRoom(item); + break; + case kOpticalBiochip: + _privateFlags.setFlag(kWSCPrivateGotOpticalChipFlag, true); + Neighborhood::takeItemFromRoom(item); + break; + default: + Neighborhood::takeItemFromRoom(item); + break; + } +} + +Hotspot *WSC::getItemScreenSpot(Item *item, DisplayElement *element) { + HotSpotID destSpotID; + + switch (item->getObjectID()) { + case kNitrogenCanister: + destSpotID = kWSC02SouthTakeNitrogenSpotID; + break; + case kArgonPickup: + destSpotID = kWSC02SouthTakeArgonSpotID; + break; + case kAntidote: + destSpotID = kWSC03SouthPickUpAntidoteSpotID; + break; + case kMachineGun: + destSpotID = kW61SouthMachineGunSpotID; + break; + case kRetinalScanBiochip: + destSpotID = kW98RetinalChipSpotID; + break; + case kMapBiochip: + destSpotID = kW98MapChipSpotID; + break; + case kOpticalBiochip: + destSpotID = kW98OpticalChipSpotID; + break; + default: + destSpotID = kNoHotSpotID; + break; + } + + if (destSpotID == kNoHotSpotID) + return Neighborhood::getItemScreenSpot(item, element); + + return _vm->getAllHotspots().findHotspotByID(destSpotID); +} + +void WSC::pickedUpItem(Item *item) { + switch (item->getObjectID()) { + case kAntidote: + if (!GameState.getWSCPickedUpAntidote()) { + GameState.setWSCPoisoned(false); + GameState.setWSCRemovedDart(false); + GameState.setWSCPickedUpAntidote(true); + _privateFlags.setFlag(kWSCDraggingAntidoteFlag, false); + playSpotSoundSync(kDrinkAntidoteIn, kDrinkAntidoteOut); + setUpPoison(); + startExtraSequence(kW03SouthDeactivate, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kArgonPickup: + _vm->removeItemFromInventory((InventoryItem *)item); + item = (Item *)_vm->getAllItems().findItemByID(kArgonCanister); + _vm->addItemToInventory((InventoryItem *)item); + item = (Item *)_vm->getAllItems().findItemByID(kSinclairKey); + _vm->addItemToInventory((InventoryItem *)item); + _vm->getAllHotspots().setHotspotRect(kWSC02SouthMorphOutSpotID, + Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop)); + break; + case kArgonCanister: + GameState.setScoringGotArgonCanister(); + break; + case kSinclairKey: + GameState.setScoringGotSinclairKey(); + break; + case kNitrogenCanister: + GameState.setScoringGotNitrogenCanister(); + break; + case kRetinalScanBiochip: + if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag) && _privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) { + if (GameState.getWSCCatwalkDark()) + startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kMapBiochip: + if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag) && _privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) { + if (GameState.getWSCCatwalkDark()) + startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kOpticalBiochip: + g_opticalChip->addMercury(); + GameState.setScoringGotWSCOpMemChip(); + if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag) && _privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) { + if (GameState.getWSCCatwalkDark()) + startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); + else + startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); + } + break; + case kStunGun: + GameState.setWSCFinished(true); + + if (!GameState.getWSCCatwalkDark()) + GameState.setScoringWSCGandhi(); + + recallToTSASuccess(); + break; + } +} + +void WSC::checkPeopleCrossing() { + switch (GameState.getCurrentRoomAndView()) { + case MakeRoomView(kWSC17, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt17WestFlag)) + startExtraSequence(kW17WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC21, kSouth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt21SouthFlag)) + startExtraSequence(kW21SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC24, kSouth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt24SouthFlag)) + startExtraSequence(kW24SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC34, kEast): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt34EastFlag)) + startExtraSequence(kW34EastPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC36, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt36WestFlag)) + startExtraSequence(kW36WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC38, kNorth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt38NorthFlag)) + startExtraSequence(kW38NorthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC46, kSouth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt46SouthFlag)) + startExtraSequence(kW46SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC49, kNorth): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt49NorthFlag)) + startExtraSequence(kW49NorthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + case MakeRoomView(kWSC73, kWest): + if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt73WestFlag)) + startExtraSequence(kW73WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); + break; + default: + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt21SouthFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, true); + forceStridingStop(kWSC18, kSouth, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, false); + restoreStriding(kWSC18, kSouth, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt19NorthFlag) && _vm->getRandomNumber(2) == 0) { + forceStridingStop(kWSC22, kNorth, kAltWSCNormal); + } else { + restoreStriding(kWSC22, kNorth, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt24SouthFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, true); + forceStridingStop(kWSC22, kSouth, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, false); + restoreStriding(kWSC22, kSouth, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt34EastFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, true); + forceStridingStop(kWSC28, kEast, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, false); + restoreStriding(kWSC28, kEast, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt36WestFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, true); + forceStridingStop(kWSC40, kWest, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, false); + restoreStriding(kWSC40, kWest, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt38NorthFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, true); + forceStridingStop(kWSC42, kNorth, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, false); + restoreStriding(kWSC42, kNorth, kAltWSCNormal); + } + + if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt46SouthFlag) && _vm->getRandomNumber(2) == 0) { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, true); + forceStridingStop(kWSC44, kSouth, kAltWSCNormal); + } else { + _privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, false); + restoreStriding(kWSC44, kSouth, kAltWSCNormal); + } + break; + } +} + +void WSC::setUpPoison() { + if (GameState.getWSCPoisoned()) { + if (GameState.getWSCRemovedDart()) { + if (g_energyMonitor->getEnergyDrainRate() != kWSCPoisonEnergyDrainNoDart) { + g_energyMonitor->setEnergyDrainRate(kWSCPoisonEnergyDrainNoDart); + _vm->setEnergyDeathReason(kDeathDidntStopPoison); + } + } else { + if (g_energyMonitor->getEnergyDrainRate() != kWSCPoisonEnergyDrainWithDart) { + g_energyMonitor->setEnergyDrainRate(kWSCPoisonEnergyDrainWithDart); + _vm->setEnergyDeathReason(kDeathDidntStopPoison); + } + } + } else if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { + g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); + _vm->resetEnergyDeathReason(); + } +} + +bool WSC::inSynthesizerGame() { + return _moleculesMovie.isMovieValid(); +} + +bool WSC::canSolve() { + return (inSynthesizerGame() || (GameState.getCurrentRoom() == kWSC98 && !GameState.getWSCRobotDead())); +} + +void WSC::doSolve() { + if (inSynthesizerGame()) { + _moleculesMovie.releaseMovie(); + _moleculeBin.cleanUpMoleculeBin(); + requestExtraSequence(kW03NorthFinishSynthesis, kExtraCompletedFlag, kFilterNoInput); + } else if (GameState.getCurrentRoom() == kWSC98 && !GameState.getWSCRobotDead()) { + cancelEvent(); + startExtraSequence(kW98RobotShocked, kExtraCompletedFlag, kFilterNoInput); + } +} + +Common::String WSC::getNavMovieName() { + return "Images/World Science Center/WSC.movie"; +} + +Common::String WSC::getSoundSpotsName() { + return "Sounds/World Science Center/WSC Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/wsc/wsc.h b/engines/pegasus/neighborhood/wsc/wsc.h new file mode 100644 index 0000000000..d9634b3539 --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/wsc.h @@ -0,0 +1,166 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_WSC_WSC_H +#define PEGASUS_NEIGHBORHOOD_WSC_WSC_H + +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/wsc/moleculebin.h" + +namespace Pegasus { + +static const DisplayOrder kWSCMoleculeBinOrder = kMonitorLayer; +static const DisplayOrder kWSCMoleculesMovieOrder = kWSCMoleculeBinOrder + 1; + +static const RoomID kWSC01 = 0; +static const RoomID kWSC02Morph = 2; +static const RoomID kWSC02Messages = 3; +static const RoomID kWSC62 = 62; + +class WSC : public Neighborhood { +public: + WSC(InputHandler *, PegasusEngine *); + virtual ~WSC() {} + + void flushGameState(); + + virtual uint16 getDateResID() const; + + bool okayToJump(); + + void checkContinuePoint(const RoomID, const DirectionConstant); + + bool inSynthesizerGame(); + + bool canSolve(); + void doSolve(); + + virtual void prepareForAIHint(const Common::String &); + virtual void cleanUpAfterAIHint(const Common::String &); + + void init(); + void start(); + +protected: + enum { + kWSCDraggingAntidoteFlag, + + kWSCPrivateLabMessagesOpenFlag, + kWSCPrivateInterruptedMorphFlag, + kWSCPrivateInMoleculeGameFlag, + kWSCPrivateSinclairOfficeOpenFlag, + kWSCPrivateOfficeLogOpenFlag, + kWSCPrivate58SouthOpenFlag, + kWSCPrivateClickedCatwalkCableFlag, + kWSCPrivateRobotHeadOpenFlag, + + kWSCPrivateSeenPeopleAt17WestFlag, + kWSCPrivateSeenPeopleAt19NorthFlag, + kWSCPrivateSeenPeopleAt21SouthFlag, + kWSCPrivateSeenPeopleAt24SouthFlag, + kWSCPrivateSeenPeopleAt34EastFlag, + kWSCPrivateSeenPeopleAt36WestFlag, + kWSCPrivateSeenPeopleAt38NorthFlag, + kWSCPrivateSeenPeopleAt46SouthFlag, + kWSCPrivateSeenPeopleAt49NorthFlag, + kWSCPrivateSeenPeopleAt73WestFlag, + + kWSCPrivateNeedPeopleAt17WestFlag, + kWSCPrivateNeedPeopleAt21SouthFlag, + kWSCPrivateNeedPeopleAt24SouthFlag, + kWSCPrivateNeedPeopleAt34EastFlag, + kWSCPrivateNeedPeopleAt36WestFlag, + kWSCPrivateNeedPeopleAt38NorthFlag, + kWSCPrivateNeedPeopleAt46SouthFlag, + kWSCPrivateNeedPeopleAt49NorthFlag, + kWSCPrivateNeedPeopleAt73WestFlag, + + kWSCPrivateGotRetScanChipFlag, + kWSCPrivateGotMapChipFlag, + kWSCPrivateGotOpticalChipFlag, + + kNumWSCPrivateFlags + }; + + void arriveAt(const RoomID, const DirectionConstant); + void turnTo(const DirectionConstant); + void receiveNotification(Notification *, const NotificationFlags); + void dropItemIntoRoom(Item *, Hotspot *); + void clickInHotspot(const Input &, const Hotspot *); + TimeValue getViewTime(const RoomID, const DirectionConstant); + void getZoomEntry(const HotSpotID, ZoomTable::Entry &); + CanMoveForwardReason canMoveForward(ExitTable::Entry &entry); + void cantMoveThatWay(CanMoveForwardReason reason); + CanTurnReason canTurn(TurnDirection turn, DirectionConstant &nextDir); + void zoomTo(const Hotspot *hotspot); + void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); + void setUpMoleculeGame(); + void nextMoleculeGameLevel(); + void startMoleculeGameLevel(); + void moleculeGameClick(const HotSpotID); + void loadAmbientLoops(); + CanOpenDoorReason canOpenDoor(DoorTable::Entry &); + void cantOpenDoor(CanOpenDoorReason); + void pickedUpItem(Item *); + void doorOpened(); + void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits); + void getExtraEntry(const uint32, ExtraTable::Entry &); + void takeItemFromRoom(Item *item); + void checkPeopleCrossing(); + void turnLeft(); + void turnRight(); + void moveForward(); + Hotspot *getItemScreenSpot(Item *, DisplayElement *); + int16 getStaticCompassAngle(const RoomID, const DirectionConstant); + void getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove); + void getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove); + void bumpIntoWall(); + void activateHotspots(); + void setUpAIRules(); + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + void closeDoorOffScreen(const RoomID, const DirectionConstant); + void setUpPoison(); + void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + void timerExpired(const uint32); + + Common::String getSoundSpotsName(); + Common::String getNavMovieName(); + + FlagsArray<byte, kNumWSCPrivateFlags> _privateFlags; + const Hotspot *_cachedZoomSpot; + MoleculeBin _moleculeBin; + int32 _moleculeGameLevel, _numCorrect; + Movie _moleculesMovie; + uint32 _levelArray[6]; + Common::Rational _energyDrainRate; + Sprite *_argonSprite; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/zoom.cpp b/engines/pegasus/neighborhood/zoom.cpp new file mode 100644 index 0000000000..478ec6e493 --- /dev/null +++ b/engines/pegasus/neighborhood/zoom.cpp @@ -0,0 +1,74 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/zoom.h" + +namespace Pegasus { + +void ZoomTable::loadFromStream(Common::SeekableReadStream *stream) { + uint32 count = stream->readUint32BE(); + _entries.resize(count); + + for (uint32 i = 0; i < count; i++) { + _entries[i].hotspot = stream->readUint16BE(); + _entries[i].movieStart = stream->readUint32BE(); + _entries[i].movieEnd = stream->readUint32BE(); + _entries[i].room = stream->readUint16BE(); + _entries[i].direction = stream->readByte(); + debug(0, "Zoom[%d]: %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].movieStart, + _entries[i].movieEnd, _entries[i].room, _entries[i].direction); + stream->readByte(); // alignment + } +} + +void ZoomTable::clear() { + _entries.clear(); +} + +ZoomTable::Entry::Entry() { + clear(); +} + +void ZoomTable::Entry::clear() { + hotspot = kNoHotSpotID; + movieStart = 0xffffffff; + movieEnd = 0xffffffff; + room = kNoRoomID; + direction = kNoDirection; +} + +ZoomTable::Entry ZoomTable::findEntry(HotSpotID hotspot) { + for (uint32 i = 0; i < _entries.size(); i++) + if (_entries[i].hotspot == hotspot) + return _entries[i]; + + return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/zoom.h b/engines/pegasus/neighborhood/zoom.h new file mode 100644 index 0000000000..8bcf8974f8 --- /dev/null +++ b/engines/pegasus/neighborhood/zoom.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_ZOOM_H +#define PEGASUS_NEIGHBORHOOD_ZOOM_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Pegasus { + +class ZoomTable { +public: + ZoomTable() {} + ~ZoomTable() {} + + static uint32 getResTag() { return MKTAG('Z', 'o', 'o', 'm'); } + + void loadFromStream(Common::SeekableReadStream *stream); + void clear(); + + struct Entry { + Entry(); + void clear(); + bool isEmpty() { return movieStart == 0xffffffff; } + + HotSpotID hotspot; + TimeValue movieStart; + TimeValue movieEnd; + RoomID room; + DirectionConstant direction; + }; + + Entry findEntry(HotSpotID hotspot); + +private: + Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/notification.cpp b/engines/pegasus/notification.cpp new file mode 100644 index 0000000000..2d57fcc5e7 --- /dev/null +++ b/engines/pegasus/notification.cpp @@ -0,0 +1,149 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/constants.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +typedef ReceiverList::iterator ReceiverIterator; + +Notification::Notification(const NotificationID id, NotificationManager *owner) : IDObject(id) { + _owner = owner; + _currentFlags = kNoNotificationFlags; + if (_owner) + _owner->addNotification(this); +} + +Notification::~Notification() { + for (uint i = 0; i < _receivers.size(); i++) + _receivers[i].receiver->newNotification(NULL); + + if (_owner) + _owner->removeNotification(this); +} + +// Selectively set or clear notificiation bits. +// Wherever mask is 0, leave existing bits untouched. +// Wherever mask is 1, set bit equivalent to flags. +void Notification::notifyMe(NotificationReceiver *receiver, NotificationFlags flags, NotificationFlags mask) { + for (uint i = 0; i < _receivers.size(); i++) { + if (_receivers[i].receiver == receiver) { + _receivers[i].mask = (_receivers[i].mask & ~mask) | (flags & mask); + receiver->newNotification(this); + return; + } + } + + ReceiverEntry newEntry; + newEntry.receiver = receiver; + newEntry.mask = flags; + _receivers.push_back(newEntry); + + receiver->newNotification(this); +} + +void Notification::cancelNotification(NotificationReceiver *receiver) { + for (uint i = 0; i < _receivers.size(); i++) { + if (_receivers[i].receiver == receiver) { + _receivers.remove_at(i); + i--; + } + } +} + +void Notification::setNotificationFlags(NotificationFlags flags, NotificationFlags mask) { + _currentFlags = (_currentFlags & ~mask) | flags; +} + +void Notification::checkReceivers() { + NotificationFlags currentFlags = _currentFlags; + _currentFlags = kNoNotificationFlags; + + for (uint i = 0; i < _receivers.size(); i++) + if (_receivers[i].mask & currentFlags) + _receivers[i].receiver->receiveNotification(this, currentFlags); +} + +// Receiver entries are equal if their receivers are equal. + +int operator==(const ReceiverEntry &entry1, const ReceiverEntry &entry2) { + return entry1.receiver == entry2.receiver; +} + +int operator!=(const ReceiverEntry &entry1, const ReceiverEntry &entry2) { + return entry1.receiver != entry2.receiver; +} + +NotificationReceiver::NotificationReceiver() { + _notification = NULL; +} + +NotificationReceiver::~NotificationReceiver() { + if (_notification) + _notification->cancelNotification(this); +} + +void NotificationReceiver::receiveNotification(Notification *, const NotificationFlags) { +} + +void NotificationReceiver::newNotification(Notification *notification) { + _notification = notification; +} + +typedef NotificationList::iterator NotificationIterator; + +NotificationManager::NotificationManager() { +} + +NotificationManager::~NotificationManager() { + detachNotifications(); +} + +void NotificationManager::addNotification(Notification *notification) { + _notifications.push_back(notification); +} + +void NotificationManager::removeNotification(Notification *notification) { + for (NotificationIterator it = _notifications.begin(); it != _notifications.end();) { + if ((*it) == notification) + it = _notifications.erase(it); + else + it++; + } +} + +void NotificationManager::detachNotifications() { + for (NotificationIterator it = _notifications.begin(); it != _notifications.end(); it++) + (*it)->_owner = 0; +} + +void NotificationManager::checkNotifications() { + for (NotificationIterator it = _notifications.begin(); it != _notifications.end(); it++) + if ((*it)->_currentFlags != kNoNotificationFlags) + (*it)->checkReceivers(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/notification.h b/engines/pegasus/notification.h new file mode 100644 index 0000000000..19b69829be --- /dev/null +++ b/engines/pegasus/notification.h @@ -0,0 +1,123 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NOTIFICATION_H +#define PEGASUS_NOTIFICATION_H + +#include "common/array.h" +#include "common/list.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class NotificationManager; +class NotificationReceiver; + +struct ReceiverEntry { + NotificationReceiver *receiver; + NotificationFlags mask; +}; + +int operator==(const ReceiverEntry &entry1, const ReceiverEntry &entry2); +int operator!=(const ReceiverEntry &entry1, const ReceiverEntry &entry2); + +typedef Common::Array<ReceiverEntry> ReceiverList; + +/* + A notification can have 32 flags associated with it, which can be user-defined. +*/ + +class Notification : public IDObject { +friend class NotificationManager; + +public: + Notification(const NotificationID id, NotificationManager *owner); + virtual ~Notification(); + + // notifyMe will have this receiver notified when any of the specified notification + // flags are set. + // If there is already a notification set for this receiver, notifyMe does a bitwise + // OR with the receiver's current notification flags. + + // Can selectively set or clear notification bits by using the flags and mask argument. + + void notifyMe(NotificationReceiver*, NotificationFlags flags, NotificationFlags mask); + void cancelNotification(NotificationReceiver *receiver); + + void setNotificationFlags(NotificationFlags flags, NotificationFlags mask); + NotificationFlags getNotificationFlags() { return _currentFlags; } + + void clearNotificationFlags() { setNotificationFlags(0, ~(NotificationFlags)0); } + +protected: + void checkReceivers(); + + NotificationManager *_owner; + ReceiverList _receivers; + NotificationFlags _currentFlags; +}; + +class NotificationReceiver { +friend class Notification; + +public: + NotificationReceiver(); + virtual ~NotificationReceiver(); + +protected: + // receiveNotification is called automatically whenever a notification that this + // receiver depends on has its flags set + + virtual void receiveNotification(Notification *, const NotificationFlags); + virtual void newNotification(Notification *notification); + +private: + Notification *_notification; +}; + +typedef Common::List<Notification *> NotificationList; + +class NotificationManager : public NotificationReceiver { +friend class Notification; + +public: + NotificationManager(); + virtual ~NotificationManager(); + + void checkNotifications(); + +protected: + void addNotification(Notification *notification); + void removeNotification(Notification *notification); + void detachNotifications(); + + NotificationList _notifications; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp new file mode 100644 index 0000000000..420ca39331 --- /dev/null +++ b/engines/pegasus/pegasus.cpp @@ -0,0 +1,2347 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/config-manager.h" +#include "common/error.h" +#include "common/events.h" +#include "common/fs.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "common/translation.h" +#include "common/random.h" +#include "base/plugins.h" +#include "base/version.h" +#include "gui/saveload.h" +#include "video/qt_decoder.h" + +#include "pegasus/console.h" +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/menu.h" +#include "pegasus/movie.h" +#include "pegasus/pegasus.h" +#include "pegasus/timers.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/items/biochips/retscanchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/items/inventory/gascanister.h" +#include "pegasus/items/inventory/inventoryitem.h" +#include "pegasus/items/inventory/keycard.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +PegasusEngine::PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc) : Engine(syst), InputHandler(0), _gameDescription(gamedesc), + _shellNotification(kJMPDCShellNotificationID, this), _returnHotspot(kInfoReturnSpotID), _itemDragger(this), _bigInfoMovie(kNoDisplayElement), + _smallInfoMovie(kNoDisplayElement) { + _continuePoint = 0; + _saveAllowed = _loadAllowed = true; + _saveRequested = _loadRequested = false; + _gameMenu = 0; + _deathReason = kDeathStranded; + _neighborhood = 0; + _FXLevel = 0x80; + _ambientLevel = 0x80; + _gameMode = kNoMode; + _switchModesSync = false; + _draggingItem = 0; + _dragType = kDragNoDrag; + _idlerHead = 0; + _currentCD = 1; + _introTimer = 0; + _aiSaveStream = 0; +} + +PegasusEngine::~PegasusEngine() { + delete _resFork; + delete _console; + delete _cursor; + delete _continuePoint; + delete _gameMenu; + delete _neighborhood; + delete _rnd; + delete _introTimer; + delete _aiSaveStream; + + for (ItemIterator it = _allItems.begin(); it != _allItems.end(); it++) + delete *it; + + InputDeviceManager::destroy(); + GameStateManager::destroy(); + + // NOTE: This must be deleted last! + delete _gfx; +} + +Common::Error PegasusEngine::run() { + _console = new PegasusConsole(this); + _gfx = new GraphicsManager(this); + _resFork = new Common::MacResManager(); + _cursor = new Cursor(); + _rnd = new Common::RandomSource("Pegasus"); + + if (!_resFork->open("JMP PP Resources") || !_resFork->hasResFork()) + error("Could not load JMP PP Resources"); + + // Initialize items + createItems(); + + // Initialize cursors + _cursor->addCursorFrames(0x80); // Main + _cursor->addCursorFrames(900); // Mars Shuttle + + // Initialize the item dragger bounds + _itemDragger.setHighlightBounds(); + + if (!isDemo() && !detectOpeningClosingDirectory()) { + Common::String message = "Missing intro directory. "; + + // Give Mac OS X a more specific message because we can +#ifdef MACOSX + message += "Make sure \"Opening/Closing\" is present."; +#else + message += "Be sure to rename \"Opening/Closing\" to \"Opening_Closing\"."; +#endif + + GUIErrorMessage(message); + warning("%s", message.c_str()); + return Common::kNoGameDataFoundError; + } + + // Set up input + InputHandler::setInputHandler(this); + allowInput(true); + + // Set up inventories + _items.setWeightLimit(9); + _items.setOwnerID(kPlayerID); + _biochips.setWeightLimit(8); + _biochips.setOwnerID(kPlayerID); + + _returnHotspot.setArea(Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop)); + _returnHotspot.setHotspotFlags(kInfoReturnSpotFlag); + _allHotspots.push_back(&_returnHotspot); + + _screenDimmer.setBounds(Common::Rect(0, 0, 640, 480)); + _screenDimmer.setDisplayOrder(kScreenDimmerOrder); + + // Load from the launcher/cli if requested (and don't show the intro in those cases) + bool doIntro = true; + if (ConfMan.hasKey("save_slot")) { + uint32 gameToLoad = ConfMan.getInt("save_slot"); + doIntro = (loadGameState(gameToLoad).getCode() != Common::kNoError); + } + + _shellNotification.notifyMe(this, kJMPShellNotificationFlags, kJMPShellNotificationFlags); + + if (doIntro) + // Start up the first notification + _shellNotification.setNotificationFlags(kGameStartingFlag, kGameStartingFlag); + + if (!isDemo()) { + _introTimer = new FuseFunction(); + _introTimer->setFunctor(new Common::Functor0Mem<void, PegasusEngine>(this, &PegasusEngine::introTimerExpired)); + } + + while (!shouldQuit()) { + processShell(); + _system->delayMillis(10); // Ease off the CPU + } + + return Common::kNoError; +} + +bool PegasusEngine::canLoadGameStateCurrently() { + return _loadAllowed && !isDemo(); +} + +bool PegasusEngine::canSaveGameStateCurrently() { + return _saveAllowed && !isDemo() && g_neighborhood; +} + +bool PegasusEngine::detectOpeningClosingDirectory() { + // We need to detect what our Opening/Closing directory is listed as + // On the original disc, it was 'Opening/Closing' but only HFS(+) supports the slash + // Mac OS X will display this as 'Opening:Closing' and we can use that directly + // On other systems, users will need to rename to "Opening_Closing" + + Common::FSNode gameDataDir(ConfMan.get("path")); + gameDataDir = gameDataDir.getChild("Images"); + + if (!gameDataDir.exists()) + return false; + + Common::FSList fsList; + if (!gameDataDir.getChildren(fsList, Common::FSNode::kListDirectoriesOnly, true)) + return false; + + for (uint i = 0; i < fsList.size() && _introDirectory.empty(); i++) { + Common::String name = fsList[i].getName(); + + if (name.equalsIgnoreCase("Opening:Closing")) + _introDirectory = name; + else if (name.equalsIgnoreCase("Opening_Closing")) + _introDirectory = name; + } + + if (_introDirectory.empty()) + return false; + + debug(0, "Detected intro location as '%s'", _introDirectory.c_str()); + _introDirectory = Common::String("Images/") + _introDirectory; + return true; +} + +void PegasusEngine::createItems() { + Common::SeekableReadStream *res = _resFork->getResource(MKTAG('N', 'I', 't', 'm'), 0x80); + + uint16 entryCount = res->readUint16BE(); + + for (uint16 i = 0; i < entryCount; i++) { + ItemID itemID = res->readUint16BE(); + NeighborhoodID neighborhoodID = res->readUint16BE(); + RoomID roomID = res->readUint16BE(); + DirectionConstant direction = res->readByte(); + res->readByte(); // alignment + + createItem(itemID, neighborhoodID, roomID, direction); + } + + delete res; +} + +void PegasusEngine::createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction) { + switch (itemID) { + case kInterfaceBiochip: + // Unused in game, but still in the data and we need to create + // it because it's saved/loaded from save files. + new BiochipItem(itemID, neighborhoodID, roomID, direction); + break; + case kAIBiochip: + new AIChip(itemID, neighborhoodID, roomID, direction); + break; + case kPegasusBiochip: + new PegasusChip(itemID, neighborhoodID, roomID, direction); + break; + case kOpticalBiochip: + new OpticalChip(itemID, neighborhoodID, roomID, direction); + break; + case kMapBiochip: + new MapChip(itemID, neighborhoodID, roomID, direction); + break; + case kRetinalScanBiochip: + new RetScanChip(itemID, neighborhoodID, roomID, direction); + break; + case kShieldBiochip: + new ShieldChip(itemID, neighborhoodID, roomID, direction); + break; + case kAirMask: + new AirMask(itemID, neighborhoodID, roomID, direction); + break; + case kKeyCard: + new KeyCard(itemID, neighborhoodID, roomID, direction); + break; + case kGasCanister: + new GasCanister(itemID, neighborhoodID, roomID, direction); + break; + default: + // Everything else is a normal inventory item + new InventoryItem(itemID, neighborhoodID, roomID, direction); + break; + } +} + +void PegasusEngine::runIntro() { + stopIntroTimer(); + + bool skipped = false; + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (video->loadFile(_introDirectory + "/BandaiLogo.movie")) { + video->start(); + + while (!shouldQuit() && !video->endOfVideo() && !skipped) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) { + _system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 0, 0, frame->w, frame->h); + _system->updateScreen(); + } + } + + Input input; + InputDevice.getInput(input, kFilterAllInput); + if (input.anyInput()) + skipped = true; + + _system->delayMillis(10); + } + } + + delete video; + + if (shouldQuit() || skipped) + return; + + video = new Video::QuickTimeDecoder(); + + if (!video->loadFile(_introDirectory + "/Big Movie.movie")) + error("Could not load intro movie"); + + video->seek(Audio::Timestamp(0, 10 * 600, 600)); + video->start(); + + playMovieScaled(video, 0, 0); + + delete video; +} + +Common::Error PegasusEngine::showLoadDialog() { + GUI::SaveLoadChooser slc(_("Load game:"), _("Load"), false); + + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); + + Common::Error result; + + if (slot >= 0) { + if (loadGameState(slot).getCode() == Common::kNoError) + result = Common::kNoError; + else + result = Common::kUnknownError; + } else { + result = Common::kUserCanceled; + } + + return result; +} + +Common::Error PegasusEngine::showSaveDialog() { + GUI::SaveLoadChooser slc(_("Save game:"), _("Save"), true); + + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); + + Common::Error result; + + if (slot >= 0) { + if (saveGameState(slot, slc.getResultString()).getCode() == Common::kNoError) + result = Common::kNoError; + else + result = Common::kUnknownError; + } else { + result = Common::kUserCanceled; + } + + return result; +} + +GUI::Debugger *PegasusEngine::getDebugger() { + return _console; +} + +void PegasusEngine::addIdler(Idler *idler) { + idler->_nextIdler = _idlerHead; + if (_idlerHead) + _idlerHead->_prevIdler = idler; + idler->_prevIdler = 0; + _idlerHead = idler; +} + +void PegasusEngine::removeIdler(Idler *idler) { + if (idler->_prevIdler) + idler->_prevIdler->_nextIdler = idler->_nextIdler; + if (idler->_nextIdler) + idler->_nextIdler->_prevIdler = idler->_prevIdler; + if (idler == _idlerHead) + _idlerHead = idler->_nextIdler; + idler->_nextIdler = 0; + idler->_prevIdler = 0; +} + +void PegasusEngine::giveIdleTime() { + for (Idler *idler = _idlerHead; idler != 0; idler = idler->_nextIdler) + idler->useIdleTime(); +} + +void PegasusEngine::addTimeBase(TimeBase *timeBase) { + _timeBases.push_back(timeBase); +} + +void PegasusEngine::removeTimeBase(TimeBase *timeBase) { + _timeBases.remove(timeBase); +} + +bool PegasusEngine::loadFromStream(Common::ReadStream *stream) { + // Dispose currently running stuff + useMenu(0); + useNeighborhood(0); + removeAllItemsFromInventory(); + removeAllItemsFromBiochips(); + _currentItemID = kNoItemID; + _currentBiochipID = kNoItemID; + + if (!g_interface) + createInterface(); + + // Signature + uint32 creator = stream->readUint32BE(); + if (creator != kPegasusPrimeCreator) { + warning("Bad save creator '%s'", tag2str(creator)); + return false; + } + + uint32 gameType = stream->readUint32BE(); + int saveType; + + switch (gameType) { + case kPegasusPrimeDisk1GameType: + case kPegasusPrimeDisk2GameType: + case kPegasusPrimeDisk3GameType: + case kPegasusPrimeDisk4GameType: + _currentCD = gameType - kPegasusPrimeDisk1GameType + 1; + saveType = kNormalSave; + break; + case kPegasusPrimeContinueType: + saveType = kContinueSave; + break; + default: + // There are five other possible game types on the Pippin + // version, but hopefully we don't see any of those here + warning("Unhandled pegasus game type '%s'", tag2str(gameType)); + return false; + } + + uint32 version = stream->readUint32BE(); + if (version != kPegasusPrimeVersion) { + warning("Where did you get this save? It's a beta (v%04x)!", version & 0x7fff); + return false; + } + + // Game State + GameState.readGameState(stream); + + // Energy + setLastEnergyValue(stream->readUint32BE()); + + // Death reason + setEnergyDeathReason(stream->readByte()); + + // Items + _allItems.readFromStream(stream); + + // Inventory + byte itemCount = stream->readByte(); + + if (itemCount > 0) { + for (byte i = 0; i < itemCount; i++) { + InventoryItem *inv = (InventoryItem *)_allItems.findItemByID((ItemID)stream->readUint16BE()); + addItemToInventory(inv); + } + + g_interface->setCurrentInventoryItemID((ItemID)stream->readUint16BE()); + } + + // Biochips + byte biochipCount = stream->readByte(); + + if (biochipCount > 0) { + for (byte i = 0; i < biochipCount; i++) { + BiochipItem *biochip = (BiochipItem *)_allItems.findItemByID((ItemID)stream->readUint16BE()); + addItemToBiochips(biochip); + } + + g_interface->setCurrentBiochipID((ItemID)stream->readUint16BE()); + } + + + // TODO: Disc check + + // Jump to environment + jumpToNewEnvironment(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); + _shellNotification.setNotificationFlags(0, kNeedNewJumpFlag); + performJump(GameState.getCurrentNeighborhood()); + + // AI rules + if (g_AIArea) + g_AIArea->readAIRules(stream); + + startNeighborhood(); + + // Make a new continue point if this isn't already one + if (saveType == kNormalSave) + makeContinuePoint(); + + return true; +} + +bool PegasusEngine::writeToStream(Common::WriteStream *stream, int saveType) { + // WORKAROUND: If we don't have the interface, we can't actually save. + // However, we should still have a continue point, so we will just dump that + // out. This is needed for saving a game while in the space chase. + if (!g_interface) { + // Saving a continue stream from a continue stream should + // never happen. In addition, we do need to have a continue + // stream for this to work. + if (saveType != kNormalSave || !_continuePoint) + return false; + + writeContinueStream(stream); + return true; + } + + if (g_neighborhood) + g_neighborhood->flushGameState(); + + // Signature + stream->writeUint32BE(kPegasusPrimeCreator); + + if (saveType == kNormalSave) + stream->writeUint32BE(kPegasusPrimeDisk1GameType + _currentCD - 1); + else // Continue + stream->writeUint32BE(kPegasusPrimeContinueType); + + stream->writeUint32BE(kPegasusPrimeVersion); + + // Game State + GameState.writeGameState(stream); + + // Energy + stream->writeUint32BE(getSavedEnergyValue()); + + // Death reason + stream->writeByte(getEnergyDeathReason()); + + // Items + _allItems.writeToStream(stream); + + // Inventory + byte itemCount = _items.getNumItems(); + stream->writeByte(itemCount); + + if (itemCount > 0) { + for (uint32 i = 0; i < itemCount; i++) + stream->writeUint16BE(_items.getItemIDAt(i)); + + stream->writeUint16BE(g_interface->getCurrentInventoryItem()->getObjectID()); + } + + // Biochips + byte biochipCount = _biochips.getNumItems(); + stream->writeByte(biochipCount); + + if (biochipCount > 0) { + for (uint32 i = 0; i < biochipCount; i++) + stream->writeUint16BE(_biochips.getItemIDAt(i)); + + stream->writeUint16BE(g_interface->getCurrentBiochip()->getObjectID()); + } + + // AI rules + if (g_AIArea) + g_AIArea->writeAIRules(stream); + + return true; +} + +void PegasusEngine::makeContinuePoint() { + // WORKAROUND: Do not attempt to make a continue point if the interface is not set + // up. The original did *not* do this and attempting to restore the game using the pause + // menu during the canyon/space chase sequence would segfault the game and crash the + // system. Nice! + if (!g_interface) + return; + + delete _continuePoint; + + Common::MemoryWriteStreamDynamic newPoint(DisposeAfterUse::NO); + writeToStream(&newPoint, kContinueSave); + _continuePoint = new Common::MemoryReadStream(newPoint.getData(), newPoint.size(), DisposeAfterUse::YES); +} + +void PegasusEngine::loadFromContinuePoint() { + // Failure to load a continue point is fatal + + if (!_continuePoint) + error("Attempting to load from non-existant continue point"); + + _continuePoint->seek(0); + + if (!loadFromStream(_continuePoint)) + error("Failed loading continue point"); +} + +void PegasusEngine::writeContinueStream(Common::WriteStream *stream) { + // We're going to pretty much copy the stream, except for the save type + _continuePoint->seek(0); + stream->writeUint32BE(_continuePoint->readUint32BE()); + _continuePoint->readUint32BE(); // skip the continue type + stream->writeUint32BE(kPegasusPrimeDisk1GameType + _currentCD - 1); + + // Now just copy over the rest + uint32 size = _continuePoint->size() - _continuePoint->pos(); + byte *data = new byte[size]; + _continuePoint->read(data, size); + stream->write(data, size); + delete[] data; +} + +Common::Error PegasusEngine::loadGameState(int slot) { + Common::StringArray filenames = _saveFileMan->listSavefiles("pegasus-*.sav"); + Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filenames[slot]); + if (!loadFile) + return Common::kUnknownError; + + bool valid = loadFromStream(loadFile); + delete loadFile; + + return valid ? Common::kNoError : Common::kUnknownError; +} + +Common::Error PegasusEngine::saveGameState(int slot, const Common::String &desc) { + Common::String output = Common::String::format("pegasus-%s.sav", desc.c_str()); + Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(output, false); + if (!saveFile) + return Common::kUnknownError; + + bool valid = writeToStream(saveFile, kNormalSave); + delete saveFile; + + return valid ? Common::kNoError : Common::kUnknownError; +} + +void PegasusEngine::receiveNotification(Notification *notification, const NotificationFlags flags) { + if (&_shellNotification == notification) { + switch (flags) { + case kGameStartingFlag: { + useMenu(new MainMenu()); + + if (!isDemo()) { + runIntro(); + resetIntroTimer(); + } else { + showTempScreen("Images/Demo/NGsplashScrn.pict"); + } + + if (shouldQuit()) + return; + + _gfx->invalRect(Common::Rect(0, 0, 640, 480)); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + break; + } + case kPlayerDiedFlag: + doDeath(); + break; + case kNeedNewJumpFlag: + performJump(GameState.getNextNeighborhood()); + startNeighborhood(); + break; + default: + break; + } + } +} + +void PegasusEngine::checkCallBacks() { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->checkCallBacks(); +} + +void PegasusEngine::resetIntroTimer() { + if (!isDemo() && _gameMenu && _gameMenu->getObjectID() == kMainMenuID) { + _introTimer->stopFuse(); + _introTimer->primeFuse(kIntroTimeOut); + _introTimer->lightFuse(); + } +} + +void PegasusEngine::introTimerExpired() { + if (_gameMenu && _gameMenu->getObjectID() == kMainMenuID) { + ((MainMenu *)_gameMenu)->stopMainMenuLoop(); + + bool skipped = false; + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile(_introDirectory + "/LilMovie.movie")) + error("Failed to load little movie"); + + bool saveAllowed = swapSaveAllowed(false); + bool openAllowed = swapLoadAllowed(false); + + video->start(); + skipped = playMovieScaled(video, 0, 0); + + delete video; + + if (shouldQuit()) + return; + + if (!skipped) { + runIntro(); + + if (shouldQuit()) + return; + } + + resetIntroTimer(); + _gfx->invalRect(Common::Rect(0, 0, 640, 480)); + + swapSaveAllowed(saveAllowed); + swapLoadAllowed(openAllowed); + + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + } +} + +void PegasusEngine::stopIntroTimer() { + if (_introTimer) + _introTimer->stopFuse(); +} + +void PegasusEngine::delayShell(TimeValue time, TimeScale scale) { + if (time == 0 || scale == 0) + return; + + uint32 startTime = g_system->getMillis(); + uint32 timeInMillis = time * 1000 / scale; + + while (g_system->getMillis() < startTime + timeInMillis) { + checkCallBacks(); + _gfx->updateDisplay(); + } +} + +void PegasusEngine::useMenu(GameMenu *newMenu) { + if (_gameMenu) { + _gameMenu->restorePreviousHandler(); + delete _gameMenu; + } + + _gameMenu = newMenu; + + if (_gameMenu) + _gameMenu->becomeCurrentHandler(); +} + +bool PegasusEngine::checkGameMenu() { + GameMenuCommand command = kMenuCmdNoCommand; + + if (_gameMenu) { + command = _gameMenu->getLastCommand(); + if (command != kMenuCmdNoCommand) { + _gameMenu->clearLastCommand(); + doGameMenuCommand(command); + } + } + + return command != kMenuCmdNoCommand; +} + +void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) { + Common::Error result; + + switch (command) { + case kMenuCmdStartAdventure: + stopIntroTimer(); + GameState.setWalkthroughMode(false); + startNewGame(); + break; + case kMenuCmdCredits: + if (isDemo()) { + showTempScreen("Images/Demo/DemoCredits.pict"); + _gfx->doFadeOutSync(); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } else { + stopIntroTimer(); + _gfx->doFadeOutSync(); + useMenu(new CreditsMenu()); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } + break; + case kMenuCmdQuit: + case kMenuCmdDeathQuitDemo: + if (isDemo()) + showTempScreen("Images/Demo/NGquitScrn.pict"); + _system->quit(); + break; + case kMenuCmdOverview: + stopIntroTimer(); + doInterfaceOverview(); + resetIntroTimer(); + break; + case kMenuCmdStartWalkthrough: + stopIntroTimer(); + GameState.setWalkthroughMode(true); + startNewGame(); + break; + case kMenuCmdRestore: + stopIntroTimer(); + // fall through + case kMenuCmdDeathRestore: + result = showLoadDialog(); + if (command == kMenuCmdRestore && result.getCode() != Common::kNoError) + resetIntroTimer(); + break; + case kMenuCmdCreditsMainMenu: + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + break; + case kMenuCmdDeathContinue: + if (((DeathMenu *)_gameMenu)->playerWon()) { + if (isDemo()) { + showTempScreen("Images/Demo/DemoCredits.pict"); + _gfx->doFadeOutSync(); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + } else { + _gfx->doFadeOutSync(); + useMenu(0); + _gfx->clearScreen(); + _gfx->updateDisplay(); + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile(_introDirectory + "/Closing.movie")) + error("Could not load closing movie"); + + uint16 x = (640 - video->getWidth() * 2) / 2; + uint16 y = (480 - video->getHeight() * 2) / 2; + + video->start(); + playMovieScaled(video, x, y); + + delete video; + + if (shouldQuit()) + return; + + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + } + } else { + loadFromContinuePoint(); + } + break; + case kMenuCmdDeathMainMenuDemo: + case kMenuCmdDeathMainMenu: + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + break; + case kMenuCmdPauseSave: + if (showSaveDialog().getCode() != Common::kUserCanceled) + pauseMenu(false); + break; + case kMenuCmdPauseContinue: + pauseMenu(false); + break; + case kMenuCmdPauseRestore: + makeContinuePoint(); + result = showLoadDialog(); + + if (result.getCode() == Common::kNoError) { + // Successfully loaded, unpause the game + pauseMenu(false); + } else if (result.getCode() != Common::kUserCanceled) { + // Try to get us back to a sane state + loadFromContinuePoint(); + } + break; + case kMenuCmdPauseQuit: + _gfx->doFadeOutSync(); + throwAwayEverything(); + pauseMenu(false); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + resetIntroTimer(); + break; + case kMenuCmdNoCommand: + break; + default: + error("Unknown menu command %d", command); + } +} + +void PegasusEngine::handleInput(const Input &input, const Hotspot *cursorSpot) { + if (!checkGameMenu()) + shellGameInput(input, cursorSpot); + + // Handle the console here + if (input.isConsoleRequested()) { + _console->attach(); + _console->onFrame(); + } + + // Handle save requests here + if (_saveRequested && _saveAllowed) { + _saveRequested = false; + + // Can only save during a game and not in the demo + if (g_neighborhood && !isDemo()) { + pauseEngine(true); + showSaveDialog(); + pauseEngine(false); + } + } + + // Handle load requests here + if (_loadRequested && _loadAllowed) { + _loadRequested = false; + + // WORKAROUND: Do not entertain load requests when the pause menu is up + // The original did and the game entered a bad state after loading. + // It's theoretically possible to make it so it does work while the + // pause menu is up, but the pause state of the engine is just too weird. + // Just use the pause menu's restore button since it's there for that + // for you to load anyway. + if (!isDemo() && !(_gameMenu && _gameMenu->getObjectID() == kPauseMenuID)) { + pauseEngine(true); + + if (g_neighborhood) { + makeContinuePoint(); + + Common::Error result = showLoadDialog(); + if (result.getCode() != Common::kNoError && result.getCode() != Common::kUserCanceled) + loadFromContinuePoint(); + } else { + if (_introTimer) + _introTimer->stopFuse(); + + Common::Error result = showLoadDialog(); + if (result.getCode() != Common::kNoError) { + if (!_gameMenu) { + useMenu(new MainMenu()); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + } + + resetIntroTimer(); + } + } + + pauseEngine(false); + } + } +} + +void PegasusEngine::doInterfaceOverview() { + static const short kNumOverviewSpots = 11; + static const Common::Rect overviewSpots[kNumOverviewSpots] = { + Common::Rect(354, 318, 354 + 204, 318 + 12), + Common::Rect(211, 34, 211 + 114, 34 + 28), + Common::Rect(502, 344, 502 + 138, 344 + 120), + Common::Rect(132, 40, 132 + 79, 40 + 22), + Common::Rect(325, 40, 332 + 115, 40 + 22), + Common::Rect(70, 318, 70 + 284, 318 + 12), + Common::Rect(76, 334, 76 + 96, 334 + 96), + Common::Rect(64, 64, 64 + 512, 64 + 256), + Common::Rect(364, 334, 364 + 96, 334 + 96), + Common::Rect(172, 334, 172 + 192, 334 + 96), + Common::Rect(542, 36, 542 + 58, 36 + 20) + }; + + _gfx->doFadeOutSync(); + useMenu(0); + + Picture leftBackground(kNoDisplayElement); + leftBackground.initFromPICTFile("Images/Interface/OVLeft.mac"); + leftBackground.setDisplayOrder(0); + leftBackground.moveElementTo(kBackground1Left, kBackground1Top); + leftBackground.startDisplaying(); + leftBackground.show(); + + Picture topBackground(kNoDisplayElement); + topBackground.initFromPICTFile("Images/Interface/OVTop.mac"); + topBackground.setDisplayOrder(0); + topBackground.moveElementTo(kBackground2Left, kBackground2Top); + topBackground.startDisplaying(); + topBackground.show(); + + Picture rightBackground(kNoDisplayElement); + rightBackground.initFromPICTFile("Images/Interface/OVRight.mac"); + rightBackground.setDisplayOrder(0); + rightBackground.moveElementTo(kBackground3Left, kBackground3Top); + rightBackground.startDisplaying(); + rightBackground.show(); + + Picture bottomBackground(kNoDisplayElement); + bottomBackground.initFromPICTFile("Images/Interface/OVBottom.mac"); + bottomBackground.setDisplayOrder(0); + bottomBackground.moveElementTo(kBackground4Left, kBackground4Top); + bottomBackground.startDisplaying(); + bottomBackground.show(); + + Picture controllerHighlight(kNoDisplayElement); + controllerHighlight.initFromPICTFile("Images/Interface/OVcontrollerHilite.mac"); + controllerHighlight.setDisplayOrder(0); + controllerHighlight.moveElementTo(kOverviewControllerLeft, kOverviewControllerTop); + controllerHighlight.startDisplaying(); + + Movie overviewText(kNoDisplayElement); + overviewText.initFromMovieFile("Images/Interface/Overview Mac.movie"); + overviewText.setDisplayOrder(0); + overviewText.moveElementTo(kNavAreaLeft, kNavAreaTop); + overviewText.startDisplaying(); + overviewText.show(); + overviewText.redrawMovieWorld(); + + DropHighlight highlight(kNoDisplayElement); + highlight.setDisplayOrder(1); + highlight.startDisplaying(); + highlight.setHighlightThickness(4); + highlight.setHighlightColor(g_system->getScreenFormat().RGBToColor(239, 239, 0)); + highlight.setHighlightCornerDiameter(8); + + Input input; + InputDevice.getInput(input, kFilterAllInput); + + Common::Point cursorLoc; + input.getInputLocation(cursorLoc); + + uint16 i; + for (i = 0; i < kNumOverviewSpots; ++i) + if (overviewSpots[i].contains(cursorLoc)) + break; + + TimeValue time; + if (i == kNumOverviewSpots) + time = 5; + else if (i > 4) + time = i + 1; + else + time = i; + + if (time == 2) { + highlight.hide(); + controllerHighlight.show(); + } else if (i != kNumOverviewSpots) { + controllerHighlight.hide(); + Common::Rect r = overviewSpots[i]; + r.grow(5); + highlight.setBounds(r); + highlight.show(); + } else { + highlight.hide(); + controllerHighlight.hide(); + } + + overviewText.setTime(time * 3 + 2, 15); + overviewText.redrawMovieWorld(); + + _cursor->setCurrentFrameIndex(3); + _cursor->show(); + + _gfx->updateDisplay(); + _gfx->doFadeInSync(); + + for (;;) { + InputDevice.getInput(input, kFilterAllInput); + + if (input.anyInput() || shouldQuit() || _loadRequested || _saveRequested) + break; + + input.getInputLocation(cursorLoc); + for (i = 0; i < kNumOverviewSpots; ++i) + if (overviewSpots[i].contains(cursorLoc)) + break; + + if (i == kNumOverviewSpots) + time = 5; + else if (i > 4) + time = i + 1; + else + time = i; + + if (time == 2) { + highlight.hide(); + controllerHighlight.show(); + } else if (i != kNumOverviewSpots) { + controllerHighlight.hide(); + Common::Rect r = overviewSpots[i]; + r.grow(5); + highlight.setBounds(r); + highlight.show(); + } else { + highlight.hide(); + controllerHighlight.hide(); + } + + overviewText.setTime(time * 3 + 2, 15); + overviewText.redrawMovieWorld(); + + refreshDisplay(); + _system->delayMillis(10); + } + + if (shouldQuit()) + return; + + highlight.hide(); + _cursor->hide(); + + _gfx->doFadeOutSync(); + useMenu(new MainMenu()); + _gfx->updateDisplay(); + ((MainMenu *)_gameMenu)->startMainMenuLoop(); + _gfx->doFadeInSync(); + + _saveRequested = false; + _loadRequested = false; +} + +void PegasusEngine::showTempScreen(const Common::String &fileName) { + _gfx->doFadeOutSync(); + + Picture picture(0); + picture.initFromPICTFile(fileName); + picture.setDisplayOrder(kMaxAvailableOrder); + picture.startDisplaying(); + picture.show(); + _gfx->updateDisplay(); + + _gfx->doFadeInSync(); + + // Wait for the next event + bool done = false; + while (!shouldQuit() && !done) { + Common::Event event; + while (_eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + case Common::EVENT_KEYDOWN: + done = true; + break; + default: + break; + } + } + + _system->delayMillis(10); + } +} + +void PegasusEngine::refreshDisplay() { + giveIdleTime(); + _gfx->updateDisplay(); +} + +void PegasusEngine::resetEnergyDeathReason() { + switch (getCurrentNeighborhoodID()) { + case kMarsID: + _deathReason = kDeathArrestedInMars; + break; + case kNoradAlphaID: + case kNoradDeltaID: + _deathReason = kDeathArrestedInNorad; + break; + case kWSCID: + _deathReason = kDeathArrestedInWSC; + break; + default: + _deathReason = kDeathStranded; + break; + } +} + +bool PegasusEngine::playerHasItem(const Item *item) { + return playerHasItemID(item->getObjectID()); +} + +bool PegasusEngine::playerHasItemID(const ItemID itemID) { + return itemInInventory(itemID) || itemInBiochips(itemID); +} + +InventoryItem *PegasusEngine::getCurrentInventoryItem() { + if (g_interface) + return g_interface->getCurrentInventoryItem(); + + return 0; +} + +bool PegasusEngine::itemInInventory(InventoryItem *item) { + return _items.itemInInventory(item); +} + +bool PegasusEngine::itemInInventory(ItemID id) { + return _items.itemInInventory(id); +} + +BiochipItem *PegasusEngine::getCurrentBiochip() { + if (g_interface) + return g_interface->getCurrentBiochip(); + + return 0; +} + +bool PegasusEngine::itemInBiochips(BiochipItem *item) { + return _biochips.itemInInventory(item); +} + +bool PegasusEngine::itemInBiochips(ItemID id) { + return _biochips.itemInInventory(id); +} + +bool PegasusEngine::playerAlive() { + return (_shellNotification.getNotificationFlags() & kPlayerDiedFlag) == 0; +} + +Common::String PegasusEngine::getBriefingMovie() { + if (_neighborhood) + return _neighborhood->getBriefingMovie(); + + return Common::String(); +} + +Common::String PegasusEngine::getEnvScanMovie() { + if (_neighborhood) + return _neighborhood->getEnvScanMovie(); + + return Common::String(); +} + +uint PegasusEngine::getNumHints() { + if (_neighborhood) + return _neighborhood->getNumHints(); + + return 0; +} + +Common::String PegasusEngine::getHintMovie(uint hintNum) { + if (_neighborhood) + return _neighborhood->getHintMovie(hintNum); + + return Common::String(); +} + +bool PegasusEngine::canSolve() { + if (_neighborhood) + return _neighborhood->canSolve(); + + return false; +} + +void PegasusEngine::prepareForAIHint(const Common::String &movieName) { + if (g_neighborhood) + g_neighborhood->prepareForAIHint(movieName); +} + +void PegasusEngine::cleanUpAfterAIHint(const Common::String &movieName) { + if (g_neighborhood) + g_neighborhood->cleanUpAfterAIHint(movieName); +} + +void PegasusEngine::jumpToNewEnvironment(const NeighborhoodID neighborhoodID, const RoomID roomID, const DirectionConstant direction) { + GameState.setNextLocation(neighborhoodID, roomID, direction); + _shellNotification.setNotificationFlags(kNeedNewJumpFlag, kNeedNewJumpFlag); +} + +void PegasusEngine::checkFlashlight() { + if (_neighborhood) + _neighborhood->checkFlashlight(); +} + +bool PegasusEngine::playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16 y) { + bool skipped = false; + + while (!shouldQuit() && !video->endOfVideo() && !skipped) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) + drawScaledFrame(frame, x, y); + } + + Input input; + InputDevice.getInput(input, kFilterAllInput); + if (input.anyInput() || _saveRequested || _loadRequested) + skipped = true; + + _system->delayMillis(10); + } + + return skipped; +} + +void PegasusEngine::die(const DeathReason reason) { + Input dummy; + if (isDragging()) + _itemDragger.stopTracking(dummy); + + _deathReason = reason; + _shellNotification.setNotificationFlags(kPlayerDiedFlag, kPlayerDiedFlag); +} + +void PegasusEngine::doDeath() { + _gfx->doFadeOutSync(); + throwAwayEverything(); + useMenu(new DeathMenu(_deathReason)); + _gfx->updateDisplay(); + _gfx->doFadeInSync(); +} + +void PegasusEngine::throwAwayEverything() { + if (_items.getNumItems() != 0 && g_interface) + _currentItemID = g_interface->getCurrentInventoryItem()->getObjectID(); + else + _currentItemID = kNoItemID; + + if (_biochips.getNumItems() != 0 && g_interface) + _currentBiochipID = g_interface->getCurrentBiochip()->getObjectID(); + else + _currentBiochipID = kNoItemID; + + useMenu(0); + useNeighborhood(0); + + delete g_interface; + g_interface = 0; +} + +void PegasusEngine::processShell() { + checkCallBacks(); + checkNotifications(); + InputHandler::pollForInput(); + refreshDisplay(); +} + +void PegasusEngine::createInterface() { + if (!g_interface) + new Interface(); + + g_interface->createInterface(); +} + +void PegasusEngine::setGameMode(const GameMode newMode) { + if (newMode != _gameMode && canSwitchGameMode(newMode, _gameMode)) { + switchGameMode(newMode, _gameMode); + _gameMode = newMode; + } +} + +void PegasusEngine::switchGameMode(const GameMode newMode, const GameMode oldMode) { + // Start raising panels before lowering panels, to give the activating panel time + // to set itself up without cutting into the lowering panel's animation time. + + if (_switchModesSync) { + if (newMode == kModeInventoryPick) + raiseInventoryDrawerSync(); + else if (newMode == kModeBiochipPick) + raiseBiochipDrawerSync(); + else if (newMode == kModeInfoScreen) + showInfoScreen(); + + if (oldMode == kModeInventoryPick) + lowerInventoryDrawerSync(); + else if (oldMode == kModeBiochipPick) + lowerBiochipDrawerSync(); + else if (oldMode == kModeInfoScreen) + hideInfoScreen(); + } else { + if (newMode == kModeInventoryPick) + raiseInventoryDrawer(); + else if (newMode == kModeBiochipPick) + raiseBiochipDrawer(); + else if (newMode == kModeInfoScreen) + showInfoScreen(); + + if (oldMode == kModeInventoryPick) + lowerInventoryDrawer(); + else if (oldMode == kModeBiochipPick) + lowerBiochipDrawer(); + else if (oldMode == kModeInfoScreen) + hideInfoScreen(); + } +} + +bool PegasusEngine::canSwitchGameMode(const GameMode newMode, const GameMode oldMode) { + if (newMode == kModeInventoryPick && oldMode == kModeBiochipPick) + return false; + if (newMode == kModeBiochipPick && oldMode == kModeInventoryPick) + return false; + return true; +} + +bool PegasusEngine::itemInLocation(const ItemID itemID, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { + NeighborhoodID itemNeighborhood; + RoomID itemRoom; + DirectionConstant itemDirection; + + Item *item = _allItems.findItemByID(itemID); + item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + + return itemNeighborhood == neighborhood && itemRoom == room && itemDirection == direction; +} + +InventoryResult PegasusEngine::addItemToInventory(InventoryItem *item) { + InventoryResult result; + + do { + if (g_interface) + result = g_interface->addInventoryItem(item); + else + result = _items.addItem(item); + + if (result == kTooMuchWeight) + destroyInventoryItem(pickItemToDestroy()); + } while (result != kInventoryOK); + + GameState.setTakenItem(item, true); + if (g_neighborhood) + g_neighborhood->pickedUpItem(item); + + g_AIArea->checkMiddleArea(); + + return result; +} + +void PegasusEngine::useNeighborhood(Neighborhood *neighborhood) { + delete _neighborhood; + _neighborhood = neighborhood; + + if (_neighborhood) { + InputHandler::setInputHandler(_neighborhood); + _neighborhood->init(); + _neighborhood->moveNavTo(kNavAreaLeft, kNavAreaTop); + g_interface->setDate(_neighborhood->getDateResID()); + } else { + InputHandler::setInputHandler(this); + } +} + +void PegasusEngine::performJump(NeighborhoodID neighborhoodID) { + if (_neighborhood) + useNeighborhood(0); + + // Sub chase is special + if (neighborhoodID == kNoradSubChaseID) { + throwAwayEverything(); + _loadAllowed = false; + doSubChase(); + + if (shouldQuit()) + return; + + neighborhoodID = kNoradDeltaID; + GameState.setNextRoom(kNorad41); + GameState.setNextDirection(kEast); + _loadAllowed = true; + } + + Neighborhood *neighborhood; + makeNeighborhood(neighborhoodID, neighborhood); + useNeighborhood(neighborhood); + + // Update the CD variable (used just for saves currently) + _currentCD = getNeighborhoodCD(neighborhoodID); +} + +void PegasusEngine::startNeighborhood() { + if (g_interface && _currentItemID != kNoItemID) + g_interface->setCurrentInventoryItemID(_currentItemID); + + if (g_interface && _currentBiochipID != kNoItemID) + g_interface->setCurrentBiochipID(_currentBiochipID); + + setGameMode(kModeNavigation); + + if (_neighborhood) + _neighborhood->start(); +} + +void PegasusEngine::startNewGame() { + // WORKAROUND: The original game ignored the menu difficulty + // setting. We're going to pass it through here so that + // the menu actually makes sense now. + bool isWalkthrough = GameState.getWalkthroughMode(); + GameState.resetGameState(); + GameState.setWalkthroughMode(isWalkthrough); + + // TODO: Enable erase + _gfx->doFadeOutSync(); + useMenu(0); + _gfx->updateDisplay(); + _gfx->enableUpdates(); + + createInterface(); + + if (isDemo()) { + setLastEnergyValue(kFullEnergy); + jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth); + GameState.setPrehistoricSeenTimeStream(false); + GameState.setPrehistoricSeenFlyer1(false); + GameState.setPrehistoricSeenFlyer2(false); + GameState.setPrehistoricSeenBridgeZoom(false); + GameState.setPrehistoricBreakerThrown(false); + } else { + jumpToNewEnvironment(kCaldoriaID, kCaldoria00, kEast); + } + + removeAllItemsFromInventory(); + removeAllItemsFromBiochips(); + + BiochipItem *biochip = (BiochipItem *)_allItems.findItemByID(kAIBiochip); + addItemToBiochips(biochip); + + if (isDemo()) { + biochip = (BiochipItem *)_allItems.findItemByID(kPegasusBiochip); + addItemToBiochips(biochip); + biochip = (BiochipItem *)_allItems.findItemByID(kMapBiochip); + addItemToBiochips(biochip); + InventoryItem *item = (InventoryItem *)_allItems.findItemByID(kKeyCard); + addItemToInventory(item); + item = (InventoryItem *)_allItems.findItemByID(kJourneymanKey); + addItemToInventory(item); + _currentItemID = kJourneymanKey; + } else { + _currentItemID = kNoItemID; + } + + _currentBiochipID = kAIBiochip; + + // Clear jump notification flags and just perform the jump... + _shellNotification.setNotificationFlags(0, kNeedNewJumpFlag); + + performJump(GameState.getNextNeighborhood()); + + startNeighborhood(); +} + +void PegasusEngine::makeNeighborhood(NeighborhoodID neighborhoodID, Neighborhood *&neighborhood) { + // TODO: CD check + + switch (neighborhoodID) { + case kCaldoriaID: + neighborhood = new Caldoria(g_AIArea, this); + break; + case kMarsID: + neighborhood = new Mars(g_AIArea, this); + break; + case kPrehistoricID: + neighborhood = new Prehistoric(g_AIArea, this); + break; + case kFullTSAID: + neighborhood = new FullTSA(g_AIArea, this); + break; + case kTinyTSAID: + neighborhood = new TinyTSA(g_AIArea, this); + break; + case kWSCID: + neighborhood = new WSC(g_AIArea, this); + break; + case kNoradAlphaID: + neighborhood = new NoradAlpha(g_AIArea, this); + break; + case kNoradDeltaID: + createInterface(); + neighborhood = new NoradDelta(g_AIArea, this); + break; + default: + error("Unknown neighborhood %d", neighborhoodID); + } +} + +bool PegasusEngine::wantsCursor() { + return _gameMenu == 0; +} + +void PegasusEngine::updateCursor(const Common::Point, const Hotspot *cursorSpot) { + if (_itemDragger.isTracking()) { + _cursor->setCurrentFrameIndex(5); + } else { + if (!cursorSpot) { + _cursor->setCurrentFrameIndex(0); + } else { + uint32 id = cursorSpot->getObjectID(); + + switch (id) { + case kCurrentItemSpotID: + if (countInventoryItems() != 0) + _cursor->setCurrentFrameIndex(4); + else + _cursor->setCurrentFrameIndex(0); + break; + default: + HotSpotFlags flags = cursorSpot->getHotspotFlags(); + + if (flags & kZoomInSpotFlag) + _cursor->setCurrentFrameIndex(1); + else if (flags & kZoomOutSpotFlag) + _cursor->setCurrentFrameIndex(2); + else if (flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) + _cursor->setCurrentFrameIndex(4); + else if (flags & kJMPClickingSpotFlags) + _cursor->setCurrentFrameIndex(3); + else + _cursor->setCurrentFrameIndex(0); + } + } + } +} + +void PegasusEngine::toggleInventoryDisplay() { + if (_gameMode == kModeInventoryPick) + setGameMode(kModeNavigation); + else + setGameMode(kModeInventoryPick); +} + +void PegasusEngine::toggleBiochipDisplay() { + if (_gameMode == kModeBiochipPick) + setGameMode(kModeNavigation); + else + setGameMode(kModeBiochipPick); +} + +void PegasusEngine::showInfoScreen() { + if (g_neighborhood) { + // Break the input handler chain... + _savedHandler = InputHandler::getCurrentHandler(); + InputHandler::setInputHandler(this); + + Picture *pushPicture = ((Neighborhood *)g_neighborhood)->getTurnPushPicture(); + + _bigInfoMovie.shareSurface(pushPicture); + _smallInfoMovie.shareSurface(pushPicture); + + g_neighborhood->hideNav(); + + _smallInfoMovie.initFromMovieFile("Images/Items/Info Right Movie"); + _smallInfoMovie.setDisplayOrder(kInfoSpinOrder); + _smallInfoMovie.moveElementTo(kNavAreaLeft + 304, kNavAreaTop + 8); + _smallInfoMovie.moveMovieBoxTo(304, 8); + _smallInfoMovie.startDisplaying(); + _smallInfoMovie.show(); + + TimeValue startTime, stopTime; + g_AIArea->getSmallInfoSegment(startTime, stopTime); + _smallInfoMovie.setSegment(startTime, stopTime); + _smallInfoMovie.setTime(startTime); + _smallInfoMovie.setFlags(kLoopTimeBase); + + _bigInfoMovie.initFromMovieFile("Images/Items/Info Left Movie"); + _bigInfoMovie.setDisplayOrder(kInfoBackgroundOrder); + _bigInfoMovie.moveElementTo(kNavAreaLeft, kNavAreaTop); + _bigInfoMovie.startDisplaying(); + _bigInfoMovie.show(); + _bigInfoMovie.setTime(g_AIArea->getBigInfoTime()); + + _bigInfoMovie.redrawMovieWorld(); + _smallInfoMovie.redrawMovieWorld(); + _smallInfoMovie.start(); + } +} + +void PegasusEngine::hideInfoScreen() { + if (g_neighborhood) { + InputHandler::setInputHandler(_savedHandler); + + _bigInfoMovie.hide(); + _bigInfoMovie.stopDisplaying(); + _bigInfoMovie.releaseMovie(); + + _smallInfoMovie.hide(); + _smallInfoMovie.stopDisplaying(); + _smallInfoMovie.stop(); + _smallInfoMovie.releaseMovie(); + + g_neighborhood->showNav(); + } +} + +void PegasusEngine::raiseInventoryDrawer() { + if (g_interface) + g_interface->raiseInventoryDrawer(); +} + +void PegasusEngine::raiseBiochipDrawer() { + if (g_interface) + g_interface->raiseBiochipDrawer(); +} + +void PegasusEngine::lowerInventoryDrawer() { + if (g_interface) + g_interface->lowerInventoryDrawer(); +} + +void PegasusEngine::lowerBiochipDrawer() { + if (g_interface) + g_interface->lowerBiochipDrawer(); +} + +void PegasusEngine::raiseInventoryDrawerSync() { + if (g_interface) + g_interface->raiseInventoryDrawerSync(); +} + +void PegasusEngine::raiseBiochipDrawerSync() { + if (g_interface) + g_interface->raiseBiochipDrawerSync(); +} + +void PegasusEngine::lowerInventoryDrawerSync() { + if (g_interface) + g_interface->lowerInventoryDrawerSync(); +} + +void PegasusEngine::lowerBiochipDrawerSync() { + if (g_interface) + g_interface->lowerBiochipDrawerSync(); +} + +void PegasusEngine::toggleInfo() { + if (_gameMode == kModeInfoScreen) + setGameMode(kModeNavigation); + else if (_gameMode == kModeNavigation) + setGameMode(kModeInfoScreen); +} + +void PegasusEngine::dragTerminated(const Input &) { + Hotspot *finalSpot = _itemDragger.getLastHotspot(); + InventoryResult result; + + if (_dragType == kDragInventoryPickup) { + if (finalSpot && finalSpot->getObjectID() == kInventoryDropSpotID) + result = addItemToInventory((InventoryItem *)_draggingItem); + else + result = kTooMuchWeight; + + if (result != kInventoryOK) + autoDragItemIntoRoom(_draggingItem, _draggingSprite); + else + delete _draggingSprite; + } else if (_dragType == kDragBiochipPickup) { + if (finalSpot && finalSpot->getObjectID() == kBiochipDropSpotID) + result = addItemToBiochips((BiochipItem *)_draggingItem); + else + result = kTooMuchWeight; + + if (result != kInventoryOK) + autoDragItemIntoRoom(_draggingItem, _draggingSprite); + else + delete _draggingSprite; + } else if (_dragType == kDragInventoryUse) { + if (finalSpot && (finalSpot->getHotspotFlags() & kDropItemSpotFlag) != 0) { + // *** Need to decide on a case by case basis what to do here. + // the crowbar should break the cover off the Mars reactor if its frozen, the + // global transport card should slide through the slot, the oxygen mask should + // attach to the filling station, and so on... + _neighborhood->dropItemIntoRoom(_draggingItem, finalSpot); + delete _draggingSprite; + } else { + autoDragItemIntoInventory(_draggingItem, _draggingSprite); + } + } + + _dragType = kDragNoDrag; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + + +void PegasusEngine::dragItem(const Input &input, Item *item, DragType type) { + _draggingItem = item; + _dragType = type; + + // Create the sprite. + _draggingSprite = _draggingItem->getDragSprite(kDraggingSpriteID); + Common::Point where; + input.getInputLocation(where); + Common::Rect r1; + _draggingSprite->getBounds(r1); + r1 = Common::Rect::center(where.x, where.y, r1.width(), r1.height()); + _draggingSprite->setBounds(r1); + + // Set up drag constraints. + DisplayElement *navMovie = _gfx->findDisplayElement(kNavMovieID); + Common::Rect r2; + navMovie->getBounds(r2); + r2.left -= r1.width() / 3; + r2.right += r1.width() / 3; + r2.top -= r1.height() / 3; + r2.bottom += r2.height() / 3; + + r1 = Common::Rect(-30000, -30000, 30000, 30000); + _itemDragger.setDragConstraints(r2, r1); + + // Start dragging. + _draggingSprite->setDisplayOrder(kDragSpriteOrder); + _draggingSprite->startDisplaying(); + _draggingSprite->show(); + _itemDragger.setDragSprite(_draggingSprite); + _itemDragger.setNextHandler(_neighborhood); + _itemDragger.startTracking(input); + + if (g_AIArea) + g_AIArea->lockAIOut(); +} + +void PegasusEngine::shellGameInput(const Input &input, const Hotspot *cursorSpot) { + if (_gameMode == kModeInfoScreen) { + if (JMPPPInput::isToggleAIMiddleInput(input)) { + LowerClientSignature middleOwner = g_AIArea->getMiddleAreaOwner(); + g_AIArea->toggleMiddleAreaOwner(); + + if (middleOwner != g_AIArea->getMiddleAreaOwner()) { + _bigInfoMovie.setTime(g_AIArea->getBigInfoTime()); + _smallInfoMovie.stop(); + _smallInfoMovie.setFlags(0); + + TimeValue startTime, stopTime; + g_AIArea->getSmallInfoSegment(startTime, stopTime); + _smallInfoMovie.setSegment(startTime, stopTime); + _smallInfoMovie.setTime(startTime); + _smallInfoMovie.setFlags(kLoopTimeBase); + + _bigInfoMovie.redrawMovieWorld(); + _smallInfoMovie.redrawMovieWorld(); + _smallInfoMovie.start(); + } + } + } else { + if (JMPPPInput::isRaiseInventoryInput(input)) + toggleInventoryDisplay(); + + if (JMPPPInput::isRaiseBiochipsInput(input)) + toggleBiochipDisplay(); + + if (JMPPPInput::isTogglePauseInput(input) && _neighborhood) + pauseMenu(!isPaused()); + } + + if (JMPPPInput::isToggleInfoInput(input)) + toggleInfo(); +} + +void PegasusEngine::activateHotspots() { + if (_gameMode == kModeInfoScreen) { + _allHotspots.activateOneHotspot(kInfoReturnSpotID); + } else { + // Set up hot spots. + if (_dragType == kDragInventoryPickup) + _allHotspots.activateOneHotspot(kInventoryDropSpotID); + else if (_dragType == kDragBiochipPickup) + _allHotspots.activateOneHotspot(kBiochipDropSpotID); + else if (_dragType == kDragNoDrag) + _allHotspots.activateMaskedHotspots(kShellSpotFlag); + } +} + +bool PegasusEngine::isClickInput(const Input &input, const Hotspot *cursorSpot) { + if (_cursor->isVisible() && cursorSpot) + return JMPPPInput::isClickInput(input); + else + return false; +} + +InputBits PegasusEngine::getClickFilter() { + return JMPPPInput::getClickInputFilter(); +} + +void PegasusEngine::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { + if (clickedSpot->getObjectID() == kCurrentItemSpotID) { + InventoryItem *currentItem = getCurrentInventoryItem(); + if (currentItem) { + removeItemFromInventory(currentItem); + dragItem(input, currentItem, kDragInventoryUse); + } + } else if (clickedSpot->getObjectID() == kInfoReturnSpotID) { + toggleInfo(); + } +} + +InventoryResult PegasusEngine::removeItemFromInventory(InventoryItem *item) { + InventoryResult result; + + if (g_interface) + result = g_interface->removeInventoryItem(item); + else + result = _items.removeItem(item); + + // This should never happen + assert(result == kInventoryOK); + + return result; +} + +void PegasusEngine::removeAllItemsFromInventory() { + if (g_interface) + g_interface->removeAllItemsFromInventory(); + else + _items.removeAllItems(); +} + +///////////////////////////////////////////// +// +// Biochip handling. + +// Adding biochips to the biochip drawer is a little funny, because of two things: +// -- We get the map biochip and pegasus biochip at the same time by dragging +// one sprite in TSA +// -- We can drag in more than one copy of the various biochips. +// Because of this we need to make sure that no more than one copy of each biochip +// is ever added. + +InventoryResult PegasusEngine::addItemToBiochips(BiochipItem *biochip) { + InventoryResult result; + + if (g_interface) + result = g_interface->addBiochip(biochip); + else + result = _biochips.addItem(biochip); + + // This can never happen + assert(result == kInventoryOK); + + GameState.setTakenItem(biochip, true); + + if (g_neighborhood) + g_neighborhood->pickedUpItem(biochip); + + g_AIArea->checkMiddleArea(); + + return result; +} + +void PegasusEngine::removeAllItemsFromBiochips() { + if (g_interface) + g_interface->removeAllItemsFromBiochips(); + else + _biochips.removeAllItems(); +} + +void PegasusEngine::setSoundFXLevel(uint16 fxLevel) { + _FXLevel = fxLevel; + if (_neighborhood) + _neighborhood->setSoundFXLevel(fxLevel); + if (g_AIArea) + g_AIArea->setAIVolume(fxLevel); +} + +void PegasusEngine::setAmbienceLevel(uint16 ambientLevel) { + _ambientLevel = ambientLevel; + if (_neighborhood) + _neighborhood->setAmbienceLevel(ambientLevel); +} + +void PegasusEngine::pauseMenu(bool menuUp) { + if (menuUp) { + pauseEngine(true); + _screenDimmer.startDisplaying(); + _screenDimmer.show(); + _gfx->updateDisplay(); + useMenu(new PauseMenu()); + } else { + pauseEngine(false); + _screenDimmer.hide(); + _screenDimmer.stopDisplaying(); + useMenu(0); + g_AIArea->checkMiddleArea(); + } +} + +void PegasusEngine::autoDragItemIntoRoom(Item *item, Sprite *draggingSprite) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + Common::Point start, stop; + draggingSprite->getLocation(start.x, start.y); + + Hotspot *dropSpot = _neighborhood->getItemScreenSpot(item, draggingSprite); + + if (dropSpot) { + dropSpot->getCenter(stop.x, stop.y); + } else { + stop.x = kNavAreaLeft + 256; + stop.y = kNavAreaTop + 128; + } + + Common::Rect bounds; + draggingSprite->getBounds(bounds); + stop.x -= bounds.width() >> 1; + stop.y -= bounds.height() >> 1; + + int dx = ABS(stop.x - start.x); + int dy = ABS(stop.y - start.y); + TimeValue time = MAX(dx, dy); + + allowInput(false); + _autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale); + + while (_autoDragger.isDragging()) { + checkCallBacks(); + refreshDisplay(); + _system->delayMillis(10); + } + + _neighborhood->dropItemIntoRoom(_draggingItem, dropSpot); + allowInput(true); + delete _draggingSprite; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + +void PegasusEngine::autoDragItemIntoInventory(Item *, Sprite *draggingSprite) { + if (g_AIArea) + g_AIArea->lockAIOut(); + + Common::Point start; + draggingSprite->getLocation(start.x, start.y); + + Common::Rect r; + draggingSprite->getBounds(r); + + Common::Point stop((76 + 172 - r.width()) / 2, 334 - (2 * r.height() / 3)); + + int dx = ABS(stop.x - start.x); + int dy = ABS(stop.y - start.y); + TimeValue time = MAX(dx, dy); + + allowInput(false); + _autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale); + + while (_autoDragger.isDragging()) { + checkCallBacks(); + refreshDisplay(); + _system->delayMillis(10); + } + + addItemToInventory((InventoryItem *)_draggingItem); + allowInput(true); + delete _draggingSprite; + + if (g_AIArea) + g_AIArea->unlockAI(); +} + +NeighborhoodID PegasusEngine::getCurrentNeighborhoodID() const { + if (_neighborhood) + return _neighborhood->getObjectID(); + + return kNoNeighborhoodID; +} + +void PegasusEngine::pauseEngineIntern(bool pause) { + Engine::pauseEngineIntern(pause); + + if (pause) { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->pause(); + } else { + for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) + (*it)->resume(); + } +} + +uint PegasusEngine::getRandomBit() { + return _rnd->getRandomBit(); +} + +uint PegasusEngine::getRandomNumber(uint max) { + return _rnd->getRandomNumber(max); +} + +void PegasusEngine::shuffleArray(int32 *arr, int32 count) { + if (count > 1) { + for (int32 i = 1; i < count; ++i) { + int32 j = _rnd->getRandomNumber(i); + if (j != i) + SWAP(arr[i], arr[j]); + } + } +} + +void PegasusEngine::playEndMessage() { + if (g_interface) { + allowInput(false); + g_interface->playEndMessage(); + allowInput(true); + } + + die(kPlayerWonGame); +} + +void PegasusEngine::doSubChase() { + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadFile("Images/Norad Alpha/Sub Chase Movie")) + error("Failed to load sub chase"); + + video->setEndTime(Audio::Timestamp(0, 133200, 600)); + video->start(); + + while (!shouldQuit() && !video->endOfVideo()) { + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) + drawScaledFrame(frame, 0, 0); + } + + Common::Event event; + while (_eventMan->pollEvent(event)) + ; + + _system->delayMillis(10); + } + + delete video; +} + +template<typename PixelInt> +static void scaleFrame(const PixelInt *src, PixelInt *dst, int w, int h, int srcPitch) { + assert((srcPitch % sizeof(PixelInt)) == 0); // sanity check; allows some simpler code + + PixelInt *dst1 = dst; + PixelInt *dst2 = dst + w * 2; + + int srcInc = (srcPitch / sizeof(PixelInt)) - w; + int dstInc = w * 2; + + while (h--) { + for (int x = 0; x < w; x++) { + PixelInt pixel = *src++; + *dst1++ = pixel; + *dst1++ = pixel; + *dst2++ = pixel; + *dst2++ = pixel; + } + + src += srcInc; + dst1 += dstInc; + dst2 += dstInc; + } +} + +void PegasusEngine::drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y) { + // Scale up the frame doing some simple scaling + Graphics::Surface scaledFrame; + scaledFrame.create(frame->w * 2, frame->h * 2, frame->format); + + if (frame->format.bytesPerPixel == 2) + scaleFrame<uint16>((uint16 *)frame->pixels, (uint16 *)scaledFrame.pixels, frame->w, frame->h, frame->pitch); + else + scaleFrame<uint32>((uint32 *)frame->pixels, (uint32 *)scaledFrame.pixels, frame->w, frame->h, frame->pitch); + + _system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h); + _system->updateScreen(); + scaledFrame.free(); +} + +void PegasusEngine::destroyInventoryItem(const ItemID itemID) { + InventoryItem *item = (InventoryItem *)_allItems.findItemByID(itemID); + + ItemExtraEntry entry; + + switch (itemID) { + case kAirMask: + item->findItemExtra(kRemoveAirMask, entry); + item->setItemRoom(kMarsID, kMars49, kSouth); + break; + case kArgonCanister: + item->findItemExtra(kRemoveArgon, entry); + item->setItemRoom(kWSCID, kWSC02Morph, kSouth); + break; + case kCrowbar: + item->findItemExtra(kRemoveCrowbar, entry); + item->setItemRoom(kMarsID, kMars34, kSouth); + break; + case kJourneymanKey: + item->findItemExtra(kRemoveJourneymanKey, entry); + item->setItemRoom(kFullTSAID, kTSA22Red, kEast); + break; + case kMarsCard: + item->findItemExtra(kRemoveMarsCard, entry); + item->setItemRoom(kMarsID, kMars31South, kSouth); + break; + case kNitrogenCanister: + item->findItemExtra(kRemoveNitrogen, entry); + item->setItemRoom(kWSCID, kWSC02Messages, kSouth); + break; + case kOrangeJuiceGlassEmpty: + item->findItemExtra(kRemoveGlass, entry); + item->setItemRoom(kCaldoriaID, kCaldoriaReplicator, kNorth); + break; + case kPoisonDart: + item->findItemExtra(kRemoveDart, entry); + item->setItemRoom(kWSCID, kWSC01, kWest); + break; + case kSinclairKey: + item->findItemExtra(kRemoveSinclairKey, entry); + item->setItemRoom(kWSCID, kWSC02Morph, kSouth); + break; + default: + return; + } + + g_interface->setCurrentInventoryItemID(itemID); + g_AIArea->playAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); + removeItemFromInventory(item); +} + +ItemID PegasusEngine::pickItemToDestroy() { +/* + Must pick an item to destroy + + Part I: Polite -- try to find an item that's been used + Part II: Desperate -- return the first available item. +*/ + + // Polite: + if (playerHasItemID(kOrangeJuiceGlassEmpty)) + return kOrangeJuiceGlassEmpty; + if (playerHasItemID(kPoisonDart)) { + if (GameState.getCurrentNeighborhood() != kWSCID || + GameState.getWSCAnalyzedDart()) + return kPoisonDart; + } + if (playerHasItemID(kJourneymanKey)) { + if (GameState.getTSAState() >= kTSAPlayerGotHistoricalLog && + GameState.getTSAState() != kPlayerOnWayToPrehistoric && + GameState.getTSAState() != kPlayerWentToPrehistoric) + return kJourneymanKey; + } + if (playerHasItemID(kMarsCard)) { + if (GameState.getCurrentNeighborhood() != kMarsID || GameState.getMarsArrivedBelow()) + return kMarsCard; + } + + // Don't want to deal with deleting the sinclair key and argon canister, since it's + // impossible to pick them up one at a time. + + if (playerHasItemID(kNitrogenCanister)) { + if (GameState.getScoringGotCardBomb() && GameState.getCurrentNeighborhood() != kMarsID) + return kNitrogenCanister; + } + if (playerHasItemID(kCrowbar)) { + if (GameState.getCurrentNeighborhood() == kWSCID) { + if (GameState.getCurrentRoom() >= kWSC62) + return kCrowbar; + } else if (GameState.getCurrentNeighborhood() == kMarsID) { + if (GameState.getScoringGotCardBomb()) + return kCrowbar; + } else + return kCrowbar; + } + if (playerHasItemID(kAirMask)) { + if (GameState.getCurrentNeighborhood() == kMarsID) { + if (g_neighborhood->getAirQuality(GameState.getCurrentRoom()) == kAirQualityGood) + return kAirMask; + } else if (GameState.getCurrentNeighborhood() != kNoradAlphaID && + GameState.getCurrentNeighborhood() != kNoradDeltaID) { + return kAirMask; + } + } + + // Desperate: + if (playerHasItemID(kPoisonDart)) + return kPoisonDart; + if (playerHasItemID(kJourneymanKey)) + return kJourneymanKey; + if (playerHasItemID(kMarsCard)) + return kMarsCard; + if (playerHasItemID(kNitrogenCanister)) + return kNitrogenCanister; + if (playerHasItemID(kCrowbar)) + return kCrowbar; + if (playerHasItemID(kAirMask)) + return kAirMask; + + // Should never get this far... + error("Could not find item to delete"); + + return kNoItemID; +} + +uint PegasusEngine::getNeighborhoodCD(const NeighborhoodID neighborhood) const { + switch (neighborhood) { + case kCaldoriaID: + case kNoradAlphaID: + case kNoradSubChaseID: + return 1; + case kFullTSAID: + case kPrehistoricID: + return 2; + case kMarsID: + return 3; + case kWSCID: + case kNoradDeltaID: + return 4; + case kTinyTSAID: + // Tiny TSA exists on three of the CD's, so just continue + // with the CD we're on + return _currentCD; + } + + // Can't really happen, but it's a good fallback anyway :P + return 1; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h new file mode 100644 index 0000000000..2a8ba22470 --- /dev/null +++ b/engines/pegasus/pegasus.h @@ -0,0 +1,327 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_H +#define PEGASUS_H + +#include "common/list.h" +#include "common/macresman.h" +#include "common/rect.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/util.h" + +#include "engines/engine.h" + +#include "pegasus/graphics.h" +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" +#include "pegasus/items/autodragger.h" +#include "pegasus/items/inventory.h" +#include "pegasus/items/itemdragger.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Common { + class RandomSource; +} + +namespace Video { + class VideoDecoder; +} + +namespace Pegasus { + +class PegasusConsole; +struct PegasusGameDescription; +class SoundManager; +class GraphicsManager; +class Idler; +class Cursor; +class TimeBase; +class GameMenu; +class InventoryItem; +class BiochipItem; +class Neighborhood; + +class PegasusEngine : public ::Engine, public InputHandler, public NotificationManager { +friend class InputHandler; + +public: + PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc); + virtual ~PegasusEngine(); + + // Engine stuff + const PegasusGameDescription *_gameDescription; + bool hasFeature(EngineFeature f) const; + GUI::Debugger *getDebugger(); + bool canLoadGameStateCurrently(); + bool canSaveGameStateCurrently(); + Common::Error loadGameState(int slot); + Common::Error saveGameState(int slot, const Common::String &desc); + + // Base classes + GraphicsManager *_gfx; + Common::MacResManager *_resFork; + Cursor *_cursor; + + // Menu + void useMenu(GameMenu *menu); + bool checkGameMenu(); + + // Misc. + bool isDemo() const; + void addIdler(Idler *idler); + void removeIdler(Idler *idler); + void addTimeBase(TimeBase *timeBase); + void removeTimeBase(TimeBase *timeBase); + void delayShell(TimeValue time, TimeScale scale); + void resetIntroTimer(); + void introTimerExpired(); + void refreshDisplay(); + bool playerAlive(); + void processShell(); + void checkCallBacks(); + void createInterface(); + void setGameMode(const GameMode); + GameMode getGameMode() const { return _gameMode; } + uint getRandomBit(); + uint getRandomNumber(uint max); + void shuffleArray(int32 *arr, int32 count); + void drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y); + HotspotList &getAllHotspots() { return _allHotspots; } + + // Energy + void setLastEnergyValue(const int32 value) { _savedEnergyValue = value; } + int32 getSavedEnergyValue() { return _savedEnergyValue; } + + // Death + void setEnergyDeathReason(const DeathReason reason) { _deathReason = reason; } + DeathReason getEnergyDeathReason() { return _deathReason; } + void resetEnergyDeathReason(); + void die(const DeathReason); + void playEndMessage(); + + // Volume + uint16 getSoundFXLevel() { return _FXLevel; } + void setSoundFXLevel(uint16); + uint16 getAmbienceLevel() { return _ambientLevel; } + void setAmbienceLevel(uint16); + + // Items + ItemList &getAllItems() { return _allItems; } + bool playerHasItem(const Item *); + bool playerHasItemID(const ItemID); + void checkFlashlight(); + bool itemInLocation(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + + // Inventory Items + InventoryItem *getCurrentInventoryItem(); + bool itemInInventory(InventoryItem *); + bool itemInInventory(ItemID); + Inventory *getItemsInventory() { return &_items; } + InventoryResult addItemToInventory(InventoryItem *); + void removeAllItemsFromInventory(); + InventoryResult removeItemFromInventory(InventoryItem *); + uint32 countInventoryItems() { return _items.getNumItems(); } + + // Biochips + BiochipItem *getCurrentBiochip(); + bool itemInBiochips(BiochipItem *); + bool itemInBiochips(ItemID); + Inventory *getBiochipsInventory() { return &_biochips; } + void removeAllItemsFromBiochips(); + InventoryResult addItemToBiochips(BiochipItem *); + + // AI + Common::String getBriefingMovie(); + Common::String getEnvScanMovie(); + uint getNumHints(); + Common::String getHintMovie(uint); + bool canSolve(); + void prepareForAIHint(const Common::String &); + void cleanUpAfterAIHint(const Common::String &); + Common::SeekableReadStream *_aiSaveStream; + + // Neighborhood + void jumpToNewEnvironment(const NeighborhoodID, const RoomID, const DirectionConstant); + NeighborhoodID getCurrentNeighborhoodID() const; + + // Dragging + void dragItem(const Input &, Item *, DragType); + bool isDragging() const { return _dragType != kDragNoDrag; } + DragType getDragType() const { return _dragType; } + Item *getDraggingItem() const { return _draggingItem; } + void dragTerminated(const Input &); + void autoDragItemIntoRoom(Item *, Sprite *); + void autoDragItemIntoInventory(Item *, Sprite*); + + // Save/Load + void makeContinuePoint(); + bool swapSaveAllowed(bool allow) { + bool old = _saveAllowed; + _saveAllowed = allow; + return old; + } + bool swapLoadAllowed(bool allow) { + bool old = _loadAllowed; + _loadAllowed = allow; + return old; + } + void requestSave() { _saveRequested = true; } + bool saveRequested() const { return _saveRequested; } + void requestLoad() { _loadRequested = true; } + bool loadRequested() const { return _loadRequested; } + +protected: + Common::Error run(); + void pauseEngineIntern(bool pause); + + Notification _shellNotification; + virtual void receiveNotification(Notification *notification, const NotificationFlags flags); + + void handleInput(const Input &input, const Hotspot *cursorSpot); + virtual bool isClickInput(const Input &, const Hotspot *); + virtual InputBits getClickFilter(); + + void clickInHotspot(const Input &, const Hotspot *); + void activateHotspots(void); + + void updateCursor(const Common::Point, const Hotspot *); + bool wantsCursor(); + +private: + // Console + PegasusConsole *_console; + + // Intro + void runIntro(); + void stopIntroTimer(); + bool detectOpeningClosingDirectory(); + Common::String _introDirectory; + FuseFunction *_introTimer; + + // Idlers + Idler *_idlerHead; + void giveIdleTime(); + + // Items + ItemList _allItems; + void createItems(); + void createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction); + Inventory _items; + Inventory _biochips; + ItemID _currentItemID; + ItemID _currentBiochipID; + void destroyInventoryItem(const ItemID itemID); + ItemID pickItemToDestroy(); + + // TimeBases + Common::List<TimeBase *> _timeBases; + + // Save/Load + bool loadFromStream(Common::ReadStream *stream); + bool writeToStream(Common::WriteStream *stream, int saveType); + void loadFromContinuePoint(); + void writeContinueStream(Common::WriteStream *stream); + Common::SeekableReadStream *_continuePoint; + bool _saveAllowed, _loadAllowed; // It's so nice that this was in the original code already :P + Common::Error showLoadDialog(); + Common::Error showSaveDialog(); + bool _saveRequested, _loadRequested; + + // Misc. + Hotspot _returnHotspot; + HotspotList _allHotspots; + InputHandler *_savedHandler; + void showTempScreen(const Common::String &fileName); + bool playMovieScaled(Video::VideoDecoder *video, uint16 x, uint16 y); + void throwAwayEverything(); + void shellGameInput(const Input &input, const Hotspot *cursorSpot); + Common::RandomSource *_rnd; + void doSubChase(); + uint getNeighborhoodCD(const NeighborhoodID neighborhood) const; + uint _currentCD; + + // Menu + GameMenu *_gameMenu; + void doGameMenuCommand(const GameMenuCommand); + void doInterfaceOverview(); + ScreenDimmer _screenDimmer; + void pauseMenu(bool menuUp); + + // Energy + int32 _savedEnergyValue; + + // Death + DeathReason _deathReason; + void doDeath(); + + // Neighborhood + Neighborhood *_neighborhood; + void useNeighborhood(Neighborhood *neighborhood); + void performJump(NeighborhoodID start); + void startNewGame(); + void startNeighborhood(); + void makeNeighborhood(NeighborhoodID, Neighborhood *&); + + // Sound + uint16 _ambientLevel; + uint16 _FXLevel; + + // Game Mode + GameMode _gameMode; + bool _switchModesSync; + void switchGameMode(const GameMode, const GameMode); + bool canSwitchGameMode(const GameMode, const GameMode); + + // Dragging + ItemDragger _itemDragger; + Item *_draggingItem; + Sprite *_draggingSprite; + DragType _dragType; + AutoDragger _autoDragger; + + // Interface + void toggleInventoryDisplay(); + void toggleBiochipDisplay(); + void raiseInventoryDrawer(); + void raiseBiochipDrawer(); + void lowerInventoryDrawer(); + void lowerBiochipDrawer(); + void raiseInventoryDrawerSync(); + void raiseBiochipDrawerSync(); + void lowerInventoryDrawerSync(); + void lowerBiochipDrawerSync(); + void showInfoScreen(); + void hideInfoScreen(); + void toggleInfo(); + Movie _bigInfoMovie, _smallInfoMovie; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/scoring.h b/engines/pegasus/scoring.h new file mode 100644 index 0000000000..fbf8641ecb --- /dev/null +++ b/engines/pegasus/scoring.h @@ -0,0 +1,281 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_SCORING_H +#define PEGASUS_SCORING_H + +#include "pegasus/types.h" + +namespace Pegasus { + +///////////////////////////////////////////// +// +// Scoring. + +static const CoordType kDeathScreenScoreLeft = 151; +static const CoordType kDeathScreenScoreTop = 212; +static const CoordType kDeathScreenScoreWidth = 124; +static const CoordType kDeathScreenScoreHeight = 12; +static const CoordType kDeathScreenScoreSkipVert = -16; + +// Caldoria & TSA + +static const GameScoreType kSawINNScore = 5; +static const GameScoreType kTookShowerScore = 2; +static const GameScoreType kFixedHairScore = 2; +static const GameScoreType kGotKeyCardScore = 5; +static const GameScoreType kReadPaperScore = 2; +static const GameScoreType kLookThroughTelescopeScore = 2; +static const GameScoreType kSawCaldoriaKioskScore = 2; +static const GameScoreType kGoToTSAScore = 3; + +static const GameScoreType kEnterTSAScore = 2; +static const GameScoreType kSawBust1Score = 2; +static const GameScoreType kSawBust2Score = 2; +static const GameScoreType kSawBust3Score = 2; +static const GameScoreType kSawBust4Score = 2; +static const GameScoreType kSawBust5Score = 2; +static const GameScoreType kSawBust6Score = 2; +static const GameScoreType kSawTheoryScore = 4; +static const GameScoreType kSawBackgroundScore = 4; +static const GameScoreType kSawProcedureScore = 4; +static const GameScoreType kGotJourneymanKeyScore = 5; +static const GameScoreType kGotPegasusBiochipScore = 5; +static const GameScoreType kGotBiosuitScore = 5; +static const GameScoreType kGoToPrehistoricScore = 5; + +static const GameScoreType kPutLogInReaderScore = 5; +static const GameScoreType kSawCaldoriaNormalScore = 2; +static const GameScoreType kSawCaldoriaAlteredScore = 2; +static const GameScoreType kSawNoradNormalScore = 2; +static const GameScoreType kSawNoradAlteredScore = 2; +static const GameScoreType kSawMarsNormalScore = 2; +static const GameScoreType kSawMarsAlteredScore = 2; +static const GameScoreType kSawWSCNormalScore = 2; +static const GameScoreType kSawWSCAlteredScore = 2; +static const GameScoreType kWentToReadyRoom2Score = 5; +static const GameScoreType kWentAfterSinclairScore = 5; +static const GameScoreType kUsedCardBombScore = 10; +static const GameScoreType kShieldedCardBombScore = 5; +static const GameScoreType kStunnedSinclairScore = 10; +static const GameScoreType kDisarmedNukeScore = 10; + +static const GameScoreType kMaxCaldoriaTSAScoreBefore = kSawINNScore + + kTookShowerScore + + kFixedHairScore + + kGotKeyCardScore + + kReadPaperScore + + kLookThroughTelescopeScore + + kSawCaldoriaKioskScore + + kGoToTSAScore + + kEnterTSAScore + + kSawBust1Score + + kSawBust2Score + + kSawBust3Score + + kSawBust4Score + + kSawBust5Score + + kSawBust6Score + + kSawTheoryScore + + kSawBackgroundScore + + kSawProcedureScore + + kGotJourneymanKeyScore + + kGotPegasusBiochipScore + + kGotBiosuitScore + + kGoToPrehistoricScore + + kPutLogInReaderScore + + kSawCaldoriaNormalScore + + kSawCaldoriaAlteredScore + + kSawNoradNormalScore + + kSawNoradAlteredScore + + kSawMarsNormalScore + + kSawMarsAlteredScore + + kSawWSCNormalScore + + kSawWSCAlteredScore + + kWentToReadyRoom2Score; + +static const GameScoreType kMaxCaldoriaTSAScoreAfter = kWentAfterSinclairScore + + kUsedCardBombScore + + kShieldedCardBombScore + + kStunnedSinclairScore + + kDisarmedNukeScore; + +static const GameScoreType kMaxCaldoriaTSAScore = kMaxCaldoriaTSAScoreBefore + + kMaxCaldoriaTSAScoreAfter; + +// Prehistoric + +static const GameScoreType kThrewBreakerScore = 10; +static const GameScoreType kExtendedBridgeScore = 10; +static const GameScoreType kGotHistoricalLogScore = 5; +static const GameScoreType kFinishedPrehistoricScore = 10; + +static const GameScoreType kMaxPrehistoricScore = kThrewBreakerScore + + kExtendedBridgeScore + + kGotHistoricalLogScore + + kFinishedPrehistoricScore; + +// Mars + +static const GameScoreType kThrownByRobotScore = 3; +static const GameScoreType kGotMarsCardScore = 5; +static const GameScoreType kSawMarsKioskScore = 2; +static const GameScoreType kSawTransportMapScore = 2; +static const GameScoreType kGotCrowBarScore = 5; +static const GameScoreType kTurnedOnTransportScore = 5; +static const GameScoreType kGotOxygenMaskScore = 5; +static const GameScoreType kAvoidedRobotScore = 5; +static const GameScoreType kActivatedPlatformScore = 2; +static const GameScoreType kUsedLiquidNitrogenScore = 3; +static const GameScoreType kUsedCrowBarScore = 3; +static const GameScoreType kFoundCardBombScore = 4; +static const GameScoreType kDisarmedCardBombScore = 8; +static const GameScoreType kGotCardBombScore = 5; +static const GameScoreType kThreadedMazeScore = 5; +static const GameScoreType kThreadedGearRoomScore = 2; +static const GameScoreType kEnteredShuttleScore = 2; +static const GameScoreType kEnteredLaunchTubeScore = 4; +static const GameScoreType kStoppedRobotsShuttleScore = 10; +static const GameScoreType kGotMarsOpMemChipScore = 10; +static const GameScoreType kFinishedMarsScore = 10; + +static const GameScoreType kMaxMarsScore = kThrownByRobotScore + + kGotMarsCardScore + + kSawMarsKioskScore + + kSawTransportMapScore + + kGotCrowBarScore + + kTurnedOnTransportScore + + kGotOxygenMaskScore + + kAvoidedRobotScore + + kActivatedPlatformScore + + kUsedLiquidNitrogenScore + + kUsedCrowBarScore + + kFoundCardBombScore + + kDisarmedCardBombScore + + kGotCardBombScore + + kThreadedMazeScore + + kThreadedGearRoomScore + + kEnteredShuttleScore + + kEnteredLaunchTubeScore + + kStoppedRobotsShuttleScore + + kGotMarsOpMemChipScore + + kFinishedMarsScore; + +// Norad + +static const GameScoreType kSawSecurityMonitorScore = 5; +static const GameScoreType kFilledOxygenCanisterScore = 5; +static const GameScoreType kFilledArgonCanisterScore = 5; +static const GameScoreType kSawUnconsciousOperatorScore = 5; +static const GameScoreType kWentThroughPressureDoorScore = 5; +static const GameScoreType kPreppedSubScore = 5; +static const GameScoreType kEnteredSubScore = 5; +static const GameScoreType kExitedSubScore = 10; +static const GameScoreType kSawRobotAt54NorthScore = 5; +static const GameScoreType kPlayedWithClawScore = 5; +static const GameScoreType kUsedRetinalChipScore = 5; +static const GameScoreType kFinishedGlobeGameScore = 10; +static const GameScoreType kStoppedNoradRobotScore = 10; +static const GameScoreType kGotNoradOpMemChipScore = 10; +static const GameScoreType kFinishedNoradScore = 10; + +static const GameScoreType kMaxNoradScore = kSawSecurityMonitorScore + + kFilledOxygenCanisterScore + + kFilledArgonCanisterScore + + kSawUnconsciousOperatorScore + + kWentThroughPressureDoorScore + + kPreppedSubScore + + kEnteredSubScore + + kExitedSubScore + + kSawRobotAt54NorthScore + + kPlayedWithClawScore + + kUsedRetinalChipScore + + kFinishedGlobeGameScore + + kStoppedNoradRobotScore + + kGotNoradOpMemChipScore + + kFinishedNoradScore; + +// WSC + +static const GameScoreType kRemovedDartScore = 5; +static const GameScoreType kAnalyzedDartScore = 5; +static const GameScoreType kBuiltAntidoteScore = 5; +static const GameScoreType kGotSinclairKeyScore = 5; +static const GameScoreType kGotArgonCanisterScore = 5; +static const GameScoreType kGotNitrogenCanisterScore = 5; +static const GameScoreType kPlayedWithMessagesScore = 2; +static const GameScoreType kSawMorphExperimentScore = 3; +static const GameScoreType kEnteredSinclairOfficeScore = 2; +static const GameScoreType kSawBrochureScore = 3; +static const GameScoreType kSawSinclairEntry1Score = 3; +static const GameScoreType kSawSinclairEntry2Score = 3; +static const GameScoreType kSawSinclairEntry3Score = 3; +static const GameScoreType kSawWSCDirectoryScore = 3; +static const GameScoreType kUsedCrowBarInWSCScore = 5; +static const GameScoreType kFinishedPlasmaDodgeScore = 10; +static const GameScoreType kOpenedCatwalkScore = 3; +static const GameScoreType kStoppedWSCRobotScore = 10; +static const GameScoreType kGotWSCOpMemChipScore = 10; +static const GameScoreType kFinishedWSCScore = 10; + +static const GameScoreType kMaxWSCScore = kRemovedDartScore + + kAnalyzedDartScore + + kBuiltAntidoteScore + + kGotSinclairKeyScore + + kGotArgonCanisterScore + + kGotNitrogenCanisterScore + + kPlayedWithMessagesScore + + kSawMorphExperimentScore + + kEnteredSinclairOfficeScore + + kSawBrochureScore + + kSawSinclairEntry1Score + + kSawSinclairEntry2Score + + kSawSinclairEntry3Score + + kSawWSCDirectoryScore + + kUsedCrowBarInWSCScore + + kFinishedPlasmaDodgeScore + + kOpenedCatwalkScore + + kStoppedWSCRobotScore + + kGotWSCOpMemChipScore + + kFinishedWSCScore; + +// Gandhi + +static const GameScoreType kMarsGandhiScore = 10; +static const GameScoreType kNoradGandhiScore = 10; +static const GameScoreType kWSCGandhiScore = 10; + +static const GameScoreType kMaxGandhiScore = kMarsGandhiScore + + kNoradGandhiScore + + kWSCGandhiScore; + +static const GameScoreType kMaxTotalScore = kMaxCaldoriaTSAScore + + kMaxPrehistoricScore + + kMaxMarsScore + + kMaxNoradScore + + kMaxWSCScore + + kMaxGandhiScore; +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/sound.cpp b/engines/pegasus/sound.cpp new file mode 100644 index 0000000000..bf15858e80 --- /dev/null +++ b/engines/pegasus/sound.cpp @@ -0,0 +1,181 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "audio/audiostream.h" +#include "audio/decoders/aiff.h" +#include "audio/decoders/quicktime.h" +#include "common/file.h" +#include "common/system.h" + +#include "pegasus/fader.h" +#include "pegasus/sound.h" + +namespace Pegasus { + +Sound::Sound() { + _stream = 0; + _volume = 0xFF; + _fader = 0; +} + +Sound::~Sound() { + disposeSound(); +} + +void Sound::disposeSound() { + stopSound(); + delete _stream; _stream = 0; +} + +void Sound::initFromAIFFFile(const Common::String &fileName) { + disposeSound(); + + Common::File *file = new Common::File(); + if (!file->open(fileName)) { + warning("Failed to open AIFF file '%s'", fileName.c_str()); + delete file; + return; + } + + _stream = Audio::makeAIFFStream(file, DisposeAfterUse::YES); +} + +void Sound::initFromQuickTime(const Common::String &fileName) { + disposeSound(); + + _stream = Audio::makeQuickTimeStream(fileName); + + if (!_stream) + warning("Failed to open QuickTime file '%s'", fileName.c_str()); +} + +void Sound::attachFader(SoundFader *fader) { + if (_fader) + _fader->attachSound(0); + + _fader = fader; + + if (_fader) + _fader->attachSound(this); +} + +void Sound::playSound() { + if (!isSoundLoaded()) + return; + + stopSound(); + + if (_fader) + setVolume(_fader->getFaderValue()); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, _stream, -1, _volume, 0, DisposeAfterUse::NO); +} + +void Sound::loopSound() { + if (!isSoundLoaded()) + return; + + stopSound(); + + // Create a looping stream + Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO); + + // Assume that if there is a fader, we're going to fade the sound in. + if (_fader) + setVolume(0); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::playSoundSegment(uint32 start, uint32 end) { + if (!isSoundLoaded()) + return; + + stopSound(); + + Audio::AudioStream *subStream = new Audio::SubSeekableAudioStream(_stream, Audio::Timestamp(0, start, 600), Audio::Timestamp(0, end, 600), DisposeAfterUse::NO); + + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, subStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::stopSound() { + g_system->getMixer()->stopHandle(_handle); +} + +void Sound::setVolume(const uint16 volume) { + // Clipping the volume to [0x00, 0xFF] instead of Apple's [0, 0x100] + // We store the volume in case SetVolume is called before the sound starts + + _volume = (volume == 0x100) ? 0xFF : volume; + g_system->getMixer()->setChannelVolume(_handle, _volume); +} + +bool Sound::isPlaying() { + return isSoundLoaded() && g_system->getMixer()->isSoundHandleActive(_handle); +} + +bool Sound::isSoundLoaded() const { + return _stream != 0; +} + +SoundTimeBase::SoundTimeBase() { + setScale(600); + _startScale = 600; + _stopScale = 600; + _setToStart = false; +} + +void SoundTimeBase::playSoundSegment(uint32 startTime, uint32 endTime) { + _startTime = startTime; + _stopTime = endTime; + _setToStart = true; + _time = Common::Rational(startTime, getScale()); + setRate(1); + Sound::playSoundSegment(startTime, endTime); +} + +void SoundTimeBase::updateTime() { + if (_setToStart) { + if (isPlaying()) { + // Not at the end, let's get the time + uint numFrames = g_system->getMixer()->getSoundElapsedTime(_handle) * 600 / 1000; + + // WORKAROUND: Our mixer is woefully inaccurate and quite often returns + // times that exceed the actual length of the clip. We'll just fake times + // that are under the final time to ensure any trigger for the end time is + // only sent when the sound has actually stopped. + if (numFrames >= (_stopTime - _startTime)) + numFrames = _stopTime - _startTime - 1; + + _time = Common::Rational(_startTime + numFrames, getScale()); + } else { + // Assume we reached the end + _setToStart = false; + _time = Common::Rational(_stopTime, getScale()); + } + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/sound.h b/engines/pegasus/sound.h new file mode 100644 index 0000000000..57cfd52e41 --- /dev/null +++ b/engines/pegasus/sound.h @@ -0,0 +1,108 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_SOUND_H +#define PEGASUS_SOUND_H + +#include "audio/mixer.h" +#include "common/str.h" +#include "pegasus/timers.h" + +namespace Audio { + class SeekableAudioStream; +} + +namespace Pegasus { + +class SoundFader; + +// Things you might want to do with sound: +// - Start it +// - Stop it +// - Loop it +// - Pause it +// - Set the volume +// - Set the pitch (rate) +// - Pan the sound +// - Change these settings dynamically over time + +class Sound { +public: + Sound(); + ~Sound(); + + // We only have one access point here because we should + // only be opening an AIFF file from a file name. We're + // not using the resource fork string resources. + void initFromAIFFFile(const Common::String &fileName); + + // Unlike the original game, we're going to use a regular + // audio stream for sound spots. The original treated them + // as movies. + void initFromQuickTime(const Common::String &fileName); + + void disposeSound(); + bool isSoundLoaded() const; + void playSound(); + void loopSound(); + void playSoundSegment(uint32 start, uint32 end); + void stopSound(); + void setVolume(const uint16 volume); + bool isPlaying(); + + void attachFader(SoundFader *fader); + +protected: + Audio::SeekableAudioStream *_stream; + Audio::SoundHandle _handle; + byte _volume; + + SoundFader *_fader; +}; + +// TODO: Make this class follow TimeBase better +// Right now it's just a loose wrapper to plug callbacks +// into sounds. Since this is only used for spot sounds, +// I'm not too worried about it right now as its usage +// is very limited. +// At the very least, the regular TimeBase functions for +// setting/getting should be neutered. +class SoundTimeBase : public Sound, public TimeBase { +public: + SoundTimeBase(); + ~SoundTimeBase() {} + + void playSoundSegment(uint32 start, uint32 end); + +protected: + void updateTime(); + +private: + bool _setToStart; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/surface.cpp b/engines/pegasus/surface.cpp new file mode 100644 index 0000000000..cdcb3c6e79 --- /dev/null +++ b/engines/pegasus/surface.cpp @@ -0,0 +1,396 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/file.h" +#include "common/macresman.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/surface.h" +#include "graphics/decoders/pict.h" +#include "video/video_decoder.h" + +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +Surface::Surface() { + _ownsSurface = false; + _surface = 0; +} + +Surface::~Surface() { + deallocateSurface(); +} + +void Surface::deallocateSurface() { + if (_surface) { + if (_ownsSurface) { + _surface->free(); + delete _surface; + } + + _surface = 0; + _bounds = Common::Rect(); + _ownsSurface = false; + } +} + +void Surface::shareSurface(Surface *surface) { + deallocateSurface(); + + if (surface) { + _ownsSurface = false; + _surface = surface->getSurface(); + surface->getSurfaceBounds(_bounds); + } +} + +void Surface::allocateSurface(const Common::Rect &bounds) { + deallocateSurface(); + + if (bounds.isEmpty()) + return; + + _bounds = bounds; + _surface = new Graphics::Surface(); + _surface->create(bounds.width(), bounds.height(), g_system->getScreenFormat()); + _ownsSurface = true; +} + +void Surface::getImageFromPICTFile(const Common::String &fileName) { + Common::File pict; + if (!pict.open(fileName)) + error("Could not open picture '%s'", fileName.c_str()); + + if (!getImageFromPICTStream(&pict)) + error("Failed to load PICT '%s'", fileName.c_str()); +} + +void Surface::getImageFromPICTResource(Common::MacResManager *resFork, uint16 id) { + Common::SeekableReadStream *res = resFork->getResource(MKTAG('P', 'I', 'C', 'T'), id); + if (!res) + error("Could not open PICT resource %d from '%s'", id, resFork->getBaseFileName().c_str()); + + if (!getImageFromPICTStream(res)) + error("Failed to load PICT resource %d from '%s'", id, resFork->getBaseFileName().c_str()); + + delete res; +} + +bool Surface::getImageFromPICTStream(Common::SeekableReadStream *stream) { + Graphics::PICTDecoder pict; + + if (!pict.loadStream(*stream)) + return false; + + _surface = pict.getSurface()->convertTo(g_system->getScreenFormat(), pict.getPalette()); + _ownsSurface = true; + _bounds = Common::Rect(0, 0, _surface->w, _surface->h); + return true; +} + +void Surface::getImageFromMovieFrame(Video::VideoDecoder *video, TimeValue time) { + video->seek(Audio::Timestamp(0, time, 600)); + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (frame) { + if (!_surface) + _surface = new Graphics::Surface(); + + _surface->copyFrom(*frame); + _ownsSurface = true; + _bounds = Common::Rect(0, 0, _surface->w, _surface->h); + } else { + deallocateSurface(); + } +} + +void Surface::copyToCurrentPort() const { + copyToCurrentPort(_bounds); +} + +void Surface::copyToCurrentPortTransparent() const { + copyToCurrentPortTransparent(_bounds); +} + +void Surface::copyToCurrentPort(const Common::Rect &rect) const { + copyToCurrentPort(rect, rect); +} + +void Surface::copyToCurrentPortTransparent(const Common::Rect &rect) const { + copyToCurrentPortTransparent(rect, rect); +} + +void Surface::copyToCurrentPort(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + memcpy(dst, src, lineSize); + src += _surface->pitch; + dst += screen->pitch; + } +} + +void Surface::copyToCurrentPortTransparent(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + for (int x = 0; x < srcRect.width(); x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16(src); + if (!isTransparent(color)) + memcpy(dst, src, 2); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32(src); + if (!isTransparent(color)) + memcpy(dst, src, 4); + } + + src += g_system->getScreenFormat().bytesPerPixel; + dst += g_system->getScreenFormat().bytesPerPixel; + } + + src += _surface->pitch - lineSize; + dst += screen->pitch - lineSize; + } +} + +void Surface::copyToCurrentPortMasked(const Common::Rect &srcRect, const Common::Rect &dstRect, const Surface *mask) const { + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + byte *maskSrc = (byte *)mask->getSurface()->getBasePtr(0, y); + + for (int x = 0; x < srcRect.width(); x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16(maskSrc); + if (!isTransparent(color)) + memcpy(dst, src, 2); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32(maskSrc); + if (!isTransparent(color)) + memcpy(dst, src, 4); + } + + src += g_system->getScreenFormat().bytesPerPixel; + maskSrc += g_system->getScreenFormat().bytesPerPixel; + dst += g_system->getScreenFormat().bytesPerPixel; + } + + src += _surface->pitch - lineSize; + dst += screen->pitch - lineSize; + } +} + +void Surface::copyToCurrentPortTransparentGlow(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // This is the same as copyToCurrentPortTransparent(), but turns the red value of each + // pixel all the way up. + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); + byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + + int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + + for (int y = 0; y < srcRect.height(); y++) { + for (int x = 0; x < srcRect.width(); x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16(src); + if (!isTransparent(color)) + WRITE_UINT16(dst, getGlowColor(color)); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32(src); + if (!isTransparent(color)) + WRITE_UINT32(dst, getGlowColor(color)); + } + + src += g_system->getScreenFormat().bytesPerPixel; + dst += g_system->getScreenFormat().bytesPerPixel; + } + + src += _surface->pitch - lineSize; + dst += screen->pitch - lineSize; + } +} + +void Surface::scaleTransparentCopy(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // I'm doing simple linear scaling here + // dstRect(x, y) = srcRect(x * srcW / dstW, y * srcH / dstH); + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + + int srcW = srcRect.width(); + int srcH = srcRect.height(); + int dstW = dstRect.width(); + int dstH = dstRect.height(); + + for (int y = 0; y < dstH; y++) { + for (int x = 0; x < dstW; x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16((byte *)_surface->getBasePtr( + x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + if (!isTransparent(color)) + WRITE_UINT16((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), color); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32((byte *)_surface->getBasePtr( + x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + if (!isTransparent(color)) + WRITE_UINT32((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), color); + } + } + } +} + +void Surface::scaleTransparentCopyGlow(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // This is the same as scaleTransparentCopy(), but turns the red value of each + // pixel all the way up. + + Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + + int srcW = srcRect.width(); + int srcH = srcRect.height(); + int dstW = dstRect.width(); + int dstH = dstRect.height(); + + for (int y = 0; y < dstH; y++) { + for (int x = 0; x < dstW; x++) { + if (g_system->getScreenFormat().bytesPerPixel == 2) { + uint16 color = READ_UINT16((byte *)_surface->getBasePtr( + x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + if (!isTransparent(color)) + WRITE_UINT16((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), getGlowColor(color)); + } else if (g_system->getScreenFormat().bytesPerPixel == 4) { + uint32 color = READ_UINT32((byte *)_surface->getBasePtr( + x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + if (!isTransparent(color)) + WRITE_UINT32((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), getGlowColor(color)); + } + } + } +} + +uint32 Surface::getGlowColor(uint32 color) const { + // Can't just 'or' it on like the original did :P + byte r, g, b; + g_system->getScreenFormat().colorToRGB(color, r, g, b); + return g_system->getScreenFormat().RGBToColor(0xff, g, b); +} + +bool Surface::isTransparent(uint32 color) const { + // HACK: Seems we're truncating some color data somewhere... + uint32 transColor1 = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff); + uint32 transColor2 = g_system->getScreenFormat().RGBToColor(0xf8, 0xf8, 0xf8); + + return color == transColor1 || color == transColor2; +} + +PixelImage::PixelImage() { + _transparent = false; +} + +void PixelImage::drawImage(const Common::Rect &sourceBounds, const Common::Rect &destBounds) { + if (!isSurfaceValid()) + return; + + // Draw from sourceBounds to destBounds based on _transparent + if (_transparent) + copyToCurrentPortTransparent(sourceBounds, destBounds); + else + copyToCurrentPort(sourceBounds, destBounds); +} + +void Frame::initFromPICTFile(const Common::String &fileName, bool transparent) { + getImageFromPICTFile(fileName); + _transparent = transparent; +} + +void Frame::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) { + getImageFromPICTResource(resFork, id); + _transparent = transparent; +} + +void Frame::initFromMovieFrame(Video::VideoDecoder *video, TimeValue time, bool transparent) { + getImageFromMovieFrame(video, time); + _transparent = transparent; +} + +void Picture::draw(const Common::Rect &r) { + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + Common::Rect r1 = r; + + Common::Rect bounds; + getBounds(bounds); + surfaceBounds.moveTo(bounds.left, bounds.top); + r1 = r1.findIntersectingRect(surfaceBounds); + getSurfaceBounds(surfaceBounds); + + Common::Rect r2 = r1; + r2.translate(surfaceBounds.left - bounds.left, surfaceBounds.top - bounds.top); + drawImage(r2, r1); +} + +void Picture::initFromPICTFile(const Common::String &fileName, bool transparent) { + Frame::initFromPICTFile(fileName, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +void Picture::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) { + Frame::initFromPICTResource(resFork, id, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +void Picture::initFromMovieFrame(Video::VideoDecoder *video, TimeValue time, bool transparent) { + Frame::initFromMovieFrame(video, time, transparent); + + Common::Rect surfaceBounds; + getSurfaceBounds(surfaceBounds); + sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/surface.h b/engines/pegasus/surface.h new file mode 100644 index 0000000000..47e3ef538c --- /dev/null +++ b/engines/pegasus/surface.h @@ -0,0 +1,140 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_SURFACE_H +#define PEGASUS_SURFACE_H + +#include "common/rect.h" +#include "common/str.h" + +#include "pegasus/elements.h" +#include "pegasus/types.h" + +namespace Common { + class MacResManager; +} + +namespace Graphics { + struct Surface; +} + +namespace Video { + class VideoDecoder; +} + +namespace Pegasus { + +// Surface bounds are always normalized. + +class Surface { +public: + Surface(); + virtual ~Surface(); + + virtual void allocateSurface(const Common::Rect &); + virtual void deallocateSurface(); + virtual void shareSurface(Surface *surface); + bool isSurfaceValid() const { return _surface != 0; } + + Graphics::Surface *getSurface() const { return _surface; } + void getSurfaceBounds(Common::Rect &r) { r = _bounds; } + + // None of the copyToCurrentPort* functions do any sanity checks. + // It's up to clients to make sure that the Surface is valid. + void copyToCurrentPort() const; + void copyToCurrentPortTransparent() const; + void copyToCurrentPort(const Common::Rect &) const; + void copyToCurrentPortTransparent(const Common::Rect &) const; + void copyToCurrentPort(const Common::Rect &, const Common::Rect &) const; + void copyToCurrentPortTransparent(const Common::Rect &, const Common::Rect &) const; + void copyToCurrentPortMasked(const Common::Rect &, const Common::Rect &, const Surface *) const; + void copyToCurrentPortTransparentGlow(const Common::Rect &, const Common::Rect &) const; + void scaleTransparentCopy(const Common::Rect &, const Common::Rect &) const; + void scaleTransparentCopyGlow(const Common::Rect &, const Common::Rect &) const; + + virtual void getImageFromPICTFile(const Common::String &fileName); + virtual void getImageFromPICTResource(Common::MacResManager *resFork, uint16 id); + virtual void getImageFromMovieFrame(Video::VideoDecoder *, TimeValue); + +protected: + bool _ownsSurface; + Graphics::Surface *_surface; + Common::Rect _bounds; + +private: + bool getImageFromPICTStream(Common::SeekableReadStream *stream); + + uint32 getGlowColor(uint32 color) const; + bool isTransparent(uint32 color) const; +}; + +class PixelImage : public Surface { +public: + PixelImage(); + virtual ~PixelImage() {} + + void drawImage(const Common::Rect &, const Common::Rect &); + +protected: + virtual void setTransparent(bool transparent) { _transparent = transparent; } + + bool _transparent; +}; + +class Frame : public PixelImage { +public: + Frame() {} + virtual ~Frame() {} + + virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); + virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); + virtual void initFromMovieFrame(Video::VideoDecoder *, TimeValue, bool transparent = false); +}; + +class SpriteFrame : public Frame { +friend class Sprite; +public: + SpriteFrame() { _referenceCount = 0; } + virtual ~SpriteFrame() {} + +protected: + uint32 _referenceCount; +}; + +class Picture : public DisplayElement, public Frame { +public: + Picture(const DisplayElementID id) : DisplayElement(id) {} + virtual ~Picture() {} + + virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); + virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); + virtual void initFromMovieFrame(Video::VideoDecoder *, TimeValue, bool transparent = false); + + virtual void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/timers.cpp b/engines/pegasus/timers.cpp new file mode 100644 index 0000000000..3b875038cc --- /dev/null +++ b/engines/pegasus/timers.cpp @@ -0,0 +1,429 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +Idler::Idler() { + _isIdling = false; + _nextIdler = 0; + _prevIdler = 0; +} + +Idler::~Idler() { + stopIdling(); +} + +void Idler::startIdling() { + if (!isIdling()) { + ((PegasusEngine *)g_engine)->addIdler(this); + _isIdling = true; + } +} + +void Idler::stopIdling() { + if (isIdling()) { + ((PegasusEngine *)g_engine)->removeIdler(this); + _isIdling = false; + } +} + +TimeBase::TimeBase(const TimeScale preferredScale) { + _preferredScale = preferredScale; + _callBackList = 0; + _paused = false; + _flags = 0; + _lastMillis = 0; + _time = 0; + _rate = 0; + _startTime = 0; + _startScale = 1; + _stopTime = 0xffffffff; + _stopScale = 1; + _master = 0; + _pausedRate = 0; + _pauseStart = 0; + + ((PegasusEngine *)g_engine)->addTimeBase(this); +} + +TimeBase::~TimeBase() { + if (_master) + _master->_slaves.remove(this); + + ((PegasusEngine *)g_engine)->removeTimeBase(this); + disposeAllCallBacks(); + + // TODO: Remove slaves? Make them remove themselves? +} + +void TimeBase::setTime(const TimeValue time, const TimeScale scale) { + _time = Common::Rational(time, (scale == 0) ? _preferredScale : scale); + _lastMillis = 0; +} + +TimeValue TimeBase::getTime(const TimeScale scale) { + return _time.getNumerator() * ((scale == 0) ? _preferredScale : scale) / _time.getDenominator(); +} + +void TimeBase::setRate(const Common::Rational rate) { + _rate = rate; + + if (_rate == 0) + _paused = false; +} + +Common::Rational TimeBase::getEffectiveRate() const { + return _rate * ((_master == 0) ? 1 : _master->getEffectiveRate()); +} + +void TimeBase::start() { + if (_paused) + _pausedRate = 1; + else + setRate(1); +} + +void TimeBase::stop() { + setRate(0); + _paused = false; +} + +void TimeBase::pause() { + if (isRunning() && !_paused) { + _pausedRate = getRate(); + stop(); + _paused = true; + _pauseStart = g_system->getMillis(); + } +} + +void TimeBase::resume() { + if (_paused) { + setRate(_pausedRate); + _paused = false; + + if (isRunning()) + _lastMillis += g_system->getMillis() - _pauseStart; + } +} + +bool TimeBase::isRunning() { + if (_paused && _pausedRate != 0) + return true; + + Common::Rational rate = getRate(); + + if (rate == 0) + return false; + + if (getFlags() & kLoopTimeBase) + return true; + + if (rate > 0) + return getTime() != getStop(); + + return getTime() != getStart(); +} + +void TimeBase::setStart(const TimeValue startTime, const TimeScale scale) { + _startTime = startTime; + _startScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStart(const TimeScale scale) const { + if (scale) + return _startTime * scale / _startScale; + + return _startTime * _preferredScale / _startScale; +} + +void TimeBase::setStop(const TimeValue stopTime, const TimeScale scale) { + _stopTime = stopTime; + _stopScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStop(const TimeScale scale) const { + if (scale) + return _stopTime * scale / _stopScale; + + return _stopTime * _preferredScale / _stopScale; +} + +void TimeBase::setSegment(const TimeValue startTime, const TimeValue stopTime, const TimeScale scale) { + setStart(startTime, scale); + setStop(stopTime, scale); +} + +void TimeBase::getSegment(TimeValue &startTime, TimeValue &stopTime, const TimeScale scale) const { + startTime = getStart(scale); + stopTime = getStop(scale); +} + +TimeValue TimeBase::getDuration(const TimeScale scale) const { + TimeValue startTime, stopTime; + getSegment(startTime, stopTime, scale); + return stopTime - startTime; +} + +void TimeBase::setMasterTimeBase(TimeBase *tb) { + // TODO: We're just ignoring the master (except for effective rate) + // for now to simplify things + if (_master) + _master->_slaves.remove(this); + + _master = tb; + + if (_master) + _master->_slaves.push_back(this); +} + +void TimeBase::updateTime() { + if (_lastMillis == 0) { + _lastMillis = g_system->getMillis(); + } else { + uint32 curTime = g_system->getMillis(); + if (_lastMillis == curTime) // No change + return; + + _time += Common::Rational(curTime - _lastMillis, 1000) * getEffectiveRate(); + _lastMillis = curTime; + } +} + +void TimeBase::checkCallBacks() { + // Nothing to do if we're paused or not running + if (_paused || !isRunning()) + return; + + Common::Rational startTime = Common::Rational(_startTime, _startScale); + Common::Rational stopTime = Common::Rational(_stopTime, _stopScale); + + // First step: update the times + updateTime(); + + // Clip time to the boundaries + if (_time >= stopTime) + _time = stopTime; + else if (_time <= startTime) + _time = startTime; + + // TODO: Update the slaves? + + Common::Rational time = Common::Rational(getTime(), getScale()); + + // Check if we've triggered any callbacks + for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = runner->_nextCallBack) { + if (runner->_hasBeenTriggered) + continue; + + if (runner->_type == kCallBackAtTime && runner->_trigger == kTriggerTimeFwd) { + if (getTime() >= (runner->_param2 * _preferredScale / runner->_param3) && getRate() > 0) { + uint param2 = runner->_param2, param3 = runner->_param3; + runner->callBack(); + // HACK: Only stop future time forward callbacks if the parameters have not been changed + // This fixes striding callbacks. Since only striding callbacks do this kind of + // craziness, I'm not too worried about this. + runner->_hasBeenTriggered = (runner->_param2 == param2 && runner->_param3 == param3); + } + } else if (runner->_type == kCallBackAtExtremes) { + if (runner->_trigger == kTriggerAtStop) { + if (time == stopTime) { + runner->callBack(); + runner->_hasBeenTriggered = true; + } + } else if (runner->_trigger == kTriggerAtStart) { + if (time == startTime) { + runner->callBack(); + runner->_hasBeenTriggered = true; + } + } + } + } + + if (getFlags() & kLoopTimeBase) { + // Loop if necessary + if (getRate() < 0 && time == startTime) + setTime(_stopTime, _stopScale); + else if (getRate() > 0 && time == stopTime) + setTime(_startTime, _startScale); + } else { + // Stop at the end + if ((getRate() > 0 && time == stopTime) || (getRate() < 0 && time == startTime)) + stop(); + } +} + +// Protected functions only called by TimeBaseCallBack. + +void TimeBase::addCallBack(TimeBaseCallBack *callBack) { + callBack->_nextCallBack = _callBackList; + _callBackList = callBack; +} + +void TimeBase::removeCallBack(TimeBaseCallBack *callBack) { + if (_callBackList == callBack) { + _callBackList = callBack->_nextCallBack; + } else { + TimeBaseCallBack *runner, *prevRunner; + + for (runner = _callBackList->_nextCallBack, prevRunner = _callBackList; runner != callBack; prevRunner = runner, runner = runner->_nextCallBack) + ; + + prevRunner->_nextCallBack = runner->_nextCallBack; + } + + callBack->_nextCallBack = 0; +} + +void TimeBase::disposeAllCallBacks() { + TimeBaseCallBack *nextRunner; + + for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = nextRunner) { + nextRunner = runner->_nextCallBack; + runner->disposeCallBack(); + runner->_nextCallBack = 0; + } + + _callBackList = 0; +} + +TimeBaseCallBack::TimeBaseCallBack() { + _timeBase = 0; + _nextCallBack = 0; + _trigger = kTriggerNone; + _type = kCallBackNone; + _hasBeenTriggered = false; +} + +TimeBaseCallBack::~TimeBaseCallBack() { + releaseCallBack(); +} + +void TimeBaseCallBack::initCallBack(TimeBase *tb, CallBackType type) { + releaseCallBack(); + _timeBase = tb; + _timeBase->addCallBack(this); + _type = type; +} + +void TimeBaseCallBack::releaseCallBack() { + if (_timeBase) + _timeBase->removeCallBack(this); + disposeCallBack(); +} + +void TimeBaseCallBack::disposeCallBack() { + _timeBase = 0; + _hasBeenTriggered = false; +} + +void TimeBaseCallBack::scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3) { + // TODO: Rename param2/param3? + _trigger = trigger; + _param2 = param2; + _param3 = param3; + _hasBeenTriggered = false; +} + +void TimeBaseCallBack::cancelCallBack() { + _trigger = kTriggerNone; + _hasBeenTriggered = false; +} + +IdlerTimeBase::IdlerTimeBase() { + _lastTime = 0xffffffff; + startIdling(); +} + +void IdlerTimeBase::useIdleTime() { + uint32 currentTime = getTime(); + if (currentTime != _lastTime) { + _lastTime = currentTime; + timeChanged(_lastTime); + } +} + +NotificationCallBack::NotificationCallBack() { + _callBackFlag = 0; + _notifier = 0; +} + +void NotificationCallBack::callBack() { + if (_notifier) + _notifier->setNotificationFlags(_callBackFlag, _callBackFlag); +} + +static const NotificationFlags kFuseExpiredFlag = 1; + +Fuse::Fuse() : _fuseNotification(0, (NotificationManager *)((PegasusEngine *)g_engine)) { + _fuseNotification.notifyMe(this, kFuseExpiredFlag, kFuseExpiredFlag); + _fuseCallBack.setNotification(&_fuseNotification); + _fuseCallBack.initCallBack(&_fuseTimer, kCallBackAtExtremes); + _fuseCallBack.setCallBackFlag(kFuseExpiredFlag); +} + +void Fuse::primeFuse(const TimeValue frequency, const TimeScale scale) { + stopFuse(); + _fuseTimer.setScale(scale); + _fuseTimer.setSegment(0, frequency); + _fuseTimer.setTime(0); +} + +void Fuse::lightFuse() { + if (!_fuseTimer.isRunning()) { + _fuseCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + _fuseTimer.start(); + } +} + +void Fuse::stopFuse() { + _fuseTimer.stop(); + _fuseCallBack.cancelCallBack(); + // Make sure the fuse has not triggered but not been caught yet... + _fuseNotification.setNotificationFlags(0, 0xffffffff); +} + +void Fuse::advanceFuse(const TimeValue time) { + if (_fuseTimer.isRunning()) { + _fuseTimer.stop(); + _fuseTimer.setTime(_fuseTimer.getTime() + time); + _fuseTimer.start(); + } +} + +TimeValue Fuse::getTimeRemaining() { + return _fuseTimer.getStop() - _fuseTimer.getTime(); +} + +void Fuse::receiveNotification(Notification *, const NotificationFlags) { + stopFuse(); + invokeAction(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/timers.h b/engines/pegasus/timers.h new file mode 100644 index 0000000000..bcdca6e860 --- /dev/null +++ b/engines/pegasus/timers.h @@ -0,0 +1,260 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_TIMERS_H +#define PEGASUS_TIMERS_H + +#include "common/list.h" +#include "common/rational.h" +#include "common/func.h" + +#include "pegasus/constants.h" +#include "pegasus/notification.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class Idler { +friend class PegasusEngine; + +public: + Idler(); + virtual ~Idler(); + + virtual void startIdling(); + virtual void stopIdling(); + bool isIdling() const { return _isIdling; } + +protected: + virtual void useIdleTime() {} + + bool _isIdling; + Idler *_nextIdler, *_prevIdler; +}; + +enum { + kLoopTimeBase = 1, + kPalindromeLoopTimeBase = 2, + kMaintainTimeBaseZero = 4 +}; + +class TimeBaseCallBack; + +class TimeBase { +friend class TimeBaseCallBack; +public: + TimeBase(const TimeScale = kDefaultTimeScale); + virtual ~TimeBase(); + + virtual void setTime(const TimeValue, const TimeScale = 0); + virtual TimeValue getTime(const TimeScale = 0); + + virtual void setScale(const TimeScale scale) { _preferredScale = scale; } + virtual TimeScale getScale() const { return _preferredScale; } + + virtual void setRate(const Common::Rational); + virtual Common::Rational getRate() const { return _rate; } + + virtual void start(); + virtual void stop(); + virtual bool isRunning(); + + virtual void pause(); + virtual void resume(); + bool isPaused() const { return _paused; } + + virtual void setFlags(const uint32 flags) { _flags = flags; } + virtual uint32 getFlags() const { return _flags; } + + virtual void setStart(const TimeValue, const TimeScale = 0); + virtual TimeValue getStart(const TimeScale = 0) const; + + virtual void setStop(const TimeValue, const TimeScale = 0); + virtual TimeValue getStop(const TimeScale = 0) const; + + virtual void setSegment(const TimeValue, const TimeValue, const TimeScale = 0); + virtual void getSegment(TimeValue&, TimeValue&, const TimeScale = 0) const; + + virtual TimeValue getDuration(const TimeScale = 0) const; + + virtual void setMasterTimeBase(TimeBase *timeBase); + + void disposeAllCallBacks(); + + // ScummVM's API additions (to replace the need for actual timers) + virtual void checkCallBacks(); + +protected: + void addCallBack(TimeBaseCallBack *); + void removeCallBack(TimeBaseCallBack *); + virtual void updateTime(); + + TimeBase *_master; + TimeScale _preferredScale; + TimeBaseCallBack *_callBackList; + Common::Rational _rate, _pausedRate; + bool _paused; + uint32 _startTime, _startScale; + uint32 _stopTime, _stopScale; + uint32 _flags; + + Common::Rational _time; + uint32 _lastMillis, _pauseStart; + +private: + Common::Rational getEffectiveRate() const; + + Common::List<TimeBase *> _slaves; +}; + +// Type passed to initCallBack() +enum CallBackType { + kCallBackNone = 0, + kCallBackAtTime = 1, + kCallBackAtExtremes = 4 +}; + +// Trigger passed to scheduleCallBack() +enum CallBackTrigger { + kTriggerNone = 0, + + // AtTime flags + kTriggerTimeFwd = 1, + + // AtExtremes flags + kTriggerAtStart = 1, + kTriggerAtStop = 2 +}; + +class TimeBaseCallBack { +friend class TimeBase; + +public: + TimeBaseCallBack(); + virtual ~TimeBaseCallBack(); + + void initCallBack(TimeBase *, CallBackType type); + + void releaseCallBack(); + + void scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3); + void cancelCallBack(); + +protected: + virtual void callBack() = 0; + + TimeBase *_timeBase; + + // Owned and operated by TimeBase; + TimeBaseCallBack *_nextCallBack; + + // Our storage of the QuickTime timer crap + CallBackType _type; + CallBackTrigger _trigger; + uint32 _param2, _param3; + bool _hasBeenTriggered; + +private: + void disposeCallBack(); +}; + +class IdlerTimeBase : public Idler, public TimeBase { +public: + IdlerTimeBase(); + virtual ~IdlerTimeBase() { stopIdling(); } + + TimeValue getLastTime() const { return _lastTime; } + +protected: + virtual void useIdleTime(); + virtual void timeChanged(const TimeValue) {} + + TimeValue _lastTime; + +}; + +class NotificationCallBack : public TimeBaseCallBack { +public: + NotificationCallBack(); + virtual ~NotificationCallBack() {} + + void setNotification(Notification *notifier) { _notifier = notifier; } + + void setCallBackFlag(const NotificationFlags flag) { _callBackFlag = flag; } + NotificationFlags getCallBackFlag() const { return _callBackFlag; } + +protected: + void callBack(); + + Notification *_notifier; + NotificationFlags _callBackFlag; +}; + +class DynamicElement : public TimeBase { +public: + TimeValue percentSeconds(const uint32 percent) { return getScale() * percent / 100; } +}; + +class Fuse : private NotificationReceiver { +public: + Fuse(); + virtual ~Fuse() {} + + void primeFuse(const TimeValue, const TimeScale = 1); // An appropriately named function :P + void lightFuse(); + void stopFuse(); + bool isFuseLit() { return _fuseTimer.isRunning() || _fuseTimer.isPaused(); } + void advanceFuse(const TimeValue); + TimeValue getTimeRemaining(); + TimeScale getFuseScale() { return _fuseTimer.getScale(); } + + void pauseFuse() { _fuseTimer.pause(); } + void resumeFuse() { _fuseTimer.resume(); } + bool isFusePaused() { return _fuseTimer.isPaused(); } + +protected: + virtual void receiveNotification(Notification *, const NotificationFlags); + virtual void invokeAction() {} + + TimeBase _fuseTimer; + NotificationCallBack _fuseCallBack; + Notification _fuseNotification; +}; + +class FuseFunction : public Fuse { +public: + FuseFunction() : _functor(0) {} + virtual ~FuseFunction() { delete _functor; } + + void setFunctor(Common::Functor0<void> *functor) { delete _functor; _functor = functor; } +protected: + virtual void invokeAction() { if (_functor && _functor->isValid()) (*_functor)(); } + + Common::Functor0<void> *_functor; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/transition.cpp b/engines/pegasus/transition.cpp new file mode 100644 index 0000000000..1ae212df85 --- /dev/null +++ b/engines/pegasus/transition.cpp @@ -0,0 +1,200 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "graphics/surface.h" + +#include "pegasus/transition.h" + +namespace Pegasus { + +ScreenFader::ScreenFader() { + _isBlack = true; + // Initially, assume screens are on at full brightness. + Fader::setFaderValue(100); + _screen = new Graphics::Surface(); +} + +ScreenFader::~ScreenFader() { + _screen->free(); + delete _screen; +} + +void ScreenFader::doFadeOutSync(const TimeValue duration, const TimeValue scale, bool isBlack) { + _isBlack = isBlack; + _screen->copyFrom(*g_system->lockScreen()); + g_system->unlockScreen(); + + FaderMoveSpec spec; + spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 0); + startFaderSync(spec); + + _screen->free(); +} + +void ScreenFader::doFadeInSync(const TimeValue duration, const TimeValue scale, bool isBlack) { + _isBlack = isBlack; + _screen->copyFrom(*g_system->lockScreen()); + g_system->unlockScreen(); + + FaderMoveSpec spec; + spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 100); + startFaderSync(spec); + + _screen->free(); +} + +void ScreenFader::setFaderValue(const int32 value) { + if (value != getFaderValue()) { + Fader::setFaderValue(value); + + if (_screen->pixels) { + // The original game does a gamma fade here using the Mac API. In order to do + // that, it would require an immense amount of CPU processing. This does a + // linear fade instead, which looks fairly well, IMO. + Graphics::Surface *screen = g_system->lockScreen(); + + for (uint y = 0; y < _screen->h; y++) { + for (uint x = 0; x < _screen->w; x++) { + if (_screen->format.bytesPerPixel == 2) + WRITE_UINT16(screen->getBasePtr(x, y), fadePixel(READ_UINT16(_screen->getBasePtr(x, y)), value)); + else + WRITE_UINT32(screen->getBasePtr(x, y), fadePixel(READ_UINT32(_screen->getBasePtr(x, y)), value)); + } + } + + g_system->unlockScreen(); + g_system->updateScreen(); + } + } +} + +static inline byte fadeComponent(byte comp, int32 percent) { + return comp * percent / 100; +} + +uint32 ScreenFader::fadePixel(uint32 color, int32 percent) const { + byte r, g, b; + g_system->getScreenFormat().colorToRGB(color, r, g, b); + + if (_isBlack) { + r = fadeComponent(r, percent); + g = fadeComponent(g, percent); + b = fadeComponent(b, percent); + } else { + r = 0xFF - fadeComponent(0xFF - r, percent); + g = 0xFF - fadeComponent(0xFF - g, percent); + b = 0xFF - fadeComponent(0xFF - b, percent); + } + + return g_system->getScreenFormat().RGBToColor(r, g, b); +} + +Transition::Transition(const DisplayElementID id) : FaderAnimation(id) { + _outPicture = 0; + _inPicture = 0; +} + +void Transition::setBounds(const Common::Rect &r) { + FaderAnimation::setBounds(r); + _boundsWidth = _bounds.width(); + _boundsHeight = _bounds.height(); +} + +void Transition::setInAndOutElements(DisplayElement *inElement, DisplayElement *outElement) { + _inPicture = inElement; + _outPicture = outElement; + + Common::Rect r; + + if (_outPicture) + _outPicture->getBounds(r); + else if (_inPicture) + _inPicture->getBounds(r); + + setBounds(r); +} + +void Slide::draw(const Common::Rect &r) { + Common::Rect oldBounds, newBounds; + + adjustSlideRects(oldBounds, newBounds); + drawElements(r, oldBounds, newBounds); +} + +void Slide::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) { + oldBounds = _bounds; + newBounds = _bounds; +} + +void Slide::drawElements(const Common::Rect &drawRect, const Common::Rect &oldBounds, const Common::Rect &newBounds) { + drawSlideElement(drawRect, oldBounds, _outPicture); + drawSlideElement(drawRect, newBounds, _inPicture); +} + +void Slide::drawSlideElement(const Common::Rect &drawRect, const Common::Rect &oldBounds, DisplayElement *picture) { + if (picture && drawRect.intersects(oldBounds)) { + picture->moveElementTo(oldBounds.left, oldBounds.top); + picture->draw(drawRect.findIntersectingRect(oldBounds)); + } +} + +void Push::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) { + switch (_direction & kSlideHorizMask) { + case kSlideLeftMask: + newBounds.left = oldBounds.right = _bounds.right - pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange); + newBounds.right = newBounds.left + _boundsWidth; + oldBounds.left = oldBounds.right - _boundsWidth; + break; + case kSlideRightMask: + oldBounds.left = newBounds.right = _bounds.left + pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange); + oldBounds.right = oldBounds.left + _boundsWidth; + newBounds.left = newBounds.right - _boundsWidth; + break; + default: + newBounds.left = oldBounds.left = _bounds.left; + newBounds.right = oldBounds.right = _bounds.right; + break; + } + + switch (_direction & kSlideVertMask) { + case kSlideDownMask: + oldBounds.top = newBounds.bottom = _bounds.top + pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange); + oldBounds.bottom = oldBounds.top + _boundsHeight; + newBounds.top = newBounds.bottom - _boundsHeight; + break; + case kSlideUpMask: + newBounds.top = oldBounds.bottom = _bounds.bottom - pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange); + newBounds.bottom = newBounds.top + _boundsHeight; + oldBounds.top = oldBounds.bottom - _boundsHeight; + break; + default: + newBounds.top = oldBounds.top = _bounds.top; + newBounds.bottom = oldBounds.bottom = _bounds.bottom; + break; + } +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/transition.h b/engines/pegasus/transition.h new file mode 100644 index 0000000000..84241a2bd2 --- /dev/null +++ b/engines/pegasus/transition.h @@ -0,0 +1,108 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_TRANSITION_H +#define PEGASUS_TRANSITION_H + +#include "pegasus/fader.h" + +namespace Graphics { +struct Surface; +} + +namespace Pegasus { + +class ScreenFader : public Fader { +public: + ScreenFader(); + virtual ~ScreenFader(); + + void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true); + void doFadeInSync(const TimeValue = kHalfSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true); + + void setFaderValue(const int32); + +private: + bool _isBlack; + uint32 fadePixel(uint32 color, int32 percent) const; + Graphics::Surface *_screen; +}; + +// Transitions are faders that range over [0,1000], which makes their +// "resolution" one tenth of a percent + +static const int kTransitionBottom = 0; +static const int kTransitionTop = 1000; + +static const int kTransitionRange = kTransitionTop - kTransitionBottom; + +class Transition : public FaderAnimation { +public: + Transition(const DisplayElementID id); + virtual ~Transition() {} + + virtual void setBounds(const Common::Rect &); + + virtual void setInAndOutElements(DisplayElement *, DisplayElement *); + DisplayElement *getInElement() { return _inPicture; } + DisplayElement *getOutElement() { return _outPicture; } + +protected: + DisplayElement *_outPicture; + DisplayElement *_inPicture; + + CoordType _boundsWidth, _boundsHeight; +}; + +class Slide : public Transition { +public: + Slide(const DisplayElementID id) : Transition(id) {} + virtual ~Slide() {} + + virtual void setSlideDirection(SlideDirection dir) { _direction = dir; } + virtual void draw(const Common::Rect &); + + virtual void setDirection(const SlideDirection dir) { _direction = dir; } + +protected: + virtual void adjustSlideRects(Common::Rect &, Common::Rect &); + virtual void drawElements(const Common::Rect &, const Common::Rect &, const Common::Rect &); + virtual void drawSlideElement(const Common::Rect &, const Common::Rect &, DisplayElement *); + + SlideDirection _direction; +}; + +class Push : public Slide { +public: + Push(const DisplayElementID id) : Slide(id) {} + virtual ~Push() {} + +protected: + virtual void adjustSlideRects(Common::Rect &, Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/types.h b/engines/pegasus/types.h new file mode 100644 index 0000000000..64ab4e5bb2 --- /dev/null +++ b/engines/pegasus/types.h @@ -0,0 +1,161 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_TYPES_H +#define PEGASUS_TYPES_H + +#include "common/scummsys.h" + +namespace Pegasus { + +// TODO: Probably all of these don't really need to be typedef'd... + +typedef int32 DisplayElementID; +typedef int32 DisplayOrder; + +typedef int16 HotSpotID; +typedef uint32 HotSpotFlags; + +typedef byte ButtonState; +typedef uint32 InputBits; + +typedef int32 NotificationID; +typedef uint32 NotificationFlags; + +// Mac types. +typedef int16 ResIDType; +typedef int16 CoordType; + +enum SlideDirection { + kSlideLeftMask = 1, + kSlideRightMask = kSlideLeftMask << 1, + kSlideUpMask = kSlideRightMask << 1 << 1, + kSlideDownMask = kSlideUpMask << 1, + + kSlideHorizMask = kSlideLeftMask | kSlideRightMask, + kSlideVertMask = kSlideUpMask | kSlideDownMask, + + kSlideUpLeftMask = kSlideLeftMask | kSlideUpMask, + kSlideUpRightMask = kSlideRightMask | kSlideUpMask, + kSlideDownLeftMask = kSlideLeftMask | kSlideDownMask, + kSlideDownRightMask = kSlideRightMask | kSlideDownMask +}; + +// ScummVM QuickTime/QuickDraw replacement types +typedef uint TimeValue; +typedef uint TimeScale; + +typedef int16 GameID; + +typedef GameID ItemID; +typedef GameID ActorID; +typedef GameID RoomID; +typedef GameID NeighborhoodID; +typedef byte AlternateID; +typedef int8 HotSpotActivationID; + +typedef int16 WeightType; + +typedef byte DirectionConstant; +typedef byte TurnDirection; + +// Meant to be room in low 16 bits and direction in high 16 bits. +typedef uint32 RoomViewID; + +#define MakeRoomView(room, direction) (((RoomViewID) (room)) | (((RoomViewID) (direction)) << 16)) + +typedef uint32 ExtraID; + +typedef int16 GameMode; + +typedef int16 WeightType; + +typedef int16 ItemState; + +typedef int8 DeathReason; + +typedef int32 GameMenuCommand; + +typedef int32 GameScoreType; + +typedef long CanMoveForwardReason; + +typedef long CanTurnReason; + +typedef long CanOpenDoorReason; + +enum InventoryResult { + kInventoryOK, + kTooMuchWeight, + kItemNotInInventory +}; + +typedef int32 InteractionID; + +typedef int32 AIConditionID; + +enum EnergyStage { + kStageNoStage, + kStageCasual, // more than 50% energy + kStageWorried, // more than 25% energy + kStageNervous, // more than 5% energy + kStagePanicStricken // less than 5% energy +}; + +enum NoradSubPrepState { + kSubNotPrepped, + kSubPrepped, + kSubDamaged +}; + +enum LowerClientSignature { + kNoClientSignature, + kInventorySignature, + kBiochipSignature, + kAISignature +}; + +enum LowerAreaSignature { + kLeftAreaSignature, + kMiddleAreaSignature, + kRightAreaSignature +}; + +enum AirQuality { + kAirQualityGood, + kAirQualityDirty, + kAirQualityVacuum +}; + +enum DragType { + kDragNoDrag, + kDragInventoryPickup, + kDragBiochipPickup, + kDragInventoryUse +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/util.cpp b/engines/pegasus/util.cpp new file mode 100644 index 0000000000..59df610c33 --- /dev/null +++ b/engines/pegasus/util.cpp @@ -0,0 +1,77 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/random.h" +#include "common/system.h" +#include "common/util.h" + +#include "pegasus/util.h" + +namespace Pegasus { + +IDObject::IDObject(const int32 id) { + _objectID = id; +} + +IDObject::~IDObject() { +} + +int32 IDObject::getObjectID() const { + return _objectID; +} + +int operator==(const IDObject &arg1, const IDObject &arg2) { + return arg1.getObjectID() == arg2.getObjectID(); +} + +int operator!=(const IDObject &arg1, const IDObject &arg2) { + return arg1.getObjectID() != arg2.getObjectID(); +} + +int32 pegasusRound(const int32 a, const int32 b) { + if (b < 0) + if (a < 0) + return -((a - (-b >> 1)) / -b); + else + return -((a + (-b >> 1)) / -b); + else + if (a < 0) + return (a - (b >> 1)) / b; + else + return (a + (b >> 1)) / b; +} + +int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2) { + if (start2 == stop2) + return start2; + else + return start2 + pegasusRound((current1 - start1) * (stop2 - start2), (stop1 - start1)); +} + +uint32 tickCount() { + return g_system->getMillis() * 60 / 1000; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/util.h b/engines/pegasus/util.h new file mode 100644 index 0000000000..97ba1c20c3 --- /dev/null +++ b/engines/pegasus/util.h @@ -0,0 +1,117 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_UTIL_H +#define PEGASUS_UTIL_H + +#include "common/stream.h" + +#include "pegasus/types.h" + +namespace Common { + class RandomSource; +} + +namespace Pegasus { + +class IDObject { +public: + IDObject(const int32 id); + ~IDObject(); + + int32 getObjectID() const; + +private: + int32 _objectID; +}; + +#define NUM_FLAGS (sizeof(Unit) * 8) +#define BIT_INDEX_SHIFT (sizeof(Unit) + 2 - (sizeof(Unit)) / 3) +#define BIT_INDEX_MASK (NUM_FLAGS - 1) + +template <typename Unit, uint32 kNumFlags> +class FlagsArray { +public: + FlagsArray() { clearAllFlags(); } + void clearAllFlags() { memset(_flags, 0, sizeof(_flags)); } + void setAllFlags() { memset(_flags, ~((Unit)0), sizeof(_flags)); } + void setFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] |= 1 << (flag & BIT_INDEX_MASK); } + void clearFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] &= ~(1 << (flag & BIT_INDEX_MASK)); } + void setFlag(uint32 flag, bool val) { if (val) setFlag(flag); else clearFlag(flag); } + bool getFlag(uint32 flag) { return (_flags[flag >> BIT_INDEX_SHIFT] & (1 << (flag & BIT_INDEX_MASK))) != 0; } + bool anyFlagSet() { + for (uint32 i = 0; i < sizeof(_flags); i++) + if (_flags[i] != 0) + return true; + return false; + } + + void readFromStream(Common::ReadStream *stream) { + // Shortcut + if (sizeof(Unit) == 1) { + stream->read(_flags, sizeof(_flags)); + return; + } + + for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) { + if (sizeof(Unit) == 2) + _flags[i] = stream->readUint16BE(); + else /* if (sizeof(Unit) == 4) */ + _flags[i] = stream->readUint32BE(); + } + } + + void writeToStream(Common::WriteStream *stream) { + // Shortcut + if (sizeof(Unit) == 1) { + stream->write(_flags, sizeof(_flags)); + return; + } + + for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) { + if (sizeof(Unit) == 2) + stream->writeUint16BE(_flags[i]); + else /* if (sizeof(Unit) == 4) */ + stream->writeUint32BE(_flags[i]); + } + } + +private: + Unit _flags[(kNumFlags - 1) / NUM_FLAGS + 1]; +}; + +#undef NUM_FLAGS +#undef BIT_INDEX_SHIFT +#undef BIT_INDEX_MASK + +int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2); + +int32 pegasusRound(const int32 a, const int32 b); + +uint32 tickCount(); + +} // End of namespace Pegasus + +#endif diff --git a/engines/plugins_table.h b/engines/plugins_table.h index c07dbc66a4..e5ac5efeb4 100644 --- a/engines/plugins_table.h +++ b/engines/plugins_table.h @@ -59,6 +59,9 @@ LINK_PLUGIN(MOHAWK) #if PLUGIN_ENABLED_STATIC(PARALLACTION) LINK_PLUGIN(PARALLACTION) #endif +#if PLUGIN_ENABLED_STATIC(PEGASUS) +LINK_PLUGIN(PEGASUS) +#endif #if PLUGIN_ENABLED_STATIC(QUEEN) LINK_PLUGIN(QUEEN) #endif diff --git a/engines/queen/display.cpp b/engines/queen/display.cpp index 83dc1a9f60..cd9a1075fa 100644 --- a/engines/queen/display.cpp +++ b/engines/queen/display.cpp @@ -23,9 +23,13 @@ #include "common/system.h" #include "common/events.h" +#include "common/stream.h" +#include "common/memstream.h" #include "graphics/cursorman.h" #include "graphics/palette.h" +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" #include "queen/display.h" #include "queen/input.h" @@ -806,28 +810,22 @@ void Display::fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w, } void Display::decodePCX(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd) { - *w = READ_LE_UINT16(src + 12); - *h = READ_LE_UINT16(src + 14); + Common::MemoryReadStream str(src, srcSize); + + ::Graphics::PCXDecoder pcx; + if (!pcx.loadStream(str)) + error("Error while reading PCX image"); + + const ::Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + *w = pcxSurface->w; + *h = pcxSurface->h; assert(palStart <= palEnd && palEnd <= 256); - const uint8 *palData = src + srcSize - 768; - memcpy(pal, palData + palStart * 3, (palEnd - palStart) * 3); - - src += 128; - for (int y = 0; y < *h; ++y) { - uint8 *p = dst; - while (p < dst + *w) { - uint8 col = *src++; - if ((col & 0xC0) == 0xC0) { - uint8 len = col & 0x3F; - memset(p, *src++, len); - p += len; - } else { - *p++ = col; - } - } - dst += dstPitch; - } + memcpy(pal, pcx.getPalette() + palStart * 3, (palEnd - palStart) * 3); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(dst + y * dstPitch, pcxSurface->getBasePtr(0, y), pcxSurface->w); } void Display::decodeLBM(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd, uint8 colorBase) { diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp index f3b183c84f..c403536e22 100644 --- a/engines/queen/queen.cpp +++ b/engines/queen/queen.cpp @@ -113,12 +113,12 @@ int QueenMetaEngine::getMaximumSaveSlot() const { return 99; } const ExtraGuiOptions QueenMetaEngine::getExtraGuiOptions(const Common::String &target) const { Common::String guiOptions; ExtraGuiOptions options; - + if (target.empty()) { options.push_back(queenExtraGuiOption); return options; } - + if (ConfMan.hasKey("guioptions", target)) { guiOptions = ConfMan.get("guioptions", target); guiOptions = parseGameGUIOptions(guiOptions); diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp index 96746b538c..3efc554cb3 100644 --- a/engines/saga/script.cpp +++ b/engines/saga/script.cpp @@ -948,7 +948,7 @@ void Script::opSpeak(SCRIPTOP_PARAMS) { // scripts change to scene 5, but do not clear the cutaway that appears // before Gorrister's speech starts, resulting in a deadlock. We do this // manually here. - if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 1 && + if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 1 && _vm->_scene->currentSceneNumber() == 5 && _vm->_anim->hasCutaway()) { _vm->_anim->returnFromCutaway(); } diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 1889d53480..5ae8245e5a 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -2882,17 +2882,17 @@ bool Console::cmdDisassemble(int argc, const char **argv) { reg_t addr = NULL_REG; if (!obj) { - DebugPrintf("Not an object."); + DebugPrintf("Not an object.\n"); return true; } if (selectorId < 0) { - DebugPrintf("Not a valid selector name."); + DebugPrintf("Not a valid selector name.\n"); return true; } if (lookupSelector(_engine->_gamestate->_segMan, objAddr, selectorId, NULL, &addr) != kSelectorMethod) { - DebugPrintf("Not a method."); + DebugPrintf("Not a method.\n"); return true; } diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 58ac5f1fa6..ebad3d039a 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -103,6 +103,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = { {"pq4", "Police Quest IV: Open Season"}, // floppy is SCI2, CD SCI2.1 {"qfg4", "Quest for Glory IV: Shadows of Darkness"}, // floppy is SCI2, CD SCI2.1 // === SCI2.1 games ======================================================== + {"chest", "Inside the Chest"}, // aka Behind the Developer's Shield {"gk2", "The Beast Within: A Gabriel Knight Mystery"}, // TODO: Inside The Chest/Behind the Developer's Shield {"kq7", "King's Quest VII: The Princeless Bride"}, @@ -132,6 +133,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "astrochicken", GID_ASTROCHICKEN }, { "camelot", GID_CAMELOT }, { "castlebrain", GID_CASTLEBRAIN }, + { "chest", GID_CHEST }, { "christmas1988", GID_CHRISTMAS1988 }, { "christmas1990", GID_CHRISTMAS1990 }, { "christmas1992", GID_CHRISTMAS1992 }, @@ -208,6 +210,7 @@ struct OldNewIdTableEntry { }; static const OldNewIdTableEntry s_oldNewTable[] = { + { "archive", "chest", SCI_VERSION_NONE }, { "arthur", "camelot", SCI_VERSION_NONE }, { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga { "brain", "castlebrain", SCI_VERSION_1_LATE }, @@ -834,12 +837,16 @@ Common::Error SciEngine::saveGameState(int slot, const Common::String &desc) { return Common::kNoError; } +// Before enabling the load option in the ScummVM menu, the main game loop must +// have run at least once. When the game loop runs, kGameIsRestarting is invoked, +// thus the speed throttler is initialized. Hopefully fixes bug #3565505. + bool SciEngine::canLoadGameStateCurrently() { - return !_gamestate->executionStackBase; + return !_gamestate->executionStackBase && (_gamestate->_throttleLastTime > 0 || _gamestate->_throttleTrigger); } bool SciEngine::canSaveGameStateCurrently() { - return !_gamestate->executionStackBase; + return !_gamestate->executionStackBase && (_gamestate->_throttleLastTime > 0 || _gamestate->_throttleTrigger); } } // End of namespace Sci diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index b978f40aba..8e8b818854 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -129,6 +129,16 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Castle of Dr. Brain - English DOS 5.25" Floppy VGA 1.1 (from rnjacobs, bug report #3578286) + {"castlebrain", "", { + {"resource.map", 0, "a1deac2647ad09472c63656bfb950a4d", 2739}, + {"resource.000", 0, "27ec5fa09cd12a7fd16e86d96a2ed245", 347071}, + {"resource.001", 0, "13e81e1839cd7b216d2bb5615c1ca160", 356812}, + {"resource.002", 0, "583d348c908f89f94f8551d7fe0a2eca", 991752}, + {"resource.003", 0, "6c3d1bb26ad532c94046bc9ac49b5ff4", 728315}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Castle of Dr. Brain - English DOS Floppy 1.1 {"castlebrain", "", { {"resource.map", 0, "f77728304c70017c54793eb6ca648174", 2745}, @@ -162,6 +172,16 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, +#ifdef ENABLE_SCI32 + // Inside the Chest / Behind the Developer's Shield + // SCI interpreter version 2.000.000 + {"chest", "", { + {"resource.map", 0, "9dd015e79cac4f91e7de805448f39775", 1912}, + {"resource.000", 0, "e4efcd042f86679dd4e1834bb3a38edb", 3770943}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO3(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_FB01_MIDI) }, +#endif + // Christmas Card 1988 - English DOS // SCI interpreter version 0.000.294 {"christmas1988", "", { @@ -268,7 +288,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.006", 0, "08050329aa113a9f14ed99cbfe3536ec", 232942}, {"resource.007", 0, "64f342463f6f35ba71b3509ef696ae3f", 267702}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Conquests of Camelot - English Amiga (from www.back2roots.org) // Executable scanning reports "1.002.030" diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index 22c0a1479d..49e2bfc79f 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -466,6 +466,14 @@ bool GameFeatures::autoDetectSci21KernelType() { // This case doesn't occur in early SCI2.1 games, and we've only // seen it happen in the RAMA demo, thus we can assume that the // game is using a SCI2.1 table + + // HACK: The Inside the Chest Demo doesn't have sounds at all, but + // it's using a SCI2 kernel + if (g_sci->getGameId() == GID_CHEST) { + _sci21KernelType = SCI_VERSION_2; + return true; + } + warning("autoDetectSci21KernelType(): Sound object not loaded, assuming a SCI2.1 table"); _sci21KernelType = SCI_VERSION_2_1; return true; diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp index a0f7ebf4a2..3dc042389e 100644 --- a/engines/sci/engine/file.cpp +++ b/engines/sci/engine/file.cpp @@ -95,7 +95,7 @@ reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool u outFile = saveFileMan->openForSaving(wrappedName, isCompressed); if (!outFile) debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str()); - + // QfG1 opens the character export file with _K_FILE_MODE_CREATE first, // closes it immediately and opens it again with this here. Perhaps // other games use this for read access as well. I guess changing this @@ -387,7 +387,7 @@ uint32 VirtualIndexFile::read(char *buffer, uint32 size) { uint32 VirtualIndexFile::write(const char *buffer, uint32 size) { _changed = true; uint32 curPos = _ptr - _buffer; - + // Check if the buffer needs to be resized if (curPos + size >= _bufferSize) { _bufferSize = curPos + size + 1; diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index f5f46285be..d0c9b9b1cf 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -94,7 +94,7 @@ static const SciKernelMapSubEntry kDoSound_subops[] = { { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL }, { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL }, { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL }, - { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds }, + { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "[o0]", kDoSoundFade_workarounds }, { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL }, { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL }, { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, @@ -524,7 +524,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PalCycle), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // SCI2 Empty functions - + // Debug function used to track resources { MAP_EMPTY(ResourceTrack), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // Future TODO: This call is used in the floppy version of QFG4 to add diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index f7cc4f44b5..e977f15c0c 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -340,7 +340,7 @@ reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) { if (argv[0] == SIGNAL_REG) return s->r_acc; - + uint16 handle = argv[0].toUint16(); #ifdef ENABLE_SCI32 @@ -624,7 +624,7 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) { // Special case for KQ6 Mac: The game checks for two video files to see // if they exist before it plays them. Since we support multiple naming // schemes for resource fork files, we also need to support that here in - // case someone has a "HalfDome.bin" file, etc. + // case someone has a "HalfDome.bin" file, etc. if (!exists && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformMacintosh && (name == "HalfDome" || name == "Kq6Movie")) exists = Common::MacResManager::exists(name); @@ -998,7 +998,7 @@ reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv) { if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END)) error("kMakeSaveFileName: invalid savegame ID specified"); uint saveSlot = virtualId - SAVEGAMEID_OFFICIALRANGE_START; - + Common::Array<SavegameDesc> saves; listSavegames(saves); diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 002ef1ff07..b839ac51c3 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -31,6 +31,9 @@ #include "common/debug-channels.h" #include "common/list.h" #include "common/system.h" +#include "common/math.h" + +//#define DEBUG_MERGEPOLY namespace Sci { @@ -71,11 +74,25 @@ enum { struct FloatPoint { FloatPoint() : x(0), y(0) {} FloatPoint(float x_, float y_) : x(x_), y(y_) {} + FloatPoint(Common::Point p) : x(p.x), y(p.y) {} Common::Point toPoint() { return Common::Point((int16)(x + 0.5), (int16)(y + 0.5)); } + float operator*(const FloatPoint &p) const { + return x*p.x + y*p.y; + } + FloatPoint operator*(float l) const { + return FloatPoint(l*x, l*y); + } + FloatPoint operator-(const FloatPoint &p) const { + return FloatPoint(x-p.x, y-p.y); + } + float norm() const { + return x*x+y*y; + } + float x, y; }; @@ -135,15 +152,20 @@ public: return _head; } - void insertHead(Vertex *elm) { + void insertAtEnd(Vertex *elm) { if (_head == NULL) { elm->_next = elm->_prev = elm; + _head = elm; } else { elm->_next = _head; elm->_prev = _head->_prev; _head->_prev = elm; elm->_prev->_next = elm; } + } + + void insertHead(Vertex *elm) { + insertAtEnd(elm); _head = elm; } @@ -788,10 +810,10 @@ int PathfindingState::findNearPoint(const Common::Point &p, Polygon *polygon, Co * including the vertices themselves) * Parameters: (const Common::Point &) a, b: The line segment (a, b) * (Vertex *) vertex: The first vertex of the edge - * Returns : (int) FP_OK on success, PF_ERROR otherwise + * Returns : (int) PF_OK on success, PF_ERROR otherwise * (FloatPoint) *ret: The intersection point */ -static int intersection(const Common::Point &a, const Common::Point &b, Vertex *vertex, FloatPoint *ret) { +static int intersection(const Common::Point &a, const Common::Point &b, const Vertex *vertex, FloatPoint *ret) { // Parameters of parametric equations float s, t; // Numerator and denominator of equations @@ -1344,7 +1366,16 @@ static void AStar(PathfindingState *s) { // other, while we apply a penalty to paths traversing it. // This difference might lead to problems, but none are // known at the time of writing. - if (s->pointOnScreenBorder(vertex->v)) + + // WORKAROUND: This check fails in QFG1VGA, room 81 (bug report #3568452). + // However, it is needed in other SCI1.1 games, such as LB2. Therefore, we + // add this workaround for that scene in QFG1VGA, until our algorithm matches + // better what SSCI is doing. With this workaround, QFG1VGA no longer freezes + // in that scene. + bool qfg1VgaWorkaround = (g_sci->getGameId() == GID_QFG1VGA && + g_sci->getEngineState()->currentRoomNumber() == 81); + + if (s->pointOnScreenBorder(vertex->v) && !qfg1VgaWorkaround) new_dist += 10000; if (new_dist < vertex->costG) { @@ -1783,39 +1814,619 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) { } } +// ========================================================================== +// kMergePoly utility functions + +// Compute square of the distance of p to the segment a-b. +static float pointSegDistance(const Common::Point &a, const Common::Point &b, + const Common::Point &p) { + FloatPoint ba(b-a); + FloatPoint pa(p-a); + FloatPoint bp(b-p); + + // Check if the projection of p on the line a-b lies between a and b + if (ba*pa >= 0.0f && ba*bp >= 0.0f) { + // If yes, return the (squared) distance of p to the line a-b: + // translate a to origin, project p and subtract + float linedist = (ba*((ba*pa)/(ba*ba)) - pa).norm(); + + return linedist; + } else { + // If no, return the (squared) distance to either a or b, whichever + // is closest. + + // distance to a: + float adist = pa.norm(); + // distance to b: + float bdist = FloatPoint(p-b).norm(); + + return MIN(adist, bdist); + } +} + +// find intersection between edges of two polygons. +// endpoints count, except v2->_next +static bool segSegIntersect(const Vertex *v1, const Vertex *v2, Common::Point &intp) { + const Common::Point &a = v1->v; + const Common::Point &b = v1->_next->v; + const Common::Point &c = v2->v; + const Common::Point &d = v2->_next->v; + + // First handle the endpoint cases manually + + if (collinear(a, b, c) && collinear(a, b, d)) + return false; + + if (collinear(a, b, c)) { + // a, b, c collinear + // return true/c if c is between a and b + intp = c; + if (a.x != b.x) { + if ((a.x <= c.x && c.x <= b.x) || (b.x <= c.x && c.x <= a.x)) + return true; + } else { + if ((a.y <= c.y && c.y <= b.y) || (b.y <= c.y && c.y <= a.y)) + return true; + } + } + + if (collinear(a, b, d)) { + intp = d; + // a, b, d collinear + // return false/d if d is between a and b + if (a.x != b.x) { + if ((a.x <= d.x && d.x <= b.x) || (b.x <= d.x && d.x <= a.x)) + return false; + } else { + if ((a.y <= d.y && d.y <= b.y) || (b.y <= d.y && d.y <= a.y)) + return false; + } + } + + int len_dc = c.sqrDist(d); + + if (!len_dc) error("zero length edge in polygon"); + + if (pointSegDistance(c, d, a) <= 2.0f) { + intp = a; + return true; + } + + if (pointSegDistance(c, d, b) <= 2.0f) { + intp = b; + return true; + } + + // If not an endpoint, call the generic intersection function + + FloatPoint p; + if (intersection(a, b, v2, &p) == PF_OK) { + intp = p.toPoint(); + return true; + } else { + return false; + } +} + +// For intersecting polygon segments, determine if +// * the v2 edge enters polygon 1 at this intersection: positive return value +// * the v2 edge and the v1 edges are parallel: zero return value +// * the v2 edge exits polygon 1 at this intersection: negative return value +static int intersectDir(const Vertex *v1, const Vertex *v2) { + Common::Point p1 = v1->_next->v - v1->v; + Common::Point p2 = v2->_next->v - v2->v; + return (p1.x*p2.y - p2.x*p1.y); +} + +// Direction of edge in degrees from pos. x-axis, between -180 and 180 +static int edgeDir(const Vertex *v) { + Common::Point p = v->_next->v - v->v; + int deg = (int)Common::rad2deg(atan2((double)p.y, (double)p.x)); + if (deg < -180) deg += 360; + if (deg > 180) deg -= 360; + return deg; +} + +// For points p1, p2 on the polygon segment v, determine if +// * p1 lies before p2: negative return value +// * p1 and p2 are the same: zero return value +// * p1 lies after p2: positive return value +static int liesBefore(const Vertex *v, const Common::Point &p1, const Common::Point &p2) { + return v->v.sqrDist(p1) - v->v.sqrDist(p2); +} + +// Structure describing an "extension" to the work polygon following edges +// of the polygon being merged. + +// The patch begins on the point intersection1, being the intersection +// of the edges starting at indexw1/vertexw1 on the work polygon, and at +// indexp1/vertexp1 on the polygon being merged. +// It ends with the point intersection2, being the analogous intersection. +struct Patch { + unsigned int indexw1; + unsigned int indexp1; + const Vertex *vertexw1; + const Vertex *vertexp1; + Common::Point intersection1; + + unsigned int indexw2; + unsigned int indexp2; + const Vertex *vertexw2; + const Vertex *vertexp2; + Common::Point intersection2; + + bool disabled; // If true, this Patch was made superfluous by another Patch +}; + + +// Check if the given vertex on the work polygon is bypassed by this patch. +static bool isVertexCovered(const Patch &p, unsigned int wi) { + + // / v (outside) + // ---w1--1----p----w2--2---- + // ^ \ (inside) + if (wi > p.indexw1 && wi <= p.indexw2) + return true; + + // v / (outside) + // ---w2--2----p----w1--1---- + // \ ^ (inside) + if (p.indexw1 > p.indexw2 && (wi <= p.indexw2 || wi > p.indexw1)) + return true; + + // v / (outside) + // ---w1--2--1-------p----- + // w2 \ ^ (inside) + if (p.indexw1 == p.indexw2 && liesBefore(p.vertexw1, p.intersection1, p.intersection2) > 0) + return true; // This patch actually covers _all_ vertices on work + + return false; +} + +// Check if patch p1 makes patch p2 superfluous. +static bool isPatchCovered(const Patch &p1, const Patch &p2) { + + // Same exit and entry points + if (p1.intersection1 == p2.intersection1 && p1.intersection2 == p2.intersection2) + return true; + + // / * v (outside) + // ---p1w1--1----p2w1-1---p1w2--2---- + // ^ * \ (inside) + if (p1.indexw1 < p2.indexw1 && p2.indexw1 < p1.indexw2) + return true; + if (p1.indexw1 > p1.indexw2 && (p2.indexw1 > p1.indexw1 || p2.indexw1 < p1.indexw2)) + return true; + + + // / * v (outside) + // ---p1w1--11----p2w2-2---p1w2--12---- + // ^ * \ (inside) + if (p1.indexw1 < p2.indexw2 && p2.indexw2 < p1.indexw2) + return true; + if (p1.indexw1 > p1.indexw2 && (p2.indexw2 > p1.indexw1 || p2.indexw2 < p1.indexw2)) + return true; + + // Opposite of two above situations + if (p2.indexw1 < p1.indexw1 && p1.indexw1 < p2.indexw2) + return false; + if (p2.indexw1 > p2.indexw2 && (p1.indexw1 > p2.indexw1 || p1.indexw1 < p2.indexw2)) + return false; + + if (p2.indexw1 < p1.indexw2 && p1.indexw2 < p2.indexw2) + return false; + if (p2.indexw1 > p2.indexw2 && (p1.indexw2 > p2.indexw1 || p1.indexw2 < p2.indexw2)) + return false; + + + // The above checks covered the cases where one patch covers the other and + // the intersections of the patches are on different edges. + + // So, if we passed the above checks, we have to check the order of + // intersections on edges. + + + if (p1.indexw1 != p1.indexw2) { + + // / * v (outside) + // ---p1w1--11---21--------p1w2--2---- + // p2w1 ^ * \ (inside) + if (p1.indexw1 == p2.indexw1) + return (liesBefore(p1.vertexw1, p1.intersection1, p2.intersection1) < 0); + + // / * v (outside) + // ---p1w1--11---------p1w2--21---12---- + // ^ p2w1 * \ (inside) + if (p1.indexw2 == p2.indexw1) + return (liesBefore(p1.vertexw2, p1.intersection2, p2.intersection1) > 0); + + // If neither of the above, then the intervals of the polygon + // covered by patch1 and patch2 are disjoint + return false; + } + + // p1w1 == p1w2 + // Also, p1w1/p1w2 isn't strictly between p2 + + + // v / * (outside) + // ---p1w1--12--11-------p2w1-21---- + // p1w2 \ ^ * (inside) + + // v / / (outside) + // ---p1w1--12--21--11--------- + // p1w2 \ ^ ^ (inside) + // p2w1 + if (liesBefore(p1.vertexw1, p1.intersection1, p1.intersection2) > 0) + return (p1.indexw1 != p2.indexw1); + + // CHECKME: This is meaningless if p2w1 != p2w2 ?? + if (liesBefore(p2.vertexw1, p2.intersection1, p2.intersection2) > 0) + return false; + + // CHECKME: This is meaningless if p1w1 != p2w1 ?? + if (liesBefore(p2.vertexw1, p2.intersection1, p1.intersection1) <= 0) + return false; + + // CHECKME: This is meaningless if p1w2 != p2w1 ?? + if (liesBefore(p2.vertexw1, p2.intersection1, p1.intersection2) >= 0) + return false; + + return true; +} + +// Merge a single polygon into the work polygon. +// If there is an intersection between work and polygon, this function +// returns true, and replaces the vertex list of work by an extended version, +// that covers polygon. +// +// NOTE: The strategy used matches qfg1new closely, and is a bit error-prone. +// A more robust strategy would be inserting all intersection points directly +// into both vertex lists as a first pass. This would make finding the merged +// polygon a much more straightforward edge-walk, and avoid cases where SSCI's +// algorithm mixes up the order of multiple intersections on a single edge. +bool mergeSinglePolygon(Polygon &work, const Polygon &polygon) { +#ifdef DEBUG_MERGEPOLY + const Vertex *vertex; + debugN("work:"); + CLIST_FOREACH(vertex, &(work.vertices)) { + debugN(" (%d,%d) ", vertex->v.x, vertex->v.y); + } + debugN("\n"); + debugN("poly:"); + CLIST_FOREACH(vertex, &(polygon.vertices)) { + debugN(" (%d,%d) ", vertex->v.x, vertex->v.y); + } + debugN("\n"); +#endif + uint workSize = work.vertices.size(); + uint polygonSize = polygon.vertices.size(); + + int patchCount = 0; + Patch patchList[8]; + + const Vertex *workv = work.vertices._head; + const Vertex *polyv = polygon.vertices._head; + for (uint wi = 0; wi < workSize; ++wi, workv = workv->_next) { + for (uint pi = 0; pi < polygonSize; ++pi, polyv = polyv->_next) { + Common::Point intersection1; + Common::Point intersection2; + + bool intersects = segSegIntersect(workv, polyv, intersection1); + if (!intersects) + continue; + +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: intersection at work %d, poly %d", wi, pi); +#endif + + if (intersectDir(workv, polyv) >= 0) + continue; + +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: intersection in right direction"); +#endif + + int angle = 0; + int baseAngle = edgeDir(workv); + + // We now found the point where an edge of 'polygon' left 'work'. + // Now find the re-entry point. + + // NOTE: The order in which this searches does not always work + // properly if the correct patch would only use a single partial + // edge of poly. Because it starts at polyv->_next, it will skip + // the correct re-entry and proceed to the next. + + const Vertex *workv2; + const Vertex *polyv2 = polyv->_next; + + intersects = false; + + uint pi2, wi2; + for (pi2 = 0; pi2 < polygonSize; ++pi2, polyv2 = polyv2->_next) { + + int newAngle = edgeDir(polyv2); + + int relAngle = newAngle - baseAngle; + if (relAngle > 180) relAngle -= 360; + if (relAngle < -180) relAngle += 360; + + angle += relAngle; + baseAngle = newAngle; + + workv2 = workv; + for (wi2 = 0; wi2 < workSize; ++wi2, workv2 = workv2->_next) { + intersects = segSegIntersect(workv2, polyv2, intersection2); + if (!intersects) + continue; +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: re-entry intersection at work %d, poly %d", (wi + wi2) % workSize, (pi + 1 + pi2) % polygonSize); +#endif + + if (intersectDir(workv2, polyv2) > 0) { +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: re-entry intersection in right direction, angle = %d", angle); +#endif + break; // found re-entry point + } + + } + + if (intersects) + break; + + } + + if (!intersects || angle < 0) + continue; + + + if (patchCount >= 8) + error("kMergePoly: Too many patches"); + + // convert relative to absolute vertex indices + pi2 = (pi + 1 + pi2) % polygonSize; + wi2 = (wi + wi2) % workSize; + + Patch &newPatch = patchList[patchCount]; + newPatch.indexw1 = wi; + newPatch.vertexw1 = workv; + newPatch.indexp1 = pi; + newPatch.vertexp1 = polyv; + newPatch.intersection1 = intersection1; + + newPatch.indexw2 = wi2; + newPatch.vertexw2 = workv2; + newPatch.indexp2 = pi2; + newPatch.vertexp2 = polyv2; + newPatch.intersection2 = intersection2; + newPatch.disabled = false; + +#ifdef DEBUG_MERGEPOLY + debug("mergePoly: adding patch at work %d, poly %d", wi, pi); +#endif + + if (patchCount == 0) { + patchCount++; + continue; + } + + bool necessary = true; + for (int i = 0; i < patchCount; ++i) { + if (isPatchCovered(patchList[i], newPatch)) { + necessary = false; + break; + } + } + + if (!necessary) + continue; + + patchCount++; + + if (patchCount > 1) { + // check if this patch makes other patches superfluous + for (int i = 0; i < patchCount-1; ++i) + if (isPatchCovered(newPatch, patchList[i])) + patchList[i].disabled = true; + } + } + } + + + if (patchCount == 0) + return false; // nothing changed + + + // Determine merged work by doing a walk over the edges + // of work, crossing over to polygon when encountering a patch. + + Polygon output(0); + + workv = work.vertices._head; + for (uint wi = 0; wi < workSize; ++wi, workv = workv->_next) { + + bool covered = false; + for (int p = 0; p < patchCount; ++p) { + if (patchList[p].disabled) continue; + if (isVertexCovered(patchList[p], wi)) { + covered = true; + break; + } + } + + if (!covered) { + // Add vertex to output + output.vertices.insertAtEnd(new Vertex(workv->v)); + } + + + // CHECKME: Why is this the correct order in which to process + // the patches? (What if two of them start on this line segment + // in the opposite order?) + + for (int p = 0; p < patchCount; ++p) { + + const Patch &patch = patchList[p]; + if (patch.disabled) continue; + if (patch.indexw1 != wi) continue; + if (patch.intersection1 != workv->v) { + // Add intersection point to output + output.vertices.insertAtEnd(new Vertex(patch.intersection1)); + } + + // Add vertices from polygon between vertexp1 (excl) and vertexp2 (incl) + for (polyv = patch.vertexp1->_next; polyv != patch.vertexp2; polyv = polyv->_next) + output.vertices.insertAtEnd(new Vertex(polyv->v)); + + output.vertices.insertAtEnd(new Vertex(patch.vertexp2->v)); + + if (patch.intersection2 != patch.vertexp2->v) { + // Add intersection point to output + output.vertices.insertAtEnd(new Vertex(patch.intersection2)); + } + + // TODO: We could continue after the re-entry point here? + } + } + // Remove last vertex if it's the same as the first vertex + if (output.vertices._head->v == output.vertices._head->_prev->v) + output.vertices.remove(output.vertices._head->_prev); + + + // Slight hack: swap vertex lists of output and work polygons. + SWAP(output.vertices._head, work.vertices._head); + + return true; +} + + /** * This is a quite rare kernel function. An example of when it's called * is in QFG1VGA, after killing any monster. + * + * It takes a polygon, and extends it to also cover any polygons from the + * input list with which it intersects. Any of those polygons so covered + * from the input list are marked by adding 0x10 to their type field. */ reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) { -#if 0 // 3 parameters: raw polygon data, polygon list, list size reg_t polygonData = argv[0]; List *list = s->_segMan->lookupList(argv[1]); - Node *node = s->_segMan->lookupNode(list->first); - // List size is not needed - Polygon *polygon; - int count = 0; + // The size of the "work" point list SSCI uses. We use a dynamic one instead + //reg_t listSize = argv[2]; + + SegmentRef pointList = s->_segMan->dereference(polygonData); + if (!pointList.isValid() || pointList.skipByte) { + warning("kMergePoly: Polygon data pointer is invalid"); + return make_reg(0, 0); + } + + Node *node; + +#ifdef DEBUG_MERGEPOLY + node = s->_segMan->lookupNode(list->first); + while (node) { + draw_polygon(s, node->value, 320, 190); + node = s->_segMan->lookupNode(node->succ); + } + Common::Point prev, first; + prev = first = readPoint(pointList, 0); + for (int i = 1; readPoint(pointList, i).x != 0x7777; i++) { + Common::Point point = readPoint(pointList, i); + draw_line(s, prev, point, 1, 320, 190); + prev = point; + } + draw_line(s, prev, first, 1, 320, 190); + // Update the whole screen + g_sci->_gfxScreen->copyToScreen(); + g_system->updateScreen(); + g_system->delayMillis(1000); +#endif + + // The work polygon which we're going to merge with the polygons in list + Polygon work(0); + + for (int i = 0; true; ++i) { + Common::Point p = readPoint(pointList, i); + if (p.x == POLY_LAST_POINT) + break; + Vertex *vertex = new Vertex(p); + work.vertices.insertAtEnd(vertex); + } + + // TODO: Check behaviour for single-vertex polygons + node = s->_segMan->lookupNode(list->first); while (node) { - polygon = convert_polygon(s, node->value); + Polygon *polygon = convert_polygon(s, node->value); if (polygon) { - count += readSelectorValue(s->_segMan, node->value, SELECTOR(size)); + // CHECKME: Confirm vertex order that convert_polygon and + // fix_vertex_order output. For now, we re-reverse the order since + // convert_polygon reads the vertices reversed, and fix up head. + polygon->vertices.reverse(); + polygon->vertices._head = polygon->vertices._head->_next; + + // Merge this polygon into the work polygon if there is an + // intersection. + bool intersected = mergeSinglePolygon(work, *polygon); + + // If so, flag it + if (intersected) { + writeSelectorValue(s->_segMan, node->value, + SELECTOR(type), polygon->type + 0x10); +#ifdef DEBUG_MERGEPOLY + debugN("Merged polygon: "); + // Iterate over edges + Vertex *vertex; + CLIST_FOREACH(vertex, &(work.vertices)) { + debugN(" (%d,%d) ", vertex->v.x, vertex->v.y); + } + debugN("\n"); +#endif + } } node = s->_segMan->lookupNode(node->succ); } -#endif - // TODO: actually merge the polygon. We return an empty polygon for now. - // In QFG1VGA, you can walk over enemy bodies after killing them, since - // this is a stub. - reg_t output = allocateOutputArray(s->_segMan, 1); + + // Allocate output array + reg_t output = allocateOutputArray(s->_segMan, work.vertices.size()+1); SegmentRef arrayRef = s->_segMan->dereference(output); - writePoint(arrayRef, 0, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT)); - warning("Stub: kMergePoly"); + + // Copy work.vertices into arrayRef + Vertex *vertex; + unsigned int n = 0; + CLIST_FOREACH(vertex, &work.vertices) { + if (vertex == work.vertices._head || vertex->v != vertex->_prev->v) + writePoint(arrayRef, n++, vertex->v); + } + + writePoint(arrayRef, n, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT)); + +#ifdef DEBUG_MERGEPOLY + prev = first = readPoint(arrayRef, 0); + for (int i = 1; readPoint(arrayRef, i).x != 0x7777; i++) { + Common::Point point = readPoint(arrayRef, i); + draw_line(s, prev, point, 3, 320, 190); + prev = point; + } + + draw_line(s, prev, first, 3, 320, 190); + + // Update the whole screen + g_sci->_gfxScreen->copyToScreen(); + g_system->updateScreen(); + if (!g_sci->_gfxPaint16) + g_system->delayMillis(1000); + + debug("kMergePoly done"); +#endif + return output; } diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index c22d7c7b1e..c4db0b891c 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -165,6 +165,7 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) { // do clipping. In SQ4 we get the door code in here and that's even // larger than uint32! if (*source == '-') { + // FIXME: Setting result to -1 does _not_ negate the output. result = -1; source++; } diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 6bf9aff2fe..9b0cb38f51 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -275,7 +275,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { // Signal the engine scripts that the video is done writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); } else { - writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); + writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); } break; default: diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 037f4ab700..36d2841b07 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -144,7 +144,7 @@ void Script::load(int script_nr, ResourceManager *resMan) { _heapStart = _buf + _scriptSize; - assert(_bufSize - _scriptSize <= heap->size); + assert(_bufSize - _scriptSize >= heap->size); memcpy(_heapStart, heap->data, heap->size); } diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 659c13b13e..8639b6ef71 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -848,10 +848,47 @@ const uint16 qfg1vgaPatchFightEvents[] = { PATCH_END }; +// Script 814 of QFG1VGA is responsible for showing dialogs. However, the death +// screen message shown when the hero dies in room 64 (ghost room) is too large +// (254 chars long). Since the window header and main text are both stored in +// temp space, this is an issue, as the scripts read the window header, then the +// window text, which erases the window header text because of its length. To +// fix that, we allocate more temp space and move the pointer used for the +// window header a little bit, wherever it's used in script 814. +// Fixes bug #3568431. + +// Patch 1: Increase temp space +const byte qfg1vgaSignatureTempSpace[] = { + 4, + 0x3f, 0xba, // link 0xba + 0x87, 0x00, // lap 0 + 0 +}; + +const uint16 qfg1vgaPatchTempSpace[] = { + 0x3f, 0xca, // link 0xca + PATCH_END +}; + +// Patch 2: Move the pointer used for the window header a little bit +const byte qfg1vgaSignatureDialogHeader[] = { + 4, + 0x5b, 0x04, 0x80, // lea temp[0x80] + 0x36, // push + 0 +}; + +const uint16 qfg1vgaPatchDialogHeader[] = { + 0x5b, 0x04, 0x90, // lea temp[0x90] + PATCH_END +}; + // script, description, magic DWORD, adjust const SciScriptSignature qfg1vgaSignatures[] = { { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents }, + { 814, "window text temp space", 1, PATCH_MAGICDWORD(0x3f, 0xba, 0x87, 0x00), 0, qfg1vgaSignatureTempSpace, qfg1vgaPatchTempSpace }, + { 814, "dialog header offset", 3, PATCH_MAGICDWORD(0x5b, 0x04, 0x80, 0x36), 0, qfg1vgaSignatureDialogHeader, qfg1vgaPatchDialogHeader }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -915,9 +952,53 @@ const uint16 qfg3PatchImportDialog[] = { PATCH_END }; + + +// =========================================================================== +// Patch for the Woo dialog option in Uhura's conversation. Bug #3040722 +// Problem: The Woo dialog option (0xffb5) is negative, and therefore +// treated as an option opening a submenu. This leads to uhuraTell::doChild +// being called, which calls hero::solvePuzzle and then proceeds with +// Teller::doChild to open the submenu. However, there is no actual submenu +// defined for option -75 since -75 does not show up in uhuraTell::keys. +// This will cause Teller::doChild to run out of bounds while scanning through +// uhuraTell::keys. +// Strategy: there is another conversation option in uhuraTell::doChild calling +// hero::solvePuzzle (0xfffc) which does a ret afterwards without going to +// Teller::doChild. We jump to this call of hero::solvePuzzle to get that same +// behaviour. + +const byte qfg3SignatureWooDialog[] = { + 30, + 0x67, 0x12, // pTos 12 (query) + 0x35, 0xb6, // ldi b6 + 0x1a, // eq? + 0x2f, 0x05, // bt 05 + 0x67, 0x12, // pTos 12 (query) + 0x35, 0x9b, // ldi 9b + 0x1a, // eq? + 0x31, 0x0c, // bnt 0c + 0x38, 0x97, 0x02, // pushi 0297 + 0x7a, // push2 + 0x38, 0x0c, 0x01, // pushi 010c + 0x7a, // push2 + 0x81, 0x00, // lag 00 + 0x4a, 0x08, // send 08 + 0x67, 0x12, // pTos 12 (query) + 0x35, 0xb5, // ldi b5 + 0 +}; + +const uint16 qfg3PatchWooDialog[] = { + PATCH_ADDTOOFFSET | +0x29, + 0x33, 0x11, // jmp to 0x6a2, the call to hero::solvePuzzle for 0xFFFC + PATCH_END +}; + // script, description, magic DWORD, adjust const SciScriptSignature qfg3Signatures[] = { { 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, + { 440, "dialog crash when asking about Woo", 1, PATCH_MAGICDWORD(0x67, 0x12, 0x35, 0xb5), -26, qfg3SignatureWooDialog, qfg3PatchWooDialog }, SCI_SIGNATUREENTRY_TERMINATOR }; diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index d3abb9ff41..b2f22aa985 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -514,7 +514,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { if (!objType) { debugN("End of script object (#0) encountered.\n"); - debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)", + debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)\n", objectctr[6], objectctr[1], objectctr[7], objectctr[10]); return; } @@ -527,7 +527,8 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { _seeker += objsize; - objectctr[objType]++; + if (objType >= 0 && objType < ARRAYSIZE(objectctr)) + objectctr[objType]++; switch (objType) { case SCI_OBJ_OBJECT: diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 951fc7c363..04c1dab158 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -825,7 +825,7 @@ byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) { } bool SegManager::freeDynmem(reg_t addr) { - if (addr.getSegment() < 1 || addr.getSegment() >= _heap.size() || + if (addr.getSegment() < 1 || addr.getSegment() >= _heap.size() || !_heap[addr.getSegment()] || _heap[addr.getSegment()]->getType() != SEG_TYPE_DYNMEM) return false; // error diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 3f43966976..ef8f165084 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -228,7 +228,7 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP uint32 exportAddr = scr->validateExportFunc(pubfunct, false); if (!exportAddr) return NULL; - + // Check if a breakpoint is set on this method g_sci->checkExportBreakpoint(script, pubfunct); diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 9fa0368784..db510c2545 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -243,9 +243,6 @@ const SciWorkaroundEntry kDisposeScript_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, call,index, workaround const SciWorkaroundEntry kDoSoundFade_workarounds[] = { - { GID_CAMELOT, -1, 989, 0, "rmMusic", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called frequently with a NULL reference (i.e. 0:0) - bug #3035149 - { GID_KQ1, -1, 989, 0, "gameSound", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called in several scenes (e.g. graham cracker) with 0:0 - { GID_KQ4, -1, 989, 0, "mySound", "", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called in the demo when trying to open the non-existent menu with 0:0 - bug #3036942 { GID_KQ5, 213, 989, 0, "globalSound3", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #3037594 { GID_KQ6, 105, 989, 0, "globalSound", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object { GID_KQ6, 460, 989, 0, "globalSound2", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #3034567 diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index ad1d9e8623..5535a7408a 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -68,7 +68,7 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) { while (captureEvents) { curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); - + if (curEvent.type == SCI_EVENT_NONE) { eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event } else { @@ -170,11 +170,11 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) { // Note: the following checkAltInput call might make the text // too wide to fit, but SSCI fails to check that too. } - + reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap)); Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject); //texteditCursorErase(); // TODO: Cursor - + // Write back string _segMan->strcpy(textReference, text.c_str()); // Modify the buffer and show it diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 968014c032..8b7fa2c384 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -197,7 +197,7 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) { } else { it->planeOffsetX = 0; } - + if (it->planeRect.top < 0) { it->planeOffsetY = -it->planeRect.top; it->planeRect.top = 0; @@ -420,7 +420,7 @@ void GfxFrameout::deletePlaneItems(reg_t planeObject) { } else { objectMatches = true; } - + if (objectMatches) { FrameoutEntry *itemEntry = *listIterator; listIterator = _screenItems.erase(listIterator); @@ -661,7 +661,7 @@ void GfxFrameout::kernelFrameout() { if (!itemEntry->visible) continue; - + if (itemEntry->object.isNull()) { // Picture cel data _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x); @@ -703,7 +703,7 @@ void GfxFrameout::kernelFrameout() { view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); else - view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, + view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect); @@ -755,10 +755,10 @@ void GfxFrameout::kernelFrameout() { if (view) { if (!clipRect.isEmpty()) { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) - view->draw(itemEntry->celRect, clipRect, translatedClipRect, + view->draw(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires()); else - view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, + view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY); } } @@ -817,7 +817,7 @@ void GfxFrameout::printPlaneItemList(Console *con, reg_t planeObject) { for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) { FrameoutEntry *e = *listIterator; reg_t itemPlane = readSelector(_segMan, e->object, SELECTOR(plane)); - + if (planeObject == itemPlane) { Common::String curItemName = _segMan->getObjectName(e->object); Common::Rect icr = e->celRect; diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index 89f3625e2c..d20aa80c77 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -559,8 +559,8 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { SciTrackOriginReply originReply; SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply); if (solution.type == WORKAROUND_NONE) - error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)", - PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(), + error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)", + PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset); assert(solution.type == WORKAROUND_IGNORE); break; diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index 6b4c8180bf..8acdeed763 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -380,17 +380,50 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor int16 oldtop = pwnd->dims.top; int16 oldleft = pwnd->dims.left; - if (wmprect.top > pwnd->dims.top) + // WORKAROUND: We also adjust the restore rect when adjusting the window + // rect. + // SSCI does not do this. It wasn't necessary in the original interpreter, + // but it is needed for Freddy Pharkas CD. This version does not normally + // have text, but we allow this by modifying the text/speech setting + // according to what is set in the ScummVM GUI (refer to syncIngameAudioOptions() + // in sci.cpp). Since the text used in Freddy Pharkas CD is quite large in + // some cases, it ends up being offset in order to fit inside the screen, + // but the associated restore rect isn't adjusted accordingly, leading to + // artifacts being left on screen when some text boxes are removed. The + // fact that the restore rect wasn't ever adjusted doesn't make sense, and + // adjusting it shouldn't have any negative side-effects (it *should* be + // adjusted, normally, but SCI doesn't do it). The big text boxes are still + // odd-looking, because the text rect is drawn outside the text window rect, + // but at least there aren't any leftover textbox artifacts left when the + // boxes are removed. Adjusting the text window rect would require more + // invasive changes than this one, thus it's not really worth the effort + // for a feature that was not present in the original game, and its + // implementation is buggy in the first place. + // Adjusting the restore rect properly fixes bug #3575276. + + if (wmprect.top > pwnd->dims.top) { pwnd->dims.moveTo(pwnd->dims.left, wmprect.top); + if (restoreRect) + pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.top); + } - if (wmprect.bottom < pwnd->dims.bottom) + if (wmprect.bottom < pwnd->dims.bottom) { pwnd->dims.moveTo(pwnd->dims.left, wmprect.bottom - pwnd->dims.bottom + pwnd->dims.top); + if (restoreRect) + pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.bottom - pwnd->restoreRect.bottom + pwnd->restoreRect.top); + } - if (wmprect.right < pwnd->dims.right) + if (wmprect.right < pwnd->dims.right) { pwnd->dims.moveTo(wmprect.right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top); + if (restoreRect) + pwnd->restoreRect.moveTo(wmprect.right + pwnd->restoreRect.left - pwnd->restoreRect.right, pwnd->restoreRect.top); + } - if (wmprect.left > pwnd->dims.left) + if (wmprect.left > pwnd->dims.left) { pwnd->dims.moveTo(wmprect.left, pwnd->dims.top); + if (restoreRect) + pwnd->restoreRect.moveTo(wmprect.left, pwnd->restoreRect.top); + } pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop); diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 42ae00b525..15b18ce8e6 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -876,6 +876,7 @@ void SciEngine::syncIngameAudioOptions() { if (getGameId() == GID_SQ4 || getGameId() == GID_FREDDYPHARKAS || getGameId() == GID_ECOQUEST + || getGameId() == GID_LSL6 // TODO: The following need script patches for simultaneous speech and subtitles //|| getGameId() == GID_KQ6 //|| getGameId() == GID_LAURABOW2 diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 9f18219cb7..3b9844b326 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -110,6 +110,7 @@ enum SciGameId { GID_ASTROCHICKEN, GID_CAMELOT, GID_CASTLEBRAIN, + GID_CHEST, GID_CHRISTMAS1988, GID_CHRISTMAS1990, GID_CHRISTMAS1992, @@ -228,6 +229,26 @@ public: bool canLoadGameStateCurrently(); bool canSaveGameStateCurrently(); void syncSoundSettings(); + + /** + * Syncs the audio options of the ScummVM launcher (speech, subtitles or + * both) with the in-game audio options of certain CD game versions. For + * some games, this allows simultaneous playing of speech and subtitles, + * even if the original games didn't support this feature. + * + * SCI1.1 games which support simultaneous speech and subtitles: + * - EcoQuest 1 CD + * - Leisure Suit Larry 6 CD + * SCI1.1 games which don't support simultaneous speech and subtitles, + * and we add this functionality in ScummVM: + * - Space Quest 4 CD + * - Freddy Pharkas CD + * SCI1.1 games which don't support simultaneous speech and subtitles, + * and we haven't added any extra functionality in ScummVM because extra + * script patches are needed: + * - Laura Bow 2 CD + * - King's Quest 6 CD + */ void syncIngameAudioOptions(); const SciGameId &getGameId() const { return _gameId; } diff --git a/engines/sci/sound/drivers/fmtowns.cpp b/engines/sci/sound/drivers/fmtowns.cpp index 6d8bb2e525..21cb2f1e43 100644 --- a/engines/sci/sound/drivers/fmtowns.cpp +++ b/engines/sci/sound/drivers/fmtowns.cpp @@ -52,8 +52,8 @@ public: uint16 _duration; private: - uint8 _id; - uint8 _velo; + uint8 _id; + uint8 _velo; uint8 _program; MidiDriver_FMTowns *_drv; @@ -76,7 +76,7 @@ public: void addChannels(int num); void dropChannels(int num); - + uint8 currentProgram() const; private: @@ -132,7 +132,7 @@ private: TownsMidiPart **_parts; TownsChannel **_out; - + uint8 _masterVolume; bool _soundOn; @@ -590,7 +590,7 @@ void MidiDriver_FMTowns::addMissingChannels() { avlChan -= _parts[i]->_chanMissing; uint8 m = _parts[i]->_chanMissing; _parts[i]->_chanMissing = 0; - _parts[i]->addChannels(m); + _parts[i]->addChannels(m); } else { _parts[i]->_chanMissing -= avlChan; _parts[i]->addChannels(avlChan); @@ -601,7 +601,7 @@ void MidiDriver_FMTowns::addMissingChannels() { void MidiDriver_FMTowns::updateParser() { if (_timerProc) - _timerProc(_timerProcPara); + _timerProc(_timerProcPara); } void MidiDriver_FMTowns::updateChannels() { diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 422948f975..4e54797960 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -98,7 +98,7 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in midiMixChannels(); } - _num_tracks = 1; + _numTracks = 1; _tracks[0] = _mixedData; if (_pSnd) setTrack(0); @@ -144,7 +144,7 @@ byte *MidiParser_SCI::midiMixChannels() { _mixedData = outData; long ticker = 0; byte channelNr, curDelta; - byte midiCommand = 0, midiParam, global_prev = 0; + byte midiCommand = 0, midiParam, globalPrev = 0; long newDelta; SoundResource::Channel *channel; @@ -190,13 +190,13 @@ byte *MidiParser_SCI::midiMixChannels() { byte midiChannel = midiCommand & 0xF; _channelUsed[midiChannel] = true; - if (midiCommand != global_prev) + if (midiCommand != globalPrev) *outData++ = midiCommand; *outData++ = midiParam; if (nMidiParams[(midiCommand >> 4) - 8] == 2) *outData++ = channel->data[channel->curPos++]; channel->prev = midiCommand; - global_prev = midiCommand; + globalPrev = midiCommand; } } @@ -372,8 +372,8 @@ void MidiParser_SCI::unloadMusic() { resetTracking(); allNotesOff(); } - _num_tracks = 0; - _active_track = 255; + _numTracks = 0; + _activeTrack = 255; _resetOnPause = false; if (_mixedData) { @@ -454,26 +454,26 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { debugC(4, kDebugLevelSound, "signal %04x", _signalToSet); } - info.start = _position._play_pos; + info.start = _position._playPos; info.delta = 0; - while (*_position._play_pos == 0xF8) { + while (*_position._playPos == 0xF8) { info.delta += 240; - _position._play_pos++; + _position._playPos++; } - info.delta += *(_position._play_pos++); + info.delta += *(_position._playPos++); // Process the next info. - if ((_position._play_pos[0] & 0xF0) >= 0x80) - info.event = *(_position._play_pos++); + if ((_position._playPos[0] & 0xF0) >= 0x80) + info.event = *(_position._playPos++); else - info.event = _position._running_status; + info.event = _position._runningStatus; if (info.event < 0x80) return; - _position._running_status = info.event; + _position._runningStatus = info.event; switch (info.command()) { case 0xC: - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; if (info.channel() == 0xF) {// SCI special case if (info.basic.param1 != kSetSignalLoop) { @@ -488,23 +488,23 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { // in glitches (e.g. the intro of LB1 Amiga gets stuck - bug // #3297883). Refer to MusicEntry::setSignal() in sound/music.cpp. if (_soundVersion <= SCI_VERSION_0_LATE || - _position._play_tick || info.delta) { + _position._playTick || info.delta) { _signalSet = true; _signalToSet = info.basic.param1; } } else { - _loopTick = _position._play_tick + info.delta; + _loopTick = _position._playTick + info.delta; } } break; case 0xD: - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; case 0xB: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); // Reference for some events: // http://wiki.scummvm.org/index.php/SCI/Specifications/Sound/SCI0_Resource_Format#Status_Reference @@ -588,8 +588,8 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { case 0x9: case 0xA: case 0xE: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); if (info.command() == 0x9 && info.basic.param2 == 0) info.event = info.channel() | 0x80; info.length = 0; @@ -598,12 +598,12 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { case 0xF: // System Common, Meta or SysEx event switch (info.event & 0x0F) { case 0x2: // Song Position Pointer - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); break; case 0x3: // Song Select - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; @@ -617,16 +617,16 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { break; case 0x0: // SysEx - info.length = readVLQ(_position._play_pos); - info.ext.data = _position._play_pos; - _position._play_pos += info.length; + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; break; case 0xF: // META event - info.ext.type = *(_position._play_pos++); - info.length = readVLQ(_position._play_pos); - info.ext.data = _position._play_pos; - _position._play_pos += info.length; + info.ext.type = *(_position._playPos++); + info.length = readVLQ(_position._playPos); + info.ext.data = _position._playPos; + _position._playPos += info.length; if (info.ext.type == 0x2F) {// end of track reached if (_pSnd->loop) _pSnd->loop--; @@ -677,21 +677,21 @@ void MidiParser_SCI::allNotesOff() { // Turn off all active notes for (i = 0; i < 128; ++i) { for (j = 0; j < 16; ++j) { - if ((_active_notes[i] & (1 << j)) && (_channelRemap[j] != -1)){ + if ((_activeNotes[i] & (1 << j)) && (_channelRemap[j] != -1)){ sendToDriver(0x80 | j, i, 0); } } } // Turn off all hanging notes - for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) { - byte midiChannel = _hanging_notes[i].channel; - if ((_hanging_notes[i].time_left) && (_channelRemap[midiChannel] != -1)) { - sendToDriver(0x80 | midiChannel, _hanging_notes[i].note, 0); - _hanging_notes[i].time_left = 0; + for (i = 0; i < ARRAYSIZE(_hangingNotes); i++) { + byte midiChannel = _hangingNotes[i].channel; + if ((_hangingNotes[i].timeLeft) && (_channelRemap[midiChannel] != -1)) { + sendToDriver(0x80 | midiChannel, _hangingNotes[i].note, 0); + _hangingNotes[i].timeLeft = 0; } } - _hanging_notes_count = 0; + _hangingNotesCount = 0; // To be sure, send an "All Note Off" event (but not all MIDI devices // support this...). @@ -703,7 +703,7 @@ void MidiParser_SCI::allNotesOff() { } } - memset(_active_notes, 0, sizeof(_active_notes)); + memset(_activeNotes, 0, sizeof(_activeNotes)); } void MidiParser_SCI::setMasterVolume(byte masterVolume) { diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index 82f34070a4..d3fd337644 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -65,7 +65,7 @@ public: void setMasterVolume(byte masterVolume); void setVolume(byte volume); void stop() { - _abort_parse = true; + _abortParse = true; allNotesOff(); } void pause() { diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index 5d32f40f18..7782ab4e48 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -340,6 +340,12 @@ reg_t SoundCommandParser::kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc) reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { reg_t obj = argv[0]; + // The object can be null in several SCI0 games (e.g. Camelot, KQ1, KQ4, MUMG). + // Check bugs #3035149, #3036942 and #3578335. + // In this case, we just ignore the call. + if (obj.isNull() && argc == 1) + return acc; + MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { debugC(kDebugLevelSound, "kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj)); diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp index 608c77136f..0337a8d306 100644 --- a/engines/sci/video/robot_decoder.cpp +++ b/engines/sci/video/robot_decoder.cpp @@ -109,13 +109,13 @@ bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { } bool RobotDecoder::load(GuiResourceId id) { - // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - + // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - // its drawn at odd coordinates. SV can't play it either (along with some // others), so it must be some new functionality added in RAMA's robot // videos. Skip it for now. if (g_sci->getGameId() == GID_RAMA && id == 1003) return false; - + // TODO: The robot video in the Lighthouse demo gets stuck if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) return false; @@ -247,7 +247,7 @@ void RobotDecoder::readNextPacket() { audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2); } else { _fileStream->skip(audioChunkSize); - } + } } void RobotDecoder::readHeaderChunk() { diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h index ebc3262939..437954f7fb 100644 --- a/engines/sci/video/robot_decoder.h +++ b/engines/sci/video/robot_decoder.h @@ -45,13 +45,13 @@ public: bool loadStream(Common::SeekableReadStream *stream); bool load(GuiResourceId id); void close(); - + void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); } Common::Point getPos() const { return _pos; } protected: void readNextPacket(); - + private: class RobotVideoTrack : public FixedRateVideoTrack { public: diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index b8722b6963..0c375efcdd 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -573,19 +573,19 @@ bool Actor_v2::checkWalkboxesHaveDirectPath(Common::Point &foundPath) { return false; } -bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, - const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result) +bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, + const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result) { const Common::Point v1 = line1End - line1Start; // line1(n1) = line1Start + n1 * v1 const Common::Point v2 = line2End - line2Start; // line2(n2) = line2Start + n2 * v2 - + double det = v2.x * v1.y - v1.x * v2.y; if (det == 0) return false; - double n1 = ((double)v2.x * (line2Start.y - line1Start.y) - + double n1 = ((double)v2.x * (line2Start.y - line1Start.y) - (double)v2.y * (line2Start.x - line1Start.x)) / det; - double n2 = ((double)v1.x * (line2Start.y - line1Start.y) - + double n2 = ((double)v1.x * (line2Start.y - line1Start.y) - (double)v1.y * (line2Start.x - line1Start.x)) / det; // both coefficients have to be in [0, 1], otherwise the intersection is @@ -599,16 +599,16 @@ bool Actor_v0::intersectLineSegments(const Common::Point &line1Start, const Comm } /* - * MM v0 allows the actor to walk in a direct line between boxes to the target + * MM v0 allows the actor to walk in a direct line between boxes to the target * if actor and target share a horizontal or vertical corridor. - * If such a corridor is found the actor is not forced to go horizontally or + * If such a corridor is found the actor is not forced to go horizontally or * vertically from one box to the next but can also walk diagonally. * - * Note: the original v0 interpreter sets the target destination for diagonal + * Note: the original v0 interpreter sets the target destination for diagonal * walking only once and then rechecks whenever the actor reaches a new box if the - * walk destination is still suitable for the current box. - * ScummVM does not perform such a check, so it is possible to leave the walkboxes - * in some cases, for example L-shaped rooms like the swimming pool (actor walks over water) + * walk destination is still suitable for the current box. + * ScummVM does not perform such a check, so it is possible to leave the walkboxes + * in some cases, for example L-shaped rooms like the swimming pool (actor walks over water) * or the medical room (actor walks over examination table). * To solve this we intersect the new walk destination with the actor's walkbox borders, * so a recheck is done when the actor leaves his box. This is done by the @@ -992,7 +992,7 @@ void Actor_v0::setDirection(int direction) { res = 7; // Face Camera break; } - + _animFrameRepeat = -1; animateActor(res); if (_moving) @@ -1408,7 +1408,7 @@ void Actor::showActor() { if (_vm->_game.version == 0) { Actor_v0 *a = ((Actor_v0 *)this); - + a->_costCommand = a->_costCommandNew = 0xFF; for (int i = 0; i < 8; ++i) { @@ -2056,7 +2056,7 @@ void Actor_v0::animateCostume() { void Actor_v0::speakCheck() { if (v0ActorTalkArray[_number] & 0x80) return; - + int cmd = newDirToOldDir(_facing); if (_speaking & 0x80) @@ -2884,7 +2884,7 @@ void Actor_v0::animateActor(int anim) { _costCommandNew = anim; _vm->_costumeLoader->costumeDecodeData(this, 0, 0); - + if (dir == -1) return; diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h index 0ed239d005..a733fdb4ed 100644 --- a/engines/scumm/actor.h +++ b/engines/scumm/actor.h @@ -377,7 +377,7 @@ public: virtual void saveLoadWithSerializer(Serializer *ser); protected: - bool intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, + bool intersectLineSegments(const Common::Point &line1Start, const Common::Point &line1End, const Common::Point &line2Start, const Common::Point &line2End, Common::Point &result); virtual bool checkWalkboxesHaveDirectPath(Common::Point &foundPath); }; diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index 4064853b6b..9ae75b6683 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -69,7 +69,7 @@ void ScummEngine::loadCJKFont() { _cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode); _2byteWidth = _2byteHeight = 12; - _useCJKMode = true; + _useCJKMode = true; #endif } else if (_game.id == GID_MONKEY && _game.platform == Common::kPlatformSegaCD && _language == Common::JA_JPN) { int numChar = 1413; @@ -499,7 +499,7 @@ int CharsetRendererV3::getCharWidth(uint16 chr) { if (_vm->_useCJKMode && (chr & 0x80)) spacing = _vm->_2byteWidth / 2; - + if (!spacing) spacing = *(_widthTable + chr); @@ -644,7 +644,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { drawBits1(*vs, _left + vs->xstart, drawTop, charPtr, drawTop, origWidth, origHeight); else drawBits1(_vm->_textSurface, _left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier, charPtr, drawTop, origWidth, origHeight); - + if (is2byte) { origWidth /= _vm->_textSurfaceMultiplier; height /= _vm->_textSurfaceMultiplier; @@ -1399,7 +1399,7 @@ void CharsetRendererTownsClassic::drawBitsN(const Graphics::Surface&, byte *dst, _vm->_cjkFont->drawChar(_vm->_textSurface, _sjisCurChar, _left * _vm->_textSurfaceMultiplier, (_top - _vm->_screenTop) * _vm->_textSurfaceMultiplier, _vm->_townsCharsetColorMap[1], _shadowColor); return; } - + bool scale2x = (_vm->_textSurfaceMultiplier == 2); dst = (byte *)_vm->_textSurface.pixels + (_top - _vm->_screenTop) * _vm->_textSurface.pitch * _vm->_textSurfaceMultiplier + _left * _vm->_textSurfaceMultiplier; diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index b8f1d84045..1c1df51921 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -112,7 +112,7 @@ public: class CharsetRendererClassic : public CharsetRendererCommon { protected: virtual void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height); - void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask); + void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask); virtual bool prepareDraw(uint16 chr); int _width, _height, _origWidth, _origHeight; @@ -195,7 +195,7 @@ public: int getCharWidth(uint16 chr); int getFontHeight(); - + private: void enableShadow(bool enable); void drawBits1(Graphics::Surface &dest, int x, int y, const byte *src, int drawTop, int width, int height); diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp index 3f89bc9611..4ebdd00fdc 100644 --- a/engines/scumm/costume.cpp +++ b/engines/scumm/costume.cpp @@ -1188,7 +1188,7 @@ byte V0CostumeRenderer::drawLimb(const Actor *a, int limb) { _draw_top = 200; _draw_bottom = 0; } - + // Invalid current position? if (a->_cost.curpos[limb] == 0xFFFF) return 0; @@ -1377,7 +1377,7 @@ byte V0CostumeLoader::increaseAnim(Actor *a, int limb) { // Reset the comstume command a0->_costCommandNew = 0xFF; a0->_costCommand = 0xFF; - + // Set the frame/start to invalid a0->_cost.frame[limb] = 0xFFFF; a0->_cost.start[limb] = 0xFFFF; diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp index 88681898f5..269ae9e10a 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -180,7 +180,7 @@ void ScummEngine_v70he::setDefaultCursor() { 0xff, 0xff, 0xff, 0, 0, 0, }; - + memset(_grabbedCursor, 5, sizeof(_grabbedCursor)); _cursor.hotspotX = _cursor.hotspotY = 2; diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index edcf2e6fea..dc5acbdb7d 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -382,7 +382,7 @@ bool ScummDebugger::Cmd_Actor(int argc, const char **argv) { DebugPrintf("Actor[%d].costume = %d\n", actnum, a->_costume); } } else if (!strcmp(argv[2], "name")) { - DebugPrintf("Name of actor %d: %s\n", actnum, + DebugPrintf("Name of actor %d: %s\n", actnum, _vm->getObjOrActorName(_vm->actorToObj(actnum))); } else if (!strcmp(argv[2], "condmask")) { if (argc > 3) { diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index 5404c7f8b1..e5c3023380 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -242,6 +242,10 @@ static Common::String generateFilenameForDetection(const char *pattern, Filename return result; } +bool ScummEngine::isMacM68kIMuse() const { + return _game.platform == Common::kPlatformMacintosh && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && !(_game.features & GF_MAC_CONTAINER); +} + struct DetectorDesc { Common::FSNode node; Common::String md5; @@ -473,6 +477,11 @@ static void computeGameSettingsFromMD5(const Common::FSList &fslist, const GameF if (dr.language == UNK_LANG) { dr.language = detectLanguage(fslist, dr.game.id); } + + // HACK: Detect between 68k and PPC versions + if (dr.game.platform == Common::kPlatformMacintosh && dr.game.version >= 5 && dr.game.heversion == 0 && strstr(gfp->pattern, "Data")) + dr.game.features |= GF_MAC_CONTAINER; + break; } } diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index be1b90e356..3120017db6 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -236,7 +236,7 @@ static const GameSettings gameVariantsTable[] = { {"monkey", "VGA", "vga", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO1(GUIO_NOSPEECH)}, {"monkey", "EGA", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, GF_16COLOR, Common::kPlatformPC, GUIO1(GUIO_NOSPEECH)}, {"monkey", "No AdLib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR, GF_16COLOR, Common::kPlatformAtariST, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, - {"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, + {"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"monkey", "CD", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, {"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 0e531daf73..945c5b6611 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -180,7 +180,7 @@ static const ResString string_map_table_v345[] = { // "Moechten Sie wirklich neu starten? (J/N)J" // Will react to J as 'Yes' {5, _s("Are you sure you want to restart? (Y/N)")}, - // I18N: you may specify 'Yes' symbol at the end of the line. See previous comment + // I18N: you may specify 'Yes' symbol at the end of the line. See previous comment {6, _s("Are you sure you want to quit? (Y/N)")}, // Added in SCUMM4 diff --git a/engines/scumm/he/logic/soccer.cpp b/engines/scumm/he/logic/soccer.cpp index 05f377a736..2b29f93173 100644 --- a/engines/scumm/he/logic/soccer.cpp +++ b/engines/scumm/he/logic/soccer.cpp @@ -303,7 +303,7 @@ int LogicHEsoccer::op_1008(int outArray, int srcX, int srcY, int srcZ, int vecX, putInArray(outArray, segmentsSoFar, 5, vecX); putInArray(outArray, segmentsSoFar, 6, vecY); putInArray(outArray, segmentsSoFar++, 7, vecZ); - } + } } else { srcY = 0; int thisVecX = vecX; @@ -628,7 +628,7 @@ int LogicHEsoccer::op_1014(int32 srcX, int32 srcY, int32 srcZ, int32 velX, int32 adjustedVelZ = ((double)srcZ - 3869.0) / 100.0; break; } - + int foundCollision = 0; // work out which collision objects we might collide with (if any) diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 47b4b8ad33..798f703db6 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -2286,8 +2286,7 @@ void Wiz::fillWizLine(const WizParameters *params) { lineP.depth = bitDepth; if (params->processFlags & kWPFParams) { - assert (params->params2 == 1); // Catch untested usage - Graphics::drawThickLine(x1, y1, x2, y2, params->params1, color, drawProc, &lineP); + Graphics::drawThickLine(x1, y1, x2, y2, params->params1, params->params2, color, drawProc, &lineP); } else { Graphics::drawLine(x1, y1, x2, y2, color, drawProc, &lineP); } diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp index 27a72c2afe..016ba89e7b 100644 --- a/engines/scumm/imuse/imuse.cpp +++ b/engines/scumm/imuse/imuse.cpp @@ -164,7 +164,7 @@ bool IMuseInternal::isMT32(int sound) { return true; case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2 - return true; + return false; case MKTAG('G', 'M', 'D', ' '): return false; @@ -226,6 +226,45 @@ bool IMuseInternal::isMIDI(int sound) { return false; } +bool IMuseInternal::supportsPercussion(int sound) { + byte *ptr = g_scumm->_res->_types[rtSound][sound]._address; + if (ptr == NULL) + return false; + + uint32 tag = READ_BE_UINT32(ptr); + switch (tag) { + case MKTAG('A', 'D', 'L', ' '): + case MKTAG('A', 'S', 'F', 'X'): // Special AD class for old AdLib sound effects + case MKTAG('S', 'P', 'K', ' '): + return false; + + case MKTAG('A', 'M', 'I', ' '): + case MKTAG('R', 'O', 'L', ' '): + return true; + + case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2 + // This is MIDI, i.e. uses MIDI style program changes, but without a + // special percussion channel. + return false; + + case MKTAG('G', 'M', 'D', ' '): + case MKTAG('M', 'I', 'D', 'I'): // Occurs in Sam & Max + return true; + } + + // Old style 'RO' has equivalent properties to 'ROL' + if (ptr[0] == 'R' && ptr[1] == 'O') + return true; + // Euphony tracks show as 'SO' and have equivalent properties to 'ADL' + // FIXME: Right now we're pretending it's GM. + if (ptr[4] == 'S' && ptr[5] == 'O') + return true; + + error("Unknown music type: '%c%c%c%c'", (char)tag >> 24, (char)tag >> 16, (char)tag >> 8, (char)tag); + + return false; +} + MidiDriver *IMuseInternal::getBestMidiDriver(int sound) { MidiDriver *driver = NULL; diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h index 3b0d36e119..846e2d7545 100644 --- a/engines/scumm/imuse/imuse_internal.h +++ b/engines/scumm/imuse/imuse_internal.h @@ -204,6 +204,7 @@ protected: bool _isMT32; bool _isMIDI; + bool _supportsPercussion; protected: // Player part @@ -458,6 +459,7 @@ protected: byte *findStartOfSound(int sound, int ct = (kMThd | kFORM)); bool isMT32(int sound); bool isMIDI(int sound); + bool supportsPercussion(int sound); int get_queue_sound_status(int sound) const; void handle_marker(uint id, byte data); int get_channel_volume(uint a); diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp index 73e7704469..89c16a8bb5 100644 --- a/engines/scumm/imuse/imuse_part.cpp +++ b/engines/scumm/imuse/imuse_part.cpp @@ -27,6 +27,7 @@ #include "common/util.h" #include "scumm/imuse/imuse_internal.h" #include "scumm/saveload.h" +#include "scumm/scumm.h" namespace Scumm { @@ -365,7 +366,17 @@ void Part::set_instrument(uint b) { _bank = (byte)(b >> 8); if (_bank) error("Non-zero instrument bank selection. Please report this"); - _instrument.program((byte)b, _player->isMT32()); + // HACK: Horrible hack to allow tracing of program change source. + // The Mac m68k versions of MI2 and Indy4 use a different program "bank" + // when it gets program change events through the iMuse SysEx handler. + // We emulate this by introducing a special instrument, which sets + // the instrument via sysEx_customInstrument. This seems to be + // exclusively used for special sound effects like the "spit" sound. + if (g_scumm->isMacM68kIMuse()) { + _instrument.macSfx(b); + } else { + _instrument.program((byte)b, _player->isMT32()); + } if (clearToTransmit()) _instrument.send(_mc); } diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp index 53ccfb3734..3a9c42a920 100644 --- a/engines/scumm/imuse/imuse_player.cpp +++ b/engines/scumm/imuse/imuse_player.cpp @@ -78,6 +78,7 @@ Player::Player() : _speed(128), _isMT32(false), _isMIDI(false), + _supportsPercussion(false), _se(0), _vol_chan(0) { } @@ -103,6 +104,7 @@ bool Player::startSound(int sound, MidiDriver *midi) { _isMT32 = _se->isMT32(sound); _isMIDI = _se->isMIDI(sound); + _supportsPercussion = _se->supportsPercussion(sound); _parts = NULL; _active = true; @@ -386,6 +388,8 @@ void Player::sysEx(const byte *p, uint16 len) { // SysEx manufacturer 0x97 has been spotted in the // Monkey Island 2 AdLib music, so don't make this a // fatal error. See bug #1481383. + // The Macintosh version of Monkey Island 2 simply + // ignores these SysEx events too. if (a == 0) warning("Unknown SysEx manufacturer 0x00 0x%02X 0x%02X", p[0], p[1]); else @@ -1009,6 +1013,7 @@ void Player::fixAfterLoad() { _parser->jumpToTick(_music_tick); // start_seq_sound already switched tracks _isMT32 = _se->isMT32(_id); _isMIDI = _se->isMIDI(_id); + _supportsPercussion = _se->supportsPercussion(_id); } } diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp index 11bb4e7605..61c73b1e2d 100644 --- a/engines/scumm/imuse/instrument.cpp +++ b/engines/scumm/imuse/instrument.cpp @@ -278,6 +278,21 @@ private: byte _instrument[23]; }; +class Instrument_MacSfx : public InstrumentInternal { +private: + byte _program; + +public: + Instrument_MacSfx(byte program); + Instrument_MacSfx(Serializer *s); + void saveOrLoad(Serializer *s); + void send(MidiChannel *mc); + void copy_to(Instrument *dest) { dest->macSfx(_program); } + bool is_valid() { + return (_program < 128); + } +}; + //////////////////////////////////////// // // Instrument class members @@ -326,6 +341,14 @@ void Instrument::pcspk(const byte *instrument) { _instrument = new Instrument_PcSpk(instrument); } +void Instrument::macSfx(byte prog) { + clear(); + if (prog > 127) + return; + _type = itMacSfx; + _instrument = new Instrument_MacSfx(prog); +} + void Instrument::saveOrLoad(Serializer *s) { if (s->isSaving()) { s->saveByte(_type); @@ -349,6 +372,9 @@ void Instrument::saveOrLoad(Serializer *s) { case itPcSpk: _instrument = new Instrument_PcSpk(s); break; + case itMacSfx: + _instrument = new Instrument_MacSfx(s); + break; default: warning("No known instrument classification #%d", (int)_type); _type = itNone; @@ -528,4 +554,38 @@ void Instrument_PcSpk::send(MidiChannel *mc) { mc->sysEx_customInstrument('SPK ', (byte *)&_instrument); } +//////////////////////////////////////// +// +// Instrument_MacSfx class members +// +//////////////////////////////////////// + +Instrument_MacSfx::Instrument_MacSfx(byte program) : + _program(program) { + if (program > 127) { + _program = 255; + } +} + +Instrument_MacSfx::Instrument_MacSfx(Serializer *s) { + _program = 255; + if (!s->isSaving()) { + saveOrLoad(s); + } +} + +void Instrument_MacSfx::saveOrLoad(Serializer *s) { + if (s->isSaving()) { + s->saveByte(_program); + } else { + _program = s->loadByte(); + } +} + +void Instrument_MacSfx::send(MidiChannel *mc) { + if (_program > 127) { + return; + } + mc->sysEx_customInstrument('MAC ', &_program); +} } // End of namespace Scumm diff --git a/engines/scumm/imuse/instrument.h b/engines/scumm/imuse/instrument.h index a855c64155..7e09e86fa5 100644 --- a/engines/scumm/imuse/instrument.h +++ b/engines/scumm/imuse/instrument.h @@ -52,7 +52,8 @@ public: itProgram = 1, itAdLib = 2, itRoland = 3, - itPcSpk = 4 + itPcSpk = 4, + itMacSfx = 5 }; Instrument() : _type(0), _instrument(0) { } @@ -72,6 +73,7 @@ public: void adlib(const byte *instrument); void roland(const byte *instrument); void pcspk(const byte *instrument); + void macSfx(byte program); byte getType() { return _type; } bool isValid() { return (_instrument ? _instrument->is_valid() : false); } diff --git a/engines/scumm/imuse/mac_m68k.cpp b/engines/scumm/imuse/mac_m68k.cpp new file mode 100644 index 0000000000..0980ef1fd2 --- /dev/null +++ b/engines/scumm/imuse/mac_m68k.cpp @@ -0,0 +1,514 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "scumm/imuse/mac_m68k.h" + +#include "common/util.h" +#include "common/macresman.h" +#include "common/stream.h" + +namespace Scumm { + +MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer) + : MidiDriver_Emulated(mixer) { +} + +MacM68kDriver::~MacM68kDriver() { +} + +int MacM68kDriver::open() { + if (_isOpen) { + return MERR_ALREADY_OPEN; + } + + const int error = MidiDriver_Emulated::open(); + if (error) { + return error; + } + + for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { + _channels[i].init(this, i); + } + + memset(_voiceChannels, 0, sizeof(_voiceChannels)); + _lastUsedVoiceChannel = 0; + + loadAllInstruments(); + + _pitchTable[116] = 1664510; + _pitchTable[117] = 1763487; + _pitchTable[118] = 1868350; + _pitchTable[119] = 1979447; + _pitchTable[120] = 2097152; + _pitchTable[121] = 2221855; + _pitchTable[122] = 2353973; + _pitchTable[123] = 2493948; + _pitchTable[124] = 2642246; + _pitchTable[125] = 2799362; + _pitchTable[126] = 2965820; + _pitchTable[127] = 3142177; + for (int i = 115; i >= 0; --i) { + _pitchTable[i] = _pitchTable[i + 12] / 2; + } + + _volumeTable = new byte[8192]; + for (int i = 0; i < 32; ++i) { + for (int j = 0; j < 256; ++j) { + _volumeTable[i * 256 + j] = ((-128 + j) * _volumeBaseTable[i]) / 127 - 128; + } + } + + _mixBuffer = 0; + _mixBufferLength = 0; + + // We set the output sound type to music here to allow sound volume + // adjustment. The drawback here is that we can not control the music and + // sfx separately here. But the AdLib output has the same issue so it + // should not be that bad. + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + + return 0; +} + +void MacM68kDriver::close() { + if (!_isOpen) { + return; + } + + _mixer->stopHandle(_mixerSoundHandle); + _isOpen = false; + for (InstrumentMap::iterator i = _instruments.begin(); i != _instruments.end(); ++i) { + delete[] i->_value.data; + } + _instruments.clear(); + delete[] _volumeTable; + _volumeTable = 0; + delete[] _mixBuffer; + _mixBuffer = 0; + _mixBufferLength = 0; +} + +void MacM68kDriver::send(uint32 d) { + assert(false); +} + +void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { + assert(false); +} + +MidiChannel *MacM68kDriver::allocateChannel() { + for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { + if (_channels[i].allocate()) { + return &_channels[i]; + } + } + + return 0; +} + +MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const { + InstrumentMap::const_iterator i = _instruments.find(idx); + if (i != _instruments.end()) { + return i->_value; + } else { + return _defaultInstrument; + } +} + +void MacM68kDriver::generateSamples(int16 *buf, int len) { + int silentChannels = 0; + + if (_mixBufferLength < len) { + delete[] _mixBuffer; + + _mixBufferLength = len; + _mixBuffer = new int[_mixBufferLength]; + assert(_mixBuffer); + } + memset(_mixBuffer, 0, sizeof(int) * _mixBufferLength); + + for (int i = 0; i < kChannelCount; ++i) { + OutputChannel &out = _voiceChannels[i].out; + if (out.isFinished) { + ++silentChannels; + continue; + } + + byte *volumeTable = &_volumeTable[(out.volume / 4) * 256]; + int *buffer = _mixBuffer; + + int samplesLeft = len; + while (samplesLeft) { + out.subPos += out.pitchModifier; + while (out.subPos >= 0x10000) { + out.subPos -= 0x10000; + out.instrument++; + } + + if (out.instrument >= out.end) { + if (!out.start) { + break; + } + + out.instrument = out.start; + out.subPos = 0; + } + + *buffer++ += volumeTable[*out.instrument]; + --samplesLeft; + } + + if (samplesLeft) { + out.isFinished = true; + while (samplesLeft--) { + *buffer++ += 0x80; + } + } + } + + const int *buffer = _mixBuffer; + const int silenceAdd = silentChannels << 7; + while (len--) { + *buf++ = (((*buffer++ + silenceAdd) >> 3) << 8) ^ 0x8000; + } +} + +void MacM68kDriver::loadAllInstruments() { + Common::MacResManager resource; + if (resource.open("iMUSE Setups")) { + if (!resource.hasResFork()) { + error("MacM68kDriver::loadAllInstruments: \"iMUSE Setups\" loaded, but no resource fork present"); + } + + for (int i = 0x3E7; i < 0x468; ++i) { + Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i); + if (stream) { + addInstrument(i, stream); + delete stream; + } + } + + for (int i = 0x7D0; i < 0x8D0; ++i) { + Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i); + if (stream) { + addInstrument(i, stream); + delete stream; + } + } + + InstrumentMap::iterator inst = _instruments.find(kDefaultInstrument); + if (inst != _instruments.end()) { + _defaultInstrument = inst->_value; + } else { + error("MacM68kDriver::loadAllInstruments: Could not load default instrument"); + } + } else { + error("MacM68kDriver::loadAllInstruments: Could not load \"iMUSE Setups\""); + } +} + +void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) { + // We parse the "SND" files manually here, since we need special data + // from their header and need to work on them raw while mixing. + data->skip(2); + int count = data->readUint16BE(); + data->skip(2 * (3 * count)); + count = data->readUint16BE(); + data->skip(2 * (4 * count)); + + Instrument inst; + // Skip (optional) pointer to data + data->skip(4); + inst.length = data->readUint32BE(); + inst.sampleRate = data->readUint32BE(); + inst.loopStart = data->readUint32BE(); + inst.loopEnd = data->readUint32BE(); + // Skip encoding + data->skip(1); + inst.baseFrequency = data->readByte(); + + inst.data = new byte[inst.length]; + assert(inst.data); + data->read(inst.data, inst.length); + _instruments[idx] = inst; +} + +void MacM68kDriver::setPitch(OutputChannel *out, int frequency) { + out->frequency = frequency; + out->isFinished = false; + + const int pitchIdx = (frequency >> 7) + 60 - out->baseFrequency; + assert(pitchIdx >= 0); + + const int low7Bits = frequency & 0x7F; + if (low7Bits) { + out->pitchModifier = _pitchTable[pitchIdx] + (((_pitchTable[pitchIdx + 1] - _pitchTable[pitchIdx]) * low7Bits) >> 7); + } else { + out->pitchModifier = _pitchTable[pitchIdx]; + } +} + +void MacM68kDriver::VoiceChannel::off() { + if (out.start) { + out.isFinished = true; + } + + part->removeVoice(this); + part = 0; +} + +void MacM68kDriver::MidiChannel_MacM68k::release() { + _allocated = false; + while (_voice) { + _voice->off(); + } +} + +void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) { + uint8 type = b & 0xF0; + uint8 p1 = (b >> 8) & 0xFF; + uint8 p2 = (b >> 16) & 0xFF; + + switch (type) { + case 0x80: + noteOff(p1); + break; + + case 0x90: + if (p2) { + noteOn(p1, p2); + } else { + noteOff(p1); + } + break; + + case 0xB0: + controlChange(p1, p2); + break; + + case 0xE0: + pitchBend((p1 | (p2 << 7)) - 0x2000); + break; + + default: + break; + } +} + +void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) { + for (VoiceChannel *i = _voice; i; i = i->next) { + if (i->note == note) { + if (_sustain) { + i->sustainNoteOff = true; + } else { + i->off(); + } + } + } +} + +void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) { + // Do not start a not unless there is an instrument set up + if (!_instrument.data) { + return; + } + + // Allocate a voice channel + VoiceChannel *voice = _owner->allocateVoice(_priority); + if (!voice) { + return; + } + addVoice(voice); + + voice->note = note; + // This completly ignores the note's volume, but is in accordance + // to the original. + voice->out.volume = _volume; + + // Set up the instrument data + voice->out.baseFrequency = _instrument.baseFrequency; + voice->out.soundStart = _instrument.data; + voice->out.soundEnd = _instrument.data + _instrument.length; + if (_instrument.loopEnd && _instrument.loopEnd - 12 > _instrument.loopStart) { + voice->out.loopStart = _instrument.data + _instrument.loopStart; + voice->out.loopEnd = _instrument.data + _instrument.loopEnd; + } else { + voice->out.loopStart = 0; + voice->out.loopEnd = voice->out.soundEnd; + } + + voice->out.start = voice->out.loopStart; + voice->out.end = voice->out.loopEnd; + + // Set up the pitch + _owner->setPitch(&voice->out, (note << 7) + _pitchBend); + + // Set up the sample position + voice->out.instrument = voice->out.soundStart; + voice->out.subPos = 0; +} + +void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) { + _instrument = _owner->getInstrument(program + kProgramChangeBase); +} + +void MacM68kDriver::MidiChannel_MacM68k::pitchBend(int16 bend) { + _pitchBend = (bend * _pitchBendFactor) >> 6; + for (VoiceChannel *i = _voice; i; i = i->next) { + _owner->setPitch(&i->out, (i->note << 7) + _pitchBend); + } +} + +void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value) { + switch (control) { + // volume change + case 7: + _volume = value; + for (VoiceChannel *i = _voice; i; i = i->next) { + i->out.volume = value; + i->out.isFinished = false; + } + break; + + // sustain + case 64: + _sustain = value; + if (!_sustain) { + for (VoiceChannel *i = _voice; i; i = i->next) { + if (i->sustainNoteOff) { + i->off(); + } + } + } + break; + + // all notes off + case 123: + for (VoiceChannel *i = _voice; i; i = i->next) { + i->off(); + } + break; + + default: + break; + } +} + +void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) { + _pitchBendFactor = value; +} + +void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) { + _priority = value; +} + +void MacM68kDriver::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) { + assert(instr); + if (type == 'MAC ') { + _instrument = _owner->getInstrument(*instr + kSysExBase); + } +} + +void MacM68kDriver::MidiChannel_MacM68k::init(MacM68kDriver *owner, byte channel) { + _owner = owner; + _number = channel; + _allocated = false; +} + +bool MacM68kDriver::MidiChannel_MacM68k::allocate() { + if (_allocated) { + return false; + } + + _allocated = true; + _voice = 0; + _priority = 0; + memset(&_instrument, 0, sizeof(_instrument)); + _pitchBend = 0; + _pitchBendFactor = 0; + _volume = 0; + return true; +} + +void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) { + voice->next = _voice; + voice->prev = 0; + voice->part = this; + if (_voice) { + _voice->prev = voice; + } + _voice = voice; +} + +void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) { + VoiceChannel *i = _voice; + while (i && i != voice) { + i = i->next; + } + + if (i) { + if (i->next) { + i->next->prev = i->prev; + } + + if (i->prev) { + i->prev->next = i->next; + } else { + _voice = i->next; + } + } +} + +MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) { + VoiceChannel *channel = 0; + for (int i = 0; i < kChannelCount; ++i) { + if (++_lastUsedVoiceChannel == kChannelCount) { + _lastUsedVoiceChannel = 0; + } + + VoiceChannel *cur = &_voiceChannels[_lastUsedVoiceChannel]; + if (!cur->part) { + memset(cur, 0, sizeof(*cur)); + return cur; + } else if (!cur->next) { + if (cur->part->_priority <= priority) { + priority = cur->part->_priority; + channel = cur; + } + } + } + + if (channel) { + channel->off(); + memset(channel, 0, sizeof(*channel)); + } + + return channel; +} + +const int MacM68kDriver::_volumeBaseTable[32] = { + 0, 0, 1, 1, 2, 3, 5, 6, + 8, 11, 13, 16, 19, 22, 26, 30, + 34, 38, 43, 48, 53, 58, 64, 70, + 76, 83, 89, 96, 104, 111, 119, 127 +}; + +} // End of namespace Scumm diff --git a/engines/scumm/imuse/mac_m68k.h b/engines/scumm/imuse/mac_m68k.h new file mode 100644 index 0000000000..59e2f68b9b --- /dev/null +++ b/engines/scumm/imuse/mac_m68k.h @@ -0,0 +1,177 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SCUMM_IMUSE_MAC_M68K_H +#define SCUMM_IMUSE_MAC_M68K_H + +#include "audio/softsynth/emumidi.h" + +#include "common/hashmap.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Scumm { + +class MacM68kDriver : public MidiDriver_Emulated { + friend class MidiChannel_MacM68k; +public: + MacM68kDriver(Audio::Mixer *mixer); + ~MacM68kDriver(); + + virtual int open(); + virtual void close(); + + virtual void send(uint32 d); + virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); + + virtual MidiChannel *allocateChannel(); + virtual MidiChannel *getPercussionChannel() { return 0; } + + virtual bool isStereo() const { return false; } + virtual int getRate() const { + // The original is using a frequency of approx. 22254.54546 here. + // To be precise it uses the 16.16 fixed point value 0x56EE8BA3. + return 22254; + } + +protected: + virtual void generateSamples(int16 *buf, int len); + virtual void onTimer() {} + +private: + int *_mixBuffer; + int _mixBufferLength; + + struct Instrument { + uint length; + uint sampleRate; + uint loopStart; + uint loopEnd; + int baseFrequency; + + byte *data; + }; + + enum { + kDefaultInstrument = 0x3E7, + kProgramChangeBase = 0x3E8, + kSysExBase = 0x7D0 + }; + + Instrument getInstrument(int idx) const; + typedef Common::HashMap<int, Instrument> InstrumentMap; + InstrumentMap _instruments; + Instrument _defaultInstrument; + void loadAllInstruments(); + void addInstrument(int idx, Common::SeekableReadStream *data); + + struct OutputChannel { + int pitchModifier; + + const byte *instrument; + uint subPos; + + const byte *start; + const byte *end; + + const byte *soundStart; + const byte *soundEnd; + const byte *loopStart; + const byte *loopEnd; + + int frequency; + int volume; + + bool isFinished; + + int baseFrequency; + }; + + void setPitch(OutputChannel *out, int frequency); + int _pitchTable[128]; + + byte *_volumeTable; + static const int _volumeBaseTable[32]; + + class MidiChannel_MacM68k; + + struct VoiceChannel { + MidiChannel_MacM68k *part; + VoiceChannel *prev, *next; + int channel; + int note; + bool sustainNoteOff; + OutputChannel out; + + void off(); + }; + + class MidiChannel_MacM68k : public MidiChannel { + friend class MacM68kDriver; + public: + virtual MidiDriver *device() { return _owner; } + virtual byte getNumber() { return _number; } + virtual void release(); + + virtual void send(uint32 b); + virtual void noteOff(byte note); + virtual void noteOn(byte note, byte velocity); + virtual void programChange(byte program); + virtual void pitchBend(int16 bend); + virtual void controlChange(byte control, byte value); + virtual void pitchBendFactor(byte value); + virtual void priority(byte value); + virtual void sysEx_customInstrument(uint32 type, const byte *instr); + + void init(MacM68kDriver *owner, byte channel); + bool allocate(); + + void addVoice(VoiceChannel *voice); + void removeVoice(VoiceChannel *voice); + private: + MacM68kDriver *_owner; + bool _allocated; + int _number; + + VoiceChannel *_voice; + int _priority; + int _sustain; + Instrument _instrument; + int _pitchBend; + int _pitchBendFactor; + int _volume; + }; + + MidiChannel_MacM68k _channels[32]; + + enum { + kChannelCount = 8 + }; + VoiceChannel _voiceChannels[kChannelCount]; + int _lastUsedVoiceChannel; + VoiceChannel *allocateVoice(int priority); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp index 85ffc86f47..8f230ebac3 100644 --- a/engines/scumm/imuse/sysex_scumm.cpp +++ b/engines/scumm/imuse/sysex_scumm.cpp @@ -71,7 +71,7 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) { part->set_pri(buf[2]); part->volume(buf[3]); part->set_pan(buf[4]); - part->_percussion = player->_isMIDI ? ((buf[5] & 0x80) > 0) : false; + part->_percussion = player->_supportsPercussion ? ((buf[5] & 0x80) > 0) : false; part->set_transpose(buf[5]); part->set_detune(buf[6]); part->pitchBendFactor(buf[7]); diff --git a/engines/scumm/midiparser_ro.cpp b/engines/scumm/midiparser_ro.cpp index 1a31d1ca82..8549a9262d 100644 --- a/engines/scumm/midiparser_ro.cpp +++ b/engines/scumm/midiparser_ro.cpp @@ -62,13 +62,13 @@ void MidiParser_RO::parseNextEvent (EventInfo &info) { info.delta = 0; do { - info.start = _position._play_pos; - info.event = *(_position._play_pos++); + info.start = _position._playPos; + info.event = *(_position._playPos++); if (info.command() == 0xA) { ++_lastMarkerCount; info.event = 0xF0; } else if (info.event == 0xF0 || info.event == 0xF1) { - byte delay = *(_position._play_pos++); + byte delay = *(_position._playPos++); info.delta += delay; if (info.event == 0xF1) { // This event is, as far as we have been able @@ -95,16 +95,16 @@ void MidiParser_RO::parseNextEvent (EventInfo &info) { if (info.event < 0x80) return; - _position._running_status = info.event; + _position._runningStatus = info.event; switch (info.command()) { case 0xC: - info.basic.param1 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); info.basic.param2 = 0; break; case 0x8: case 0x9: case 0xB: - info.basic.param1 = *(_position._play_pos++); - info.basic.param2 = *(_position._play_pos++); + info.basic.param1 = *(_position._playPos++); + info.basic.param2 = *(_position._playPos++); if (info.command() == 0x9 && info.basic.param2 == 0) info.event = info.channel() | 0x80; info.length = 0; @@ -133,7 +133,7 @@ bool MidiParser_RO::loadMusic (byte *data, uint32 size) { return false; } - _num_tracks = 1; + _numTracks = 1; _ppqn = 120; _tracks[0] = pos + 2; _markerCount = _lastMarkerCount = 0; diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 1f219f5187..8499c9bad3 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -27,6 +27,7 @@ MODULE_OBJS := \ imuse/imuse_part.o \ imuse/imuse_player.o \ imuse/instrument.o \ + imuse/mac_m68k.o \ imuse/pcspk.o \ imuse/sysex_samnmax.o \ imuse/sysex_scumm.o \ diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp index 399cd91324..77c75c4ad6 100644 --- a/engines/scumm/object.cpp +++ b/engines/scumm/object.cpp @@ -335,7 +335,7 @@ int ScummEngine::whereIsObject(int object) const { return WIO_NOT_FOUND; if ((_game.version != 0 || OBJECT_V0_TYPE(object) == 0) && - _objectOwnerTable[object] != OF_OWNER_ROOM) + _objectOwnerTable[object] != OF_OWNER_ROOM) { for (i = 0; i < _numInventory; i++) if (_inventory[i] == object) @@ -1225,7 +1225,7 @@ byte *ScummEngine::getOBCDFromObject(int obj, bool v0CheckInventory) { byte *ptr; if ((_game.version != 0 || OBJECT_V0_TYPE(obj) == 0) && - _objectOwnerTable[obj] != OF_OWNER_ROOM) + _objectOwnerTable[obj] != OF_OWNER_ROOM) { if (_game.version == 0 && !v0CheckInventory) return 0; diff --git a/engines/scumm/player_apple2.cpp b/engines/scumm/player_apple2.cpp index a8e150caa9..58e4f78a94 100644 --- a/engines/scumm/player_apple2.cpp +++ b/engines/scumm/player_apple2.cpp @@ -61,8 +61,8 @@ public: private: void _update(int interval /*a*/, int count /*y*/) { // D076 - assert(interval > 0); // 0 == 256? - assert(count > 0); // 0 == 256? + assert(interval > 0); // 0 == 256? + assert(count > 0); // 0 == 256? for (; count >= 0; --count) { _player->speakerToggle(); @@ -99,7 +99,7 @@ public: ++_pos; return false; - } + } return true; } @@ -112,7 +112,7 @@ private: assert(interval > 0); // 0 == 256? int a = (interval >> 3) + count; - for (int y = a; y > 0; --y) { + for (int y = a; y > 0; --y) { _player->generateSamples(1292 - 5*interval); _player->speakerToggle(); @@ -206,7 +206,7 @@ private: _bitmask1 = 0x3; _bitmask2 = 0x3; - + _updateInterval2 = param0; if (_updateInterval2 == 0) _bitmask2 = 0x0; @@ -234,9 +234,9 @@ private: if (_updateRemain2 == 0) { _updateRemain2 = _updateInterval2; - // use only first voice's data (bitmask1) if both voices are triggered + // use only first voice's data (bitmask1) if both voices are triggered if (_updateRemain1 != 0) { - _speakerShiftReg ^= _bitmask2; + _speakerShiftReg ^= _bitmask2; } } @@ -256,7 +256,7 @@ private: protected: const byte *_params; - + byte _updateRemain1; byte _updateRemain2; @@ -309,7 +309,7 @@ private: for (int i = count; i > 0; --i) { _player->generateSamples(10 + 5*interval); _player->speakerToggle(); - + _player->generateSamples(5 + 5*interval); _player->speakerToggle(); } @@ -332,20 +332,20 @@ private: // LD000[loc] ^ LD00A[loc] const byte AppleII_SoundFunction5_Noise::_noiseTable[256] = { - 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63, - 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c, - 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc, - 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64, + 0x65, 0x1b, 0xda, 0x11, 0x61, 0xe5, 0x77, 0x57, 0x92, 0xc8, 0x51, 0x1c, 0xd4, 0x91, 0x62, 0x63, + 0x00, 0x38, 0x57, 0xd5, 0x18, 0xd8, 0xdc, 0x40, 0x03, 0x86, 0xd3, 0x2f, 0x10, 0x11, 0xd8, 0x3c, + 0xbe, 0x00, 0x19, 0xc5, 0xd2, 0xc3, 0xca, 0x34, 0x00, 0x28, 0xbf, 0xb9, 0x18, 0x20, 0x01, 0xcc, + 0xda, 0x08, 0xbc, 0x75, 0x7c, 0xb0, 0x8d, 0xe0, 0x09, 0x18, 0xbf, 0x5d, 0xe9, 0x8c, 0x75, 0x64, 0xe5, 0xb5, 0x5d, 0xe0, 0xb7, 0x7d, 0xe9, 0x8c, 0x55, 0x65, 0xc5, 0xb5, 0x5d, 0xd8, 0x09, 0x0d, - 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3, - 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a, - 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d, - 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c, + 0x64, 0xf0, 0xf0, 0x08, 0x63, 0x03, 0x00, 0x55, 0x35, 0xc0, 0x00, 0x20, 0x74, 0xa5, 0x1e, 0xe3, + 0x00, 0x06, 0x3c, 0x52, 0xd1, 0x70, 0xd0, 0x57, 0x02, 0xf0, 0x00, 0xb6, 0xfc, 0x02, 0x11, 0x9a, + 0x3b, 0xc8, 0x38, 0xdf, 0x1a, 0xb0, 0xd1, 0xb8, 0xd0, 0x18, 0x8a, 0x4a, 0xea, 0x1b, 0x12, 0x5d, + 0x29, 0x58, 0xd8, 0x43, 0xb8, 0x2d, 0xd2, 0x61, 0x10, 0x3c, 0x0c, 0x5d, 0x1b, 0x61, 0x10, 0x3c, 0x0a, 0x5d, 0x1d, 0x61, 0x10, 0x3c, 0x0b, 0x19, 0x88, 0x21, 0xc0, 0x21, 0x07, 0x00, 0x65, 0x62, - 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06, - 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d, - 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48, - 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37, + 0x08, 0xe9, 0x36, 0x40, 0x20, 0x41, 0x06, 0x00, 0x20, 0x00, 0x00, 0xed, 0xa3, 0x00, 0x88, 0x06, + 0x98, 0x01, 0x5d, 0x7f, 0x02, 0x1d, 0x78, 0x03, 0x60, 0xcb, 0x3a, 0x01, 0xbd, 0x78, 0x02, 0x5d, + 0x7e, 0x03, 0x1d, 0xf5, 0xa6, 0x40, 0x81, 0xb4, 0xd0, 0x8d, 0xd3, 0xd0, 0x6d, 0xd5, 0x61, 0x48, + 0x61, 0x4d, 0xd1, 0xc8, 0xb1, 0xd8, 0x69, 0xff, 0x61, 0xd9, 0xed, 0xa0, 0xfe, 0x19, 0x91, 0x37, 0x19, 0x37, 0x00, 0xf1, 0x00, 0x01, 0x1f, 0x00, 0xad, 0xc1, 0x01, 0x01, 0x2e, 0x00, 0x40, 0xc6, 0x7a, 0x9b, 0x95, 0x43, 0xfc, 0x18, 0xd2, 0x9e, 0x2a, 0x5a, 0x4b, 0x2a, 0xb6, 0x87, 0x30, 0x6c }; @@ -394,20 +394,20 @@ void Player_AppleII::startSound(int nr) { case 0: // empty (nothing to play) resetState(); return; - case 1: - _soundFunc = new AppleII_SoundFunction1_FreqUpDown(); + case 1: + _soundFunc = new AppleII_SoundFunction1_FreqUpDown(); break; - case 2: - _soundFunc = new AppleII_SoundFunction2_SymmetricWave(); + case 2: + _soundFunc = new AppleII_SoundFunction2_SymmetricWave(); break; - case 3: - _soundFunc = new AppleII_SoundFunction3_AsymmetricWave(); + case 3: + _soundFunc = new AppleII_SoundFunction3_AsymmetricWave(); break; - case 4: - _soundFunc = new AppleII_SoundFunction4_Polyphone(); + case 4: + _soundFunc = new AppleII_SoundFunction4_Polyphone(); break; - case 5: - _soundFunc = new AppleII_SoundFunction5_Noise(); + case 5: + _soundFunc = new AppleII_SoundFunction5_Noise(); break; } _soundFunc->init(this, _params); @@ -484,7 +484,7 @@ int Player_AppleII::readBuffer(int16 *buffer, const int numSamples) { // toggle speaker on/off void Player_AppleII::speakerToggle() { - _speakerState ^= 0x1; + _speakerState ^= 0x1; } void Player_AppleII::generateSamples(int cycles) { @@ -492,8 +492,8 @@ void Player_AppleII::generateSamples(int cycles) { } void Player_AppleII::wait(int interval, int count /*y*/) { - assert(count > 0); // 0 == 256? - assert(interval > 0); // 0 == 256? + assert(count > 0); // 0 == 256? + assert(interval > 0); // 0 == 256? generateSamples(11 + count*(8 + 5 * interval)); } diff --git a/engines/scumm/player_apple2.h b/engines/scumm/player_apple2.h index b4a7d409fb..e1ec9d8946 100644 --- a/engines/scumm/player_apple2.h +++ b/engines/scumm/player_apple2.h @@ -36,7 +36,7 @@ namespace Scumm { class ScummEngine; /* - * Optimized for use with periodical read/write phases when the buffer + * Optimized for use with periodical read/write phases when the buffer * is filled in a write phase and completely read in a read phase. * The growing strategy is optimized for repeated small (e.g. 2 bytes) * single writes resulting in large buffers @@ -133,7 +133,7 @@ static const double APPLEII_CPU_CLOCK = 1020484.5; // ~ 1.02 MHz /* * Converts the 1-bit speaker state values into audio samples. - * This is done by aggregation of the speaker states at each + * This is done by aggregation of the speaker states at each * CPU cycle in a sampling period into an audio sample. */ class SampleConverter { @@ -144,7 +144,7 @@ private: } public: - SampleConverter() : + SampleConverter() : _cyclesPerSampleFP(0), _missingCyclesFP(0), _sampleCyclesSumFP(0), @@ -156,7 +156,7 @@ public: void reset() { _missingCyclesFP = 0; _sampleCyclesSumFP = 0; - _buffer.clear(); + _buffer.clear(); } uint32 availableSize() const { @@ -245,7 +245,7 @@ public: virtual void setMusicVolume(int vol) { _sampleConverter.setMusicVolume(vol); } void setSampleRate(int rate) { _sampleRate = rate; - _sampleConverter.setSampleRate(rate); + _sampleConverter.setSampleRate(rate); } virtual void startSound(int sound); virtual void stopSound(int sound); diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp index 2588026e59..33e3e40e39 100644 --- a/engines/scumm/player_towns.cpp +++ b/engines/scumm/player_towns.cpp @@ -87,7 +87,7 @@ void Player_Towns::restoreAfterLoad() { if (!_v2) restoredSounds.push_back(_pcmCurrentSound[i].index); - + uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index); if (!ptr) continue; diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp index d4b21774ed..c1242e0645 100644 --- a/engines/scumm/player_v2cms.cpp +++ b/engines/scumm/player_v2cms.cpp @@ -718,38 +718,38 @@ void Player_V2CMS::playMusicChips(const MusicChip *table) { } const Player_V2CMS::MidiNote Player_V2CMS::_midiNotes[132] = { - { 3, 0 }, { 31, 0 }, { 58, 0 }, { 83, 0 }, - { 107, 0 }, { 130, 0 }, { 151, 0 }, { 172, 0 }, - { 191, 0 }, { 209, 0 }, { 226, 0 }, { 242, 0 }, - { 3, 1 }, { 31, 1 }, { 58, 1 }, { 83, 1 }, - { 107, 1 }, { 130, 1 }, { 151, 1 }, { 172, 1 }, - { 191, 1 }, { 209, 1 }, { 226, 1 }, { 242, 1 }, - { 3, 2 }, { 31, 2 }, { 58, 2 }, { 83, 2 }, - { 107, 2 }, { 130, 2 }, { 151, 2 }, { 172, 2 }, - { 191, 2 }, { 209, 2 }, { 226, 2 }, { 242, 2 }, - { 3, 3 }, { 31, 3 }, { 58, 3 }, { 83, 3 }, - { 107, 3 }, { 130, 3 }, { 151, 3 }, { 172, 3 }, - { 191, 3 }, { 209, 3 }, { 226, 3 }, { 242, 3 }, - { 3, 4 }, { 31, 4 }, { 58, 4 }, { 83, 4 }, - { 107, 4 }, { 130, 4 }, { 151, 4 }, { 172, 4 }, - { 191, 4 }, { 209, 4 }, { 226, 4 }, { 242, 4 }, - { 3, 5 }, { 31, 5 }, { 58, 5 }, { 83, 5 }, - { 107, 5 }, { 130, 5 }, { 151, 5 }, { 172, 5 }, - { 191, 5 }, { 209, 5 }, { 226, 5 }, { 242, 5 }, - { 3, 6 }, { 31, 6 }, { 58, 6 }, { 83, 6 }, - { 107, 6 }, { 130, 6 }, { 151, 6 }, { 172, 6 }, - { 191, 6 }, { 209, 6 }, { 226, 6 }, { 242, 6 }, - { 3, 7 }, { 31, 7 }, { 58, 7 }, { 83, 7 }, - { 107, 7 }, { 130, 7 }, { 151, 7 }, { 172, 7 }, - { 191, 7 }, { 209, 7 }, { 226, 7 }, { 242, 7 }, - { 3, 8 }, { 31, 8 }, { 58, 8 }, { 83, 8 }, - { 107, 8 }, { 130, 8 }, { 151, 8 }, { 172, 8 }, - { 191, 8 }, { 209, 8 }, { 226, 8 }, { 242, 8 }, - { 3, 9 }, { 31, 9 }, { 58, 9 }, { 83, 9 }, - { 107, 9 }, { 130, 9 }, { 151, 9 }, { 172, 9 }, - { 191, 9 }, { 209, 9 }, { 226, 9 }, { 242, 9 }, - { 3, 10 }, { 31, 10 }, { 58, 10 }, { 83, 10 }, - { 107, 10 }, { 130, 10 }, { 151, 10 }, { 172, 10 }, + { 3, 0 }, { 31, 0 }, { 58, 0 }, { 83, 0 }, + { 107, 0 }, { 130, 0 }, { 151, 0 }, { 172, 0 }, + { 191, 0 }, { 209, 0 }, { 226, 0 }, { 242, 0 }, + { 3, 1 }, { 31, 1 }, { 58, 1 }, { 83, 1 }, + { 107, 1 }, { 130, 1 }, { 151, 1 }, { 172, 1 }, + { 191, 1 }, { 209, 1 }, { 226, 1 }, { 242, 1 }, + { 3, 2 }, { 31, 2 }, { 58, 2 }, { 83, 2 }, + { 107, 2 }, { 130, 2 }, { 151, 2 }, { 172, 2 }, + { 191, 2 }, { 209, 2 }, { 226, 2 }, { 242, 2 }, + { 3, 3 }, { 31, 3 }, { 58, 3 }, { 83, 3 }, + { 107, 3 }, { 130, 3 }, { 151, 3 }, { 172, 3 }, + { 191, 3 }, { 209, 3 }, { 226, 3 }, { 242, 3 }, + { 3, 4 }, { 31, 4 }, { 58, 4 }, { 83, 4 }, + { 107, 4 }, { 130, 4 }, { 151, 4 }, { 172, 4 }, + { 191, 4 }, { 209, 4 }, { 226, 4 }, { 242, 4 }, + { 3, 5 }, { 31, 5 }, { 58, 5 }, { 83, 5 }, + { 107, 5 }, { 130, 5 }, { 151, 5 }, { 172, 5 }, + { 191, 5 }, { 209, 5 }, { 226, 5 }, { 242, 5 }, + { 3, 6 }, { 31, 6 }, { 58, 6 }, { 83, 6 }, + { 107, 6 }, { 130, 6 }, { 151, 6 }, { 172, 6 }, + { 191, 6 }, { 209, 6 }, { 226, 6 }, { 242, 6 }, + { 3, 7 }, { 31, 7 }, { 58, 7 }, { 83, 7 }, + { 107, 7 }, { 130, 7 }, { 151, 7 }, { 172, 7 }, + { 191, 7 }, { 209, 7 }, { 226, 7 }, { 242, 7 }, + { 3, 8 }, { 31, 8 }, { 58, 8 }, { 83, 8 }, + { 107, 8 }, { 130, 8 }, { 151, 8 }, { 172, 8 }, + { 191, 8 }, { 209, 8 }, { 226, 8 }, { 242, 8 }, + { 3, 9 }, { 31, 9 }, { 58, 9 }, { 83, 9 }, + { 107, 9 }, { 130, 9 }, { 151, 9 }, { 172, 9 }, + { 191, 9 }, { 209, 9 }, { 226, 9 }, { 242, 9 }, + { 3, 10 }, { 31, 10 }, { 58, 10 }, { 83, 10 }, + { 107, 10 }, { 130, 10 }, { 151, 10 }, { 172, 10 }, { 191, 10 }, { 209, 10 }, { 226, 10 }, { 242, 10 } }; diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index beac077fd1..72896e097a 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -1298,7 +1298,7 @@ void ScummEngine::saveOrLoad(Serializer *s) { s->saveLoadArrayOf(_16BitPalette, 512, sizeof(_16BitPalette[0]), sleUint16); } - + // FM-Towns specific (extra palette data, color cycle data, etc.) // In earlier save game versions (below 87) the FM-Towns specific data would get saved (and loaded) even in non FM-Towns games. // This would cause an unnecessary save file incompatibility between DS (which uses the DISABLE_TOWNS_DUAL_LAYER_MODE setting) diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index d5f7ea526e..a640bc1e17 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -47,7 +47,7 @@ namespace Scumm { * only saves/loads those which are valid for the version of the savegame * which is being loaded/saved currently. */ -#define CURRENT_VER 92 +#define CURRENT_VER 93 /** * An auxillary macro, used to specify savegame versions. We use this instead diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp index 44b77f1d18..361287d29f 100644 --- a/engines/scumm/script_v0.cpp +++ b/engines/scumm/script_v0.cpp @@ -602,7 +602,7 @@ void ScummEngine_v0::o_loadRoomWithEgo() { x = r.x; y = r.y; a->putActor(x, y, _currentRoom); - + camera._dest.x = camera._cur.x = a->getPos().x; setCameraAt(a->getPos().x, a->getPos().y); setCameraFollows(a); @@ -635,18 +635,18 @@ void ScummEngine_v0::setMode(byte mode) { case kModeCutscene: _redrawSentenceLine = false; // Note: do not change freeze state here - state = USERSTATE_SET_IFACE | + state = USERSTATE_SET_IFACE | USERSTATE_SET_CURSOR; break; case kModeKeypad: _redrawSentenceLine = false; - state = USERSTATE_SET_IFACE | + state = USERSTATE_SET_IFACE | USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON | USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON; break; case kModeNormal: case kModeNoNewKid: - state = USERSTATE_SET_IFACE | USERSTATE_IFACE_ALL | + state = USERSTATE_SET_IFACE | USERSTATE_IFACE_ALL | USERSTATE_SET_CURSOR | USERSTATE_CURSOR_ON | USERSTATE_SET_FREEZE; break; @@ -688,7 +688,7 @@ void ScummEngine_v0::o_animateActor() { Actor_v0 *a = (Actor_v0*) derefActor(act, "o_animateActor"); a->_animFrameRepeat = repeat; - + switch (anim) { case 0xFE: @@ -700,7 +700,7 @@ void ScummEngine_v0::o_animateActor() { // 0x69A3 a->_speaking = 0x00; return; - + case 0xFF: a->stopActorMoving(); return; diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp index ce162b4a6a..96d422d5bb 100644 --- a/engines/scumm/script_v2.cpp +++ b/engines/scumm/script_v2.cpp @@ -993,7 +993,7 @@ void ScummEngine_v2::o2_drawSentence() { const byte *temp; int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0); - if (!((_userState & USERSTATE_IFACE_SENTENCE) || + if (!((_userState & USERSTATE_IFACE_SENTENCE) || (_game.platform == Common::kPlatformNES && (_userState & USERSTATE_IFACE_ALL)))) return; @@ -1486,8 +1486,8 @@ void ScummEngine_v2::o2_cutscene() { VAR(VAR_CURSORSTATE) = 200; // Hide inventory, freeze scripts, hide cursor - setUserState(USERSTATE_SET_IFACE | - USERSTATE_SET_CURSOR | + setUserState(USERSTATE_SET_IFACE | + USERSTATE_SET_CURSOR | USERSTATE_SET_FREEZE | USERSTATE_FREEZE_ON); _sentenceNum = 0; diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp index a5591b701f..0bf51a2816 100644 --- a/engines/scumm/script_v5.cpp +++ b/engines/scumm/script_v5.cpp @@ -1097,7 +1097,7 @@ void ScummEngine_v5::o5_getDist() { int r; getResultPos(); - + o1 = getVarOrDirectWord(PARAM_1); o2 = getVarOrDirectWord(PARAM_2); diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index d0f46f3e56..2c79fb8de0 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -73,6 +73,7 @@ #include "scumm/util.h" #include "scumm/verbs.h" #include "scumm/imuse/pcspk.h" +#include "scumm/imuse/mac_m68k.h" #include "backends/audiocd/audiocd.h" @@ -1835,17 +1836,31 @@ void ScummEngine::setupMusic(int midi) { } else if (_game.version >= 3 && _game.heversion <= 62) { MidiDriver *nativeMidiDriver = 0; MidiDriver *adlibMidiDriver = 0; - - if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) + bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB); + bool useOnlyNative = false; + + if (isMacM68kIMuse()) { + // We setup this driver as native MIDI driver to avoid playback + // of the Mac music via a selected MIDI device. + nativeMidiDriver = new MacM68kDriver(_mixer); + // The Mac driver is never MT-32. + _native_mt32 = false; + // Ignore non-native drivers. This also ignores the multi MIDI setting. + useOnlyNative = true; + } else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) { nativeMidiDriver = MidiDriver::createMidi(dev); + } + if (nativeMidiDriver != NULL && _native_mt32) nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); - bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB); - if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) { - adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB)); - adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0); - } else if (_sound->_musicType == MDT_PCSPK) { - adlibMidiDriver = new PcSpkDriver(_mixer); + + if (!useOnlyNative) { + if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) { + adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB)); + adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0); + } else if (_sound->_musicType == MDT_PCSPK) { + adlibMidiDriver = new PcSpkDriver(_mixer); + } } _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver); @@ -1971,11 +1986,11 @@ Common::Error ScummEngine::go() { if (delta < 1) // Ensure we don't get into an endless loop delta = 1; // by not decreasing sleepers. - // WORKAROUND: walking speed in the original v0/v1 interpreter + // WORKAROUND: walking speed in the original v0/v1 interpreter // is sometimes slower (e.g. during scrolling) than in ScummVM. // This is important for the door-closing action in the dungeon, - // otherwise (delta < 6) a single kid is able to escape. - if ((_game.version == 0 && isScriptRunning(132)) || + // otherwise (delta < 6) a single kid is able to escape. + if ((_game.version == 0 && isScriptRunning(132)) || (_game.version == 1 && isScriptRunning(137))) delta = 6; diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index c8cf096a19..a77c1c0141 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -150,7 +150,13 @@ enum GameFeatures { GF_HE_985 = 1 << 14, /** HE games with 16 bit color */ - GF_16BIT_COLOR = 1 << 15 + GF_16BIT_COLOR = 1 << 15, + + /** + * SCUMM v5-v7 Mac games stored in a container file + * Used to differentiate between m68k and PPC versions of Indy4 + */ + GF_MAC_CONTAINER = 1 << 16 }; /* SCUMM Debug Channels */ @@ -713,6 +719,9 @@ public: bool openFile(BaseScummFile &file, const Common::String &filename, bool resourceFile = false); + /** Is this game a Mac m68k v5 game with iMuse? */ + bool isMacM68kIMuse() const; + protected: int _resourceHeaderSize; byte _resourceMapper[128]; @@ -1363,7 +1372,7 @@ public: public: bool towns_isRectInStringBox(int x1, int y1, int x2, int y2); byte _townsPaletteFlags; - byte _townsCharsetColorMap[16]; + byte _townsCharsetColorMap[16]; protected: void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h); diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index 1dc026ad52..a1cecfa0b3 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -248,7 +248,10 @@ void Sound::playSound(int soundID) { _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID); } // Support for sampled sound effects in Monkey Island 1 and 2 - else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) { + else if (_vm->_game.platform != Common::kPlatformFMTowns + // The Macintosh m68k versions of MI2/Indy4 just ignore SBL effects. + && !_vm->isMacM68kIMuse() + && READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) { debugC(DEBUG_SOUND, "Using SBL sound effect"); // SBL resources essentially contain VOC sound data. @@ -954,7 +957,7 @@ void Sound::setupSfxFile() { if (file.open(tmp)) _sfxFilename = tmp; - + if (_vm->_game.heversion <= 74) _sfxFileEncByte = 0x69; @@ -1179,7 +1182,7 @@ int ScummEngine::readSoundResource(ResId idx) { // its sound resources, and Amiga games, which feature only ROL // resources, since we are a doing Midi -> AdLib conversion for // these. - if ((_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS) && pri != 16 + if ((_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS) && pri != 16 && pri != 15 && pri != 10 && pri != 2 && _game.platform != Common::kPlatformAmiga) pri = -1; diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp index 567ca31485..0d0f6cdb95 100644 --- a/engines/scumm/verbs.cpp +++ b/engines/scumm/verbs.cpp @@ -84,7 +84,7 @@ int ScummEngine_v0::verbPrepIdType(int verbid) { switch (verbid) { case kVerbUse: // depends on object1 return kVerbPrepObject; - case kVerbGive: + case kVerbGive: return kVerbPrepTo; case kVerbUnlock: case kVerbFix: return kVerbPrepWith; @@ -693,7 +693,7 @@ void ScummEngine_v0::verbExec() { if (_activeVerb == kVerbWhatIs) return; - + if (!(_activeVerb == kVerbWalkTo && _activeObject == 0)) { doSentence(_activeVerb, _activeObject, _activeObject2); if (_activeVerb != kVerbWalkTo) { diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp index dfa3ded50b..8f6c2bb6a2 100644 --- a/engines/sky/detection.cpp +++ b/engines/sky/detection.cpp @@ -119,12 +119,12 @@ GameList SkyMetaEngine::getSupportedGames() const { const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &target) const { Common::String guiOptions; ExtraGuiOptions options; - + if (target.empty()) { options.push_back(skyExtraGuiOption); return options; } - + if (ConfMan.hasKey("guioptions", target)) { guiOptions = ConfMan.get("guioptions", target); guiOptions = parseGameGUIOptions(guiOptions); diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index f7add4eed2..ff3c897dba 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -69,7 +69,7 @@ static const char *const sequenceList[20] = { }; // This is the list of the names of the PlayStation videos -// TODO: fight.str, flashy.str, +// TODO: fight.str, flashy.str, static const char *const sequenceListPSX[20] = { "e_ferr1", "ladder1", @@ -152,14 +152,14 @@ bool MoviePlayer::load(uint32 id) { warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.c_str(), lineNo, startFrame, lastEnd); continue; } - + int color = 0; if (*ptr == '@') { ++ptr; color = strtoul(ptr, const_cast<char **>(&ptr), 10); while (*ptr && Common::isSpace(*ptr)) ptr++; - } + } _movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color)); lastEnd = endFrame; diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp index 6e395116f9..7cd85dff54 100644 --- a/engines/sword1/control.cpp +++ b/engines/sword1/control.cpp @@ -541,7 +541,9 @@ void Control::setupMainPanel() { if (SwordEngine::_systemVars.controlPanelMode == CP_DEATHSCREEN) panelId = SR_DEATHPANEL; else { - if (SwordEngine::_systemVars.language <= BS1_SPANISH) + if (SwordEngine::_systemVars.realLanguage == Common::EN_USA) + panelId = SR_PANEL_AMERICAN; + else if (SwordEngine::_systemVars.language <= BS1_SPANISH) panelId = SR_PANEL_ENGLISH + SwordEngine::_systemVars.language; else panelId = SR_PANEL_ENGLISH; diff --git a/engines/sword1/objectman.cpp b/engines/sword1/objectman.cpp index 5d1864d58d..3e70a95699 100644 --- a/engines/sword1/objectman.cpp +++ b/engines/sword1/objectman.cpp @@ -107,7 +107,7 @@ char *ObjectMan::lockText(uint32 textId) { warning("Missing translation for textId %u (\"%s\")", textId, text); unlockText(textId, BS1_ENGLISH); } - + return _missingSubTitleStr; } return text; @@ -164,7 +164,7 @@ char *ObjectMan::lockText(uint32 textId, uint8 lang) { // We use the hardcoded text in this case. if (textId == 2950145) return const_cast<char *>(_translationId2950145[lang]); - + warning("ObjectMan::lockText(%d): text number has no text lines", textId); return NULL; } diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp index 75e8f72d9d..fa593b8df4 100644 --- a/engines/sword1/sword1.cpp +++ b/engines/sword1/sword1.cpp @@ -116,8 +116,9 @@ Common::Error SwordEngine::init() { _systemVars.controlPanelMode = CP_NEWGAME; _systemVars.forceRestart = false; _systemVars.wantFade = true; + _systemVars.realLanguage = Common::parseLanguage(ConfMan.get("language")); - switch (Common::parseLanguage(ConfMan.get("language"))) { + switch (_systemVars.realLanguage) { case Common::DE_DEU: _systemVars.language = BS1_GERMAN; break; @@ -138,6 +139,7 @@ Common::Error SwordEngine::init() { break; default: _systemVars.language = BS1_ENGLISH; + break; } _systemVars.showText = ConfMan.getBool("subtitles"); diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h index ccdc2d3a59..ec6555b4b3 100644 --- a/engines/sword1/sword1.h +++ b/engines/sword1/sword1.h @@ -75,6 +75,7 @@ struct SystemVars { uint8 language; bool isDemo; Common::Platform platform; + Common::Language realLanguage; }; class SwordEngine : public Engine { diff --git a/engines/sword2/sprite.cpp b/engines/sword2/sprite.cpp index cb0923cc2f..91a5e2e86b 100644 --- a/engines/sword2/sprite.cpp +++ b/engines/sword2/sprite.cpp @@ -772,7 +772,7 @@ int32 Screen::drawSprite(SpriteInfo *s) { src = sprite + rs.top * srcPitch + rs.left; dst = _buffer + _screenWide * rd.top + rd.left; - if (s->type & RDSPR_BLEND) { + if (s->type & RDSPR_BLEND) { // The original code had two different blending cases. One for // s->blend & 0x01 and one for s->blend & 0x02. However, the // only values that actually appear in the cluster files are @@ -783,7 +783,7 @@ int32 Screen::drawSprite(SpriteInfo *s) { // The only correct way to simulate this would be using 16-bit mode. // As this is not yet available for this engine, fake transparency is used // as placeholder. - if (!(_renderCaps & RDBLTFX_SPRITEBLEND) || Sword2Engine::isPsx()) { + if (!(_renderCaps & RDBLTFX_SPRITEBLEND) || Sword2Engine::isPsx()) { for (i = 0; i < rs.height(); i++) { for (j = 0; j < rs.width(); j++) { if (src[j] && ((i & 1) == (j & 1))) diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp index 69fae3dc4e..61d53c89a7 100644 --- a/engines/sword25/sfx/soundengine.cpp +++ b/engines/sword25/sfx/soundengine.cpp @@ -370,7 +370,7 @@ bool SoundEngine::unpersist(InputPersistenceBlock &reader) { int loopStart; int loopEnd; uint layer; - + reader.readString(fileName); reader.read(sndType); reader.read(volume); diff --git a/engines/teenagent/actor.cpp b/engines/teenagent/actor.cpp index cb8c798fb6..d65a367309 100644 --- a/engines/teenagent/actor.cpp +++ b/engines/teenagent/actor.cpp @@ -22,47 +22,46 @@ #include "teenagent/actor.h" #include "teenagent/objects.h" #include "teenagent/resources.h" +#include "teenagent/teenagent.h" #include "common/random.h" #include "common/textconsole.h" namespace TeenAgent { -Actor::Actor() : head_index(0), idle_type(0) {} +Actor::Actor(TeenAgentEngine *vm) : _vm(vm), headIndex(0), idleType(0) {} -//idle animation lists at dseg: 0x6540 -Common::Rect Actor::renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int delta_frame, uint zoom, Common::RandomSource &rnd) { +Common::Rect Actor::renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, uint zoom, Common::RandomSource &rnd) { if (index == 0) { - idle_type = rnd.getRandomNumber(2); - debug(0, "switched to idle animation %u", idle_type); + idleType = rnd.getRandomNumber(2); + debugC(kDebugActor, "switched to idle animation %u", idleType); } - Resources *res = Resources::instance(); - byte *frames_idle; + byte *framesIdle; do { - frames_idle = res->dseg.ptr(res->dseg.get_word(0x6540 + idle_type * 2)) + index; - index += delta_frame; - if (*frames_idle == 0) { - idle_type = rnd.getRandomNumber(2); - debug(0, "switched to idle animation %u[loop]", idle_type); + framesIdle = _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_idleAnimationListPtr + idleType * 2)) + index; + index += deltaFrame; + if (*framesIdle == 0) { + idleType = rnd.getRandomNumber(2); + debugC(kDebugActor, "switched to idle animation %u[loop]", idleType); index = 3; //put 4th frame (base 1) if idle animation loops } - } while (*frames_idle == 0); + } while (*framesIdle == 0); bool mirror = orientation == kActorLeft; - Surface *s = frames + *frames_idle - 1; + Surface *s = frames + *framesIdle - 1; - ///\todo remove copy-paste here and below + //TODO: remove copy-paste here and below int xp = position.x - s->w * zoom / 512 - s->x, yp = position.y - 62 * zoom / 256 - s->y; //hardcoded in original game return s->render(surface, xp, yp, mirror, Common::Rect(), zoom); } -Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int delta_frame, bool render_head, uint zoom) { - const uint8 frames_left_right[] = {0, 1, 2, 3, 4, 5, /* step */ 6, 7, 8, 9}; - const uint8 frames_up[] = {18, 19, 20, 21, 22, 23, 24, 25, }; - const uint8 frames_down[] = {10, 11, 12, 13, 14, 15, 16, 17, }; +Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, bool renderHead, uint zoom) { + const uint8 framesLeftRight[] = {0, 1, 2, 3, 4, 5, /* step */ 6, 7, 8, 9}; + const uint8 framesUp[] = {18, 19, 20, 21, 22, 23, 24, 25, }; + const uint8 framesDown[] = {10, 11, 12, 13, 14, 15, 16, 17, }; - const uint8 frames_head_left_right[] = { + const uint8 framesHeadLeftRight[] = { 0x27, 0x1a, 0x1b, 0x27, 0x1c, 0x1d, 0x27, 0x1a, @@ -73,14 +72,14 @@ Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &posi 0x27, 0x1a, }; - const uint8 frames_head_up[] = { + const uint8 framesHeadUp[] = { 0x29, 0x25, 0x29, 0x29, 0x26, 0x29, 0x26, 0x29, 0x29, 0x25, 0x29, 0x25, 0x29, 0x29, 0x29, 0x25, 0x25, 0x29, 0x29, 0x26 }; - const uint8 frames_head_down[] = { + const uint8 framesHeadDown[] = { 0x20, 0x21, 0x22, 0x23, 0x28, 0x24, 0x28, 0x28, 0x24, 0x28, 0x20, 0x21, @@ -91,45 +90,45 @@ Common::Rect Actor::render(Graphics::Surface *surface, const Common::Point &posi Surface *s = NULL, *head = NULL; bool mirror = orientation == kActorLeft; - index += delta_frame; + index += deltaFrame; switch (orientation) { case kActorLeft: case kActorRight: - if (render_head) { - if (head_index >= ARRAYSIZE(frames_head_left_right)) - head_index = 0; - head = frames + frames_head_left_right[head_index]; - ++head_index; + if (renderHead) { + if (headIndex >= ARRAYSIZE(framesHeadLeftRight)) + headIndex = 0; + head = frames + framesHeadLeftRight[headIndex]; + ++headIndex; } - if (index >= ARRAYSIZE(frames_left_right)) + if (index >= ARRAYSIZE(framesLeftRight)) index = 1; - s = frames + frames_left_right[index]; + s = frames + framesLeftRight[index]; break; case kActorUp: - if (render_head) { - if (head_index >= ARRAYSIZE(frames_head_up)) - head_index = 0; - head = frames + frames_head_up[head_index]; - ++head_index; + if (renderHead) { + if (headIndex >= ARRAYSIZE(framesHeadUp)) + headIndex = 0; + head = frames + framesHeadUp[headIndex]; + ++headIndex; } - if (index >= ARRAYSIZE(frames_up)) + if (index >= ARRAYSIZE(framesUp)) index = 1; - s = frames + frames_up[index]; + s = frames + framesUp[index]; break; case kActorDown: - if (render_head) { - if (head_index >= ARRAYSIZE(frames_head_down)) - head_index = 0; - head = frames + frames_head_down[head_index]; - ++head_index; + if (renderHead) { + if (headIndex >= ARRAYSIZE(framesHeadDown)) + headIndex = 0; + head = frames + framesHeadDown[headIndex]; + ++headIndex; } - if (index >= ARRAYSIZE(frames_down)) + if (index >= ARRAYSIZE(framesDown)) index = 1; - s = frames + frames_down[index]; + s = frames + framesDown[index]; break; default: return Common::Rect(); diff --git a/engines/teenagent/actor.h b/engines/teenagent/actor.h index 9a7d395547..942397c636 100644 --- a/engines/teenagent/actor.h +++ b/engines/teenagent/actor.h @@ -28,13 +28,20 @@ class RandomSource; namespace TeenAgent { +class TeenAgentEngine; + class Actor : public Animation { - uint head_index; - uint idle_type; +private: + TeenAgentEngine *_vm; + + uint headIndex; + uint idleType; + public: - Actor(); - Common::Rect render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int delta_frame, bool head, uint zoom); - Common::Rect renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int delta_frame, uint zoom, Common::RandomSource &rnd); + Actor(TeenAgentEngine *vm); + + Common::Rect render(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, bool renderHead, uint zoom); + Common::Rect renderIdle(Graphics::Surface *surface, const Common::Point &position, uint8 orientation, int deltaFrame, uint zoom, Common::RandomSource &rnd); }; } // End of namespace TeenAgent diff --git a/engines/teenagent/animation.cpp b/engines/teenagent/animation.cpp index 56107b67ca..effafcaac6 100644 --- a/engines/teenagent/animation.cpp +++ b/engines/teenagent/animation.cpp @@ -19,24 +19,30 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "teenagent/teenagent.h" #include "teenagent/animation.h" + #include "common/endian.h" #include "common/textconsole.h" namespace TeenAgent { -Animation::Animation() : id(0), x(0), y(0), loop(true), paused(false), ignore(false), data(0), data_size(0), frames_count(0), frames(0), index(0) { +Animation::Animation() : id(0), x(0), y(0), loop(true), paused(false), ignore(false), data(0), dataSize(0), framesCount(0), frames(0), index(0) { +} + +Animation::~Animation() { + free(); } Surface *Animation::firstFrame() { - if (frames == NULL || frames_count == 0) + if (frames == NULL || framesCount == 0) return NULL; Surface *r = frames; uint16 pos = READ_LE_UINT16(data + 1); if (pos != 0) { - r->x = pos % 320; - r->y = pos / 320; + r->x = pos % kScreenWidth; + r->y = pos / kScreenWidth; } return r; } @@ -45,38 +51,38 @@ Surface *Animation::currentFrame(int dt) { if (paused) return firstFrame(); - if (frames == NULL || frames_count == 0) + if (frames == NULL || framesCount == 0) return NULL; Surface *r; if (data != NULL) { uint32 frame = 3 * index; - //debug(0, "%u/%u", index, data_size / 3); + debugC(2, kDebugAnimation, "%u/%u", index, dataSize / 3); index += dt; - if (!loop && index >= data_size / 3) { + if (!loop && index >= dataSize / 3) { return NULL; } - if (data[frame] - 1 >= frames_count) { - warning("invalid frame %u(0x%x) (max %u) index %u, mod %u", frame, frame, frames_count, index - 1, data_size / 3); + if (data[frame] - 1 >= framesCount) { + warning("invalid frame %u(0x%x) (max %u) index %u, mod %u", frame, frame, framesCount, index - 1, dataSize / 3); return NULL; } r = frames + data[frame] - 1; uint16 pos = READ_LE_UINT16(data + frame + 1); - index %= (data_size / 3); + index %= (dataSize / 3); if (pos != 0) { - x = r->x = pos % 320; - y = r->y = pos / 320; + x = r->x = pos % kScreenWidth; + y = r->y = pos / kScreenWidth; } } else { - //debug(0, "index %u", index); + debugC(2, kDebugAnimation, "index %u", index); r = frames + index; index += dt; - index %= frames_count; + index %= framesCount; } return r; @@ -97,9 +103,9 @@ void Animation::free() { delete[] data; data = NULL; - data_size = 0; + dataSize = 0; - frames_count = 0; + framesCount = 0; delete[] frames; frames = NULL; @@ -107,44 +113,42 @@ void Animation::free() { } void Animation::load(Common::SeekableReadStream &s, Type type) { - //fixme: do not reload the same animation each time + //FIXME: do not reload the same animation each time free(); if (s.size() <= 1) { - debug(1, "empty animation"); + debugC(1, kDebugAnimation, "empty animation"); return; } - //uint16 pos = 0; + uint16 pos = 0; int off = 0; switch (type) { case kTypeLan: - data_size = s.readUint16LE(); + dataSize = s.readUint16LE(); if (s.eos()) { - debug(1, "empty animation"); + debugC(1, kDebugAnimation, "empty animation"); return; } - data_size -= 2; - data = new byte[data_size]; - data_size = s.read(data, data_size); - /* for (int i = 0; i < data_size; ++i) { - debug(0, "%02x ", data[i]); - } - debug(0, ", %u frames", data_size / 3); - */ - frames_count = s.readByte(); - debug(1, "%u physical frames", frames_count); - if (frames_count == 0) + dataSize -= 2; + data = new byte[dataSize]; + dataSize = s.read(data, dataSize); + for (int i = 0; i < dataSize; ++i) + debugC(2, kDebugAnimation, "%02x ", data[i]); + debugC(2, kDebugAnimation, ", %u frames", dataSize / 3); + framesCount = s.readByte(); + debugC(1, kDebugAnimation, "%u physical frames", framesCount); + if (framesCount == 0) return; - frames = new Surface[frames_count]; + frames = new Surface[framesCount]; - s.skip(frames_count * 2 - 2); //sizes - /*pos = */s.readUint16LE(); - //debug(0, "pos?: %04x", pos); + s.skip(framesCount * 2 - 2); //sizes + pos = s.readUint16LE(); + debugC(3, kDebugAnimation, "pos?: 0x%04x", pos); - for (uint16 i = 0; i < frames_count; ++i) { + for (uint16 i = 0; i < framesCount; ++i) { frames[i].load(s, Surface::kTypeLan); frames[i].x = 0; frames[i].y = 0; @@ -152,43 +156,43 @@ void Animation::load(Common::SeekableReadStream &s, Type type) { break; case kTypeInventory: { - data_size = 3 * s.readByte(); - data = new byte[data_size]; + dataSize = 3 * s.readByte(); + data = new byte[dataSize]; - frames_count = 0; - for (byte i = 0; i < data_size / 3; ++i) { + framesCount = 0; + for (byte i = 0; i < dataSize / 3; ++i) { int idx = i * 3; - /* byte unk = */ - s.readByte(); + byte unk = s.readByte(); + debugC(3, kDebugAnimation, "unk?: 0x%02x", unk); data[idx] = s.readByte(); if (data[idx] == 0) data[idx] = 1; //fixme: investigate - if (data[idx] > frames_count) - frames_count = data[idx]; + if (data[idx] > framesCount) + framesCount = data[idx]; data[idx + 1] = 0; data[idx + 2] = 0; - //debug(0, "frame #%u", data[idx]); + debugC(2, kDebugAnimation, "frame #%u", data[idx]); } - frames = new Surface[frames_count]; + frames = new Surface[framesCount]; - for (uint16 i = 0; i < frames_count; ++i) { + for (uint16 i = 0; i < framesCount; ++i) { frames[i].load(s, Surface::kTypeOns); } } break; case kTypeVaria: - frames_count = s.readByte(); - debug(1, "loading varia resource, %u physical frames", frames_count); + framesCount = s.readByte(); + debugC(1, kDebugAnimation, "loading varia resource, %u physical frames", framesCount); uint16 offset[255]; - for (byte i = 0; i < frames_count; ++i) { + for (byte i = 0; i < framesCount; ++i) { offset[i] = s.readUint16LE(); - //debug(0, "%u: %04x", i, offset[i]); + debugC(0, kDebugAnimation, "%u: %04x", i, offset[i]); } - frames = new Surface[frames_count]; - for (uint16 i = 0; i < frames_count; ++i) { - //debug(0, "%04x", offset[i]); + frames = new Surface[framesCount]; + for (uint16 i = 0; i < framesCount; ++i) { + debugC(0, kDebugAnimation, "%04x", offset[i]); s.seek(offset[i] + off); frames[i].load(s, Surface::kTypeOns); } @@ -196,11 +200,7 @@ void Animation::load(Common::SeekableReadStream &s, Type type) { break; } - debug(0, "%u frames", data_size / 3); -} - -Animation::~Animation() { - free(); + debugC(2, kDebugAnimation, "%u frames", dataSize / 3); } } // End of namespace TeenAgent diff --git a/engines/teenagent/animation.h b/engines/teenagent/animation.h index 6942cc74eb..9be21a4c3d 100644 --- a/engines/teenagent/animation.h +++ b/engines/teenagent/animation.h @@ -35,6 +35,8 @@ public: enum Type {kTypeLan, kTypeVaria, kTypeInventory}; Animation(); + ~Animation(); + void load(Common::SeekableReadStream &, Type type = kTypeLan); void free(); @@ -43,8 +45,6 @@ public: uint16 currentIndex() const { return index; } void resetIndex() { index = 0; } - ~Animation(); - bool empty() const { return frames == NULL; } void restart(); @@ -53,9 +53,9 @@ public: protected: byte *data; - uint16 data_size; + uint16 dataSize; - uint16 frames_count; + uint16 framesCount; Surface *frames; uint16 index; }; diff --git a/engines/teenagent/callbacks.cpp b/engines/teenagent/callbacks.cpp index 934727a478..2de81abb37 100644 --- a/engines/teenagent/callbacks.cpp +++ b/engines/teenagent/callbacks.cpp @@ -19,8 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "teenagent/scene.h" #include "teenagent/teenagent.h" +#include "teenagent/scene.h" +#include "teenagent/inventory.h" #include "teenagent/resources.h" #include "teenagent/dialog.h" @@ -33,159 +34,536 @@ namespace TeenAgent { #define GET_FLAG(addr) (res->dseg.get_byte(addr)) #define INC_FLAG(addr) (++*res->dseg.ptr(addr)) -void TeenAgentEngine::rejectMessage() { - Resources *res = Resources::instance(); - //random reject message: - uint i = _rnd.getRandomNumber(3); - //debug(0, "reject message: %s", (const char *)res->dseg.ptr(res->dseg.get_word(0x339e + 2 * i))); - displayMessage(res->dseg.get_word(0x339e + 2 * i)); +void TeenAgentEngine::fnIntro() { + hideActor(); + + loadScene(41, 139, 156, 3); + playSound(41, 12); + playAnimation(912, 1); + setOns(0, 108); + playSound(62, 8); + playSound(58, 40); + playAnimation(913, 1); + setOns(1, 109); + setLan(2, 1); + dialog->show(192, scene, 914, 915, textColorGoldDriver, textColorBankGuard, 2, 1); + displayCredits(dsAddr_introCredits1); + + loadScene(42, 139, 156, 3); + playSound(15, 20); + playAnimation(916, 1); + playSound(40, 18); + playSound(40, 22); + for (byte i = 27; i < 37; i += 2) + playSound(40, i); + playSound(29, 44); + playAnimation(918, 0, true); + playAnimation(917, 1, true); + waitAnimation(); + displayCredits(dsAddr_introCredits2); + + loadScene(40, 139, 156, 3); + playMusic(3); + dialog->show(193, scene, 920, 924, textColorRGBBoss, textColorFortuneTeller, 1, 2); + playSound(26, 50); + playAnimation(925, 0, true); + playAnimation(926, 1, true); + waitAnimation(); + dialog->show(194, scene, 927, 920, textColorFortuneTeller, textColorRGBBoss, 2, 1); + displayCredits(dsAddr_introCredits3); + + loadScene(39, 139, 156, 3); + playMusic(11); + playSound(81, 2); + playSound(81, 8); + playSound(81, 11); + playSound(81, 14); + playSound(81, 16); + playSound(81, 18); + playSound(81, 20); + playSound(81, 21); + playAnimation(928, 1); + setOns(0, 112); + dialog->showMono(195, scene, 929, textColorMark, 1); + showActor(); + moveTo(319, 150, 1, true); + moveTo(63, 150, 1); + displayAsyncMessage(dsAddr_HeyWtmQMsg, 4, 62, 18, 36); // hey, what's the matter? + playAnimation(851, 0, true); + playActorAnimation(930, true); + waitAnimation(); + playSound(24, 11); + playActorAnimation(931); + + displayCredits(dsAddr_introCredits4); + + playMusic(3); + loadScene(40, 50, 186, 1); + setOns(0, 113); + dialog->show(196, scene, 919, 0, textColorRGBBoss, textColorMark, 1, 0); + moveTo(196, 186, 1); + dialog->show(197, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + playActorAnimation(932); + dialog->show(198, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + playActorAnimation(932); + dialog->show(199, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + playActorAnimation(932); + dialog->show(200, scene, 0, 922, textColorMark, textColorRGBBoss, 0, 1); + playActorAnimation(933); + dialog->show(201, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + moveTo(174, 186, 1); + playAnimation(851, 0, true); + playActorAnimation(934, true); + waitAnimation(); + loadScene(10, 136, 153, 3); } +void TeenAgentEngine::fnPoleClimbFail() { + moveTo(86, 195, 1, true); + playActorAnimation(868); +} -bool TeenAgentEngine::processCallback(uint16 addr) { - if (addr == 0) - return false; +void TeenAgentEngine::fnGotAnchor() { + SET_FLAG(dsAddr_timedCallbackState, 0); + setTimerCallback(0, 0); + scene->getActorAnimation()->free(); + playSound(64, 7); + playActorAnimation(618); + disableObject(5); + setOns(0, 0); + playSound(31, 1); + playActorAnimation(619); + fnGetOutOfLake(); + inventory->add(kInvItemAnchor); + displayMessage(dsAddr_hookedAnchorMsg); // "I was really hooked on this anchor!" +} - Resources *res = Resources::instance(); - debug(0, "processCallback(%04x)", addr); - byte *code = res->cseg.ptr(addr); - - //try trivial callbacks first - if (code[0] == 0xbb && code[3] == 0xe8 && code[6] == 0xc3) { - //call display_message, r - uint16 msg = READ_LE_UINT16(code + 1); - uint16 func = 6 + addr + READ_LE_UINT16(code + 4); - debug(0, "call %04x", func); - //debug(0, "trivial callback, showing message %s", (const char *)res->dseg.ptr(addr)); - switch (func) { - case 0xa055: - displayMessage(msg); - return true; - } +void TeenAgentEngine::fnGetOutOfLake() { + loadScene(15, 156, 180, 3); + playSound(5, 5); + playSound(38, 14); + playSound(38, 20); + playSound(5, 25); + playActorAnimation(616); +} + +void TeenAgentEngine::fnGuardDrinking() { + SET_FLAG(dsAddr_timedCallbackState, 0); + setTimerCallback(0, 0); + scene->getAnimation(0)->free(); + SET_FLAG(dsAddr_scaredGuardAlreadyFlag, 1); + + displayAsyncMessage(dsAddr_BooMsg, 300, 130, 1, 5); // "Booo!" + setOns(0, 16); + enableObject(2); + + playSound(17, 5); + playAnimation(545, 0); + + dialog->show(5, scene, 0, 546, textColorMark, textColorMansionGuard, 0, 1); + SET_FLAG(dsAddr_spokenWithMansionGuardFlag, 1); + SET_FLAG(dsAddr_haveNotSpokenWithMansionGuardFlag, 0); +} + +void TeenAgentEngine::fnEgoDefaultPosition() { + if (scene->getPosition().y <= 149) + moveTo(94, 115, 4); + else + moveTo(51, 149, 4); +} + +void TeenAgentEngine::fnEnterCave() { + loadScene(24, 230, 170, 1); + playSound(52, 3); + playSound(52, 7); + playSound(52, 11); + playSound(52, 14); + playSound(52, 18); + playSound(52, 21); + playSound(52, 25); + playActorAnimation(601); + moveTo(230, 179, 3); + if (!CHECK_FLAG(dsAddr_lightOnFlag, 1)) + displayMessage(dsAddr_kindaDarkMsg); // "It's kinda dark here" +} + +void TeenAgentEngine::fnEgoScaredBySpider() { + if (CHECK_FLAG(dsAddr_egoAlreadyScaredBySpiderFlag, 1)) { + fnMoveToLadderAndLeaveCellar(); + dialog->showMark(75, scene); + } else { + dialog->showMark(73, scene); + fnMoveToLadderAndLeaveCellar(); + wait(100); + dialog->showMark(74, scene); + SET_FLAG(dsAddr_egoAlreadyScaredBySpiderFlag, 1); } +} - if (code[0] == 0xe8 && code[3] == 0xc3) { - uint func = 3 + addr + READ_LE_UINT16(code + 1); - debug(0, "call %04x and return", func); - if (func == 0xa4d6) { - rejectMessage(); +void TeenAgentEngine::fnMoveToLadderAndLeaveCellar() { + Object *objTemp = scene->getObject(3); + moveTo(objTemp); + fnLeaveCellar(); + moveTo(48, 190, 3); +} + +void TeenAgentEngine::fnLeaveCellar() { + playSound(52, 10); + playSound(52, 14); + playSound(52, 18); + playSound(52, 21); + playSound(52, 25); + playSound(52, 28); + playSound(52, 32); + playActorAnimation(600); + loadScene(21, 297, 178, 3); +} + +void TeenAgentEngine::fnPutRockInHole() { + if (CHECK_FLAG(dsAddr_timedCallbackState, 0)) { + playSound(5, 2); + playSound(15, 12); + playActorAnimation(638); + inventory->remove(kInvItemMouse); + setTimerCallback(csAddr_mouseOutOfHoleTimeout, 100); + SET_FLAG(dsAddr_timedCallbackState, 1); + } else if (CHECK_FLAG(dsAddr_timedCallbackState, 1)) { + playSound(5, 2); + playSound(52, 13); + playActorAnimation(648); + setOns(1, 46); + inventory->remove(kInvItemRock); + setTimerCallback(csAddr_mouseOutOfHoleTimeout, 100); + SET_FLAG(dsAddr_timedCallbackState, 2); + } else if (CHECK_FLAG(dsAddr_timedCallbackState, 2)) { + playActorAnimation(649); + setOns(1, 47); + wait(300); + for (byte i = 1; i <= 37; i += 4) + playSound(68, i); + playAnimation(639, 2); + setOns(0, 42); + enableObject(6); + disableObject(5); + SET_FLAG(dsAddr_mouseGotGoldNuggetFlag, 1); + SET_FLAG(dsAddr_timedCallbackState, 0); + setTimerCallback(0, 0); + } +} + +void TeenAgentEngine::fnEgoBottomRightTurn() { + Common::Point p = scene->getPosition(); + if (p.x == 208 && p.y == 151) + moveRel(0, 0, 2); + else + moveTo(208, 151, 1); +} + +bool TeenAgentEngine::fnCheckingDrawers() { + uint16 v = GET_FLAG(dsAddr_drawerPuzzleBookValue) - 1; + if (GET_FLAG(dsAddr_blueDrawerOpenFlag + v) != 1) + return false; + else { + uint16 sum = 0; + for (uint i = 0; i < 6; ++i) + sum += GET_FLAG(dsAddr_blueDrawerOpenFlag + i); + if (sum != 1) + return false; + else return true; - } } +} - if (code[0] == 0xc7 && code[1] == 0x06 && code[2] == 0xf3 && code[3] == 0xb4 && - code[6] == 0xb8 && code[9] == 0xbb && code[12] == 0xbf && - code[22] == 0xe8 && code[25] == 0xc3) { - loadScene(code[4], Common::Point( - (READ_LE_UINT16(code + 7) + READ_LE_UINT16(code + 13) + 1) / 2 , - READ_LE_UINT16(code + 10))); - scene->setOrientation(code[21]); +void TeenAgentEngine::fnDrawerOpenMessage() { + if (CHECK_FLAG(dsAddr_drawerPuzzleHintGivenFlag, 1)) + displayMessage(dsAddr_drawerOpenMsg); // "I cannot open the drawer if the next one is open!" + else { + displayMessage(dsAddr_strangeDrawerMsg); // "Strange, but the drawer is stuck if the next drawer is open" + displayMessage(dsAddr_notOrdinaryDrawersMsg); // "Maybe these are not just ordinary drawers!" + SET_FLAG(dsAddr_drawerPuzzleHintGivenFlag, 1); + } +} + +bool TeenAgentEngine::fnRobotSafeAlreadyUnlockedCheck() { + if (CHECK_FLAG(dsAddr_MansionRobotSafeUnlockedFlag, 1)) { return true; + } else { + displayMessage(dsAddr_noReasonMsg); // "There's no reason to do it" + return false; } +} - switch (addr) { +void TeenAgentEngine::fnRobotSafeUnlockCheck() { + if (CHECK_FLAG(dsAddr_MansionRobotSafeVoiceTestPassedFlag, 1) && + CHECK_FLAG(dsAddr_MansionRobotSafeScentTestPassedFlag, 1) && + CHECK_FLAG(dsAddr_MansionRobotSafeViewTestPassedFlag, 1)) { + waitLanAnimationFrame(1, 1); + playSound(89, 2); + playActorAnimation(731); + setOns(0, 70); + setLan(1, 0); + disableObject(1); + enableObject(2); + enableObject(3); + } +} - case 0x024c: //intro - hideActor(); +bool TeenAgentEngine::fnMansionIntrusionAttempt() { + wait(50); + byte attempts = res->dseg.get_byte(dsAddr_mansionEntryCount) + 1; + res->dseg.set_byte(dsAddr_mansionEntryCount, attempts); + debugC(0, kDebugCallbacks, "mansion intrusion attempt #%u", attempts); + if (attempts >= 7) + return false; + else { + byte id = scene->getId(); - loadScene(41, 139, 156, 3); - playSound(41, 12); - playAnimation(912, 1); - setOns(0, 108); - playSound(62, 8); - playSound(58, 40); - playAnimation(913, 1); - setOns(1, 109); - setLan(2, 1); - Dialog::show(scene, 0x748e, 914, 915, 0xe7, 0xd7, 2, 1); - displayCredits(0xe3c2); - - loadScene(42, 139, 156, 3); - playSound(15, 20); - playAnimation(916, 1); - playSound(40, 18); - playSound(40, 22); - for (byte i = 27; i < 37; i += 2) - playSound(40, i); - playSound(29, 44); - playAnimation(918, 0, true); - playAnimation(917, 1, true); - waitAnimation(); - displayCredits(0xe3e6); + playMusic(11); + displayCutsceneMessage(dsAddr_cutsceneMsg2, 84, 95); // "Meanwhile in the mansion" + switch (attempts) { + case 2: + fnSecondMansionIntrusion(); + break; + case 3: + fnThirdMansionIntrusion(); + break; + case 4: + fnFourthMansionIntrusion(); + break; + case 5: + fnFifthMansionIntrusion(); + break; + case 6: + fnSixthMansionIntrusion(); + break; + default: + error("mansion intrusion attempts out of range!"); + break; + } + playMusic(6); + if (getFlag(dsAddr_johnNotyOutsideMansionDoorFlag) != 1 || attempts != 6) + loadScene(id, scene->getPosition()); + return true; + } +} - loadScene(40, 139, 156, 3); - playMusic(3); - Dialog::show(scene, 0x750d, 920, 924, 0xe7, 0xeb, 1, 2); //as i told you, our organization... - playSound(26, 50); - playAnimation(925, 0, true); - playAnimation(926, 1, true); - waitAnimation(); - Dialog::show(scene, 0x78a6, 927, 920, 0xeb, 0xe7, 2, 1); - displayCredits(0xe3ff); +void TeenAgentEngine::fnSecondMansionIntrusion() { + hideActor(); + loadScene(34, scene->getPosition()); + playAnimation(986, 0, true); + playAnimation(987, 1, true); + waitAnimation(); + dialog->show(178, scene, 988, 989, textColorMansionGuard, textColorJohnNoty, 1, 2); + playAnimation(990, 0, true); + playAnimation(991, 1, true); + waitAnimation(); + showActor(); +} - loadScene(39, 139, 156, 3); - playMusic(11); - playSound(81, 2); - playSound(81, 8); - playSound(81, 11); - playSound(81, 14); - playSound(81, 16); - playSound(81, 18); - playSound(81, 20); - playSound(81, 21); - playAnimation(928, 1); - setOns(0, 112); - Dialog::showMono(scene, 0x78e1, 929, 0xd1, 1); //he's coming - showActor(); - moveTo(319, 150, 1, true); - moveTo(63, 150, 1); - displayAsyncMessage(0x5da8, 19844, 18, 36); //hey, what's the matter? - playAnimation(851, 0, true); - playActorAnimation(930, true); - waitAnimation(); - playSound(24, 11); - playActorAnimation(931); +void TeenAgentEngine::fnThirdMansionIntrusion() { + hideActor(); + loadScene(30, scene->getPosition()); + playAnimation(887, 1); + playAnimation(888, 2, true, true, true); + //waitAnimation(); + dialog->showMono(179, scene, 889, textColorMansionGuard, 2); + playSound(26, 3); + playAnimation(891, 1, true, true, true); + playAnimation(892, 2); + waitAnimation(); + dialog->show(180, scene, 890, 889, textColorJohnNoty, textColorMansionGuard, 3, 2); + showActor(); +} - displayCredits(0xe42f); +void TeenAgentEngine::fnFourthMansionIntrusion() { + hideActor(); + loadScene(32, scene->getPosition()); + playAnimation(894, 1, true, true, true); + playAnimation(893, 2, true); + waitAnimation(); + dialog->showMono(181, scene, 895, textColorMansionGuard, 3); + playSound(75, 9); + playAnimation(898, 1, true); + playAnimation(897, 2, true); + dialog->show(182, scene, 896, 895, textColorJohnNoty, textColorMansionGuard, 2, 3); + showActor(); +} - playMusic(3); - loadScene(40, 50, 186, 1); - setOns(0, 113); - Dialog::show(scene, 0x78f1, 919, 0, 0xe7, 0xd1, 1, 0); - moveTo(196, 186, 1); - Dialog::show(scene, 0x7958, 0, 920, 0xd1, 0xe7, 0, 1); - playActorAnimation(932); - Dialog::show(scene, 0x7e07, 0, 920, 0xd1, 0xe7, 0, 1); - playActorAnimation(932); - Dialog::show(scene, 0x7e1a, 0, 920, 0xd1, 0xe7, 0, 1); - playActorAnimation(932); - Dialog::show(scene, 0x7e2c, 0, 922, 0xd1, 0xe7, 0, 1); - playActorAnimation(933); - Dialog::show(scene, 0x7e70, 0, 920, 0xd1, 0xe7, 0, 1); - moveTo(174, 186, 1); - playAnimation(851, 0, true); - playActorAnimation(934, true); - waitAnimation(); - loadScene(10, 136, 153, 3); +void TeenAgentEngine::fnFifthMansionIntrusion() { + hideActor(); + loadScene(29, scene->getPosition()); + playActorAnimation(901, true); + playAnimation(900, 1, true); + waitAnimation(); + dialog->show(183, scene, 903, 902, textColorJohnNoty, textColorMansionGuard, 2, 3); + for (byte i = 3; i <= 9; i += 2) + playSound(56, i); + + playActorAnimation(905, true); + playAnimation(904, 1, true); + dialog->show(184, scene, 903, 902, textColorJohnNoty, textColorMansionGuard, 2, 3); + showActor(); +} + +void TeenAgentEngine::fnSixthMansionIntrusion() { + hideActor(); + loadScene(35, scene->getPosition()); + playAnimation(907, 2, true); + playAnimation(906, 3, true); + waitAnimation(); + dialog->show(185, scene, 908, 909, textColorMansionGuard, textColorJohnNoty, 2, 3); + dialog->show(186, scene, 910, 908, textColorJohnNoty, textColorMansionGuard, 3, 2); + loadScene(11, scene->getPosition()); + showActor(); + setOns(3, 51); + playAnimation(911, 1); + playAnimation(899, 1); + setFlag(dsAddr_johnNotyOutsideMansionDoorFlag, 1); + reloadLan(); + wait(200); + enableObject(8); + setLan(2, 8); +} +void TeenAgentEngine::fnTooDark() { + displayMessage(dsAddr_TooDarkMsg); // "It's too dark to see clearly" +} + +bool TeenAgentEngine::fnIsCookGone() { + if (CHECK_FLAG(dsAddr_MansionCookGoneFlag, 1)) { return true; + } else { + displayMessage(dsAddr_cookAroundMsg); // "I can't do anything with this cook around" + return false; + } +} + +void TeenAgentEngine::fnEgoSuspiciousPosition() { + Common::Point p = scene->getPosition(); + if (p.x != 203 && p.y != 171) + moveTo(203, 169, 2); + else + moveTo(203, 169, 1); +} + +void TeenAgentEngine::fnGivingFlowerToOldLady() { + playSound(5, 2); + dialog->show(37, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); + playActorAnimation(537, true); + playAnimation(538, 0, true); + waitAnimation(); + wait(100); + dialog->show(38, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); +} + +void TeenAgentEngine::fnGiveAnotherFlowerToOldLady() { + dialog->pop(scene, dsAddr_dialogStackOldLady, 0, 523, textColorMark, textColorOldLady, 0, 1); +} + +void TeenAgentEngine::fnGivingFlowerToAnne() { + dialog->show(53, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + playSound(5, 10); + playActorAnimation(540, true); + playAnimation(539, 1, true); + waitAnimation(); + wait(100); + dialog->show(54, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + wait(50); + dialog->show(55, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + dialog->show(56, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + wait(50); + moveRel(0, 1, 0); + dialog->show(57, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + moveRel(0, -1, 0); + wait(50); +} + +void TeenAgentEngine::fnGiveAnotherFlowerToAnne() { + dialog->pop(scene, dsAddr_dialogStackAnotherFlowerToAnne, 0, 524, textColorMark, textColorAnne, 0, 2); +} + +void TeenAgentEngine::rejectMessage() { + uint i = _rnd.getRandomNumber(3); + switch (i) { + case 0: + displayMessage(dsAddr_rejectMsg0); // "I have no idea what to do with it" + break; + case 1: + displayMessage(dsAddr_rejectMsg1); // "I can't imagine what I could do with this" + break; + case 2: + displayMessage(dsAddr_rejectMsg2); // "I can't figure out what I should do with this" + break; + case 3: + displayMessage(dsAddr_rejectMsg3); // "I can't find any reason to mess with it" + break; + default: + error("rejectMessage() index out of range"); + break; + } +} + +bool TeenAgentEngine::processCallback(uint16 addr) { + if (addr == 0) + return false; + + debugC(0, kDebugCallbacks, "processCallback(%04x)", addr); + + bool retVal = true; + switch (addr) { + case csAddr_intro: // intro + fnIntro(); + break; + + case 0x3fed: + loadScene(3, Common::Point(305, 104)); + scene->setOrientation(4); + break; + + case 0x4007: + loadScene(5, Common::Point(300, 131)); + scene->setOrientation(3); + break; case 0x4021: - //pulling out mysterious object - if (CHECK_FLAG(0xdbe1, 1)) { + // pulling out mysterious object + if (CHECK_FLAG(dsAddr_cutFenceFlag, 1)) { playActorAnimation(844); playActorAnimation(846); playActorAnimation(845); - displayMessage(0x5696); + displayMessage(dsAddr_pullObjMsg1); // "I can't pull it out" } else { - displayMessage(0x570f); + displayMessage(dsAddr_pullObjMsg2); // "I can't reach it" } - return true; - - case 0x4094: //climbing to the pole near mudpool - if (CHECK_FLAG(0xDBE4, 1)) { - displayMessage(0x57b2); - return true; + break; + + case 0x4048: + displayMessage(dsAddr_dontWantToTouchMsg); // "I don't want to touch it - I might get hurt" + break; + + case 0x404f: + displayMessage(dsAddr_notWantToSleepMsg); // "I don't want to sleep" + break; + + case 0x4056: + // FIXME - This is the bird use callback in the first act at + // the mudpool. Current Code based on behaviour. Need to analyse cseg data. + dialog->popMark(scene, dsAddr_dialogStackMudpoolBird); + break; + + case 0x4060: + loadScene(2, Common::Point(28, 180)); + scene->setOrientation(2); + break; + + case 0x407a: + loadScene(4, Common::Point(297, 128)); + scene->setOrientation(4); + break; + + case 0x4094: // climbing to the pole near mudpool + if (CHECK_FLAG(dsAddr_gotMugOfMudFlag, 1)) { + displayMessage(dsAddr_poleClimbDoneMsg); // "Never Again!" } else { for (byte i = 11; i <= 27; i += 4) playSound(76, i); @@ -196,39 +574,49 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(865, 1); playActorAnimation(866); //InventoryObject *obj = inventory->selectedObject(); - //if (obj != NULL && obj->id == 0x55) { + //if (obj != NULL && obj->id == kInvItemMug) { - //implement pause and using real object: - if (inventory->has(0x55)) { + // FIXME: implement pause in mudpool and using of Mug object, as per original interpreter + if (inventory->has(kInvItemMug)) { playSound(5, 4); playSound(5, 19); playSound(64, 11); playActorAnimation(867); - inventory->remove(0x55); - inventory->add(0x56); + inventory->remove(kInvItemMug); + inventory->add(kInvItemMugOfMud); moveTo(86, 195, 1, true); playActorAnimation(868); - SET_FLAG(0xDBE4, 1); + SET_FLAG(dsAddr_gotMugOfMudFlag, 1); } else { - processCallback(0x4173); - Dialog::pop(scene, 0xDB72, 0, 0, 0xd1, 0xd1, 0, 0); + fnPoleClimbFail(); + dialog->popMark(scene, dsAddr_dialogStackFallIntoMudpool); } - return true; } - case 0x4173: - //fail! - moveTo(86, 195, 1, true); - playActorAnimation(868); - return true; + break; + + case csAddr_poleClimbFail: + fnPoleClimbFail(); + break; - case 0x419c: //getting the bird + case 0x4195: + displayMessage(dsAddr_preferWaterMsg); // "I prefer water" + break; + + case 0x419c: // getting the bird setOns(0, 0); playSound(56, 10); playActorAnimation(875); disableObject(6); - inventory->add(0x5c); - return true; + inventory->add(kInvItemBird); + break; + case 0x41c3: + displayMessage(dsAddr_pullObjMsg2); // "I can't reach it" + break; + + case 0x41ca: + rejectMessage(); + break; case 0x41ce: moveTo(197, 159, 4); @@ -236,9 +624,23 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(71, 8); playActorAnimation(833); moveTo(225, 159, 4); - inventory->add(0x4e); + inventory->add(kInvItemDelicatePlant); disableObject(3); - return true; + break; + + case 0x422c: + displayMessage(dsAddr_tooWeakToClimbMsg); // "I'm too weak to climb it" + break; + + case 0x4233: + loadScene(3, Common::Point(216, 199)); + scene->setOrientation(1); + break; + + case 0x424d: + loadScene(5, Common::Point(18, 174)); + scene->setOrientation(2); + break; case 0x4267: hideActor(); @@ -252,92 +654,111 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(5, 26); playActorAnimation(842); wait(100); - //shown in different positions - displayMessage(0x5656, 0xd1, 0x5510); + // shown in different positions + displayMessage(dsAddr_fnMsg2, textColorMark, 16, 68); // "And how am I supposed to get back?" wait(50); - displayMessage(0x567a, 0xd1, 0x555c); + displayMessage(dsAddr_fnMsg3, textColorMark, 92, 68); // "Great" wait(50); - displayMessage(0x5682, 0xd1, 0x553e); + displayMessage(dsAddr_fnMsg4, textColorMark, 62, 68); // "Oh, yeah, right" wait(50); playActorAnimation(843); showActor(); moveTo(223, 149, 0, true); disableObject(7); disableObject(1); - inventory->add(0x51); - displayMessage(0x5646); - return true; + inventory->add(kInvItemShovelAct1); + displayMessage(dsAddr_fnMsg1); // "Piece of cake" + break; + + case 0x433a: + loadScene(10, Common::Point(294, 183)); + scene->setOrientation(4); + break; + + case 0x4354: + loadScene(4, Common::Point(300, 185)); + scene->setOrientation(4); + break; + + case 0x436e: + loadScene(2, Common::Point(219, 199)); + scene->setOrientation(1); + break; case 0x4388: playSound(80, 4); playActorAnimation(961); loadScene(8, 155, 199, 1); - return true; + break; - case 0x43b5: //HQ, first trial - prison + case 0x43b5: // HQ, first trial - prison playSound(70, 6); playActorAnimation(962); loadScene(7, 30, 184, 2); - if (res->dseg.get_byte(0xDBDF) < 2) { + if (res->dseg.get_byte(dsAddr_FirstActTrialState) < 2) { wait(150); moveTo(134, 167, 2); - displayMessage(0x54f7); + displayMessage(dsAddr_firstTrialMsg); // "Sir, I'm Mark. A rookie" setLan(1, 0); playAnimation(812, 0, true); playActorAnimation(811); - Dialog::show(scene, 0x6117, 0, 813, 0xd1, 0xec, 0, 1); + dialog->show(148, scene, 0, 813, textColorMark, textColorCaptain, 0, 1); loadScene(6, 230, 184); playMusic(5); - Dialog::show(scene, 0x626a, 0, 814, 0xd1, 0xec, 0, 1); + dialog->show(149, scene, 0, 814, textColorMark, textColorCaptain, 0, 1); playSound(4, 14); playAnimation(815, 0); setOns(1, 0); - Dialog::showMono(scene, 0x62dc, 0, 0xd1, 0); + dialog->showMono(150, scene, 0, textColorMark, 0); - SET_FLAG(0xDBDF, 1); + SET_FLAG(dsAddr_FirstActTrialState, 1); } - return true; + break; case 0x4482: - if (CHECK_FLAG(0xDBDF, 0)) { + if (CHECK_FLAG(dsAddr_FirstActTrialState, 0)) { playActorAnimation(968); - displayMessage(0x5511); + displayMessage(dsAddr_lockedMsg); // "It's Locked!" } else { playSound(80, 3); playSound(79, 4); playActorAnimation(968); loadScene(6, 280, 186, 4); } - return true; + break; - case 0x44fc: //pull out spring from bed + case 0x44fc: // pull out spring from bed playSound(53, 25); playSound(24, 27); playSound(5, 36); playActorAnimation(839); moveTo(278, scene->getPosition().y, 0, true); - inventory->add(0x50); + inventory->add(kInvItemSpring); disableObject(1); - return true; + break; case 0x44cb: - if (CHECK_FLAG(0xDBE5, 1)) { - displayMessage(0x57c0); + if (CHECK_FLAG(dsAddr_gotRopeAct1Flag, 1)) { + displayMessage(dsAddr_vacMsg); // "What am I? A vacuum cleaner?!" } else { playSound(49, 14); playSound(5, 21); playActorAnimation(869); - inventory->add(0x58); - SET_FLAG(0xDBE5, 1); + inventory->add(kInvItemRopeAct1); + SET_FLAG(dsAddr_gotRopeAct1Flag, 1); } - return true; + break; + + case 0x4532: + displayMessage(dsAddr_springPrickMsg); // "The springs would prick my back" + break; - case 0x4539: //prison cell: use crates - if (CHECK_FLAG(0xdbdd, 2)) { - //finished the meal - trap - displayMessage(0x55c0); + case 0x4539: // prison cell: use crates + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 2)) { + // finished the meal - trap + displayMessage(dsAddr_mealFinishedMsg); // "Hey! I finished my meal." moveTo(306, 196, 2); wait(50); //playAnimation(825, 1); //very long empty animation. what for? @@ -354,66 +775,65 @@ bool TeenAgentEngine::processCallback(uint16 addr) { loadScene(6, scene->getPosition()); setOns(3, 0x5b); wait(50); - displayMessage(0x55db); - SET_FLAG(0xdbdd, 3); + displayMessage(dsAddr_bowlWeldedMsg); // "Wow. He got welded to the bowl" + SET_FLAG(dsAddr_JailCableAndBowlState, 3); scene->getObject(4)->setName("body"); } else { - if (Dialog::pop(scene, 0xdb5c, 0, 0, 0xd1, 0xd1, 0, 0) != 0x636b) //not 'im getting hungry' - return true; - - wait(100); - playSound(52, 8); - playSound(52, 13); - playAnimation(820, 1); - setOns(3, 0x59); - wait(50); - moveTo(scene->getPosition().x, scene->getPosition().y + 1, 3); - wait(150); - moveTo(scene->getPosition().x, scene->getPosition().y - 1, 2); - wait(100); - displayMessage(0x551f); - enableObject(4); - SET_FLAG(0xdbdc, 1); + if (dialog->pop(scene, dsAddr_dialogStackJailDoorGrates, 0, 0, textColorMark, textColorMark, 0, 0) == 0x636b) { // 'im getting hungry' + wait(100); + playSound(52, 8); + playSound(52, 13); + playAnimation(820, 1); + setOns(3, 0x59); + wait(50); + moveTo(scene->getPosition().x, scene->getPosition().y + 1, 3); + wait(150); + moveTo(scene->getPosition().x, scene->getPosition().y - 1, 2); + wait(100); + displayMessage(dsAddr_ThanksMsg); // "Thanks." + enableObject(4); + SET_FLAG(dsAddr_GotFoodBowlInJailFlag, 1); + } } - return true; + break; case 0x4662: - if (CHECK_FLAG(0xDBDD, 3)) { - if (CHECK_FLAG(0xDBDE, 1)) { - displayMessage(0x5608); + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 3)) { + if (CHECK_FLAG(dsAddr_GotJailKeyFlag, 1)) { + displayMessage(dsAddr_noPocketMsg); // "I don't want to touch his pockets again." } else { moveTo(280, 179, 2); playSound(49, 7); playSound(5, 17); playActorAnimation(827); - inventory->add(0x4d); - SET_FLAG(0xDBDE, 1); + inventory->add(kInvItemJailKey); + SET_FLAG(dsAddr_GotJailKeyFlag, 1); } } else - displayMessage(0x5905); - return true; + displayMessage(dsAddr_foodAliveMsg); // "No, thanks. This food seems still alive" + break; - case 0x46af: //prison cell: use live cable - if (CHECK_FLAG(0xdbdc, 1)) { - displayMessage(0x555d); + case 0x46af: // prison cell: use live cable + if (CHECK_FLAG(dsAddr_GotFoodBowlInJailFlag, 1)) { + displayMessage(dsAddr_ideaMsg); // "That gives me an idea" setOns(2, 0); playActorAnimation(821); setOns(2, 0x5a); setOns(3, 0); playSound(22, 2); playActorAnimation(822); - displayMessage(0x5577); + displayMessage(dsAddr_checkWorksMsg); // "Now I got to check if it works" disableObject(5); - SET_FLAG(0xdbdd, 1); + SET_FLAG(dsAddr_JailCableAndBowlState, 1); } else - displayMessage(0x5528); - return true; + displayMessage(dsAddr_unkUsageMsg); // "I don't have any idea what to do with it right now" + break; - case 0x4705: //prison: getting lamp bulb + case 0x4705: // prison: getting lamp bulb wait(50); moveTo(144, 185, 4); playSound(56, 15); - setOns(0, 86); //hiding lamp + setOns(0, 86); // hiding lamp playActorAnimation(816, true); playAnimation(817, 0, true); waitAnimation(); @@ -430,69 +850,102 @@ bool TeenAgentEngine::processCallback(uint16 addr) { disableObject(6); enableObject(5); - inventory->add(0x4c); - return true; + inventory->add(kInvItemBulb); + break; - case 0x4794: //prison cell door - if (res->dseg.get_byte(0xDBDF) >= 2) { + case 0x4794: // prison cell door + if (res->dseg.get_byte(dsAddr_FirstActTrialState) >= 2) { loadScene(5, 287, 143); } else { - displayMessage(0x592f); + displayMessage(dsAddr_doorClosedMsg); // "The door is closed. What a surprise." } - return true; + break; - case 0x47bc: //prison: examining trash can + case 0x47bc: // prison: examining trash can playSound(79, 5); playSound(1, 14); playActorAnimation(966); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; - case 0x47db: //prison: use switch - if (CHECK_FLAG(0xDBDF, 1)) { + case 0x47db: // prison: use switch + if (CHECK_FLAG(dsAddr_FirstActTrialState, 1)) { playSound(71, 4); playActorAnimation(823); - if (CHECK_FLAG(0xDBDD, 0)) { - displayMessage(0x4d80); + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 0)) { + displayMessage(dsAddr_NotHappenMsg); // "Nothing happened" } else { playSound(74, 1); playAnimation(824, 1); - if (CHECK_FLAG(0xDBDD, 1)) { + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 1)) { wait(100); - displayMessage(0x559a); - SET_FLAG(0xDBDD, 2); + displayMessage(dsAddr_timeToCallMsg); // "I think it is time to call captain" + SET_FLAG(dsAddr_JailCableAndBowlState, 2); } } } else { - displayMessage(0x52f6); + displayMessage(dsAddr_nahMsg); // "Nah" } - return true; + break; + + case 0x4836: + rejectMessage(); + break; case 0x4871: playActorAnimation(965); - displayMessage(0x5511); - return true; + displayMessage(dsAddr_lockedMsg); // "It's Locked!" + break; + + case 0x487e: + displayMessage(dsAddr_geographyClassMsg); // "I should have paid more attention in geography classes." + break; + + case 0x4885: + displayMessage(dsAddr_dontNeedMessMsg); // "I don't need this mess" + break; + + case 0x488c: + displayMessage(dsAddr_seenSofterRocksMsg); // "Thanks, but I've seen softer rocks" + break; - case 0x4893: //taking pills - if (CHECK_FLAG(0xDBE6, 1)) { - SET_FLAG(0xDBE6, 2); + case 0x4893: // taking pills + if (CHECK_FLAG(dsAddr_captainDrawerState, 1)) { + SET_FLAG(dsAddr_captainDrawerState, 2); setOns(1, 0x67); playSound(5, 9); playActorAnimation(872); - inventory->add(0x5a); + inventory->add(kInvItemMedicine); disableObject(7); } else { playActorAnimation(964); - displayMessage(0x5511); + displayMessage(dsAddr_lockedMsg); // "It's Locked!" } - return true; + break; + + case 0x48d4: + displayMessage(dsAddr_tooBluntMsg); // "They are too blunt to be of any use" + break; + + case 0x48db: + displayMessage(dsAddr_uselessModelsMsg); // "What's the use of the models?" + break; + + case 0x48e2: + case 0x48e6: + rejectMessage(); + break; + + case 0x4911: + displayMessage(dsAddr_barmanWillNoticeMsg); // "The barman will surely notice its disappearing" + break; - case 0x4918: //talking with barmen - if (CHECK_FLAG(0xDBE7, 1)) { + case 0x4918: // talking with barmen + if (CHECK_FLAG(dsAddr_birdOnBarRadioAntennaFlag, 1)) { moveTo(140, 152, 1); - if (CHECK_FLAG(0xDBE8, 1)) { - Dialog::showMono(scene, 0x6f20, 0, 0xd1, 0); //aren't you thirsty? - displayMessage(0x5883, 0xef, 21472); + if (CHECK_FLAG(dsAddr_swappedBarmanMugFlag, 1)) { + dialog->showMono(177, scene, 0, textColorMark, 0); + displayMessage(dsAddr_yeahRightMsg, textColorBarman, 32, 67); // "Yeah right!" //reloadLan(); setLan(1, 0); playAnimation(882, 0); @@ -504,86 +957,145 @@ bool TeenAgentEngine::processCallback(uint16 addr) { shakeScreen(); disableObject(1); disableObject(2); - SET_FLAG(0xDBE9, 1); + SET_FLAG(dsAddr_barmanPassedOutFlag, 1); } else - displayMessage(0x5855); + displayMessage(dsAddr_talkNotNowMsg); // "I've got no reason to talk to him right now." } else { - if (CHECK_FLAG(0xDBDF, 3)) { - if (CHECK_FLAG(0xDBE3, 1)) { - Dialog::show(scene, 0x6BD6, 0, 857, 0xd1, 0xef, 0, 1); + if (CHECK_FLAG(dsAddr_FirstActTrialState, 3)) { + if (CHECK_FLAG(dsAddr_spokeToBarmanAboutThirdTrialFlag, 1)) { + dialog->show(168, scene, 0, 857, textColorMark, textColorBarman, 0, 1); } else { - Dialog::show(scene, 0x69B5, 0, 857, 0xd1, 0xef, 0, 1); //taking mug + dialog->show(166, scene, 0, 857, textColorMark, textColorBarman, 0, 1); // taking mug playActorAnimation(859, true); playAnimation(858, 0, true); waitAnimation(); playSound(75, 6); playActorAnimation(860); - Dialog::show(scene, 0x69C2, 0, 857, 0xd1, 0xef, 0, 1); - inventory->add(0x55); - SET_FLAG(0xDBE3, 1); - SET_FLAG(0xDBF0, 0); + dialog->show(167, scene, 0, 857, textColorMark, textColorBarman, 0, 1); + inventory->add(kInvItemMug); + SET_FLAG(dsAddr_spokeToBarmanAboutThirdTrialFlag, 1); + SET_FLAG(dsAddr_gotPasswordNeedSpeakBarmanFlag, 0); } } else { - Dialog::pop(scene, 0xDB68, 0, 857, 0xd1, 0xef, 0, 1); + dialog->pop(scene, dsAddr_dialogStackBarman, 0, 857, textColorMark, textColorBarman, 0, 1); } } - return true; + break; - case 0x4f14: //use the hollow - displayMessage(CHECK_FLAG(0xDBA1, 1) ? 0x370f : 0x36c2); - return true; + case 0x4d7d: + case 0x4d81: + rejectMessage(); + break; + + case 0x4d89: + displayMessage(dsAddr_getRidOfGuardFirstMsg); // "If I want to get inside I must get rid of this guard first..." + break; + + case 0x4d90: + rejectMessage(); + break; + + case 0x4e47: + loadScene(13, Common::Point(9, 172)); + scene->setOrientation(2); + break; + + case 0x4e85: + loadScene(15, Common::Point(291, 162)); + scene->setOrientation(4); + break; + + case 0x4e9f: + loadScene(12, Common::Point(310, 152)); + scene->setOrientation(4); + break; + + case 0x4f14: // use the hollow + if (CHECK_FLAG(dsAddr_mansionTreeHollowEmptyFlag, 1)) + displayMessage(dsAddr_totalEmptyMsg); // "I can see it's totally empty" + else + displayMessage(dsAddr_noHandsMsg); // "I'd better not put my hands in there..." + break; case 0x4a64: - if (CHECK_FLAG(0xDBF0, 1)) { - displayMessage(0x5e25); - } else { + if (CHECK_FLAG(dsAddr_gotPasswordNeedSpeakBarmanFlag, 1)) + displayMessage(dsAddr_firstBusinessMsg); // "First I've got some business to take care of" + else loadScene(5, 35, 162); - } - return true; + break; case 0x4bf5: playActorAnimation(959); loadScene(8, 40, 152, 3); - return true; + break; + + case 0x4c18: + rejectMessage(); + break; + + case 0x4c29: + displayMessage(dsAddr_tooManyToSearchMsg); // "There are too many of them to search" + break; + + case 0x4c30: + case 0x4c37: + displayMessage(dsAddr_captainWouldNotFitMsg); // "Captain surely wouldn't fit them. I must look elsewhere" + break; case 0x483a: - Dialog::pop(scene, 0xdb82, 0, 0, 0xd1, 0xd1, 0, 0); - return true; + dialog->popMark(scene, dsAddr_dialogStackInterrogateCaptain); + break; case 0x4844: playSound(80, 4); playActorAnimation(963); loadScene(5, 166, 158); - return true; + break; case 0x48ea: setOns(0, 0); playSound(5, 9); playActorAnimation(836); - inventory->add(0x4f); + inventory->add(kInvItemSwissArmyKnife); disableObject(12); - return true; + break; case 0x4a8c: - if (CHECK_FLAG(0xDBE9, 1)) { + if (CHECK_FLAG(dsAddr_barmanPassedOutFlag, 1)) { playSound(89, 5); playActorAnimation(958); loadScene(9, 240, 182, 4); - } else if (CHECK_FLAG(0xDBE7, 1)) { - displayMessage(0x5894); + } else if (CHECK_FLAG(dsAddr_birdOnBarRadioAntennaFlag, 1)) { + displayMessage(dsAddr_barmanTooCloseMsg); // "The barman is too close" } else { - Dialog::pop(scene, 0xDB8A, 0, 857, 0xd1, 0xef, 0, 1); + dialog->pop(scene, dsAddr_dialogStackBarCellarDoor, 0, 857, textColorMark, textColorBarman, 0, 1); } - return true; + break; - case 0x4af4: //taking the crumbs + case 0x4aed: + displayMessage(dsAddr_tooBigMsg); // "It's too big and I doubt if I'll ever need it" + break; + + case 0x4af4: // taking the crumbs setOns(0, 0); playSound(49, 6); playSound(5, 13); playActorAnimation(861); - inventory->add(0x57); + inventory->add(kInvItemCrumbs); disableObject(6); - return true; + break; + + case 0x4b23: + rejectMessage(); + break; + + case 0x4b27: + displayMessage(dsAddr_tooMuchToDrinkMsg); // "It'd take too much time to drink it..." + break; + + case 0x4b2e: + displayMessage(dsAddr_notThiefMsg); // "I'm not a thief. And it's empty, by the way." + break; case 0x4b35: playSound(15, 7); @@ -591,140 +1103,149 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(55, 1); playSound(24, 12); playAnimation(885, 0); - Dialog::show(scene, 0x67e5, 886, 0, 0xd0, 0xd1, 1, 0); + dialog->show(164, scene, 886, 0, textColorJohnNoty, textColorMark, 1, 0); playMusic(3); loadScene(40, 198, 186, 1); - Dialog::show(scene, 0x7f20, 0, 920, 0xd1, 0xe7, 0, 1); + dialog->show(202, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); inventory->clear(); - inventory->add(0x1d); - displayCredits(0xe45c); + inventory->add(kInvItemSuperGlue); + displayCredits(dsAddr_credits5); loadScene(1, 198, 186); hideActor(); playActorAnimation(956); - Dialog::showMono(scene, 0x8bc4, 957, 0xd1, 1); + dialog->showMono(212, scene, 957, textColorMark, 1); waitAnimation(); loadScene(15, 157, 199, 1); playMusic(6); - return true; + break; - case 0x4c3e: //get the grenade + case 0x4c3e: // get the grenade playSound(32, 24); playActorAnimation(862); reloadLan(); playAnimation(863, 1); - inventory->add(0x54); + inventory->add(kInvItemGrenade); disableObject(1); - SET_FLAG(0xDBE2, 2); - return true; + SET_FLAG(dsAddr_act1GuardState, 2); + break; case 0x4c70: - if (CHECK_FLAG(0xDBE2, 0)) { - if (CHECK_FLAG(0xDBDA, 1)) { //papers are shown - Dialog::pop(scene, 0xDB4C, 0, 809, 0xd1, 0xd0, 0, 1); + if (CHECK_FLAG(dsAddr_act1GuardState, 0)) { + if (CHECK_FLAG(dsAddr_ShownPassToGuardFlag, 1)) { // papers are shown + dialog->pop(scene, dsAddr_dialogStackCampGuardReadingNews, 0, 809, textColorMark, textColorCampGuard, 0, 1); } else { - Dialog::pop(scene, 0xDB40, 0, 809, 0xd1, 0xd0, 0, 1); + dialog->pop(scene, dsAddr_dialogStackCampGuardWantsDocuments, 0, 809, textColorMark, textColorCampGuard, 0, 1); } } else { - displayMessage(0x5722); + displayMessage(dsAddr_helloQMsg); // "Hello?" wait(100); - displayMessage(0x572a); + displayMessage(dsAddr_totallyAddictedMsg); // "He's totally addicted" } - return true; + break; case 0x4c1c: playActorAnimation(960); - displayMessage(0x5511); - return true; + displayMessage(dsAddr_lockedMsg); // "It's Locked!" + break; + + case 0x4ca5: + displayMessage(dsAddr_chickenNeverMsg); // "Chickening? Me? Never!" + break; case 0x4cac: - if (CHECK_FLAG(0xdbda, 1)) { //papers are shown + if (CHECK_FLAG(dsAddr_ShownPassToGuardFlag, 1)) { // papers are shown loadScene(5, 124, 199); } else { - Dialog::show(scene, 0x5FE9, 0, 809, 0xd1, 0xd0, 0, 1); + dialog->show(144, scene, 0, 809, textColorMark, textColorCampGuard, 0, 1); moveTo(269, 175, 4); - Dialog::pop(scene, 0xDB56, 0, 809, 0xd1, 0xd0, 0, 1); + dialog->pop(scene, dsAddr_dialogStackCampGuardShowPass, 0, 809, textColorMark, textColorCampGuard, 0, 1); } - return true; - - case 0x4cf1: { //talking with mansion guard - SET_FLAG(0xda96, 1); - if (Dialog::pop(scene, 0xdaa6, 0, 529, 0xd1, 0xd9, 0, 1) != 0x1b4) - return true; + break; - Common::Point p = scene->getPosition(); - moveTo(189, 159, 0); - //waitLanAnimationFrame(1, 1); + case 0x4cf1: // talking with mansion guard + SET_FLAG(dsAddr_spokenWithMansionGuardFlag, 1); + if (dialog->pop(scene, dsAddr_dialogStackPleadingToMansionGuard, 0, 529, textColorMark, textColorMansionGuard, 0, 1) == 0x01b4) { // 2nd try + Common::Point p = scene->getPosition(); + moveTo(189, 159, 0); + //waitLanAnimationFrame(1, 1); - playSound(5, 2); - playSound(5, 19); - playActorAnimation(550, true); - playAnimation(551, 0, true); - waitAnimation(); + playSound(5, 2); + playSound(5, 19); + playActorAnimation(550, true); + playAnimation(551, 0, true); + waitAnimation(); - moveTo(p, 2); - inventory->add(0x13); - Dialog::pop(scene, 0xdaa6, 0, 529, 0xd1, 0xd9, 0, 1); - } - return true; + moveTo(p, 2); + inventory->add(kInvItemChocCandy); + dialog->pop(scene, dsAddr_dialogStackPleadingToMansionGuard, 0, 529, textColorMark, textColorMansionGuard, 0, 1); + } + break; - case 0x4d94: //talking with fatso - Dialog::show(scene, 0x33bd, 0, 666, 0xd1, 0xd0, 0, 2); - displayAsyncMessage(0x49ae, /*25060*/ 35000, 1, 10, 0xd0); + case 0x4d94: // talking with fatso + dialog->show(87, scene, 0, 666, textColorMark, textColorJohnNoty, 0, 2); + displayAsyncMessage(dsAddr_BribeMsg, 120, 109, 1, 10, textColorJohnNoty); // FIXME: Original (x,y) was (100, 78), rather than (120, 109)? playSound(5, 3); playAnimation(667, 1); playAnimation(668, 1); setOns(2, 50); - Dialog::show(scene, 0x36c7, 0, 666, 0xd1, 0xd0, 0, 2); + dialog->show(88, scene, 0, 666, textColorMark, textColorJohnNoty, 0, 2); setOns(3, 0); - setFlag(0xDBEC, 0); + setFlag(dsAddr_johnNotyOutsideMansionDoorFlag, 0); reloadLan(); playSound(82, 19); playAnimation(669, 1); - Dialog::showMark(scene, 0x3779); + dialog->showMark(89, scene); enableObject(15); disableObject(8); - return true; + break; case 0x4e61: loadScene(14, 280, 198); - return true; + break; case 0x4ee5: setOns(2, 0); playSound(5, 12); playActorAnimation(676); - displayMessage(0x4ab0); + displayMessage(dsAddr_WimpMsg); // "I'm a pathetic little wimp" disableObject(15); - inventory->add(51); - return true; + inventory->add(kInvItemBanknote); + break; case 0x4d56: - inventory->add(16); + inventory->add(kInvItemWhisky); disableObject(2); setOns(0, 0); playSound(5, 12); playActorAnimation(547); - return true; + break; + case 0x4d85: + rejectMessage(); + break; - case 0x4eb9://Pick up wrapper + case 0x4eb9: // Pick up wrapper playSound(5, 12); playSound(5, 18); - inventory->add(0x12); + inventory->add(kInvItemWrapper); setOns(1, 0); playActorAnimation(549); disableObject(13); - return true; + break; + + case 0x4ee1: + rejectMessage(); + break; case 0x4f25: playActorAnimation(967); - displayMessage(0x3542); - return true; + displayMessage(dsAddr_tooHardWoodMsg); // "This wood is too hard to break" + break; - case 0x4f32: //use tree near the mansion - if (CHECK_FLAG(0xDBA1, 1)) { - if (CHECK_FLAG(0xDBA2, 1)) { - displayMessage(0x3766); + case 0x4f32: // use tree near the mansion + if (CHECK_FLAG(dsAddr_mansionTreeHollowEmptyFlag, 1)) { + if (CHECK_FLAG(dsAddr_climbedMansionTreeAlreadyFlag, 1)) { + displayMessage(dsAddr_noChanceMsg); // "I won't take my chances a second time" } else { playSound(26, 13); playSound(26, 15); @@ -739,95 +1260,126 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(60, 16); playActorAnimation(591); wait(50); - displayMessage(0x372e); - SET_FLAG(0xDBA2, 1); - processCallback(0x9d45); + displayMessage(dsAddr_oneSmallStepMsg); // "One small step for man, one big pain in the head" + SET_FLAG(dsAddr_climbedMansionTreeAlreadyFlag, 1); + fnMansionIntrusionAttempt(); } } else { playActorAnimation(49); playSound(56, 8); playSound(56, 12); playSound(49, 10); - displayAsyncMessage(0x4652, 31579, 16, 24); + displayAsyncMessage(dsAddr_laughterMsg, 219, 98, 16, 24); // "(laughter)" playActorAnimation(587); moveRel(0, 0, 2); wait(100); - displayMessage(0x3668); + displayMessage(dsAddr_tickledMsg); // "Something tickled me!" } - return true; + break; - case 0x500d: //picking up wild plant - if (CHECK_FLAG(0xDB9E, 1)) { - displayMessage(0x35E8); //there are no more + case 0x500d: // picking up wild plant + if (CHECK_FLAG(dsAddr_gotPotatoAlreadyFlag, 1)) { + displayMessage(dsAddr_noPotatoMsg); // "There are no more potatoes" } else { - SET_FLAG(0xDB9E, 1); + SET_FLAG(dsAddr_gotPotatoAlreadyFlag, 1); setOns(2, 0); playSound(21, 9); playSound(34, 21); playSound(26, 30); playActorAnimation(552); setOns(2, 0x12); - inventory->add(0x14); + inventory->add(kInvItemPotato); } - return true; + break; + + case 0x505f: + displayMessage(dsAddr_wallTooSmoothMsg); // "The wall surface is too smooth to climb" + break; + + case 0x5066: + loadScene(11, Common::Point(183, 109)); + scene->setOrientation(3); + break; + + case 0x5080: + loadScene(13, Common::Point(290, 181)); + scene->setOrientation(4); + break; + + case 0x50f6: + displayMessage(dsAddr_tooMuchResinToClimbMsg); // "I could climb it if there wasn't so much resin" + break; + + case 0x50fd: + displayMessage(dsAddr_onlyGreenRectMsg); // "The only green stuff that I like is that rectangular piece of paper with..." + break; case 0x5104: loadScene(11, 319, 198, 4); - if (CHECK_FLAG(0xDB9C, 1)) - return true; - - //guard's drinking - SET_FLAG(0, 3); - setTimerCallback(0x516d, 40); - playAnimation(544, 0, true, true); //ignore busy flag for this animation - return true; + if (!CHECK_FLAG(dsAddr_scaredGuardAlreadyFlag, 1)) { + // guard is drinking + SET_FLAG(dsAddr_timedCallbackState, 3); + setTimerCallback(csAddr_guardScareTimeout, 40); + playAnimation(544, 0, true, true); // ignore busy flag for this animation + } + break; - case 0x516d: //too late to scare guard, resetting - SET_FLAG(0, 0); - return true; + case csAddr_guardScareTimeout: // too late to scare guard, resetting + SET_FLAG(dsAddr_timedCallbackState, 0); + break; - case 0x5189: //guard's drinking, boo! - SET_FLAG(0, 0); - setTimerCallback(0, 0); - scene->getAnimation(0)->free(); - SET_FLAG(0xDB9C, 1); + case csAddr_guardDrinking: + fnGuardDrinking(); + break; - displayAsyncMessage(0x3563, 320 * 130 + 300, 1, 5); - setOns(0, 16); - enableObject(2); + case 0x51c8: + displayMessage(dsAddr_wallTooSmoothMsg); // "The wall surface is too smooth to climb" + break; - playSound(17, 5); - playAnimation(545, 0); + case 0x51cf: + loadScene(12, Common::Point(15, 189)); + scene->setOrientation(2); + break; - Dialog::show(scene, 0x0917, 0, 546, 0xd1, 0xd9, 0, 1); - SET_FLAG(0xDA96, 1); - SET_FLAG(0xDA97, 0); - return true; + case 0x51e9: + displayMessage(dsAddr_dontWannaTouchHedgehogMsg); // "I don't wanna touch it. Its spines could hurt my delicate hands" + break; case 0x51f0: setOns(0, 0); playSound(5, 11); playActorAnimation(637); disableObject(7); - inventory->add(49); - return true; + inventory->add(kInvItemRock); + break; case 0x5217: - displayMessage(CHECK_FLAG(0xDB9F, 1) ? 0x402e : 0x34e1); - return true; + if (CHECK_FLAG(dsAddr_beesGoneFlag, 1)) + displayMessage(dsAddr_notHungryMsg); // "Thanks, I'm not hungry" + else + displayMessage(dsAddr_avoidBeesMsg); // "I'm going to stay at least five meters away from these bees!" + break; + + case 0x522c: + displayMessage(dsAddr_avoidBeesMsg); // "I'm going to stay at least five meters away from these bees!" + break; + + case 0x5233: + rejectMessage(); + break; case 0x5237: - if (!CHECK_FLAG(0xDB9F, 1)) { - displayMessage(0x34e1); - } else if (CHECK_FLAG(0xDBA0, 1)) - displayMessage(0x3E31); + if (!CHECK_FLAG(dsAddr_beesGoneFlag, 1)) { + displayMessage(dsAddr_avoidBeesMsg); // "I'm going to stay at least five meters away from these bees!" + } else if (CHECK_FLAG(dsAddr_mansionTunnelDoneFlag, 1)) + displayMessage(dsAddr_roadNowhereMsg); // "Nah. It's a road to nowhere" else { moveTo(173, 138, 2); playSound(28, 5); playActorAnimation(583); playActorAnimation(584); - loadScene(0, 0, 0, 0); //clear background + loadScene(0, 0, 0, 0); // clear background playSound(72, 18); playSound(73, 39); @@ -837,82 +1389,257 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(28, 2); playActorAnimation(586); moveTo(138, 163, 3); - displayMessage(0x3650); - SET_FLAG(0xDBA0, 1); - processCallback(0x9d45); //another mansion try + displayMessage(dsAddr_lifeBrutalMsg); // "Life is really brutal" + SET_FLAG(dsAddr_mansionTunnelDoneFlag, 1); + fnMansionIntrusionAttempt(); } - return true; - - case 0x55a8: { - uint16 d = Dialog::pop(scene, 0xdb08, 0, 0, 0xd1, 0xd1, 0, 0); - if (d == 0x2c5d) { - waitLanAnimationFrame(1, 0x23); - setOns(0, 0); - playSound(52, 9); - playSound(52, 11); - playSound(52, 13); - playSound(53, 32); - playAnimation(570, 0); - wait(50); - displayMessage(0x551f); - disableObject(5); - SET_FLAG(0xDBB0, 1); - } else if (d != 0x2c9b) { - waitLanAnimationFrame(1, 0x23); - playSound(52, 9); - playSound(52, 11); - playSound(52, 13); - playAnimation(569, 0); + break; + + case 0x5320: + loadScene(11, Common::Point(30, 124)); + scene->setOrientation(2); + break; + + case 0x533a: + displayMessage(dsAddr_noLongHandsMsg); // "I really don't have such long hands" + break; + + case 0x5341: + displayMessage(dsAddr_tooFarToSwimMsg); // "It's too far to swim there" + break; + + case 0x5403: + displayMessage(dsAddr_noBucketMsg); // "It's not a barrel-organ. And there's no bucket." + break; + + case 0x540a: + loadScene(20, Common::Point(10, 185)); + scene->setOrientation(2); + break; + + case 0x5424: + loadScene(11, Common::Point(30, 170)); + scene->setOrientation(2); + break; + + case 0x543e: + loadScene(18, Common::Point(224, 199)); + scene->setOrientation(4); + break; + + case 0x5547: + loadScene(15, Common::Point(15, 172)); + scene->setOrientation(2); + break; + + case 0x55a8: + { + uint16 d = dialog->popMark(scene, dsAddr_dialogStackSquirrel); + if (d == 0x2c5d) { // 4th try - Throw Nut + waitLanAnimationFrame(1, 0x23); + setOns(0, 0); + playSound(52, 9); + playSound(52, 11); + playSound(52, 13); + playSound(53, 32); + playAnimation(570, 0); + wait(50); + displayMessage(dsAddr_ThanksMsg); // "Thanks." + disableObject(5); + SET_FLAG(dsAddr_squirrelNutState, 1); + } else if (d != 0x2c9b) { // 5th (last) try + waitLanAnimationFrame(1, 0x23); + playSound(52, 9); + playSound(52, 11); + playSound(52, 13); + playAnimation(569, 0); + } } - } - return true; + break; case 0x5663: - displayMessage(CHECK_FLAG(0xDBB0, 1) ? 0x41b1 : 0x417e); - return true; + if (CHECK_FLAG(dsAddr_squirrelNutState, 1)) + displayMessage(dsAddr_findNutMsg); // "I won't find the nut just like that. The grass is too dense" + else + displayMessage(dsAddr_hmmGrassMsg); // "Hmmm. Grass..." + break; + + case 0x5674: + loadScene(18, Common::Point(94, 115)); + scene->setOrientation(3); + break; + + case 0x568e: + displayMessage(dsAddr_notHornyMsg); // "I'm not horny" + break; + + case 0x5695: + displayMessage(dsAddr_dontNeedToOpenMsg); // "I don't need to open it" + break; case 0x569c: playSound(67, 5); playActorAnimation(983); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; + + case 0x56b3: + rejectMessage(); + break; case 0x56b7: playSound(66, 5); playSound(67, 11); playActorAnimation(984); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; + + case 0x56d6: + displayMessage(dsAddr_CantJumpMsg); // "No way I can jump so high, cause, err, white men can't jump" + break; + + case 0x56dd: + displayMessage(dsAddr_dontNeedItMsg); // "I don't need it" + break; + + case 0x56e4: + displayMessage(dsAddr_notSantaClausMsg); // "I'm not Santa Claus" + break; + + case 0x56eb: + displayMessage(dsAddr_noPlasticImitationsMsg); // "I don't need plastic imitations" + break; + + case 0x56f2: + rejectMessage(); + break; + + case 0x5721: + displayMessage(dsAddr_dontNeedItMsg); // "I don't need it" + break; case 0x5728: - inventory->add(0x0d); + inventory->add(kInvItemChainsaw); disableObject(14); setOns(0, 0); playSound(5, 10); playActorAnimation(566); - return true; + break; + + case 0x574f: + displayMessage(dsAddr_tooFragileMsg); // "It's too fragile to carry around" + break; case 0x5793: - if (!CHECK_FLAG(0xDB94, 1)) { - displayMessage(0x3e63); - } else if (CHECK_FLAG(0xDB95, 1)) { - displayMessage(0x3e75); + if (!CHECK_FLAG(dsAddr_alreadyPulledTrunkReleaseLeverFlag, 1)) { + displayMessage(dsAddr_shutTightMsg); // "It's shut tight" + } else if (CHECK_FLAG(dsAddr_carTrunkEmptyFlag, 1)) { + displayMessage(dsAddr_bootEmptyMsg); // "There's nothing else in the boot" } else { - SET_FLAG(0xDB95, 1); + SET_FLAG(dsAddr_carTrunkEmptyFlag, 1); moveTo(188, 179, 0); playSound(7, 16); playActorAnimation(519); wait(150); moveTo(168, 179, 2); - inventory->add(3); + inventory->add(kInvItemToolboxFull); } - return true; + break; + + case 0x57fa: + displayMessage(dsAddr_dontNeedItMsg); // "I don't need it" + break; + + case 0x5801: + rejectMessage(); + break; + + case 0x583f: + case 0x5846: + displayMessage(dsAddr_dontNeedToOpenMsg); + break; + + case 0x584d: + displayMessage(dsAddr_pullObjMsg2); + break; + + case 0x5854: + loadScene(15, Common::Point(157, 199)); + scene->setOrientation(1); + break; + + case 0x586e: + loadScene(21, Common::Point(24, 187)); + scene->setOrientation(2); + break; + + case 0x5888: + loadScene(27, Common::Point(108, 199)); + scene->setOrientation(2); + break; + + case 0x5903: + displayMessage(dsAddr_keepItOpenMsg); // "I'd like to keep it open" + break; + + case 0x590a: + loadScene(20, Common::Point(304, 190)); + scene->setOrientation(4); + break; + + case 0x5924: + loadScene(25, Common::Point(298, 146)); + scene->setOrientation(4); + break; + + case 0x5978: + displayMessage(dsAddr_notTakingSocksMsg); // "I really don't want to walk around with someone else's socks" + break; + + case 0x597f: + case 0x5986: + case 0x598d: + displayMessage(dsAddr_dontNeedToOpenMsg); // "I don't need to open it" + break; + + case 0x5b44: + // FIXME - This is the doorbell use callback on House #2 + // i.e. Granny and Anne's House. Need to analyse cseg data properly. + // Current code inferred from behaviour. + // FIXME - Add animation call for Ego pushing doorbell. + displayMessage(dsAddr_ItsOpenMsg); + break; + + case 0x5c72: + displayMessage(dsAddr_notTiredMsg); // "Thanks, I'm not tired" + break; + + case 0x5c79: + displayMessage(dsAddr_dontNeedToOpenMsg); // "I don't need to open it" + break; + + case 0x5c80: + rejectMessage(); + break; + + case 0x5cdb: + case 0x5ce2: + displayMessage(dsAddr_dontNeedItMsg); // "I don't need it" + break; + + case 0x5ce9: + displayMessage(dsAddr_tooBigMsg); // "It's too big and I doubt if I'll ever need it" + break; + + case 0x5d1d: + displayMessage(dsAddr_CantJumpMsg); // "No way I can jump so high, cause, err, white men can't jump" + break; case 0x5d88: - if (CHECK_FLAG(0xDBA5, 1)) { //dry laundry - SET_FLAG(0xDBA5, 2); - Dialog::show(scene, 0x1F4F, 0, 523, 0xd1, 0xe5, 0, 1); - //waitLanAnimationFrame(1, 1); //another long waiting + if (CHECK_FLAG(dsAddr_laundryState, 1)) { // dry laundry + SET_FLAG(dsAddr_laundryState, 2); + dialog->show(46, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); + //waitLanAnimationFrame(1, 1); // another long waiting playAnimation(604, 0); loadScene(21, scene->getPosition()); @@ -924,184 +1651,393 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(0, 33); loadScene(23, scene->getPosition()); playAnimation(605, 0); - Dialog::show(scene, 0x2002, 0, 523, 0xd1, 0xe5, 0, 1); + dialog->show(47, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); } else { - uint16 d = Dialog::pop(scene, 0xdada, 0, 523, 0xd1, 0xe5, 0, 1); - if (d == 0x1913) { + uint16 d = dialog->pop(scene, dsAddr_dialogStackAskOldLadyOK, 0, 523, textColorMark, textColorOldLady, 0, 1); + if (d == 0x1913) { // 3rd time wait(100); moveRel(0, 0, 3); wait(50); - displayMessage(0x34d5); //I give up + displayMessage(dsAddr_giveUpMsg); // "I give up" wait(50); } } - return true; + break; + + case 0x5f9a: + case 0x5fa1: + displayMessage(dsAddr_dontNeedToOpenMsg); // "I don't need to open it" + break; - case 0x5ff3: //get duster - if (CHECK_FLAG(0xDB9A, 0)) { - Dialog::pop(scene, 0xdaf6, 0, 523, 0xd1, 0xe5, 0, 1); + case 0x5fa8: + displayMessage(dsAddr_CantJumpMsg); // "No way I can jump so high, cause, err, white men can't jump" + break; + + case 0x5faf: + displayMessage(dsAddr_noSecretPassageMsg); // "I don't think there's any secret passage inside" + break; + + case 0x5fe5: + displayMessage(dsAddr_jugMeMsg); // "They can jug me if I steal this" + break; + + case 0x5fec: + displayMessage(dsAddr_leaveFlowersAloneMsg); // "I'd better leave it. Women are really oversensitive about flowers." + break; + + case 0x5ff3: // get duster + if (CHECK_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 0)) { + dialog->pop(scene, dsAddr_dialogStackBorrowDusterFromOldLady, 0, 523, textColorMark, textColorOldLady, 0, 1); } else { - Dialog::show(scene, 0x1e1e, 0, 523, 0xd1, 0xe5, 0, 1); + dialog->show(43, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); wait(50); - inventory->add(12); + inventory->add(kInvItemFeatherDusterClean); disableObject(12); setOns(0, 0); playSound(5, 6); playActorAnimation(541); } - return true; + break; + + case 0x603a: + rejectMessage(); + break; case 0x603e: - if (CHECK_FLAG(0xDBB3, 1)) { - displayMessage(0x44a7); + if (CHECK_FLAG(dsAddr_spokenToMirrorFlag, 1)) { + displayMessage(dsAddr_busyThinkingMsg); // "I'd better not interrupt it's thought process" } else { - displayMessage(0x4412); + displayMessage(dsAddr_mirrorMirrorMsg); // "Mirror, Mirror on the wall...." wait(150); - displayMessage(0x444f); + displayMessage(dsAddr_thinkTooLongMsg); // "Hey, don't think too long" wait(150); - displayMessage(0x446b); + displayMessage(dsAddr_HintMaleMsg); // "A hint: Someone in this room, a male" wait(150); - displayMessage(0x4492); + displayMessage(dsAddr_okWaitMsg); // "OK, take your time" wait(150); - SET_FLAG(0xDBB3, 1); + SET_FLAG(dsAddr_spokenToMirrorFlag, 1); } - return true; + break; + + case 0x6074: + rejectMessage(); + break; + + case 0x6078: + displayMessage(dsAddr_tooBigMsg); // "It's too big and I doubt if I'll ever need it" + break; case 0x6205: - if (CHECK_FLAG(0xDBA4, 1)) - displayMessage(0x450e); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + displayMessage(dsAddr_tooHeavyMsg); // "It's too heavy. Not that I'm wimp" else - processCallback(0x61fe); - return true; + fnTooDark(); + break; case 0x6217: - if (CHECK_FLAG(0xDBA4, 1)) - displayMessage(0x44d6); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + displayMessage(dsAddr_noDentistsMsg); // "I don't want to have anything in common with dentists" else - processCallback(0x61fe); - return true; + fnTooDark(); + break; case 0x62c1: - if (CHECK_FLAG(0xDBA4, 1)) - return false; + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + fnTooDark(); + break; - processCallback(0x61fe); - return true; + case 0x634a: + displayMessage(dsAddr_noHandsSharpThornsMsg); // "I can't remove it with my hands. these thorns look really sharp" + break; + + case 0x637f: + loadScene(21, Common::Point(201, 199)); + scene->setOrientation(1); + break; + + case 0x6399: + displayMessage(dsAddr_rockWalkingGeeMsg); // "Yeah, great idea. Let's take this rock and walk around a bit. Gee..." + break; + + case 0x63a0: + case 0x63a7: + displayMessage(dsAddr_butterflyMsg); // "I'd better leave them alone, they make this place beautiful" + break; + + case 0x63ae: + displayMessage(dsAddr_notSureIfAliveMsg); // "I'm not sure if it's alive" + break; case 0x63bc: playMusic(6); loadScene(25, 151, 156, 2); - return true; + break; case 0x63dc: - Dialog::showMono(scene, 0x3375, 0, 0xd1, 0); - return true; + dialog->showMono(86, scene, 0, textColorMark, 0); + break; + + case 0x63e3: + displayMessage(dsAddr_holeTooNarrowMsg); // "The hole is too narrow to fit my hand" + break; case 0x646e: case 0x6475: - Dialog::showMono(scene, 0x32C1, 0, 0xd1, 0); - return true; + dialog->showMono(85, scene, 0, textColorMark, 0); + break; case 0x6479: - Dialog::showMono(scene, 0x325e, 0, 0xd1, 0); - return true; + dialog->showMono(84, scene, 0, textColorMark, 0); + break; case 0x6507: - if (CHECK_FLAG(0xDB96, 1)) { + if (CHECK_FLAG(dsAddr_birdsGoneFromScarecrowFlag, 1)) rejectMessage(); - } else - displayMessage(0x47e7); - return true; + else + displayMessage(dsAddr_birdAttackMsg); // "Hey You! Wake up! Bird attack!" + break; + + case 0x6541: + loadScene(20, Common::Point(10, 131)); + scene->setOrientation(3); + break; + + case 0x6635: + displayMessage(dsAddr_uninterestingHaystackMsg); // "I don't see anything interesting about this haystack" + break; + + case 0x666a: + displayMessage(dsAddr_moreComplicatedMsg); // "It's more complicated than that" + break; case 0x65c3: - if (CHECK_FLAG(0xDBA9, 1)) { + if (CHECK_FLAG(dsAddr_mouseHoleState, 1)) { playActorAnimation(635); setOns(5, 0); playSound(63, 11); playSound(15, 20); playSound(32, 31); playActorAnimation(636); - inventory->add(47); - inventory->add(48); + inventory->add(kInvItemHandkerchief); + inventory->add(kInvItemMouse); moveTo(scene->getPosition().x - 1, 139, 1, true); - displayMessage(0x3b83); - SET_FLAG(0xDBA9, 2); - SET_FLAG(0xDBA8, 0); + displayMessage(dsAddr_yikesMsg); // "Yikes!" + SET_FLAG(dsAddr_mouseHoleState, 2); + SET_FLAG(dsAddr_HankerchiefInMouseholeFlag, 0); } else - displayMessage(0x4808); - return true; + displayMessage(dsAddr_noSearchWarrantMsg); // "I don't have a search-warrant" + break; + + case 0x6671: + displayMessage(dsAddr_cantOpenItMsg); // "I can't open it" + break; + + case 0x6678: + rejectMessage(); + break; + + case 0x670f: + displayMessage(dsAddr_dontNeedThemMsg); // "I don't need them" + break; + + case 0x6716: + displayMessage(dsAddr_pullObjMsg2); // "I can't reach it" + break; + + case 0x6772: + loadScene(31, Common::Point(20, 188)); + scene->setOrientation(2); + break; + + case 0x678c: + loadScene(28, Common::Point(189, 153)); + scene->setOrientation(4); + break; + + case 0x67fa: + rejectMessage(); + break; + + case 0x67fe: + displayMessage(dsAddr_troubleWithStairsMsg); // "If I put it on I might have trouble walking up the stairs" + break; + + case 0x6911: + displayMessage(dsAddr_9LivesToReadMsg); // "I'd need 9 lives to read them all" + break; + + case 0x6954: + displayMessage(dsAddr_thanksNotTiredMsg); // "Thanks, I'm not so tired" + break; + + case 0x695b: + displayMessage(dsAddr_noNeedToTurnOnMsg); // "There's no need to turn it on" + break; + + case 0x6ba6: + displayMessage(dsAddr_wontBearWeightMsg); // "It won't bear my weight" + break; + + case 0x6bda: + displayMessage(dsAddr_peepingTomMsg); // "What am I? A Peeping Tom?" + break; + + case 0x6c1c: + case 0x6c20: + rejectMessage(); + break; + + case 0x6c24: + displayMessage(dsAddr_dontNeedThemMsg); // "I don't need them" + break; + + case 0x6c2b: + loadScene(29, Common::Point(300, 188)); + scene->setOrientation(4); + break; + + case 0x6c7c: + displayMessage(dsAddr_bigPocketsMsg); // "I have big pockets, but there are limits" + break; + + case 0x724e: + displayMessage(dsAddr_soSharpMsg); // "They're so sharp they'd rip my trousers!" + break; + + case 0x72be: + rejectMessage(); + break; + + case 0x7305: + rejectMessage(); + break; + + case 0x7328: + displayMessage(dsAddr_noTimeForPleasuresMsg); // "I don't have time for pleasures" + break; + + case 0x732f: + displayMessage(dsAddr_notSocksWithBareHandsMsg); // "I won't touch these socks with my bare hands!" + break; + + case 0x739c: + displayMessage(dsAddr_notHalloweenMsg); // "It's not Halloween" + break; + + case 0x7401: + displayMessage(dsAddr_NotManualMsg); // "It can't be controlled manually! I hate it!" + break; + + case 0x746f: + displayMessage(dsAddr_nothingToPlayMsg); // "I have nothing to play" + break; + + case 0x74b3: + loadScene(29, Common::Point(256, 171)); + scene->setOrientation(3); + break; + + case 0x74cd: + rejectMessage(); + break; + + case 0x74f9: + loadScene(38, Common::Point(160, 199)); + scene->setOrientation(1); + break; + + case 0x784a: + displayMessage(dsAddr_notMineMsg); // "I can't take it. It's not mine." + break; + + case 0x7851: + displayMessage(dsAddr_lockedMsg); // "It's Locked!" + break; + + case 0x7858: + displayMessage(dsAddr_lockedMsg); // "It's Locked!" + break; + + case 0x785f: + displayMessage(dsAddr_pullObjMsg2); // "I can't reach it" + break; case 0x7866: - if (CHECK_FLAG(0xdbdd, 3)) { - displayMessage(0x55ff); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_JailCableAndBowlState, 3)) + displayMessage(dsAddr_gotchaMsg); // "Gotcha" + else + retVal = false; + break; - case 0x7878: { - byte v = res->dseg.get_byte(0xDBDB) + 1; - if (v <= 6) - SET_FLAG(0xDBDB, v); + case 0x7878: + { + byte v = res->dseg.get_byte(dsAddr_graffitiMsgId) + 1; + if (v <= 6) + SET_FLAG(dsAddr_graffitiMsgId, v); - switch (v) { - case 1: - displayMessage(0x5411); - return true; - case 2: - displayMessage(0x5463); - return true; - case 3: - displayMessage(0x5475); - return true; - case 4: - displayMessage(0x5484); - return true; - case 5: - displayMessage(0x54c4); - return true; - default: - displayMessage(0x54d5); - return true; + switch (v) { + case 1: + displayMessage(dsAddr_SavingFineMsg); // "Saving is a very fine thing..." + break; + case 2: + displayMessage(dsAddr_loveCaptainMsg); // "I love captain" + break; + case 3: + displayMessage(dsAddr_soccerRulzMsg); // "Soccer rulz" + break; + case 4: + displayMessage(dsAddr_treeCutMsg); // "Don't cut the trees..." + break; + case 5: + displayMessage(dsAddr_visaAcceptedMsg); // "VISA Accepted" + break; + default: + displayMessage(dsAddr_otherGraffitiMsg); // "The rest of graffiti is obscene" + break; + } } - } + break; case 0x78a9: - if (CHECK_FLAG(0xDBE6, 1)) { - displayMessage(0x5827); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_captainDrawerState, 1)) + displayMessage(dsAddr_nowOpenMsg); // "Now it's open" + else + retVal = false; + break; case 0x78bb: - if (CHECK_FLAG(0xDBE8, 1)) { - displayMessage(0x58b0); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_swappedBarmanMugFlag, 1)) + displayMessage(dsAddr_yuckMsg); // "Yuck!" + else + retVal = false; + break; case 0x78ce: - if (!CHECK_FLAG(0xDBA1, 1)) { - displayMessage(0x3694); - return true; - } else - return false; + if (!CHECK_FLAG(dsAddr_mansionTreeHollowEmptyFlag, 1)) + displayMessage(dsAddr_monstersMsg); // "Who knows what monsters live in there" + else + retVal = false; + break; - case 0x792b: //left click on ann + case 0x792b: // left click on ann moveTo(245, 198, 1); - if (CHECK_FLAG(0xDBAF, 1)) - return false; - - Dialog::showMono(scene, 0x2193, 0, 0xd1, 0); - SET_FLAG(0xDBAF, 1); - return true; + if (!CHECK_FLAG(dsAddr_alreadySaidAnneBeautifulFlag, 1)) { + dialog->showMono(50, scene, 0, textColorMark, 0); + SET_FLAG(dsAddr_alreadySaidAnneBeautifulFlag, 1); + } else + retVal = false; + break; case 0x79c3: - if (CHECK_FLAG(0xDBA4, 1)) - return false; - processCallback(0x61fe); - return true; + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + fnTooDark(); + break; - case 0x7b26: //cutting the fence + case 0x7b26: // cutting the fence setOns(0, 0); playSound(5, 2); playSound(51, 11); @@ -1114,11 +2050,11 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(0, 0x60); moveTo(281, scene->getPosition().y, 0, true); disableObject(4); - SET_FLAG(0xDBE1, 1); - return true; + SET_FLAG(dsAddr_cutFenceFlag, 1); + break; - case 0x7b89: //digging mysterious object - if (CHECK_FLAG(0xDBE1, 1)) { + case 0x7b89: // digging mysterious object + if (CHECK_FLAG(dsAddr_cutFenceFlag, 1)) { playActorAnimation(844); setOns(1, 0); playSound(5, 5); @@ -1130,11 +2066,15 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(1, 0x64); playActorAnimation(845); disableObject(3); - inventory->add(0x52); - inventory->remove(0x51); + inventory->add(kInvItemKaleidoscope); + inventory->remove(kInvItemShovelAct1); } else - displayMessage(0x56da); - return true; + displayMessage(dsAddr_fenceBlocksMsg); // "The fence blocks the way" + break; + + case 0x7bf6: + displayMessage(dsAddr_noDiggingKnifeMsg); // "Digging it out with the knife could take a hundred years" + break; case 0x7bfd: playSound(76, 18); @@ -1149,7 +2089,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(76, 63); playActorAnimation(873); moveTo(240, 163, 4); - displayMessage(0x5837); + displayMessage(dsAddr_cmonBabyMsg); // "C'mon baby, it's all yours!" waitLanAnimationFrame(1, 0x22); playSound(77, 2); playSound(77, 12); @@ -1164,23 +2104,36 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setLan(1, 0); playAnimation(874, 1); setOns(0, 0x68); - inventory->remove(0x5b); + inventory->remove(kInvItemDruggedFood); enableObject(6); disableObject(1); - return true; + break; + + case 0x7cc9: + case 0x7cd0: + displayMessage(dsAddr_throwCrumbsToBirdQMsg); // "Should I throw the crumbs to the bird?" + break; + + case 0x7cd7: + displayMessage(dsAddr_dontWasteCrumbs); // "I don't want to waste these tasty crumbs" + break; - case 0x7ce5: //put spring on the solid ground + case 0x7cde: + displayMessage(dsAddr_mightSlipFallInMsg); // "Better not... I might slip and fall in..." + break; + + case 0x7ce5: // put spring on the solid ground playSound(5, 2); playSound(19, 11); playActorAnimation(840); setOns(1, 0x61); - inventory->remove(0x50); + inventory->remove(kInvItemSpring); disableObject(2); enableObject(7); - return true; + break; - case 0x7d1a: //captain's key + door - if (res->dseg.get_byte(0xDBDF) <= 1) { + case 0x7d1a: // captain's key + door + if (res->dseg.get_byte(dsAddr_FirstActTrialState) <= 1) { playSound(5, 2); playSound(57, 12); playSound(70, 19); @@ -1198,24 +2151,23 @@ bool TeenAgentEngine::processCallback(uint16 addr) { wait(200); playAnimation(0, 1); setOns(0, 0); - Dialog::showMono(scene, 0x63a5, 830, 0xd0, 1); + dialog->showMono(156, scene, 830, textColorShockedCaptain, 1); loadScene(7, 130, 195, 2); playMusic(4); setLan(1, 1); wait(100); - Dialog::show(scene, 0x6406, 0, 832, 0xd1, 0xec, 0, 1); + dialog->show(157, scene, 0, 832, textColorMark, textColorCaptain, 0, 1); //playAnimation(831, 1); - SET_FLAG(0xDBDF, 2); - + SET_FLAG(dsAddr_FirstActTrialState, 2); } else - displayMessage(0x52f6); - return true; + displayMessage(dsAddr_nahMsg); // "Nah" + break; - case 0x7e02: //tickling the captain - if (CHECK_FLAG(0xdbe0, 1)) { - displayMessage(0x5632); + case 0x7e02: // tickling the captain + if (CHECK_FLAG(dsAddr_AlreadyTickledCaptainFlag, 1)) { + displayMessage(dsAddr_doesNotWorkMsg); // "That doesn't work" } else { playSound(5, 6); playSound(27, 49); @@ -1224,37 +2176,37 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); setOns(0, 94); - Dialog::show(scene, 0x65e9, 0, 832, 0xd1, 0xec, 0, 1); + dialog->show(161, scene, 0, 832, textColorMark, textColorCaptain, 0, 1); enableObject(12); - SET_FLAG(0xdbe0, 1); + SET_FLAG(dsAddr_AlreadyTickledCaptainFlag, 1); } - return true; + break; - case 0x7e4f: //giving magazine to captain - Dialog::show(scene, 0x66c0, 0, 856, 0xd1, 0xec, 0, 1); + case 0x7e4f: // giving magazine to captain + dialog->show(162, scene, 0, 856, textColorMark, textColorCaptain, 0, 1); playSound(5, 3); playActorAnimation(852, true); playActorAnimation(853, true); - displayMessage(0x5742); - displayMessage(0x5757); - displayMessage(0x5770); - displayMessage(0x5782); - displayMessage(0x5799); + displayMessage(dsAddr_whatAboutMsg); // "What about a new" + displayMessage(dsAddr_hotOffMsg); // "hot off the press" + displayMessage(dsAddr_fullColorMsg); // "full-color" + displayMessage(dsAddr_specialEdMsg); // "special edition" + displayMessage(dsAddr_soldierNewsMsg); // "of Soldier News?!" playAnimation(856, 1); playSound(5, 3); //playActorAnimation(854); - Dialog::show(scene, 0x66fe, 0, 856, 0xd1, 0xec, 0, 1); + dialog->show(163, scene, 0, 856, textColorMark, textColorCaptain, 0, 1); playAnimation(855, 1); wait(200); moveTo(30, 181, 0); disableObject(1); setLan(1, 0); - SET_FLAG(0xDBDF, 3); - SET_FLAG(0xDBF0, 1); + SET_FLAG(dsAddr_FirstActTrialState, 3); + SET_FLAG(dsAddr_gotPasswordNeedSpeakBarmanFlag, 1); loadScene(8, 155, 199); - return true; + break; - case 0x7fbd: //using bird & bartender + case 0x7fbd: // using bird & bartender playSound(5, 3); playActorAnimation(876); setOns(1, 0); @@ -1263,15 +2215,15 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(877, 1); playAnimation(880, 1, true); - Dialog::show(scene, 0x6f0e, 0, 857, 0xd1, 0xef, 0, 1); + dialog->show(176, scene, 0, 857, textColorMark, textColorBarman, 0, 1); setOns(2, 0x6a); reloadLan(); playAnimation(878, 0); - //playAnimation(879, 0); //background bartender animation - inventory->remove(0x5c); + //playAnimation(879, 0); // background bartender animation + inventory->remove(kInvItemBird); enableObject(1); - SET_FLAG(0xDBE7, 1); - return true; + SET_FLAG(dsAddr_birdOnBarRadioAntennaFlag, 1); + break; case 0x8047: playSound(32, 5); @@ -1279,27 +2231,26 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(52, 23); playActorAnimation(881); setOns(2, 0x6b); - inventory->remove(0x56); - inventory->add(0x55); - SET_FLAG(0xDBE8, 1); - return true; + inventory->remove(kInvItemMugOfMud); + inventory->add(kInvItemMug); + SET_FLAG(dsAddr_swappedBarmanMugFlag, 1); + break; case 0x808b: - if (CHECK_FLAG(0xDBDA, 1)) { - //alredy shown - displayMessage(0x53F2); + if (CHECK_FLAG(dsAddr_ShownPassToGuardFlag, 1)) { + displayMessage(dsAddr_gotPermissionMsg); // "I already got the permission" } else { - displayMessage(0x53DD); + displayMessage(dsAddr_showPapersMsg); // "Here are my papers" playSound(5, 2); playSound(5, 18); playActorAnimation(810); - Dialog::show(scene, 0x60BF, 0, 809, 0xd1, 0xd0, 0, 1); - SET_FLAG(0xDBDA, 1); + dialog->show(147, scene, 0, 809, textColorMark, textColorCampGuard, 0, 1); + SET_FLAG(dsAddr_ShownPassToGuardFlag, 1); } - return true; + break; - case 0x80c3: //show kaleydoscope to the guard - Dialog::show(scene, 0x6811, 0, 809, 0xd1, 0xd0, 0, 1); + case 0x80c3: // show kaleidoscope to the guard + dialog->show(165, scene, 0, 809, textColorMark, textColorCampGuard, 0, 1); playSound(5, 3); playSound(5, 30); playSound(26, 14); @@ -1309,124 +2260,146 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(851, 0); playAnimation(850, 0); reloadLan(); - inventory->add(0x53); - inventory->remove(0x52); + inventory->add(kInvItemSoldierNews); + inventory->remove(kInvItemKaleidoscope); enableObject(1); - SET_FLAG(0xDBE2, 1); - return true; + SET_FLAG(dsAddr_act1GuardState, 1); + break; + + case 0x8398: + displayMessage(dsAddr_trySomewhereElseMsg); // "I'd better try somewhere else - I suppose this side is heavily guarded" + break; + + case 0x85dd: + displayMessage(dsAddr_branchNotPaddleMsg); // "This branch is not a paddle. It doesn't even look like one" + break; + + case 0x85e4: + displayMessage(dsAddr_sharpenNotPulverizeMsg); // "I needed to sharpen it, not pulverize" + break; + + case 0x8d42: + displayMessage(dsAddr_bluntSickleMsg); // "The sickle is too blunt" + break; - //Shore + case 0x8d49: + displayMessage(dsAddr_noChainsawFuelMsg); // "There's no fuel in the chainsaw" + break; + + case 0x8d50: + displayMessage(dsAddr_thornsTooThinMsg); // "Thorns are too thin, the chainsaw is useless here" + break; + + // Shore case 0x5348: - if (CHECK_FLAG(0xdb99, 1)) { //got broken paddle from boat - displayMessage(0x351f); + if (CHECK_FLAG(dsAddr_alreadyGotBrokenPaddleFlag, 1)) { // got broken paddle from boat + displayMessage(dsAddr_boatEmptyMsg); // "There's nothing else in the boat" } else { - SET_FLAG(0xDB99, 1); + SET_FLAG(dsAddr_alreadyGotBrokenPaddleFlag, 1); playSound(57, 6); playActorAnimation(536); - Dialog::showMono(scene, 0x30c3, 0, 0xd1, 0); - inventory->add(0x8); + dialog->showMono(77, scene, 0, textColorMark, 0); + inventory->add(kInvItemBrokenPaddle); } - return true; + break; case 0x53a1: - if (CHECK_FLAG(0xdbb2, 1)) { //spoken to man in well - displayMessage(0x411d); + if (CHECK_FLAG(dsAddr_spokenToManInWellFlag, 1)) { // spoken to man in well + displayMessage(dsAddr_stillThereMsg); // "Are you still there?" } else { - displayMessage(0x408a); - displayMessage(0x4091, 0xe5, 52728); - displayMessage(0x4098); - displayMessage(0x40a7, 0xe5, 52705); - displayMessage(0x40b6); - displayMessage(0x40ce, 0xe5, 52652); - displayMessage(0x40e8); - displayMessage(0x410f, 0xe5, 52712); + displayMessage(dsAddr_echoMsg); // "Echo!" + displayMessage(dsAddr_loudEchoMsg, textColorWellEcho, 248, 164); // "ECHO!" + displayMessage(dsAddr_whoThereMsg); // "Who's there?!" + displayMessage(dsAddr_loudWhoThereMsg, textColorWellEcho, 225, 164); // "WHO'S THERE?!" + displayMessage(dsAddr_dontCopyMsg); // "DON'T COPY ME!" + displayMessage(dsAddr_loudDontCopyMsg, textColorWellEcho, 172, 164); // "DON'T COPY ME!!!" + displayMessage(dsAddr_throwRockMsg); // "OR I WILL THROW A ROCK DOWN THERE!" + displayMessage(dsAddr_orIWillMsg, textColorWellEcho, 232, 164); // "OR I WILL" wait(100); - displayMessage(0x4091, 0xe5, 52728); - SET_FLAG(0xDBB2, 1); + displayMessage(dsAddr_loudEchoMsg, textColorWellEcho, 248, 164); + SET_FLAG(dsAddr_spokenToManInWellFlag, 1); } - return true; + break; + case 0x5458: + { + setOns(2, 0); + playSound(34, 7); + playActorAnimation(535); + inventory->add(kInvItemSecondFlower); + disableObject(1); - case 0x5458: { - setOns(2, 0); - playSound(34, 7); - playActorAnimation(535); - inventory->add(11); - disableObject(1); - - byte *scene_15_ons = scene->getOns(15); //patch ons for the scene 15 - scene_15_ons[0] = 0; + byte *scene_15_ons = scene->getOns(15); // patch ons for the scene 15 + scene_15_ons[0] = 0; - byte f = GET_FLAG(0xDB98) + 1; - SET_FLAG(0xDB98, f); - if (f >= 2) { - //disable object boat for scene 15!! - disableObject(1, 15); + byte f = GET_FLAG(dsAddr_flowerIsleState) + 1; + SET_FLAG(dsAddr_flowerIsleState, f); + if (f >= 2) { + // disable object boat for scene 15!! + disableObject(1, 15); + } } - } - return true; + break; - case 0x54b3: { - setOns(1, 0); - setOns(3, 0); - playSound(33, 6); - playActorAnimation(534); - inventory->add(10); - disableObject(2); - setOns(1, 10); - setOns(1, 0, 15); - byte f = GET_FLAG(0xDB98) + 1; - SET_FLAG(0xDB98, f); - if (f >= 2) { - //disable object boat for scene 15!! - disableObject(1, 15); + case 0x54b3: + { + setOns(1, 0); + setOns(3, 0); + playSound(33, 6); + playActorAnimation(534); + inventory->add(kInvItemFirstFlower); + disableObject(2); + setOns(1, 10); + setOns(1, 0, 15); + byte f = GET_FLAG(dsAddr_flowerIsleState) + 1; + SET_FLAG(dsAddr_flowerIsleState, f); + if (f >= 2) { + // disable object boat for scene 15!! + disableObject(1, 15); + } } - } - return true; + break; case 0x5502: setOns(0, 0); loadScene(15, 115, 180, 1); playMusic(6); playActorAnimation(568); - return true; + break; - case 0x5561://Enter lakeside house - moveTo(94, 115, 4); //call 557e, but it's not needed I guess + case 0x5561: // Enter lakeside house + fnEgoDefaultPosition(); loadScene(19, 223, 199, 1); - return true; + break; case 0x55a1: - processCallback(0x557e); + fnEgoDefaultPosition(); rejectMessage(); - return true; + break; - case 0x557e: - if (scene->getPosition().y <= 149) - moveTo(94, 115, 4); - else - moveTo(51, 149, 4); - return true; + case csAddr_egoDefaultPosition: + fnEgoDefaultPosition(); + break; case 0x563b: playSound(5, 10); setOns(1, 0); playActorAnimation(561); - inventory->add(26); + inventory->add(kInvItemNut); disableObject(6); - return true; + break; case 0x56f6: playSound(32, 7); setOns(1, 0); playActorAnimation(626); disableObject(12); - inventory->add(45); - displayMessage(0x3b04); - return true; + inventory->add(kInvItemCheese); + displayMessage(dsAddr_foundFoodMsg); // "People leave food in unbelievable places" + break; - case 0x5756://Open car door + case 0x5756: // Open car door playSound(11, 4); playActorAnimation(514); setOns(4, 8); @@ -1435,106 +2408,94 @@ bool TeenAgentEngine::processCallback(uint16 addr) { enableObject(15); enableObject(16); disableObject(1); - return true; + break; - case 0x5805://Enter basketball house + case 0x5805: // Enter basketball house playSound(70, 6); playActorAnimation(513); loadScene(22, 51, 180, 2); - return true; + break; - case 0x5832://Ring doorbell + case 0x5832: // Ring doorbell playActorAnimation(509); - displayMessage(0x5dce); - return true; + displayMessage(dsAddr_outOfOrderMsg); // "It's out of order" + break; case 0x58a2: - Dialog::pop(scene, 0xdaba, 0, 502, 0xd1, 0xe5, 0, 1); - scene->getObject(13)->setName((const char *)res->dseg.ptr(0x92e5)); - return true; + dialog->pop(scene, dsAddr_dialogStackSonny, 0, 502, textColorMark, textColorSonny, 0, 1); + scene->getObject(13)->setName((const char *)res->dseg.ptr(dsAddr_scnObjNameSonny)); + break; - case 0x58b7://Get comb from car + case 0x58b7: // Get comb from car disableObject(14); setOns(4, 0); playSound(5, 7); playActorAnimation(521); setOns(4, 0); - inventory->add(0x6); - return true; + inventory->add(kInvItemComb); + break; - case 0x58df://Pull trunk lever in car - SET_FLAG(0xDB94, 1); + case 0x58df: // Pull trunk lever in car + SET_FLAG(dsAddr_alreadyPulledTrunkReleaseLeverFlag, 1); playSound(6, 1); setOns(3, 6); playActorAnimation(515); - return true; + break; - case 0x593e://Enter annes house + case 0x593e: // Enter annes house playSound(89, 4); playActorAnimation(980); loadScene(23, 76, 199, 1); - if (CHECK_FLAG(0xDBEE, 1)) + if (CHECK_FLAG(dsAddr_lovestruckByAnneFlag, 1)) playMusic(7); - return true; + break; case 0x5994: - processCallback(0x599b); - processCallback(0x5a21); - return true; + fnEnterCave(); + break; - case 0x599b: - return true; + case csAddr_caveNOP: + break; - case 0x5a21: - loadScene(24, 230, 170, 1); - playSound(52, 3); - playSound(52, 7); - playSound(52, 11); - playSound(52, 14); - playSound(52, 18); - playSound(52, 21); - playSound(52, 25); - playActorAnimation(601); - moveTo(230, 179, 3); - if (!CHECK_FLAG(0xDBA4, 1)) - displayMessage(0x37ea); //it's kinda dark here - return true; + case csAddr_enterCave: + fnEnterCave(); + break; case 0x5a8b: - if (!CHECK_FLAG(0xDBAD, 1)) { - playSound(43, 4); //grrrrrr + if (!CHECK_FLAG(dsAddr_dogHasBoneFlag, 1)) { + playSound(43, 4); // grrrrrr playSound(42, 15); playSound(42, 17); playSound(42, 19); playAnimation(656, 0); wait(50); - displayMessage(0x3c16); - } else if (!CHECK_FLAG(0xDBA3, 1)) {//Dog has bone + displayMessage(dsAddr_goodDoggyMsg); // "I understand. Good doggy" + } else if (!CHECK_FLAG(dsAddr_cellarDoorOpenFlag, 1)) { // Dog has bone playSound(28, 3); playActorAnimation(596); setOns(1, 30); - SET_FLAG(0xDBA3, 1); + SET_FLAG(dsAddr_cellarDoorOpenFlag, 1); enableObject(8); } else { setOns(1, 0); playSound(4, 4); playActorAnimation(597); - SET_FLAG(0xDBA3, 0); + SET_FLAG(dsAddr_cellarDoorOpenFlag, 0); disableObject(8); - displayMessage(0x37b8); + displayMessage(dsAddr_wallShakenMsg); // "Wow! This must have shaken all the nearby walls!" setOns(1, 32, 24); enableObject(4, 24); } - return true; + break; - case 0x5b3a://Click on dog - Dialog::popMark(scene, 0xDB14); - return true; + case 0x5b3a: // Click on dog + dialog->popMark(scene, dsAddr_dialogStackDog); + break; - case 0x5b59: //picking up the rope - Dialog::showMark(scene, 0x2cbd); + case 0x5b59: // picking up the rope + dialog->showMark(70, scene); wait(150); - Dialog::showMark(scene, 0x2dc2); + dialog->showMark(71, scene); moveRel(0, -12, 0); playSound(34, 5); playActorAnimation(607); @@ -1545,81 +2506,81 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(5, 25); playActorAnimation(611); moveTo(16, scene->getPosition().y, 4, true); - inventory->add(38); + inventory->add(kInvItemRopeAct2); disableObject(12); - return true; + break; - case 0x5be1://Talk to grandpa - Dialog::pop(scene, 0xDAC4, 0, 522, 0xd1, 0xd8, 0, 1); - return true; + case 0x5be1: // Talk to grandpa + dialog->pop(scene, dsAddr_dialogStackGrandpa, 0, 522, textColorMark, textColorGrandpa, 0, 1); + break; case 0x5bee: playSound(89, 5); playSound(67, 11); playActorAnimation(982); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; - case 0x5c0d: //grandpa - drawers - if (CHECK_FLAG(0xDBA7, 1)) { - displayMessage(0x3bac); + case 0x5c0d: // grandpa - drawers + if (CHECK_FLAG(dsAddr_SearchedGrandpaDrawersFlag, 1)) { + displayMessage(dsAddr_drawersEmptyMsg); // "There's nothing else in the drawers" } else { - if (!CHECK_FLAG(0xDB92, 1)) - Dialog::show(scene, 0x15a0, 0, 522, 0xd1, 0xd8, 0, 1); //can I search your drawers? + if (!CHECK_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1)) + dialog->show(24, scene, 0, 522, textColorMark, textColorGrandpa, 0, 1); playSound(66, 5); playSound(67, 20); playSound(5, 23); playActorAnimation(631); - inventory->add(47); - SET_FLAG(0xDBA7, 1); + inventory->add(kInvItemHandkerchief); + SET_FLAG(dsAddr_SearchedGrandpaDrawersFlag, 1); } - return true; + break; case 0x5c84: - if (CHECK_FLAG(0xDB92, 1)) { - inventory->add(2); + if (CHECK_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1)) { + inventory->add(kInvItemShotgun); disableObject(7); playSound(32, 7); setOns(0, 0); playActorAnimation(520); } else { - Dialog::pop(scene, 0xDACE, 0, 522, 0xd1, 0xd8, 0, 1); + dialog->pop(scene, dsAddr_dialogStackGrandpaShotgun, 0, 522, textColorMark, textColorGrandpa, 0, 1); } - return true; + break; - case 0x5cf0://Exit basketball house + case 0x5cf0:// Exit basketball house playSound(88, 5); playActorAnimation(981); loadScene(20, 161, 165); - return true; + break; - case 0x5d24: //getting the fan - if (CHECK_FLAG(0xDB92, 1)) { + case 0x5d24: // getting the fan + if (CHECK_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1)) { setLan(2, 0); playSound(32, 7); playActorAnimation(508); disableObject(13); - inventory->add(7); + inventory->add(kInvItemFan); } else { - Dialog::pop(scene, 0xDAD4, 0, 522, 0xd1, 0xd8, 0, 1); + dialog->pop(scene, dsAddr_dialogStackGrandpaFan, 0, 522, textColorMark, textColorGrandpa, 0, 1); } - return true; + break; - case 0x5e4d: //right click on ann - if (!CHECK_FLAG(0xDB97, 0)) { - displayMessage(0x3d59); + case 0x5e4d: // right click on ann + if (!CHECK_FLAG(dsAddr_alreadySpokenToAnneFlag, 0)) { + displayMessage(dsAddr_girlTalkMsg); // "I really don't know how to talk to girls" } else { moveTo(245, 198, 1); - Dialog::show(scene, 0x21d7, 0, 524, 0xd1, 0xe5, 0, 2); - //waitLanAnimationFrame(2, 1); //too long, about 200 frames! seems to be present in original game (sic) - SET_FLAG(0xDB97, 1); + dialog->show(51, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + //waitLanAnimationFrame(2, 1); // too long, about 200 frames! seems to be present in original game (sic) + SET_FLAG(dsAddr_alreadySpokenToAnneFlag, 1); for (byte i = 10; i <= 20; i += 2) playSound(13, i); playAnimation(528, 1); wait(50); playMusic(7); - SET_FLAG(0xDBEE, 1); + SET_FLAG(dsAddr_lovestruckByAnneFlag, 1); for (byte i = 3; i <= 17; i += 2) playSound(56, i); playActorAnimation(525); @@ -1633,163 +2594,146 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(55, 5); playActorAnimation(527); wait(50); - Dialog::show(scene, 0x2219, 0, 524, 0xd1, 0xe5, 0, 2); - scene->getObject(2)->setName((const char *)res->dseg.ptr(0x9820)); + dialog->show(52, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + scene->getObject(2)->setName((const char *)res->dseg.ptr(dsAddr_scnObjNameAnne)); } - return true; + break; - case 0x5f73: //exiting ann's house - if (CHECK_FLAG(0xDBEE, 1)) + case 0x5f73: // exiting ann's house + if (CHECK_FLAG(dsAddr_lovestruckByAnneFlag, 1)) playMusic(6); loadScene(21, 99, 180, 3); - return true; + break; case 0x5fba: - if (CHECK_FLAG(0xDBB1, 1)) { - displayMessage(0x4380); + if (CHECK_FLAG(dsAddr_nutSwappedForAppleFlag, 1)) { + displayMessage(dsAddr_noFruitMsg); // "There are no more interesting fruits here" } else { - Dialog::pop(scene, 0xDAFC, 0, 523, 0xd1, 0xe5, 0, 1); + dialog->pop(scene, dsAddr_dialogStackGetAppleOldLady, 0, 523, textColorMark, textColorOldLady, 0, 1); } - return true; + break; case 0x607f: - return processCallback(0x60b5); + fnEgoScaredBySpider(); + break; case 0x6083: - if (CHECK_FLAG(0xDBA4, 1)) { + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) { setOns(0, 0); playSound(56, 10); playActorAnimation(599); - inventory->add(37); + inventory->add(kInvItemShovelAct2); disableObject(2); } else - processCallback(0x60b5); - return true; + fnEgoScaredBySpider(); + break; - case 0x60b5: - if (CHECK_FLAG(0xDBAE, 1)) { - processCallback(0x60d9); - Dialog::showMark(scene, 0x2fdd); - } else { - Dialog::showMark(scene, 0x2e41); - processCallback(0x60d9); - wait(100); - Dialog::showMark(scene, 0x2e6d); - } - return true; + case csAddr_egoScaredBySpider: + fnEgoScaredBySpider(); + break; - case 0x60d9: { - Object *obj = scene->getObject(3); - moveTo(obj); - processCallback(0x612b); - moveTo(48, 190, 3); - } - return true; + case csAddr_moveToLadderAndLeaveCellar: + fnMoveToLadderAndLeaveCellar(); + break; - case 0x612b: - playSound(52, 10); - playSound(52, 14); - playSound(52, 18); - playSound(52, 21); - playSound(52, 25); - playSound(52, 28); - playSound(52, 32); - playActorAnimation(600); - loadScene(21, 297, 178, 3); - return true; + case csAddr_leaveCellar: + fnLeaveCellar(); + break; case 0x6176: - if (CHECK_FLAG(0xDBA4, 1)) { - displayMessage(0x3801); - return true; + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) { + displayMessage(dsAddr_notInDarkMsg); // "I'm not going to wander here in the dark again" + } else { + playSound(71, 6); + playActorAnimation(598); + loadScene(24, scene->getPosition()); + setOns(2, 0); + setLan(1, 0); + playAnimation(660, 0); + disableObject(1); + SET_FLAG(dsAddr_lightOnFlag, 1); + loadScene(24, scene->getPosition()); } - playSound(71, 6); - playActorAnimation(598); - loadScene(24, scene->getPosition()); - setOns(2, 0); - setLan(1, 0); - playAnimation(660, 0); - disableObject(1); - SET_FLAG(0xDBA4, 1); - loadScene(24, scene->getPosition()); - - return true; + break; case 0x61e9: - if (CHECK_FLAG(0xDBA4, 1)) { - Dialog::popMark(scene, 0xdb1e); - } else - processCallback(0x61fe); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + dialog->popMark(scene, dsAddr_dialogStackTakeAxe); + else + fnTooDark(); + break; - return true; + case csAddr_TooDark: + displayMessage(dsAddr_TooDarkMsg); // "It's too dark to see clearly" + break; - case 0x6229: //shelves in cellar - if (CHECK_FLAG(0xDBA4, 1)) { + case 0x6229: // shelves in cellar + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) { Common::Point p = scene->getPosition(); - byte v = GET_FLAG(0xDBB4); + byte v = GET_FLAG(dsAddr_cellarShelfExamineCount); switch (v) { case 0: - displayMessage(0x4532); + displayMessage(dsAddr_whatGotMsg); // "Let's look what we've got here" moveRel(-34, 0, 1); - displayMessage(0x4555); + displayMessage(dsAddr_strawberryJamMsg); // "Strawberry jam" moveRel(20, 0, 1); - displayMessage(0x4568); + displayMessage(dsAddr_gooseberryJamMsg); // "Gooseberry jam" moveRel(20, 0, 1); - displayMessage(0x457b); + displayMessage(dsAddr_blackberryJamMsg); // "Blackberry jam" moveRel(20, 0, 1); - displayMessage(0x458e); + displayMessage(dsAddr_bilberryJamMsg); // "Bilberry jam" moveTo(p, 3); - displayMessage(0x459f); - SET_FLAG(0xDBB4, 1); + displayMessage(dsAddr_getMeOutJamMsg); // "Get me out of this jam!" + SET_FLAG(dsAddr_cellarShelfExamineCount, 1); break; case 1: - displayMessage(0x45b8); + displayMessage(dsAddr_rosemaryJamMsg); // "Oh, and there is Rosemary jam" wait(100); - displayMessage(0x45da); - SET_FLAG(0xDBB4, 2); + displayMessage(dsAddr_knowRosemaryMsg); // "I used to know someone called Rosemary" + SET_FLAG(dsAddr_cellarShelfExamineCount, 2); break; default: - displayMessage(0x4603); + displayMessage(dsAddr_unwantedJamsMsg); // "I don't want those jams" + break; } } else - processCallback(0x61fe); - - return true; + fnTooDark(); + break; - case 0x6480: //dive mask - if (CHECK_FLAG(0xDB96, 1)) { + case 0x6480: // dive mask + if (CHECK_FLAG(dsAddr_birdsGoneFromScarecrowFlag, 1)) { playSound(56, 7); playSound(5, 15); playActorAnimation(613); setOns(3, 36); - inventory->add(39); + inventory->add(kInvItemMask); disableObject(5); - displayMessage(0x387c); + displayMessage(dsAddr_needSunglassesMsg); // "Sorry buddy, but I need your sunglasses" } else - displayMessage(0x3eb2); - return true; + displayMessage(dsAddr_crowKillMsg); // "I'm sure these crows will kill me" + break; - case 0x64c4: //flippers - if (CHECK_FLAG(0xDB96, 1)) { + case 0x64c4: // flippers + if (CHECK_FLAG(dsAddr_birdsGoneFromScarecrowFlag, 1)) { setOns(2, 35); playSound(63, 8); playSound(24, 10); playActorAnimation(612); - inventory->add(40); + inventory->add(kInvItemFins); disableObject(6); } else - displayMessage(0x3eb2); - return true; + displayMessage(dsAddr_crowKillMsg); // "I'm sure these crows will kill me" + break; - case 0x7907://Describe car lever - if (CHECK_FLAG(0xdb94, 1)) {//Already pulled lever? - displayMessage(0x3e4f); - return true; + case 0x7907: // Describe car lever + if (CHECK_FLAG(dsAddr_alreadyPulledTrunkReleaseLeverFlag, 1)) { // Already pulled lever? + displayMessage(dsAddr_openBootMsg); // "It opens the boot" } else - return false; + retVal = false; + break; - case 0x62d0://Get bone from under rock - displayAsyncMessage(0x463c, 30938, 16, 24); + case 0x62d0: // Get bone from under rock + displayAsyncMessage(dsAddr_yeowMsg, 218, 96, 16, 24); // "YEEEOOOWWWW!" playSound(26, 6); playSound(26, 10); playSound(24, 13); @@ -1798,35 +2742,34 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(594); setOns(0, 29); disableObject(1); - inventory->add(36); + inventory->add(kInvItemBone); playSound(5, 2); playActorAnimation(595); - displayMessage(0x3790); - return true; + displayMessage(dsAddr_dinoBoneMsg); // "I really hope this is DINOSAUR bone" + break; case 0x6351: - if (CHECK_FLAG(0xdaca, 1)) { //cave bush is cut down + if (CHECK_FLAG(dsAddr_caveThornsCutDownFlag, 1)) { // cave bush is cut down playMusic(8); loadScene(26, 319, 169, 4); } else - displayMessage(0x3bd2); - return true; + displayMessage(dsAddr_ridBushMsg); // "I must get rid of this bush first" + break; case 0x63ea: playSound(5, 10); setOns(0, 0); playActorAnimation(640); - inventory->add(50); + inventory->add(kInvItemNugget); disableObject(6); - return true; + break; - case 0x6411://Kick hen - if (CHECK_FLAG(0xdb93, 1)) { //already kicked hen - displayMessage(0x3e08); - return true; + case 0x6411: // Kick hen + if (CHECK_FLAG(dsAddr_alreadyKickedHenFlag, 1)) { // already kicked hen + displayMessage(dsAddr_ridFrustationsMsg); // "I'd already got rid of my frustrations" } else { - SET_FLAG(0xdb93, 1); - displayMessage(0x3dc6); + SET_FLAG(dsAddr_alreadyKickedHenFlag, 1); + displayMessage(dsAddr_henFlyMsg); // "I wonder if hens can fly. Come here, baby" waitLanAnimationFrame(1, 87); playSound(30, 26); playSound(29, 49); @@ -1835,198 +2778,191 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); setOns(0, 1); enableObject(14); - displayMessage(0x3df4); - return true; + displayMessage(dsAddr_firstTestFailMsg); // "First test failed" } + break; - case 0x6592: //Rake + case 0x6592: // Rake setOns(1, 0); playSound(18, 10); playActorAnimation(553); - inventory->add(0x15); + inventory->add(kInvItemRakeBroken); wait(50); - displayMessage(0x3605); + displayMessage(dsAddr_trousersMsg); // "Good I always asked mum for trousers with BIG pockets" disableObject(11); - return true; + break; case 0x66b5: playSound(89, 5); playActorAnimation(969); loadScene(33, 319, 181, 4); - return true; + break; - case 0x6519://Sickle + case 0x6519: // Sickle setOns(4, 0); playSound(5, 11); playActorAnimation(625); - inventory->add(0x2c); + inventory->add(kInvItemSickleBlunt); disableObject(8); - return true; + break; - case 0x655b://Get needle from haystack - if (CHECK_FLAG(0xdb9d, 1)) { //already have needle - displayMessage(0x356a); - return true; + case 0x655b: // Get needle from haystack + if (CHECK_FLAG(dsAddr_gotNeedleAlreadyFlag, 1)) { // already have needle + displayMessage(dsAddr_dontPushLuckMsg); // "I don't think I should push my luck" } else { - SET_FLAG(0xdb9d, 1); + SET_FLAG(dsAddr_gotNeedleAlreadyFlag, 1); playSound(49, 3); playActorAnimation(548); - inventory->add(0x11); - displayMessage(0x35b2); - return true; + inventory->add(kInvItemNeedle); + displayMessage(dsAddr_needleHaystackMsg); // "And they say you can't find a needle in a haystack" } + break; - case 0x663c://Feather + case 0x663c: // Feather setOns(0, 0); playSound(5, 9); playActorAnimation(511); - inventory->add(1); + inventory->add(kInvItemFeather); disableObject(15); - return true; + break; case 0x667c: playSound(70, 4); playActorAnimation(972); loadScene(29, 160, 199, 1); - return true; + break; case 0x66a9: - displayMessage(0x4a7e); + displayMessage(dsAddr_dontLeaveMansionMsg); // "I don't want to leave the mansion, I want blood!" disableObject(4); - return true; + break; case 0x66e2: playSound(88, 4); playActorAnimation(970); loadScene(35, 160, 199, 1); - return true; + break; case 0x70bb: - Dialog::pop(scene, 0xdb24, 0, 709, 0xd1, 0xef, 0, 1); - return true; + dialog->pop(scene, dsAddr_dialogStackBusyCook, 0, 709, textColorMark, textColorCook, 0, 1); + break; case 0x71ae: - if (CHECK_FLAG(0xDBCD, 1)) { - if (CHECK_FLAG(0xDBCE, 1)) { - displayMessage(0x4f9b); + if (CHECK_FLAG(dsAddr_MansionRadioBrokenFlag, 1)) { + if (CHECK_FLAG(dsAddr_MansionGotRadioBatteriesFlag, 1)) { + displayMessage(dsAddr_restUselessMsg); // "The rest is useless" } else { - displayMessage(0x4fb1); + displayMessage(dsAddr_twoBatteriesMsg); // "Wow! Two 1.5V batteries!" playSound(32, 6); playActorAnimation(717); - inventory->add(66); - SET_FLAG(0xDBCE, 1); + inventory->add(kInvItemBatteries); + SET_FLAG(dsAddr_MansionGotRadioBatteriesFlag, 1); } } else - Dialog::showMark(scene, 0x3c9d); - return true; + dialog->showMark(97, scene); + break; case 0x70c8: - if (!processCallback(0x70e0)) - return true; - moveTo(81, 160, 4); - displayMessage(0x5cac); - return true; - - case 0x70e0: - if (!CHECK_FLAG(0xDBCC, 1)) { - displayMessage(0x4ece); - return false; + if (fnIsCookGone()) { + moveTo(81, 160, 4); + displayMessage(dsAddr_cognacMsg); // "Pfui! The cognac really didn't do any good" } - return true; + break; + + case csAddr_isCookGone: + retVal = fnIsCookGone(); + break; case 0x70ef: - if (!processCallback(0x70e0)) - return true; - displayMessage(0x5046); - return true; + if (fnIsCookGone()) + displayMessage(dsAddr_tooHotMsg); // "It's too hot to touch!" + break; case 0x70f9: - if (inventory->has(68)) { - inventory->remove(68); + if (inventory->has(kInvItemBurningPaper)) { + inventory->remove(kInvItemBurningPaper); loadScene(29, 40, 176, 2); - displayMessage(0x500a); + displayMessage(dsAddr_paperBurntMsg); // "The paper burnt out completely!" } else loadScene(29, 40, 176, 2); - return true; + break; case 0x712c: - if (!processCallback(0x70e0)) - return true; - - if (CHECK_FLAG(0xDBCF, 1)) { - playSound(89, 4); - playActorAnimation(719); - setOns(4, 67); - ++ *res->dseg.ptr(READ_LE_UINT16(res->dseg.ptr(0x6746 + (scene->getId() - 1) * 2))); - disableObject(5); - enableObject(12); - } else { - playSound(89, 4); - playSound(89, 4); - playSound(87, 45); - displayAsyncMessage(0x4fcb, 34672, 11, 35, 0xe5); - playActorAnimation(718); - wait(100); - displayMessage(0x4fe2); - SET_FLAG(0xDBCF, 1); + if (fnIsCookGone()) { + if (CHECK_FLAG(dsAddr_MansionHaveOpenedFridgeBeforeFlag, 1)) { + playSound(89, 4); + playActorAnimation(719); + setOns(4, 67); + ++ *res->dseg.ptr(READ_LE_UINT16(res->dseg.ptr(dsAddr_sceneWalkboxTablePtr + (scene->getId() - 1) * 2))); + disableObject(5); + enableObject(12); + } else { + playSound(89, 4); + playSound(89, 4); + playSound(87, 45); + displayAsyncMessage(dsAddr_oneTakenMsg, 112, 108, 11, 35, textColorEskimo); // "This one's taken, OK?" + playActorAnimation(718); + wait(100); + displayMessage(dsAddr_slightMadMsg); // "It finally happened. I'm slightly mad" + SET_FLAG(dsAddr_MansionHaveOpenedFridgeBeforeFlag, 1); + } } - return true; + break; case 0x71eb: setOns(2, 0); playSound(32, 7); playActorAnimation(710); - inventory->add(62); + inventory->add(kInvItemChilliWithLabel); disableObject(7); enableObject(8); - return true; + break; case 0x7244: - if (!processCallback(0x70e0)) - return true; - displayMessage(0x5c60); - return true; + if (fnIsCookGone()) + displayMessage(dsAddr_neverLearntMsg); // "I never learnt to how use one" + break; case 0x7255: - if (CHECK_FLAG(0xDBD0, 1)) { + if (CHECK_FLAG(dsAddr_MansionPutBurningPaperInFridgeFlag, 1)) { setOns(4, 69); playSound(32, 5); playActorAnimation(725); disableObject(12); - inventory->add(69); + inventory->add(kInvItemMeat); } else { playActorAnimation(721); - displayMessage(0x505e); + displayMessage(dsAddr_frozenShelfMsg); // "It has frozen hard onto the shelf!" } - return true; + break; case 0x721c: setOns(3, 0); playSound(32, 7); playActorAnimation(715); - inventory->add(63); + inventory->add(kInvItemPastryRoller); disableObject(9); - return true; + break; case 0x7336: setOns(1, 0); playSound(5, 42); - displayAsyncMessage(0x4d02, 32642, 20, 38); + displayAsyncMessage(dsAddr_noDepraveMsg, 2, 102, 20, 38); // "Nah, I don't want to deprave the kids" playActorAnimation(697); - inventory->add(56); + inventory->add(kInvItemCognac); disableObject(1); - return true; + break; case 0x7381: playSound(5, 12); playActorAnimation(704); disableObject(2); - inventory->add(58); - return true; + inventory->add(kInvItemIceTongs); + break; case 0x7408: - if (CHECK_FLAG(0xDBC4, 1)) { - displayMessage(0x4d2a); + if (CHECK_FLAG(dsAddr_mansionReadNewspaperFlag, 1)) { + displayMessage(dsAddr_noReadAgainMsg); // "I don't want to read it again. I might like it." } else { setOns(0, 0); playSound(26, 17); @@ -2038,45 +2974,45 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(698); setOns(0, 52); setOns(2, 61); - Dialog::showMark(scene, 0x38b6); + dialog->showMark(92, scene); enableObject(11); - SET_FLAG(0xDBC4, 1); + SET_FLAG(dsAddr_mansionReadNewspaperFlag, 1); } - return true; + break; case 0x7476: - if (CHECK_FLAG(0xDBC9, 1)) { - displayMessage(0x4dbb); + if (CHECK_FLAG(dsAddr_mansionExaminedCouchBeforeFlag, 1)) { + displayMessage(dsAddr_noSleepMsg); // "I don't want to sleep" } else { - SET_FLAG(0xDBC9, 1); - Dialog::showMark(scene, 0x3aca); + SET_FLAG(dsAddr_mansionExaminedCouchBeforeFlag, 1); + dialog->showMark(94, scene); playSound(61, 5); playSound(5, 14); playActorAnimation(705); - displayMessage(0x4dd3); - inventory->add(59); + displayMessage(dsAddr_justCorkMsg); // "It's just a cork" + inventory->add(kInvItemCork); } - return true; + break; case 0x74d1: setOns(2, 0); playSound(5, 12); playActorAnimation(699); - inventory->add(57); + inventory->add(kInvItemRemoteControl); disableObject(11); - return true; + break; - case 0x7513: //fatso + doctor: pre-final - if (CHECK_FLAG(0xDBD7, 1)) { - if (CHECK_FLAG(0xDBD8, 1)) { + case 0x7513: // fatso + doctor: pre-final + if (CHECK_FLAG(dsAddr_MansionThruFanByTimePillFlag, 1)) { + if (CHECK_FLAG(dsAddr_MansionVentFanStoppedFlag, 1)) { playSound(88, 4); playActorAnimation(979); loadScene(37, 51, 183); - Dialog::show(scene, 0x54ea, 768, 769, 0xd9, 0xe5, 1, 2); + dialog->show(125, scene, 768, 769, textColorMansionGuard, textColorProfessor, 1, 2); playAnimation(770, 0, true, true, true); playAnimation(771, 1, true, true, true); - Dialog::showMono(scene, 0x5523, 0, 0xd1, 0); + dialog->showMono(126, scene, 0, textColorMark, 0); playAnimation(770, 0, true, true, true); playAnimation(771, 1, true, true, true); playSound(5, 3); @@ -2090,11 +3026,11 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); setOns(0, 74); hideActor(); - Dialog::showMono(scene, 0x5556, 775, 0xd0, 1); + dialog->showMono(127, scene, 775, textColorJohnNoty, 1); playAnimation(771, 1, true, true, true); playAnimation(776, 0); - Dialog::show(scene, 0x55f7, 777, 778, 0xd0, 0xe5, 1, 2); //i have to kill you anyway + dialog->show(128, scene, 777, 778, textColorJohnNoty, textColorProfessor, 1, 2); playAnimation(779, 0, true, true, true); playAnimation(780, 1, true, true, true); @@ -2151,7 +3087,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(0, 80); playAnimation(792, 3, true, true, true); - Dialog::show(scene, 0x5665, 0, 791, 0xd1, 0xd0, 0, 4); + dialog->show(129, scene, 0, 791, textColorMark, textColorJohnNoty, 0, 4); playAnimation(792, 3, true, true, true); moveTo(40, 171, 4); @@ -2161,43 +3097,47 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(0, 3); loadScene(31, 298, 177, 4); - SET_FLAG(0xDBD9, 1); - } else { - displayMessage(0x52fe); - } + SET_FLAG(dsAddr_MansionJohnNotyEscapingFlag, 1); + } else + displayMessage(dsAddr_ventFirstMsg); // "I'd better stop this ventilator first" } else - displayMessage(0x52cb); - return true; + displayMessage(dsAddr_noSaladMsg); // "I don't want to turn myself into a salad" + break; case 0x783d: - Dialog::pop(scene, 0xdb36, 0, 797, 0xd1, 0xd0, 0, 1); - return true; + dialog->pop(scene, dsAddr_dialogStackJohnNotyEndgame, 0, 797, textColorMark, textColorJohnNoty, 0, 1); + break; case 0x7966: - if (CHECK_FLAG(0xDBA4, 1)) - return false; - return processCallback(0x60b5); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + fnEgoScaredBySpider(); + break; case 0x7ad0: case 0x7ad7: - return !processCallback(0x70e0); + retVal = !fnIsCookGone(); + break; case 0x7ab9: - if (CHECK_FLAG(0xDBB6, 1)) - return false; - Dialog::showMono(scene, 0x37d0, 0, 0xd1, 0); - SET_FLAG(0xDBB6, 1); - return true; + if (CHECK_FLAG(dsAddr_vgaArtistQuipAlreadySaidFlag, 1)) + retVal = false; + else { + dialog->showMono(90, scene, 0, textColorMark, 0); + SET_FLAG(dsAddr_vgaArtistQuipAlreadySaidFlag, 1); + } + break; case 0x7ade: - if (CHECK_FLAG(0xdbcd, 1)) { - displayMessage(0x4f69); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_MansionRadioBrokenFlag, 1)) + displayMessage(dsAddr_whatInsideMsg); // "I was always curious what's inside these things" + else + retVal = false; + break; - case 0x7f23://Use grenade on captains drawer - if (CHECK_FLAG(0xDBDF, 3)) { + case 0x7f23: // Use grenade on captains drawer + if (CHECK_FLAG(dsAddr_FirstActTrialState, 3)) { enableOn(false); playSound(5, 3); playSound(58, 11); @@ -2207,123 +3147,120 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(870); playSound(54, 15); playActorAnimation(871); - SET_FLAG(0xDBE6, 1); + SET_FLAG(dsAddr_captainDrawerState, 1); setOns(1, 0x66); moveTo(224, 194, 0, true); - displayCutsceneMessage(0x57df, 30423); - inventory->remove(0x59); + displayCutsceneMessage(dsAddr_cutsceneMsg1, 23, 95); // "sixty seven rude words later" + inventory->remove(kInvItemRopeAndGrenade); enableOn(true); - } else { - displayMessage(0x5de2); - } - return true; + } else + displayMessage(dsAddr_captainWatchingMsg); // "with captain watching? Better not" + break; - case 0x505c: { - //suspicious stuff - Common::Point p = scene->getPosition(); - if (p.x != 203 && p.y != 171) - moveTo(203, 169, 2); - else - moveTo(203, 169, 1); - } - return true; + case csAddr_egoSuspiciousPosition: + fnEgoSuspiciousPosition(); + break; case 0x509a: - processCallback(0x505c); + fnEgoSuspiciousPosition(); setOns(1, 0); playSound(5, 10); playActorAnimation(543); - inventory->add(15); + inventory->add(kInvItemBranch); disableObject(9); - return true; + break; case 0x7802: - if (CHECK_FLAG(0xDBD7, 1)) { - if (CHECK_FLAG(0xDBD8, 1)) - displayMessage(0x52f6); + if (CHECK_FLAG(dsAddr_MansionThruFanByTimePillFlag, 1)) { + if (CHECK_FLAG(dsAddr_MansionVentFanStoppedFlag, 1)) + displayMessage(dsAddr_nahMsg); // "Nah" else { playSound(71, 4); playActorAnimation(796); setLan(1, 0); - SET_FLAG(0xDBD8, 1); + SET_FLAG(dsAddr_MansionVentFanStoppedFlag, 1); } } else - displayMessage(0x52cb); - return true; + displayMessage(dsAddr_noSaladMsg); // "I don't want to turn myself into a salad" + break; case 0x78e0: - processCallback(0x505c); - return false; + fnEgoSuspiciousPosition(); + retVal = false; + break; case 0x78e7: - processCallback(0x557e); - return false; - case 0x78ee: - processCallback(0x557e); - return false; + fnEgoDefaultPosition(); + retVal = false; + break; case 0x78f5: - if (CHECK_FLAG(0xDB95, 1)) { - displayMessage(0x3E75); - return true; + if (CHECK_FLAG(dsAddr_carTrunkEmptyFlag, 1)) { + displayMessage(dsAddr_bootEmptyMsg); // "There's nothing else in the boot" } else - return false; + retVal = false; + break; case 0x7919: - if (!CHECK_FLAG(0xDBA5, 1)) - return false; - displayMessage(0x3E98); - return true; + if (!CHECK_FLAG(dsAddr_laundryState, 1)) + retVal = false; + else + displayMessage(dsAddr_clothesDryMsg); // "The clothes are dry now." + break; case 0x7950: - if (!CHECK_FLAG(0xDBB1, 1)) - return false; - - displayMessage(0x3DAF); - return true; + if (CHECK_FLAG(dsAddr_nutSwappedForAppleFlag, 1)) + displayMessage(dsAddr_nutRealMsg); // "Only the nut is real" + else + retVal = false; + break; case 0x7975: - if (CHECK_FLAG(0xDBA4, 1)) - return false; - displayMessage(0x3832); - return true; + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + displayMessage(dsAddr_shutValveMsg); // "Shutting the valve shook the dirt from the wall..." + break; case 0x7987: case 0x7996: case 0x79a5: case 0x79b4: - if (CHECK_FLAG(0xDBA4, 1)) - return false; - return processCallback(0x61fe); + if (CHECK_FLAG(dsAddr_lightOnFlag, 1)) + retVal = false; + else + fnTooDark(); + break; case 0x79d2: - if (!CHECK_FLAG(0xDB9D, 1)) - return false; - displayMessage(0x3590); - return true; + if (!CHECK_FLAG(dsAddr_gotNeedleAlreadyFlag, 1)) + retVal = false; + else + displayMessage(dsAddr_ordinaryHaystackMsg); // "Just an ordinary hay stack. Now." + break; case 0x7af0: - if (!processCallback(0x70e0)) - return true; - return false; + if (fnIsCookGone()) + retVal = false; + break; case 0x8117: - Dialog::show(scene, 0x0a41, 0, 529, 0xd1, 0xd9, 0, 1); + dialog->show(9, scene, 0, 529, textColorMark, textColorMansionGuard, 0, 1); playSound(5, 2); playSound(5, 44); playAnimation(642, 0, true); playActorAnimation(641, true); waitAnimation(); - Dialog::show(scene, 0x0aff, 0, 529, 0xd1, 0xd9, 0, 1); + dialog->show(10, scene, 0, 529, textColorMark, textColorMansionGuard, 0, 1); wait(170); - Dialog::show(scene, 0x0ba0, 0, 529, 0xd1, 0xd9, 0, 1); + dialog->show(11, scene, 0, 529, textColorMark, textColorMansionGuard, 0, 1); moveRel(0, 1, 0); wait(100); - Dialog::show(scene, 0x0c10, 0, 529, 0xd1, 0xd9, 0, 1); - inventory->remove(50); - processCallback(0x9d45); - return true; + dialog->show(12, scene, 0, 529, textColorMark, textColorMansionGuard, 0, 1); + inventory->remove(kInvItemNugget); + fnMansionIntrusionAttempt(); + break; case 0x8174: setOns(0, 0); @@ -2336,7 +3273,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(1, 15); disableObject(3); enableObject(9); - return true; + break; case 0x81c2: playSound(56, 11); @@ -2353,12 +3290,12 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(588, true); waitAnimation(); wait(50); - displayMessage(0x367f); - inventory->remove(34); - SET_FLAG(0xDBA1, 1); - return true; + displayMessage(dsAddr_itsGoneMsg); // "At least it's gone" + inventory->remove(kInvItemPaintedPotato); + SET_FLAG(dsAddr_mansionTreeHollowEmptyFlag, 1); + break; - case 0x823d: //grappling hook on the wall + case 0x823d: // grappling hook on the wall playSound(5, 3); for (byte i = 16; i <= 28; i += 2) playSound(65, i); @@ -2367,27 +3304,26 @@ bool TeenAgentEngine::processCallback(uint16 addr) { for (byte i = 3; i <= 18; i += 3) playSound(56, i); - displayAsyncMessage(0x3ace, 3878, 20, 37, 0xd9); + displayAsyncMessage(dsAddr_heyLetGoMsg, 38, 12, 20, 37, textColorMansionGuard); // "Hey, let go, will ya?!" playActorAnimation(621, true); playAnimation(623, 1, true); waitAnimation(); - displayAsyncMessage(0x3ae6, 3870, 1, 9, 0xd9); + displayAsyncMessage(dsAddr_aaahhhMsg, 30, 12, 1, 9, textColorMansionGuard); // "Aaaaaaaaaaaaahhh!" playSound(35, 1); playActorAnimation(622, true); playAnimation(624, 0, true); waitAnimation(); wait(150); - displayMessage(0x3afd); - - inventory->remove(43); - processCallback(0x9d45); - return true; + displayMessage(dsAddr_oopsMsg); // "Oops" + inventory->remove(kInvItemGrapplingHook); + fnMansionIntrusionAttempt(); + break; - case 0x8312: //hedgehog + plastic apple - Dialog::showMark(scene, 0x3000); + case 0x8312: // hedgehog + plastic apple + dialog->showMark(76, scene); setLan(1, 0); playSound(5, 24); playSound(26, 32); @@ -2405,13 +3341,13 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); disableObject(6); - displayMessage(0x363f); - inventory->remove(27); - inventory->add(28); - return true; + displayMessage(dsAddr_lifeIsBrutalMsg); // "Life is brutal" + inventory->remove(kInvItemPlasticApple); + inventory->add(kInvItemCone); + break; case 0x839f: - inventory->remove(32); + inventory->remove(kInvItemDart); playSound(37, 14); playSound(16, 17); playActorAnimation(564, true); @@ -2437,10 +3373,10 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(55, 18); playAnimation(581, 1); disableObject(2); - SET_FLAG(0xDB9F, 1); - return true; + SET_FLAG(dsAddr_beesGoneFlag, 1); + break; - case 0x84c7: //using paddle on boat + case 0x84c7: // using paddle on boat playSound(20, 9); playActorAnimation(530); loadScene(16, 236, 95, 1); @@ -2451,12 +3387,12 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(533); setOns(0, 9); moveTo(236, 95, 1, true); - return true; + break; - case 0x8538://Sharpen sickle on well + case 0x8538: // Sharpen sickle on well moveTo(236, 190, 0); setOns(2, 0); - //TODO: Remove handle sprite + // FIXME: Add code to Remove handle sprite (visible GFX glitch) playSound(5, 4); playSound(14, 14); playSound(14, 33); @@ -2464,52 +3400,58 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(643); setOns(2, 43); moveTo(236, 179, 3); - inventory->remove(0x2c); - inventory->add(0x2e); - return true; + inventory->remove(kInvItemSickleBlunt); + inventory->add(kInvItemSickleSharp); + break; + + case 0x85d6: + displayMessage(dsAddr_paddleBrokenMsg); // "The paddle is BROKEN" + break; case 0x85eb: - if (CHECK_FLAG(0xDBB0, 1)) { + if (CHECK_FLAG(dsAddr_squirrelNutState, 1)) { enableObject(6); playSound(25, 10); playSound(25, 14); playSound(25, 18); playActorAnimation(559); setOns(1, 23); - SET_FLAG(0xDBB0, 2); + SET_FLAG(dsAddr_squirrelNutState, 2); } else - displayMessage(0x3d86); - - return true; + displayMessage(dsAddr_dontWorkPurposeMsg); // "I usually don't work without a purpose" + break; case 0x863d: playSound(12, 4); playSound(50, 20); playSound(50, 29); playActorAnimation(554); - inventory->remove(19); - inventory->add(22); - return true; + inventory->remove(kInvItemChocCandy); + inventory->add(kInvItemHeartShapedCandy); + break; case 0x8665: playSound(5, 3); for (byte i = 12; i <= 24; i += 2) playSound(56, i); playActorAnimation(567); - inventory->remove(12); - inventory->add(33); - return true; + inventory->remove(kInvItemFeatherDusterClean); + inventory->add(kInvItemFeatherDusterDirty); + break; case 0x862c: - displayMessage(CHECK_FLAG(0xDBB0, 1) ? 0x4882 : 0x3457); - return true; + if (CHECK_FLAG(dsAddr_squirrelNutState, 1)) + displayMessage(dsAddr_nutRakeMsg); // "It's pointless, the nut will slip between the rake's teeth" + else + displayMessage(dsAddr_objErrorMsg); // "That's no good" + break; - case 0x86a9: //correcting height of the pole with spanner - if (CHECK_FLAG(0xDB92, 1)) { - displayMessage(0x3d40); + case 0x86a9: // correcting height of the pole with spanner + if (CHECK_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1)) { + displayMessage(dsAddr_noNeedMsg); // "No need to do it again" } else { - SET_FLAG(0xDB92, 1); - Dialog::show(scene, 0x0fcd, 0, 502, 0xd0, 0xe5, 0, 1); + SET_FLAG(dsAddr_alreadyAdjustedHoopPoleFlag, 1); + dialog->show(17, scene, 0, 502, textColorMark, textColorSonny, 0, 1); waitLanAnimationFrame(1, 7); playSound(5, 16); playSound(1, 25); @@ -2528,7 +3470,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(5, 43); playSound(61, 70); playSound(61, 91); - displayAsyncMessage(0x3cfb, 28877, 6, 17); + displayAsyncMessage(dsAddr_ConfusionMsg, 77, 90, 6, 17); // "!?&!" playActorAnimation(505, true); playAnimation(507, 0, true); waitAnimation(); @@ -2552,8 +3494,8 @@ bool TeenAgentEngine::processCallback(uint16 addr) { disableObject(15); disableObject(16); moveTo(162, 164, 2); - displayMessage(0x3d01, 0xe5, 24390); - displayMessage(0x3d20, 0xd8, 24410); + displayMessage(dsAddr_grandpaPromiseMsg, textColorSonny, 70, 76); // "But grandpa, you promised!" + displayMessage(dsAddr_ohLetsGoMsg, textColorGrandpa, 90, 76); // "Oh all right. Let's go" moveTo(162, 191, 2); setOns(1, 0); setOns(2, 0); @@ -2572,133 +3514,118 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(512, 0); wait(100); - displayMessage(0x3d3a); + displayMessage(dsAddr_byeMsg); // "Bye." { Object *obj = scene->getObject(7); - obj->actor_rect.left = obj->actor_rect.right = 228; - obj->actor_rect.top = obj->actor_rect.bottom = 171; - obj->actor_rect.save(); + obj->actorRect.left = obj->actorRect.right = 228; + obj->actorRect.top = obj->actorRect.bottom = 171; + obj->actorRect.save(); } { Object *obj = scene->getObject(8); - obj->actor_rect.left = obj->actor_rect.right = 290; - obj->actor_rect.top = obj->actor_rect.bottom = 171; - obj->actor_rect.save(); + obj->actorRect.left = obj->actorRect.right = 290; + obj->actorRect.top = obj->actorRect.bottom = 171; + obj->actorRect.save(); } } - return true; + break; - case 0x88c9: //give flower to old lady - if (CHECK_FLAG(0xDB9A, 1)) - return processCallback(0x890b); - - inventory->remove(10); - SET_FLAG(0xDB9A, 1); - processCallback(0x88DE); - return true; - - case 0x88de: - playSound(5, 2); - Dialog::show(scene, 0x1B5F, 0, 523, 0xd1, 0xe5, 0, 1); - playActorAnimation(537, true); - playAnimation(538, 0, true); - waitAnimation(); - wait(100); - Dialog::show(scene, 0x1BE0, 0, 523, 0xd1, 0xe5, 0, 1); - return true; + case 0x88c9: // give flower to old lady + if (CHECK_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 1)) + fnGiveAnotherFlowerToOldLady(); + else { + inventory->remove(kInvItemFirstFlower); + SET_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 1); + fnGivingFlowerToOldLady(); + } + break; - case 0x890b: - Dialog::pop(scene, 0xDAF0, 0, 523, 0xd1, 0xe5, 0, 1); - return true; + case csAddr_givingFlowerToOldLady: + fnGivingFlowerToOldLady(); + break; - case 0x8918://give flower to old lady - if (CHECK_FLAG(0xDB9A, 1)) - return processCallback(0x890B); + case csAddr_giveAnotherFlowerToOldLady: + fnGiveAnotherFlowerToOldLady(); + break; - inventory->remove(11); - SET_FLAG(0xDB9A, 1); - processCallback(0x88DE); - return true; + case 0x8918: // give flower to old lady + if (CHECK_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 1)) + fnGiveAnotherFlowerToOldLady(); + else { + inventory->remove(kInvItemSecondFlower); + SET_FLAG(dsAddr_givenFlowerToOldLadyAlreadyFlag, 1); + fnGivingFlowerToOldLady(); + } + break; case 0x892d: - if (CHECK_FLAG(0xDB9B, 1)) - return processCallback(0x89aa); + if (CHECK_FLAG(dsAddr_givenFlowerToAnneAlreadyFlag, 1)) + fnGiveAnotherFlowerToAnne(); + else { + fnGivingFlowerToAnne(); + inventory->remove(kInvItemFirstFlower); + SET_FLAG(dsAddr_givenFlowerToAnneAlreadyFlag, 1); + } + break; - processCallback(0x8942); - inventory->remove(10); - SET_FLAG(0xDB9B, 1); - return true; + case csAddr_givingFlowerToAnne: + fnGivingFlowerToAnne(); + break; - case 0x8942: - Dialog::show(scene, 0x2293, 0, 524, 0xd1, 0xe5, 0, 2); - playSound(5, 10); - playActorAnimation(540, true); - playAnimation(539, 1, true); - waitAnimation(); - wait(100); - Dialog::show(scene, 0x24b1, 0, 524, 0xd1, 0xe5, 0, 2); - wait(50); - Dialog::show(scene, 0x24d7, 0, 524, 0xd1, 0xe5, 0, 2); - Dialog::show(scene, 0x2514, 0, 524, 0xd1, 0xe5, 0, 2); - wait(50); - moveRel(0, 1, 0); - Dialog::show(scene, 0x2570, 0, 524, 0xd1, 0xe5, 0, 2); - moveRel(0, -1, 0); - wait(50); - return true; - - case 0x89aa: - Dialog::pop(scene, 0xdb02, 0, 524, 0xd1, 0xe5, 0, 2); - return true; + case csAddr_giveAnotherFlowerToAnne: + fnGiveAnotherFlowerToAnne(); + break; case 0x89b7: - if (CHECK_FLAG(0xDB9B, 1)) - return processCallback(0x89aa); - - processCallback(0x8942); - inventory->remove(11); - SET_FLAG(0xDB9B, 1); - return true; + if (CHECK_FLAG(dsAddr_givenFlowerToAnneAlreadyFlag, 1)) + fnGiveAnotherFlowerToAnne(); + else { + fnGivingFlowerToAnne(); + inventory->remove(kInvItemSecondFlower); + SET_FLAG(dsAddr_givenFlowerToAnneAlreadyFlag, 1); + } + break; case 0x89cc: - inventory->remove(23); + inventory->remove(kInvItemWrappedCandy); playSound(5, 6); - Dialog::show(scene, 0x2634, 0, 524, 0xd1, 0xe5, 0, 2); + dialog->show(60, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + // FIXME - Dialog #61 not explicitly called. Does Dialog #60 run on somehow? playActorAnimation(555, true); playAnimation(556, 1, true); waitAnimation(); playActorAnimation(557, true); playAnimation(558, 1, true); waitAnimation(); - Dialog::show(scene, 0x2971, 0, 524, 0xd1, 0xe5, 0, 2); - inventory->add(24); - return true; + dialog->show(62, scene, 0, 524, textColorMark, textColorAnne, 0, 2); + inventory->add(kInvItemRibbon); + break; case 0x8a22: playSound(45, 16); playActorAnimation(560); - inventory->remove(26); - inventory->add(27); + inventory->remove(kInvItemNut); + inventory->add(kInvItemPlasticApple); wait(50); - Dialog::show(scene, 0x1ecd, 0, 523, 0xd1, 0xe5, 0, 1); - Dialog::show(scene, 0x1f09, 0, 523, 0xd1, 0xe5, 0, 1); - SET_FLAG(0xDBB1, 1); - return true; - - case 0x8a6f: //banknote + ann - if (CHECK_FLAG(0xDBB5, 1)) { - Dialog::show(scene, 0x2992, 0, 524, 0xd1, 0xe5, 0, 2); + dialog->show(44, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); + dialog->show(45, scene, 0, 523, textColorMark, textColorOldLady, 0, 1); + SET_FLAG(dsAddr_nutSwappedForAppleFlag, 1); + break; + + case 0x8a6f: // banknote + ann + if (CHECK_FLAG(dsAddr_examinedBanknoteFlag, 1)) { + dialog->show(63, scene, 0, 524, textColorMark, textColorAnne, 0, 2); playSound(5, 3); playSound(5, 20); playAnimation(671, 1, true); playActorAnimation(670, true); waitAnimation(); //playAnimation(672, 1); - Dialog::show(scene, 0x2a00, 524, 672, 0xd1, 0xe5, 0, 2); + dialog->show(64, scene, 524, 672, textColorMark, textColorAnne, 0, 2); //playAnimation(672, 1); playSound(83, 12); - displayAsyncMessage(0x4a5b, 36684, 23, 38, 0xe5); + displayAsyncMessage(dsAddr_hundredBucksMsg, 204, 114, 23, 38, textColorAnne); // "A hundred bucks!!!" playActorAnimation(673); loadScene(11, scene->getPosition()); playSound(24, 31); @@ -2710,14 +3637,14 @@ bool TeenAgentEngine::processCallback(uint16 addr) { loadScene(28, 0, 167, 2); playMusic(10); moveTo(66, 167, 2); - displayMessage(0x4a6f); + displayMessage(dsAddr_wantBloodMsg); // "I want Blood!" inventory->clear(); - inventory->add(29); + inventory->add(kInvItemSuperGlue); } else - displayMessage(0x4a29); - return true; + displayMessage(dsAddr_showHerMoneyMsg); // "If I just show her the money, she might take it" + break; - case 0x8b82: //use fan on laundry + case 0x8b82: // use fan on laundry setOns(0, 0); playSound(5, 3); playSound(5, 6); @@ -2725,16 +3652,16 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(92, 20); playSound(92, 38); playSound(92, 58); - displayAsyncMessage(0x464a, 36510, 58, 67); + displayAsyncMessage(dsAddr_yawnMsg, 30, 114, 58, 67); // "(yawn)" playActorAnimation(602); playSound(5, 3); playActorAnimation(603); setOns(0, 27); - SET_FLAG(0xDBA5, 1); - return true; + SET_FLAG(dsAddr_laundryState, 1); + break; - case 0x8bfc://Give bone to dog - displayMessage(0x3c31); + case 0x8bfc: // Give bone to dog + displayMessage(dsAddr_hereBoyMsg); // "Here, boy" playSound(5, 3); playSound(26, 13); playActorAnimation(657, true); @@ -2744,20 +3671,20 @@ bool TeenAgentEngine::processCallback(uint16 addr) { reloadLan(); playAnimation(659, 0); - inventory->remove(36); - SET_FLAG(0xDBAD, 1); + inventory->remove(kInvItemBone); + SET_FLAG(dsAddr_dogHasBoneFlag, 1); { Object *o = scene->getObject(7); - o->actor_rect.left = o->actor_rect.right = 297; - o->actor_rect.top = o->actor_rect.bottom = 181; - o->actor_orientation = 1; + o->actorRect.left = o->actorRect.right = 297; + o->actorRect.top = o->actorRect.bottom = 181; + o->actorOrientation = 1; o->save(); } { Object *o = scene->getObject(9); - o->actor_rect.left = o->actor_rect.right = 297; - o->actor_rect.top = o->actor_rect.bottom = 181; - o->actor_orientation = 1; + o->actorRect.left = o->actorRect.right = 297; + o->actorRect.top = o->actorRect.bottom = 181; + o->actorOrientation = 1; o->save(); } { @@ -2767,10 +3694,10 @@ bool TeenAgentEngine::processCallback(uint16 addr) { w->save(); } wait(100); - displayMessage(0x3c3d); - return true; + displayMessage(dsAddr_friendsNowMsg); // "I hope we're friends now" + break; - case 0x8c6e://Use car jack on rock + case 0x8c6e: // Use car jack on rock playSound(5, 3); playSound(26, 13); playSound(24, 22); @@ -2782,10 +3709,10 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(593); setOns(0, 28); enableObject(1); - inventory->remove(35); - return true; + inventory->remove(kInvItemCarJack); + break; - case 0x8cc8://Cut bush with sickle + case 0x8cc8: // Cut bush with sickle playSound(5, 3); playActorAnimation(644); setOns(1, 45); @@ -2799,16 +3726,16 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(646); playSound(5, 21); playActorAnimation(647); - SET_FLAG(0xdaca, 1); - inventory->remove(0x2e); + SET_FLAG(dsAddr_caveThornsCutDownFlag, 1); + inventory->remove(kInvItemSickleSharp); disableObject(2); - scene->getObject(3)->actor_rect.right = 156; + scene->getObject(3)->actorRect.right = 156; scene->getObject(3)->save(); - return true; + break; - case 0x8d79: //mouse falls back from the hole (cave) - if (CHECK_FLAG(0, 1)) { - inventory->add(48); + case csAddr_mouseOutOfHoleTimeout: // mouse falls back from the hole (cave) + if (CHECK_FLAG(dsAddr_timedCallbackState, 1)) { + inventory->add(kInvItemMouse); playSound(24, 26); playActorAnimation(650, true); playAnimation(651, 2, true); @@ -2824,54 +3751,26 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(654, true); playAnimation(655, 2, true); waitAnimation(); - displayMessage(0x3bf6); - inventory->add(49); + displayMessage(dsAddr_mouseGoneMsg); // "The mouse has gone!" + inventory->add(kInvItemRock); setLan(2, 4, 27); enableObject(4, 27); - SET_FLAG(0xdba9, 0); + SET_FLAG(dsAddr_mouseHoleState, 0); } - SET_FLAG(0, 0); - return true; + SET_FLAG(dsAddr_timedCallbackState, 0); + break; - case 0x8d57: - if (CHECK_FLAG(0, 0)) { - playSound(5, 2); - playSound(15, 12); - playActorAnimation(638); - inventory->remove(48); - setTimerCallback(0x8d79, 100); - SET_FLAG(0, 1); - } else if (CHECK_FLAG(0, 1)) { - playSound(5, 2); - playSound(52, 13); - playActorAnimation(648); - setOns(1, 46); - inventory->remove(49); - setTimerCallback(0x8d79, 100); - SET_FLAG(0, 2); - } else if (CHECK_FLAG(0, 2)) { - playActorAnimation(649); - setOns(1, 47); - wait(300); - for (byte i = 1; i <= 37; i += 4) - playSound(68, i); - playAnimation(639, 2); - setOns(0, 42); - enableObject(6); - disableObject(5); - SET_FLAG(0xDBAB, 1); - SET_FLAG(0, 0); - setTimerCallback(0, 0); - } - return true; + case csAddr_putRockInHole: + fnPutRockInHole(); + break; case 0x8f1d: - Dialog::showMark(scene, 0x2dd6); + dialog->showMark(72, scene); for (uint i = 16; i <= 30; i += 2) playSound(56, i); playSound(2, 64); playSound(3, 74); - displayAsyncMessage(0x34c7, 25812, 35, 50); + displayAsyncMessage(dsAddr_lastChanceMsg, 212, 80, 35, 50); // "Last chance?" playActorAnimation(516, true); playAnimation(517, 2, true); playAnimation(518, 3, true); @@ -2880,12 +3779,12 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setLan(4, 0); disableObject(2); disableObject(3); - inventory->remove(2); - SET_FLAG(0xDB96, 1); - return true; + inventory->remove(kInvItemShotgun); + SET_FLAG(dsAddr_birdsGoneFromScarecrowFlag, 1); + break; case 0x8fc8: - displayMessage(0x3b2f); + displayMessage(dsAddr_comeHereMsg); // "Come here, I've got something for you" waitLanAnimationFrame(2, 4); playSound(5, 3); playActorAnimation(627, true); @@ -2894,25 +3793,25 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(41, 10); playSound(41, 47); playSound(55, 52); - if (CHECK_FLAG(0xDBA8, 1)) { + if (CHECK_FLAG(dsAddr_HankerchiefInMouseholeFlag, 1)) { setLan(2, 0); playActorAnimation(628, true); playAnimation(634, 1, true); waitAnimation(); disableObject(4); - displayMessage(0x3b6c); - SET_FLAG(0xDBA9, 1); + displayMessage(dsAddr_trappedMouseMsg); // "The mouse is trapped!" + SET_FLAG(dsAddr_mouseHoleState, 1); } else { playActorAnimation(628, true); playAnimation(630, 1, true); waitAnimation(); - displayMessage(0x3b59); + displayMessage(dsAddr_cantCatchMsg); // "I can't catch it!" } - return true; + break; - case 0x9054: //mouse hole - if (CHECK_FLAG(0xDBAB, 1)) { - displayMessage(0x3c0b); + case 0x9054: // mouse hole + if (CHECK_FLAG(dsAddr_mouseGotGoldNuggetFlag, 1)) { + displayMessage(dsAddr_nonsenseMsg); // "Nonsense" } else { playSound(5, 11); playSound(49, 21); @@ -2920,50 +3819,48 @@ bool TeenAgentEngine::processCallback(uint16 addr) { setOns(5, 40); moveTo(239, 139, 0, true); playActorAnimation(633); - SET_FLAG(0xDBA8, 1); - inventory->remove(47); - if (!CHECK_FLAG(0xDBAA, 1)) { - SET_FLAG(0xDBAA, 1); - displayMessage(0x3b8b); + SET_FLAG(dsAddr_HankerchiefInMouseholeFlag, 1); + inventory->remove(kInvItemHandkerchief); + if (!CHECK_FLAG(dsAddr_mouseNerveMsgSaidFlag, 1)) { + SET_FLAG(dsAddr_mouseNerveMsgSaidFlag, 1); + displayMessage(dsAddr_mouseNerveMsg); // "Boy, this mouse has some nerve!" } } - return true; + break; case 0x933d: - if (!processCallback(0x70e0)) - return true; - - if (CHECK_FLAG(0xdbcd, 1)) { - displayMessage(0x4f3d); - return true; + if (fnIsCookGone()) { + if (CHECK_FLAG(dsAddr_MansionRadioBrokenFlag, 1)) + displayMessage(dsAddr_breakFlattenMsg); // "I wanted to break it, not to flatten it!" + else { + setOns(1, 0); + playSound(5, 3); + playSound(5, 33); + playSound(24, 13); + playSound(24, 19); + playSound(24, 23); + playSound(24, 26); + playSound(24, 29); + playSound(23, 21); + playSound(74, 25); + playActorAnimation(716); + setOns(1, 66); + SET_FLAG(dsAddr_MansionRadioBrokenFlag, 1); + } } + break; - setOns(1, 0); - playSound(5, 3); - playSound(5, 33); - playSound(24, 13); - playSound(24, 19); - playSound(24, 23); - playSound(24, 26); - playSound(24, 29); - playSound(23, 21); - playSound(74, 25); - playActorAnimation(716); - setOns(1, 66); - SET_FLAG(0xDBCD, 1); - return true; - - case 0x93af: //sheet + hot plate - if (!processCallback(0x70e0)) - return true; - playSound(5, 3); - playSound(86, 11); - playActorAnimation(720); - inventory->add(68); - inventory->remove(55); - return true; + case 0x93af: // sheet + hot plate + if (fnIsCookGone()) { + playSound(5, 3); + playSound(86, 11); + playActorAnimation(720); + inventory->add(kInvItemBurningPaper); + inventory->remove(kInvItemSheetOfPaper); + } + break; - case 0x93d5: //burning sheet + plate + case 0x93d5: // burning sheet + plate setOns(4, 0); playSound(87, 7); playActorAnimation(722); @@ -2971,323 +3868,298 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(88, 12); playSound(87, 24); playActorAnimation(723); - displayMessage(0x502b); + displayMessage(dsAddr_burnBabyMsg); // "Burn, baby, burn!" wait(100); playSound(89, 4); playActorAnimation(724); setOns(4, 68); - displayMessage(0x503e); - inventory->remove(68); - SET_FLAG(0xDBD0, 1); - return true; - - case 0x98fa://Right click to open toolbox - inventory->remove(3); - inventory->add(4); - inventory->add(35); + displayMessage(dsAddr_voilaMsg); // "Voila" + inventory->remove(kInvItemBurningPaper); + SET_FLAG(dsAddr_MansionPutBurningPaperInFridgeFlag, 1); + break; + + case csAddr_openFullToolbox: // Right click to open toolbox + inventory->remove(kInvItemToolboxFull); + inventory->add(kInvItemToolboxHalfEmpty); + inventory->add(kInvItemCarJack); inventory->activate(false); inventory->resetSelectedObject(); - displayMessage(0x3468); - return true; + displayMessage(dsAddr_carJackMsg); // "Wow! There's a car jack inside! Great!" + break; - case 0x9910: - inventory->remove(4); - inventory->add(5); + case csAddr_openHalfEmptyToolbox: + inventory->remove(kInvItemToolboxHalfEmpty); + inventory->add(kInvItemSpanner); inventory->activate(false); inventory->resetSelectedObject(); - displayMessage(0x3490); - return true; + displayMessage(dsAddr_spannerMsg); // "There's something else inside the toolbox! A spanner!" + break; - - //very last part of the game: - case 0x671d: + case 0x671d: // very last part of the game moveTo(153, 163, 4); playActorAnimation(973); - if (CHECK_FLAG(0xDBC1, 0)) { - SET_FLAG(0xDBC1, _rnd.getRandomNumber(5) + 1); + if (CHECK_FLAG(dsAddr_drawerPuzzleBookValue, 0)) { + SET_FLAG(dsAddr_drawerPuzzleBookValue, _rnd.getRandomNumber(5) + 1); } loadScene(30, 18, 159, 2); - return true; + break; case 0x67a6: loadScene(29, 149, 163, 1); playActorAnimation(974); moveTo(160, 188, 0); - return true; + break; case 0x6805: - processCallback(0x6849); + fnEgoBottomRightTurn(); playSound(32, 12); playActorAnimation(694); playSound(15, 8); playAnimation(693, 0); setOns(6, 0); - displayMessage(0x4cc7); - inventory->add(54); + displayMessage(dsAddr_fullAutomaticMsg); // "Fully Automatic" + inventory->add(kInvItemVideoTape); disableObject(4); - return true; + break; - case 0x6849: { - Common::Point p = scene->getPosition(); - if (p.x == 208 && p.y == 151) { - moveRel(0, 0, 2); - } else - moveTo(208, 151, 1); - } - return true; + case csAddr_egoBottomRightTurn: + fnEgoBottomRightTurn(); + break; - case 0x687a: //using the book - if (CHECK_FLAG(0xDBC2, 1)) { - displayMessage(0x4ca0); + case 0x687a: // using the book + if (CHECK_FLAG(dsAddr_drawerPuzzleSolvedFlag, 1)) { + displayMessage(dsAddr_dontMessMsg); // "I don't need to mess with it anymore" } else { playSound(49, 5); playSound(49, 17); playActorAnimation(691); - if (!processCallback(0x68e6)) { - if (!CHECK_FLAG(0xDBC0, 1)) { - displayMessage(0x4c61); - SET_FLAG(0xDBC0, 1); + if (!fnCheckingDrawers()) { + if (!CHECK_FLAG(dsAddr_drawerPuzzleBookMessageFlag, 1)) { + displayMessage(dsAddr_bookHeldMsg); // "Something's got hold of the book!" + SET_FLAG(dsAddr_drawerPuzzleBookMessageFlag, 1); } } else { - playSound(15, 8); //secret compartment + playSound(15, 8); // secret compartment playAnimation(692, 0); setOns(6, 59); enableObject(4); - displayMessage(0x4c84); - SET_FLAG(0xDBC2, 1); + displayMessage(dsAddr_secretCompartmentMsg); // "Wow! A secret compartment!" + SET_FLAG(dsAddr_drawerPuzzleSolvedFlag, 1); } } - return true; + break; - case 0x68e6: { //checking drawers - uint16 v = GET_FLAG(0xDBC1) - 1; - uint bx = 0xDBB7; - if (GET_FLAG(bx + v) != 1) - return false; - - uint16 sum = 0; - for (uint i = 0; i < 6; ++i) { - sum += GET_FLAG(bx + i); - } - return sum == 1; - } + case csAddr_checkingDrawers: + fnCheckingDrawers(); + break; case 0x6918: - if (inventory->has(55)) { - displayMessage(0x4cd9); - return true; - } - if (!CHECK_FLAG(0xDBC3, 1)) { - playActorAnimation(695); - Dialog::showMark(scene, 0x386a); - SET_FLAG(0xDBC3, 1); - } + if (inventory->has(kInvItemSheetOfPaper)) + displayMessage(dsAddr_noMoreSheetsMsg); // "Right now I don't need any more sheets" + else { + if (!CHECK_FLAG(dsAddr_mansionTrashcanSearchedFlag, 1)) { + playActorAnimation(695); + dialog->showMark(91, scene); + SET_FLAG(dsAddr_mansionTrashcanSearchedFlag, 1); + } - playSound(5, 11); - playActorAnimation(696); - inventory->add(55); - return true; + playSound(5, 11); + playActorAnimation(696); + inventory->add(kInvItemSheetOfPaper); + } + break; case 0x6962: - if (CHECK_FLAG(0xDBB7, 1)) { + if (CHECK_FLAG(dsAddr_blueDrawerOpenFlag, 1)) { setOns(0, 0); playSound(67, 4); playActorAnimation(678); - SET_FLAG(0xDBB7, 0); - } else if (CHECK_FLAG(0xDBB8, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_blueDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_redDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(66, 4); playActorAnimation(677); setOns(0, 53); - SET_FLAG(0xDBB7, 1); + SET_FLAG(dsAddr_blueDrawerOpenFlag, 1); } - return true; + break; case 0x69b8: - if (CHECK_FLAG(0xDBB8, 1)) { + if (CHECK_FLAG(dsAddr_redDrawerOpenFlag, 1)) { setOns(1, 0); playSound(67, 4); playActorAnimation(680); - SET_FLAG(0xDBB8, 0); - } else if (CHECK_FLAG(0xDBB7, 1)) { - processCallback(0x6b86); - } else if (CHECK_FLAG(0xDBB9, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_redDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_blueDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); + } else if (CHECK_FLAG(dsAddr_greyDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(66, 5); playActorAnimation(679); setOns(1, 54); - SET_FLAG(0xDBB8, 1); + SET_FLAG(dsAddr_redDrawerOpenFlag, 1); } - return true; + break; case 0x6a1b: - if (CHECK_FLAG(0xDBB9, 1)) { + if (CHECK_FLAG(dsAddr_greyDrawerOpenFlag, 1)) { setOns(2, 0); playSound(67, 5); playActorAnimation(682); - SET_FLAG(0xDBB9, 0); - } else if (CHECK_FLAG(0xDBB8, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_greyDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_redDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(67, 5); playActorAnimation(681); setOns(2, 55); - SET_FLAG(0xDBB9, 1); + SET_FLAG(dsAddr_greyDrawerOpenFlag, 1); } - return true; + break; case 0x6a73: - if (CHECK_FLAG(0xDBBA, 1)) { + if (CHECK_FLAG(dsAddr_greenDrawerOpenFlag, 1)) { setOns(3, 0); playSound(67, 4); playActorAnimation(684); - SET_FLAG(0xDBBA, 0); - } else if (!CHECK_FLAG(0xDBBB, 1)) { + SET_FLAG(dsAddr_greenDrawerOpenFlag, 0); + } else if (!CHECK_FLAG(dsAddr_brownDrawerOpenFlag, 1)) { playSound(66, 4); playActorAnimation(683); setOns(3, 56); - SET_FLAG(0xDBBA, 1); + SET_FLAG(dsAddr_greenDrawerOpenFlag, 1); } else - processCallback(0x6b86); - return true; + fnDrawerOpenMessage(); + break; case 0x6acb: - if (CHECK_FLAG(0xDBBB, 1)) { + if (CHECK_FLAG(dsAddr_brownDrawerOpenFlag, 1)) { setOns(4, 0); playSound(67, 4); playActorAnimation(686); - SET_FLAG(0xDBBB, 0); - } else if (CHECK_FLAG(0xDBBA, 1)) { - processCallback(0x6b86); - } else if (CHECK_FLAG(0xDBBC, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_brownDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_greenDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); + } else if (CHECK_FLAG(dsAddr_pinkDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(66, 5); playActorAnimation(685); setOns(4, 57); - SET_FLAG(0xDBBB, 1); + SET_FLAG(dsAddr_brownDrawerOpenFlag, 1); } - return true; + break; case 0x6b2e: - if (CHECK_FLAG(0xdbbc, 1)) { + if (CHECK_FLAG(dsAddr_pinkDrawerOpenFlag, 1)) { setOns(5, 0); playSound(67, 5); playActorAnimation(688); - SET_FLAG(0xdbbc, 0); - } else if (CHECK_FLAG(0xdbbb, 1)) { - processCallback(0x6b86); + SET_FLAG(dsAddr_pinkDrawerOpenFlag, 0); + } else if (CHECK_FLAG(dsAddr_brownDrawerOpenFlag, 1)) { + fnDrawerOpenMessage(); } else { playSound(66, 6); playActorAnimation(687); setOns(5, 58); - SET_FLAG(0xDBBC, 1); + SET_FLAG(dsAddr_pinkDrawerOpenFlag, 1); } - return true; - + break; - case 0x6b86: - if (CHECK_FLAG(0xDBBD, 1)) { - displayMessage(0x4b39); - } else { - displayMessage(0x4acd); - displayMessage(0x4b0d); - SET_FLAG(0xDBBD, 1); - } - return true; + case csAddr_DrawerOpenMessage: + fnDrawerOpenMessage(); + break; - case 0x6be1: //handle to the bathroom - if (CHECK_FLAG(0xDBD9, 1)) { - displayMessage(0x5326); //i'd better catch johnny - } else { + case 0x6be1: // handle to the bathroom + if (CHECK_FLAG(dsAddr_MansionJohnNotyEscapingFlag, 1)) + displayMessage(dsAddr_catchJohnFirstMsg); // "I'd better catch John Noty first" + else { playSound(88, 4); playActorAnimation(808); loadScene(36, 41, 195, 2); } - return true; + break; case 0x6bad: playSound(80, 4); playActorAnimation(971); loadScene(32, 139, 199, 1); - return true; + break; case 0x6c45: playSound(89, 6); - playActorAnimation(CHECK_FLAG(0xDBEF, 1) ? 985 : 806); + playActorAnimation(CHECK_FLAG(dsAddr_mansionHandleInDoorHoleFlag, 1) ? 985 : 806); loadScene(34, 40, 133, 2); - return true; + break; case 0x6c83: waitLanAnimationFrame(1, 1); - Dialog::pop(scene, 0xdb2e, 0, 727, 0xd1, 0xef, 0, 1); - scene->getObject(1)->setName((const char *)res->dseg.ptr(0xaa94)); - SET_FLAG(0xDBD1, 1); - return true; + dialog->pop(scene, dsAddr_dialogStackRobotSafe, 0, 727, textColorMark, textColorMike, 0, 1); + scene->getObject(1)->setName((const char *)res->dseg.ptr(dsAddr_scnObjNameMike)); + SET_FLAG(dsAddr_MansionRobotSafeUnlockedFlag, 1); + break; - case 0x6c9d: //getting jar + case 0x6c9d: // getting jar setOns(0, 71); playSound(32, 5); playActorAnimation(732); disableObject(2); - inventory->add(72); - return true; + inventory->add(kInvItemTimePills); + break; - case 0x6cc4: //secret diary + case 0x6cc4: // secret diary playActorAnimation(754); hideActor(); - displayCutsceneMessage(0x517b, 30430); + displayCutsceneMessage(dsAddr_cutsceneMsg0, 30, 95); // "A secret diary of ..." playMusic(3); loadScene(11, scene->getPosition()); playAnimation(750, 2); - Dialog::show(scene, 0x4f50, 751, 529, 0xe5, 0xd9, 2, 1); + dialog->show(117, scene, 751, 529, textColorProfessor, textColorMansionGuard, 2, 1); playAnimation(752, 0, true); playAnimation(753, 1, true); waitAnimation(); - Dialog::show(scene, 0x5168, 529, 751, 0xd9, 0xe5, 1, 2); + dialog->show(118, scene, 529, 751, textColorMansionGuard, textColorProfessor, 1, 2); loadScene(30, scene->getPosition()); - Dialog::show(scene, 0x449e, 733, 734, 0xe5, 0xd0, 2, 3); + dialog->show(108, scene, 733, 734, textColorProfessor, textColorJohnNoty, 2, 3); playSound(75, 13); playSound(32, 22); playAnimation(735, 1, true); playAnimation(736, 2, true); waitAnimation(); - Dialog::show(scene, 0x46cf, 737, 738, 0xd0, 0xe5, 3, 2); - + dialog->show(109, scene, 737, 738, textColorJohnNoty, textColorProfessor, 3, 2); playSound(32, 1); playAnimation(739, 1, true); playAnimation(740, 2, true); waitAnimation(); - Dialog::show(scene, 0x4772, 733, 734, 0xe5, 0xd0, 2, 3); + dialog->show(110, scene, 733, 734, textColorProfessor, textColorJohnNoty, 2, 3); playAnimation(742, 1, true); playAnimation(741, 2, true); waitAnimation(); - Dialog::show(scene, 0x481c, 743, 733, 0xd0, 0xe5, 3, 2); //where's my wallet?? + dialog->show(111, scene, 743, 733, textColorJohnNoty, textColorProfessor, 3, 2); playAnimation(744, 1, true); playAnimation(745, 2, true); waitAnimation(); - Dialog::show(scene, 0x4873, 734, 733, 0xd0, 0xe5, 3, 2); + dialog->show(112, scene, 734, 733, textColorJohnNoty, textColorProfessor, 3, 2); playAnimation(746, 1, true); playAnimation(747, 2, true); waitAnimation(); - - Dialog::show(scene, 0x4da5, 734, 734, 0xd0, 0xd0, 3, 3); - Dialog::show(scene, 0x4eb9, 748, 748, 0xd0, 0xd0, 3, 3); - Dialog::show(scene, 0x4f15, 749, 749, 0xd0, 0xd0, 3, 3); - Dialog::show(scene, 0x4f2f, 748, 748, 0xd0, 0xd0, 3, 3); + dialog->show(113, scene, 734, 734, textColorJohnNoty, textColorJohnNoty, 3, 3); + dialog->show(114, scene, 748, 748, textColorJohnNoty, textColorJohnNoty, 3, 3); + dialog->show(115, scene, 749, 749, textColorJohnNoty, textColorJohnNoty, 3, 3); + dialog->show(116, scene, 748, 748, textColorJohnNoty, textColorJohnNoty, 3, 3); playMusic(10); loadScene(32, scene->getPosition()); @@ -3296,35 +4168,34 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(755); moveRel(0, 0, 3); - Dialog::show(scene, 0x51bf, 0, 0, 0xd1, 0xd1, 0, 0); + dialog->showMark(119, scene); hideActor(); loadScene(31, scene->getPosition()); - Dialog::show(scene, 0x539f, 763, 764, 0xd9, 0xd0, 1, 2); + dialog->show(123, scene, 763, 764, textColorMansionGuard, textColorJohnNoty, 1, 2); loadScene(32, scene->getPosition()); showActor(); - Dialog::show(scene, 0x52c3, 0, 0, 0xd1, 0xd1, 0, 0); //i have to hide somewhere + dialog->showMark(120, scene); disableObject(3); enableObject(7); - SET_FLAG(0xDBD5, 1); - return true; + SET_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 1); + break; case 0x6f20: - if (CHECK_FLAG(0xDBD5, 1)) { - displayMessage(0x51a7); - } else { + if (CHECK_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 1)) + displayMessage(dsAddr_cantHideMsg); // "I can't hide here!" + else rejectMessage(); - } - return true; + break; - case 0x6f75: //hiding in left corner + case 0x6f75: // hiding in left corner moveRel(0, 0, 3); playActorAnimation(756); hideActor(); playAnimation(758, 1); - Dialog::show(scene, 0x52e6, 759, 759, 0xd0, 0xd0, 2, 2); //I have to buy... + dialog->show(121, scene, 759, 759, textColorJohnNoty, textColorJohnNoty, 2, 2); playSound(40, 5); playSound(52, 13); @@ -3340,272 +4211,265 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(58, 12); playSound(58, 14); playAnimation(765, 1); - Dialog::show(scene, 0x5443, 766, 766, 0xd9, 0xd9, 1, 1); + dialog->show(124, scene, 766, 766, textColorMansionGuard, textColorMansionGuard, 1, 1); loadScene(32, scene->getPosition()); - Dialog::show(scene, 0x5358, 761, 761, 0xd0, 0xd0, 2, 2); + dialog->show(122, scene, 761, 761, textColorJohnNoty, textColorJohnNoty, 2, 2); playAnimation(762, 1); setOns(2, 0); showActor(); playActorAnimation(757); moveRel(0, 0, 1); - displayMessage(0x51e7); + displayMessage(dsAddr_wasCloseMsg); // "That was close" enableObject(8); disableObject(7); - SET_FLAG(0xDBD5, 0); - return true; + SET_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 0); + break; case 0x6f4d: - if (CHECK_FLAG(0xDBD5, 1)) { - displayMessage(0x51bb); - } else { + if (CHECK_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 1)) + displayMessage(dsAddr_johnOutsideMsg); // "There's John Noty outside! I can't go out!" + else loadScene(31, 139, 172, 3); - } - return true; + break; case 0x6f32: - if (CHECK_FLAG(0xDBD5, 1)) { - displayMessage(0x51a7); + if (CHECK_FLAG(dsAddr_MansionJohnNotyOutsideBathroomFlag, 1)) { + displayMessage(dsAddr_cantHideMsg); // "I can't hide here!" } else { playActorAnimation(977); - displayMessage(0x5511); + displayMessage(dsAddr_lockedMsg); // "It's Locked!" } - return true; + break; case 0x7096: playSound(32, 5); playActorAnimation(767); setOns(1, 0); - inventory->add(73); + inventory->add(kInvItemHandle); disableObject(8); - return true; + break; + + case 0x7218: + rejectMessage(); + break; case 0x7291: playSound(89, 3); playActorAnimation(975); loadScene(31, 298, 177, 4); - return true; + break; case 0x72c2: - if (CHECK_FLAG(0xDBD6, 2)) { - displayMessage(0x522c); + if (CHECK_FLAG(dsAddr_MansionSinkState, 2)) { + displayMessage(dsAddr_enoughWaterMsg); // "There's enough water in the sink" } else { playSound(79, 6); playSound(84, 9); playActorAnimation(801); wait(50); - if (CHECK_FLAG(0xDBD6, 1)) { - displayMessage(0x538d); - SET_FLAG(0xDBD6, 2); + if (CHECK_FLAG(dsAddr_MansionSinkState, 1)) { + displayMessage(dsAddr_sinkFullMsg); // "The sink is full of hot water" + SET_FLAG(dsAddr_MansionSinkState, 2); } else - displayMessage(0x5372); + displayMessage(dsAddr_waterHotMsg); // "The water looks very hot" } - return true; + break; case 0x7309: playSound(66, 5); playSound(67, 11); playActorAnimation(976); - displayMessage(0x5955); - return true; + displayMessage(dsAddr_emptyMsg); // "It's Empty" + break; case 0x77d5: - if (CHECK_FLAG(0xdbd7, 1) && !CHECK_FLAG(0xdbd8, 1)) { //disallow exiting through the first door until switch turned on, not present in original game - displayMessage(0x52cb); - return true; + if (CHECK_FLAG(dsAddr_MansionThruFanByTimePillFlag, 1) && !CHECK_FLAG(dsAddr_MansionVentFanStoppedFlag, 1)) { // disallow exiting through the first door until switch turned on, not present in original game + displayMessage(dsAddr_noSaladMsg); // "I don't want to turn myself into a salad" + } else { + playSound(89, 6); + playActorAnimation(978); + loadScene(31, 298, 177, 4); } - playSound(89, 6); - playActorAnimation(978); - loadScene(31, 298, 177, 4); - return true; + break; case 0x79e4: - processCallback(0x6849); - return false; + fnEgoBottomRightTurn(); + retVal = false; + break; - case 0x79eb: //color of the book - displayMessage(res->dseg.get_word(0x5f3c + GET_FLAG(0xDBC1) * 2 - 2)); - return true; + case 0x79eb: // color of the book + // FIXME - Replace with internal lookup and switch + displayMessage(res->dseg.get_word(dsAddr_bookColorMsgPtr + GET_FLAG(dsAddr_drawerPuzzleBookValue) * 2 - 2)); + break; case 0x79fd: - if (CHECK_FLAG(0xDBB7, 1)) { - displayMessage(0x4b6c); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_blueDrawerOpenFlag, 1)) + displayMessage(dsAddr_blueInteriorMsg); // "It's got a blue interior" + else + retVal = false; + break; case 0x7a0f: - if (CHECK_FLAG(0xDBB8, 1)) { - if (!CHECK_FLAG(0xDBBF, 1)) { - displayMessage(0x4c32); + if (CHECK_FLAG(dsAddr_redDrawerOpenFlag, 1)) { + if (!CHECK_FLAG(dsAddr_drawerGotPolaroidFlag, 1)) { + displayMessage(dsAddr_foundPolaroidMsg); // "There's a polaroid inside! I might need that" playSound(5, 11); playActorAnimation(690); - inventory->add(53); - SET_FLAG(0xDBBF, 1); + inventory->add(kInvItemPolaroidCamera); + SET_FLAG(dsAddr_drawerGotPolaroidFlag, 1); } - displayMessage(0x4b87); - return true; + displayMessage(dsAddr_redInteriorMsg); // "It's got a red interior" } else - return false; + retVal = false; + break; case 0x7a49: - if (CHECK_FLAG(0xDBB9, 1)) { - displayMessage(0x4ba1); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_greyDrawerOpenFlag, 1)) + displayMessage(dsAddr_greyInteriorMsg); // "It's got a grey interior" + else + retVal = false; + break; case 0x7a5b: - if (CHECK_FLAG(0xDBBA, 1)) { - displayMessage(0x4bbc); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_greenDrawerOpenFlag, 1)) + displayMessage(dsAddr_greenInteriorMsg); // "It's got a green interior" + else + retVal = false; + break; case 0x7a6d: - if (CHECK_FLAG(0xDBBB, 1)) { - displayMessage(0x4bd8); - return true; - } else - return false; + if (CHECK_FLAG(dsAddr_brownDrawerOpenFlag, 1)) + displayMessage(dsAddr_brownInteriorMsg); // "It's got a brown interior" + else + retVal = false; + break; case 0x7a7f: - if (CHECK_FLAG(0xDBBC, 1)) { - if (!CHECK_FLAG(0xDBBE, 1)) { - displayMessage(0x4c0f); //there's dictaphone inside! + if (CHECK_FLAG(dsAddr_pinkDrawerOpenFlag, 1)) { + if (!CHECK_FLAG(dsAddr_drawerGotDictaphoneFlag, 1)) { + displayMessage(dsAddr_dictaphoneInsideMsg); // "Wow! There's a dictaphone inside!" playSound(5, 12); playActorAnimation(689); - inventory->add(52); - SET_FLAG(0xDBBE, 1); + inventory->add(kInvItemDictaphoneNoBatteries); + SET_FLAG(dsAddr_drawerGotDictaphoneFlag, 1); } - displayMessage(0x4bf4); - return true; + displayMessage(dsAddr_pinkInteriorMsg); // "It's got a pink interior" } else - return false; + retVal = false; + break; case 0x7af7: - if (CHECK_FLAG(0xDBD0, 1)) { - displayMessage(0x5082); - return true; - } else - return false; - - case 0x7b09: { - byte v = GET_FLAG(0xDBD6); - switch (v) { - case 1: - displayMessage(0x51f8); - return true; - case 2: - displayMessage(0x538d); - return true; - default: - return false; - } - } + if (CHECK_FLAG(dsAddr_MansionPutBurningPaperInFridgeFlag, 1)) + displayMessage(dsAddr_yummyMsg); // "Yummy" + else + retVal = false; + break; - case 0x9166: - if (CHECK_FLAG(0xDBD1, 1)) { - return true; - } else { - displayMessage(0x50a6); - return false; + case 0x7b09: + { + byte v = GET_FLAG(dsAddr_MansionSinkState); + switch (v) { + case 1: + displayMessage(dsAddr_corkInHoleMsg); // "The cork is stuck in the hole" + break; + case 2: + displayMessage(dsAddr_sinkFullMsg); // "The sink is full of hot water" + break; + default: + retVal = false; + break; + } } + break; - case 0x9175: - if (CHECK_FLAG(0xDBD2, 0) || CHECK_FLAG(0xDBD3, 0) || CHECK_FLAG(0xDBD4, 0)) - return true; + case csAddr_robotSafeAlreadyUnlockedCheck: + fnRobotSafeAlreadyUnlockedCheck(); + break; - waitLanAnimationFrame(1, 1); - playSound(89, 2); - playActorAnimation(731); - setOns(0, 70); - setLan(1, 0); - disableObject(1); - enableObject(2); - enableObject(3); - return true; + case csAddr_robotSafeUnlockCheck: + fnRobotSafeUnlockCheck(); + break; - case 0x90bc: //handle on the hole + case 0x90bc: // handle on the hole playSound(5, 3); playSound(6, 9); playActorAnimation(807); setOns(0, 83); - inventory->remove(73); + inventory->remove(kInvItemHandle); disableObject(2); enableObject(3); - SET_FLAG(0xDBEF, 1); - return true; - - case 0x90fc: //dictaphone on robot - if (!processCallback(0x9166)) - return true; - - if (CHECK_FLAG(0xDBD2, 1)) { - displayMessage(0x50c3); - return true; - } + SET_FLAG(dsAddr_mansionHandleInDoorHoleFlag, 1); + break; - if (!CHECK_FLAG(0xDBCB, 1)) { - displayMessage(0x5101); - return true; + case 0x90fc: // dictaphone on robot + if (fnRobotSafeAlreadyUnlockedCheck()) { + if (CHECK_FLAG(dsAddr_MansionRobotSafeVoiceTestPassedFlag, 1)) { + displayMessage(dsAddr_fooledOnceMsg); // "I'd already fooled him once" + } else { + if (!CHECK_FLAG(dsAddr_usedDictaphoneOnTVFlag, 1)) { + displayMessage(dsAddr_notMyVoiceMsg); // "I won't cheat Mike with MY voice" + } else { + displayMessage(dsAddr_mikeVoiceTestMsg); // "Mike, activate the voice test" + waitLanAnimationFrame(1, 1); + + playSound(5, 3); + playSound(5, 39); + displayAsyncMessage(dsAddr_singingMsg, 68, 126, 9, 35, textColorJohnNoty); // "siiiiinging!" + playActorAnimation(728); + + waitLanAnimationFrame(1, 1); + dialog->show(98, scene, 0, 727, textColorMark, textColorMike, 0, 1); + SET_FLAG(dsAddr_MansionRobotSafeVoiceTestPassedFlag, 1); + fnRobotSafeUnlockCheck(); + } + } } + break; - displayMessage(0x50e1); - waitLanAnimationFrame(1, 1); - - playSound(5, 3); - playSound(5, 39); - displayAsyncMessage(0x5124, 40388, 9, 35, 0xd0); - playActorAnimation(728); - - waitLanAnimationFrame(1, 1); - Dialog::show(scene, 0x3d17, 0, 727, 0xd1, 0xef, 0, 1); - SET_FLAG(0xDBD2, 1); - processCallback(0x9175); - return true; + case 0x91cb: // use socks on robot + if (fnRobotSafeAlreadyUnlockedCheck()) { + if (CHECK_FLAG(dsAddr_MansionRobotSafeScentTestPassedFlag, 1)) { + displayMessage(dsAddr_fooledOnceMsg); // "I'd already fooled him once" + } else { + displayMessage(dsAddr_mikeScentTestMsg); // "Mike, let's get on with the scent test" - case 0x91cb: //use socks on robot - if (!processCallback(0x9166)) - return true; + waitLanAnimationFrame(1, 1); + playSound(5, 3); + playSound(5, 23); + playActorAnimation(729); - if (CHECK_FLAG(0xDBD3, 1)) { - displayMessage(0x50c3); - return true; + waitLanAnimationFrame(1, 1); + dialog->show(99, scene, 0, 727, textColorMark, textColorMike, 0, 1); + SET_FLAG(dsAddr_MansionRobotSafeScentTestPassedFlag, 1); + fnRobotSafeUnlockCheck(); + } } - displayMessage(0x5138); - - waitLanAnimationFrame(1, 1); - playSound(5, 3); - playSound(5, 23); - playActorAnimation(729); + break; - waitLanAnimationFrame(1, 1); - Dialog::show(scene, 0x3d70, 0, 727, 0xd1, 0xef, 0, 1); - SET_FLAG(0xDBD3, 1); - processCallback(0x9175); - return true; + case 0x9209: // photo on robot + if (fnRobotSafeAlreadyUnlockedCheck()) { + if (CHECK_FLAG(dsAddr_MansionRobotSafeViewTestPassedFlag, 1)) { + displayMessage(dsAddr_fooledOnceMsg); // "I'd already fooled him once" + } else { + displayMessage(dsAddr_mikeViewTestMsg); // "Mike, run the view test" + waitLanAnimationFrame(1, 1); - case 0x9209: //photo on robot - if (!processCallback(0x9166)) - return true; + playSound(5, 3); + playSound(5, 25); + playActorAnimation(730); - if (CHECK_FLAG(0xDBD4, 1)) { - displayMessage(0x50c3); - return true; + waitLanAnimationFrame(1, 1); + dialog->show(100, scene, 0, 727, textColorMark, textColorMike, 0, 1); + SET_FLAG(dsAddr_MansionRobotSafeViewTestPassedFlag, 1); + fnRobotSafeUnlockCheck(); + } } - displayMessage(0x5161); - waitLanAnimationFrame(1, 1); - - playSound(5, 3); - playSound(5, 25); - playActorAnimation(730); + break; - waitLanAnimationFrame(1, 1); - Dialog::show(scene, 0x3dd6, 0, 727, 0xd1, 0xef, 0, 1); - SET_FLAG(0xDBD4, 1); - processCallback(0x9175); - return true; + case 0x9247: + displayMessage(dsAddr_sameBottleMsg); // "The bottle's the same, but I doubt if it's enough to fool anyone" + break; case 0x924e: setOns(2, 64); @@ -3613,9 +4477,9 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(52, 10); playActorAnimation(711); moveRel(0, 0, 4); - Dialog::show(scene, 0x3b21, 0, 709, 0xd1, 0xef, 0, 1); + dialog->show(95, scene, 0, 709, textColorMark, textColorCook, 0, 1); moveTo(300, 190, 4); - inventory->remove(64); + inventory->remove(kInvItemFakeChilli); disableObject(8); playAnimation(712, 0); setOns(2, 0); @@ -3623,90 +4487,90 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playSound(15, 28); playSound(16, 37); playAnimation(713, 0); - Dialog::show(scene, 0x3c0d, 0, 709, 0xd1, 0xef, 0, 1); + dialog->show(96, scene, 0, 709, textColorMark, textColorCook, 0, 1); playSound(85, 2); playAnimation(714, 0); setLan(1, 0); disableObject(1); { Object *obj = scene->getObject(2); - obj->actor_rect.left = obj->actor_rect.right = 81; - obj->actor_rect.top = obj->actor_rect.bottom = 160; - obj->actor_orientation = 4; + obj->actorRect.left = obj->actorRect.right = 81; + obj->actorRect.top = obj->actorRect.bottom = 160; + obj->actorOrientation = 4; obj->save(); } { Object *obj = scene->getObject(3); - obj->actor_rect.left = obj->actor_rect.right = 64; - obj->actor_rect.top = obj->actor_rect.bottom = 168; - obj->actor_orientation = 4; + obj->actorRect.left = obj->actorRect.right = 64; + obj->actorRect.top = obj->actorRect.bottom = 168; + obj->actorOrientation = 4; obj->save(); } { Object *obj = scene->getObject(10); - obj->actor_rect.left = obj->actor_rect.right = 105; - obj->actor_rect.top = obj->actor_rect.bottom = 160; - obj->actor_orientation = 1; + obj->actorRect.left = obj->actorRect.right = 105; + obj->actorRect.top = obj->actorRect.bottom = 160; + obj->actorOrientation = 1; obj->save(); } - SET_FLAG(0xDBCC, 1); - return true; + SET_FLAG(dsAddr_MansionCookGoneFlag, 1); + break; case 0x9472: playSound(5, 4); playSound(19, 14); playActorAnimation(793); - displayMessage(0x5218); - inventory->remove(60); - SET_FLAG(0xDBD6, 1); - return true; + displayMessage(dsAddr_fitsPerfectMsg); // "It fits perfectly!" + inventory->remove(kInvItemWrappedCork); + SET_FLAG(dsAddr_MansionSinkState, 1); + break; - case 0x9449: //meat + stew + case 0x9449: // meat + stew playSound(5, 4); playSound(63, 12); playActorAnimation(726); - displayMessage(0x508a); - inventory->remove(69); - inventory->add(70); - return true; + displayMessage(dsAddr_dislikeVealMsg); // "I never liked veal anyway" + inventory->remove(kInvItemMeat); + inventory->add(kInvItemPlasticBag); + break; case 0x949b: - if (CHECK_FLAG(0xDBD6, 2)) { + if (CHECK_FLAG(dsAddr_MansionSinkState, 2)) { playSound(5, 4); playSound(5, 25); playActorAnimation(802); - displayMessage(0x5272); - inventory->remove(62); - inventory->add(74); - inventory->add(65); + displayMessage(dsAddr_labelOffMsg); // "The label has come off!" + inventory->remove(kInvItemChilliWithLabel); + inventory->add(kInvItemChilliNoLabel); + inventory->add(kInvItemLabel); } else - displayMessage(0x524f); - return true; + displayMessage(dsAddr_noHotWaterMsg); // "There's no hot water in the sink" + break; case 0x94d4: - if (inventory->has(70)) { + if (inventory->has(kInvItemPlasticBag)) { setOns(0, 0); playSound(5, 3); playSound(5, 18); playSound(13, 12); playActorAnimation(803); disableObject(7); - inventory->remove(70); - inventory->add(71); + inventory->remove(kInvItemPlasticBag); + inventory->add(kInvItemSocks); } else - displayMessage(0x53ad); - return true; + displayMessage(dsAddr_noSockStoreMsg); // "I don't have anything to store these socks in" + break; case 0x951b: playSound(5, 4); playSound(5, 22); playActorAnimation(804); - displayMessage(0x528b); - return true; + displayMessage(dsAddr_corkTooSmallMsg); // "The cork is a bit too small" + break; case 0x73a3: - if (CHECK_FLAG(0xdbc5, 1)) { - SET_FLAG(0xdbc5, 0); + if (CHECK_FLAG(dsAddr_mansionTVOnFlag, 1)) { + SET_FLAG(dsAddr_mansionTVOnFlag, 0); //call 73e6 playSound(71, 3); @@ -3714,92 +4578,94 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playAnimation(0, 0, true); reloadLan(); - if (CHECK_FLAG(0xDBC6, 1)) { - displayMessage(0x4da6); + if (CHECK_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 1)) { + displayMessage(dsAddr_muchBetterMsg); // "That's much better" } } else { - SET_FLAG(0xdbc5, 1); + SET_FLAG(dsAddr_mansionTVOnFlag, 1); //call 73e6 playSound(71, 3); playActorAnimation(700); reloadLan(); } - return true; + break; - case 0x9537: //using remote on VCR + case 0x9537: // using remote on VCR playSound(5, 3); playSound(5, 16); playActorAnimation(703); - if (!CHECK_FLAG(0xDBC8, 1)) { - displayMessage(0x4D80); //nothing happened - return true; - } - - //0x955a - if (CHECK_FLAG(0xDBC6, 0)) { - if (CHECK_FLAG(0xDBC5, 1)) { //tv on - if (!CHECK_FLAG(0xDBC7, 1)) - displayMessage(0x4d93); //the tape started - - SET_FLAG(0xDBC6, 1); - reloadLan(); - if (!CHECK_FLAG(0xDBC7, 1)) { - Dialog::show(scene, 0x392c, 0, 702, 0xd1, 0xd0, 0, 1); - SET_FLAG(0xDBC7, 1); + if (!CHECK_FLAG(dsAddr_mansionVCRTapeLoadedFlag, 1)) + displayMessage(dsAddr_NotHappenMsg); // "Nothing happened" + else { + //0x955a + if (CHECK_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 0)) { + if (CHECK_FLAG(dsAddr_mansionTVOnFlag, 1)) { + if (!CHECK_FLAG(dsAddr_mansionVCRPlayedTapeBeforeFlag, 1)) + displayMessage(dsAddr_tapeStartedMsg); // "The tape started!" + + SET_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 1); + reloadLan(); + if (!CHECK_FLAG(dsAddr_mansionVCRPlayedTapeBeforeFlag, 1)) { + dialog->show(93, scene, 0, 702, textColorMark, textColorJohnNoty, 0, 1); + SET_FLAG(dsAddr_mansionVCRPlayedTapeBeforeFlag, 1); + } + } else + displayMessage(dsAddr_tvOffMsg); // "I just realised that the TV is off" + } else { + SET_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 0); + if (CHECK_FLAG(dsAddr_mansionTVOnFlag, 1)) { + reloadLan(); + displayMessage(dsAddr_muchBetterMsg); // "That's much better" } - } else - displayMessage(0x4d5b); //i just realized that tv is off - } else { - SET_FLAG(0xDBC6, 0); - if (CHECK_FLAG(0xDBC5, 1)) { //tv on - reloadLan(); - displayMessage(0x4da6); //much better! } } - return true; + break; - case 0x95eb: //polaroid + tv - if (CHECK_FLAG(0xDBC6, 1)) { - if (CHECK_FLAG(0xDBCA, 1)) { - displayMessage(0x4de6); + case 0x95eb: // polaroid + tv + if (CHECK_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 1)) { + if (CHECK_FLAG(dsAddr_usedPolaroidOnTVFlag, 1)) { + displayMessage(dsAddr_enoughPhotosMsg); // "I don't need any more photos" } else { playSound(5, 3); playSound(5, 24); playSound(90, 18); playActorAnimation(707); - inventory->add(61); - SET_FLAG(0xDBCA, 1); + inventory->add(kInvItemPhoto); + SET_FLAG(dsAddr_usedPolaroidOnTVFlag, 1); } } else - displayMessage(0x4ea5); - return true; + displayMessage(dsAddr_notRightMomentMsg); // "I don't think this is the right moment" + break; - case 0x962f: //polaroid + tv - if (CHECK_FLAG(0xDBC6, 1)) { - if (CHECK_FLAG(0xDBCB, 1)) { - displayMessage(0x4e32); + case 0x962f: // dictaphone + tv + if (CHECK_FLAG(dsAddr_mansionVCRPlayingTapeFlag, 1)) { + if (CHECK_FLAG(dsAddr_usedDictaphoneOnTVFlag, 1)) { + displayMessage(dsAddr_alreadyRecordedMsg); // "I already recorded what I wanted to" } else { - displayMessage(0x4e05); + displayMessage(dsAddr_recordScareMsg); // "Yeah, I can record this and scare the cats" playSound(5, 3); playSound(5, 27); playActorAnimation(708); - SET_FLAG(0xDBCB, 1); + SET_FLAG(dsAddr_usedDictaphoneOnTVFlag, 1); } } else - displayMessage(0x4ea5); - return true; - + displayMessage(dsAddr_notRightMomentMsg); // "I don't think this is the right moment" + break; case 0x95c8: playSound(5, 3); playSound(91, 12); playActorAnimation(706); - inventory->remove(54); - SET_FLAG(0xDBC8, 1); - return true; + inventory->remove(kInvItemVideoTape); + SET_FLAG(dsAddr_mansionVCRTapeLoadedFlag, 1); + break; - case 0x9673: //hit fatso - final scene + case 0x966c: + displayMessage(dsAddr_cantRecordNoBatteriesMsg); // "I can't record anything until I find some batteries" + break; + + case 0x9673: // hit fatso - final scene playSound(5, 3); playSound(24, 10); playActorAnimation(798); @@ -3811,12 +4677,12 @@ bool TeenAgentEngine::processCallback(uint16 addr) { wait(100); playActorAnimation(805); moveTo(50, 170, 3); - displayMessage(0x5349); + displayMessage(dsAddr_onlyChilliMsg); // "Good this red stuff is only a chilli" //moveTo(105, 157, 0, true); playMusic(3); loadScene(11, 105, 157, 4); - Dialog::show(scene, 0x8409, 0, 938, 0xd1, 0xec, 0, 1); + dialog->show(203, scene, 0, 938, textColorMark, textColorCaptain, 0, 1); playAnimation(939, 0, true, true); playActorAnimation(942, true); @@ -3837,9 +4703,9 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(945, true); waitAnimation(); - Dialog::show(scene, 0x844f, 0, 938, 0xd1, 0xec, 0, 1); + dialog->show(204, scene, 0, 938, textColorMark, textColorCaptain, 0, 1); playAnimation(946, 0); - Dialog::show(scene, 0x87c7, 0, 938, 0xd1, 0xec, 0, 1); + dialog->show(205, scene, 0, 938, textColorMark, textColorCaptain, 0, 1); playSound(24, 7); playAnimation(948, 0, true); @@ -3847,16 +4713,16 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); loadScene(40, 198, 186, 1); - Dialog::show(scene, 0x8890, 0, 920, 0xd1, 0xe7, 0, 1); - Dialog::show(scene, 0x8a2f, 0, 921, 0xd1, 0xe7, 0, 1); + dialog->show(206, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); + dialog->show(207, scene, 0, 921, textColorMark, textColorRGBBoss, 0, 1); playAnimation(923, 0); - Dialog::show(scene, 0x8aa7, 0, 920, 0xd1, 0xe7, 0, 1); + dialog->show(208, scene, 0, 920, textColorMark, textColorRGBBoss, 0, 1); moveTo(237, 186, 0); moveTo(237, 177, 0); moveTo(192, 177, 4); playAnimation(949, 0); - Dialog::showMono(scene, 0x8af6, 950, 0xe7, 1); + dialog->showMono(209, scene, 950, textColorRGBBoss, 1); playSound(32, 5); playSound(40, 14); @@ -3869,98 +4735,89 @@ bool TeenAgentEngine::processCallback(uint16 addr) { displayCredits(); loadScene(39, 192, 177, 0); hideActor(); - Dialog::showMono(scene, 0x8b4d, 953, 0xe3, 1); //well... + dialog->showMono(210, scene, 953, textColorMarkEnd, 1); playSound(5, 15); playAnimation(954, 0); - Dialog::showMono(scene, 0x8b7a, 955, 0xe3, 1); //that's all folks + dialog->showMono(211, scene, 955, textColorMarkEnd, 1); playMusic(2); - displayCredits(0xe47c, 4500); //3 minutes (infinite until key pressed in original) + displayCredits(dsAddr_finalCredits6, 4500); // 3 minutes (infinite until key pressed in original) scene->push(SceneEvent(SceneEvent::kQuit)); + break; - return true; - - case 0x9921: { //using diving eq - int id = scene->getId(); - if (id != 15) { - displayMessage(id == 16 ? 0x38ce : 0x38a7); - } else { - playSound(5, 3); - playSound(38, 16); - playSound(38, 22); - playActorAnimation(614); - playSound(5, 3); - playSound(44, 10); - playSound(20, 26); - playActorAnimation(615); - loadScene(17, 156, 180, 3); - SET_FLAG(0, 4); - playSound(64, 7); - playSound(64, 21); - playSound(64, 42); - playSound(64, 63); - setTimerCallback(0x9a1d, 30); - playActorAnimation(617, false, true); + case csAddr_useDivingEquipment: // using diving eq + // FIXME - Some code is missing here as displayMessage(dsAddr_cantTalkUnderwaterMsg), + // displayMessage(dsAddr_notSwimmingThereMsg), displayMessage(dsAddr_tooLittleAirMsg) + // displayMessage(dsAddr_fishDontWorryMsg) are never called. + { + int id = scene->getId(); + if (id != 15) { + if (id == 16) + displayMessage(dsAddr_notHereMsg); // "Not here" + else + displayMessage(dsAddr_notBestPlaceMsg); // "It's not the best place for diving" + } else { + playSound(5, 3); + playSound(38, 16); + playSound(38, 22); + playActorAnimation(614); + playSound(5, 3); + playSound(44, 10); + playSound(20, 26); + playActorAnimation(615); + loadScene(17, 156, 180, 3); + SET_FLAG(dsAddr_timedCallbackState, 4); + playSound(64, 7); + playSound(64, 21); + playSound(64, 42); + playSound(64, 63); + setTimerCallback(csAddr_noAnchorTimeout, 30); + playActorAnimation(617, false, true); + } } - } - return true; + break; - case 0x9a1d: //no anchor, timeout - SET_FLAG(0, 0); - processCallback(0x9a7a); - INC_FLAG(0xDBA6); - switch (GET_FLAG(0xDBA6)) { + case csAddr_noAnchorTimeout: // no anchor, timeout + SET_FLAG(dsAddr_timedCallbackState, 0); + fnGetOutOfLake(); + INC_FLAG(dsAddr_lakeDivingExitMessage); + switch (GET_FLAG(dsAddr_lakeDivingExitMessage)) { case 1: - displayMessage(0x39ae); + displayMessage(dsAddr_seaweedMsg); // "This seaweed is just like the flowers I gave mum on her last birthday" break; case 2: - displayMessage(0x39f6); + displayMessage(dsAddr_fishBoatMsg); // "I wonder what fish do inside this boat at night" break; case 3: - displayMessage(0x3a28); + displayMessage(dsAddr_fishSomethingMsg); // "I think I have to fish out something down there" break; case 4: - displayMessage(0x3a85); + displayMessage(dsAddr_notRedHerringMsg); // "I hope all this fish stuff is not a red herring" break; case 5: - displayMessage(0x39ae); + displayMessage(dsAddr_seaweedMsg); // "This seaweed is just like the flowers I gave mum on her last birthday" break; default: - displayMessage(0x3ab7); + displayMessage(dsAddr_niceDownMsg); // "It's nice down there" + break; } - return true; + break; - case 0x99e0: //success getting an anchor - SET_FLAG(0, 0); - setTimerCallback(0, 0); - scene->getActorAnimation()->free(); - playSound(64, 7); - playActorAnimation(618); - disableObject(5); - setOns(0, 0); - playSound(31, 1); - playActorAnimation(619); - processCallback(0x9a7a); - inventory->add(42); - displayMessage(0x3989); - return true; + case csAddr_gotAnchor: + fnGotAnchor(); + break; - case 0x9a7a: - loadScene(15, 156, 180, 3); - playSound(5, 5); - playSound(38, 14); - playSound(38, 20); - playSound(5, 25); - playActorAnimation(616); - return true; + case csAddr_getOutOfLake: + fnGetOutOfLake(); + break; - case 0x9aca: + case csAddr_digMansionWall: if (scene->getId() == 13) { moveTo(172, 181, 1); playSound(26, 19); for (uint i = 0; i < 8; ++i) playSound(26, 30 + i * 11); playActorAnimation(661); - displayCutsceneMessage(0x3c80, 30484); + displayCutsceneMessage(dsAddr_cutsceneMsgA, 84, 95); // "Hundred moments later" playSound(56, 10); playSound(56, 21); @@ -3974,7 +4831,7 @@ bool TeenAgentEngine::processCallback(uint16 addr) { waitAnimation(); setOns(1, 49); - displayCutsceneMessage(0x3c9a, 30453); + displayCutsceneMessage(dsAddr_cutsceneMsgB, 53, 95); // "Another hundred moments later" moveTo(162, 184, 0, true); playSound(26, 6); playSound(26, 17); @@ -3986,27 +4843,32 @@ bool TeenAgentEngine::processCallback(uint16 addr) { playActorAnimation(664); playAnimation(665, 1); wait(100); - displayMessage(0x3cbc); + displayMessage(dsAddr_foundCrudeOilMsg); // "At least I found crude oil and I'll be rich" wait(100); - displayMessage(0x3cea); - inventory->remove(37); - processCallback(0x9d45); //another mansion try + displayMessage(dsAddr_myLifeMsg); // "That's my life" + inventory->remove(kInvItemShovelAct2); + fnMansionIntrusionAttempt(); } else - displayMessage(0x3c58); - return true; + displayMessage(dsAddr_notThinkRightPlaceMsg); // "I don't think this is the right place" + break; - case 0x9c6d: - displayMessage(0x49d1); - SET_FLAG(0xDBB5, 1); - return false; + case csAddr_tooDarkHere: + displayMessage(dsAddr_cantDoTooDarkMsg); // "I can't do anything here, it's too dark" + break; + + case csAddr_examineBanknote: + displayMessage(dsAddr_bankNoteMsg); // "It's a note from some bank..." + SET_FLAG(dsAddr_examinedBanknoteFlag, 1); + retVal = false; + break; - case 0x9c79: //use pills + case csAddr_useTimePills: // use pills if (scene->getId() != 36) { - displayMessage(0x52a9); - } else if (CHECK_FLAG(0xDBF1, 1)) { - displayMessage(0x52F6); + displayMessage(dsAddr_notTryNowMsg); // "There's no need to try them now" + } else if (CHECK_FLAG(dsAddr_mansionAlreadyUsedTimePillsFlag, 1)) { + displayMessage(dsAddr_nahMsg); // "Nah" } else { - SET_FLAG(0xDBF1, 1); + SET_FLAG(dsAddr_mansionAlreadyUsedTimePillsFlag, 1); moveTo(102, 195, 2); playSound(5, 3); playSound(75, 12); @@ -4020,131 +4882,59 @@ bool TeenAgentEngine::processCallback(uint16 addr) { { Walkbox *w = scene->getWalkbox(0); w->rect.left = 0; - w->rect.bottom = 199; + w->rect.bottom = kScreenHeight-1; w->save(); } setLan(1, 0xff); - Dialog::showMark(scene, 0x58a9); + dialog->showMark(130, scene); Object *obj = scene->getObject(1); - obj->actor_rect.left = obj->actor_rect.right = 270; - obj->actor_rect.top = obj->actor_rect.bottom = 193; - obj->actor_orientation = 2; + obj->actorRect.left = obj->actorRect.right = 270; + obj->actorRect.top = obj->actorRect.bottom = 193; + obj->actorOrientation = 2; obj->save(); obj = scene->getObject(3); - obj->actor_rect.left = obj->actor_rect.right = 254; - obj->actor_rect.top = obj->actor_rect.bottom = 193; - obj->actor_orientation = 1; + obj->actorRect.left = obj->actorRect.right = 254; + obj->actorRect.top = obj->actorRect.bottom = 193; + obj->actorOrientation = 1; obj->save(); - SET_FLAG(0xDBD7, 1); + SET_FLAG(dsAddr_MansionThruFanByTimePillFlag, 1); } - return true; + break; - case 0x9d45: { - wait(50); - byte attempts = ++ *(res->dseg.ptr(0xDBEA)); - debug(0, "mansion intrusion attempt #%u", attempts); - if (attempts >= 7) - return false; + case csAddr_mansionIntrusionAttempt: + retVal = fnMansionIntrusionAttempt(); + break; - uint16 ptr = res->dseg.get_word((attempts - 2) * 2 + 0x6035); - debug(0, "mansion callback = %04x", ptr); - byte id = scene->getId(); + case csAddr_secondMansionIntrusion: + fnSecondMansionIntrusion(); + break; - playMusic(11); - displayCutsceneMessage(0x580a, 30484); - processCallback(ptr); - playMusic(6); - if (getFlag(0xdbec) != 1 || ptr != 0x9f3e) //ptr check eq. scene_id == 11 - loadScene(id, scene->getPosition()); - return true; - } + case csAddr_thirdMansionIntrusion: + fnThirdMansionIntrusion(); + break; - case 0x9d90: - hideActor(); - loadScene(34, scene->getPosition()); - playAnimation(986, 0, true); - playAnimation(987, 1, true); - waitAnimation(); - Dialog::show(scene, 0x6f60, 988, 989, 0xd9, 0xd0, 1, 2); - playAnimation(990, 0, true); - playAnimation(991, 1, true); - waitAnimation(); - showActor(); - return true; - - case 0x9de5: - hideActor(); - loadScene(30, scene->getPosition()); - playAnimation(887, 1); - playAnimation(888, 2, true, true, true); - //waitAnimation(); - Dialog::showMono(scene, 0x6fb8, 889, 0xd9, 2); - playSound(26, 3); - playAnimation(891, 1, true, true, true); - playAnimation(892, 2); - waitAnimation(); - Dialog::show(scene, 0x6ff0, 890, 889, 0xd0, 0xd9, 3, 2); - showActor(); - return true; - - case 0x9e54: - hideActor(); - loadScene(32, scene->getPosition()); - playAnimation(894, 1, true, true, true); - playAnimation(893, 2, true); - waitAnimation(); - Dialog::showMono(scene, 0x706e, 895, 0xd9, 3); - playSound(75, 9); - playAnimation(898, 1, true); - playAnimation(897, 2, true); - Dialog::show(scene, 0x7096, 896, 895, 0xd0, 0xd9, 2, 3); - showActor(); - return true; + case csAddr_fourthMansionIntrusion: + fnFourthMansionIntrusion(); + break; - case 0x9ec3: - hideActor(); - loadScene(29, scene->getPosition()); - playActorAnimation(901, true); - playAnimation(900, 1, true); - waitAnimation(); - Dialog::show(scene, 0x7161, 903, 902, 0xd0, 0xd9, 2, 3); - for (byte i = 3; i <= 9; i += 2) - playSound(56, i); + case csAddr_fifthMansionIntrusion: + fnFifthMansionIntrusion(); + break; - playActorAnimation(905, true); - playAnimation(904, 1, true); - Dialog::show(scene, 0x71c6, 903, 902, 0xd0, 0xd9, 2, 3); - showActor(); - return true; + case csAddr_sixthMansionIntrusion: + fnSixthMansionIntrusion(); + break; - case 0x9f3e: - hideActor(); - loadScene(35, scene->getPosition()); - playAnimation(907, 2, true); - playAnimation(906, 3, true); - waitAnimation(); - Dialog::show(scene, 0x7243, 908, 909, 0xd9, 0xd0, 2, 3); - Dialog::show(scene, 0x7318, 910, 908, 0xd0, 0xd9, 3, 2); - loadScene(11, scene->getPosition()); - showActor(); - setOns(3, 51); - playAnimation(911, 1); - playAnimation(899, 1); - setFlag(0xDBEC, 1); - reloadLan(); - wait(200); - enableObject(8); - setLan(2, 8); - return true; + default: + error("unknown callback 0x%04x called", addr); + break; } - //error("invalid callback %04x called", addr); - warning("invalid callback %04x called", addr); - return true; + return retVal; } } // End of namespace TeenAgent diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp index 2de6f49c44..0c1268a5fc 100644 --- a/engines/teenagent/detection.cpp +++ b/engines/teenagent/detection.cpp @@ -26,6 +26,7 @@ #include "base/plugins.h" #include "engines/advancedDetector.h" +#include "teenagent/resources.h" #include "teenagent/teenagent.h" #include "graphics/thumbnail.h" @@ -168,7 +169,7 @@ public: Common::String desc = buf; - in->seek(0x777a); + in->seek(TeenAgent::saveStateSize); if (!Graphics::checkThumbnailHeader(*in)) return SaveStateDescriptor(slot, desc); diff --git a/engines/teenagent/dialog.cpp b/engines/teenagent/dialog.cpp index 400bd7cec2..870aca6400 100644 --- a/engines/teenagent/dialog.cpp +++ b/engines/teenagent/dialog.cpp @@ -22,99 +22,103 @@ #include "teenagent/dialog.h" #include "teenagent/resources.h" #include "teenagent/scene.h" +#include "teenagent/teenagent.h" namespace TeenAgent { +void Dialog::show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2) { + uint16 addr = _vm->res->getDialogAddr(dialogNum); + // WORKAROUND: For Dialog 163, The usage of this in the engine overlaps the previous dialog i.e. the + // starting offset used is two bytes early, thus implicitly changing the first command of this dialog + // from NEW_LINE to CHANGE_CHARACTER. + // FIXME: Unsure if this is correct behaviour or if this is a regression from the original. Check this. + // Similar issue occurs with Dialog 190 which is used from dialogue stack at 0x7403, rather than start of 0x7405 + // Similar issue occurs with Dialog 0 which is used from dialogue stack at 0x0001, rather than start of 0x0000 + if (dialogNum == 163) + addr -= 2; + show(scene, addr, animation1, animation2, color1, color2, slot1, slot2); +} + void Dialog::show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2) { - debug(0, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); - Resources *res = Resources::instance(); + debugC(0, kDebugDialog, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); int n = 0; Common::String message; byte color = color1; if (animation1 != 0) { - SceneEvent e(SceneEvent::kPlayAnimation); - e.animation = animation1; - e.slot = 0xc0 | slot1; //looped, paused - scene->push(e); + SceneEvent e1(SceneEvent::kPlayAnimation); + e1.animation = animation1; + e1.slot = 0xc0 | slot1; //looped, paused + scene->push(e1); } if (animation2 != 0) { - SceneEvent e(SceneEvent::kPlayAnimation); - e.animation = animation2; - e.slot = 0xc0 | slot2; //looped, paused - scene->push(e); + SceneEvent e2(SceneEvent::kPlayAnimation); + e2.animation = animation2; + e2.slot = 0xc0 | slot2; //looped, paused + scene->push(e2); } while (n < 4) { - byte c = res->eseg.get_byte(addr++); - //debug(0, "%02x: %c", c, c > 0x20? c: '.'); + byte c = _vm->res->eseg.get_byte(addr++); + debugC(1, kDebugDialog, "%02x: %c", c, c > 0x20? c: '.'); switch (c) { case 0: ++n; switch (n) { case 1: - //debug(0, "new line\n"); + debugC(1, kDebugDialog, "new line\n"); if (!message.empty()) message += '\n'; break; case 2: - //debug(0, "displaymessage %s", message.c_str()); + debugC(1, kDebugDialog, "displaymessage %s", message.c_str()); if (color == color2) { //pause animation in other slot - { - SceneEvent e(SceneEvent::kPauseAnimation); - e.slot = 0x80 | slot1; - scene->push(e); - } - { - SceneEvent e(SceneEvent::kPlayAnimation); - e.animation = animation2; - e.slot = 0x80 | slot2; - scene->push(e); - } + SceneEvent e1(SceneEvent::kPauseAnimation); + e1.slot = 0x80 | slot1; + scene->push(e1); + + SceneEvent e2(SceneEvent::kPlayAnimation); + e2.animation = animation2; + e2.slot = 0x80 | slot2; + scene->push(e2); } else if (color == color1) { //pause animation in other slot - { - SceneEvent e(SceneEvent::kPauseAnimation); - e.slot = 0x80 | slot2; - scene->push(e); - } - { - SceneEvent e(SceneEvent::kPlayAnimation); - e.animation = animation1; - e.slot = 0x80 | slot1; - scene->push(e); - } - } + SceneEvent e2(SceneEvent::kPauseAnimation); + e2.slot = 0x80 | slot2; + scene->push(e2); - { - message.trim(); - if (message.empty()) - break; + SceneEvent e1(SceneEvent::kPlayAnimation); + e1.animation = animation1; + e1.slot = 0x80 | slot1; + scene->push(e1); + } - SceneEvent e(SceneEvent::kMessage); - e.message = message; - e.color = color; + message.trim(); + if (!message.empty()) { + SceneEvent em(SceneEvent::kMessage); + em.message = message; + em.color = color; if (color == color1) - e.slot = slot1; + em.slot = slot1; if (color == color2) - e.slot = slot2; - scene->push(e); + em.slot = slot2; + scene->push(em); message.clear(); } break; case 3: - color = color == color1 ? color2 : color1; - //debug(0, "changing color to %02x", color); + color = (color == color1) ? color2 : color1; + debugC(1, kDebugDialog, "changing color to %02x", color); break; } break; case 0xff: { - //fixme : wait for the next cycle of the animation + //FIXME : wait for the next cycle of the animation } break; @@ -124,21 +128,20 @@ void Dialog::show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation } } - SceneEvent e(SceneEvent::kClearAnimations); - scene->push(e); + SceneEvent ec(SceneEvent::kClearAnimations); + scene->push(ec); } uint16 Dialog::pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2) { - debug(0, "Dialog::pop(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); - Resources *res = Resources::instance(); + debugC(0, kDebugDialog, "Dialog::pop(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); uint16 next; do { - next = res->dseg.get_word(addr); + next = _vm->res->dseg.get_word(addr); addr += 2; } while (next == 0); - uint16 next2 = res->dseg.get_word(addr); + uint16 next2 = _vm->res->dseg.get_word(addr); if (next2 != 0xffff) - res->dseg.set_word(addr - 2, 0); + _vm->res->dseg.set_word(addr - 2, 0); show(scene, next, animation1, animation2, color1, color2, slot1, slot2); return next; } diff --git a/engines/teenagent/dialog.h b/engines/teenagent/dialog.h index 3bb7d818c1..6672ce7206 100644 --- a/engines/teenagent/dialog.h +++ b/engines/teenagent/dialog.h @@ -27,20 +27,59 @@ namespace TeenAgent { +// Text Color Symbols +enum { + textColorJohnNoty = 0xd0, + textColorCampGuard = 0xd0, + textColorShockedCaptain = 0xd0, + textColorMark = 0xd1, + textColorCredits = 0xd1, + textColorBankGuard = 0xd7, + textColorGrandpa = 0xd8, + textColorMansionGuard = 0xd9, + textColorMarkEnd = 0xe3, + textColorProfessor = 0xe5, + textColorOldLady = 0xe5, + textColorAnne = 0xe5, + textColorWellEcho = 0xe5, + textColorSonny = 0xe5, + textColorEskimo = 0xe5, + textColorRGBBoss = 0xe7, + textColorGoldDriver = 0xe7, + textColorFortuneTeller = 0xeb, + textColorCaptain = 0xec, + textColorMike = 0xef, + textColorCook = 0xef, + textColorBarman = 0xef +}; + class Scene; +class TeenAgentEngine; + class Dialog { public: - static uint16 pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); - static uint16 popMark(Scene *scene, uint16 addr) { - return pop(scene, addr, 0, 0, 0xd1, 0xd1, 0, 0); + Dialog(TeenAgentEngine *vm) : _vm(vm) { } + + uint16 pop(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); + + uint16 popMark(Scene *scene, uint16 addr) { + return pop(scene, addr, 0, 0, textColorMark, textColorMark, 0, 0); } - static void show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); - static void showMono(Scene *scene, uint16 addr, uint16 animation, byte color, byte slot) { - show(scene, addr, animation, animation, color, color, slot, slot); + + void show(uint16 dialogNum, Scene *scene, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); + + void showMono(uint16 dialogNum, Scene *scene, uint16 animation, byte color, byte slot) { + show(dialogNum, scene, animation, animation, color, color, slot, slot); } - static void showMark(Scene *scene, uint16 addr) { - show(scene, addr, 0, 0, 0xd1, 0xd1, 0, 0); + + void showMark(uint16 dialogNum, Scene *scene) { + show(dialogNum, scene, 0, 0, textColorMark, textColorMark, 0, 0); } + +private: + TeenAgentEngine *_vm; + + void show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2); }; } // End of namespace TeenAgent diff --git a/engines/teenagent/font.cpp b/engines/teenagent/font.cpp index f7558b60f2..47f52ff90f 100644 --- a/engines/teenagent/font.cpp +++ b/engines/teenagent/font.cpp @@ -20,8 +20,10 @@ */ #include "teenagent/font.h" + #include "teenagent/pack.h" -#include "common/debug.h" +#include "teenagent/teenagent.h" + #include "common/endian.h" #include "common/stream.h" #include "common/textconsole.h" @@ -30,34 +32,41 @@ namespace TeenAgent { -Font::Font() : grid_color(0xd0), shadow_color(0), height(0), width_pack(0), data(0) { +Font::Font() : _gridColor(0xd0), _shadowColor(0), _height(0), _widthPack(0), _data(0) { +} + +Font::~Font() { + delete[] _data; } -void Font::load(const Pack &pack, int id) { - delete[] data; - data = NULL; +void Font::load(const Pack &pack, int id, byte height, byte widthPack) { + delete[] _data; + _data = NULL; Common::ScopedPtr<Common::SeekableReadStream> s(pack.getStream(id)); if (!s) error("loading font %d failed", id); - data = new byte[s->size()]; - s->read(data, s->size()); - debug(0, "font size: %d", s->size()); + _data = new byte[s->size()]; + s->read(_data, s->size()); + debugC(0, kDebugFont, "font size: %d", s->size()); + + _height = height; + _widthPack = widthPack; } uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) { unsigned idx = (unsigned char)c; if (idx < 0x20 || idx >= 0x81) { - debug(0, "unhandled char 0x%02x", idx); + debugC(0, kDebugFont, "unhandled char 0x%02x", idx); return 0; } idx -= 0x20; - byte *glyph = data + READ_LE_UINT16(data + idx * 2); + byte *glyph = _data + READ_LE_UINT16(_data + idx * 2); int h = glyph[0], w = glyph[1]; - if (surface == NULL || surface->pixels == NULL || y + h <= 0 || y >= 200 || x + w <= 0 || x >= 320) - return w - width_pack; + if (surface == NULL || surface->pixels == NULL || y + h <= 0 || y >= kScreenHeight || x + w <= 0 || x >= kScreenWidth) + return w - _widthPack; int i0 = 0, j0 = 0; if (x < 0) { @@ -68,7 +77,7 @@ uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) i0 = -y; y = 0; } - //debug(0, "char %c, width: %dx%d", c, w, h); + debugC(0, kDebugFont, "char %c, width: %dx%d", c, w, h); glyph += 2; glyph += i0 * w + j0; byte *dst = (byte *)surface->getBasePtr(x, y); @@ -80,7 +89,7 @@ uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) case 0: break; case 1: - dst[j] = shadow_color; + dst[j] = _shadowColor; break; case 2: dst[j] = color; @@ -91,57 +100,57 @@ uint Font::render(Graphics::Surface *surface, int x, int y, char c, byte color) } dst += surface->pitch; } - return w - width_pack; + return w - _widthPack; } -static uint find_in_str(const Common::String &str, char c, uint pos = 0) { +static uint findInStr(const Common::String &str, char c, uint pos = 0) { while (pos < str.size() && str[pos] != c) ++pos; return pos; } -uint Font::render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool show_grid) { +uint Font::render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool showGrid) { if (surface != NULL) { - uint max_w = render(NULL, 0, 0, str, false); - if (show_grid) - grid(surface, x - 4, y - 2, max_w + 8, 8 + 6, grid_color); + uint maxW = render(NULL, 0, 0, str, false); + if (showGrid) + grid(surface, x - 4, y - 2, maxW + 8, 8 + 6, _gridColor); uint i = 0, j; do { - j = find_in_str(str, '\n', i); + j = findInStr(str, '\n', i); Common::String line(str.c_str() + i, j - i); - //debug(0, "line: %s", line.c_str()); + debugC(0, kDebugFont, "line: %s", line.c_str()); - if (y + (int)height >= 0) { + if (y + (int)_height >= 0) { uint w = render(NULL, 0, 0, line, false); - int xp = x + (max_w - w) / 2; + int xp = x + (maxW - w) / 2; for (uint k = 0; k < line.size(); ++k) { xp += render(surface, xp, y, line[k], color); } - } else if (y >= 200) + } else if (y >= kScreenHeight) break; - y += height; + y += _height; i = j + 1; } while (i < str.size()); - return max_w; + return maxW; } else { - //surface == NULL; - uint w = 0, max_w = 0; + // surface == NULL; + uint w = 0, maxW = 0; for (uint i = 0; i < str.size(); ++i) { char c = str[i]; if (c == '\n') { - y += height; - if (w > max_w) - max_w = w; + y += _height; + if (w > maxW) + maxW = w; w = 0; continue; } w += render(NULL, 0, 0, c, color); } - if (w > max_w) - max_w = w; + if (w > maxW) + maxW = w; - return max_w; + return maxW; } } @@ -156,8 +165,4 @@ void Font::grid(Graphics::Surface *surface, int x, int y, int w, int h, byte col } } -Font::~Font() { - delete[] data; -} - } // End of namespace TeenAgent diff --git a/engines/teenagent/font.h b/engines/teenagent/font.h index 5146ace21f..a61f145fa6 100644 --- a/engines/teenagent/font.h +++ b/engines/teenagent/font.h @@ -28,20 +28,24 @@ namespace TeenAgent { class Pack; + class Font { public: - byte grid_color, shadow_color; - byte height, width_pack; - Font(); - void load(const Pack &pack, int id); - uint render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool grid = false); + ~Font(); + + void load(const Pack &pack, int id, byte height, byte widthPack); + uint render(Graphics::Surface *surface, int x, int y, const Common::String &str, byte color, bool showGrid = false); uint render(Graphics::Surface *surface, int x, int y, char c, byte color); static void grid(Graphics::Surface *surface, int x, int y, int w, int h, byte color); - ~Font(); + byte getHeight() { return _height; } + void setShadowColor(byte color) { _shadowColor = color; } private: - byte *data; + byte *_data; + + byte _gridColor, _shadowColor; + byte _height, _widthPack; }; } // End of namespace TeenAgent diff --git a/engines/teenagent/inventory.cpp b/engines/teenagent/inventory.cpp index 59dd44baa3..354371666c 100644 --- a/engines/teenagent/inventory.cpp +++ b/engines/teenagent/inventory.cpp @@ -24,6 +24,7 @@ #include "common/textconsole.h" #include "teenagent/inventory.h" + #include "teenagent/resources.h" #include "teenagent/objects.h" #include "teenagent/teenagent.h" @@ -31,47 +32,44 @@ namespace TeenAgent { -Inventory::Inventory(TeenAgentEngine *engine) { - _engine = engine; +Inventory::Inventory(TeenAgentEngine *vm) : _vm(vm) { _active = false; FilePack varia; varia.open("varia.res"); - { - Common::ScopedPtr<Common::SeekableReadStream> s(varia.getStream(3)); - if (!s) - error("no inventory background"); - debug(0, "loading inventory background..."); - _background.load(*s, Surface::kTypeOns); - } + Common::ScopedPtr<Common::SeekableReadStream> s(varia.getStream(3)); + if (!s) + error("no inventory background"); + debugC(0, kDebugInventory, "loading inventory background..."); + _background.load(*s, Surface::kTypeOns); - uint32 items_size = varia.getSize(4); - if (items_size == 0) + uint32 itemsSize = varia.getSize(4); + if (itemsSize == 0) error("invalid inventory items size"); - debug(0, "loading items, size: %u", items_size); - _items = new byte[items_size]; - varia.read(4, _items, items_size); + debugC(0, kDebugInventory, "loading items, size: %u", itemsSize); + _items = new byte[itemsSize]; + varia.read(4, _items, itemsSize); byte offsets = _items[0]; - assert(offsets == 92); + assert(offsets == kNumInventoryItems); for (byte i = 0; i < offsets; ++i) { _offset[i] = READ_LE_UINT16(_items + i * 2 + 1); } - _offset[92] = items_size; + _offset[kNumInventoryItems] = itemsSize; - Resources *res = Resources::instance(); - for (byte i = 0; i <= 92; ++i) { + InventoryObject ioBlank; + _objects.push_back(ioBlank); + for (byte i = 0; i < kNumInventoryItems; ++i) { InventoryObject io; - uint16 obj_addr = res->dseg.get_word(0xc4a4 + i * 2); - if (obj_addr != 0) - io.load(res->dseg.ptr(obj_addr)); + uint16 objAddr = vm->res->dseg.get_word(dsAddr_inventoryItemDataPtrTable + i * 2); + io.load(vm->res->dseg.ptr(objAddr)); _objects.push_back(io); } - _inventory = res->dseg.ptr(0xc48d); + _inventory = vm->res->dseg.ptr(dsAddr_inventory); - for (int y = 0; y < 4; ++y) + for (int y = 0; y < 4; ++y) { for (int x = 0; x < 6; ++x) { int i = y * 6 + x; _graphics[i]._rect.left = 28 + 45 * x - 1; @@ -79,6 +77,7 @@ Inventory::Inventory(TeenAgentEngine *engine) { _graphics[i]._rect.right = _graphics[i]._rect.left + 40; _graphics[i]._rect.bottom = _graphics[i]._rect.top + 26; } + } varia.close(); _hoveredObj = _selectedObj = NULL; @@ -89,7 +88,7 @@ Inventory::~Inventory() { } bool Inventory::has(byte item) const { - for (int i = 0; i < 24; ++i) { + for (int i = 0; i < kInventorySize; ++i) { if (_inventory[i] == item) return true; } @@ -97,34 +96,34 @@ bool Inventory::has(byte item) const { } void Inventory::remove(byte item) { - debug(0, "removing %u from inventory", item); + debugC(0, kDebugInventory, "removing %u from inventory", item); int i; - for (i = 0; i < 24; ++i) { + for (i = 0; i < kInventorySize; ++i) { if (_inventory[i] == item) { break; } } - for (; i < 23; ++i) { + for (; i < (kInventorySize - 1); ++i) { _inventory[i] = _inventory[i + 1]; _graphics[i].free(); } - _inventory[23] = 0; - _graphics[23].free(); + _inventory[kInventorySize - 1] = kInvItemNoItem; + _graphics[kInventorySize - 1].free(); } void Inventory::clear() { - debug(0, "clearing inventory"); - for (int i = 0; i < 24; ++i) { - _inventory[i] = 0; + debugC(0, kDebugInventory, "clearing inventory"); + for (int i = 0; i < kInventorySize; ++i) { + _inventory[i] = kInvItemNoItem; _graphics[i].free(); } } void Inventory::reload() { - for (int i = 0; i < 24; ++i) { + for (int i = 0; i < kInventorySize; ++i) { _graphics[i].free(); uint item = _inventory[i]; - if (item != 0) + if (item != kInvItemNoItem) _graphics[i].load(this, item); } } @@ -132,9 +131,9 @@ void Inventory::reload() { void Inventory::add(byte item) { if (has(item)) return; - debug(0, "adding %u to inventory", item); - for (int i = 0; i < 24; ++i) { - if (_inventory[i] == 0) { + debugC(0, kDebugInventory, "adding %u to inventory", item); + for (int i = 0; i < kInventorySize; ++i) { + if (_inventory[i] == kInvItemNoItem) { _inventory[i] = item; return; } @@ -143,13 +142,14 @@ void Inventory::add(byte item) { } bool Inventory::tryObjectCallback(InventoryObject *obj) { - byte id = obj->id; - uint i = 0; - for (byte *table = Resources::instance()->dseg.ptr(0xBB6F + 3); table[0] != 0 && i < 7; table += 3, ++i) { - if (table[0] == id) { + byte objId = obj->id; + for (uint i = 0; i < 7; ++i) { + byte tableId = _vm->res->dseg.get_byte(dsAddr_objCallbackTablePtr + (3 * i)); + uint16 callbackAddr = _vm->res->dseg.get_word(dsAddr_objCallbackTablePtr + (3 * i) + 1); + if (tableId == objId) { resetSelectedObject(); activate(false); - if (_engine->processCallback(READ_LE_UINT16(table + 1))) + if (_vm->processCallback(callbackAddr)) return true; } } @@ -157,8 +157,6 @@ bool Inventory::tryObjectCallback(InventoryObject *obj) { } bool Inventory::processEvent(const Common::Event &event) { - Resources *res = Resources::instance(); - switch (event.type) { case Common::EVENT_MOUSEMOVE: @@ -178,9 +176,9 @@ bool Inventory::processEvent(const Common::Event &event) { _mouse = event.mouse; _hoveredObj = NULL; - for (int i = 0; i < 24; ++i) { + for (int i = 0; i < kInventorySize; ++i) { byte item = _inventory[i]; - if (item == 0) + if (item == kInvItemNoItem) continue; _graphics[i]._hovered = _graphics[i]._rect.in(_mouse); @@ -197,14 +195,14 @@ bool Inventory::processEvent(const Common::Event &event) { if (_hoveredObj == NULL) return true; - debug(0, "lclick on %u:%s", _hoveredObj->id, _hoveredObj->name.c_str()); + debugC(0, kDebugInventory, "lclick on %u:%s", _hoveredObj->id, _hoveredObj->name.c_str()); if (_selectedObj == NULL) { if (tryObjectCallback(_hoveredObj)) return true; //activate(false); - int w = res->font7.render(NULL, 0, 0, _hoveredObj->description, 0xd1); - _engine->scene->displayMessage(_hoveredObj->description, 0xd1, Common::Point((320 - w) / 2, 162)); + int w = _vm->res->font7.render(NULL, 0, 0, _hoveredObj->description, textColorMark); + _vm->scene->displayMessage(_hoveredObj->description, textColorMark, Common::Point((kScreenWidth - w) / 2, 162)); return true; } @@ -213,30 +211,27 @@ bool Inventory::processEvent(const Common::Event &event) { if (id1 == id2) return true; - debug(0, "combine(%u, %u)!", id1, id2); - byte *table = res->dseg.ptr(0xC335); + debugC(0, kDebugInventory, "combine(%u, %u)!", id1, id2); + byte *table = _vm->res->dseg.ptr(dsAddr_objCombiningTablePtr); while (table[0] != 0 && table[1] != 0) { - if ( - (id1 == table[0] && id2 == table[1]) || - (id2 == table[0] && id1 == table[1]) - ) { - byte new_obj = table[2]; - if (new_obj != 0) { + if ((id1 == table[0] && id2 == table[1]) || (id2 == table[0] && id1 == table[1])) { + byte newObj = table[2]; + if (newObj != 0) { remove(id1); remove(id2); - debug(0, "adding object %u", new_obj); - add(new_obj); - _engine->playSoundNow(69); + debugC(0, kDebugInventory, "adding object %u", newObj); + add(newObj); + _vm->playSoundNow(69); } uint16 msg = READ_LE_UINT16(table + 3); - _engine->displayMessage(msg); + _vm->displayMessage(msg); activate(false); resetSelectedObject(); return true; } table += 5; } - _engine->displayMessage(0xc3e2); + _vm->displayMessage(dsAddr_objCombineErrorMsg); activate(false); resetSelectedObject(); return true; @@ -247,14 +242,15 @@ bool Inventory::processEvent(const Common::Event &event) { return false; if (_hoveredObj != NULL) { - debug(0, "rclick object %u:%s", _hoveredObj->id, _hoveredObj->name.c_str()); - if (_hoveredObj->id != 51 && tryObjectCallback(_hoveredObj)) //do not process callback for banknote on r-click + debugC(0, kDebugInventory, "rclick object %u:%s", _hoveredObj->id, _hoveredObj->name.c_str()); + // do not process callback for banknote on r-click + if (_hoveredObj->id != kInvItemBanknote && tryObjectCallback(_hoveredObj)) return true; } _selectedObj = _hoveredObj; if (_selectedObj) - debug(0, "selected object %s", _selectedObj->name.c_str()); + debugC(0, kDebugInventory, "selected object %s", _selectedObj->name.c_str()); return true; case Common::EVENT_KEYDOWN: @@ -262,7 +258,7 @@ bool Inventory::processEvent(const Common::Event &event) { activate(false); return true; } - if (event.kbd.keycode == Common::KEYCODE_RETURN) { //triangle button on psp + if (event.kbd.keycode == Common::KEYCODE_RETURN) { activate(!_active); return true; } @@ -277,7 +273,6 @@ bool Inventory::processEvent(const Common::Event &event) { } } - void Inventory::Item::free() { _animation.free(); _surface.free(); @@ -294,30 +289,29 @@ void Inventory::Item::backgroundEffect(Graphics::Surface *s) { } } -void Inventory::Item::load(Inventory *inventory, uint item_id) { - InventoryObject *obj = &inventory->_objects[item_id]; +void Inventory::Item::load(Inventory *inventory, uint itemId) { + InventoryObject *obj = &inventory->_objects[itemId]; if (obj->animated) { if (_animation.empty()) { - debug(0, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]); + debugC(0, kDebugInventory, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]); Common::MemoryReadStream s(inventory->_items + inventory->_offset[obj->id - 1], inventory->_offset[obj->id] - inventory->_offset[obj->id - 1]); _animation.load(s, Animation::kTypeInventory); } } else { if (_surface.empty()) { - debug(0, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]); + debugC(0, kDebugInventory, "loading item %d from offset %x", obj->id, inventory->_offset[obj->id - 1]); Common::MemoryReadStream s(inventory->_items + inventory->_offset[obj->id - 1], inventory->_offset[obj->id] - inventory->_offset[obj->id - 1]); _surface.load(s, Surface::kTypeOns); } } } -void Inventory::Item::render(Inventory *inventory, uint item_id, Graphics::Surface *dst, int delta) { - InventoryObject *obj = &inventory->_objects[item_id]; - Resources *res = Resources::instance(); +void Inventory::Item::render(Inventory *inventory, uint itemId, Graphics::Surface *dst, int delta) { + InventoryObject *obj = &inventory->_objects[itemId]; backgroundEffect(dst); _rect.render(dst, _hovered ? 233 : 234); - load(inventory, item_id); + load(inventory, itemId); if (obj->animated) { if (_hovered) { Surface *s = _animation.currentFrame(delta); @@ -342,15 +336,16 @@ void Inventory::Item::render(Inventory *inventory, uint item_id, Graphics::Surfa if (inventory->_selectedObj != inventory->_hoveredObj) name += obj->name; - if (_hovered && inventory->_engine->scene->getMessage().empty()) { - int w = res->font7.render(NULL, 0, 0, name, 0xd1, true); - res->font7.render(dst, (320 - w) / 2, 180, name, 0xd1, true); + if (_hovered && inventory->_vm->scene->getMessage().empty()) { + int w = inventory->_vm->res->font7.render(NULL, 0, 0, name, textColorMark, true); + inventory->_vm->res->font7.render(dst, (kScreenWidth - w) / 2, 180, name, textColorMark, true); } } void Inventory::render(Graphics::Surface *surface, int delta) { if (!_active) return; + debugC(0, kDebugInventory, "Inventory::render()"); _background.render(surface); @@ -358,11 +353,10 @@ void Inventory::render(Graphics::Surface *surface, int delta) { for (int x = 0; x < 6; x++) { int idx = x + 6 * y; byte item = _inventory[idx]; - if (item == 0) - continue; - - //debug(0, "%d,%d -> %u", x0, y0, item); - _graphics[idx].render(this, item, surface, delta); + if (item != 0) { + debugC(0, kDebugInventory, "\t(x, y): %d,%d -> item: %u", x, y, item); + _graphics[idx].render(this, item, surface, delta); + } } } } diff --git a/engines/teenagent/inventory.h b/engines/teenagent/inventory.h index 61e5364542..d487848c2c 100644 --- a/engines/teenagent/inventory.h +++ b/engines/teenagent/inventory.h @@ -33,9 +33,112 @@ namespace TeenAgent { struct InventoryObject; class TeenAgentEngine; +// Maximum number of items found within game +const uint8 kNumInventoryItems = 92; + +// Inventory Item Ids +enum { + kInvItemNoItem = 0, // No item i.e. empty inventory slot + kInvItemFeather = 1, + kInvItemShotgun = 2, + kInvItemToolboxFull = 3, // Contains Car Jack and Spanner + kInvItemToolboxHalfEmpty = 4, // Contains Spanner + kInvItemSpanner = 5, + kInvItemComb = 6, + kInvItemFan = 7, + kInvItemBrokenPaddle = 8, + kInvItemPaddle = 9, // Repaired - BrokenPaddle combined with Branch (with Glue) + kInvItemFirstFlower = 10, // Smells nice + kInvItemSecondFlower = 11, // Really beautiful + kInvItemFeatherDusterClean = 12, + kInvItemChainsaw = 13, // Unfueled + kInvItemDrunkenChainsaw = 14, // Fueled with Whisky (Chainsaw combined with Whiskey) + kInvItemBranch = 15, + kInvItemWhisky = 16, + kInvItemNeedle = 17, + kInvItemWrapper = 18, + kInvItemChocCandy = 19, + kInvItemPotato = 20, + kInvItemRakeBroken = 21, + kInvItemHeartShapedCandy = 22, + kInvItemWrappedCandy = 23, // HeartShapedCandy combined with Wrapper + kInvItemRibbon = 24, + kInvItemRakeFixed = 25, // Rake combined with Ribbon + kInvItemNut = 26, + kInvItemPlasticApple = 27, + kInvItemCone = 28, + kInvItemSuperGlue = 29, + kInvItemConeAndNeedle = 30, // Cone combined with Needle + kInvItemConeAndFeather = 31, // Cone combined with Feather + kInvItemDart = 32, // Needle combined with ConeAndFeather or Feather combined with ConeAndNeedle + kInvItemFeatherDusterDirty = 33, + kInvItemPaintedPotato = 34, // Potato combined with Dirty Feather Duster (Soot) + kInvItemCarJack = 35, + kInvItemBone = 36, + kInvItemShovelAct2 = 37, + kInvItemRopeAct2 = 38, + kInvItemMask = 39, + kInvItemFins = 40, + kInvItemDiveEquipment = 41, // Mask combined with Fins + kInvItemAnchor = 42, + kInvItemGrapplingHook = 43, + kInvItemSickleBlunt = 44, + kInvItemCheese = 45, + kInvItemSickleSharp = 46, + kInvItemHandkerchief = 47, + kInvItemMouse = 48, + kInvItemRock = 49, + kInvItemNugget = 50, + kInvItemBanknote = 51, + kInvItemDictaphoneNoBatteries = 52, + kInvItemPolaroidCamera = 53, + kInvItemVideoTape = 54, + kInvItemSheetOfPaper = 55, + kInvItemCognac = 56, + kInvItemRemoteControl = 57, + kInvItemIceTongs = 58, + kInvItemCork = 59, + kInvItemWrappedCork = 60, // Cork combined with Sheet Of Paper + kInvItemPhoto = 61, + kInvItemChilliWithLabel = 62, + kInvItemPastryRoller = 63, + kInvItemFakeChilli = 64, + kInvItemLabel = 65, + kInvItemBatteries = 66, + kInvItemDictaphoneWithBatteries = 67, // Dictaphone combined with Batteries + kInvItemBurningPaper = 68, + kInvItemMeat = 69, + kInvItemPlasticBag = 70, + kInvItemSocks = 71, + kInvItemTimePills = 72, + kInvItemHandle = 73, + kInvItemChilliNoLabel = 74, + kInvItemPass = 75, + kInvItemBulb = 76, + kInvItemJailKey = 77, + kInvItemDelicatePlant = 78, + kInvItemSwissArmyKnife = 79, + kInvItemSpring = 80, + kInvItemShovelAct1 = 81, + kInvItemKaleidoscope = 82, + kInvItemSoldierNews = 83, + kInvItemGrenade = 84, + kInvItemMug = 85, // Empty + kInvItemMugOfMud = 86, // Full of mud + kInvItemCrumbs = 87, + kInvItemRopeAct1 = 88, + kInvItemRopeAndGrenade = 89, // Rope combined with Grenade + kInvItemMedicine = 90, + kInvItemDruggedFood = 91, // Crumbs combined with Medicine + kInvItemBird = 92 +}; + +// Maximum number of inventory items held by Ego (Mark) +const uint8 kInventorySize = 24; + class Inventory { public: - Inventory(TeenAgentEngine *engine); + Inventory(TeenAgentEngine *vm); ~Inventory(); void render(Graphics::Surface *surface, int delta); @@ -55,10 +158,10 @@ public: void resetSelectedObject() { _selectedObj = NULL; } private: - TeenAgentEngine *_engine; + TeenAgentEngine *_vm; Surface _background; byte *_items; - uint _offset[93]; + uint _offset[kNumInventoryItems+1]; Common::Array<InventoryObject> _objects; byte *_inventory; @@ -71,12 +174,12 @@ private: Item() : _hovered(false) {} void free(); - void load(Inventory *inventory, uint item_id); + void load(Inventory *inventory, uint itemId); void backgroundEffect(Graphics::Surface *s); - void render(Inventory *inventory, uint item_id, Graphics::Surface *surface, int delta); + void render(Inventory *inventory, uint itemId, Graphics::Surface *surface, int delta); }; - Item _graphics[24]; + Item _graphics[kInventorySize]; bool _active; Common::Point _mouse; diff --git a/engines/teenagent/music.cpp b/engines/teenagent/music.cpp index 1f44e9cfcb..b06a5f1f5e 100644 --- a/engines/teenagent/music.cpp +++ b/engines/teenagent/music.cpp @@ -22,6 +22,8 @@ #include "teenagent/music.h" #include "teenagent/resources.h" +#include "teenagent/teenagent.h" + #include "common/debug.h" #include "common/ptr.h" #include "common/textconsole.h" @@ -34,36 +36,36 @@ static const uint32 noteToPeriod[3][12] = { {214, 201, 189, 179, 170, 160, 151, 143, 135, 127, 120, 113} }; -MusicPlayer::MusicPlayer() : Paula(false, 44100, 5000), _id(0) { +MusicPlayer::MusicPlayer(TeenAgentEngine *vm) : Paula(false, 44100, 5000), _vm(vm), _id(0) { } MusicPlayer::~MusicPlayer() { + stop(); } bool MusicPlayer::load(int id) { - Resources *res = Resources::instance(); - - Common::ScopedPtr<Common::SeekableReadStream> stream(res->mmm.getStream(id)); + debugC(0, kDebugMusic, "MusicPlayer::load(%d)", id); + Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->res->mmm.getStream(id)); if (!stream) return false; char header[4]; stream->read(header, 4); - //check header? + // check header? Common::StackLock lock(_mutex); // Load the samples sampleCount = stream->readByte(); - debug(0, "sampleCount = %d", sampleCount); + debugC(0, kDebugMusic, "sampleCount = %d", sampleCount); for (byte currSample = 0; currSample < sampleCount; currSample++) { byte sample = stream->readByte(); // Load the sample data - byte sampleResource = ((sample >> 4) & 0x0F) * 10 + (sample & 0x0F); - debug(0, "currSample = %d, sample = 0x%02x, resource: %d", currSample, sample, sampleResource); - uint32 sampleSize = res->sam_mmm.getSize(sampleResource); + byte sampleResource = ((sample >> 4) & 0x0f) * 10 + (sample & 0x0f); + debugC(0, kDebugMusic, "currSample = %d, sample = 0x%02x, resource: %d", currSample, sample, sampleResource); + uint32 sampleSize = _vm->res->sam_mmm.getSize(sampleResource); if (sampleSize == 0) { warning("load: invalid sample %d (0x%02x)", sample, sample); _samples[sample].clear(); @@ -71,7 +73,7 @@ bool MusicPlayer::load(int id) { } _samples[sample].resize(sampleSize); - res->sam_mmm.read(sampleResource, _samples[sample].data, sampleSize); + _vm->res->sam_mmm.read(sampleResource, _samples[sample].data, sampleSize); } // Load the music data @@ -87,17 +89,17 @@ bool MusicPlayer::load(int id) { row.channels[1].note = stream->readByte(); row.channels[2].note = stream->readByte(); _rows.push_back(row); - } else if ((cmd & 0xF0) == 0x50) { + } else if ((cmd & 0xf0) == 0x50) { byte sample = stream->readByte(); - //debug(1, "%02x: set sample %02x", cmd, sample); - row.channels[(cmd & 0x0F) - 1].sample = sample; - } else if ((cmd & 0xF0) == 0x40) { + debugC(1, kDebugMusic, "%02x: set sample %02x", cmd, sample); + row.channels[(cmd & 0x0f) - 1].sample = sample; + } else if ((cmd & 0xf0) == 0x40) { byte vol = stream->readByte(); - //debug(1, "%02x: set volume %02x -> %02x", cmd, row.channels[(cmd & 0x0F) - 1].volume, vol); - //channel volume 0x40 * music volume 0x40 mixed with high bytes - row.channels[(cmd & 0x0F) - 1].volume = vol * 16; + debugC(1, kDebugMusic, "%02x: set volume %02x -> %02x", cmd, row.channels[(cmd & 0x0f) - 1].volume, vol); + // channel volume 0x40 * music volume 0x40 mixed with high bytes + row.channels[(cmd & 0x0f) - 1].volume = vol * 16; } else { - debug(0, "unhandled music command %02x", cmd); + debugC(0, kDebugMusic, "unhandled music command %02x", cmd); } } _currRow = 0; @@ -124,13 +126,13 @@ void MusicPlayer::interrupt() { for (int chn = 0; chn < 3; ++chn) { setChannelVolume(chn, row->channels[chn].volume); - //debug(0, "row->channels[%d].volume = %d", chn, row->channels[chn].volume); + debugC(2, kDebugMusic, "row->channels[%d].volume = %d", chn, row->channels[chn].volume); byte sample = (row->channels[chn].sample); if (row->channels[chn].note != 0 && sample != 0) { - //debug(0, "row->channels[%d].note = %d", chn, row->channels[chn].note); - //debug(0, "row->channels[%d].sample = %d", chn, row->channels[chn].sample); + debugC(2, kDebugMusic, "row->channels[%d].note = %d", chn, row->channels[chn].note); + debugC(2, kDebugMusic, "row->channels[%d].sample = %d", chn, row->channels[chn].sample); byte note = row->channels[chn].note; if (_samples[sample].size == 0) { @@ -139,11 +141,11 @@ void MusicPlayer::interrupt() { } setChannelData(chn, (const int8 *)_samples[sample].data, NULL, _samples[sample].size, 0); - setChannelPeriod(chn, noteToPeriod[((note >> 4) & 0x0F) - 1][(note & 0x0F)]); + setChannelPeriod(chn, noteToPeriod[((note >> 4) & 0x0f) - 1][(note & 0x0f)]); } } - //debug(0, "------------------------------------------------"); + debugC(2, kDebugMusic, "------------------------------------------------"); ++_currRow; } diff --git a/engines/teenagent/music.h b/engines/teenagent/music.h index 22b4fa5e8e..408436cf3a 100644 --- a/engines/teenagent/music.h +++ b/engines/teenagent/music.h @@ -28,10 +28,11 @@ namespace TeenAgent { +class TeenAgentEngine; + class MusicPlayer : public Audio::Paula { public: - - MusicPlayer(); + MusicPlayer(TeenAgentEngine *vm); ~MusicPlayer(); bool load(int id); @@ -41,6 +42,8 @@ public: void stop(); private: + TeenAgentEngine *_vm; + int _id; struct Row { diff --git a/engines/teenagent/objects.cpp b/engines/teenagent/objects.cpp index 748f342d54..5dad9ab99d 100644 --- a/engines/teenagent/objects.cpp +++ b/engines/teenagent/objects.cpp @@ -21,8 +21,10 @@ #include "common/debug.h" #include "common/memstream.h" + #include "teenagent/objects.h" #include "teenagent/resources.h" +#include "teenagent/teenagent.h" namespace TeenAgent { @@ -51,7 +53,6 @@ void Rect::render(Graphics::Surface *surface, uint8 color) const { surface->vLine(right, bottom, top, color); } - void Object::load(byte *src) { _base = src; @@ -59,39 +60,39 @@ void Object::load(byte *src) { rect.load(src); src += 8; - actor_rect.load(src); + actorRect.load(src); src += 8; - actor_orientation = *src++; + actorOrientation = *src++; enabled = *src++; name = (const char *)src; - description = parse_description((const char *)src); + description = parseDescription((const char *)src); } void Object::save() const { assert(_base != NULL); rect.save(); - actor_rect.save(); - _base[17] = actor_orientation; + actorRect.save(); + _base[17] = actorOrientation; _base[18] = enabled; } -void Object::setName(const Common::String &new_name) { +void Object::setName(const Common::String &newName) { assert(_base != 0); - strcpy((char *)(_base + 19), new_name.c_str()); - name = new_name; + strcpy((char *)(_base + 19), newName.c_str()); + name = newName; } void Object::dump(int level) const { - debug(level, "object: %u %u [%u,%u,%u,%u], actor: [%u,%u,%u,%u], orientation: %u, name: %s", id, enabled, + debugC(level, kDebugObject, "object: %u %u [%u,%u,%u,%u], actor: [%u,%u,%u,%u], orientation: %u, name: %s", id, enabled, rect.left, rect.top, rect.right, rect.bottom, - actor_rect.left, actor_rect.top, actor_rect.right, actor_rect.bottom, - actor_orientation, name.c_str() + actorRect.left, actorRect.top, actorRect.right, actorRect.bottom, + actorOrientation, name.c_str() ); } -Common::String Object::parse_description(const char *name) { +Common::String Object::parseDescription(const char *name) { const char *desc = name + strlen(name) + 1; if (*desc == 0) return Common::String(); @@ -101,7 +102,7 @@ Common::String Object::parse_description(const char *name) { while (*desc != 1 && *desc != 0) { Common::String line; while (*desc != 1 && *desc != 0) { - //debug(0, "%02x ", *desc); + debugC(2, kDebugObject, "%02x ", *desc); line += *desc++; } @@ -115,7 +116,7 @@ Common::String Object::parse_description(const char *name) { if (!result.empty()) result.deleteLastChar(); else - result = "Cool."; + result = "Cool."; // FIXME - Use dsAddr_coolMsg ? return result; } @@ -124,31 +125,31 @@ void InventoryObject::load(byte *src) { id = *src++; animated = *src++; name = (const char *)src; - description = Object::parse_description((const char *)src); + description = Object::parseDescription((const char *)src); } void UseHotspot::load(byte *src) { Common::MemoryReadStream in(src, 9); - inventory_id = in.readByte(); - object_id = in.readByte(); + inventoryId = in.readByte(); + objectId = in.readByte(); orientation = in.readByte(); - actor_x = in.readUint16LE(); - actor_y = in.readUint16LE(); + actorX = in.readUint16LE(); + actorY = in.readUint16LE(); callback = in.readUint16LE(); } void UseHotspot::dump(int level) const { - debug(level, - "hotspot: inv_id: %02x, obj_id: %02x, orientation?: %02x, actor position: (%d,%d), callback: %04x", - inventory_id, object_id, orientation, actor_x, actor_y, callback + debugC(level, kDebugObject, + "hotspot: invId: %02x, objId: %02x, orientation: %02x, actor position: (%d,%d), callback: %04x", + inventoryId, objectId, orientation, actorX, actorY, callback ); } void Walkbox::dump(int level) const { - debug(level, "walkbox %02x %02x [%d, %d, %d, %d] top: %u, right: %u, bottom: %u, left: %u", + debugC(level, kDebugObject, "walkbox %02x %02x [%d, %d, %d, %d] top: %u, right: %u, bottom: %u, left: %u", type, orientation, rect.left, rect.top, rect.right, rect.bottom, - side_hint[0], side_hint[1], side_hint[2], side_hint[3]); + sideHint[0], sideHint[1], sideHint[2], sideHint[3]); } void Walkbox::load(byte *src) { @@ -159,7 +160,7 @@ void Walkbox::load(byte *src) { rect.load(src); src += 8; for (byte i = 0; i < 4; ++i) - side_hint[i] = *src++; + sideHint[i] = *src++; } void Walkbox::save() const { diff --git a/engines/teenagent/objects.h b/engines/teenagent/objects.h index 555287fc56..6e7955766f 100644 --- a/engines/teenagent/objects.h +++ b/engines/teenagent/objects.h @@ -26,6 +26,8 @@ #include "common/rect.h" #include "graphics/surface.h" +#include "teenagent/teenagent.h" + namespace TeenAgent { enum {kActorUp = 1, kActorRight = 2, kActorDown = 3, kActorLeft = 4 }; @@ -46,13 +48,13 @@ struct Rect { } inline bool valid() const { - return left >= 0 && left < 320 && right >= 0 && right < 320 && top >= 0 && top < 200 && bottom >= 0 && bottom < 200; + return left >= 0 && left < kScreenWidth && right >= 0 && right < kScreenWidth && top >= 0 && top < kScreenHeight && bottom >= 0 && bottom < kScreenHeight; } void render(Graphics::Surface *surface, uint8 color) const; void dump(int level = 0) const { - debug(level, "rect[%u, %u, %u, %u]", left, top, right, bottom); + debugC(level, kDebugObject, "rect[%u, %u, %u, %u]", left, top, right, bottom); } inline void clear() { @@ -154,22 +156,21 @@ protected: }; struct Object { - byte id; //0 Rect rect; //1 - Rect actor_rect; //9 - byte actor_orientation; //17 + Rect actorRect; //9 + byte actorOrientation; //17 byte enabled; //18 //19 Common::String name, description; Object(): _base(NULL) {} void dump(int level = 0) const; - void setName(const Common::String &name); + void setName(const Common::String &newName); void load(byte *addr); void save() const; - static Common::String parse_description(const char *name); + static Common::String parseDescription(const char *name); protected: byte *_base; @@ -188,10 +189,10 @@ protected: }; struct UseHotspot { - byte inventory_id; - byte object_id; + byte inventoryId; + byte objectId; byte orientation; - uint16 actor_x, actor_y; + uint16 actorX, actorY; uint16 callback; void load(byte *src); void dump(int level = 0) const; @@ -201,7 +202,7 @@ struct Walkbox { byte type; byte orientation; Rect rect; - byte side_hint[4]; + byte sideHint[4]; Walkbox() : _base(NULL) {} void dump(int level = 0) const; diff --git a/engines/teenagent/pack.cpp b/engines/teenagent/pack.cpp index 5302e2eceb..2e6c913a72 100644 --- a/engines/teenagent/pack.cpp +++ b/engines/teenagent/pack.cpp @@ -20,6 +20,8 @@ */ #include "teenagent/pack.h" +#include "teenagent/teenagent.h" + #include "common/util.h" #include "common/debug.h" #include "common/memstream.h" @@ -44,7 +46,7 @@ bool FilePack::open(const Common::String &filename) { return false; _fileCount = file.readUint32LE(); - debug(0, "opened %s, found %u entries", filename.c_str(), _fileCount); + debugC(0, kDebugPack, "opened %s, found %u entries", filename.c_str(), _fileCount); offsets = new uint32[_fileCount + 1]; for (uint32 i = 0; i <= _fileCount; ++i) { offsets[i] = file.readUint32LE(); @@ -65,18 +67,17 @@ uint32 FilePack::read(uint32 id, byte *dst, uint32 size) const { file.seek(offsets[id - 1]); uint32 rsize = offsets[id] - offsets[id - 1]; uint32 r = file.read(dst, MIN(rsize, size)); - //debug(0, "read(%u, %u) = %u", id, size, r); + debugC(0, kDebugPack, "read(%u, %u) = %u", id, size, r); return r; } Common::SeekableReadStream *FilePack::getStream(uint32 id) const { if (id < 1 || id > _fileCount) return NULL; - //debug(0, "stream: %04x-%04x", offsets[id - 1], offsets[id]); + debugC(0, kDebugPack, "stream: %04x-%04x", offsets[id - 1], offsets[id]); return new Common::SeekableSubReadStream(&file, offsets[id - 1], offsets[id]); } - TransientFilePack::TransientFilePack() : offsets(0) {} TransientFilePack::~TransientFilePack() { @@ -97,7 +98,7 @@ bool TransientFilePack::open(const Common::String &filename) { return false; _fileCount = file.readUint32LE(); - debug(0, "opened %s, found %u entries", filename.c_str(), _fileCount); + debugC(0, kDebugPack, "opened %s, found %u entries", filename.c_str(), _fileCount); offsets = new uint32[_fileCount + 1]; for (uint32 i = 0; i <= _fileCount; ++i) { offsets[i] = file.readUint32LE(); @@ -124,14 +125,14 @@ uint32 TransientFilePack::read(uint32 id, byte *dst, uint32 size) const { uint32 rsize = offsets[id] - offsets[id - 1]; uint32 r = file.read(dst, MIN(rsize, size)); file.close(); - //debug(0, "read(%u, %u) = %u", id, size, r); + debugC(0, kDebugPack, "read(%u, %u) = %u", id, size, r); return r; } Common::SeekableReadStream *TransientFilePack::getStream(uint32 id) const { if (id < 1 || id > _fileCount) return NULL; - //debug(0, "stream: %04x-%04x", offsets[id - 1], offsets[id]); + debugC(0, kDebugPack, "stream: %04x-%04x", offsets[id - 1], offsets[id]); Common::File file; if (!file.open(_filename)) return NULL; @@ -146,7 +147,6 @@ Common::SeekableReadStream *TransientFilePack::getStream(uint32 id) const { return new Common::MemoryReadStream(ptr, r, DisposeAfterUse::YES); } - void MemoryPack::close() { chunks.clear(); } @@ -157,7 +157,7 @@ bool MemoryPack::open(const Common::String &filename) { return false; uint32 count = file.readUint32LE(); - debug(0, "opened %s, found %u entries [memory]", filename.c_str(), count); + debugC(0, kDebugPack, "opened %s, found %u entries [memory]", filename.c_str(), count); for (uint32 i = 0; i < count; ++i) { uint32 offset = file.readUint32LE(); int32 pos = file.pos(); @@ -199,5 +199,4 @@ Common::SeekableReadStream *MemoryPack::getStream(uint32 id) const { return new Common::MemoryReadStream(c.data, c.size, DisposeAfterUse::NO); } - } // End of namespace TeenAgent diff --git a/engines/teenagent/resources.cpp b/engines/teenagent/resources.cpp index dff58f98e2..442d0abf16 100644 --- a/engines/teenagent/resources.cpp +++ b/engines/teenagent/resources.cpp @@ -27,14 +27,10 @@ namespace TeenAgent { -Resources::Resources() {} - -Resources *Resources::instance() { - static Resources i; - return &i; +Resources::Resources() { } -void Resources::deinit() { +Resources::~Resources() { off.close(); on.close(); ons.close(); @@ -61,6 +57,36 @@ quick note on varia resources: 11: quit shareware */ +#define CSEG_SIZE 46000 // 0xb3b0 +#define DSEG_SIZE 59280 // 0xe790 +#define ESEG_SIZE 35810 // 0x8be2 + +void Resources::precomputeDialogOffsets() { + dialogOffsets.push_back(0); + int n = 0; + uint8 current, last = 0xff; + for (uint i = 0; i < eseg.size(); i++) { + current = eseg.get_byte(i); + + if (n == 4) { + dialogOffsets.push_back(i); + n = 0; + } + + if (current != 0x00 && last == 0x00) + n = 0; + + if (current == 0x00) + n++; + + last = current; + } + + debug(1, "Resources::precomputeDialogOffsets() - Found %d dialogs", dialogOffsets.size()); + for (uint i = 0; i < dialogOffsets.size(); i++) + debug(1, "\tDialog #%d: Offset 0x%04x", i, dialogOffsets[i]); +} + bool Resources::loadArchives(const ADGameDescription *gd) { Common::File *dat_file = new Common::File(); if (!dat_file->open("teenagent.dat")) { @@ -93,18 +119,17 @@ bool Resources::loadArchives(const ADGameDescription *gd) { } #endif - cseg.read(dat, 0xb3b0); - dseg.read(dat, 0xe790); - eseg.read(dat, 0x8be2); + dat->skip(CSEG_SIZE); + dseg.read(dat, DSEG_SIZE); + eseg.read(dat, ESEG_SIZE); delete dat; + precomputeDialogOffsets(); + FilePack varia; varia.open("varia.res"); - font7.load(varia, 7); - font7.width_pack = 1; - font7.height = 11; - font8.load(varia, 8); - font8.height = 31; + font7.load(varia, 7, 11, 1); + font8.load(varia, 8, 31, 0); varia.close(); off.open("off.res"); @@ -150,13 +175,13 @@ Common::SeekableReadStream *Resources::loadLan000(uint32 id) const { switch (id) { case 81: - if (dseg.get_byte(0xDBAD)) + if (dseg.get_byte(dsAddr_dogHasBoneFlag)) return lan500.getStream(160); break; case 137: - if (dseg.get_byte(0xDBC5) == 1) { - if (dseg.get_byte(0xDBC6) == 1) + if (dseg.get_byte(dsAddr_mansionTVOnFlag) == 1) { + if (dseg.get_byte(dsAddr_mansionVCRPlayingTapeFlag) == 1) return lan500.getStream(203); else return lan500.getStream(202); @@ -164,31 +189,31 @@ Common::SeekableReadStream *Resources::loadLan000(uint32 id) const { break; case 25: - if (dseg.get_byte(0xDBDF) == 2) { + if (dseg.get_byte(dsAddr_FirstActTrialState) == 2) { return lan500.getStream(332); } break; case 37: - if (dseg.get_byte(0xdbe2) == 1) { + if (dseg.get_byte(dsAddr_act1GuardState) == 1) { return lan500.getStream(351); - } else if (dseg.get_byte(0xdbe2) == 2) { + } else if (dseg.get_byte(dsAddr_act1GuardState) == 2) { return lan500.getStream(364); } break; case 29: - if (dseg.get_byte(0xDBE7) == 1) { + if (dseg.get_byte(dsAddr_birdOnBarRadioAntennaFlag) == 1) { return lan500.getStream(380); } case 30: - if (dseg.get_byte(0xDBE7) == 1) { + if (dseg.get_byte(dsAddr_birdOnBarRadioAntennaFlag) == 1) { return lan500.getStream(381); } case 42: - if (dseg.get_byte(0xDBEC) == 1) { + if (dseg.get_byte(dsAddr_johnNotyOutsideMansionDoorFlag) == 1) { return lan500.getStream(400); } } diff --git a/engines/teenagent/resources.h b/engines/teenagent/resources.h index 5c08a46489..7aae2f9ec8 100644 --- a/engines/teenagent/resources.h +++ b/engines/teenagent/resources.h @@ -31,18 +31,1144 @@ struct ADGameDescription; namespace TeenAgent { +// Code Segment Addresses (Read Only) +// Intro function : 0x024c +const uint16 csAddr_intro = 0x024c; +// Pole Climb Fail function : 0x4173 +const uint16 csAddr_poleClimbFail = 0x4173; +// Move Ego (Mark) To Suspicious Position function : 0x505c +const uint16 csAddr_egoSuspiciousPosition = 0x505c; +// Guard Scare Timeout function : 0x516d +const uint16 csAddr_guardScareTimeout = 0x516d; +// Guard Drinking function : 0x5189 +const uint16 csAddr_guardDrinking = 0x5189; +// Move Ego (Mark) To Default Position function : 0x557e +const uint16 csAddr_egoDefaultPosition = 0x557e; +// Cave NOP function : 0x599b +const uint16 csAddr_caveNOP = 0x599b; +// Enter Cave function : 0x5a21 +const uint16 csAddr_enterCave = 0x5a21; +// Ego (Mark) Scared By Spider function : 0x60b5 +const uint16 csAddr_egoScaredBySpider = 0x60b5; +// Move to Ladder and Leave Cellar function : 0x60d9 +const uint16 csAddr_moveToLadderAndLeaveCellar = 0x60d9; +// Leave Cellar function : 0x612b +const uint16 csAddr_leaveCellar = 0x612b; +// Too Dark function : 0x61fe +const uint16 csAddr_TooDark = 0x61fe; +// Move Ego (Mark) To Bottom-Right or Turn function : 0x6849 +const uint16 csAddr_egoBottomRightTurn = 0x6849; +// Checking Drawers function : 0x68e6 +const uint16 csAddr_checkingDrawers = 0x68e6; +// Drawer Open Message function : 0x6b86 +const uint16 csAddr_DrawerOpenMessage = 0x6b86; +// Is Cook Gone function : 0x70e0 +const uint16 csAddr_isCookGone = 0x70e0; +// Giving Flower to Old Lady function : 0x88de +const uint16 csAddr_givingFlowerToOldLady = 0x88de; +// Give Another Flower to Old Lady function : 0x890b +const uint16 csAddr_giveAnotherFlowerToOldLady = 0x890b; +// Giving Flower to Anne function : 0x8942 +const uint16 csAddr_givingFlowerToAnne = 0x8942; +// Give Another Flower to Anne function : 0x89aa +const uint16 csAddr_giveAnotherFlowerToAnne = 0x89aa; +// Putting Rock in Hole function : 0x8d57 +const uint16 csAddr_putRockInHole = 0x8d57; +// Mouse Out Of Hole Timeout function : 0x8d79 +const uint16 csAddr_mouseOutOfHoleTimeout = 0x8d79; +// Robot Safe (Mike) Already Unlocked Check function : 0x9166 +const uint16 csAddr_robotSafeAlreadyUnlockedCheck = 0x9166; +// Robot Safe (Mike) Unlock Check function : 0x9175 +const uint16 csAddr_robotSafeUnlockCheck = 0x9175; +// Open Full Toolbox function : 0x98fa +const uint16 csAddr_openFullToolbox = 0x98fa; +// Open Half Empty Toolbox function : 0x9910 +const uint16 csAddr_openHalfEmptyToolbox = 0x9910; +// Use Diving Equipment function : 0x9921 +const uint16 csAddr_useDivingEquipment = 0x9921; +// Successfully Got Anchor function : 0x99e0 +const uint16 csAddr_gotAnchor = 0x99e0; +// No Anchor Timeout function : 0x9a1d +const uint16 csAddr_noAnchorTimeout = 0x9a1d; +// Get Out of Lake function : 0x9a7a +const uint16 csAddr_getOutOfLake = 0x9a7a; +// Dig Under Mansion Wall function : 0x9aca +const uint16 csAddr_digMansionWall = 0x9aca; +// Too Dark Here function : 0x9c66 +const uint16 csAddr_tooDarkHere = 0x9c66; +// Examine Banknote function : 0x9c6d +const uint16 csAddr_examineBanknote = 0x9c6d; +// Use Time Pills function : 0x9c79 +const uint16 csAddr_useTimePills = 0x9c79; +// Mansion Intrusion Attempt function : 0x9d45 +const uint16 csAddr_mansionIntrusionAttempt = 0x9d45; +// Second Mansion Intrusion function : 0x9d90 +const uint16 csAddr_secondMansionIntrusion = 0x9d90; +// Third Mansion Intrusion function : 0x9de5 +const uint16 csAddr_thirdMansionIntrusion = 0x9de5; +// Fourth Mansion Intrusion function : 0x9e54 +const uint16 csAddr_fourthMansionIntrusion = 0x9e54; +// Fifth Mansion Intrusion function : 0x9ec3 +const uint16 csAddr_fifthMansionIntrusion = 0x9ec3; +// Sixth Mansion Intrusion function : 0x9f3e +const uint16 csAddr_sixthMansionIntrusion = 0x9f3e; +// Display Message function : 0xa055 +const uint16 csAddr_displayMsg = 0xa055; +// Reject Message function : 0xa4d6 +const uint16 csAddr_rejectMsg = 0xa4d6; + +// Data Segment Addresses +// Timed Callback State Variable : 0x0000 +const uint16 dsAddr_timedCallbackState = 0x0000; // 1 byte + +// Cursor Graphic 8*12px : 0x00da to 0x0139 (Read Only) +const uint16 dsAddr_cursor = 0x00da; + +// Reject Message Address Pointers : (4 * 2-byte) = 0x339e to 0x33a5 +const uint16 dsAddr_rejectMsgPtr = 0x339e; +// Reject Message #0 : 0x33a6 to 0x33c9 +const uint16 dsAddr_rejectMsg0 = 0x33a6; // "I have no idea what to do with it" +// Reject Message #1 : 0x33ca to 0x33f5 +const uint16 dsAddr_rejectMsg1 = 0x33ca; // "I can't imagine what I could do with this" +// Reject Message #2 : 0x33f6 to 0x3425 +const uint16 dsAddr_rejectMsg2 = 0x33f6; // "I can't figure out what I should do with this" +// Reject Message #3 : 0x3426 to 0x344f +const uint16 dsAddr_rejectMsg3 = 0x3426; // "I can't find any reason to mess with it" +// Cool Message : 0x3450 to 0x3456 +const uint16 dsAddr_coolMsg = 0x3450; // "Cool." +// Object Usage Error Message : 0x3457 to 0x3467 +const uint16 dsAddr_objErrorMsg = 0x3457; // "That's no good" +// Car Jack Message : 0x3468 to 0x348f +const uint16 dsAddr_carJackMsg = 0x3468; // "Wow! There's a car jack inside! Great!" +// Spanner Message : 0x3490 to 0x34c6 +const uint16 dsAddr_spannerMsg = 0x3490; // "There's something else inside the toolbox! A spanner!" +// Last Chance Message : 0x34c7 to 0x34d4 +const uint16 dsAddr_lastChanceMsg = 0x34c7; // "Last chance?" +// Give Up Message : 0x34d5 to 0x34e0 +const uint16 dsAddr_giveUpMsg = 0x34d5; // "I give up" +// Avoid Bees Message : 0x34e1 to 0x351e +const uint16 dsAddr_avoidBeesMsg = 0x34e1; // "I'm going to stay at least five meters away from these bees!" +// Boat Empty Message : 0x351f to 0x3541 +const uint16 dsAddr_boatEmptyMsg = 0x351f; // "There's nothing else in the boat" +// Too Hard Wood Message : 0x3542 to 0x3562 +const uint16 dsAddr_tooHardWoodMsg = 0x3542; // "This wood is too hard to break" +// Boo Message : 0x3563 to 0x3569 +const uint16 dsAddr_BooMsg = 0x3563; // "Booo!" +// Dont Push Luck Message : 0x356a to 0x358f +const uint16 dsAddr_dontPushLuckMsg = 0x356a; // "I don't think I should push my luck" +// Ordinary Haystack Message : 0x3590 to 0x35b1 +const uint16 dsAddr_ordinaryHaystackMsg = 0x3590; // "Just an ordinary hay stack. Now." +// Needle Haystack Message : 0x35b2 to 0x35e7 +const uint16 dsAddr_needleHaystackMsg = 0x35b2; // "And they say you can't find a needle in a haystack" +// No Potatoes Message : 0x35e8 to 0x3604 +const uint16 dsAddr_noPotatoMsg = 0x35e8; // "There are no more potatoes" +// Trousers Message : 0x3605 to 0x363e +const uint16 dsAddr_trousersMsg = 0x3605; // "Good I always asked mum for trousers with BIG pockets" +// Life Is Brutal Message : 0x363f to 0x364f +const uint16 dsAddr_lifeIsBrutalMsg = 0x363f; // "Life is brutal" +// Life Brutal Message : 0x3650 to 0x3667 +const uint16 dsAddr_lifeBrutalMsg = 0x3650; // "Life is really brutal" +// Tickled Message : 0x3668 to 0x367e +const uint16 dsAddr_tickledMsg = 0x3668; // "Something tickled me!" +// Its Gone Message : 0x367f to 0x3693 +const uint16 dsAddr_itsGoneMsg = 0x367f; // "At least it's gone" +// Monsters Message : 0x3694 to 0x36c1 +const uint16 dsAddr_monstersMsg = 0x3694; // "Who knows what monsters live in there" +// No Hands Message : 0x36c2 to 0x370e +const uint16 dsAddr_noHandsMsg = 0x36c2; // "I'd better not put my hands in there..." +// Totally Empty Message : 0x370f to 0x372d +const uint16 dsAddr_totalEmptyMsg = 0x370f; // "I can see it's totally empty" +// One Small Step Message : 0x372e to 0x3765 +const uint16 dsAddr_oneSmallStepMsg = 0x372e; // "One small step for man, one big pain in the head" +// No Chance Message : 0x3766 to 0x378f +const uint16 dsAddr_noChanceMsg = 0x3766; // "I won't take my chances a second time" +// Dinosaur Bone Message : 0x3790 to 0x37b7 +const uint16 dsAddr_dinoBoneMsg = 0x3790; // "I really hope this is DINOSAUR bone" +// Wall Shaken Message : 0x37b8 to 0x37e9 +const uint16 dsAddr_wallShakenMsg = 0x37b8; // "Wow! This must have shaken all the nearby walls!" +// Kinda Dark Message : 0x37ea to 0x3800 +const uint16 dsAddr_kindaDarkMsg = 0x37ea; // "It's kinda dark here" +// Not in Dark Message : 0x3801 to 0x3831 +const uint16 dsAddr_notInDarkMsg = 0x3801; // "I'm not going to wander here in the dark again" +// Shut Valve Message : 0x3832 to 0x387b +const uint16 dsAddr_shutValveMsg = 0x3832; // "Shutting the valve shook the dirt from the wall..." +// Need Sunglasses Message : 0x387c to 0x38a6 +const uint16 dsAddr_needSunglassesMsg = 0x387c; // "Sorry buddy, but I need your sunglasses" +// Not Best Place Message : 0x38a7 to 0x38cd +const uint16 dsAddr_notBestPlaceMsg = 0x38a7; // "It's not the best place for diving" +// Not Here Message : 0x38ce to 0x38da +const uint16 dsAddr_notHereMsg = 0x38ce; // "Not here" +// Can't Talk Underwater Message : 0x38db to 0x38fe +const uint16 dsAddr_cantTalkUnderwaterMsg = 0x38db; // "I really can't talk underwater!" +// Not Swimming There Message : 0x38ff to 0x3931 +const uint16 dsAddr_notSwimmingThereMsg = 0x38ff; // "I don't think swimming there is worth the effort" +// Too Little Air Message : 0x3932 to 0x3988 +const uint16 dsAddr_tooLittleAirMsg = 0x3932; // "If I want to get this anchor I have to swim there when I have more air in my lungs" +// Hooked Anchor Message : 0x3989 to 0x39ad +const uint16 dsAddr_hookedAnchorMsg = 0x3989; // "I was really hooked on this anchor!" +// Seaweed Message : 0x39ae to 0x39f5 +const uint16 dsAddr_seaweedMsg = 0x39ae; // "This seaweed is just like the flowers I gave mum on her last birthday" +// Fish Boat Message : 0x39f6 to 0x3a27 +const uint16 dsAddr_fishBoatMsg = 0x39f6; // "I wonder what fish do inside this boat at night" +// Fish Something Message : 0x3a28 to 0x3a59 +const uint16 dsAddr_fishSomethingMsg = 0x3a28; // "I think I have to fish out something down there" +// Fish Don't Worry Message : 0x3a5a to 0x3a84 +const uint16 dsAddr_fishDontWorryMsg = 0x3a5a; // "At least fish don't worry about the rain" +// Not Red Herring Message : 0x3a85 to 0x3ab6 +const uint16 dsAddr_notRedHerringMsg = 0x3a85; // "I hope all this fish stuff is not a red herring" +// Nice Down Message : 0x3ab7 to 0x3acd +const uint16 dsAddr_niceDownMsg = 0x3ab7; // "It's nice down there" +// Hey Let Go Message : 0x3ace to 0x3ae5 +const uint16 dsAddr_heyLetGoMsg = 0x3ace; // "Hey, let go, will ya?!" +// Aaahhh Message : 0x3ae6 to 0x3afc +const uint16 dsAddr_aaahhhMsg = 0x3ae6; // "Aaaaaaaaaaaaahhh!" +// Oops Message : 0x3afd to 0x3b03 +const uint16 dsAddr_oopsMsg = 0x3afd; // "Oops" +// Found Food Message : 0x3b04 to 0x3b2e +const uint16 dsAddr_foundFoodMsg = 0x3b04; // "People leave food in unbelievable places" +// Come Here Message : 0x3b2f to 0x3b58 +const uint16 dsAddr_comeHereMsg = 0x3b2f; // "Come here, I've got something for you" +// Cant Catch Message : 0x3b59 to 0x3b6b +const uint16 dsAddr_cantCatchMsg = 0x3b59; // "I can't catch it!" +// Trapped Mouse Message : 0x3b6c to 0x3b82 +const uint16 dsAddr_trappedMouseMsg = 0x3b6c; // "The mouse is trapped!" +// Yikes Message : 0x3b83 to 0x3b8a +const uint16 dsAddr_yikesMsg = 0x3b83; // "Yikes!" +// Mouse Nerve Message : 0x3b8b to 0x3bab +const uint16 dsAddr_mouseNerveMsg = 0x3b8b; // "Boy, this mouse has some nerve!" +// Drawers Empty Message : 0x3bac to 0x3bd1 +const uint16 dsAddr_drawersEmptyMsg = 0x3bac; // "There's nothing else in the drawers" +// Rid Bush Message 0x3bd2 to 0x3bf5 +const uint16 dsAddr_ridBushMsg = 0x3bd2; // "I must get rid of this bush first" +// Mouse Gone Message : 0x3bf6 to 0x3c0a +const uint16 dsAddr_mouseGoneMsg = 0x3bf6; // "The mouse has gone!" +// Nonsense Message : 0x3c0b to 0x3c15 +const uint16 dsAddr_nonsenseMsg = 0x3c0b; // "Nonsense" +// Good Doggy Message : 0x3c16 to 0x3c30 +const uint16 dsAddr_goodDoggyMsg = 0x3c16; // "I understand. Good doggy" +// Here Boy Message : 0x3c31 to 0x3c3c +const uint16 dsAddr_hereBoyMsg = 0x3c31; // "Here, boy" +// Friends Now Message : 0x3c3d to 0x3c57 +const uint16 dsAddr_friendsNowMsg = 0x3c3d; // "I hope we're friends now" +// Not Think Right Place Message : 0x3c58 to 0x3c7f +const uint16 dsAddr_notThinkRightPlaceMsg = 0x3c58; // "I don't think this is the right place" +// Cutscene Message A : 0x3c80 to 0x3c99 +const uint16 dsAddr_cutsceneMsgA = 0x3c80; // "Hundred moments later" +// Cutscene Message B : 0x3c9a to 0x3cbb +const uint16 dsAddr_cutsceneMsgB = 0x3c9a; // "Another hundred moments later" +// Found Crude Oil Message : 0x3cbc to 0x3ce9 +const uint16 dsAddr_foundCrudeOilMsg = 0x3cbc; // "At least I found crude oil and I'll be rich" +// My Life Message : 0x3cea to 0x3cfa +const uint16 dsAddr_myLifeMsg = 0x3cea; // "That's my life" +// Confusion Message : 0x3cfb to 0x3d00 +const uint16 dsAddr_ConfusionMsg = 0x3cfb; // "!?&!" +// Grandpa Promise Message : 0x3d01 to 0x3d1f +const uint16 dsAddr_grandpaPromiseMsg = 0x3d01; // "But grandpa, you promised!" +// Oh Lets Go Message : 0x3d20 to 0x3d39 +const uint16 dsAddr_ohLetsGoMsg = 0x3d20; // "Oh all right. Let's go" +// Bye Message : 0x3d3a to 0x3d3f +const uint16 dsAddr_byeMsg = 0x3d3a; // "Bye." +// No Need Message : 0x3d40 to 0x3d58 +const uint16 dsAddr_noNeedMsg = 0x3d40; // "No need to do it again" +// Girl Talk Message : 0x3d59 to 0x3d85 +const uint16 dsAddr_girlTalkMsg = 0x3d59; // "I really don't know how to talk to girls" +// Dont Work Purpose Message : 0x3d86 to 0x3dae +const uint16 dsAddr_dontWorkPurposeMsg = 0x3d86; // "I usually don't work without a purpose" +// Nut Real Message : 0x3daf to 0x3dc5 +const uint16 dsAddr_nutRealMsg = 0x3daf; // "Only the nut is real" +// Hen Fly Message : 0x3dc6 to 0x3df3 +const uint16 dsAddr_henFlyMsg = 0x3dc6; // "I wonder if hens can fly. Come here, baby" +// First Test Fail Message : 0x3df4 to 0x3e07 +const uint16 dsAddr_firstTestFailMsg = 0x3df4; // "First test failed" +// Rid Frustations Message : 0x3e08 to 3e30 +const uint16 dsAddr_ridFrustationsMsg = 0x3e08; // "I'd already got rid of my frustrations" +// Road Nowhere Message : 0x3e31 to 0x3e4e +const uint16 dsAddr_roadNowhereMsg = 0x3e31; // "Nah. It's a road to nowhere" +// Open Boot Message 0x3e4f to 0x3e62 +const uint16 dsAddr_openBootMsg = 0x3e4f; // "It opens the boot" +// Shut Tight Message : 0x3e63 to 0x3e74 +const uint16 dsAddr_shutTightMsg = 0x3e63; // "It's shut tight" +// Boot Empty Message : 0x3e75 to 0x3e97 +const uint16 dsAddr_bootEmptyMsg = 0x3e75; // "There's nothing else in the boot" +// Clothes Dry Message : 0x3e98 to 0x3eb1 +const uint16 dsAddr_clothesDryMsg = 0x3e98; // "The clothes are dry now." +// Crow Kill Message : 0x3eb2 to 0x3ed5 +const uint16 dsAddr_crowKillMsg = 0x3eb2; // "I'm sure these crows will kill me" +// Get Rid Of Guard First Message : 0x3ed6 to 0x3f29 +const uint16 dsAddr_getRidOfGuardFirstMsg = 0x3ed6; // "If I want to get inside I must get rid of this guard first..." +// Wall Too Smooth Message : 0x3f2a to 0x3f53 +const uint16 dsAddr_wallTooSmoothMsg = 0x3f2a; // "The wall surface is too smooth to climb" +// Too Much Resin To Climb Message : 0x3f54 to 0x3f84 +const uint16 dsAddr_tooMuchResinToClimbMsg = 0x3f54; // "I could climb it if there wasn't so much resin" +// Only Green Rect Message : 0x3f85 to 0x3feb +const uint16 dsAddr_onlyGreenRectMsg = 0x3f85; // "The only green stuff that I like is that rectangular piece of paper with..." +// Don't Wanna Touch Hedgehog Message : 0x3fec to 0x402d +const uint16 dsAddr_dontWannaTouchHedgehogMsg = 0x3fec; // "I don't wanna touch it. Its spines could hurt my delicate hands" +// Not Hungry Message : 0x402e to 0x4046 +const uint16 dsAddr_notHungryMsg = 0x402e; // "Thanks, I'm not hungry" +// No Long Hands Message : 0x4047 to 0x406c +const uint16 dsAddr_noLongHandsMsg = 0x4047; // "I really don't have such long hands" +// Too Far To Swim Message : 0x406d to 0x4089 +const uint16 dsAddr_tooFarToSwimMsg = 0x406d; // "It's too far to swim there" +// Echo Message : 0x408a to 0x4090 +const uint16 dsAddr_echoMsg = 0x408a; // "Echo!" +// Loud Echo Message : 0x4091 to 0x4097 +const uint16 dsAddr_loudEchoMsg = 0x4091; // "ECHO!" +// Who There Message : 0x4098 to 0x40a6 +const uint16 dsAddr_whoThereMsg = 0x4098; // "Who's there?!" +// Loud Who There Message : 0x40a7 to 0x40b5 +const uint16 dsAddr_loudWhoThereMsg = 0x40a7; // "WHO'S THERE?!" +// Dont Copy Message : 0x40b6 to 0x40cd +const uint16 dsAddr_dontCopyMsg = 0x40b6; // "DON'T COPY ME!" +// Loud Dont Copy Message : 0x40ce to 0x40e7 +const uint16 dsAddr_loudDontCopyMsg = 0x40ce; // "DON'T COPY ME!!!" +// Throw Rock Message : 0x40e8 to 0x410e +const uint16 dsAddr_throwRockMsg = 0x40e8; // "OR I WILL THROW A ROCK DOWN THERE!" +// Or I Will Message : 0x410f to 0x411c +const uint16 dsAddr_orIWillMsg = 0x410f; // "OR I WILL" +// Still There Message : 0x411d to 0x4132 +const uint16 dsAddr_stillThereMsg = 0x411d; // "Are you still there?" +// No Bucket Message : 0x4133 to 0x4163 +const uint16 dsAddr_noBucketMsg = 0x4133; // "It's not a barrel-organ. And there's no bucket." +// Dont Need To Open Message : 0x4164 to 0x417d +const uint16 dsAddr_dontNeedToOpenMsg = 0x4164; // "I don't need to open it" +// Hmm Grass Message : 0x417e to 41b0 +const uint16 dsAddr_hmmGrassMsg = 0x417e; // "Hmmm. Grass..." +// Find Nut Message : 0x41b1 to 0x41ee +const uint16 dsAddr_findNutMsg = 0x41b1; // "I won't find the nut just like that. The grass is too dense" +// Not Horny Message : 0x41ef to 0x41fe +const uint16 dsAddr_notHornyMsg = 0x41ef; // "I'm not horny" +// Can't Jump So High Message : 0x41ff to 0x423e +const uint16 dsAddr_CantJumpMsg = 0x41ff; // "No way I can jump so high, cause, err, white men can't jump" +// Don't Need It Message : 0x423f to 0x4250 +const uint16 dsAddr_dontNeedItMsg = 0x423f; // "I don't need it" +// Not Santa Claus Message : 0x4251 to 0x4266 +const uint16 dsAddr_notSantaClausMsg = 0x4251; // "I'm not Santa Claus" +// No Plastic Imitations Message : 0x4267 to 0x4288 +const uint16 dsAddr_noPlasticImitationsMsg = 0x4267; // "I don't need plastic imitations" +// Too Fragile Message : 0x4289 to 0x42ab +const uint16 dsAddr_tooFragileMsg = 0x4289; // "It's too fragile to carry around" +// Keep It Open Message : 0x42ac to 0x42c6 +const uint16 dsAddr_keepItOpenMsg = 0x42ac; // "I'd like to keep it open" +// Not Taking Socks Message : 0x42c7 to 0x4305 +const uint16 dsAddr_notTakingSocksMsg = 0x42c7; // "I really don't want to walk around with someone else's socks" +// Not Tired Message : 0x4306 to 0x431d +const uint16 dsAddr_notTiredMsg = 0x4306; // "Thanks, I'm not tired" +// Too Big Message : 0x431e to 0x434d +const uint16 dsAddr_tooBigMsg = 0x431e; // "It's too big and I doubt if I'll ever need it" +// No Secret Passage Message : 0x434e to 0x437f +const uint16 dsAddr_noSecretPassageMsg = 0x434e; // "I don't think there's any secret passage inside" +// No Fruit Message : 0x4380 to 0x43ab +const uint16 dsAddr_noFruitMsg = 0x4380; // "There are no more interesting fruits here" +// Jug Me Message : 0x43ac to 0x43cd +const uint16 dsAddr_jugMeMsg = 0x43ac; // "They can jug me if I steal this" +// Leave Flowers Alone Message : 0x43ce to 0x4411 +const uint16 dsAddr_leaveFlowersAloneMsg = 0x43ce; // "I'd better leave it. Women are really oversensitive about flowers." +// Mirror Mirror Message : 0x4412 to 0x444e +const uint16 dsAddr_mirrorMirrorMsg = 0x4412; // "Mirror, Mirror on the wall...." +// Think Too Long Message : 0x444f to 0x446a +const uint16 dsAddr_thinkTooLongMsg = 0x444f; // "Hey, don't think too long" +// Hint Male Message : 0x446b to 0x4491 +const uint16 dsAddr_HintMaleMsg = 0x446b; // "A hint: Someone in this room, a male" +// OK Wait Message : 0x4492 to 0x44a6 +const uint16 dsAddr_okWaitMsg = 0x4492; // "OK, take your time" +// Busy Thinking Message : 0x44a7 to 0x44d5 +const uint16 dsAddr_busyThinkingMsg = 0x44a7; // "I'd better not interrupt it's thought process" +// No Dentists Message : 0x44d6 to 0x450d +const uint16 dsAddr_noDentistsMsg = 0x44d6; // "I don't want to have anything in common with dentists" +// Too Heavy Message : 0x450e to 0x4531 +const uint16 dsAddr_tooHeavyMsg = 0x450e; // "It's too heavy. Not that I'm wimp" +// What Got Message : 0x4532 to 0x4554 +const uint16 dsAddr_whatGotMsg = 0x4532; // "Let's look what we've got here" +// Strawberry Jam Message : 0x4555 to 0x4567 +const uint16 dsAddr_strawberryJamMsg = 0x4555; // "Strawberry jam" +// Gooseberry Jam Message : 0x4568 to 0x457a +const uint16 dsAddr_gooseberryJamMsg = 0x4568; // "Gooseberry jam" +// Blackberry Jam Message : 0x457b to 0x458d +const uint16 dsAddr_blackberryJamMsg = 0x457b; // "Blackberry jam" +// Bilberry Jam Message : 0x458e to 0x459e +const uint16 dsAddr_bilberryJamMsg = 0x458e; // "Bilberry jam" +// Get Me Out Jam Message : 0x459f to 0x45b7 +const uint16 dsAddr_getMeOutJamMsg = 0x459f; // "Get me out of this jam!" +// Rosemary Jam Message : 0x45b8 to 0x45d9 +const uint16 dsAddr_rosemaryJamMsg = 0x45b8; // "Oh, and there is Rosemary jam" +// Know Rosemary Message : 0x45da to 0x4602 +const uint16 dsAddr_knowRosemaryMsg = 0x45da; // "I used to know someone called Rosemary" +// Unwanted Jams Message : 0x4603 to 0x461c +const uint16 dsAddr_unwantedJamsMsg = 0x4603; // "I don't want those jams" +// Too Dark Message : 0x461d to 0x463b +const uint16 dsAddr_TooDarkMsg = 0x461d; // "It's too dark to see clearly" +// Yeow Message : 0x463c to 0x4649 +const uint16 dsAddr_yeowMsg = 0x463c; // "YEEEOOOWWWW!" +// Yawn Message : 0x464a to 0x4651 +const uint16 dsAddr_yawnMsg = 0x464a; // "(yawn)" +// Laughter Message : 0x4652 to 0x465d +const uint16 dsAddr_laughterMsg = 0x4652; // "(laughter)" +// No Hands Sharp Thorns Message : 0x465e to 0x46a0 +const uint16 dsAddr_noHandsSharpThornsMsg = 0x465e; // "I can't remove it with my hands. these thorns look really sharp" +// No Chainsaw Fuel Message : 0x46a1 to 0x46c2 +const uint16 dsAddr_noChainsawFuelMsg = 0x46a1; // "There's no fuel in the chainsaw" +// Thorns Too Thin Message : 0x46c3 to 0x46f6 +const uint16 dsAddr_thornsTooThinMsg = 0x46c3; // "Thorns are too thin, the chainsaw is useless here" +// Rock Walking Gee Message : 0x46f7 to 0x473c +const uint16 dsAddr_rockWalkingGeeMsg = 0x46f7; // "Yeah, great idea. Let's take this rock and walk around a bit. Gee..." +// Butterfly Message : 0x473d to 0x477a +const uint16 dsAddr_butterflyMsg = 0x473d; // "I'd better leave them alone, they make this place beautiful" +// Not Sure If Alive Message : 0x477b to 0x4797 +const uint16 dsAddr_notSureIfAliveMsg = 0x477b; // "I'm not sure if it's alive" + +// FIXME - Unknown where this is used.. Talking to SOMETHING... +// Unknown Language Message : 0x4798 to 0x47be +const uint16 dsAddr_unknownLanguageMsg = 0x4798; // "I don't know what language it speaks" + +// Hole Too Narrow Message : 0x47bf to 0x47e6 +const uint16 dsAddr_holeTooNarrowMsg = 0x47bf; // "The hole is too narrow to fit my hand" +// Bird Attack Message : 0x47e7 to 0x4807 +const uint16 dsAddr_birdAttackMsg = 0x47e7; // "Hey You! Wake up! Bird attack!" +// No Search Warrant Message : 0x4808 to 0x4827 +const uint16 dsAddr_noSearchWarrantMsg = 0x4808; // "I don't have a search-warrant" +// Uninteresting Haystack Message : 0x4828 to 0x485f +const uint16 dsAddr_uninterestingHaystackMsg = 0x4828; // "I don't see anything interesting about this haystack" +// More Complicated Message : 0x4860 to 0x4881 +const uint16 dsAddr_moreComplicatedMsg = 0x4860; // "It's more complicated than that" +// Nut Rake Message : 0x4882 to 0x48be +const uint16 dsAddr_nutRakeMsg = 0x4882; // "It's pointless, the nut will slip between the rake's teeth" +// Paddle Broken Message : 0x48bf to 0x48d5 +const uint16 dsAddr_paddleBrokenMsg = 0x48bf; // "The paddle is BROKEN" +// Branch Not Paddle Message : 0x48d6 to 0x4912 +const uint16 dsAddr_branchNotPaddleMsg = 0x48d6; // "This branch is not a paddle. It doesn't even look like one" +// Try Somewhere Else Message : 0x4913 to 0x495b +const uint16 dsAddr_trySomewhereElseMsg = 0x4913; // "I'd better try somewhere else - I suppose this side is heavily guarded" +// Sharpen Not Pulverize Message : 0x495c to 0x4983 +const uint16 dsAddr_sharpenNotPulverizeMsg = 0x495c; // "I needed to sharpen it, not pulverize" +// Can't Do Anything Too Dark Message : 0x4984 to 0x49ad +const uint16 dsAddr_cantDoTooDarkMsg = 0x4984; // "I can't do anything here, it's too dark" +// Bribe Message : 0x49ae to 0x49d0 +const uint16 dsAddr_BribeMsg = 0x49ae; // "Here, let's make your pocket fat." +// Bank Note Message : 0x49d1 to 0x4a28 +const uint16 dsAddr_bankNoteMsg = 0x49d1; // "It's a note from some bank..." +// Show Her Money Message : 0x4a29 to 0x4a5a +const uint16 dsAddr_showHerMoneyMsg = 0x4a29; // "If I just show her the money, she might take it" +// Hundred Bucks Message : 0x4a5b to 0x4a6e +const uint16 dsAddr_hundredBucksMsg = 0x4a5b; // "A hundred bucks!!!" +// Want Blood Message : 0x4a6f to 0x4a7d +const uint16 dsAddr_wantBloodMsg = 0x4a6f; // "I want Blood!" +// Dont Leave Mansion Message : 0x4a7e to 0x4aaf +const uint16 dsAddr_dontLeaveMansionMsg = 0x4a7e; // "I don't want to leave the mansion, I want blood!" +// Wimp Message : 0x4ab0 to 0x4acc +const uint16 dsAddr_WimpMsg = 0x4ab0; // "I'm a pathetic little wimp" +// Strange Drawer Message : 0x4acd to 0x4b0c +const uint16 dsAddr_strangeDrawerMsg = 0x4acd; // "Strange, but the drawer is stuck if the next drawer is open" +// Not Ordinary Drawers Message : 0x4b0d to 0x4b38 +const uint16 dsAddr_notOrdinaryDrawersMsg = 0x4b0d; // "Maybe these are not just ordinary drawers!" +// Drawer Open Message : 0x4b39 to 0x4b6b +const uint16 dsAddr_drawerOpenMsg = 0x4b39; // "I cannot open the drawer if the next one is open!" +// Blue Interior Message 0x4b6c to 0x4b86 +const uint16 dsAddr_blueInteriorMsg = 0x4b6c; // "It's got a blue interior" +// Red Interior Message : 0x4b87 to 0x4ba0 +const uint16 dsAddr_redInteriorMsg = 0x4b87; // "It's got a red interior" +// Grey Interior Message : 0x4ba1 to 0x4bbb +const uint16 dsAddr_greyInteriorMsg = 0x4ba1; // "It's got a grey interior" +// Green Interior Message : 0x4bbc to 0x4bd7 +const uint16 dsAddr_greenInteriorMsg = 0x4bbc; // "It's got a green interior" +// Brown Interior Message : 0x4bd8 to 0x4bf3 +const uint16 dsAddr_brownInteriorMsg = 0x4bd8; // "It's got a brown interior" +// Pink Interior Message : 0x4bf4 to 0x4c0e +const uint16 dsAddr_pinkInteriorMsg = 0x4bf4; // "It's got a pink interior" +// Dictaphone Inside Message : 0x4c0f to 0x4c31 +const uint16 dsAddr_dictaphoneInsideMsg = 0x4c0f; // "Wow! There's a dictaphone inside!" +// Found Polaroid Message : 0x4c32 to 0x4c60 +const uint16 dsAddr_foundPolaroidMsg = 0x4c32; // "There's a polaroid inside! I might need that" +// Book Held Message : 0x4c61 to 0x4c83 +const uint16 dsAddr_bookHeldMsg = 0x4c61; // "Something's got hold of the book!" +// Secret Compartment Message : 0x4c84 to 0x4c9f +const uint16 dsAddr_secretCompartmentMsg = 0x4c84; // "Wow! A secret compartment!" +// Dont Mess Message : 0x4ca0 to 0x4cc6 +const uint16 dsAddr_dontMessMsg = 0x4ca0; // "I don't need to mess with it anymore" +// Full Automatic Message : 0x4cc7 to 0x4cd8 +const uint16 dsAddr_fullAutomaticMsg = 0x4cc7; // "Fully Automatic" +// No More Sheets Message : 0x4cd9 to 0x4d01 +const uint16 dsAddr_noMoreSheetsMsg = 0x4cd9; // "Right now I don't need any more sheets" +// No Deprave Message : 0x4d02 to 0x4d29 +const uint16 dsAddr_noDepraveMsg = 0x4d02; // "Nah, I don't want to deprave the kids" +// No Read Again Message : 0x4d2a to 0x4d5a +const uint16 dsAddr_noReadAgainMsg = 0x4d2a; // "I don't want to read it again. I might like it." +// TV Off Message : 0x4d5b to 0x4d7f +const uint16 dsAddr_tvOffMsg = 0x4d5b; // "I just realised that the TV is off" +// Not Happen Message : 0x4d80 to 0x4d92 +const uint16 dsAddr_NotHappenMsg = 0x4d80; // "Nothing happened" +// Tape Started Message : 0x4d93 to 0x4da5 +const uint16 dsAddr_tapeStartedMsg = 0x4d93; // "The tape started!" +// Much Better Message : 0x4da6 to 0x4dba +const uint16 dsAddr_muchBetterMsg = 0x4da6; // "That's much better" +// No Sleep Message : 0x4dbb to 0x4dd2 +const uint16 dsAddr_noSleepMsg = 0x4dbb; // "I don't want to sleep" +// Just Cork Message : 0x4dd3 to 0x4de5 +const uint16 dsAddr_justCorkMsg = 0x4dd3; // "It's just a cork" +// Enough Photos Message : 0x4de6 to 0x4e04 +const uint16 dsAddr_enoughPhotosMsg = 0x4de6; // "I don't need any more photos" +// Record Scare Message : 0x4e05 to 0x4e31 +const uint16 dsAddr_recordScareMsg = 0x4e05; // "Yeah, I can record this and scare the cats" +// Already Recorded Message : 0x4e32 to 0x4e57 +const uint16 dsAddr_alreadyRecordedMsg = 0x4e32; // "I already recorded what I wanted to" +// Can't Record No Batteries Message : 0x4e58 to 0x4e8d +const uint16 dsAddr_cantRecordNoBatteriesMsg = 0x4e58; // "I can't record anything until I find some batteries" + +// FIXME - Not sure how to get this message. Dictaphone with no batteries somewhere? Radio? +// No Batteries No Fun Message : 0x4e8e to 0x4ea4 +const uint16 dsAddr_NoBatteriesNoFunMsg = 0x4e8e; // "No batteries, no fun" + +// Not Right Moment Message : 0x4ea5 to 0x4ecd +const uint16 dsAddr_notRightMomentMsg = 0x4ea5; // "I don't think this is the right moment" +// Cook Around Message : 0x4ece to 0x4ef9 +const uint16 dsAddr_cookAroundMsg = 0x4ece; // "I can't do anything with this cook around" +// Same Bottle Message : 0x4efa to 0x4f3c +const uint16 dsAddr_sameBottleMsg = 0x4efa; // "The bottle's the same, but I doubt if it's enough to fool anyone" +// Break Flatten Message : 0x4f3d to 0x4f68 +const uint16 dsAddr_breakFlattenMsg = 0x4f3d; // "I wanted to break it, not to flatten it!" +// What Inside Message : 0x4f69 to 0x4f9a +const uint16 dsAddr_whatInsideMsg = 0x4f69; // "I was always curious what's inside these things" +// Rest Useless Message : 0x4f9b to 0x4fb0 +const uint16 dsAddr_restUselessMsg = 0x4f9b; // "The rest is useless" +// Two Batteries Message : 0x4fb1 to 0x4fca +const uint16 dsAddr_twoBatteriesMsg = 0x4fb1; // "Wow! Two 1.5V batteries!" +// One Taken Message : 0x4fcb to 0x4fe1 +const uint16 dsAddr_oneTakenMsg = 0x4fcb; // "This one's taken, OK?" +// Slight Mad Message : 0x4fe2 to 0x5009 +const uint16 dsAddr_slightMadMsg = 0x4fe2; // "It finally happened. I'm slightly mad" +// Paper Burnt Message : 0x500a to 0x502a +const uint16 dsAddr_paperBurntMsg = 0x500a; // "The paper burnt out completely!" +// Burn Baby Message : 0x502b to 0x503d +const uint16 dsAddr_burnBabyMsg = 0x502b; // "Burn, baby, burn!" +// Voila Message : 0x503e to 0x5045 +const uint16 dsAddr_voilaMsg = 0x503e; // "Voila" +// Too Hot Message : 0x5046 to 0x505d +const uint16 dsAddr_tooHotMsg = 0x5046; // "It's too hot to touch!" +// Frozen Shelf Message : 0x505e to 0x5081 +const uint16 dsAddr_frozenShelfMsg = 0x505e; // "It has frozen hard onto the shelf!" +// Yummy Message : 0x5082 to 0x5089 +const uint16 dsAddr_yummyMsg = 0x5082; // "Yummy" +// Dislike Veal Message : 0x508a to 0x50a5 +const uint16 dsAddr_dislikeVealMsg = 0x508a; // "I never liked veal anyway" +// No Reason Message : 0x50a6 to 0x50c2 +const uint16 dsAddr_noReasonMsg = 0x50a6; // "There's no reason to do it" +// Fooled Once Message : 0x50c3 to 0x50e0 +const uint16 dsAddr_fooledOnceMsg = 0x50c3; // "I'd already fooled him once" +// Mike Voice Test Message : 0x50e1 to 0x5100 +const uint16 dsAddr_mikeVoiceTestMsg = 0x50e1; // "Mike, activate the voice test" +// Not My Voice Message : 0x5101 to 0x5123 +const uint16 dsAddr_notMyVoiceMsg = 0x5101; // "I won't cheat Mike with MY voice" +// Singing Message : 0x5124 to 0x5137 +const uint16 dsAddr_singingMsg = 0x5124; // "siiiiinging!" +// Mike Scent Test Message : 0x5138 to 0x5160 +const uint16 dsAddr_mikeScentTestMsg = 0x5138; // "Mike, let's get on with the scent test" +// Mike View Test Message : 0x5161 to 0x517a +const uint16 dsAddr_mikeViewTestMsg = 0x5161; // "Mike, run the view test" +// Cutscene Message #0 : 0x517b to 0x51a6 +const uint16 dsAddr_cutsceneMsg0 = 0x517b; // "A secret diary of ..." +// Cant Hide Message : 0x51a7 to 0x51ba +const uint16 dsAddr_cantHideMsg = 0x51a7; // "I can't hide here!" +// John Outside Message : 0x51bb to 0x51e6 +const uint16 dsAddr_johnOutsideMsg = 0x51bb; // "There's John Noty outside! I can't go out!" +// Was Close Message : 0x51e7 to 0x51f7 +const uint16 dsAddr_wasCloseMsg = 0x51e7; // "That was close" +// Cork In Hole Message : 0x51f8 to 0x5217 +const uint16 dsAddr_corkInHoleMsg = 0x51f8; // "The cork is stuck in the hole" +// Fits Perfect Message : 0x5218 to 0x522b +const uint16 dsAddr_fitsPerfectMsg = 0x5218; // "It fits perfectly!" +// Enough Water Message : 0x522c to 0x524e +const uint16 dsAddr_enoughWaterMsg = 0x522c; // "There's enough water in the sink" +// No Hot Water Message : 0x524f to 0x5271 +const uint16 dsAddr_noHotWaterMsg = 0x524f; // "There's no hot water in the sink" +// Label Off Message : 0x5272 to 0x528a +const uint16 dsAddr_labelOffMsg = 0x5272; // "The label has come off!" +// Cork Too Small Message : 0x528b to 0x52a8 +const uint16 dsAddr_corkTooSmallMsg = 0x528b; // "The cork is a bit too small" +// Not Try Now Message : 0x52a9 to 0x52ca +const uint16 dsAddr_notTryNowMsg = 0x52a9; // "There's no need to try them now" +// No Salad Message : 0x52cb to 0x52f5 +const uint16 dsAddr_noSaladMsg = 0x52cb; // "I don't want to turn myself into a salad" +// Nah Message : 0x52f6 to 0x52fd +const uint16 dsAddr_nahMsg = 0x52f6; // "Nah" +// Vent First Message : 0x52fe to 0x5325 +const uint16 dsAddr_ventFirstMsg = 0x52fe; // "I'd better stop this ventilator first" +// Catch John First Message : 0x5326 to 0x5348 +const uint16 dsAddr_catchJohnFirstMsg = 0x5326; // "I'd better catch John Noty first" +// Only Chilli Message : 0x5349 to 0x5371 +const uint16 dsAddr_onlyChilliMsg = 0x5349; // "Good this red stuff is only a chilli" +// Water Hot Message : 0x5372 to 0x538c +const uint16 dsAddr_waterHotMsg = 0x5372; // "The water looks very hot" +// Sink Full Message : 0x538d to 0x53ac +const uint16 dsAddr_sinkFullMsg = 0x538d; // "The sink is full of hot water" +// No Sock Store Message : 0x53ad to 0x53dc +const uint16 dsAddr_noSockStoreMsg = 0x53ad; // "I don't have anything to store these socks in" +// Show Papers Message : 0x53dd to 0x53f1 +const uint16 dsAddr_showPapersMsg = 0x53dd; // "Here are my papers" +// Got Permission Message : 0x53f2 to 0x5410 +const uint16 dsAddr_gotPermissionMsg = 0x53f2; // "I already got the permission" +// Saving Fine Message : 0x5411 to 0x5462 +const uint16 dsAddr_SavingFineMsg = 0x5411; // "Saving is a very fine thing..." +// Love Captain Message : 0x5463 to 0x5474 +const uint16 dsAddr_loveCaptainMsg = 0x5463; // "I love captain" +// Soccer Rulz Message : 0x5475 to 0x5483 +const uint16 dsAddr_soccerRulzMsg = 0x5475; // "Soccer rulz" +// Tree Cut Message : 0x5484 to 0x54c3 +const uint16 dsAddr_treeCutMsg = 0x5484; // "Don't cut the trees..." +// Visa Accepted Message : 0x54c4 to 0x54d4 +const uint16 dsAddr_visaAcceptedMsg = 0x54c4; // "VISA Accepted" +// Other Graffiti Message : 0x54d5 to 0x54f6 +const uint16 dsAddr_otherGraffitiMsg = 0x54d5; // "The rest of graffiti is obscene" +// First Trial Message : 0x54f7 to 0x5510 +const uint16 dsAddr_firstTrialMsg = 0x54f7; // "Sir, I'm Mark. A rookie" +// Locked Message : 0x5511 to 0x551e +const uint16 dsAddr_lockedMsg = 0x5511; // "It's Locked!" +// Thanks Message : 0x551f to 0x5527 +const uint16 dsAddr_ThanksMsg = 0x551f; // "Thanks." +// Unknown Usage Message : 0x5528 to 0x555c +const uint16 dsAddr_unkUsageMsg = 0x5528; // "I don't have any idea what to do with it right now" +// Idea Message : 0x555d to 0x5576 +const uint16 dsAddr_ideaMsg = 0x555d; // "That gives me an idea" +// Check Works Message : 0x5577 to 0x5599 +const uint16 dsAddr_checkWorksMsg = 0x5577; // "Now I got to check if it works" +// Time To Call Message : 0x559a to 0x55bf +const uint16 dsAddr_timeToCallMsg = 0x559a; // "I think it is time to call captain" +// Meal Finished Message : 0x55c0 to 0x55da +const uint16 dsAddr_mealFinishedMsg = 0x55c0; // "Hey! I finished my meal." +// Bowl Welded Message : 0x55db to 0x55fe +const uint16 dsAddr_bowlWeldedMsg = 0x55db; // "Wow. He got welded to the bowl" +// Gotcha Message : 0x55ff to 0x5607 +const uint16 dsAddr_gotchaMsg = 0x55ff; // "Gotcha" +// No Pocket Message : 0x5608 to 0x5631 +const uint16 dsAddr_noPocketMsg = 0x5608; // "I don't want to touch his pockets again." +// Does Not Work Message : 0x5632 to 0x5645 +const uint16 dsAddr_doesNotWorkMsg = 0x5632; // "That doesn't work" +// Message : 0x5646 to 0x5655 +const uint16 dsAddr_fnMsg1 = 0x5646; // "Piece of cake" +// Message : 0x5656 to 0x5679 +const uint16 dsAddr_fnMsg2 = 0x5656; // "And how am I supposed to get back?" +// Message : 0x567a to 0x5681 +const uint16 dsAddr_fnMsg3 = 0x567a; // "Great" +// Message : 0x5682 to 0x5695 +const uint16 dsAddr_fnMsg4 = 0x5682; // "Oh, yeah, right" +// Pull Object Message #1 : 0x5696 to 0x56ab +const uint16 dsAddr_pullObjMsg1 = 0x5696; // "I can't pull it out" +// Dont Want To Touch Message : 0x56ac to 0x56d9 +const uint16 dsAddr_dontWantToTouchMsg = 0x56ac; // "I don't want to touch it - I might get hurt" +// Fence Blocks Message : 0x56da to 0x56f6 +const uint16 dsAddr_fenceBlocksMsg = 0x56da; // "The fence blocks the way" +// Not Want To Sleep Message : 0x56f7 to 0x570e +const uint16 dsAddr_notWantToSleepMsg = 0x56f7; // "I don't want to sleep" +// Pull Object Message #2 : 0x570f to 0x5721 +const uint16 dsAddr_pullObjMsg2 = 0x570f; // "I can't reach it" +// Hello Question Message : 0x5722 to 0x5729 +const uint16 dsAddr_helloQMsg = 0x5722; // "Hello?" +// Totally Addicted Message : 0x572a to 0x5741 +const uint16 dsAddr_totallyAddictedMsg = 0x572a; // "He's totally addicted" +// What About Message : 0x5742 to 0x5756 +const uint16 dsAddr_whatAboutMsg = 0x5742; // "What about a new" +// Hot Off Message : 0x5757 to 0x576f +const uint16 dsAddr_hotOffMsg = 0x5757; // "hot off the press" +// Full Color Message : 0x5770 to 0x5781 +const uint16 dsAddr_fullColorMsg = 0x5770; // "full-color" +// Special Edition Message : 0x5782 to 0x5798 +const uint16 dsAddr_specialEdMsg = 0x5782; // "special edition" +// Soldier News Message : 0x5799 to 0x57b1 +const uint16 dsAddr_soldierNewsMsg = 0x5799; // "of Soldier News?!" +// Pole Climb Done Message : 0x57b2 to 0x57bf +const uint16 dsAddr_poleClimbDoneMsg = 0x57b2; // "Never Again!" +// Vac Message : 0x57c0 to 0x57de +const uint16 dsAddr_vacMsg = 0x57c0; // "What am I? A vacuum cleaner?!" +// Cutscene Message #1 : 0x57df to 0x5809 +const uint16 dsAddr_cutsceneMsg1 = 0x57df; // "sixty seven rude words later" +// Cutscene Message #2 : 0x580a to 0x5826 +const uint16 dsAddr_cutsceneMsg2 = 0x580a; // "Meanwhile in the mansion" +// Now Open Message : 0x5827 to 0x5836 +const uint16 dsAddr_nowOpenMsg = 0x5827; // "Now it's open" +// Cmon Baby Message : 0x5837 to 0x5854 +const uint16 dsAddr_cmonBabyMsg = 0x5837; // "C'mon baby, it's all yours!" +// Talk Not Now Message : 0x5855 to 0x5882 +const uint16 dsAddr_talkNotNowMsg = 0x5855; // "I've got no reason to talk to him right now." +// Yeah Right Message : 0x5883 to 0x5893 +const uint16 dsAddr_yeahRightMsg = 0x5883; // "Yeah right!" +// Barman Too Close Message : 0x5894 to 0x58af +const uint16 dsAddr_barmanTooCloseMsg = 0x5894; // "The barman is too close" +// Yuck Message : 0x58b0 to 0x58b6 +const uint16 dsAddr_yuckMsg = 0x58b0; // "Yuck!" +// Prefer Water Message : 0x58b7 to 0x58c7 +const uint16 dsAddr_preferWaterMsg = 0x58b7; // "I prefer water" +// Too Weak To Climb Message : 0x58c8 to 0x58e2 +const uint16 dsAddr_tooWeakToClimbMsg = 0x58c8; // "I'm too weak to climb it" +// Spring Prick Message : 0x58e3 to 0x5904 +const uint16 dsAddr_springPrickMsg = 0x58e3; // "The springs would prick my back" +// Food Alive Message : 0x5905 to 0x592e +const uint16 dsAddr_foodAliveMsg = 0x5905; // "No, thanks. This food seems still alive" +// Door Closed Message : 0x592f to 0x5954 +const uint16 dsAddr_doorClosedMsg = 0x592f; // "The door is closed. What a surprise." +// Empty Message : 0x5955 to 0x5961 +const uint16 dsAddr_emptyMsg = 0x5955; // "It's Empty" +// Geography Class Message : 0x5962 to 0x599c +const uint16 dsAddr_geographyClassMsg = 0x5962; // "I should have paid more attention in geography classes." +// Don't Need Mess Message : 0x599d to 0x59b5 +const uint16 dsAddr_dontNeedMessMsg = 0x599d; // "I don't need this mess" +// Seen Softer Rocks Message : 0x59b6 to 0x59da +const uint16 dsAddr_seenSofterRocksMsg = 0x59b6; // "Thanks, but I've seen softer rocks" +// Too Blunt Message : 0x59db to 0x5a00 +const uint16 dsAddr_tooBluntMsg = 0x59db; // "They are too blunt to be of any use" +// Useless Models Message : 0x5a01 to 0x5a1f +const uint16 dsAddr_uselessModelsMsg = 0x5a01; // "What's the use of the models?" +// Barman Will Notice Message : 0x5a20 to 0x5a50 +const uint16 dsAddr_barmanWillNoticeMsg = 0x5a20; // "The barman will surely notice its disappearing" +// Too Much To Drink Message : 0x5a51 to 0x5a95 +const uint16 dsAddr_tooMuchToDrinkMsg = 0x5a51; // "It'd take too much time to drink it..." +// 0x5a96 to 0x5a97 : 2 extra null bytes (padding?) +// Not Thief Message : 0x5a98 to 0x5ac5 +const uint16 dsAddr_notThiefMsg = 0x5a98; // "I'm not a thief. And it's empty, by the way." +// Too Many To Search Message : 0x5ac6 to 0x5aec +const uint16 dsAddr_tooManyToSearchMsg = 0x5ac6; // "There are too many of them to search" +// Captain Would Not Fit Message : 0x5aed to 0x5b26 +const uint16 dsAddr_captainWouldNotFitMsg = 0x5aed; // "Captain surely wouldn't fit them. I must look elsewhere" +// Chickening Never Message : 0x5b27 to 0x5b3e +const uint16 dsAddr_chickenNeverMsg = 0x5b27; // "Chickening? Me? Never!" +// Can't Open It Message : 0x5b3f to 0x5b50 +const uint16 dsAddr_cantOpenItMsg = 0x5b3f; // "I can't open it" +// Don't Need Them Message : 0x5b51 to 0x5b64 +const uint16 dsAddr_dontNeedThemMsg = 0x5b51; // "I don't need them" +// Peeping Tom Message : 0x5b65 to 0x5b7f +const uint16 dsAddr_peepingTomMsg = 0x5b65; // "What am I? A Peeping Tom?" +// Big Pockets Message : 0x5b80 to 0x5baa +const uint16 dsAddr_bigPocketsMsg = 0x5b80; // "I have big pockets, but there are limits" +// Trouble With Stairs Message : 0x5bab to 0x5be6 +const uint16 dsAddr_troubleWithStairsMsg = 0x5bab; // "If I put it on I might have trouble walking up the stairs" +// 9 Lives To Read Message : 0x5be7 to 0x5c0a +const uint16 dsAddr_9LivesToReadMsg = 0x5be7; // "I'd need 9 lives to read them all" +// Thanks Not Tired Message : 0x5c0b to 0x5c25 +const uint16 dsAddr_thanksNotTiredMsg = 0x5c0b; // "Thanks, I'm not so tired" +// No Need To Turn On Message : 0x5c26 to 0x5c45 +const uint16 dsAddr_noNeedToTurnOnMsg = 0x5c26; // "There's no need to turn it on" +// Won't Bear Weight Message : 0x5c46 to 0x5c5f +const uint16 dsAddr_wontBearWeightMsg = 0x5c46; // "It won't bear my weight" +// Never Learnt Message : 0x5c60 to 0x5c81 +const uint16 dsAddr_neverLearntMsg = 0x5c60; // "I never learnt to how use one" +// So Sharp Message : 0x5c82 to 0x5cab +const uint16 dsAddr_soSharpMsg = 0x5c82; // "They're so sharp they'd rip my trousers!" +// Cognac Message : 0x5cac to 0x5cda +const uint16 dsAddr_cognacMsg = 0x5cac; // "Pfui! The cognac really didn't do any good" +// No Time For Pleasures Message : 0x5cdb to 0x5cfc +const uint16 dsAddr_noTimeForPleasuresMsg = 0x5cdb; // "I don't have time for pleasures" +// Not Socks With Bare Hands Message : 0x5cfd to 0x5d2b +const uint16 dsAddr_notSocksWithBareHandsMsg = 0x5cfd; // "I won't touch these socks with my bare hands!" +// Not Halloween Message : 0x5d2c to 0x5d40 +const uint16 dsAddr_notHalloweenMsg = 0x5d2c; // "It's not Halloween" +// Not Manual Message : 0x5d41 to 0x5d6d +const uint16 dsAddr_NotManualMsg = 0x5d41; // "It can't be controlled manually! I hate it!" +// Nothing To Play Message : 0x5d6e to 0x5d86 +const uint16 dsAddr_nothingToPlayMsg = 0x5d6e; // "I have nothing to play" +// Not Mine Message : 0x5d87 to 0x5da7 +const uint16 dsAddr_notMineMsg = 0x5d87; // "I can't take it. It's not mine." +// Hey What's The Matter Message : 0x5da8 to 0x5dc1 +const uint16 dsAddr_HeyWtmQMsg = 0x5da8; // "Hey! What's the matter?!" +// Its Open Message : 0x5dc2 to 0x5dcd +const uint16 dsAddr_ItsOpenMsg = 0x5dc2; // "It's Open!" +// Out Of Order Message : 0x5dce to 0x5de1 +const uint16 dsAddr_outOfOrderMsg = 0x5dce; // "It's out of order" +// Captain Watching Message : 0x5de2 to 0x5e0a +const uint16 dsAddr_captainWatchingMsg = 0x5de2; // "with captain watching? Better not" +// Blunt Sickle Message : 0x5e0b to 0x5e24 +const uint16 dsAddr_bluntSickleMsg = 0x5e0b; // "The sickle is too blunt" +// First Business Message : 0x5e25 to 0x5e53 +const uint16 dsAddr_firstBusinessMsg = 0x5e25; // "First I've got some business to take care of" +// No Digging Knife Message : 0x5e54 to 0x5e8e +const uint16 dsAddr_noDiggingKnifeMsg = 0x5e54; // "Digging it out with the knife could take a hundred years" + +// FIXME - Where is this message used?! Unused? +// No Mess On Table Message : 0x5e8f to 0x5ebd +const uint16 dsAddr_noMessOnTableMsg = 0x5e8f; // "I don't want to make more mess on this table" + +// Throw Crumbs To Bird Question Message : 0x5ebe to 0x5ee5 +const uint16 dsAddr_throwCrumbsToBirdQMsg = 0x5ebe; // "Should I throw the crumbs to the bird?" +// Don't Waste Crumbs Message : 0x5ee6 to 0x5f10 +const uint16 dsAddr_dontWasteCrumbs = 0x5ee6; // "I don't want to waste these tasty crumbs" +// Might Slip Fall In Message : 0x5f11 to 0x5f3b +const uint16 dsAddr_mightSlipFallInMsg = 0x5f11; // "Better not... I might slip and fall in..." +// Book Color Message Address Pointers : (6 * 2-byte) = 0x5f3c to 0x5f47 +const uint16 dsAddr_bookColorMsgPtr = 0x5f3c; +// Book Color Message #0 : 0x5f48 to 0x5f60 +const uint16 dsAddr_bookColorMsg0 = 0x5f48; // ""The history of blues"" +// Book Color Message #1 : 0x5f61 to 0x5f8f +const uint16 dsAddr_bookColorMsg1 = 0x5f61; // ""Manchester United, or the Red Devils story"" +// Book Color Message #2 : 0x5f90 to 0x5fb5 +const uint16 dsAddr_bookColorMsg2 = 0x5f90; // ""Greyhounds and other hunting dogs"" +// Book Color Message #3 : 0x5fb6 to 0x5fe6 +const uint16 dsAddr_bookColorMsg3 = 0x5fb6; // ""Greenhorn, or my adventures in the Wild West"" +// Book Color Message #4 : 0x5fe7 to 0x6008 +const uint16 dsAddr_bookColorMsg4 = 0x5fe7; // ""Charlie Brown and his company"" +// Book Color Message #5 : 0x6009 to 0x6034 +const uint16 dsAddr_bookColorMsg5 = 0x6009; // ""Pink Panther: an unauthorised biography"" + +// Mansion Intrusion Function Pointers : (5 * 2-byte) = 0x6035 to 0x603e +const uint16 dsAddr_MansionIntrusionFnPtr = 0x6035; + +// Save State Region : 0x6478 to 0xdbf1 +const uint16 dsAddr_saveState = 0x6478; +const uint16 saveStateSize = 0x777a; + +// Save Description String (24 bytes) : 0x6478 to 0x648f + +// Ego (Mark) position in scene : 0x64af to 0x64b2 +const uint16 dsAddr_egoX = 0x64af; // 2 bytes +const uint16 dsAddr_egoY = 0x64b1; // 2 bytes + +// Idle Animation List Table : 0x6540 to 0x???? +const uint16 dsAddr_idleAnimationListPtr = 0x6540; + +// Palette Effect Data : 0x6609 to 0x???? +const uint16 dsAddr_paletteEffectData = 0x6609; + +// Scene Fade Table (2 byte address * 42): 0x663e to 0x6691 +const uint16 dsAddr_sceneFadeTablePtr = 0x663e; + +// Scene Walkbox Table (2 byte LE address * 42) : 0x6746 to 0x6799 +const uint16 dsAddr_sceneWalkboxTablePtr = 0x6746; + +// Scene Zoom Table (2 byte address * 42) : 0x70f4 to 0x7147 +const uint16 dsAddr_sceneZoomTablePtr = 0x70f4; + +// Scene Object Table (2 byte address * 42) : 0x7254 to 0x72a7 +const uint16 dsAddr_sceneObjectTablePtr = 0x7254; + +// Scene Object Name : Sonny or whatever : 0x92e5 to 0x92f6 +const uint16 dsAddr_scnObjNameSonny = 0x92e5; // "Sonny or whatever" + +// Scene Object Name - Anne : 0x9820 to 0x9824 +const uint16 dsAddr_scnObjNameAnne = 0x9820; // "Anne" + +// Scene Object Name - Mike : 0xaa94 to 0xaa98 +const uint16 dsAddr_scnObjNameMike = 0xaa94; // "Mike" + +// Current Scene Id : 0xb4f3 +const uint16 dsAddr_currentScene = 0xb4f3; // 1 byte + +// Ons Animation Table (2 byte address * ??) : 0xb4f5 to 0x???? +const uint16 dsAddr_onsAnimationTablePtr = 0xb4f5; + +// Examine Object Callback Table (2 byte LE address * ??) : 0xb5ce to 0x???? +const uint16 dsAddr_objExamineCallbackTablePtr = 0xb5ce; + +// Use Object Callback Table (2 byte LE address * ??) : 0xb89c to 0x???? +const uint16 dsAddr_objUseCallbackTablePtr = 0xb89c; + +// Inventory Object Callback Table (3 byte (id, callbackAddr) * 7) : 0xbb72 to 0xbb86 +const uint16 dsAddr_objCallbackTablePtr = 0xbb72; +// invItemToolboxFull = csAddr_openFullToolbox +// invItemToolboxHalfEmpty = csAddr_openHalfEmptyToolbox +// invItemDiveEquipment = csAddr_useDivingEquipment +// invItemShovelAct2 = csAddr_digMansionWall +// 0xff = csAddr_tooDarkHere // TODO: No object has id 0xff - Callback Disabled? +// invItemBanknote = csAddr_examineBanknote +// invItemTimePills = csAddr_useTimePills + +// Scene Hotspots Table (2 byte LE address * ??) : 0xbb87 to 0x???? +const uint16 dsAddr_sceneHotspotsPtr = 0xbb87; + +// Inventory Object Combining Table (5 byte (id, id, new object id, msgAddr) * 34) : 0xc335 to 0xc3de +const uint16 dsAddr_objCombiningTablePtr = 0xc335; +// 3 byte null terminator for Combining table 0xc3df to 0xc3e1 + +// Object Combine Error Message : 0xc3e2 to 0xc41e +const uint16 dsAddr_objCombineErrorMsg = 0xc3e2; // "Using these two objects ..." + +// Inventory (item ids held by Ego) (1 byte * 24) : 0xc48d to 0xc4a4 +const uint16 dsAddr_inventory = 0xc48d; +// 0xc4a5 is null word alignment byte +// Inventory item data address table (2 bytes * 92) : 0xc4a6 to 0xc55d +const uint16 dsAddr_inventoryItemDataPtrTable = 0xc4a6; + +// Lans Animation Table (4 byte * ??) : 0xd89e to 0x???? +const uint16 dsAddr_lansAnimationTablePtr = 0xd89e; + +// Spoken With Mansion Guard Flag : 0xda96 +// FIXME - This is probably unecessary as although this location is set, it +// doesn't now appear to be read. +const uint16 dsAddr_spokenWithMansionGuardFlag = 0xda96; // 1 byte +// Have Not Spoken With Mansion Guard Flag : 0xda97 +// FIXME - This is probably unecessary as although this location is set, it +// doesn't now appear to be read. +const uint16 dsAddr_haveNotSpokenWithMansionGuardFlag = 0xda97; // 1 byte + +// Dialog Stack - Pleading with Mansion Guard : 0xdaa6 to 0xdab1 +const uint16 dsAddr_dialogStackPleadingToMansionGuard = 0xdaa6; +// Dialog Stack - Mansion Guard Drinking : 0xdab2 to 0xdab9 +// FIXME - Can't find where this is used... +const uint16 dsAddr_dialogStackMansionGuardDrinking = 0xdab2; +// Dialog Stack - Talking To Sonny : 0xdaba to 0xdac3 +const uint16 dsAddr_dialogStackSonny = 0xdaba; +// Dialog Stack - Talking To Grandpa : 0xdac4 to 0xdacd +const uint16 dsAddr_dialogStackGrandpa = 0xdac4; +// Cave Thorns Cut Down Flag : 0xdaca +// FIXME - Cave Thorns Flag overlap with dsAddr_dialogStackGrandpa. Bug or typo? +const uint16 dsAddr_caveThornsCutDownFlag = 0xdaca; // 1 byte +// Dialog Stack - Trying To Borrow Shotgun From Grandpa : 0xdace to 0xdad3 +const uint16 dsAddr_dialogStackGrandpaShotgun = 0xdace; +// Dialog Stack - Trying To Borrow Fan From Grandpa : 0xdad4 to 0xdad9 +const uint16 dsAddr_dialogStackGrandpaFan = 0xdad4; +// Dialog Stack - Ask Old Lady if OK : 0xdada to 0xdaef +const uint16 dsAddr_dialogStackAskOldLadyOK = 0xdada; +// Dialog Stack - Talking To Old Lady : 0xdaf0 to 0xdaf5 +const uint16 dsAddr_dialogStackOldLady = 0xdaf0; +// Dialog Stack - Borrow Duster From Old Lady : 0xdaf6 to 0xdafb +const uint16 dsAddr_dialogStackBorrowDusterFromOldLady = 0xdaf6; +// Dialog Stack - Get Old Lady's Apple : 0xdafc to 0xdb01 +const uint16 dsAddr_dialogStackGetAppleOldLady = 0xdafc; +// Dialog Stack - Giving Another Flower To Anne : 0xdb02 to 0xdb07 +const uint16 dsAddr_dialogStackAnotherFlowerToAnne = 0xdb02; +// Dialog Stack - Talking To Squirrel : 0xdb08 to 0xdb13 +const uint16 dsAddr_dialogStackSquirrel = 0xdb08; +// Dialog Stack - Talking To Dog : 0xdb14 to 0xdb1d +const uint16 dsAddr_dialogStackDog = 0xdb14; +// Dialog Stack - Take Axe : 0xdb1e to 0xdb23 +const uint16 dsAddr_dialogStackTakeAxe = 0xdb1e; +// Dialog Stack - Talking To Busy Cook : 0xdb24 to 0xdb2d +const uint16 dsAddr_dialogStackBusyCook = 0xdb24; +// Dialog Stack - Talking To Mike the Robot Safe : 0xdb2e to 0xdb35 +const uint16 dsAddr_dialogStackRobotSafe = 0xdb2e; +// Dialog Stack - Talking To John Noty At Endgame : 0xdb36 to 0xdb3f +const uint16 dsAddr_dialogStackJohnNotyEndgame = 0xdb36; +// Dialog Stack - Camp Guard Waiting For Documents : 0xdb40 to 0xdb4b +const uint16 dsAddr_dialogStackCampGuardWantsDocuments = 0xdb40; +// Dialog Stack - Camp Guard Reading Soldier News : 0xdb4c to 0xdb55 +const uint16 dsAddr_dialogStackCampGuardReadingNews = 0xdb4c; +// Dialog Stack - Camp Guard Show Pass : 0xdb56 to 0xdb5b +const uint16 dsAddr_dialogStackCampGuardShowPass = 0xdb56; +// Dialog Stack - Jail Door Grates : 0xdb5c to 0xdb67 +const uint16 dsAddr_dialogStackJailDoorGrates = 0xdb5c; +// Dialog Stack - Talking to Barman : 0xdb68 to 0xdb71 +const uint16 dsAddr_dialogStackBarman = 0xdb68; +// Dialog Stack - Fall Into Mudpool : 0xdb72 to 0xdb79 +const uint16 dsAddr_dialogStackFallIntoMudpool = 0xdb72; +// Dialog Stack - Talking To Mudpool Bird : 0xdb7a to 0xdb81 +const uint16 dsAddr_dialogStackMudpoolBird = 0xdb7a; +// Dialog Stack - Interrogate Captain : 0xdb82 to 0xdb89 +const uint16 dsAddr_dialogStackInterrogateCaptain = 0xdb82; +// Dialog Stack - Bar Cellar Door : 0xdb8a to 0xdb8f +const uint16 dsAddr_dialogStackBarCellarDoor = 0xdb8a; +// Current Music Id Playing : 0xdb90 +const uint16 dsAddr_currentMusic = 0xdb90; // 1 byte +// Unused Byte : 0xdb91 +// Already Adjusted Hoop Pole Flag : 0xdb92 +const uint16 dsAddr_alreadyAdjustedHoopPoleFlag = 0xdb92; // 1 byte +// Already Kicked Hen Flag : 0xdb93 +const uint16 dsAddr_alreadyKickedHenFlag = 0xdb93; // 1 byte +// Already Pulled Trunk Release Lever Flag : 0xdb94 +const uint16 dsAddr_alreadyPulledTrunkReleaseLeverFlag = 0xdb94; // 1 byte +// Car Trunk Empty Flag : 0xdb95 +const uint16 dsAddr_carTrunkEmptyFlag = 0xdb95; // 1 byte +// Birds Gone From Scarecrow Flag : 0xdb96 +const uint16 dsAddr_birdsGoneFromScarecrowFlag = 0xdb96; // 1 byte +// Already Spoken To Anne Flag : 0xdb97 +const uint16 dsAddr_alreadySpokenToAnneFlag = 0xdb97; // 1 byte +// Flower Isle in Lake State (0 = Both Flowers Present, 1 = One Flower Taken, 2+ = Both Flowers Taken): 0xdb98 +const uint16 dsAddr_flowerIsleState = 0xdb98; // 1 byte +// Already Got Broken Paddle Flag : 0xdb99 +const uint16 dsAddr_alreadyGotBrokenPaddleFlag = 0xdb99; // 1 byte +// Given Flower To OldLady Already Flag : 0xdb9a +const uint16 dsAddr_givenFlowerToOldLadyAlreadyFlag = 0xdb9a; // 1 byte +// Given Flower To Anne Already Flag : 0xdb9b +const uint16 dsAddr_givenFlowerToAnneAlreadyFlag = 0xdb9b; // 1 byte +// Scared Guard Already Flag : 0xdb9c +const uint16 dsAddr_scaredGuardAlreadyFlag = 0xdb9c; // 1 byte +// Got Needle Already Flag : 0xdb9d +const uint16 dsAddr_gotNeedleAlreadyFlag = 0xdb9d; // 1 byte +// Got Potato Already Flag : 0xdb9e +const uint16 dsAddr_gotPotatoAlreadyFlag = 0xdb9e; // 1 byte +// Bees Gone Flag : 0xdb9f +const uint16 dsAddr_beesGoneFlag = 0xdb9f; // 1 byte +// Mansion Already Been Through Tunnel Flag : 0xdba0 +const uint16 dsAddr_mansionTunnelDoneFlag = 0xdba0; // 1 byte +// Mansion Tree Hollow Empty Flag : 0xdba1 +const uint16 dsAddr_mansionTreeHollowEmptyFlag = 0xdba1; // 1 byte +// Climbed Mansion Tree Already Flag : 0xdba2 +const uint16 dsAddr_climbedMansionTreeAlreadyFlag = 0xdba2; // 1 byte +// Cellar Door Open Flag : 0xdba3 +const uint16 dsAddr_cellarDoorOpenFlag = 0xdba3; // 1 byte +// Cellar Light On Flag : 0xdba4 +const uint16 dsAddr_lightOnFlag = 0xdba4; // 1 byte +// Laundry State (0 = Wet on Line, 1 = Dry on Line, 2 = Not Present): 0xdba5 +const uint16 dsAddr_laundryState = 0xdba5; // 1 byte +// Lake Diving Exit Message (0 to 5+) : 0xdba6 +const uint16 dsAddr_lakeDivingExitMessage = 0xdba6; // 1 byte +// Searched Grandpa Drawers Flag : 0xdba7 +const uint16 dsAddr_SearchedGrandpaDrawersFlag = 0xdba7; // 1 byte +// Hankerchief in Mousehole Flag : 0xdba8 +const uint16 dsAddr_HankerchiefInMouseholeFlag = 0xdba8; // 1 byte +// Mouse Hole State : 0xdba9, 0 = Mouse Gone, 1 = Mouse Trapped, 2 = Mouse Success(?) +const uint16 dsAddr_mouseHoleState = 0xdba9; // 1 byte +// Mouse Nerve Message Said Flag : 0xdbaa +const uint16 dsAddr_mouseNerveMsgSaidFlag = 0xdbaa; // 1 byte +// Mouse Already Got Gold Nugget Flag : 0xdbab +const uint16 dsAddr_mouseGotGoldNuggetFlag = 0xdbab; // 1 byte +// Unused Byte : 0xdbac +// Dog Has Bone Flag : 0xdbad +const uint16 dsAddr_dogHasBoneFlag = 0xdbad; // 1 byte +// Ego Already Scared By Spider Flag : 0xdbae +const uint16 dsAddr_egoAlreadyScaredBySpiderFlag = 0xdbae; // 1 byte +// Already Said That Anne is Beautiful Flag : 0xdbaf +const uint16 dsAddr_alreadySaidAnneBeautifulFlag = 0xdbaf; // 1 byte +// Squirrel's Nut State (0 = Nut in Tree, 1 = Nut in Grass, 2 = Nut Found with Rake) : 0xdbb0 +const uint16 dsAddr_squirrelNutState = 0xdbb0; // 1 byte +// Nut Swapped For Apple in Fruit Bowl Flag : 0xdbb1 +const uint16 dsAddr_nutSwappedForAppleFlag = 0xdbb1; // 1 byte +// Spoken To Man In Well Flag : 0xdbb2 +const uint16 dsAddr_spokenToManInWellFlag = 0xdbb2; // 1 byte +// Spoken To Mirror Flag : 0xdbb3 +const uint16 dsAddr_spokenToMirrorFlag = 0xdbb3; // 1 byte +// Cellar Shelves Examine Count (0 to 2(clamped))) : 0xdbb4 +const uint16 dsAddr_cellarShelfExamineCount = 0xdbb4; // 1 byte +// Examined Bank Note Flag : 0xdbb5 +const uint16 dsAddr_examinedBanknoteFlag = 0xdbb5; // 1 byte +// VGA Artist Quip Already Said Flag : 0xdbb6 +const uint16 dsAddr_vgaArtistQuipAlreadySaidFlag = 0xdbb6; // 1 byte +// Mansion Desk Blue Drawer Open Flag : 0xdbb7 +const uint16 dsAddr_blueDrawerOpenFlag = 0xdbb7; // 1 byte +// Mansion Desk Red Drawer Open Flag : 0xdbb8 +const uint16 dsAddr_redDrawerOpenFlag = 0xdbb8; // 1 byte +// Mansion Desk Grey Drawer Open Flag : 0xdbb9 +const uint16 dsAddr_greyDrawerOpenFlag = 0xdbb9; // 1 byte +// Mansion Desk Green Drawer Open Flag : 0xdbba +const uint16 dsAddr_greenDrawerOpenFlag = 0xdbba; // 1 byte +// Mansion Desk Brown Drawer Open Flag : 0xdbbb +const uint16 dsAddr_brownDrawerOpenFlag = 0xdbbb; // 1 byte +// Mansion Desk Pink Drawer Open Flag : 0xdbbc +const uint16 dsAddr_pinkDrawerOpenFlag = 0xdbbc; // 1 byte +// Mansion Colored Drawer Puzzle Hint Message Given Flag : 0xdbbd +const uint16 dsAddr_drawerPuzzleHintGivenFlag = 0xdbbd; // 1 byte +// Mansion Colored Drawer Got Dictaphone Flag : 0xdbbe +const uint16 dsAddr_drawerGotDictaphoneFlag = 0xdbbe; // 1 byte +// Mansion Colored Drawer Got Polaroid Flag : 0xdbbf +const uint16 dsAddr_drawerGotPolaroidFlag = 0xdbbf; // 1 byte +// Mansion Colored Drawer Puzzle Book Message Flag : 0xdbc0 +const uint16 dsAddr_drawerPuzzleBookMessageFlag = 0xdbc0; // 1 byte +// Mansion Colored Drawer Puzzle - Random Book Color Value (0 = No Book, 1 to 6 = Books) : 0xdbc1 +const uint16 dsAddr_drawerPuzzleBookValue = 0xdbc1; // 1 byte +// Mansion Colored Drawer Puzzle Solved Flag : 0xdbc2 +const uint16 dsAddr_drawerPuzzleSolvedFlag = 0xdbc2; // 1 byte +// Mansion Trashcan Searched Flag : 0xdbc3 +const uint16 dsAddr_mansionTrashcanSearchedFlag = 0xdbc3; // 1 byte +// Mansion Read Newspaper Flag : 0xdbc4 +const uint16 dsAddr_mansionReadNewspaperFlag = 0xdbc4; // 1 byte +// Mansion TV On Flag : 0xdbc5 +const uint16 dsAddr_mansionTVOnFlag = 0xdbc5; // 1 byte +// Mansion VCR Playing Tape Flag : 0xdbc6 +const uint16 dsAddr_mansionVCRPlayingTapeFlag = 0xdbc6; // 1 byte +// Mansion VCR Played Tape Before Flag : 0xdbc7 +const uint16 dsAddr_mansionVCRPlayedTapeBeforeFlag = 0xdbc7; // 1 byte +// Mansion VCR Tape Loaded Flag : 0xdbc8 +const uint16 dsAddr_mansionVCRTapeLoadedFlag = 0xdbc8; // 1 byte +// Mansion Examined Couch Before Flag : 0xdbc9 +const uint16 dsAddr_mansionExaminedCouchBeforeFlag = 0xdbc9; // 1 byte +// Mansion Used Polaroid on TV Flag : 0xdbca +const uint16 dsAddr_usedPolaroidOnTVFlag = 0xdbca; // 1 byte +// Mansion Used Dictaphone on TV Flag : 0xdbcb +const uint16 dsAddr_usedDictaphoneOnTVFlag = 0xdbcb; // 1 byte +// Mansion Cook Gone Flag : 0xdbcc +const uint16 dsAddr_MansionCookGoneFlag = 0xdbcc; // 1 byte +// Mansion Radio Broken Flag : 0xdbcd +const uint16 dsAddr_MansionRadioBrokenFlag = 0xdbcd; // 1 byte +// Mansion Got Radio Batteries Flag : 0xdbce +const uint16 dsAddr_MansionGotRadioBatteriesFlag = 0xdbce; // 1 byte +// Mansion Have Opened Fridge Before Flag : 0xdbcf +const uint16 dsAddr_MansionHaveOpenedFridgeBeforeFlag = 0xdbcf; // 1 byte +// Mansion Put Burning Paper In Fridge Flag : 0xdbd0 +const uint16 dsAddr_MansionPutBurningPaperInFridgeFlag = 0xdbd0; // 1 byte +// Mansion Robot Safe Unlocked Flag : 0xdbd1 +const uint16 dsAddr_MansionRobotSafeUnlockedFlag = 0xdbd1; // 1 byte +// Mansion Robot Safe Voice Test Passed Flag : 0xdbd2 +const uint16 dsAddr_MansionRobotSafeVoiceTestPassedFlag = 0xdbd2; // 1 byte +// Mansion Robot Safe Scent Test Passed Flag : 0xdbd3 +const uint16 dsAddr_MansionRobotSafeScentTestPassedFlag = 0xdbd3; // 1 byte +// Mansion Robot Safe View Test Passed Flag : 0xdbd4 +const uint16 dsAddr_MansionRobotSafeViewTestPassedFlag = 0xdbd4; // 1 byte +// Mansion John Noty Outside Bathroom Flag : 0xdbd5 +const uint16 dsAddr_MansionJohnNotyOutsideBathroomFlag = 0xdbd5; // 1 byte +// Mansion Sink State (0 - No Plug, Sink Empty, 1 - Plug, Sink Empty, 2 - Plug, Sink Full) : 0xdbd6 +const uint16 dsAddr_MansionSinkState = 0xdbd6; // 1 byte +// Mansion Through Fan By Time Pill Flag : 0xdbd7 +const uint16 dsAddr_MansionThruFanByTimePillFlag = 0xdbd7; // 1 byte +// Mansion Ventilator Fan Stopped Flag : 0xdbd8 +const uint16 dsAddr_MansionVentFanStoppedFlag = 0xdbd8; // 1 byte +// Mansion John Noty Escaping Flag : 0xdbd9 +const uint16 dsAddr_MansionJohnNotyEscapingFlag = 0xdbd9; // 1 byte +// Shown Pass To Guard Flag : 0xdbda +const uint16 dsAddr_ShownPassToGuardFlag = 0xdbda; // 1 byte +// Graffiti Message Id (0 to 6) : 0xdbdb +const uint16 dsAddr_graffitiMsgId = 0xdbdb; // 1 byte +// Got Food Bowl in Jail Flag : 0xdbdc +const uint16 dsAddr_GotFoodBowlInJailFlag = 0xdbdc; // 1 byte +// Jail Cable and Bowl State (0 = Cable not in Bowl, 1 = Cable in Bowl, 2 = Bowl Electrified 3 = Captain Shocked) : 0xdbdd +const uint16 dsAddr_JailCableAndBowlState = 0xdbdd; // 1 byte +// Got Jail Key Flag : 0xdbde +const uint16 dsAddr_GotJailKeyFlag = 0xdbde; // 1 byte +// First Act Trial State (0 = Before First Trial, 1 to 3 = Trial 1st to 3rd) : 0xdbdf +const uint16 dsAddr_FirstActTrialState = 0xdbdf; // 1 byte +// Already Tickled Captain Flag : 0xdbe0 +const uint16 dsAddr_AlreadyTickledCaptainFlag = 0xdbe0; // 1 byte +// Cut Fence Flag : 0xdbe1 +const uint16 dsAddr_cutFenceFlag = 0xdbe1; // 1 byte +// Act 1 Guard State (0 = Normal, 1 = With Kaleidoscope & Grenade, 2 = Kaleidoscope & No Grenade) : 0xdbe2 +const uint16 dsAddr_act1GuardState = 0xdbe2; // 1 byte +// Spoken to Barman About Third Trial Flag : 0xdbe3 +const uint16 dsAddr_spokeToBarmanAboutThirdTrialFlag = 0xdbe3; // 1 byte +// Got Mug Of Mud Flag : 0xdbe4 +const uint16 dsAddr_gotMugOfMudFlag = 0xdbe4; // 1 byte +// Got Rope In Act 1 Flag : 0xdbe5 +const uint16 dsAddr_gotRopeAct1Flag = 0xdbe5; // 1 byte +// Captain Drawer State : 0xdbe6 +const uint16 dsAddr_captainDrawerState = 0xdbe6; // 1 byte +// Bird on Bar Radio Antenna Flag : 0xdbe7 +const uint16 dsAddr_birdOnBarRadioAntennaFlag = 0xdbe7; // 1 byte +// Swapped Barman Mug Flag : 0xdbe8 +const uint16 dsAddr_swappedBarmanMugFlag = 0xdbe8; // 1 byte +// Barman Passed Out Flag : 0xdbe9 +const uint16 dsAddr_barmanPassedOutFlag = 0xdbe9; // 1 byte +// Counter for Mansion Intrusion Attempts : 0xdbea +const uint16 dsAddr_mansionEntryCount = 0xdbea;// 1 byte +// Unused Byte : 0xdbeb +// John Noty Outside Mansion Door Flag : 0xdbec +const uint16 dsAddr_johnNotyOutsideMansionDoorFlag = 0xdbec; // 1 byte +// Unused Byte : 0xdbed +// Lovestruck By Anne Flag : 0xdbee +const uint16 dsAddr_lovestruckByAnneFlag = 0xdbee;// 1 byte +// Mansion Handle in Door Hole Flag : 0xdbef +const uint16 dsAddr_mansionHandleInDoorHoleFlag = 0xdbef;// 1 byte +// Got Password Need to Speak To Barman Flag : 0xdbf0 +const uint16 dsAddr_gotPasswordNeedSpeakBarmanFlag = 0xdbf0; // 1 byte +// Mansion Already Used Time Pills Flag : 0xdbf1 +const uint16 dsAddr_mansionAlreadyUsedTimePillsFlag = 0xdbf1; // 1 byte + +// Intro Credits #1 : 0xe3c2 to 0xe3e5 (Read Only) +const uint16 dsAddr_introCredits1 = 0xe3c2; // "backgrounds ..." +// Intro Credits #2 : 0xe3e6 to 0xe3fe (Read Only) +const uint16 dsAddr_introCredits2 = 0xe3e6; // "music ..." +// Intro Credits #3 : 0xe3ff to 0xe42e (Read Only) +const uint16 dsAddr_introCredits3 = 0xe3ff; // "animation..." +// Intro Credits #4 : 0xe42f to 0xe45b (Read Only) +const uint16 dsAddr_introCredits4 = 0xe42f; // "programming..." +// Credits #5 : 0xe45c to 0xe47b (Read Only) +const uint16 dsAddr_credits5 = 0xe45c; // "after the tiring journey..." +// Final Credits #6 : 0xe47c to 0xe487 (Read Only) +const uint16 dsAddr_finalCredits6 = 0xe47c; // "THE END..." +// Final Credits #7 : 0xe488 to 0xe782 (Read Only) +const uint16 dsAddr_finalCredits7 = 0xe488; // "programming..." +// 0xe783 to 0xe78f: 13 null bytes at end of dseg data - segment alignment padding? + class Resources { -protected: - Resources(); public: - static Resources *instance(); + Resources(); + ~Resources(); bool loadArchives(const ADGameDescription *gd); - void deinit(); + void loadOff(Graphics::Surface &surface, byte *palette, int id); Common::SeekableReadStream *loadLan(uint32 id) const; Common::SeekableReadStream *loadLan000(uint32 id) const; - //void loadOn(Graphics::Surface &surface, int id, uint16 &dst, uint16 *flags); - //void loadOns(Graphics::Surface &surface, int id, uint16 &dst); /* * PSP (as the other sony playstation consoles - to be confirmed and 'ifdef'ed here too) @@ -56,8 +1182,17 @@ public: FilePack off, on, ons, lan000, lan500, sam_mmm, sam_sam, mmm, voices; #endif - Segment cseg, dseg, eseg; + Segment dseg; Font font7, font8; + + //const byte *getDialog(uint16 dialogNum) { return eseg.ptr(dialogOffsets[dialogNum]); } + uint16 getDialogAddr(uint16 dialogNum) { return dialogOffsets[dialogNum]; } + + Segment eseg; +private: + void precomputeDialogOffsets(); + + Common::Array<uint16> dialogOffsets; }; } // End of namespace TeenAgent diff --git a/engines/teenagent/scene.cpp b/engines/teenagent/scene.cpp index 038c8ea05e..bdeb11a841 100644 --- a/engines/teenagent/scene.cpp +++ b/engines/teenagent/scene.cpp @@ -21,6 +21,7 @@ #include "common/config-manager.h" #include "common/debug.h" +#include "common/events.h" #include "common/algorithm.h" #include "common/ptr.h" #include "common/textconsole.h" @@ -28,24 +29,23 @@ #include "graphics/palette.h" #include "teenagent/scene.h" +#include "teenagent/inventory.h" #include "teenagent/resources.h" #include "teenagent/surface.h" #include "teenagent/objects.h" #include "teenagent/teenagent.h" -#include "teenagent/dialog.h" #include "teenagent/music.h" namespace TeenAgent { -Scene::Scene(TeenAgentEngine *engine, OSystem *system) : intro(false), _id(0), ons(0), - orientation(kActorRight), actor_talking(false), - message_timer(0), message_first_frame(0), message_last_frame(0), message_animation(NULL), - current_event(SceneEvent::kNone), hide_actor(false), callback(0), callback_timer(0), _idle_timer(0) { - _engine = engine; - _system = system; +Scene::Scene(TeenAgentEngine *vm) : _vm(vm), intro(false), _id(0), ons(0), + orientation(kActorRight), actorTalking(false), teenagent(vm), teenagentIdle(vm), + messageTimer(0), messageFirstFrame(0), messageLastFrame(0), messageAnimation(NULL), + currentEvent(SceneEvent::kNone), hideActor(false), callback(0), callbackTimer(0), _idleTimer(0) { - _fade_timer = 0; - on_enabled = true; + _fadeTimer = 0; + _fadeOld = 0; + onEnabled = true; memset(palette, 0, sizeof(palette)); background.pixels = 0; @@ -65,8 +65,8 @@ Scene::Scene(TeenAgentEngine *engine, OSystem *system) : intro(false), _id(0), o if (!s) error("invalid resource data"); - teenagent_idle.load(*s, Animation::kTypeVaria); - if (teenagent_idle.empty()) + teenagentIdle.load(*s, Animation::kTypeVaria); + if (teenagentIdle.empty()) error("invalid mark animation"); varia.close(); @@ -91,10 +91,10 @@ void Scene::warp(const Common::Point &_point, byte o) { bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Point &dst) const { const Common::Array<Walkbox> &scene_walkboxes = walkboxes[_id - 1]; - if (dst.x < 0 || dst.x > 319 || dst.y < 0 || dst.y > 199) + if (dst.x < 0 || dst.x >= kScreenWidth || dst.y < 0 || dst.y >= kScreenHeight) return false; - debug(1, "findPath %d,%d -> %d,%d", src.x, src.y, dst.x, dst.y); + debugC(1, kDebugScene, "findPath %d,%d -> %d,%d", src.x, src.y, dst.x, dst.y); p.clear(); p.push_back(src); p.push_back(dst); @@ -113,7 +113,7 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi break; const Common::Point &p1 = *i, &p2 = *next; - debug(1, "%d,%d -> %d,%d", p1.x, p1.y, p2.x, p2.y); + debugC(1, kDebugScene, "%d,%d -> %d,%d", p1.x, p1.y, p2.x, p2.y); Common::List<uint>::iterator wi; for (wi = boxes.begin(); wi != boxes.end(); ++wi) { @@ -124,14 +124,14 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi } w.dump(1); - debug(1, "%u: intersection mask 0x%04x, searching hints", *wi, mask); + debugC(1, kDebugScene, "%u: intersection mask 0x%04x, searching hints", *wi, mask); int dx = p2.x - p1.x, dy = p2.y - p1.y; if (dx >= 0) { - if ((mask & 8) != 0 && w.side_hint[3] != 0) { - debug(1, "hint left: %u", w.side_hint[3]); + if ((mask & 8) != 0 && w.sideHint[3] != 0) { + debugC(1, kDebugScene, "hint left: %u", w.sideHint[3]); Common::Point w1, w2; - w.rect.side(w1, w2, w.side_hint[3], p1); - debug(1, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); + w.rect.side(w1, w2, w.sideHint[3], p1); + debugC(1, kDebugScene, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); p.insert(next, w1); if (mask & 2) p.insert(next, w2); @@ -139,11 +139,11 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi break; } } else { - if ((mask & 2) != 0 && w.side_hint[1] != 0) { - debug(1, "hint right: %u", w.side_hint[1]); + if ((mask & 2) != 0 && w.sideHint[1] != 0) { + debugC(1, kDebugScene, "hint right: %u", w.sideHint[1]); Common::Point w1, w2; - w.rect.side(w1, w2, w.side_hint[1], p1); - debug(1, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); + w.rect.side(w1, w2, w.sideHint[1], p1); + debugC(1, kDebugScene, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); p.insert(next, w1); if (mask & 8) p.insert(next, w2); @@ -153,11 +153,11 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi } if (dy >= 0) { - if ((mask & 1) != 0 && w.side_hint[0] != 0) { - debug(1, "hint top: %u", w.side_hint[0]); + if ((mask & 1) != 0 && w.sideHint[0] != 0) { + debugC(1, kDebugScene, "hint top: %u", w.sideHint[0]); Common::Point w1, w2; - w.rect.side(w1, w2, w.side_hint[0], p1); - debug(1, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); + w.rect.side(w1, w2, w.sideHint[0], p1); + debugC(1, kDebugScene, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); p.insert(next, w1); if (mask & 4) p.insert(next, w2); @@ -165,11 +165,11 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi break; } } else { - if ((mask & 4) != 0 && w.side_hint[2] != 0) { - debug(1, "hint bottom: %u", w.side_hint[2]); + if ((mask & 4) != 0 && w.sideHint[2] != 0) { + debugC(1, kDebugScene, "hint bottom: %u", w.sideHint[2]); Common::Point w1, w2; - w.rect.side(w1, w2, w.side_hint[2], p1); - debug(1, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); + w.rect.side(w1, w2, w.sideHint[2], p1); + debugC(1, kDebugScene, "hint: %d,%d-%d,%d", w1.x, w1.y, w2.x, w2.y); p.insert(next, w1); if (mask & 1) p.insert(next, w2); @@ -187,13 +187,13 @@ bool Scene::findPath(Scene::Path &p, const Common::Point &src, const Common::Poi void Scene::moveTo(const Common::Point &_point, byte orient, bool validate) { Common::Point point(_point); - debug(0, "moveTo(%d, %d, %u)", point.x, point.y, orient); + debugC(0, kDebugScene, "moveTo(%d, %d, %u)", point.x, point.y, orient); const Common::Array<Walkbox> &scene_walkboxes = walkboxes[_id - 1]; for (byte i = 0; i < scene_walkboxes.size(); ++i) { const Walkbox &w = scene_walkboxes[i]; if (w.rect.in(point)) { - debug(0, "bumped into walkbox %u", i); + debugC(0, kDebugScene, "bumped into walkbox %u", i); w.dump(); byte o = w.orientation; switch (o) { @@ -229,7 +229,7 @@ void Scene::moveTo(const Common::Point &_point, byte orient, bool validate) { } if (!findPath(path, position, point)) { - _engine->cancel(); + _vm->cancel(); return; } @@ -237,55 +237,53 @@ void Scene::moveTo(const Common::Point &_point, byte orient, bool validate) { } void Scene::loadObjectData() { - Resources *res = Resources::instance(); - //loading objects & walkboxes objects.resize(42); walkboxes.resize(42); fades.resize(42); for (byte i = 0; i < 42; ++i) { - Common::Array<Object> &scene_objects = objects[i]; - scene_objects.clear(); + Common::Array<Object> &sceneObjects = objects[i]; + sceneObjects.clear(); - uint16 scene_table = res->dseg.get_word(0x7254 + i * 2); - uint16 object_addr; - while ((object_addr = res->dseg.get_word(scene_table)) != 0) { + uint16 sceneTable = _vm->res->dseg.get_word(dsAddr_sceneObjectTablePtr + (i * 2)); + uint16 objectAddr; + while ((objectAddr = _vm->res->dseg.get_word(sceneTable)) != 0) { Object obj; - obj.load(res->dseg.ptr(object_addr)); + obj.load(_vm->res->dseg.ptr(objectAddr)); //obj.dump(); - scene_objects.push_back(obj); - scene_table += 2; + sceneObjects.push_back(obj); + sceneTable += 2; } - debug(0, "scene[%u] has %u object(s)", i + 1, scene_objects.size()); + debugC(0, kDebugScene, "scene[%u] has %u object(s)", i + 1, sceneObjects.size()); - byte *walkboxes_base = res->dseg.ptr(READ_LE_UINT16(res->dseg.ptr(0x6746 + i * 2))); - byte walkboxes_n = *walkboxes_base++; - debug(0, "scene[%u] has %u walkboxes", i + 1, walkboxes_n); + byte *walkboxesBase = _vm->res->dseg.ptr(READ_LE_UINT16(_vm->res->dseg.ptr(dsAddr_sceneWalkboxTablePtr + i * 2))); + byte walkboxesCount = *walkboxesBase++; + debugC(0, kDebugScene, "scene[%u] has %u walkboxes", i + 1, walkboxesCount); - Common::Array<Walkbox> &scene_walkboxes = walkboxes[i]; - for (byte j = 0; j < walkboxes_n; ++j) { + Common::Array<Walkbox> &sceneWalkboxes = walkboxes[i]; + for (byte j = 0; j < walkboxesCount; ++j) { Walkbox w; - w.load(walkboxes_base + 14 * j); - if ((w.side_hint[0] | w.side_hint[1] | w.side_hint[2] | w.side_hint[3]) == 0) { - w.side_hint[0] = 2; - w.side_hint[1] = 3; - w.side_hint[2] = 4; - w.side_hint[3] = 1; + w.load(walkboxesBase + 14 * j); + if ((w.sideHint[0] | w.sideHint[1] | w.sideHint[2] | w.sideHint[3]) == 0) { + w.sideHint[0] = 2; + w.sideHint[1] = 3; + w.sideHint[2] = 4; + w.sideHint[3] = 1; } //walkbox[i]->dump(); - scene_walkboxes.push_back(w); + sceneWalkboxes.push_back(w); } - byte *fade_table = res->dseg.ptr(res->dseg.get_word(0x663e + i * 2)); - Common::Array<FadeType> &scene_fades = fades[i]; - while (READ_LE_UINT16(fade_table) != 0xffff) { + byte *fadeTable = _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_sceneFadeTablePtr + i * 2)); + Common::Array<FadeType> &sceneFades = fades[i]; + while (READ_LE_UINT16(fadeTable) != 0xffff) { FadeType fade; - fade.load(fade_table); - fade_table += 9; - scene_fades.push_back(fade); + fade.load(fadeTable); + fadeTable += 9; + sceneFades.push_back(fade); } - debug(0, "scene[%u] has %u fadeboxes", i + 1, scene_fades.size()); + debugC(0, kDebugScene, "scene[%u] has %u fadeboxes", i + 1, sceneFades.size()); } } @@ -293,10 +291,10 @@ Object *Scene::findObject(const Common::Point &point) { if (_id == 0) return NULL; - Common::Array<Object> &scene_objects = objects[_id - 1]; + Common::Array<Object> &sceneObjects = objects[_id - 1]; - for (uint i = 0; i < scene_objects.size(); ++i) { - Object &obj = scene_objects[i]; + for (uint i = 0; i < sceneObjects.size(); ++i) { + Object &obj = sceneObjects[i]; if (obj.enabled != 0 && obj.rect.in(point)) return &obj; } @@ -304,41 +302,38 @@ Object *Scene::findObject(const Common::Point &point) { } byte *Scene::getOns(int id) { - Resources *res = Resources::instance(); - return res->dseg.ptr(res->dseg.get_word(0xb4f5 + (id - 1) * 2)); + return _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_onsAnimationTablePtr + (id - 1) * 2)); } byte *Scene::getLans(int id) { - Resources *res = Resources::instance(); - return res->dseg.ptr(0xd89e + (id - 1) * 4); + return _vm->res->dseg.ptr(dsAddr_lansAnimationTablePtr + (id - 1) * 4); } void Scene::loadOns() { - debug(0, "loading ons animation"); - Resources *res = Resources::instance(); + debugC(0, kDebugScene, "loading ons animation"); - uint16 addr = res->dseg.get_word(0xb4f5 + (_id - 1) * 2); - //debug(0, "ons index: %04x", addr); + uint16 addr = _vm->res->dseg.get_word(dsAddr_onsAnimationTablePtr + (_id - 1) * 2); + debugC(0, kDebugScene, "ons index: %04x", addr); - ons_count = 0; + onsCount = 0; byte b; - byte on_id[16]; - while ((b = res->dseg.get_byte(addr)) != 0xff) { - debug(0, "on: %04x = %02x", addr, b); + byte onId[16]; + while ((b = _vm->res->dseg.get_byte(addr)) != 0xff) { + debugC(0, kDebugScene, "on: %04x = %02x", addr, b); ++addr; if (b == 0) continue; - on_id[ons_count++] = b; + onId[onsCount++] = b; } delete[] ons; ons = NULL; - if (ons_count > 0) { - ons = new Surface[ons_count]; - for (uint32 i = 0; i < ons_count; ++i) { - Common::ScopedPtr<Common::SeekableReadStream> s(res->ons.getStream(on_id[i])); + if (onsCount > 0) { + ons = new Surface[onsCount]; + for (uint32 i = 0; i < onsCount; ++i) { + Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->ons.getStream(onId[i])); if (s) { ons[i].load(*s, Surface::kTypeOns); } @@ -347,21 +342,20 @@ void Scene::loadOns() { } void Scene::loadLans() { - debug(0, "loading lans animation"); - Resources *res = Resources::instance(); - //load lan000 + debugC(0, kDebugScene, "loading lans animation"); + // load lan000 for (byte i = 0; i < 4; ++i) { animation[i].free(); - uint16 bx = 0xd89e + (_id - 1) * 4 + i; - byte bxv = res->dseg.get_byte(bx); - uint16 res_id = 4 * (_id - 1) + i + 1; - debug(0, "lan[%u]@%04x = %02x, resource id: %u", i, bx, bxv, res_id); + uint16 bx = dsAddr_lansAnimationTablePtr + (_id - 1) * 4 + i; + byte bxv = _vm->res->dseg.get_byte(bx); + uint16 resId = 4 * (_id - 1) + i + 1; + debugC(0, kDebugScene, "lan[%u]@%04x = %02x, resource id: %u", i, bx, bxv, resId); if (bxv == 0) continue; - Common::ScopedPtr<Common::SeekableReadStream> s(res->loadLan000(res_id)); + Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->loadLan000(resId)); if (s) { animation[i].load(*s, Animation::kTypeLan); if (bxv != 0 && bxv != 0xff) @@ -371,24 +365,23 @@ void Scene::loadLans() { } void Scene::init(int id, const Common::Point &pos) { - debug(0, "init(%d)", id); + debugC(0, kDebugScene, "init(%d)", id); _id = id; - on_enabled = true; //reset on-rendering flag on loading. + onEnabled = true; //reset on-rendering flag on loading. sounds.clear(); for (byte i = 0; i < 4; ++i) - custom_animation[i].free(); + customAnimation[i].free(); if (background.pixels == NULL) - background.create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); + background.create(kScreenWidth, kScreenHeight, Graphics::PixelFormat::createFormatCLUT8()); warp(pos); - Resources *res = Resources::instance(); - res->loadOff(background, palette, id); + _vm->res->loadOff(background, palette, id); if (id == 24) { - //dark scene - if (res->dseg.get_byte(0xDBA4) != 1) { - //dim down palette + // dark scene + if (_vm->res->dseg.get_byte(dsAddr_lightOnFlag) != 1) { + // dim down palette uint i; for (i = 0; i < 624; ++i) { palette[i] = palette[i] > 0x20 ? palette[i] - 0x20 : 0; @@ -399,62 +392,62 @@ void Scene::init(int id, const Common::Point &pos) { } } - Common::ScopedPtr<Common::SeekableReadStream> stream(res->on.getStream(id)); - int sub_hack = 0; - if (id == 7) { //something patched in the captains room - switch (res->dseg.get_byte(0xdbe6)) { + Common::ScopedPtr<Common::SeekableReadStream> stream(_vm->res->on.getStream(id)); + int subHack = 0; + if (id == 7) { // something patched in the captains room + switch (_vm->res->dseg.get_byte(dsAddr_captainDrawerState)) { case 2: break; case 1: - sub_hack = 1; + subHack = 1; break; default: - sub_hack = 2; + subHack = 2; } } - on.load(*stream, SurfaceList::kTypeOn, sub_hack); + on.load(*stream, subHack); loadOns(); loadLans(); - //check music - int now_playing = _engine->music->getId(); + // check music + int nowPlaying = _vm->music->getId(); - if (now_playing != res->dseg.get_byte(0xDB90)) - _engine->music->load(res->dseg.get_byte(0xDB90)); + if (nowPlaying != _vm->res->dseg.get_byte(dsAddr_currentMusic)) + _vm->music->load(_vm->res->dseg.get_byte(dsAddr_currentMusic)); - _system->copyRectToScreen(background.pixels, background.pitch, 0, 0, background.w, background.h); + _vm->_system->copyRectToScreen(background.pixels, background.pitch, 0, 0, background.w, background.h); setPalette(0); } void Scene::playAnimation(byte idx, uint id, bool loop, bool paused, bool ignore) { - debug(0, "playAnimation(%u, %u, loop:%s, paused:%s, ignore:%s)", idx, id, loop ? "true" : "false", paused ? "true" : "false", ignore ? "true" : "false"); + debugC(0, kDebugScene, "playAnimation(%u, %u, loop:%s, paused:%s, ignore:%s)", idx, id, loop ? "true" : "false", paused ? "true" : "false", ignore ? "true" : "false"); assert(idx < 4); - Common::ScopedPtr<Common::SeekableReadStream> s(Resources::instance()->loadLan(id + 1)); + Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->loadLan(id + 1)); if (!s) error("playing animation %u failed", id); - custom_animation[idx].load(*s); - custom_animation[idx].loop = loop; - custom_animation[idx].paused = paused; - custom_animation[idx].ignore = ignore; + customAnimation[idx].load(*s); + customAnimation[idx].loop = loop; + customAnimation[idx].paused = paused; + customAnimation[idx].ignore = ignore; } void Scene::playActorAnimation(uint id, bool loop, bool ignore) { - debug(0, "playActorAnimation(%u, loop:%s, ignore:%s)", id, loop ? "true" : "false", ignore ? "true" : "false"); - Common::ScopedPtr<Common::SeekableReadStream> s(Resources::instance()->loadLan(id + 1)); + debugC(0, kDebugScene, "playActorAnimation(%u, loop:%s, ignore:%s)", id, loop ? "true" : "false", ignore ? "true" : "false"); + Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->loadLan(id + 1)); if (!s) error("playing animation %u failed", id); - actor_animation.load(*s); - actor_animation.loop = loop; - actor_animation.ignore = ignore; - actor_animation.id = id; + actorAnimation.load(*s); + actorAnimation.loop = loop; + actorAnimation.ignore = ignore; + actorAnimation.id = id; } Animation *Scene::getAnimation(byte slot) { assert(slot < 4); - return custom_animation + slot; + return customAnimation + slot; } byte Scene::peekFlagEvent(uint16 addr) const { @@ -463,17 +456,17 @@ byte Scene::peekFlagEvent(uint16 addr) const { if (e.type == SceneEvent::kSetFlag && e.callback == addr) return e.color; } - return Resources::instance()->dseg.get_byte(addr); + return _vm->res->dseg.get_byte(addr); } void Scene::push(const SceneEvent &event) { - //debug(0, "push"); + debugC(0, kDebugScene, "push"); //event.dump(); if (event.type == SceneEvent::kWalk && !events.empty()) { SceneEvent &prev = events.back(); if (prev.type == SceneEvent::kWalk && prev.color == event.color) { - debug(0, "fixing double-move [skipping event!]"); - if ((event.color & 2) != 0) { //relative move + debugC(0, kDebugScene, "fixing double-move [skipping event!]"); + if ((event.color & 2) != 0) { // relative move prev.dst.x += event.dst.x; prev.dst.y += event.dst.y; } else { @@ -489,7 +482,7 @@ bool Scene::processEvent(const Common::Event &event) { switch (event.type) { case Common::EVENT_LBUTTONDOWN: case Common::EVENT_RBUTTONDOWN: - if (!message.empty() && message_first_frame == 0) { + if (!message.empty() && messageFirstFrame == 0) { clearMessage(); nextEvent(); return true; @@ -505,16 +498,16 @@ bool Scene::processEvent(const Common::Event &event) { clearMessage(); events.clear(); sounds.clear(); - current_event.clear(); - message_color = 0xd1; + currentEvent.clear(); + messageColor = textColorMark; for (int i = 0; i < 4; ++i) - custom_animation[i].free(); - _engine->playMusic(4); - _engine->loadScene(10, Common::Point(136, 153)); + customAnimation[i].free(); + _vm->playMusic(4); + _vm->loadScene(10, Common::Point(136, 153)); return true; } - if (!message.empty() && message_first_frame == 0) { + if (!message.empty() && messageFirstFrame == 0) { clearMessage(); nextEvent(); return true; @@ -534,8 +527,8 @@ bool Scene::processEvent(const Common::Event &event) { if (event.kbd.flags & Common::KBD_CTRL) { uint feature = event.kbd.keycode - '1'; if (feature < DebugFeatures::kMax) { - debug_features.feature[feature] = !debug_features.feature[feature]; - debug(0, "switched feature %u %s", feature, debug_features.feature[feature] ? "on" : "off"); + debugFeatures.feature[feature] = !debugFeatures.feature[feature]; + debugC(0, kDebugScene, "switched feature %u %s", feature, debugFeatures.feature[feature] ? "on" : "off"); } } break; @@ -556,25 +549,22 @@ struct ZOrderCmp { }; int Scene::lookupZoom(uint y) const { - Resources *res = Resources::instance(); - for (byte *zoom_table = res->dseg.ptr(res->dseg.get_word(0x70f4 + (_id - 1) * 2)); - zoom_table[0] != 0xff && zoom_table[1] != 0xff; - zoom_table += 2) { - //debug(0, "%d %d->%d", y, zoom_table[0], zoom_table[1]); - if (y <= zoom_table[0]) { - //debug(0, "%d %d->%d", y, zoom_table[0], zoom_table[1]); - return 256u * (100 - zoom_table[1]) / 100; + debugC(2, kDebugScene, "lookupZoom(%d)", y); + for (byte *zoomTable = _vm->res->dseg.ptr(_vm->res->dseg.get_word(dsAddr_sceneZoomTablePtr + (_id - 1) * 2)); + zoomTable[0] != 0xff && zoomTable[1] != 0xff; + zoomTable += 2) { + debugC(2, kDebugScene, "\t%d %d->%d", y, zoomTable[0], zoomTable[1]); + if (y <= zoomTable[0]) { + return 256u * (100 - zoomTable[1]) / 100; } } return 256; } - void Scene::paletteEffect(byte step) { - Resources *res = Resources::instance(); - byte *src = res->dseg.ptr(0x6609); - byte *dst = palette + 3 * 0xf2; - for (byte i = 0; i < 0xd; ++i) { + byte *src = _vm->res->dseg.ptr(dsAddr_paletteEffectData); + byte *dst = palette + (3 * 242); + for (byte i = 0; i < 13; ++i) { for (byte c = 0; c < 3; ++c, ++src) *dst++ = *src > step ? *src - step : 0; } @@ -584,9 +574,9 @@ byte Scene::findFade() const { if (_id <= 0) return 0; - const Common::Array<FadeType> &scene_fades = fades[_id - 1]; - for (uint i = 0; i < scene_fades.size(); ++i) { - const FadeType &fade = scene_fades[i]; + const Common::Array<FadeType> &sceneFades = fades[_id - 1]; + for (uint i = 0; i < sceneFades.size(); ++i) { + const FadeType &fade = sceneFades[i]; if (fade.rect.in(position)) { return fade.value; } @@ -594,110 +584,109 @@ byte Scene::findFade() const { return 0; } -bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { - Resources *res = Resources::instance(); +bool Scene::render(bool tickGame, bool tickMark, uint32 messageDelta) { bool busy; bool restart; - uint32 game_delta = tick_game ? 1 : 0; - uint32 mark_delta = tick_mark ? 1 : 0; + uint32 gameDelta = tickGame ? 1 : 0; + uint32 markDelta = tickMark ? 1 : 0; do { restart = false; busy = processEventQueue(); - if (_fade_timer && game_delta != 0) { - if (_fade_timer > 0) { - _fade_timer -= game_delta; - setPalette(_fade_timer); + if (_fadeTimer && gameDelta != 0) { + if (_fadeTimer > 0) { + _fadeTimer -= gameDelta; + setPalette(_fadeTimer); } else { - _fade_timer += game_delta; - setPalette(_fade_timer + 4); + _fadeTimer += gameDelta; + setPalette(_fadeTimer + 4); } } - switch (current_event.type) { + switch (currentEvent.type) { case SceneEvent::kCredits: { - _system->fillScreen(0); - ///\todo: optimize me - Graphics::Surface *surface = _system->lockScreen(); - res->font7.render(surface, current_event.dst.x, current_event.dst.y -= game_delta, current_event.message, current_event.color); - _system->unlockScreen(); - - if (current_event.dst.y < -(int)current_event.timer) - current_event.clear(); + _vm->_system->fillScreen(0); + // TODO: optimize me + Graphics::Surface *surface = _vm->_system->lockScreen(); + _vm->res->font7.render(surface, currentEvent.dst.x, currentEvent.dst.y -= gameDelta, currentEvent.message, currentEvent.color); + _vm->_system->unlockScreen(); + + if (currentEvent.dst.y < -(int)currentEvent.timer) + currentEvent.clear(); } return true; default: ; } - if (!message.empty() && message_timer != 0) { - if (message_timer <= delta) { + if (!message.empty() && messageTimer != 0) { + if (messageTimer <= messageDelta) { clearMessage(); nextEvent(); continue; } else - message_timer -= delta; + messageTimer -= messageDelta; } - if (current_event.type == SceneEvent::kCreditsMessage) { - _system->fillScreen(0); - Graphics::Surface *surface = _system->lockScreen(); - if (current_event.lan == 8) { - res->font8.shadow_color = current_event.orientation; - res->font8.render(surface, current_event.dst.x, current_event.dst.y, message, current_event.color); + if (currentEvent.type == SceneEvent::kCreditsMessage) { + _vm->_system->fillScreen(0); + Graphics::Surface *surface = _vm->_system->lockScreen(); + if (currentEvent.lan == 8) { + _vm->res->font8.setShadowColor(currentEvent.orientation); + _vm->res->font8.render(surface, currentEvent.dst.x, currentEvent.dst.y, message, currentEvent.color); } else { - res->font7.render(surface, current_event.dst.x, current_event.dst.y, message, 0xd1); + _vm->res->font7.render(surface, currentEvent.dst.x, currentEvent.dst.y, message, textColorCredits); } - _system->unlockScreen(); + _vm->_system->unlockScreen(); return true; } - if (background.pixels && debug_features.feature[DebugFeatures::kShowBack]) { - _system->copyRectToScreen(background.pixels, background.pitch, 0, 0, background.w, background.h); + if (background.pixels && debugFeatures.feature[DebugFeatures::kShowBack]) { + _vm->_system->copyRectToScreen(background.pixels, background.pitch, 0, 0, background.w, background.h); } else - _system->fillScreen(0); + _vm->_system->fillScreen(0); - Graphics::Surface *surface = _system->lockScreen(); + Graphics::Surface *surface = _vm->_system->lockScreen(); - bool got_any_animation = false; + bool gotAnyAnimation = false; - if (ons != NULL && debug_features.feature[DebugFeatures::kShowOns]) { - for (uint32 i = 0; i < ons_count; ++i) { + if (ons != NULL && debugFeatures.feature[DebugFeatures::kShowOns]) { + for (uint32 i = 0; i < onsCount; ++i) { Surface *s = ons + i; if (s != NULL) s->render(surface); } } - Common::List<Surface *> z_order; + Common::List<Surface *> zOrder; for (byte i = 0; i < 4; ++i) { - Animation *a = custom_animation + i; - Surface *s = a->currentFrame(game_delta); + Animation *a = customAnimation + i; + Surface *s = a->currentFrame(gameDelta); if (s != NULL) { if (!a->ignore) busy = true; if (!a->paused && !a->loop) - got_any_animation = true; + gotAnyAnimation = true; } else { a = animation + i; - if (!custom_animation[i].empty()) { - debug(0, "custom animation ended, restart animation in the same slot."); - custom_animation[i].free(); + if (!customAnimation[i].empty()) { + debugC(0, kDebugScene, "custom animation ended, restart animation in the same slot."); + customAnimation[i].free(); a->restart(); } - s = a->currentFrame(game_delta); + s = a->currentFrame(gameDelta); } - if (current_event.type == SceneEvent::kWaitLanAnimationFrame && current_event.slot == i) { + if (currentEvent.type == SceneEvent::kWaitLanAnimationFrame && currentEvent.slot == i) { if (s == NULL) { restart |= nextEvent(); continue; } int index = a->currentIndex(); - if (index == current_event.animation) { - debug(0, "kWaitLanAnimationFrame(%d, %d) complete", current_event.slot, current_event.animation); + if (index == currentEvent.animation) { + debugC(0, kDebugScene, "kWaitLanAnimationFrame(%d, %d) complete", currentEvent.slot, currentEvent.animation); restart |= nextEvent(); } } @@ -705,8 +694,8 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { if (s == NULL) continue; - if (debug_features.feature[DebugFeatures::kShowLan]) - z_order.push_back(s); + if (debugFeatures.feature[DebugFeatures::kShowLan]) + zOrder.push_back(s); if (a->id == 0) continue; @@ -722,38 +711,36 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { } } - Common::sort(z_order.begin(), z_order.end(), ZOrderCmp()); - Common::List<Surface *>::iterator z_order_it; + Common::sort(zOrder.begin(), zOrder.end(), ZOrderCmp()); + Common::List<Surface *>::iterator zOrderIter; - Surface *mark = actor_animation.currentFrame(game_delta); + Surface *mark = actorAnimation.currentFrame(gameDelta); int horizon = position.y; - for (z_order_it = z_order.begin(); z_order_it != z_order.end(); ++z_order_it) { - Surface *s = *z_order_it; + for (zOrderIter = zOrder.begin(); zOrderIter != zOrder.end(); ++zOrderIter) { + Surface *s = *zOrderIter; if (s->y + s->h > horizon) break; s->render(surface); } if (mark != NULL) { - actor_animation_position = mark->render(surface); - if (!actor_animation.ignore) + actorAnimationPosition = mark->render(surface); + if (!actorAnimation.ignore) busy = true; else busy = false; - got_any_animation = true; - } else if (!hide_actor) { - actor_animation.free(); + gotAnyAnimation = true; + } else if (!hideActor) { + actorAnimation.free(); uint zoom = lookupZoom(position.y); - { - byte fade = findFade(); - static byte old_fade = 0; - if (fade != old_fade) { - old_fade = fade; - paletteEffect(fade); - if (_fade_timer == 0) - setPalette(4); - } + + byte fade = findFade(); + if (fade != _fadeOld) { + _fadeOld = fade; + paletteEffect(fade); + if (_fadeTimer == 0) + setPalette(4); } if (!path.empty()) { @@ -767,31 +754,31 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { o = dp.y > 0 ? kActorDown : kActorUp; } - if (tick_mark) { - int speed_x = zoom / 32; //8 * zoom / 256 - int speed_y = (o == kActorDown || o == kActorUp ? 2 : 1) * zoom / 256; - if (speed_x == 0) - speed_x = 1; - if (speed_y == 0) - speed_y = 1; + if (tickMark) { + int speedX = zoom / 32; // 8 * zoom / 256 + int speedY = (o == kActorDown || o == kActorUp ? 2 : 1) * zoom / 256; + if (speedX == 0) + speedX = 1; + if (speedY == 0) + speedY = 1; - position.y += (ABS(dp.y) < speed_y ? dp.y : SIGN(dp.y) * speed_y); + position.y += (ABS(dp.y) < speedY ? dp.y : SIGN(dp.y) * speedY); position.x += (o == kActorDown || o == kActorUp) ? - (ABS(dp.x) < speed_y ? dp.x : SIGN(dp.x) * speed_y) : - (ABS(dp.x) < speed_x ? dp.x : SIGN(dp.x) * speed_x); + (ABS(dp.x) < speedY ? dp.x : SIGN(dp.x) * speedY) : + (ABS(dp.x) < speedX ? dp.x : SIGN(dp.x) * speedX); } - _idle_timer = 0; - teenagent_idle.resetIndex(); - actor_animation_position = teenagent.render(surface, position, o, mark_delta, false, zoom); + _idleTimer = 0; + teenagentIdle.resetIndex(); + actorAnimationPosition = teenagent.render(surface, position, o, markDelta, false, zoom); - if (tick_mark && position == destination) { + if (tickMark && position == destination) { path.pop_front(); if (path.empty()) { if (orientation == 0) - orientation = o; //save last orientation + orientation = o; // save last orientation nextEvent(); - got_any_animation = true; + gotAnyAnimation = true; restart = true; } busy = true; @@ -799,65 +786,63 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { busy = true; } else { teenagent.resetIndex(); - _idle_timer += mark_delta; - if (_idle_timer < 50) - actor_animation_position = teenagent.render(surface, position, orientation, 0, actor_talking, zoom); + _idleTimer += markDelta; + if (_idleTimer < 50) + actorAnimationPosition = teenagent.render(surface, position, orientation, 0, actorTalking, zoom); else - actor_animation_position = teenagent_idle.renderIdle(surface, position, orientation, mark_delta, zoom, _engine->_rnd); + actorAnimationPosition = teenagentIdle.renderIdle(surface, position, orientation, markDelta, zoom, _vm->_rnd); } } if (restart) { - _system->unlockScreen(); + _vm->_system->unlockScreen(); continue; } - //removed mark == null. In final scene of chapter 2 mark rendered above table. - //if it'd cause any bugs, add hack here. (_id != 23 && mark == NULL) - if (on_enabled && - debug_features.feature[DebugFeatures::kShowOn]) { - on.render(surface, actor_animation_position); - } + // removed mark == null. In final scene of chapter 2 mark rendered above table. + // if it'd cause any bugs, add hack here. (_id != 23 && mark == NULL) + if (onEnabled && debugFeatures.feature[DebugFeatures::kShowOn]) + on.render(surface, actorAnimationPosition); - for (; z_order_it != z_order.end(); ++z_order_it) { - Surface *s = *z_order_it; + for (; zOrderIter != zOrder.end(); ++zOrderIter) { + Surface *s = *zOrderIter; s->render(surface); } if (!message.empty()) { bool visible = true; - if (message_first_frame != 0 && message_animation != NULL) { - int index = message_animation->currentIndex() + 1; - //debug(0, "message: %s first: %u index: %u", message.c_str(), message_first_frame, index); - if (index < message_first_frame) + if (messageFirstFrame != 0 && messageAnimation != NULL) { + int index = messageAnimation->currentIndex() + 1; + debugC(0, kDebugScene, "message: %s first: %u index: %u", message.c_str(), messageFirstFrame, index); + if (index < messageFirstFrame) visible = false; - if (index > message_last_frame) { + if (index > messageLastFrame) { clearMessage(); visible = false; } } if (visible) { - res->font7.render(surface, message_pos.x, message_pos.y, message, message_color); + _vm->res->font7.render(surface, messagePos.x, messagePos.y, message, messageColor); busy = true; } } - if (!busy && !restart && tick_game && callback_timer) { - if (--callback_timer == 0) { - if (_engine->inventory->active()) - _engine->inventory->activate(false); - _engine->processCallback(callback); + if (!busy && !restart && tickGame && callbackTimer) { + if (--callbackTimer == 0) { + if (_vm->inventory->active()) + _vm->inventory->activate(false); + _vm->processCallback(callback); } - //debug(0, "callback timer = %u", callback_timer); + debugC(0, kDebugScene, "callback timer = %u", callbackTimer); } //if (!current_event.empty()) // current_event.dump(); - if (!debug_features.feature[DebugFeatures::kHidePath]) { - const Common::Array<Walkbox> & scene_walkboxes = walkboxes[_id - 1]; - for (uint i = 0; i < scene_walkboxes.size(); ++i) { - scene_walkboxes[i].rect.render(surface, 0xd0 + i); + if (!debugFeatures.feature[DebugFeatures::kHidePath]) { + const Common::Array<Walkbox> & sceneWalkboxes = walkboxes[_id - 1]; + for (uint i = 0; i < sceneWalkboxes.size(); ++i) { + sceneWalkboxes[i].rect.render(surface, 0xd0 + i); } Common::Point last_p = position; @@ -872,39 +857,39 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { } } - _system->unlockScreen(); + _vm->_system->unlockScreen(); - if (current_event.type == SceneEvent::kWait) { - if (current_event.timer > delta) { + if (currentEvent.type == SceneEvent::kWait) { + if (currentEvent.timer > messageDelta) { busy = true; - current_event.timer -= delta; + currentEvent.timer -= messageDelta; } - if (current_event.timer <= delta) + if (currentEvent.timer <= messageDelta) restart |= nextEvent(); } - if (!restart && current_event.type == SceneEvent::kWaitForAnimation && !got_any_animation) { - debug(0, "no animations, nextevent"); + if (!restart && currentEvent.type == SceneEvent::kWaitForAnimation && !gotAnyAnimation) { + debugC(0, kDebugScene, "no animations, nextevent"); nextEvent(); restart = true; } if (busy) { - _idle_timer = 0; - teenagent_idle.resetIndex(); + _idleTimer = 0; + teenagentIdle.resetIndex(); } } while (restart); for (Sounds::iterator i = sounds.begin(); i != sounds.end();) { Sound &sound = *i; if (sound.delay == 0) { - debug(1, "sound %u started", sound.id); - _engine->playSoundNow(sound.id); + debugC(1, kDebugScene, "sound %u started", sound.id); + _vm->playSoundNow(sound.id); i = sounds.erase(i); } else { - sound.delay -= game_delta; + sound.delay -= gameDelta; ++i; } } @@ -913,363 +898,364 @@ bool Scene::render(bool tick_game, bool tick_mark, uint32 delta) { } bool Scene::processEventQueue() { - while (!events.empty() && current_event.empty()) { - //debug(0, "processing next event"); - current_event = events.front(); + while (!events.empty() && currentEvent.empty()) { + debugC(0, kDebugScene, "processing next event"); + currentEvent = events.front(); events.pop_front(); - switch (current_event.type) { + switch (currentEvent.type) { case SceneEvent::kSetOn: { - byte on_id = current_event.ons; - if (on_id != 0) { - --on_id; - byte *ptr = getOns(current_event.scene == 0 ? _id : current_event.scene); - debug(0, "on[%u] = %02x", on_id, current_event.color); - ptr[on_id] = current_event.color; + byte onId = currentEvent.ons; + if (onId != 0) { + --onId; + byte *ptr = getOns(currentEvent.scene == 0 ? _id : currentEvent.scene); + debugC(0, kDebugScene, "on[%u] = %02x", onId, currentEvent.color); + ptr[onId] = currentEvent.color; } else { - on_enabled = current_event.color != 0; - debug(0, "%s on rendering", on_enabled ? "enabling" : "disabling"); + onEnabled = currentEvent.color != 0; + debugC(0, kDebugScene, "%s on rendering", onEnabled ? "enabling" : "disabling"); } loadOns(); - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kSetLan: { - if (current_event.lan != 0) { - debug(0, "lan[%u] = %02x", current_event.lan - 1, current_event.color); - byte *ptr = getLans(current_event.scene == 0 ? _id : current_event.scene); - ptr[current_event.lan - 1] = current_event.color; + if (currentEvent.lan != 0) { + debugC(0, kDebugScene, "lan[%u] = %02x", currentEvent.lan - 1, currentEvent.color); + byte *ptr = getLans(currentEvent.scene == 0 ? _id : currentEvent.scene); + ptr[currentEvent.lan - 1] = currentEvent.color; } loadLans(); - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kLoadScene: { - if (current_event.scene != 0) { - init(current_event.scene, current_event.dst); - if (current_event.orientation != 0) - orientation = current_event.orientation; + if (currentEvent.scene != 0) { + init(currentEvent.scene, currentEvent.dst); + if (currentEvent.orientation != 0) + orientation = currentEvent.orientation; } else { - //special case, empty scene + // special case, empty scene background.free(); on.free(); delete[] ons; ons = NULL; for (byte i = 0; i < 4; ++i) { animation[i].free(); - custom_animation[i].free(); + customAnimation[i].free(); } } - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kWalk: { - Common::Point dst = current_event.dst; - if ((current_event.color & 2) != 0) { //relative move + Common::Point dst = currentEvent.dst; + if ((currentEvent.color & 2) != 0) { // relative move dst.x += position.x; dst.y += position.y; } - if ((current_event.color & 1) != 0) { - warp(dst, current_event.orientation); - current_event.clear(); + if ((currentEvent.color & 1) != 0) { + warp(dst, currentEvent.orientation); + currentEvent.clear(); } else - moveTo(dst, current_event.orientation); + moveTo(dst, currentEvent.orientation); } break; case SceneEvent::kCreditsMessage: case SceneEvent::kMessage: { - message = current_event.message; - message_animation = NULL; - if (current_event.first_frame) { - message_timer = 0; - message_first_frame = current_event.first_frame; - message_last_frame = current_event.last_frame; - if (current_event.slot > 0) { - message_animation = custom_animation + (current_event.slot - 1); - //else if (!animation[current_event.slot].empty()) - // message_animation = animation + current_event.slot; + message = currentEvent.message; + messageAnimation = NULL; + if (currentEvent.firstFrame) { + messageTimer = 0; + messageFirstFrame = currentEvent.firstFrame; + messageLastFrame = currentEvent.lastFrame; + if (currentEvent.slot > 0) { + messageAnimation = customAnimation + (currentEvent.slot - 1); + //else if (!animation[currentEvent.slot].empty()) + // messageAnimation = animation + currentEvent.slot; } else - message_animation = &actor_animation; - debug(0, "async message %d-%d (slot %u)", message_first_frame, message_last_frame, current_event.slot); + messageAnimation = &actorAnimation; + debugC(0, kDebugScene, "async message %d-%d (slot %u)", messageFirstFrame, messageLastFrame, currentEvent.slot); } else { - message_timer = current_event.timer ? current_event.timer * 110 : messageDuration(message); - message_first_frame = message_last_frame = 0; + messageTimer = currentEvent.timer ? currentEvent.timer * 110 : messageDuration(message); + messageFirstFrame = messageLastFrame = 0; } Common::Point p; - if (current_event.dst.x == 0 && current_event.dst.y == 0) { - p = Common::Point((actor_animation_position.left + actor_animation_position.right) / 2, - actor_animation_position.top); + if (currentEvent.dst.x == 0 && currentEvent.dst.y == 0) { + p = Common::Point((actorAnimationPosition.left + actorAnimationPosition.right) / 2, + actorAnimationPosition.top); } else { - p = current_event.dst; + p = currentEvent.dst; } - byte message_slot = current_event.slot; - if (message_slot != 0) { - --message_slot; - assert(message_slot < 4); - const Surface *s = custom_animation[message_slot].currentFrame(0); + byte messageSlot = currentEvent.slot; + if (messageSlot != 0) { + --messageSlot; + assert(messageSlot < 4); + const Surface *s = customAnimation[messageSlot].currentFrame(0); if (s == NULL) - s = animation[message_slot].currentFrame(0); + s = animation[messageSlot].currentFrame(0); if (s != NULL) { p.x = s->x + s->w / 2; p.y = s->y; } else - warning("no animation in slot %u", message_slot); + warning("no animation in slot %u", messageSlot); } - message_pos = messagePosition(message, p); - message_color = current_event.color; + messagePos = messagePosition(message, p); + messageColor = currentEvent.color; - if (message_first_frame) - current_event.clear(); //async message, clearing event + if (messageFirstFrame) + currentEvent.clear(); // async message, clearing event } break; case SceneEvent::kPlayAnimation: { - byte slot = current_event.slot & 7; //0 - mark's - if (current_event.animation != 0) { - debug(0, "playing animation %u in slot %u(%02x)", current_event.animation, slot, current_event.slot); + byte slot = currentEvent.slot & 7; // 0 - mark's + if (currentEvent.animation != 0) { + debugC(0, kDebugScene, "playing animation %u in slot %u(%02x)", currentEvent.animation, slot, currentEvent.slot); if (slot != 0) { --slot; assert(slot < 4); - playAnimation(slot, current_event.animation, (current_event.slot & 0x80) != 0, (current_event.slot & 0x40) != 0, (current_event.slot & 0x20) != 0); + playAnimation(slot, currentEvent.animation, (currentEvent.slot & 0x80) != 0, (currentEvent.slot & 0x40) != 0, (currentEvent.slot & 0x20) != 0); } else - actor_talking = true; + actorTalking = true; } else { if (slot != 0) { --slot; - debug(0, "cancelling animation in slot %u", slot); + debugC(0, kDebugScene, "cancelling animation in slot %u", slot); assert(slot < 4); - custom_animation[slot].free(); + customAnimation[slot].free(); } else - actor_talking = true; + actorTalking = true; } - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kPauseAnimation: { - byte slot = current_event.slot & 7; //0 - mark's + byte slot = currentEvent.slot & 7; // 0 - mark's if (slot != 0) { --slot; - debug(1, "pause animation in slot %u", slot); - custom_animation[slot].paused = (current_event.slot & 0x80) != 0; + debugC(1, kDebugScene, "pause animation in slot %u", slot); + customAnimation[slot].paused = (currentEvent.slot & 0x80) != 0; } else { - actor_talking = false; + actorTalking = false; } - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kClearAnimations: for (byte i = 0; i < 4; ++i) - custom_animation[i].free(); - actor_talking = false; - current_event.clear(); + customAnimation[i].free(); + actorTalking = false; + currentEvent.clear(); break; case SceneEvent::kPlayActorAnimation: - debug(0, "playing actor animation %u", current_event.animation); - playActorAnimation(current_event.animation, (current_event.slot & 0x80) != 0, (current_event.slot & 0x20) != 0); - current_event.clear(); + debugC(0, kDebugScene, "playing actor animation %u", currentEvent.animation); + playActorAnimation(currentEvent.animation, (currentEvent.slot & 0x80) != 0, (currentEvent.slot & 0x20) != 0); + currentEvent.clear(); break; case SceneEvent::kPlayMusic: - debug(0, "setting music %u", current_event.music); - _engine->setMusic(current_event.music); - Resources::instance()->dseg.set_byte(0xDB90, current_event.music); - current_event.clear(); + debugC(0, kDebugScene, "setting music %u", currentEvent.music); + _vm->setMusic(currentEvent.music); + _vm->res->dseg.set_byte(dsAddr_currentMusic, currentEvent.music); + currentEvent.clear(); break; case SceneEvent::kPlaySound: - debug(0, "playing sound %u, delay: %u", current_event.sound, current_event.color); - sounds.push_back(Sound(current_event.sound, current_event.color)); - current_event.clear(); + debugC(0, kDebugScene, "playing sound %u, delay: %u", currentEvent.sound, currentEvent.color); + sounds.push_back(Sound(currentEvent.sound, currentEvent.color)); + currentEvent.clear(); break; case SceneEvent::kEnableObject: { - debug(0, "%s object #%u", current_event.color ? "enabling" : "disabling", current_event.object - 1); - Object *obj = getObject(current_event.object - 1, current_event.scene == 0 ? _id : current_event.scene); - obj->enabled = current_event.color; + debugC(0, kDebugScene, "%s object #%u", currentEvent.color ? "enabling" : "disabling", currentEvent.object - 1); + Object *obj = getObject(currentEvent.object - 1, currentEvent.scene == 0 ? _id : currentEvent.scene); + obj->enabled = currentEvent.color; obj->save(); - current_event.clear(); + currentEvent.clear(); } break; case SceneEvent::kHideActor: - hide_actor = current_event.color != 0; - current_event.clear(); + hideActor = currentEvent.color != 0; + currentEvent.clear(); break; case SceneEvent::kWaitForAnimation: - debug(0, "waiting for the animation"); + debugC(0, kDebugScene, "waiting for the animation"); break; case SceneEvent::kWaitLanAnimationFrame: - debug(0, "waiting for the frame %d in slot %d", current_event.animation, current_event.slot); + debugC(0, kDebugScene, "waiting for the frame %d in slot %d", currentEvent.animation, currentEvent.slot); break; case SceneEvent::kTimer: - callback = current_event.callback; - callback_timer = current_event.timer; - debug(0, "triggering callback %04x in %u frames", callback, callback_timer); - current_event.clear(); + callback = currentEvent.callback; + callbackTimer = currentEvent.timer; + debugC(0, kDebugScene, "triggering callback %04x in %u frames", callback, callbackTimer); + currentEvent.clear(); break; case SceneEvent::kEffect: - _system->delayMillis(80); //2 vsyncs - _system->setShakePos(8); - _system->updateScreen(); + _vm->_system->delayMillis(80); // 2 vsyncs + _vm->_system->setShakePos(8); + _vm->_system->updateScreen(); - _system->delayMillis(80); //2 vsyncs - _system->setShakePos(0); - _system->updateScreen(); + _vm->_system->delayMillis(80); // 2 vsyncs + _vm->_system->setShakePos(0); + _vm->_system->updateScreen(); - _system->delayMillis(80); //2 vsyncs - _system->setShakePos(4); - _system->updateScreen(); + _vm->_system->delayMillis(80); // 2 vsyncs + _vm->_system->setShakePos(4); + _vm->_system->updateScreen(); - _system->delayMillis(80); //2 vsyncs - _system->setShakePos(0); - _system->updateScreen(); + _vm->_system->delayMillis(80); // 2 vsyncs + _vm->_system->setShakePos(0); + _vm->_system->updateScreen(); - current_event.clear(); + currentEvent.clear(); break; case SceneEvent::kFade: - _fade_timer = current_event.orientation != 0 ? 5 : -5; - current_event.clear(); + _fadeTimer = currentEvent.orientation != 0 ? 5 : -5; + currentEvent.clear(); break; case SceneEvent::kWait: - debug(0, "wait %u", current_event.timer); + debugC(0, kDebugScene, "wait %u", currentEvent.timer); break; case SceneEvent::kCredits: - debug(0, "showing credits"); + debugC(0, kDebugScene, "showing credits"); break; case SceneEvent::kQuit: - debug(0, "quit!"); - _engine->quitGame(); + debugC(0, kDebugScene, "quit!"); + _vm->quitGame(); break; case SceneEvent::kSetFlag: - debug(0, "async set_flag(%04x, %d)", current_event.callback, current_event.color); - Resources::instance()->dseg.set_byte(current_event.callback, current_event.color); - current_event.clear(); + debugC(0, kDebugScene, "async set_flag(%04x, %d)", currentEvent.callback, currentEvent.color); + _vm->res->dseg.set_byte(currentEvent.callback, currentEvent.color); + currentEvent.clear(); break; default: - error("empty/unhandler event[%d]", (int)current_event.type); + error("empty/unhandler event[%d]", (int)currentEvent.type); } } + if (events.empty()) { - message_color = 0xd1; - hide_actor = false; + messageColor = textColorMark; + hideActor = false; } - return !current_event.empty(); + + return !currentEvent.empty(); } void Scene::setPalette(unsigned mul) { - //debug(0, "setPalette(%u)", mul); + debugC(0, kDebugScene, "setPalette(%u)", mul); byte p[3 * 256]; for (int i = 0; i < 3 * 256; ++i) { p[i] = (unsigned)palette[i] * mul; } - _system->getPaletteManager()->setPalette(p, 0, 256); + _vm->_system->getPaletteManager()->setPalette(p, 0, 256); } -Object *Scene::getObject(int id, int scene_id) { +Object *Scene::getObject(int id, int sceneId) { assert(id > 0); - if (scene_id == 0) - scene_id = _id; + if (sceneId == 0) + sceneId = _id; - if (scene_id == 0) + if (sceneId == 0) return NULL; - Common::Array<Object> &scene_objects = objects[scene_id - 1]; + Common::Array<Object> &sceneObjects = objects[sceneId - 1]; --id; - if (id >= (int)scene_objects.size()) + if (id >= (int)sceneObjects.size()) return NULL; - return &scene_objects[id]; + return &sceneObjects[id]; } -Common::Point Scene::messagePosition(const Common::String &str, Common::Point message_position) { - Resources *res = Resources::instance(); +Common::Point Scene::messagePosition(const Common::String &str, Common::Point pos) { int lines = 1; for (uint i = 0; i < str.size(); ++i) if (str[i] == '\n') ++lines; - uint w = res->font7.render(NULL, 0, 0, str, 0); - uint h = res->font7.height * lines + 3; + uint w = _vm->res->font7.render(NULL, 0, 0, str, 0); + uint h = _vm->res->font7.getHeight() * lines + 3; - message_position.x -= w / 2; - message_position.y -= h; + pos.x -= w / 2; + pos.y -= h; - if (message_position.x + w > 320) - message_position.x = 320 - w; - if (message_position.x < 0) - message_position.x = 0; - if (message_position.y + h > 320) - message_position.y = 200 - h; - if (message_position.y < 0) - message_position.y = 0; + if (pos.x + w > kScreenWidth) + pos.x = kScreenWidth - w; + if (pos.x < 0) + pos.x = 0; + if (pos.y + h > kScreenHeight) + pos.y = kScreenHeight - h; + if (pos.y < 0) + pos.y = 0; - return message_position; + return pos; } uint Scene::messageDuration(const Common::String &str) { - //original game uses static delays: 100-slow, 50, 20 and 1 tick - crazy speed. - //total delay = total message length * delay / 8 + 60. - uint total_width = str.size(); + // original game uses static delays: 100-slow, 50, 20 and 1 tick - crazy speed. + // total delay = total message length * delay / 8 + 60. + uint totalWidth = str.size(); - int speed = Common::ConfigManager::instance().getInt("talkspeed"); + int speed = ConfMan.getInt("talkspeed"); if (speed < 0) speed = 60; - uint delay_delta = 1 + (255 - speed) * 99 / 255; + uint delayDelta = 1 + (255 - speed) * 99 / 255; - uint delay = 60 + (total_width * delay_delta) / 8; - //debug(0, "delay = %u, delta: %u", delay, delay_delta); + uint delay = 60 + (totalWidth * delayDelta) / 8; + debugC(0, kDebugScene, "delay = %u, delta: %u", delay, delayDelta); return delay * 10; } void Scene::displayMessage(const Common::String &str, byte color, const Common::Point &pos) { //assert(!str.empty()); - //debug(0, "displayMessage: %s", str.c_str()); + debugC(0, kDebugScene, "displayMessage: %s", str.c_str()); message = str; - message_pos = (pos.x | pos.y) ? pos : messagePosition(str, position); - message_color = color; - message_timer = messageDuration(message); + messagePos = (pos.x | pos.y) ? pos : messagePosition(str, position); + messageColor = color; + messageTimer = messageDuration(message); } void Scene::clear() { clearMessage(); events.clear(); - current_event.clear(); + currentEvent.clear(); for (int i = 0; i < 4; ++i) { animation[i].free(); - custom_animation[i].free(); + customAnimation[i].free(); } callback = 0; - callback_timer = 0; + callbackTimer = 0; } void Scene::clearMessage() { message.clear(); - message_timer = 0; - message_color = 0xd1; - message_first_frame = 0; - message_last_frame = 0; - message_animation = NULL; + messageTimer = 0; + messageColor = textColorMark; + messageFirstFrame = 0; + messageLastFrame = 0; + messageAnimation = NULL; } } // End of namespace TeenAgent diff --git a/engines/teenagent/scene.h b/engines/teenagent/scene.h index 32e784bb60..14aefa0cca 100644 --- a/engines/teenagent/scene.h +++ b/engines/teenagent/scene.h @@ -27,8 +27,8 @@ #include "teenagent/objects.h" #include "teenagent/surface.h" #include "teenagent/surface_list.h" +#include "teenagent/teenagent.h" -#include "common/system.h" #include "common/array.h" #include "common/list.h" @@ -39,7 +39,6 @@ struct Event; namespace TeenAgent { class TeenAgentEngine; -class Dialog; struct SceneEvent { enum Type { @@ -84,22 +83,22 @@ struct SceneEvent { byte lan; union { byte music; - byte first_frame; + byte firstFrame; }; union { byte sound; - byte last_frame; + byte lastFrame; }; byte object; SceneEvent(Type type_) : - type(type_), message(), color(0xd1), slot(0), animation(0), timer(0), orientation(0), dst(), + type(type_), message(), color(textColorMark), slot(0), animation(0), timer(0), orientation(0), dst(), scene(0), ons(0), lan(0), music(0), sound(0), object(0) {} void clear() { type = kNone; message.clear(); - color = 0xd1; + color = textColorMark; slot = 0; orientation = 0; animation = 0; @@ -118,7 +117,7 @@ struct SceneEvent { } void dump() const { - debug(0, "event[%d]: \"%s\"[%02x], slot: %d, animation: %u, timer: %u, dst: (%d, %d) [%u], scene: %u, ons: %u, lan: %u, object: %u, music: %u, sound: %u", + debugC(0, kDebugScene, "event[%d]: \"%s\"[%02x], slot: %d, animation: %u, timer: %u, dst: (%d, %d) [%u], scene: %u, ons: %u, lan: %u, object: %u, music: %u, sound: %u", (int)type, message.c_str(), color, slot, animation, timer, dst.x, dst.y, orientation, scene, ons, lan, object, music, sound ); } @@ -126,13 +125,13 @@ struct SceneEvent { class Scene { public: - Scene(TeenAgentEngine *engine, OSystem *system); + Scene(TeenAgentEngine *engine); ~Scene(); bool intro; void init(int id, const Common::Point &pos); - bool render(bool tick_game, bool tick_mark, uint32 message_delta); + bool render(bool tickGame, bool tickMark, uint32 messageDelta); int getId() const { return _id; } void warp(const Common::Point &point, byte orientation = 0); @@ -140,7 +139,7 @@ public: void moveTo(const Common::Point &point, byte orientation = 0, bool validate = false); Common::Point getPosition() const { return position; } - void displayMessage(const Common::String &str, byte color = 0xd1, const Common::Point &pos = Common::Point()); + void displayMessage(const Common::String &str, byte color = textColorMark, const Common::Point &pos = Common::Point()); void setOrientation(uint8 o) { orientation = o; } void push(const SceneEvent &event); byte peekFlagEvent(uint16 addr) const; @@ -153,15 +152,15 @@ public: byte *getOns(int id); byte *getLans(int id); - bool eventRunning() const { return !current_event.empty(); } + bool eventRunning() const { return !currentEvent.empty(); } Walkbox *getWalkbox(byte id) { return &walkboxes[_id - 1][id]; } - Object *getObject(int id, int scene_id = 0); + Object *getObject(int id, int sceneId = 0); Object *findObject(const Common::Point &point); void loadObjectData(); Animation *getAnimation(byte slot); - inline Animation *getActorAnimation() { return &actor_animation; } + inline Animation *getActorAnimation() { return &actorAnimation; } inline const Common::String &getMessage() const { return message; } void setPalette(unsigned mul); int lookupZoom(uint y) const; @@ -173,39 +172,38 @@ private: void playAnimation(byte idx, uint id, bool loop, bool paused, bool ignore); void playActorAnimation(uint id, bool loop, bool ignore); - byte palette[768]; + byte palette[3 * 256]; void paletteEffect(byte step); byte findFade() const; - static Common::Point messagePosition(const Common::String &str, Common::Point position); - static uint messageDuration(const Common::String &str); + Common::Point messagePosition(const Common::String &str, Common::Point pos); + uint messageDuration(const Common::String &str); bool processEventQueue(); inline bool nextEvent() { - current_event.clear(); + currentEvent.clear(); return processEventQueue(); } void clearMessage(); - TeenAgentEngine *_engine; - OSystem *_system; + TeenAgentEngine *_vm; int _id; Graphics::Surface background; SurfaceList on; - bool on_enabled; + bool onEnabled; Surface *ons; - uint32 ons_count; - Animation actor_animation, animation[4], custom_animation[4]; - Common::Rect actor_animation_position, animation_position[4]; + uint32 onsCount; + Animation actorAnimation, animation[4], customAnimation[4]; + Common::Rect actorAnimationPosition, animationPosition[4]; - Actor teenagent, teenagent_idle; + Actor teenagent, teenagentIdle; Common::Point position; typedef Common::List<Common::Point> Path; Path path; uint8 orientation; - bool actor_talking; + bool actorTalking; bool findPath(Path &p, const Common::Point &src, const Common::Point &dst) const; @@ -214,22 +212,24 @@ private: Common::Array<Common::Array<FadeType> > fades; Common::String message; - Common::Point message_pos; - byte message_color; - uint message_timer; - byte message_first_frame; - byte message_last_frame; - Animation *message_animation; + Common::Point messagePos; + byte messageColor; + uint messageTimer; + byte messageFirstFrame; + byte messageLastFrame; + Animation *messageAnimation; typedef Common::List<SceneEvent> EventList; EventList events; - SceneEvent current_event; - bool hide_actor; + SceneEvent currentEvent; + bool hideActor; - uint16 callback, callback_timer; + uint16 callback, callbackTimer; - int _fade_timer; - uint _idle_timer; + int _fadeTimer; + byte _fadeOld; + + uint _idleTimer; struct Sound { byte id, delay; @@ -254,7 +254,7 @@ private: feature[i] = true; } } - } debug_features; + } debugFeatures; }; } // End of namespace TeenAgent diff --git a/engines/teenagent/segment.h b/engines/teenagent/segment.h index 303198b071..286337d120 100644 --- a/engines/teenagent/segment.h +++ b/engines/teenagent/segment.h @@ -41,26 +41,21 @@ public: assert(offset < _size); return _data[offset]; } + inline uint16 get_word(uint32 offset) const { assert(offset + 1 < _size); return READ_LE_UINT16(_data + offset); } - inline uint32 get_quad(uint32 offset) const { - assert(offset + 3 < _size); - return READ_LE_UINT32(_data + offset); - } + inline void set_byte(uint32 offset, byte v) const { assert(offset < _size); _data[offset] = v; } + inline void set_word(uint32 offset, uint16 v) const { assert(offset + 1 < _size); return WRITE_LE_UINT16(_data + offset, v); } - inline void set_quad(uint32 offset, uint32 v) const { - assert(offset + 3 < _size); - return WRITE_LE_UINT32(_data + offset, v); - } const byte *ptr(uint32 addr) const { assert(addr < _size); @@ -71,6 +66,7 @@ public: assert(addr < _size); return _data + addr; } + uint size() const { return _size; } }; diff --git a/engines/teenagent/surface.cpp b/engines/teenagent/surface.cpp index 63312990ee..4db25bc749 100644 --- a/engines/teenagent/surface.cpp +++ b/engines/teenagent/surface.cpp @@ -21,6 +21,8 @@ #include "teenagent/surface.h" #include "teenagent/pack.h" +#include "teenagent/teenagent.h" + #include "common/stream.h" #include "common/debug.h" @@ -34,7 +36,7 @@ Surface::~Surface() { } void Surface::load(Common::SeekableReadStream &stream, Type type) { - //debug(0, "load()"); + debugC(0, kDebugSurface, "load()"); free(); x = y = 0; @@ -44,71 +46,71 @@ void Surface::load(Common::SeekableReadStream &stream, Type type) { if (type != kTypeLan) { uint16 pos = stream.readUint16LE(); - x = pos % 320; - y = pos / 320; + x = pos % kScreenWidth; + y = pos / kScreenWidth; } - //debug(0, "declared info: %ux%u (%04xx%04x) -> %u,%u", w_, h_, w_, h_, x, y); + debugC(0, kDebugSurface, "declared info: %ux%u (%04xx%04x) -> %u,%u", w_, h_, w_, h_, x, y); if (stream.eos() || w_ == 0) return; if (w_ * h_ > stream.size()) { - debug(0, "invalid surface %ux%u -> %u,%u", w_, h_, x, y); + debugC(0, kDebugSurface, "invalid surface %ux%u -> %u,%u", w_, h_, x, y); return; } - //debug(0, "creating surface %ux%u -> %u,%u", w_, h_, x, y); + debugC(0, kDebugSurface, "creating surface %ux%u -> %u,%u", w_, h_, x, y); create(w_, h_, Graphics::PixelFormat::createFormatCLUT8()); stream.read(pixels, w_ * h_); } -Common::Rect Surface::render(Graphics::Surface *surface, int dx, int dy, bool mirror, Common::Rect src_rect, uint zoom) const { - if (src_rect.isEmpty()) { - src_rect = Common::Rect(0, 0, w, h); +Common::Rect Surface::render(Graphics::Surface *surface, int dx, int dy, bool mirror, Common::Rect srcRect, uint zoom) const { + if (srcRect.isEmpty()) { + srcRect = Common::Rect(0, 0, w, h); } - Common::Rect dst_rect(x + dx, y + dy, x + dx + zoom * src_rect.width() / 256, y + dy + zoom * src_rect.height() / 256); - if (dst_rect.left < 0) { - src_rect.left = -dst_rect.left; - dst_rect.left = 0; + Common::Rect dstRect(x + dx, y + dy, x + dx + zoom * srcRect.width() / 256, y + dy + zoom * srcRect.height() / 256); + if (dstRect.left < 0) { + srcRect.left = -dstRect.left; + dstRect.left = 0; } - if (dst_rect.right > surface->w) { - src_rect.right -= dst_rect.right - surface->w; - dst_rect.right = surface->w; + if (dstRect.right > surface->w) { + srcRect.right -= dstRect.right - surface->w; + dstRect.right = surface->w; } - if (dst_rect.top < 0) { - src_rect.top -= dst_rect.top; - dst_rect.top = 0; + if (dstRect.top < 0) { + srcRect.top -= dstRect.top; + dstRect.top = 0; } - if (dst_rect.bottom > surface->h) { - src_rect.bottom -= dst_rect.bottom - surface->h; - dst_rect.bottom = surface->h; + if (dstRect.bottom > surface->h) { + srcRect.bottom -= dstRect.bottom - surface->h; + dstRect.bottom = surface->h; } - if (src_rect.isEmpty() || dst_rect.isEmpty()) + if (srcRect.isEmpty() || dstRect.isEmpty()) return Common::Rect(); if (zoom == 256) { - const byte *src = (const byte *)getBasePtr(0, src_rect.top); - byte *dst_base = (byte *)surface->getBasePtr(dst_rect.left, dst_rect.top); + const byte *src = (const byte *)getBasePtr(0, srcRect.top); + byte *dstBase = (byte *)surface->getBasePtr(dstRect.left, dstRect.top); - for (int i = src_rect.top; i < src_rect.bottom; ++i) { - byte *dst = dst_base; - for (int j = src_rect.left; j < src_rect.right; ++j) { + for (int i = srcRect.top; i < srcRect.bottom; ++i) { + byte *dst = dstBase; + for (int j = srcRect.left; j < srcRect.right; ++j) { byte p = src[(mirror ? w - j - 1 : j)]; if (p != 0xff) *dst++ = p; else ++dst; } - dst_base += surface->pitch; + dstBase += surface->pitch; src += pitch; } } else { - byte *dst = (byte *)surface->getBasePtr(dst_rect.left, dst_rect.top); - for (int i = 0; i < dst_rect.height(); ++i) { - for (int j = 0; j < dst_rect.width(); ++j) { + byte *dst = (byte *)surface->getBasePtr(dstRect.left, dstRect.top); + for (int i = 0; i < dstRect.height(); ++i) { + for (int j = 0; j < dstRect.width(); ++j) { int px = j * 256 / zoom; - const byte *src = (const byte *)getBasePtr(src_rect.left + (mirror ? w - px - 1 : px), src_rect.top + i * 256 / zoom); + const byte *src = (const byte *)getBasePtr(srcRect.left + (mirror ? w - px - 1 : px), srcRect.top + i * 256 / zoom); byte p = *src; if (p != 0xff) dst[j] = p; @@ -116,7 +118,7 @@ Common::Rect Surface::render(Graphics::Surface *surface, int dx, int dy, bool mi dst += surface->pitch; } } - return dst_rect; + return dstRect; } } // End of namespace TeenAgent diff --git a/engines/teenagent/surface.h b/engines/teenagent/surface.h index 51368c6bee..3e591ed3e0 100644 --- a/engines/teenagent/surface.h +++ b/engines/teenagent/surface.h @@ -33,16 +33,17 @@ namespace TeenAgent { class Surface : public Graphics::Surface { public: - enum Type {kTypeOns, kTypeLan}; - - uint16 x, y; - Surface(); ~Surface(); + + enum Type {kTypeOns, kTypeLan}; + void load(Common::SeekableReadStream &, Type type); - Common::Rect render(Graphics::Surface *surface, int dx = 0, int dy = 0, bool mirror = false, Common::Rect src_rect = Common::Rect(), uint zoom = 256) const; + Common::Rect render(Graphics::Surface *surface, int dx = 0, int dy = 0, bool mirror = false, Common::Rect srcRect = Common::Rect(), uint zoom = 256) const; bool empty() const { return pixels == NULL; } + + uint16 x, y; }; } // End of namespace TeenAgent diff --git a/engines/teenagent/surface_list.cpp b/engines/teenagent/surface_list.cpp index 31387ac3cb..e293ce6470 100644 --- a/engines/teenagent/surface_list.cpp +++ b/engines/teenagent/surface_list.cpp @@ -19,34 +19,35 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "teenagent/surface.h" #include "teenagent/surface_list.h" -#include "objects.h" +#include "teenagent/surface.h" +#include "teenagent/objects.h" +#include "teenagent/teenagent.h" namespace TeenAgent { -SurfaceList::SurfaceList() : surfaces(NULL), surfaces_n(0) {} +SurfaceList::SurfaceList() : surfaces(NULL), surfacesCount(0) {} SurfaceList::~SurfaceList() { free(); } -void SurfaceList::load(Common::SeekableReadStream &stream, Type type, int sub_hack) { +void SurfaceList::load(Common::SeekableReadStream &stream, int subHack) { free(); byte fn = stream.readByte(); if (stream.eos()) return; - surfaces_n = fn - sub_hack; - debug(0, "loading %u surfaces from list (skip %d)", surfaces_n, sub_hack); + surfacesCount = fn - subHack; + debugC(0, kDebugSurface, "loading %u surfaces from list (skip %d)", surfacesCount, subHack); - if (surfaces_n == 0) + if (surfacesCount == 0) return; - surfaces = new Surface[surfaces_n]; + surfaces = new Surface[surfacesCount]; - for (byte i = 0; i < surfaces_n; ++i) { + for (byte i = 0; i < surfacesCount; ++i) { uint offset = stream.readUint16LE(); uint pos = stream.pos(); stream.seek(offset); @@ -58,11 +59,11 @@ void SurfaceList::load(Common::SeekableReadStream &stream, Type type, int sub_ha void SurfaceList::free() { delete[] surfaces; surfaces = NULL; - surfaces_n = 0; + surfacesCount = 0; } void SurfaceList::render(Graphics::Surface *surface, const Common::Rect &clip) const { - for (uint i = 0; i < surfaces_n; ++i) { + for (uint i = 0; i < surfacesCount; ++i) { const Surface &s = surfaces[i]; Common::Rect r(s.x, s.y, s.x + s.w, s.y + s.h); if (r.bottom < clip.bottom || !clip.intersects(r)) diff --git a/engines/teenagent/surface_list.h b/engines/teenagent/surface_list.h index 2d7be0d52b..73a41fb5f8 100644 --- a/engines/teenagent/surface_list.h +++ b/engines/teenagent/surface_list.h @@ -23,23 +23,23 @@ #define TEENAGENT_SURFACE_LIST_H__ #include "common/stream.h" +#include "graphics/surface.h" namespace TeenAgent { class Surface; class SurfaceList { public: - enum Type { kTypeOn }; - SurfaceList(); ~SurfaceList(); - void load(Common::SeekableReadStream &, Type type, int sub_hack = 0); + + void load(Common::SeekableReadStream &, int subHack = 0); void free(); void render(Graphics::Surface *surface, const Common::Rect &clip) const; protected: Surface *surfaces; - uint surfaces_n; + uint surfacesCount; }; } diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp index 57c069fe59..0b48a18b26 100644 --- a/engines/teenagent/teenagent.cpp +++ b/engines/teenagent/teenagent.cpp @@ -21,6 +21,7 @@ #include "common/config-manager.h" #include "common/debug.h" +#include "common/debug-channels.h" #include "common/events.h" #include "common/savefile.h" #include "common/system.h" @@ -39,26 +40,56 @@ #include "graphics/thumbnail.h" #include "teenagent/console.h" +#include "teenagent/dialog.h" +#include "teenagent/inventory.h" #include "teenagent/music.h" #include "teenagent/objects.h" #include "teenagent/pack.h" +#include "teenagent/resources.h" #include "teenagent/scene.h" #include "teenagent/teenagent.h" namespace TeenAgent { TeenAgentEngine::TeenAgentEngine(OSystem *system, const ADGameDescription *gd) - : Engine(system), action(kActionNone), _gameDescription(gd), - _rnd("teenagent") { - music = new MusicPlayer(); + : Engine(system), _action(kActionNone), _gameDescription(gd), _rnd("teenagent") { + DebugMan.addDebugChannel(kDebugActor, "Actor", "Enable Actor Debug"); + DebugMan.addDebugChannel(kDebugAnimation, "Animation", "Enable Animation Debug"); + DebugMan.addDebugChannel(kDebugCallbacks, "Callbacks", "Enable Callbacks Debug"); + DebugMan.addDebugChannel(kDebugDialog, "Dialog", "Enable Dialog Debug"); + DebugMan.addDebugChannel(kDebugFont, "Font", "Enable Font Debug"); + DebugMan.addDebugChannel(kDebugInventory, "Inventory", "Enable Inventory Debug"); + DebugMan.addDebugChannel(kDebugMusic, "Music", "Enable Music Debug"); + DebugMan.addDebugChannel(kDebugObject, "Object", "Enable Object Debug"); + DebugMan.addDebugChannel(kDebugPack, "Pack", "Enable Pack Debug"); + DebugMan.addDebugChannel(kDebugScene, "Scene", "Enable Scene Debug"); + DebugMan.addDebugChannel(kDebugSurface, "Surface", "Enable Surface Debug"); + + music = new MusicPlayer(this); + dialog = new Dialog(this); + res = new Resources(); console = 0; } TeenAgentEngine::~TeenAgentEngine() { + delete dialog; + dialog = 0; + delete scene; + scene = 0; + delete inventory; + inventory = 0; delete music; + music = 0; + _mixer->stopAll(); + _useHotspots.clear(); + delete res; + res = 0; + + CursorMan.popCursor(); delete console; + DebugMan.clearAllDebugChannels(); } bool TeenAgentEngine::trySelectedObject() { @@ -66,66 +97,64 @@ bool TeenAgentEngine::trySelectedObject() { if (inv == NULL) return false; - Resources *res = Resources::instance(); - debug(0, "checking active object %u on %u", inv->id, dst_object->id); + debugC(0, kDebugObject, "checking active object %u on %u", inv->id, _dstObject->id); //mouse time challenge hack: - if ((res->dseg.get_byte(0) == 1 && inv->id == 49 && dst_object->id == 5) || - (res->dseg.get_byte(0) == 2 && inv->id == 29 && dst_object->id == 5)) { + if ((res->dseg.get_byte(dsAddr_timedCallbackState) == 1 && inv->id == kInvItemRock && _dstObject->id == 5) || + (res->dseg.get_byte(dsAddr_timedCallbackState) == 2 && inv->id == kInvItemSuperGlue && _dstObject->id == 5)) { //putting rock into hole or superglue on rock - processCallback(0x8d57); + fnPutRockInHole(); return true; } - const Common::Array<UseHotspot> &hotspots = use_hotspots[scene->getId() - 1]; + const Common::Array<UseHotspot> &hotspots = _useHotspots[scene->getId() - 1]; for (uint i = 0; i < hotspots.size(); ++i) { const UseHotspot &spot = hotspots[i]; - if (spot.inventory_id == inv->id && dst_object->id == spot.object_id) { - debug(0, "use object on hotspot!"); + if (spot.inventoryId == inv->id && _dstObject->id == spot.objectId) { + debugC(0, kDebugObject, "use object on hotspot!"); spot.dump(); - if (spot.actor_x != 0xffff && spot.actor_y != 0xffff) - moveTo(spot.actor_x, spot.actor_y, spot.orientation); + if (spot.actorX != 0xffff && spot.actorY != 0xffff) + moveTo(spot.actorX, spot.actorY, spot.orientation); if (!processCallback(spot.callback)) - debug(0, "fixme! display proper description"); + debugC(0, kDebugObject, "FIXME: display proper description"); inventory->resetSelectedObject(); return true; } } - //error + // error inventory->resetSelectedObject(); - displayMessage(0x3457); + displayMessage(dsAddr_objErrorMsg); // "That's no good" return true; } void TeenAgentEngine::processObject() { - if (dst_object == NULL) + if (_dstObject == NULL) return; - Resources *res = Resources::instance(); - switch (action) { + switch (_action) { case kActionExamine: { if (trySelectedObject()) break; - byte *dcall = res->dseg.ptr(0xb5ce); + byte *dcall = res->dseg.ptr(dsAddr_objExamineCallbackTablePtr); dcall = res->dseg.ptr(READ_LE_UINT16(dcall + scene->getId() * 2 - 2)); - dcall += 2 * dst_object->id - 2; + dcall += 2 * _dstObject->id - 2; uint16 callback = READ_LE_UINT16(dcall); if (callback == 0 || !processCallback(callback)) - displayMessage(dst_object->description); + displayMessage(_dstObject->description); } break; case kActionUse: { if (trySelectedObject()) break; - byte *dcall = res->dseg.ptr(0xb89c); + byte *dcall = res->dseg.ptr(dsAddr_objUseCallbackTablePtr); dcall = res->dseg.ptr(READ_LE_UINT16(dcall + scene->getId() * 2 - 2)); - dcall += 2 * dst_object->id - 2; + dcall += 2 * _dstObject->id - 2; uint16 callback = READ_LE_UINT16(dcall); if (!processCallback(callback)) - displayMessage(dst_object->description); + displayMessage(_dstObject->description); } break; @@ -134,20 +163,19 @@ void TeenAgentEngine::processObject() { } } - void TeenAgentEngine::use(Object *object) { if (object == NULL || scene->eventRunning()) return; - dst_object = object; + _dstObject = object; object->rect.dump(); - object->actor_rect.dump(); + object->actorRect.dump(); - action = kActionUse; - if (object->actor_rect.valid()) - scene->moveTo(Common::Point(object->actor_rect.right, object->actor_rect.bottom), object->actor_orientation); - else if (object->actor_orientation > 0) - scene->setOrientation(object->actor_orientation); + _action = kActionUse; + if (object->actorRect.valid()) + scene->moveTo(Common::Point(object->actorRect.right, object->actorRect.bottom), object->actorOrientation); + else if (object->actorOrientation > 0) + scene->setOrientation(object->actorOrientation); } void TeenAgentEngine::examine(const Common::Point &point, Object *object) { @@ -155,53 +183,39 @@ void TeenAgentEngine::examine(const Common::Point &point, Object *object) { return; if (object != NULL) { - Common::Point dst = object->actor_rect.center(); - debug(0, "click %d, %d, object %d, %d", point.x, point.y, dst.x, dst.y); - action = kActionExamine; - if (object->actor_rect.valid()) - scene->moveTo(dst, object->actor_orientation, true); //validate examine message. Original engine does not let you into walkboxes - dst_object = object; - } else if (!scene_busy) { - //do not reset anything while scene is busy, but allow interrupts while walking. - debug(0, "click %d, %d", point.x, point.y); - action = kActionNone; + Common::Point dst = object->actorRect.center(); + debugC(0, kDebugObject, "click %d, %d, object %d, %d", point.x, point.y, dst.x, dst.y); + _action = kActionExamine; + if (object->actorRect.valid()) + scene->moveTo(dst, object->actorOrientation, true); // validate examine message. Original engine does not let you into walkboxes + _dstObject = object; + } else if (!_sceneBusy) { + // do not reset anything while scene is busy, but allow interrupts while walking. + debugC(0, kDebugObject, "click %d, %d", point.x, point.y); + _action = kActionNone; scene->moveTo(point, 0, true); - dst_object = NULL; + _dstObject = NULL; } } void TeenAgentEngine::init() { - _mark_delay = 80; - _game_delay = 110; + _markDelay = 80; + _gameDelay = 110; - Resources *res = Resources::instance(); - use_hotspots.resize(42); - byte *scene_hotspots = res->dseg.ptr(0xbb87); + _useHotspots.resize(42); + byte *sceneHotspots = res->dseg.ptr(dsAddr_sceneHotspotsPtr); for (byte i = 0; i < 42; ++i) { - Common::Array<UseHotspot> & hotspots = use_hotspots[i]; - byte *hotspots_ptr = res->dseg.ptr(READ_LE_UINT16(scene_hotspots + i * 2)); - while (*hotspots_ptr) { + Common::Array<UseHotspot> & hotspots = _useHotspots[i]; + byte *hotspotsPtr = res->dseg.ptr(READ_LE_UINT16(sceneHotspots + i * 2)); + while (*hotspotsPtr) { UseHotspot h; - h.load(hotspots_ptr); - hotspots_ptr += 9; + h.load(hotspotsPtr); + hotspotsPtr += 9; hotspots.push_back(h); } } } -void TeenAgentEngine::deinit() { - _mixer->stopAll(); - delete scene; - scene = NULL; - delete inventory; - inventory = NULL; - //delete music; - //music = NULL; - use_hotspots.clear(); - Resources::instance()->deinit(); - CursorMan.popCursor(); -} - Common::Error TeenAgentEngine::loadGameState(int slot) { debug(0, "loading from slot %d", slot); Common::ScopedPtr<Common::InSaveFile> in(_saveFileMan->openForLoading(Common::String::format("teenagent.%02d", slot))); @@ -211,22 +225,19 @@ Common::Error TeenAgentEngine::loadGameState(int slot) { if (!in) return Common::kReadPermissionDenied; - Resources *res = Resources::instance(); + assert(res->dseg.size() >= dsAddr_saveState + saveStateSize); - const uint dataSize = 0x777a; - assert(res->dseg.size() >= 0x6478 + dataSize); - - char *data = (char *)malloc(dataSize); + char *data = (char *)malloc(saveStateSize); if (!data) error("[TeenAgentEngine::loadGameState] Cannot allocate buffer"); in->seek(0); - if (in->read(data, dataSize) != dataSize) { + if (in->read(data, saveStateSize) != saveStateSize) { free(data); return Common::kReadingFailed; } - memcpy(res->dseg.ptr(0x6478), data, dataSize); + memcpy(res->dseg.ptr(dsAddr_saveState), data, saveStateSize); free(data); @@ -234,10 +245,10 @@ Common::Error TeenAgentEngine::loadGameState(int slot) { inventory->activate(false); inventory->reload(); - setMusic(Resources::instance()->dseg.get_byte(0xDB90)); + setMusic(res->dseg.get_byte(dsAddr_currentMusic)); - int id = res->dseg.get_byte(0xB4F3); - uint16 x = res->dseg.get_word(0x64AF), y = res->dseg.get_word(0x64B1); + int id = res->dseg.get_byte(dsAddr_currentScene); + uint16 x = res->dseg.get_word(dsAddr_egoX), y = res->dseg.get_word(dsAddr_egoY); scene->loadObjectData(); scene->init(id, Common::Point(x, y)); scene->setPalette(4); @@ -251,15 +262,15 @@ Common::Error TeenAgentEngine::saveGameState(int slot, const Common::String &des if (!out) return Common::kWritingFailed; - Resources *res = Resources::instance(); - res->dseg.set_byte(0xB4F3, scene->getId()); + res->dseg.set_byte(dsAddr_currentScene, scene->getId()); Common::Point pos = scene->getPosition(); - res->dseg.set_word(0x64AF, pos.x); - res->dseg.set_word(0x64B1, pos.y); + res->dseg.set_word(dsAddr_egoX, pos.x); + res->dseg.set_word(dsAddr_egoY, pos.y); - assert(res->dseg.size() >= 0x6478 + 0x777a); - strncpy((char *)res->dseg.ptr(0x6478), desc.c_str(), 0x16); - out->write(res->dseg.ptr(0x6478), 0x777a); + assert(res->dseg.size() >= dsAddr_saveState + saveStateSize); + // FIXME: Description string is 24 bytes and null based on detection.cpp code, not 22? + strncpy((char *)res->dseg.ptr(dsAddr_saveState), desc.c_str(), 22); + out->write(res->dseg.ptr(dsAddr_saveState), saveStateSize); if (!Graphics::saveThumbnail(*out)) warning("saveThumbnail failed"); @@ -267,7 +278,6 @@ Common::Error TeenAgentEngine::saveGameState(int slot, const Common::String &des return Common::kNoError; } - int TeenAgentEngine::skipEvents() const { Common::EventManager *_event = _system->getEventManager(); Common::Event event; @@ -295,7 +305,7 @@ bool TeenAgentEngine::showCDLogo() { if (!cdlogo.exists("cdlogo.res") || !cdlogo.open("cdlogo.res")) return true; - const uint bgSize = 0xfa00; + const uint bgSize = kScreenWidth * kScreenHeight; const uint paletteSize = 3 * 256; byte *bg = (byte *)malloc(bgSize); @@ -314,8 +324,8 @@ bool TeenAgentEngine::showCDLogo() { for (uint c = 0; c < paletteSize; ++c) palette[c] *= 4; - _system->getPaletteManager()->setPalette(palette, 0, 0x100); - _system->copyRectToScreen(bg, 320, 0, 0, 320, 200); + _system->getPaletteManager()->setPalette(palette, 0, 256); + _system->copyRectToScreen(bg, kScreenWidth, 0, 0, kScreenWidth, kScreenHeight); _system->updateScreen(); free(bg); @@ -341,7 +351,7 @@ bool TeenAgentEngine::showLogo() { if (!frame) return true; - const uint bgSize = 0xfa00; + const uint bgSize = kScreenWidth * kScreenHeight; const uint paletteSize = 3 * 256; byte *bg = (byte *)malloc(bgSize); @@ -360,7 +370,7 @@ bool TeenAgentEngine::showLogo() { for (uint c = 0; c < paletteSize; ++c) palette[c] *= 4; - _system->getPaletteManager()->setPalette(palette, 0, 0x100); + _system->getPaletteManager()->setPalette(palette, 0, 256); free(palette); @@ -374,7 +384,7 @@ bool TeenAgentEngine::showLogo() { return r > 0 ? true : false; } } - _system->copyRectToScreen(bg, 320, 0, 0, 320, 200); + _system->copyRectToScreen(bg, kScreenWidth, 0, 0, kScreenWidth, kScreenHeight); frame.reset(logo.getStream(i)); if (!frame) { @@ -419,23 +429,23 @@ bool TeenAgentEngine::showMetropolis() { palette[c] *= 4; } - _system->getPaletteManager()->setPalette(palette, 0, 0x100); + _system->getPaletteManager()->setPalette(palette, 0, 256); free(palette); const uint varia6Size = 21760; const uint varia9Size = 18302; - byte *varia_6 = (byte *)malloc(varia6Size); - byte *varia_9 = (byte *)malloc(varia9Size); - if (!varia_6 || !varia_9) { - free(varia_6); - free(varia_9); + byte *varia6Data = (byte *)malloc(varia6Size); + byte *varia9Data = (byte *)malloc(varia9Size); + if (!varia6Data || !varia9Data) { + free(varia6Data); + free(varia9Data); error("[TeenAgentEngine::showMetropolis] Cannot allocate buffer"); } - varia.read(6, varia_6, varia6Size); - varia.read(9, varia_9, varia9Size); + varia.read(6, varia6Data, varia6Size); + varia.read(9, varia9Data, varia9Size); const uint colorsSize = 56 * 160 * 2; byte *colors = (byte *)malloc(colorsSize); @@ -449,8 +459,8 @@ bool TeenAgentEngine::showMetropolis() { { int r = skipEvents(); if (r != 0) { - free(varia_6); - free(varia_9); + free(varia6Data); + free(varia9Data); free(colors); return r > 0 ? true : false; } @@ -458,7 +468,7 @@ bool TeenAgentEngine::showMetropolis() { Graphics::Surface *surface = _system->lockScreen(); if (logo_y > 0) { - surface->fillRect(Common::Rect(0, 0, 320, logo_y), 0); + surface->fillRect(Common::Rect(0, 0, kScreenWidth, logo_y), 0); } { @@ -485,7 +495,7 @@ bool TeenAgentEngine::showMetropolis() { } byte *dst = (byte *)surface->getBasePtr(0, 131); - byte *src = varia_6; + byte *src = varia6Data; for (uint y = 0; y < 68; ++y) { for (uint x = 0; x < 320; ++x) { if (*src++ == 1) { @@ -497,7 +507,7 @@ bool TeenAgentEngine::showMetropolis() { _system->unlockScreen(); _system->copyRectToScreen( - varia_9 + (logo_y < 0 ? -logo_y * 320 : 0), 320, + varia9Data + (logo_y < 0 ? -logo_y * 320 : 0), 320, 0, logo_y >= 0 ? logo_y : 0, 320, logo_y >= 0 ? 57 : 57 + logo_y); @@ -509,38 +519,37 @@ bool TeenAgentEngine::showMetropolis() { _system->delayMillis(100); } - free(varia_6); - free(varia_9); + free(varia6Data); + free(varia9Data); free(colors); return true; } Common::Error TeenAgentEngine::run() { - Resources *res = Resources::instance(); if (!res->loadArchives(_gameDescription)) return Common::kUnknownError; Common::EventManager *_event = _system->getEventManager(); - initGraphics(320, 200, false); + initGraphics(kScreenWidth, kScreenHeight, false); console = new Console(this); - scene = new Scene(this, _system); + scene = new Scene(this); inventory = new Inventory(this); init(); - CursorMan.pushCursor(res->dseg.ptr(0x00da), 8, 12, 0, 0, 1); + CursorMan.pushCursor(res->dseg.ptr(dsAddr_cursor), 8, 12, 0, 0, 1); syncSoundSettings(); setMusic(1); _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, music, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false); - int load_slot = Common::ConfigManager::instance().getInt("save_slot"); - if (load_slot >= 0) { - loadGameState(load_slot); + int loadSlot = ConfMan.getInt("save_slot"); + if (loadSlot >= 0) { + loadGameState(loadSlot); } else { if (!showCDLogo()) return Common::kNoError; @@ -549,32 +558,30 @@ Common::Error TeenAgentEngine::run() { if (!showMetropolis()) return Common::kNoError; scene->intro = true; - scene_busy = true; - processCallback(0x24c); + _sceneBusy = true; + fnIntro(); } CursorMan.showMouse(true); - uint32 game_timer = 0; - uint32 mark_timer = 0; + uint32 gameTimer = 0; + uint32 markTimer = 0; Common::Event event; Common::Point mouse; uint32 timer = _system->getMillis(); do { - Object *current_object = scene->findObject(mouse); + Object *currentObject = scene->findObject(mouse); while (_event->pollEvent(event)) { - if (event.type == Common::EVENT_RTL) { - deinit(); + if (event.type == Common::EVENT_RTL) return Common::kNoError; - } - if ((!scene_busy && inventory->processEvent(event)) || scene->processEvent(event)) + if ((!_sceneBusy && inventory->processEvent(event)) || scene->processEvent(event)) continue; - //debug(0, "event"); + debug(5, "event"); switch (event.type) { case Common::EVENT_KEYDOWN: if ((event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_d) || @@ -583,33 +590,33 @@ Common::Error TeenAgentEngine::run() { } else if (event.kbd.hasFlags(0) && event.kbd.keycode == Common::KEYCODE_F5) { openMainMenuDialog(); } if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_f) { - _mark_delay = _mark_delay == 80 ? 40 : 80; - debug(0, "mark_delay = %u", _mark_delay); + _markDelay = _markDelay == 80 ? 40 : 80; + debug(5, "markDelay = %u", _markDelay); } break; case Common::EVENT_LBUTTONDOWN: if (scene->getId() < 0) break; - examine(event.mouse, current_object); + examine(event.mouse, currentObject); break; case Common::EVENT_RBUTTONDOWN: - //if (current_object) - // debug(0, "%d, %s", current_object->id, current_object->name.c_str()); + if (currentObject) + debugC(0, kDebugObject, "%d, %s", currentObject->id, currentObject->name.c_str()); if (scene->getId() < 0) break; - if (current_object == NULL) + if (currentObject == NULL) break; - if (res->dseg.get_byte(0) == 3 && current_object->id == 1) { - processCallback(0x5189); //boo! + if (res->dseg.get_byte(dsAddr_timedCallbackState) == 3 && currentObject->id == 1) { + fnGuardDrinking(); break; } - if (res->dseg.get_byte(0) == 4 && current_object->id == 5) { - processCallback(0x99e0); //getting an anchor + if (res->dseg.get_byte(dsAddr_timedCallbackState) == 4 && currentObject->id == 5) { + fnGotAnchor(); break; } - use(current_object); + use(currentObject); break; case Common::EVENT_MOUSEMOVE: mouse = event.mouse; @@ -622,60 +629,60 @@ Common::Error TeenAgentEngine::run() { //game delays: slow 16, normal 11, fast 5, crazy 1 //mark delays: 4 * (3 - hero_speed), normal == 1 //game delays in 1/100th of seconds - uint32 new_timer = _system->getMillis(); - uint32 delta = new_timer - timer; - timer = new_timer; + uint32 newTimer = _system->getMillis(); + uint32 delta = newTimer - timer; + timer = newTimer; - bool tick_game = game_timer <= delta; - if (tick_game) - game_timer = _game_delay - ((delta - game_timer) % _game_delay); + bool tickGame = gameTimer <= delta; + if (tickGame) + gameTimer = _gameDelay - ((delta - gameTimer) % _gameDelay); else - game_timer -= delta; + gameTimer -= delta; - bool tick_mark = mark_timer <= delta; - if (tick_mark) - mark_timer = _mark_delay - ((delta - mark_timer) % _mark_delay); + bool tickMark = markTimer <= delta; + if (tickMark) + markTimer = _markDelay - ((delta - markTimer) % _markDelay); else - mark_timer -= delta; + markTimer -= delta; - if (tick_game || tick_mark) { - bool b = scene->render(tick_game, tick_mark, delta); - if (!inventory->active() && !b && action != kActionNone) { + if (tickGame || tickMark) { + bool b = scene->render(tickGame, tickMark, delta); + if (!inventory->active() && !b && _action != kActionNone) { processObject(); - action = kActionNone; - dst_object = NULL; + _action = kActionNone; + _dstObject = NULL; } - scene_busy = b; + _sceneBusy = b; } - _system->showMouse(scene->getMessage().empty() && !scene_busy); + _system->showMouse(scene->getMessage().empty() && !_sceneBusy); - bool busy = inventory->active() || scene_busy; + bool busy = inventory->active() || _sceneBusy; Graphics::Surface *surface = _system->lockScreen(); if (!busy) { - InventoryObject *selected_object = inventory->selectedObject(); - if (current_object || selected_object) { + InventoryObject *selectedObject = inventory->selectedObject(); + if (currentObject || selectedObject) { Common::String name; - if (selected_object) { - name += selected_object->name; + if (selectedObject) { + name += selectedObject->name; name += " & "; } - if (current_object) - name += current_object->name; + if (currentObject) + name += currentObject->name; - uint w = res->font7.render(NULL, 0, 0, name, 0xd1); - res->font7.render(surface, (320 - w) / 2, 180, name, 0xd1, true); + uint w = res->font7.render(NULL, 0, 0, name, textColorMark); + res->font7.render(surface, (kScreenWidth - w) / 2, 180, name, textColorMark, true); #if 0 - if (current_object) { - current_object->rect.render(surface, 0x80); - current_object->actor_rect.render(surface, 0x81); + if (currentObject) { + currentObject->rect.render(surface, 0x80); + currentObject->actorRect.render(surface, 0x81); } #endif } } - inventory->render(surface, tick_game ? 1 : 0); + inventory->render(surface, tickGame ? 1 : 0); _system->unlockScreen(); @@ -683,20 +690,19 @@ Common::Error TeenAgentEngine::run() { console->onFrame(); - uint32 next_tick = MIN(game_timer, mark_timer); - if (next_tick > 0) { - _system->delayMillis(next_tick > 40 ? 40 : next_tick); + uint32 nextTick = MIN(gameTimer, markTimer); + if (nextTick > 0) { + _system->delayMillis(nextTick > 40 ? 40 : nextTick); } } while (!shouldQuit()); - deinit(); return Common::kNoError; } Common::String TeenAgentEngine::parseMessage(uint16 addr) { Common::String message; for ( - const char *str = (const char *)Resources::instance()->dseg.ptr(addr); + const char *str = (const char *)res->dseg.ptr(addr); str[0] != 0 || str[1] != 0; ++str) { char c = str[0]; @@ -708,12 +714,12 @@ Common::String TeenAgentEngine::parseMessage(uint16 addr) { return message; } -void TeenAgentEngine::displayMessage(const Common::String &str, byte color, uint16 position) { +void TeenAgentEngine::displayMessage(const Common::String &str, byte color, uint16 x, uint16 y) { if (str.empty()) { return; } - if (color == 0xd1) { //mark's + if (color == textColorMark) { // mark's SceneEvent e(SceneEvent::kPlayAnimation); e.animation = 0; e.slot = 0x80; @@ -725,8 +731,8 @@ void TeenAgentEngine::displayMessage(const Common::String &str, byte color, uint event.message = str; event.color = color; event.slot = 0; - event.dst.x = position % 320; - event.dst.y = position / 320; + event.dst.x = x; + event.dst.y = y; scene->push(event); } @@ -738,46 +744,45 @@ void TeenAgentEngine::displayMessage(const Common::String &str, byte color, uint } } -void TeenAgentEngine::displayMessage(uint16 addr, byte color, uint16 position) { - displayMessage(parseMessage(addr), color, position); +void TeenAgentEngine::displayMessage(uint16 addr, byte color, uint16 x, uint16 y) { + displayMessage(parseMessage(addr), color, x, y); } -void TeenAgentEngine::displayAsyncMessage(uint16 addr, uint16 position, uint16 first_frame, uint16 last_frame, byte color) { +void TeenAgentEngine::displayAsyncMessage(uint16 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, byte color) { SceneEvent event(SceneEvent::kMessage); event.message = parseMessage(addr); event.slot = 0; event.color = color; - event.dst.x = position % 320; - event.dst.y = position / 320; - event.first_frame = first_frame; - event.last_frame = last_frame; + event.dst.x = x; + event.dst.y = y; + event.firstFrame = firstFrame; + event.lastFrame = lastFrame; scene->push(event); } -void TeenAgentEngine::displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 first_frame, uint16 last_frame, byte color) { +void TeenAgentEngine::displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color) { SceneEvent event(SceneEvent::kMessage); event.message = parseMessage(addr); event.slot = slot + 1; event.color = color; - event.first_frame = first_frame; - event.last_frame = last_frame; + event.firstFrame = firstFrame; + event.lastFrame = lastFrame; scene->push(event); } - void TeenAgentEngine::displayCredits(uint16 addr, uint16 timer) { SceneEvent event(SceneEvent::kCreditsMessage); - const byte *src = Resources::instance()->dseg.ptr(addr); + const byte *src = res->dseg.ptr(addr); event.orientation = *src++; event.color = *src++; event.lan = 8; event.dst.y = *src; while (true) { - ++src; //skip y position + ++src; // skip y position Common::String line((const char *)src); event.message += line; src += line.size() + 1; @@ -785,33 +790,33 @@ void TeenAgentEngine::displayCredits(uint16 addr, uint16 timer) { break; event.message += "\n"; } - int w = Resources::instance()->font8.render(NULL, 0, 0, event.message, 0xd1); - event.dst.x = (320 - w) / 2; + int w = res->font8.render(NULL, 0, 0, event.message, textColorCredits); + event.dst.x = (kScreenWidth - w) / 2; event.timer = timer; scene->push(event); } void TeenAgentEngine::displayCredits() { SceneEvent event(SceneEvent::kCredits); - event.message = parseMessage(0xe488); - event.dst.y = 200; + event.message = parseMessage(dsAddr_finalCredits7); + event.dst.y = kScreenHeight; int lines = 1; for (uint i = 0; i < event.message.size(); ++i) if (event.message[i] == '\n') ++lines; - event.dst.x = (320 - Resources::instance()->font7.render(NULL, 0, 0, event.message, 0xd1)) / 2; + event.dst.x = (kScreenWidth - res->font7.render(NULL, 0, 0, event.message, textColorCredits)) / 2; event.timer = 11 * lines - event.dst.y + 22; - //debug(0, "credits = %s", event.message.c_str()); + debug(2, "credits = %s", event.message.c_str()); scene->push(event); } -void TeenAgentEngine::displayCutsceneMessage(uint16 addr, uint16 position) { +void TeenAgentEngine::displayCutsceneMessage(uint16 addr, uint16 x, uint16 y) { SceneEvent event(SceneEvent::kCreditsMessage); event.message = parseMessage(addr); - event.dst.x = position % 320; - event.dst.y = position / 320; + event.dst.x = x; + event.dst.y = y; event.lan = 7; scene->push(event); @@ -822,7 +827,7 @@ void TeenAgentEngine::moveTo(const Common::Point &dst, byte o, bool warp) { } void TeenAgentEngine::moveTo(Object *obj) { - moveTo(obj->actor_rect.right, obj->actor_rect.bottom, obj->actor_orientation); + moveTo(obj->actorRect.right, obj->actorRect.bottom, obj->actorOrientation); } void TeenAgentEngine::moveTo(uint16 x, uint16 y, byte o, bool warp) { @@ -865,7 +870,6 @@ void TeenAgentEngine::playActorAnimation(uint16 id, bool async, bool ignore) { waitAnimation(); } - void TeenAgentEngine::loadScene(byte id, const Common::Point &pos, byte o) { loadScene(id, pos.x, pos.y, o); } @@ -890,21 +894,21 @@ void TeenAgentEngine::enableOn(bool enable) { scene->push(event); } -void TeenAgentEngine::setOns(byte id, byte value, byte scene_id) { +void TeenAgentEngine::setOns(byte id, byte value, byte sceneId) { SceneEvent event(SceneEvent::kSetOn); event.ons = id + 1; event.color = value; - event.scene = scene_id; + event.scene = sceneId; scene->push(event); } -void TeenAgentEngine::setLan(byte id, byte value, byte scene_id) { +void TeenAgentEngine::setLan(byte id, byte value, byte sceneId) { if (id == 0) error("setting lan 0 is invalid"); SceneEvent event(SceneEvent::kSetLan); event.lan = id; event.color = value; - event.scene = scene_id; + event.scene = sceneId; scene->push(event); } @@ -925,35 +929,34 @@ void TeenAgentEngine::reloadLan() { scene->push(event); } - void TeenAgentEngine::playMusic(byte id) { SceneEvent event(SceneEvent::kPlayMusic); event.music = id; scene->push(event); } -void TeenAgentEngine::playSound(byte id, byte skip_frames) { - if (skip_frames > 0) - --skip_frames; +void TeenAgentEngine::playSound(byte id, byte skipFrames) { + if (skipFrames > 0) + --skipFrames; SceneEvent event(SceneEvent::kPlaySound); event.sound = id; - event.color = skip_frames; + event.color = skipFrames; scene->push(event); } -void TeenAgentEngine::enableObject(byte id, byte scene_id) { +void TeenAgentEngine::enableObject(byte id, byte sceneId) { SceneEvent event(SceneEvent::kEnableObject); event.object = id + 1; event.color = 1; - event.scene = scene_id; + event.scene = sceneId; scene->push(event); } -void TeenAgentEngine::disableObject(byte id, byte scene_id) { +void TeenAgentEngine::disableObject(byte id, byte sceneId) { SceneEvent event(SceneEvent::kEnableObject); event.object = id + 1; event.color = 0; - event.scene = scene_id; + event.scene = sceneId; scene->push(event); } @@ -1015,7 +1018,6 @@ void TeenAgentEngine::wait(uint16 frames) { } void TeenAgentEngine::playSoundNow(byte id) { - Resources *res = Resources::instance(); uint size = res->sam_sam.getSize(id); if (size == 0) { warning("skipping invalid sound %u", id); @@ -1024,28 +1026,26 @@ void TeenAgentEngine::playSoundNow(byte id) { byte *data = (byte *)malloc(size); res->sam_sam.read(id, data, size); - //debug(0, "playing %u samples...", size); + debug(3, "playing %u samples...", size); Audio::AudioStream *stream = Audio::makeRawStream(data, size, 11025, 0); - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream); //dispose is YES by default + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream); // dispose is YES by default } - void TeenAgentEngine::setMusic(byte id) { - debug(0, "starting music %u", id); - Resources *res = Resources::instance(); + debugC(0, kDebugMusic, "starting music %u", id); - if (id != 1) //intro music - *res->dseg.ptr(0xDB90) = id; + if (id != 1) // intro music + res->dseg.set_byte(dsAddr_currentMusic, id); if (_gameDescription->flags & ADGF_CD) { byte track2cd[] = {7, 2, 0, 9, 3, 6, 8, 10, 4, 5, 11}; if (id == 0 || id > 11 || track2cd[id - 1] == 0) { - debug(0, "no cd music for id %u", id); + debugC(0, kDebugMusic, "no cd music for id %u", id); return; } byte track = track2cd[id - 1]; - debug(0, "playing cd track %u", track); + debugC(0, kDebugMusic, "playing cd track %u", track); _system->getAudioCDManager()->play(track, -1, 0, 0); } else if (music->load(id)) music->start(); diff --git a/engines/teenagent/teenagent.h b/engines/teenagent/teenagent.h index 737f07ba85..d6a2c0d3c6 100644 --- a/engines/teenagent/teenagent.h +++ b/engines/teenagent/teenagent.h @@ -23,12 +23,15 @@ #define TEENAGENT_ENGINE_H #include "engines/engine.h" -#include "teenagent/pack.h" -#include "teenagent/resources.h" -#include "teenagent/inventory.h" + #include "audio/audiostream.h" #include "audio/mixer.h" + #include "common/random.h" +#include "common/rect.h" +#include "common/array.h" + +#include "teenagent/dialog.h" struct ADGameDescription; @@ -43,14 +46,34 @@ struct ADGameDescription; namespace TeenAgent { struct Object; +struct UseHotspot; class Scene; class MusicPlayer; +class Dialog; class Console; +class Resources; +class Inventory; + +// Engine Debug Flags +enum { + kDebugActor = (1 << 0), + kDebugAnimation = (1 << 1), + kDebugCallbacks = (1 << 2), + kDebugDialog = (1 << 3), + kDebugFont = (1 << 4), + kDebugInventory = (1 << 5), + kDebugMusic = (1 << 6), + kDebugObject = (1 << 7), + kDebugPack = (1 << 8), + kDebugScene = (1 << 9), + kDebugSurface = (1 << 10) +}; + +const uint16 kScreenWidth = 320; +const uint16 kScreenHeight = 200; class TeenAgentEngine : public Engine { public: - enum Action { kActionNone, kActionExamine, kActionUse }; - TeenAgentEngine(OSystem *system, const ADGameDescription *gd); ~TeenAgentEngine(); @@ -58,15 +81,16 @@ public: virtual Common::Error loadGameState(int slot); virtual Common::Error saveGameState(int slot, const Common::String &desc); virtual bool canLoadGameStateCurrently() { return true; } - virtual bool canSaveGameStateCurrently() { return !scene_busy; } + virtual bool canSaveGameStateCurrently() { return !_sceneBusy; } virtual bool hasFeature(EngineFeature f) const; void init(); - void deinit(); + + enum Action { kActionNone, kActionExamine, kActionUse }; void examine(const Common::Point &point, Object *object); void use(Object *object); - inline void cancel() { action = kActionNone; } + inline void cancel() { _action = kActionNone; } bool processCallback(uint16 addr); inline Scene *getScene() { return scene; } @@ -76,15 +100,15 @@ public: bool showMetropolis(); int skipEvents() const; - static Common::String parseMessage(uint16 addr); + Common::String parseMessage(uint16 addr); //event driven: - void displayMessage(uint16 addr, byte color = 0xd1, uint16 position = 0); - void displayMessage(const Common::String &str, byte color = 0xd1, uint16 position = 0); - void displayAsyncMessage(uint16 addr, uint16 position, uint16 first_frame, uint16 last_frame, byte color = 0xd1); - void displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 first_frame, uint16 last_frame, byte color = 0xd1); + void displayMessage(uint16 addr, byte color = textColorMark, uint16 x = 0, uint16 y = 0); + void displayMessage(const Common::String &str, byte color = textColorMark, uint16 x = 0, uint16 y = 0); + void displayAsyncMessage(uint16 addr, uint16 x, uint16 y, uint16 firstFrame, uint16 lastFrame, byte color = textColorMark); + void displayAsyncMessageInSlot(uint16 addr, byte slot, uint16 firstFrame, uint16 lastFrame, byte color = textColorMark); void displayCredits(uint16 addr, uint16 timer = 0); - void displayCutsceneMessage(uint16 addr, uint16 position); + void displayCutsceneMessage(uint16 addr, uint16 x, uint16 y); void moveTo(const Common::Point &dst, byte o, bool warp = false); void moveTo(uint16 x, uint16 y, byte o, bool warp = false); void moveTo(Object *obj); @@ -94,18 +118,18 @@ public: void loadScene(byte id, const Common::Point &pos, byte o = 0); void loadScene(byte id, uint16 x, uint16 y, byte o = 0); void enableOn(bool enable = true); - void setOns(byte id, byte value, byte scene_id = 0); - void setLan(byte id, byte value, byte scene_id = 0); + void setOns(byte id, byte value, byte sceneId = 0); + void setLan(byte id, byte value, byte sceneId = 0); void setFlag(uint16 addr, byte value); byte getFlag(uint16 addr); void reloadLan(); void rejectMessage(); void playMusic(byte id); //schedules play - void playSound(byte id, byte skip_frames); + void playSound(byte id, byte skipFrames); void playSoundNow(byte id); - void enableObject(byte id, byte scene_id = 0); - void disableObject(byte id, byte scene_id = 0); + void enableObject(byte id, byte sceneId = 0); + void disableObject(byte id, byte sceneId = 0); void hideActor(); void showActor(); void waitAnimation(); @@ -119,9 +143,11 @@ public: Common::RandomSource _rnd; + Resources *res; Scene *scene; Inventory *inventory; MusicPlayer *music; + Dialog *dialog; Console *console; void setMusic(byte id); @@ -130,17 +156,47 @@ private: void processObject(); bool trySelectedObject(); - bool scene_busy; - Action action; - Object *dst_object; + bool _sceneBusy; + Action _action; + Object *_dstObject; Audio::AudioStream *_musicStream; Audio::SoundHandle _musicHandle, _soundHandle; const ADGameDescription *_gameDescription; - uint _mark_delay, _game_delay; - - Common::Array<Common::Array<UseHotspot> > use_hotspots; + uint _markDelay, _gameDelay; + + Common::Array<Common::Array<UseHotspot> > _useHotspots; + + void fnIntro(); + void fnPoleClimbFail(); + void fnGotAnchor(); + void fnGetOutOfLake(); + void fnGuardDrinking(); + void fnEgoDefaultPosition(); + void fnEnterCave(); + void fnEgoScaredBySpider(); + void fnMoveToLadderAndLeaveCellar(); + void fnLeaveCellar(); + void fnPutRockInHole(); + void fnEgoBottomRightTurn(); + bool fnCheckingDrawers(); + void fnDrawerOpenMessage(); + bool fnRobotSafeAlreadyUnlockedCheck(); + void fnRobotSafeUnlockCheck(); + bool fnMansionIntrusionAttempt(); + void fnSecondMansionIntrusion(); + void fnThirdMansionIntrusion(); + void fnFourthMansionIntrusion(); + void fnFifthMansionIntrusion(); + void fnSixthMansionIntrusion(); + void fnTooDark(); + bool fnIsCookGone(); + void fnEgoSuspiciousPosition(); + void fnGivingFlowerToOldLady(); + void fnGiveAnotherFlowerToOldLady(); + void fnGivingFlowerToAnne(); + void fnGiveAnotherFlowerToAnne(); }; } // End of namespace TeenAgent diff --git a/engines/tinsel/actors.cpp b/engines/tinsel/actors.cpp index a784ff5788..0ba8b7cdba 100644 --- a/engines/tinsel/actors.cpp +++ b/engines/tinsel/actors.cpp @@ -319,8 +319,8 @@ static void ActorRestoredProcess(CORO_PARAM, const void *param) { CORO_BEGIN_CODE(_ctx); _ctx->pic = RestoreInterpretContext(r->pic); - - // The newly added check here specially sets the process to RES_NOT when loading a savegame. + + // The newly added check here specially sets the process to RES_NOT when loading a savegame. // This is needed particularly for the Psychiatrist scene in Discworld 1 - otherwise Rincewind // can't go upstairs without leaving the building and returning. If this patch causes problems // in other scenes, an added check for the hCode == 1174490602 could be added. diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp index 6ea18c8268..04bc2856ca 100644 --- a/engines/tinsel/pcode.cpp +++ b/engines/tinsel/pcode.cpp @@ -152,7 +152,7 @@ static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491), OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)}; static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)}; -static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58, +static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58, OP_IMM, FRAGMENT_DWORD((42 << 23)), OP_ONE, OP_ZERO, OP_LIBCALL | OPSIZE8, 44, OP_LIBCALL | OPSIZE8, 97, OP_JUMP | OPSIZE16, FRAGMENT_WORD(2220) }; @@ -222,7 +222,7 @@ const WorkaroundEntry workaroundList[] = { // times would cause the game to crash {TINSEL_V2, true, false, Common::kPlatformUnknown, 1109294728, 0, sizeof(fragment13), fragment13}, - // DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than + // DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than // quitting the game when no user input happens for a while {TINSEL_V1, true, true, Common::kPlatformPSX, 0, 2186, sizeof(fragment14), fragment14}, diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp index 518e27f02b..2ef92d853f 100644 --- a/engines/tinsel/saveload.cpp +++ b/engines/tinsel/saveload.cpp @@ -502,7 +502,7 @@ static bool DoRestore() { delete f; // Invalid header, or savegame too new -> skip it return false; } - + // Load in the data. For older savegame versions, we potentially need to load the data twice, once // for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames int numInterpreters = hdr.numInterpreters; diff --git a/engines/toltecs/animation.cpp b/engines/toltecs/animation.cpp index eef9cef9ed..084332cf83 100644 --- a/engines/toltecs/animation.cpp +++ b/engines/toltecs/animation.cpp @@ -53,7 +53,7 @@ void AnimationPlayer::start(uint resIndex) { _vm->_arc->closeResource(); debug(1, "AnimationPlayer::start() width = %d; height = %d; frameCount = %d", _width, _height, _frameCount); - + _vm->_sceneWidth = _width; _vm->_sceneHeight = _height; @@ -63,7 +63,7 @@ void AnimationPlayer::start(uint resIndex) { _frameNumber = 0; // TODO mov screenFlag01, 0FFFFh // TODO mov animDrawFrameFlag, 0FFFFh - + _firstNextFrameOffset = _nextFrameOffset; _firstCurFrameSize = _curFrameSize; _firstNextFrameSize = _nextFrameSize; @@ -81,25 +81,25 @@ void AnimationPlayer::nextFrame() { } else { _frameNumber++; } - + debug(1, "AnimationPlayer::nextFrame() frameNumber = %d", _frameNumber); if (_keepFrameCounter > 0) { _keepFrameCounter--; return; } - + _vm->_arc->openResource(_resIndex); _vm->_arc->seek(_nextFrameOffset, SEEK_CUR); _curFrameSize = _nextFrameSize; - + if (_curFrameSize == 0) _curFrameSize = 1; - + _vm->_arc->read(_animBuffer, _curFrameSize); _nextFrameSize = _vm->_arc->readUint32LE(); _nextFrameOffset += _curFrameSize + 4; - + if (_curFrameSize > 1) { unpackFrame(); // TODO mov animDrawFrameFlag, 0FFFFh diff --git a/engines/toltecs/animation.h b/engines/toltecs/animation.h index 22576d7535..54ec5d8afa 100644 --- a/engines/toltecs/animation.h +++ b/engines/toltecs/animation.h @@ -54,7 +54,7 @@ public: uint16 _width, _height; uint16 _frameNumber, _frameCount; uint32 _keepFrameCounter; - + uint32 _curFrameSize; uint32 _nextFrameSize, _nextFrameOffset; diff --git a/engines/toltecs/menu.cpp b/engines/toltecs/menu.cpp index 415f19ca31..6e23ff988f 100644 --- a/engines/toltecs/menu.cpp +++ b/engines/toltecs/menu.cpp @@ -21,8 +21,11 @@ * */ +#include "audio/mixer.h" #include "common/savefile.h" +#include "common/config-manager.h" + #include "toltecs/toltecs.h" #include "toltecs/menu.h" #include "toltecs/palette.h" @@ -37,7 +40,7 @@ MenuSystem::MenuSystem(ToltecsEngine *vm) : _vm(vm) { MenuSystem::~MenuSystem() { } -int MenuSystem::run() { +int MenuSystem::run(MenuID menuId) { //debug("MenuSystem::run()"); @@ -50,18 +53,13 @@ int MenuSystem::run() { memcpy(backgroundOrig.getBasePtr(0,0), _vm->_screen->_frontScreen, 640 * 400); _currMenuID = kMenuIdNone; - _newMenuID = kMenuIdMain; + _newMenuID = menuId; _currItemID = kItemIdNone; _editingDescription = false; - _cfgText = true; - _cfgVoices = true; - _cfgMasterVolume = 10; - _cfgVoicesVolume = 10; - _cfgMusicVolume = 10; - _cfgSoundFXVolume = 10; - _cfgBackgroundVolume = 10; - _running = true; + + _running = true; _top = 30 - _vm->_guiHeight / 2; + _needRedraw = false; // TODO: buildColorTransTable2 @@ -78,7 +76,7 @@ int MenuSystem::run() { update(); _vm->_system->updateScreen(); } - + // Restore original background memcpy(_vm->_screen->_frontScreen, backgroundOrig.getBasePtr(0,0), 640 * 400); _vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, 0, 640, 400); @@ -89,7 +87,7 @@ int MenuSystem::run() { _background->free(); delete _background; - return 0; + return 0; } void MenuSystem::update() { @@ -104,7 +102,7 @@ void MenuSystem::update() { if (_needRedraw) { //_vm->_system->copyRectToScreen(_vm->_screen->_frontScreen + 39 * 640 + 60, 640, 60, 39, 520, 247); - _vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, 0, 640, 400); + _vm->_system->copyRectToScreen(_vm->_screen->_frontScreen, 640, 0, _top, 640, 400 - _top); //debug("redraw"); _needRedraw = false; } @@ -204,7 +202,7 @@ void MenuSystem::handleKeyDown(const Common::KeyState& kbd) { ItemID MenuSystem::findItemAt(int x, int y) { for (Common::Array<Item>::iterator iter = _items.begin(); iter != _items.end(); iter++) { - if ((*iter).rect.contains(x, y)) + if ((*iter).rect.contains(x, y - _top)) return (*iter).id; } return kItemIdNone; @@ -241,8 +239,8 @@ void MenuSystem::initMenu(MenuID menuID) { drawString(0, 74, 320, 1, 229, _vm->getSysString(kStrWhatCanIDoForYou)); addClickTextItem(kItemIdLoad, 0, 115, 320, 0, _vm->getSysString(kStrLoad), 229, 255); addClickTextItem(kItemIdSave, 0, 135, 320, 0, _vm->getSysString(kStrSave), 229, 255); - addClickTextItem(kItemIdToggleText, 0, 165, 320, 0, _vm->getSysString(kStrTextOn), 229, 255); - addClickTextItem(kItemIdToggleVoices, 0, 185, 320, 0, _vm->getSysString(kStrVoicesOn), 229, 255); + addClickTextItem(kItemIdToggleText, 0, 165, 320, 0, _vm->getSysString(_vm->_cfgText ? kStrTextOn : kStrTextOff), 229, 255); + addClickTextItem(kItemIdToggleVoices, 0, 185, 320, 0, _vm->getSysString(_vm->_cfgVoices ? kStrVoicesOn : kStrVoicesOff), 229, 255); addClickTextItem(kItemIdVolumesMenu, 0, 215, 320, 0, _vm->getSysString(kStrVolume), 229, 255); addClickTextItem(kItemIdPlay, 0, 245, 320, 0, _vm->getSysString(kStrPlay), 229, 255); addClickTextItem(kItemIdQuit, 0, 275, 320, 0, _vm->getSysString(kStrQuit), 229, 255); @@ -326,13 +324,13 @@ void MenuSystem::clickItem(ItemID id) { _newMenuID = kMenuIdLoad; break; case kItemIdToggleText: - setCfgText(!_cfgText, true); - if (!_cfgVoices && !_cfgText) + setCfgText(!_vm->_cfgText, true); + if (!_vm->_cfgVoices && !_vm->_cfgText) setCfgVoices(true, false); break; case kItemIdToggleVoices: - setCfgVoices(!_cfgVoices, true); - if (!_cfgVoices && !_cfgText) + setCfgVoices(!_vm->_cfgVoices, true); + if (!_vm->_cfgVoices && !_vm->_cfgText) setCfgText(true, false); break; case kItemIdVolumesMenu: @@ -416,7 +414,7 @@ void MenuSystem::restoreRect(int x, int y, int w, int h) { } void MenuSystem::shadeRect(int x, int y, int w, int h, byte color1, byte color2) { - byte *src = (byte *)_background->getBasePtr(x, y); + byte *src = (byte *)_vm->_screen->_frontScreen + x + y * 640; for (int xc = 0; xc < w; xc++) { src[xc] = color2; src[xc + h * 640] = color1; @@ -518,49 +516,51 @@ void MenuSystem::clickSavegameItem(ItemID id) { } void MenuSystem::setCfgText(bool value, bool active) { - if (_cfgText != value) { + if (_vm->_cfgText != value) { Item *item = getItem(kItemIdToggleText); - _cfgText = value; + _vm->_cfgText = value; restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2); - setItemCaption(item, _vm->getSysString(_cfgText ? kStrTextOn : kStrTextOff)); + setItemCaption(item, _vm->getSysString(_vm->_cfgText ? kStrTextOn : kStrTextOff)); drawItem(kItemIdToggleText, true); + ConfMan.setBool("subtitles", value); } } void MenuSystem::setCfgVoices(bool value, bool active) { - if (_cfgVoices != value) { + if (_vm->_cfgVoices != value) { Item *item = getItem(kItemIdToggleVoices); - _cfgVoices = value; + _vm->_cfgVoices = value; restoreRect(item->rect.left, item->rect.top, item->rect.width() + 1, item->rect.height() - 2); - setItemCaption(item, _vm->getSysString(_cfgVoices ? kStrVoicesOn : kStrVoicesOff)); + setItemCaption(item, _vm->getSysString(_vm->_cfgVoices ? kStrVoicesOn : kStrVoicesOff)); drawItem(kItemIdToggleVoices, true); + ConfMan.setBool("speech_mute", !value); } } void MenuSystem::drawVolumeBar(ItemID itemID) { int w = 440, y, volume; char text[21]; - + switch (itemID) { - case kItemIdMaster: + case kItemIdMaster: // unused in ScummVM, always 20 y = 130 + 25 * 0; - volume = _cfgMasterVolume; + volume = 20; break; case kItemIdVoices: y = 130 + 25 * 1; - volume = _cfgVoicesVolume; + volume = _vm->_cfgVoicesVolume; break; case kItemIdMusic: y = 130 + 25 * 2; - volume = _cfgMusicVolume; + volume = _vm->_cfgMusicVolume; break; case kItemIdSoundFX: y = 130 + 25 * 3; - volume = _cfgSoundFXVolume; + volume = _vm->_cfgSoundFXVolume; break; - case kItemIdBackground: + case kItemIdBackground: // unused in ScummVM, always 20 y = 130 + 25 * 4; - volume = _cfgBackgroundVolume; + volume = 20; break; default: return; @@ -568,46 +568,48 @@ void MenuSystem::drawVolumeBar(ItemID itemID) { Font font(_vm->_res->load(_vm->_screen->getFontResIndex(1))->data); restoreRect(390, y - font.getHeight(), 100, 25); - + for (int i = 0; i < volume; i++) text[i] = '|'; text[volume] = 0; - + drawString(0, y, w, 0, 246, text); - + } void MenuSystem::changeVolumeBar(ItemID itemID, int delta) { - - int *volume, newVolume; + byte newVolume; switch (itemID) { - case kItemIdMaster: - volume = &_cfgMasterVolume; - break; case kItemIdVoices: - volume = &_cfgVoicesVolume; + _vm->_cfgVoicesVolume = CLIP(_vm->_cfgVoicesVolume + delta, 0, 20); + // Always round volume up instead of down. + newVolume = (_vm->_cfgVoicesVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20; + _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, newVolume); + ConfMan.setInt("speech_volume", newVolume); break; case kItemIdMusic: - volume = &_cfgMusicVolume; + _vm->_cfgMusicVolume = CLIP(_vm->_cfgMusicVolume + delta, 0, 20); + newVolume = (_vm->_cfgMusicVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20; + _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, newVolume); + ConfMan.setInt("music_volume", newVolume); break; case kItemIdSoundFX: - volume = &_cfgSoundFXVolume; + _vm->_cfgSoundFXVolume = CLIP(_vm->_cfgSoundFXVolume + delta, 0, 20); + newVolume = (_vm->_cfgSoundFXVolume * Audio::Mixer::kMaxChannelVolume + 19) / 20; + _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, newVolume); + ConfMan.setInt("sfx_volume", newVolume); break; + case kItemIdMaster: case kItemIdBackground: - volume = &_cfgBackgroundVolume; + // unused in ScummVM break; default: return; } - newVolume = CLIP(*volume + delta, 0, 20); - - if (newVolume != *volume) { - *volume = newVolume; - drawVolumeBar(itemID); - } - + _vm->syncSoundSettings(); + drawVolumeBar(itemID); } } // End of namespace Toltecs diff --git a/engines/toltecs/menu.h b/engines/toltecs/menu.h index 3e2c2da8d9..a72205c2e5 100644 --- a/engines/toltecs/menu.h +++ b/engines/toltecs/menu.h @@ -29,14 +29,6 @@ namespace Toltecs { -enum MenuID { - kMenuIdNone, - kMenuIdMain, - kMenuIdSave, - kMenuIdLoad, - kMenuIdVolumes -}; - enum ItemID { kItemIdNone, // Main menu @@ -85,10 +77,10 @@ public: MenuSystem(ToltecsEngine *vm); ~MenuSystem(); - int run(); + int run(MenuID menuId); void update(); void handleEvents(); - + protected: struct Item { @@ -99,7 +91,7 @@ protected: int x, y, w; uint fontNum; }; - + struct SavegameItem { int _slotNum; Common::String _description; @@ -124,9 +116,6 @@ protected: Common::Array<Item> _items; Common::Array<SavegameItem> _savegames; - - bool _cfgText, _cfgVoices; - int _cfgMasterVolume, _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume, _cfgBackgroundVolume; void addClickTextItem(ItemID id, int x, int y, int w, uint fontNum, const char *caption, byte defaultColor, byte activeColor); @@ -134,13 +123,13 @@ protected: void handleMouseMove(int x, int y); void handleMouseClick(int x, int y); void handleKeyDown(const Common::KeyState& kbd); - + ItemID findItemAt(int x, int y); Item *getItem(ItemID id); void setItemCaption(Item *item, const char *caption); void initMenu(MenuID menuID); - + void enterItem(ItemID id); void leaveItem(ItemID id); void clickItem(ItemID id); diff --git a/engines/toltecs/microtiles.cpp b/engines/toltecs/microtiles.cpp index 0b61ac38a5..60e65bdaf3 100644 --- a/engines/toltecs/microtiles.cpp +++ b/engines/toltecs/microtiles.cpp @@ -138,7 +138,7 @@ Common::Rect * MicroTileArray::getRectangles(int *num_rects, int min_x, int min_ x0 = CLIP (x0, min_x, max_x); y0 = CLIP (y0, min_y, max_y); y1 = CLIP (y1, min_y, max_y); - + // FIXME: Why is the following code in an #if block? #if 1 start = i; diff --git a/engines/toltecs/movie.cpp b/engines/toltecs/movie.cpp index 76d42ebf0a..35accb5d93 100644 --- a/engines/toltecs/movie.cpp +++ b/engines/toltecs/movie.cpp @@ -61,7 +61,7 @@ void MoviePlayer::playMovie(uint resIndex) { int16 savedCameraY = _vm->_cameraY; int16 savedGuiHeight = _vm->_guiHeight; byte moviePalette[768]; - + _vm->_isSaveAllowed = false; memset(moviePalette, 0, sizeof(moviePalette)); @@ -78,7 +78,7 @@ void MoviePlayer::playMovie(uint resIndex) { _vm->_arc->readUint32LE(); _vm->_arc->readUint32LE(); _framesPerSoundChunk = _vm->_arc->readUint32LE(); - _vm->_arc->readUint32LE(); + int rate = _vm->_arc->readUint32LE(); _vm->_sceneWidth = 640; _vm->_sceneHeight = 400; @@ -87,7 +87,7 @@ void MoviePlayer::playMovie(uint resIndex) { _vm->_cameraY = 0; _vm->_guiHeight = 0; - _audioStream = Audio::makeQueuingAudioStream(22050, false); + _audioStream = Audio::makeQueuingAudioStream(rate, false); _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle, _audioStream); @@ -96,49 +96,52 @@ void MoviePlayer::playMovie(uint resIndex) { fetchAudioChunks(); - uint32 lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle); + byte *chunkBuffer = NULL; + uint32 chunkBufferSize = 0; + uint32 frame = 0; while (_chunkCount--) { - byte chunkType = _vm->_arc->readByte(); uint32 chunkSize = _vm->_arc->readUint32LE(); - byte *chunkBuffer = NULL; - uint32 movieOffset; debug(0, "chunkType = %d; chunkSize = %d", chunkType, chunkSize); - + // Skip audio chunks - we've already queued them in // fetchAudioChunks() above if (chunkType == kChunkAudio) { _vm->_arc->skip(chunkSize); } else { - chunkBuffer = new byte[chunkSize]; + // Only reallocate the chunk buffer if the new chunk is bigger + if (chunkSize > chunkBufferSize) { + delete[] chunkBuffer; + chunkBuffer = new byte[chunkSize]; + chunkBufferSize = chunkSize; + } + _vm->_arc->read(chunkBuffer, chunkSize); } - movieOffset = _vm->_arc->pos(); - switch (chunkType) { case kChunkFirstImage: case kChunkSubsequentImages: unpackRle(chunkBuffer, _vm->_screen->_backScreen); - // TODO: Rework this - _vm->_screen->updateShakeScreen(); _vm->_screen->_fullRefresh = true; - _vm->updateInput(); - _vm->drawScreen(); _soundChunkFramesLeft--; if (_soundChunkFramesLeft <= _framesPerSoundChunk) { fetchAudioChunks(); } - while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < lastTime + 111) { - g_system->delayMillis(10); + while (_vm->_mixer->getSoundElapsedTime(_audioStreamHandle) < (1000 * frame) / 9) { + if (_vm->_screen->_shakeActive && _vm->_screen->updateShakeScreen()) { + _vm->_screen->_fullRefresh = true; + } + _vm->updateInput(); + _vm->drawScreen(); + // Note: drawScreen() calls delayMillis() } - lastTime = _vm->_mixer->getSoundElapsedTime(_audioStreamHandle); - + frame++; break; case kChunkPalette: unpackPalette(chunkBuffer, moviePalette, 256, 3); @@ -150,10 +153,10 @@ void MoviePlayer::playMovie(uint resIndex) { // Already processed break; case kChunkShowSubtitle: - // TODO: Check if the text is a subtitle (last character == 0xFE). - // If so, don't show it if text display is disabled. - memcpy(_vm->_script->getSlotData(subtitleSlot), chunkBuffer, chunkSize); - _vm->_screen->updateTalkText(subtitleSlot, 0); + if (_vm->_cfgText) { + memcpy(_vm->_script->getSlotData(subtitleSlot), chunkBuffer, chunkSize); + _vm->_screen->updateTalkText(subtitleSlot, 0); + } break; case kChunkShakeScreen: // start/stop shakescreen effect if (chunkBuffer[0] == 0xFF) @@ -176,20 +179,17 @@ void MoviePlayer::playMovie(uint resIndex) { error("MoviePlayer::playMovie(%04X) Unknown chunk type %d at %08X", resIndex, chunkType, _vm->_arc->pos() - 5 - chunkSize); } - delete[] chunkBuffer; - - _vm->_arc->seek(movieOffset, SEEK_SET); - if (!handleInput()) break; - } + delete[] chunkBuffer; + _audioStream->finish(); _vm->_mixer->stopHandle(_audioStreamHandle); _vm->_arc->closeResource(); - + debug(0, "playMovie() done"); _vm->_sceneWidth = savedSceneWidth; diff --git a/engines/toltecs/movie.h b/engines/toltecs/movie.h index aecfac240f..8fa48975d7 100644 --- a/engines/toltecs/movie.h +++ b/engines/toltecs/movie.h @@ -36,7 +36,7 @@ public: ~MoviePlayer(); void playMovie(uint resIndex); - + protected: ToltecsEngine *_vm; Audio::QueuingAudioStream *_audioStream; @@ -47,11 +47,11 @@ protected: void unpackPalette(byte *source, byte *dest, int elemCount, int elemSize); void unpackRle(byte *source, byte *dest); - + void fetchAudioChunks(); - + bool handleInput(); - + }; } // End of namespace Toltecs diff --git a/engines/toltecs/music.cpp b/engines/toltecs/music.cpp index c322961077..830e4a97da 100644 --- a/engines/toltecs/music.cpp +++ b/engines/toltecs/music.cpp @@ -20,15 +20,13 @@ * */ -// FIXME: This code is taken from SAGA and needs more work (e.g. setVolume). +#include "audio/midiparser.h" +#include "common/textconsole.h" #include "toltecs/toltecs.h" #include "toltecs/music.h" #include "toltecs/resource.h" -#include "audio/midiparser.h" -#include "common/textconsole.h" - namespace Toltecs { MusicPlayer::MusicPlayer(bool isGM) : _isGM(isGM), _buffer(NULL) { @@ -62,7 +60,7 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) { memcpy(_buffer, data, size); MidiParser *parser; - + if (!memcmp(data, "FORM", 4)) parser = MidiParser::createParser_XMIDI(NULL); else @@ -77,7 +75,7 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) { _parser = parser; - setVolume(127); + syncVolume(); _isLooping = loop; _isPlaying = true; @@ -86,16 +84,6 @@ void MusicPlayer::playMIDI(const byte *data, uint32 size, bool loop) { } } -void MusicPlayer::pause() { - setVolume(-1); - _isPlaying = false; -} - -void MusicPlayer::resume() { - setVolume(127); - _isPlaying = true; -} - void MusicPlayer::stopAndClear() { Common::StackLock lock(_mutex); stop(); diff --git a/engines/toltecs/music.h b/engines/toltecs/music.h index 79df1ea2f5..8d364dbb9f 100644 --- a/engines/toltecs/music.h +++ b/engines/toltecs/music.h @@ -37,8 +37,6 @@ public: MusicPlayer(bool isGM = true); void playMIDI(const byte *data, uint32 size, bool loop = false); - void pause(); - void resume(); void stopAndClear(); // MidiDriver_BASE interface implementation diff --git a/engines/toltecs/palette.cpp b/engines/toltecs/palette.cpp index 706218e0ba..74683c6d7a 100644 --- a/engines/toltecs/palette.cpp +++ b/engines/toltecs/palette.cpp @@ -31,7 +31,7 @@ namespace Toltecs { Palette::Palette(ToltecsEngine *vm) : _vm(vm) { clearFragments(); - + memset(_colorTransTable, 0, sizeof(_colorTransTable)); } @@ -81,7 +81,7 @@ void Palette::setDeltaPalette(byte *palette, byte mask, int8 deltaValue, int16 c if (mask & 4) colors[index * 3 + 2] = CLIP<int>(rgb + deltaValue, 0, 63) << 2; index++; } - + debug(0, "startIndex = %d; colorCount = %d", startIndex, colorCount); _vm->_system->getPaletteManager()->setPalette((const byte *)colors, 0, 256); @@ -101,9 +101,9 @@ void Palette::addFragment(uint resIndex, int16 id) { Resource *fragmentResource = _vm->_res->load(resIndex); byte count = fragmentResource->size / 3; - + memcpy(&_mainPalette[_fragmentIndex * 3], fragmentResource->data, count * 3); - + PaletteFragment fragment; fragment.id = id; fragment.index = _fragmentIndex; @@ -126,7 +126,7 @@ uint16 Palette::findFragment(int16 id) { break; } } - + debug(0, "Palette::findFragment() result = %04X", result); return result; @@ -140,9 +140,9 @@ void Palette::clearFragments() { void Palette::buildColorTransTable(byte limit, int8 deltaValue, byte mask) { byte r = 0, g = 0, b = 0; - + mask &= 7; - + for (int i = 0; i < 256; i++) { if (deltaValue < 0) { @@ -161,7 +161,7 @@ void Palette::buildColorTransTable(byte limit, int8 deltaValue, byte mask) { b -= deltaValue; } } - + int bestIndex = 0; uint16 bestMatch = 0xFFFF; @@ -174,7 +174,7 @@ void Palette::buildColorTransTable(byte limit, int8 deltaValue, byte mask) { bestIndex = j; } } - + _colorTransTable[i] = bestIndex; } diff --git a/engines/toltecs/palette.h b/engines/toltecs/palette.h index 7bcf06e027..570f51777e 100644 --- a/engines/toltecs/palette.h +++ b/engines/toltecs/palette.h @@ -66,7 +66,7 @@ protected: int16 id; byte index, count; }; - + typedef Common::Array<PaletteFragment> PaletteFragmentArray; ToltecsEngine *_vm; diff --git a/engines/toltecs/render.cpp b/engines/toltecs/render.cpp index 3f5356493e..4c41e6ce00 100644 --- a/engines/toltecs/render.cpp +++ b/engines/toltecs/render.cpp @@ -114,7 +114,7 @@ void RenderQueue::addMask(SegmapMaskRect &mask) { void RenderQueue::update() { bool doFullRefresh = _vm->_screen->_fullRefresh; - + _updateUta->clear(); if (!doFullRefresh) { @@ -166,7 +166,7 @@ void RenderQueue::update() { for (RenderQueueArray::iterator iter = _currQueue->begin(); iter != _currQueue->end(); iter++) { const RenderQueueItem *item = &(*iter); - + if (item->flags == kRefresh || doFullRefresh) { switch (item->type) { @@ -200,7 +200,7 @@ void RenderQueue::update() { SWAP(_currQueue, _prevQueue); _currQueue->clear(); - + } void RenderQueue::clear() { @@ -249,16 +249,16 @@ bool RenderQueue::hasItemChanged(const RenderQueueItem &item1, const RenderQueue if (item1.type != item2.type) return true; - + if (item1.rect.left != item2.rect.left || item1.rect.top != item2.rect.top || item1.rect.right != item2.rect.right || item1.rect.bottom != item2.rect.bottom) return true; - + if (item1.type == kText && item1.text.color != item2.text.color) return true; - + return false; } @@ -268,7 +268,7 @@ void RenderQueue::invalidateItemsByRect(const Common::Rect &rect, const RenderQu if (item != subItem && subItem->flags == kUnchanged && rect.intersects(subItem->rect)) { - + subItem->flags = kRefresh; invalidateItemsByRect(subItem->rect, subItem); } diff --git a/engines/toltecs/render.h b/engines/toltecs/render.h index bb9ec29959..59d7a3ddb9 100644 --- a/engines/toltecs/render.h +++ b/engines/toltecs/render.h @@ -75,7 +75,7 @@ public: void addMask(SegmapMaskRect &mask); void update(); void clear(); - + protected: typedef Common::List<RenderQueueItem> RenderQueueArray; @@ -87,7 +87,7 @@ protected: RenderQueueItem *findItemInQueue(RenderQueueArray *queue, const RenderQueueItem &item); bool hasItemChanged(const RenderQueueItem &item1, const RenderQueueItem &item2); void invalidateItemsByRect(const Common::Rect &rect, const RenderQueueItem *item); - + void addDirtyRect(const Common::Rect &rect); void restoreDirtyBackground(); void updateDirtyRects(); diff --git a/engines/toltecs/resource.cpp b/engines/toltecs/resource.cpp index b95e0444b1..0b9f7c8fcd 100644 --- a/engines/toltecs/resource.cpp +++ b/engines/toltecs/resource.cpp @@ -66,7 +66,7 @@ void ArchiveReader::dump(uint resIndex, const char *prefix) { byte *data = new byte[resourceSize]; Common::String fn; - + if (prefix) fn = Common::String::format("%s_%04X.0", prefix, resIndex); else @@ -117,11 +117,11 @@ Resource *ResourceCache::load(uint resIndex) { resItem->data = new byte[resItem->size]; _vm->_arc->read(resItem->data, resItem->size); _vm->_arc->closeResource(); - + _cache[resIndex] = resItem; - + return resItem; - + } } diff --git a/engines/toltecs/saveload.cpp b/engines/toltecs/saveload.cpp index c24d2149b0..6c195a34c2 100644 --- a/engines/toltecs/saveload.cpp +++ b/engines/toltecs/saveload.cpp @@ -36,12 +36,11 @@ namespace Toltecs { /* TODO: - - Save with F7; Load with F9 - Saving during an animation (AnimationPlayer) is not working correctly yet - Maybe switch to SCUMM/Tinsel serialization approach? */ -#define TOLTECS_SAVEGAME_VERSION 3 +#define TOLTECS_SAVEGAME_VERSION 4 ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { @@ -93,7 +92,7 @@ void ToltecsEngine::savegame(const char *filename, const char *description) { byte descriptionLen = strlen(description); out->writeByte(descriptionLen); out->write(description, descriptionLen); - + Graphics::saveThumbnail(*out); // Not used yet, reserved for future usage @@ -141,8 +140,8 @@ void ToltecsEngine::savegame(const char *filename, const char *description) { } void ToltecsEngine::loadgame(const char *filename) { - Common::InSaveFile *in; - if (!(in = g_system->getSavefileManager()->openForLoading(filename))) { + Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename); + if (!in) { warning("Can't open file '%s', game not loaded", filename); return; } @@ -150,13 +149,13 @@ void ToltecsEngine::loadgame(const char *filename) { SaveHeader header; kReadSaveHeaderError errorCode = readSaveHeader(in, false, header); - + if (errorCode != kRSHENoError) { warning("Error loading savegame '%s'", filename); delete in; return; } - + _sound->stopAll(); _music->stopSequence(); g_engine->setTotalPlayTime(header.playTime * 1000); @@ -182,7 +181,7 @@ void ToltecsEngine::loadgame(const char *filename) { _mouseX = in->readUint16LE(); _mouseY = in->readUint16LE(); _mouseDisabled = in->readUint16LE(); - + _system->warpMouse(_mouseX, _mouseY); _system->showMouse(_mouseDisabled == 0); @@ -191,7 +190,7 @@ void ToltecsEngine::loadgame(const char *filename) { _anim->loadState(in); _screen->loadState(in); if (header.version >= 2) - _sound->loadState(in); + _sound->loadState(in, header.version); if (header.version >= 3) _music->loadState(in); diff --git a/engines/toltecs/screen.cpp b/engines/toltecs/screen.cpp index 634917a7b1..c8d6740b02 100644 --- a/engines/toltecs/screen.cpp +++ b/engines/toltecs/screen.cpp @@ -43,9 +43,11 @@ Screen::Screen(ToltecsEngine *vm) : _vm(vm) { // Screen shaking _shakeActive = false; + _shakeTime = 0; _shakeCounterInit = 0; _shakeCounter = 0; _shakePos = 0; + _shakeTime = 0; // Verb line _verbLineNum = 0; @@ -73,7 +75,7 @@ Screen::~Screen() { delete[] _frontScreen; delete[] _backScreen; - + delete _renderQueue; } @@ -129,7 +131,7 @@ void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) { byte *dest = _frontScreen + x + (y + _vm->_cameraHeight) * 640; //debug(0, "Screen::drawGuiImage() x = %d; y = %d; w = %d; h = %d; resIndex = %d", x, y, width, height, resIndex); - + while (workHeight > 0) { int count = 1; byte pixel = *imageData++; @@ -156,6 +158,7 @@ void Screen::drawGuiImage(int16 x, int16 y, uint resIndex) { void Screen::startShakeScreen(int16 shakeCounter) { _shakeActive = true; + _shakeTime = 0; _shakeCounterInit = shakeCounter; _shakeCounter = shakeCounter; _shakePos = 0; @@ -166,15 +169,19 @@ void Screen::stopShakeScreen() { _vm->_system->setShakePos(0); } -void Screen::updateShakeScreen() { - if (_shakeActive) { +bool Screen::updateShakeScreen() { + // Assume shaking happens no more often than 50 times per second + if (_shakeActive && _vm->_system->getMillis() - _shakeTime >= 20) { + _shakeTime = _vm->_system->getMillis(); _shakeCounter--; if (_shakeCounter == 0) { _shakeCounter = _shakeCounterInit; _shakePos ^= 8; _vm->_system->setShakePos(_shakePos); + return true; } } + return false; } void Screen::addStaticSprite(byte *spriteItem) { @@ -247,7 +254,7 @@ void Screen::addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, i } else { loopNum |= 0x8000; } - + WRITE_LE_UINT16(spriteItem + 0, loopNum); WRITE_LE_UINT16(spriteItem + 4, frameNum); @@ -308,9 +315,9 @@ void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) { wrapState.len2 = 0; y = _verbLineY; - + memset(wrapState.textBuffer, 0, sizeof(wrapState.textBuffer)); - + for (int16 i = 0; i <= _verbLineNum; i++) { wrapState.sourceString = _vm->_script->getSlotData(_verbLineItems[i].slotIndex) + _verbLineItems[i].slotOffset; len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState); @@ -331,19 +338,19 @@ void Screen::updateVerbLine(int16 slotIndex, int16 slotOffset) { wrapState.sourceString++; wrapState.len1 -= len; wrapState.len2 = len + 1; - + drawGuiText(_verbLineX - 1 - (wrapState.width / 2), y, 0xF9, 0xFF, _fontResIndexArray[0], wrapState); wrapState.destString = wrapState.textBuffer; wrapState.width = 0; len = wrapGuiText(_fontResIndexArray[0], _verbLineWidth, wrapState); wrapState.len1 += len; - + y += 9; } y += 9; } - + wrapState.len1 -= len; wrapState.len2 = len; @@ -463,13 +470,12 @@ void Screen::addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 textRect->x = CLIP<int16>(x - width / 2, 0, 640); item->lineCount++; } - + y += font.getHeight() - 1; } void Screen::addTalkTextItemsToRenderQueue() { - for (int16 i = 0; i <= _talkTextItemNum; i++) { TalkTextItem *item = &_talkTextItems[i]; byte *text = _vm->_script->getSlotData(item->slotIndex) + item->slotOffset; @@ -482,14 +488,15 @@ void Screen::addTalkTextItemsToRenderQueue() { if (item->duration < 0) item->duration = 0; + if (!_vm->_cfgText) + return; + for (byte j = 0; j < item->lineCount; j++) { - _renderQueue->addText(item->lines[j].x, item->lines[j].y, item->color, _fontResIndexArray[item->fontNum], - text, item->lines[j].length); + _renderQueue->addText(item->lines[j].x, item->lines[j].y, item->color, + _fontResIndexArray[item->fontNum], text, item->lines[j].length); text += item->lines[j].length; } - } - } int16 Screen::getTalkTextDuration() { @@ -559,7 +566,7 @@ int16 Screen::wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wra Font font(_vm->_res->load(fontResIndex)->data); int16 len = 0; - + while (*wrapState.sourceString >= 0x20 && *wrapState.sourceString < 0xF0) { byte ch = *wrapState.sourceString; byte charWidth; @@ -573,9 +580,9 @@ int16 Screen::wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wra wrapState.width += charWidth; *wrapState.destString++ = *wrapState.sourceString++; } - + return len; - + } void Screen::drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex, GuiTextWrapState &wrapState) { @@ -765,7 +772,7 @@ void Screen::loadState(Common::ReadStream *in) { _verbLineItems[i].slotIndex = in->readUint16LE(); _verbLineItems[i].slotOffset = in->readUint16LE(); } - + // Load talk text items _talkTextX = in->readUint16LE(); _talkTextY = in->readUint16LE(); @@ -786,7 +793,7 @@ void Screen::loadState(Common::ReadStream *in) { _talkTextItems[i].lines[j].length = in->readUint16LE(); } } - + // Load GUI bitmap { byte *gui = _frontScreen + _vm->_cameraHeight * 640; diff --git a/engines/toltecs/screen.h b/engines/toltecs/screen.h index 988f59c840..788cde50c6 100644 --- a/engines/toltecs/screen.h +++ b/engines/toltecs/screen.h @@ -154,15 +154,15 @@ public: ~Screen(); void unpackRle(byte *source, byte *dest, uint16 width, uint16 height); - + void loadMouseCursor(uint resIndex); - + void drawGuiImage(int16 x, int16 y, uint resIndex); - + void startShakeScreen(int16 shakeCounter); void stopShakeScreen(); - void updateShakeScreen(); - + bool updateShakeScreen(); + // Sprite list void addStaticSprite(byte *spriteItem); void addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode); @@ -175,7 +175,7 @@ public: // Verb line void updateVerbLine(int16 slotIndex, int16 slotOffset); - + // Talk text void updateTalkText(int16 slotIndex, int16 slotOffset); void addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item); @@ -207,7 +207,7 @@ public: int16 slotIndex; int16 slotOffset; }; - + struct Rect { int16 x, y, width, height; }; @@ -215,12 +215,13 @@ public: ToltecsEngine *_vm; byte *_frontScreen, *_backScreen; - + uint _fontResIndexArray[10]; byte _fontColor1, _fontColor2; // Screen shaking bool _shakeActive; + uint32 _shakeTime; int16 _shakeCounterInit, _shakeCounter; int _shakePos; @@ -229,7 +230,7 @@ public: VerbLineItem _verbLineItems[8]; int16 _verbLineX, _verbLineY, _verbLineWidth; int16 _verbLineCount; - + // Talk text int16 _talkTextX, _talkTextY; int16 _talkTextMaxWidth; diff --git a/engines/toltecs/script.cpp b/engines/toltecs/script.cpp index 9683831980..5e8617bc43 100644 --- a/engines/toltecs/script.cpp +++ b/engines/toltecs/script.cpp @@ -183,10 +183,7 @@ void ScriptInterpreter::setMainScript(uint slotIndex) { } void ScriptInterpreter::runScript() { - uint32 lastScreenUpdate = 0; - while (!_vm->shouldQuit()) { - if (_vm->_movieSceneFlag) _vm->_mouseButton = 0; @@ -197,7 +194,7 @@ void ScriptInterpreter::runScript() { _vm->saveGameState(_vm->_saveLoadSlot, _vm->_saveLoadDescription); _vm->_saveLoadRequested = 0; } - + if (_switchLocalDataNear) { _switchLocalDataNear = false; _localData = getSlotData(_regs.reg4); @@ -214,20 +211,10 @@ void ScriptInterpreter::runScript() { _localData = _stack + 2; _switchLocalDataNear = true; } - + byte opcode = readByte(); execOpcode(opcode); - - // Update the screen at semi-regular intervals, else the mouse - // cursor will be jerky. - uint32 now = _vm->_system->getMillis(); - if (now < lastScreenUpdate || now - lastScreenUpdate > 10) { - _vm->_system->updateScreen(); - lastScreenUpdate = _vm->_system->getMillis(); - } - } - } byte ScriptInterpreter::readByte() { @@ -547,7 +534,7 @@ const char *getVarName(uint variable) { int16 ScriptInterpreter::getGameVar(uint variable) { debug(0, "ScriptInterpreter::getGameVar(%d{%s})", variable, getVarName(variable)); - + switch (variable) { case 0: return _vm->_mouseDisabled; case 1: return _vm->_mouseY; @@ -579,7 +566,7 @@ int16 ScriptInterpreter::getGameVar(uint variable) { void ScriptInterpreter::setGameVar(uint variable, int16 value) { debug(0, "ScriptInterpreter::setGameVar(%d{%s}, %d)", variable, getVarName(variable), value); - + switch (variable) { case 0: _vm->_mouseDisabled = value; @@ -718,7 +705,7 @@ void ScriptInterpreter::saveState(Common::WriteStream *out) { // Save stack out->write(_stack, kScriptStackSize); out->writeUint16LE(_savedSp); - + // Save IP out->writeUint16LE((int16)(_code - getSlotData(_regs.reg4))); @@ -1046,29 +1033,21 @@ void ScriptInterpreter::sfHandleInput() { Only scancodes known to be used (so far) are converted */ switch (_vm->_keyState.keycode) { - case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_ESCAPE: keyCode = 1; break; case Common::KEYCODE_F10: keyCode = 68; break; default: - break; + break; } } localWrite16(varOfs, keyCode); } void ScriptInterpreter::sfRunOptionsScreen() { - _vm->_screen->loadMouseCursor(12); - _vm->_palette->loadAddPalette(9, 224); - _vm->_palette->setDeltaPalette(_vm->_palette->getMainPalette(), 7, 0, 31, 224); - _vm->_screen->finishTalkTextItems(); - _vm->_screen->clearSprites(); - CursorMan.showMouse(true); - _vm->_menuSystem->run(); - _vm->_keyState.reset(); - _switchLocalDataNear = true; + _vm->showMenu(kMenuIdMain); } /* NOTE: The opcodes sfPrecacheSprites, sfPrecacheSounds1, sfPrecacheSounds2 and @@ -1076,7 +1055,7 @@ void ScriptInterpreter::sfRunOptionsScreen() { of data so the game doesn't stall while playing (due to the slow speed of CD-Drives back then). This is not needed in ScummVM since all supported systems are fast enough to load data in-game. */ - + void ScriptInterpreter::sfPrecacheSprites() { // See note above } @@ -1102,7 +1081,9 @@ void ScriptInterpreter::sfSaveStackPtr() { } void ScriptInterpreter::sfPlayMovie() { + CursorMan.showMouse(false); _vm->_moviePlayer->playMovie(arg16(3)); + CursorMan.showMouse(true); } } // End of namespace Toltecs diff --git a/engines/toltecs/script.h b/engines/toltecs/script.h index 0c1898c525..89dca4598f 100644 --- a/engines/toltecs/script.h +++ b/engines/toltecs/script.h @@ -56,6 +56,8 @@ public: void saveState(Common::WriteStream *out); void loadState(Common::ReadStream *in); + void setSwitchLocalDataNear(bool newValue) { _switchLocalDataNear = newValue; } + protected: struct ScriptRegs { @@ -88,13 +90,13 @@ protected: bool _cmpBitTest; ScriptSlot _slots[kMaxScriptSlots]; - + ScriptRegs _regs; int16 _savedSp; byte readByte(); int16 readInt16(); - + void execOpcode(byte opcode); void setupScriptFunctions(); diff --git a/engines/toltecs/segmap.cpp b/engines/toltecs/segmap.cpp index f7d806c67b..b06c0af675 100644 --- a/engines/toltecs/segmap.cpp +++ b/engines/toltecs/segmap.cpp @@ -48,7 +48,7 @@ void SegmentMap::load(byte *source) { uint16 maskRectCount = READ_LE_UINT16(source); source += 2; uint16 maskRectDataSize = maskRectCount * 12 + 2; - + debug(0, "SegmentMap::load() maskRectCount = %d", maskRectCount); for (uint16 i = 0; i < maskRectCount; i++) { @@ -74,25 +74,25 @@ void SegmentMap::load(byte *source) { // Load path rects source += 2; // skip rects array size - + uint16 pathRectCount = READ_LE_UINT16(source); source += 2; - + debug(0, "SegmentMap::load() pathRectCount = %d", pathRectCount); - + for (uint16 i = 0; i < pathRectCount; i++) { SegmapPathRect pathRect; pathRect.y1 = READ_LE_UINT16(source); pathRect.x1 = READ_LE_UINT16(source + 2); pathRect.y2 = pathRect.y1 + READ_LE_UINT16(source + 4); pathRect.x2 = pathRect.x1 + READ_LE_UINT16(source + 6); - + debug(0, "SegmentMap::load() (%d, %d, %d, %d)", pathRect.x1, pathRect.y1, pathRect.x2, pathRect.y2); source += 8; _pathRects.push_back(pathRect); } - + // Load info rects source += 2; // skip rects array size @@ -141,7 +141,7 @@ void SegmentMap::adjustPathPoint(int16 &x, int16 &y) { uint32 minDistance = 0xFFFFFFFF, distance; int16 adjustedX = 0, adjustedY = 0, x2, y2; - + for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) { if (x >= _pathRects[rectIndex].x1 && x < _pathRects[rectIndex].x2) { @@ -174,7 +174,7 @@ void SegmentMap::adjustPathPoint(int16 &x, int16 &y) { } } - + x = adjustedX; y = adjustedY; @@ -318,7 +318,7 @@ void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 so pointsArray[0] = 0; pointsArray[1] = TO_LE_16(_pathNodesCount + 1); } - + debug(0, "SegmentMap::findPath() count = %d", FROM_LE_16(pointsArray[1])); #if 0 // DEBUG: Draw the path we found @@ -335,7 +335,7 @@ void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 so sy = y; } #endif - + } int8 SegmentMap::getScalingAtPoint(int16 x, int16 y) { diff --git a/engines/toltecs/segmap.h b/engines/toltecs/segmap.h index 30182a6b71..dda0edeb88 100644 --- a/engines/toltecs/segmap.h +++ b/engines/toltecs/segmap.h @@ -61,14 +61,14 @@ public: void getRgbModifiertAtPoint(int16 x, int16 y, int16 id, byte &r, byte &g, byte &b); void addMasksToRenderQueue(); - + //protected: public: // for debugging purposes struct SegmapPathRect { int16 x1, y1, x2, y2; }; - + struct SegmapInfoRect { int16 y, x; int16 height, width; @@ -78,11 +78,11 @@ public: // for debugging purposes return py >= y && py <= y + height && px >= x && px <= x + width; } }; - + struct PathPoint { int16 y, x; }; - + typedef Common::Array<SegmapMaskRect> SegmapMaskRectArray; typedef Common::Array<SegmapPathRect> SegmapPathRectArray; typedef Common::Array<SegmapInfoRect> SegmapInfoRectArray; diff --git a/engines/toltecs/sound.cpp b/engines/toltecs/sound.cpp index c9ef00e31b..4b281392e5 100644 --- a/engines/toltecs/sound.cpp +++ b/engines/toltecs/sound.cpp @@ -34,48 +34,38 @@ namespace Toltecs { Sound::Sound(ToltecsEngine *vm) : _vm(vm) { for (int i = 0; i < kMaxChannels; i++) { - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } Sound::~Sound() { } +void Sound::clearChannel(int channel) { + channels[channel].type = kChannelTypeEmpty; + channels[channel].resIndex = -1; + channels[channel].volume = 0; + channels[channel].panning = 0; +} + void Sound::playSpeech(int16 resIndex) { debug(0, "playSpeech(%d)", resIndex); - internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0); + + if (_vm->_cfgVoices) + internalPlaySound(resIndex, kChannelTypeSpeech, 50 /*TODO*/, 0); } void Sound::playSound(int16 resIndex, int16 type, int16 volume) { - - // TODO: Use the right volumes - debug(0, "playSound(%d, %d, %d)", resIndex, type, volume); - - if (volume == -1 || type == -2) { - if (type == kChannelTypeBackground) { - internalPlaySound(resIndex, type, 50 /*TODO*/, 0); - } else { - internalPlaySound(resIndex, type, 100 /*TODO*/, 0); - } - } else { - internalPlaySound(resIndex, type, 100 /*TODO*/, 0); - } + internalPlaySound(resIndex, type, volume, 0); } void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) { - debug(0, "playSoundAtPos(%d, %d, %d)", resIndex, x, y); - int16 volume, panning = 0, deltaX = 0; - int8 scaling = _vm->_segmap->getScalingAtPoint(x, y); - - if (scaling >= 0) - volume = 50 + ABS(scaling) / 2; - else - volume = 50 - ABS(scaling) / 2; + int16 volume = 50 + ABS(_vm->_segmap->getScalingAtPoint(x, y)) / 2; + int16 panning = 0, deltaX = 0; if (_vm->_cameraX > x) deltaX = _vm->_cameraX - x; @@ -85,32 +75,31 @@ void Sound::playSoundAtPos(int16 resIndex, int16 x, int16 y) { deltaX = 600; volume = ((100 - deltaX / 6) * volume) / 100; - + if (_vm->_cameraX + 320 != x) { panning = CLIP(x - (_vm->_cameraX + 320), -381, 381) / 3; } internalPlaySound(resIndex, 1, volume, panning); - } void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning) { + // Change the game's sound volume (0 - 100) to Scummvm's scale (0 - 255) + volume = (volume == -1) ? 255 : volume * 255 / 100; if (resIndex == -1) { // Stop all sounds _vm->_mixer->stopAll(); _vm->_screen->keepTalkTextItemsAlive(); for (int i = 0; i < kMaxChannels; i++) { - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } else if (type == -2) { // Stop sounds with specified resIndex for (int i = 0; i < kMaxChannels; i++) { if (channels[i].resIndex == resIndex) { _vm->_mixer->stopHandle(channels[i].handle); - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } } else { @@ -119,7 +108,7 @@ void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 pa // Stop speech and play new sound stopSpeech(); } - + // Play new sound in empty channel int freeChannel = -1; for (int i = 0; i < kMaxChannels; i++) { @@ -128,7 +117,7 @@ void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 pa break; } } - + // If all channels are in use no new sound will be played if (freeChannel >= 0) { Resource *soundResource = _vm->_res->load(resIndex); @@ -141,19 +130,17 @@ void Sound::internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 pa channels[freeChannel].type = type; channels[freeChannel].resIndex = resIndex; + channels[freeChannel].volume = volume; + channels[freeChannel].panning = panning; - Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType; - /* - switch (type) { - } - */ + Audio::Mixer::SoundType soundType = getScummVMSoundType((SoundChannelType)type); _vm->_mixer->playStream(soundType, &channels[freeChannel].handle, - stream, -1, volume, panning); + stream, -1, volume, panning); } } - + } void Sound::updateSpeech() { @@ -170,8 +157,7 @@ void Sound::stopSpeech() { if (channels[i].type == kChannelTypeSpeech) { _vm->_mixer->stopHandle(channels[i].handle); _vm->_screen->keepTalkTextItemsAlive(); - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } } @@ -180,8 +166,7 @@ void Sound::stopAll() { for (int i = 0; i < kMaxChannels; i++) { _vm->_mixer->stopHandle(channels[i].handle); _vm->_screen->keepTalkTextItemsAlive(); - channels[i].type = kChannelTypeEmpty; - channels[i].resIndex = -1; + clearChannel(i); } } @@ -189,13 +174,22 @@ void Sound::saveState(Common::WriteStream *out) { for (int i = 0; i < kMaxChannels; i++) { out->writeSint16LE(channels[i].type); out->writeSint16LE(channels[i].resIndex); + out->writeSint16LE(channels[i].volume); + out->writeSint16LE(channels[i].panning); } } -void Sound::loadState(Common::ReadStream *in) { +void Sound::loadState(Common::ReadStream *in, int version) { for (int i = 0; i < kMaxChannels; i++) { channels[i].type = in->readSint16LE(); channels[i].resIndex = in->readSint16LE(); + if (version < 4) { + channels[i].volume = (channels[i].type == kChannelTypeBackground) ? 50 : 100; + channels[i].panning = 0; + } else { + channels[i].volume = in->readSint16LE(); + channels[i].panning = in->readSint16LE(); + } if (channels[i].type != kChannelTypeEmpty) { Resource *soundResource = _vm->_res->load(channels[i].resIndex); @@ -206,19 +200,26 @@ void Sound::loadState(Common::ReadStream *in) { DisposeAfterUse::NO), channels[i].type == kChannelTypeBackground ? 0 : 1); - Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType; - /* - switch (type) { - } - */ - - // TODO: Volume and panning - int16 volume = (channels[i].type == kChannelTypeBackground) ? 50 : 100; + Audio::Mixer::SoundType soundType = getScummVMSoundType((SoundChannelType)channels[i].type); _vm->_mixer->playStream(soundType, &channels[i].handle, - stream, -1, volume, /*panning*/0); + stream, -1, channels[i].volume, channels[i].panning); } } } +Audio::Mixer::SoundType Sound::getScummVMSoundType(SoundChannelType type) const { + switch (type) { + case kChannelTypeBackground: + case kChannelTypeSfx: + return Audio::Mixer::kSFXSoundType; + case kChannelTypeSpeech: + return Audio::Mixer::kSpeechSoundType; + break; + default: + return Audio::Mixer::kSFXSoundType; + break; + } +} + } // End of namespace Toltecs diff --git a/engines/toltecs/sound.h b/engines/toltecs/sound.h index e292d22c0f..48a6cd1318 100644 --- a/engines/toltecs/sound.h +++ b/engines/toltecs/sound.h @@ -42,6 +42,8 @@ enum SoundChannelType { struct SoundChannel { int16 resIndex; int16 type; + int16 volume; + int16 panning; Audio::SoundHandle handle; }; @@ -60,15 +62,16 @@ public: void stopAll(); void saveState(Common::WriteStream *out); - void loadState(Common::ReadStream *in); + void loadState(Common::ReadStream *in, int version); protected: ToltecsEngine *_vm; SoundChannel channels[kMaxChannels]; + void clearChannel(int channel); void internalPlaySound(int16 resIndex, int16 type, int16 volume, int16 panning); - + Audio::Mixer::SoundType getScummVMSoundType(SoundChannelType type) const; }; diff --git a/engines/toltecs/sprite.cpp b/engines/toltecs/sprite.cpp index 7a02663793..6101eb7d85 100644 --- a/engines/toltecs/sprite.cpp +++ b/engines/toltecs/sprite.cpp @@ -199,7 +199,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem sprite.frameNum = frameNum; spriteData = _vm->_res->load(drawRequest.resIndex)->data; - + if (drawRequest.flags & 0x1000) { sprite.flags |= 4; } @@ -207,7 +207,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem if (drawRequest.flags & 0x2000) { sprite.flags |= 0x10; } - + if (drawRequest.flags & 0x4000) { sprite.flags |= 0x40; } @@ -218,7 +218,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem if (spriteFrameEntry.w == 0 || spriteFrameEntry.h == 0) return false; - + sprite.offset = spriteFrameEntry.offset; sprite.width = spriteFrameEntry.w; @@ -263,12 +263,12 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem xoffs -= (xoffs * scaleValue) / 100; yoffs -= (yoffs * scaleValue) / 100; } - + } - + sprite.x -= xoffs; sprite.y -= yoffs; - + sprite.yerror = sprite.ydelta; // Now we check if the sprite needs to be clipped @@ -283,7 +283,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem sprite.height -= clipHeight; if (sprite.height <= 0) return false; - + sprite.y = _vm->_cameraY; // If the sprite is scaled @@ -311,7 +311,7 @@ bool Screen::createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem } sprite.yerror = chopHeight; } - + spriteFrameData = spriteData + sprite.offset; // Now the sprite's offset is adjusted to point to the starting line if ((sprite.flags & 0x10) == 0) { @@ -439,7 +439,7 @@ void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawIt SpriteReaderStatus status; PixelPacket packet; - + byte *destp = dest; int16 skipX = sprite.skipX; @@ -459,7 +459,7 @@ void Screen::drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawIt status = reader.readPacket(packet); } } - + if (w - packet.count < 0) packet.count = w; diff --git a/engines/toltecs/toltecs.cpp b/engines/toltecs/toltecs.cpp index 6d6c37dffd..9f3a10a03b 100644 --- a/engines/toltecs/toltecs.cpp +++ b/engines/toltecs/toltecs.cpp @@ -62,11 +62,6 @@ struct GameSettings { }; ToltecsEngine::ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { - - // Setup mixer - _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); - _rnd = new Common::RandomSource("toltecs"); } @@ -85,7 +80,7 @@ Common::Error ToltecsEngine::run() { _flag01 = 0; _saveLoadRequested = 0; - + _cameraX = 0; _cameraY = 0; _newCameraX = 0; @@ -96,7 +91,7 @@ Common::Error ToltecsEngine::run() { _sceneWidth = 0; _sceneHeight = 0; - + _doSpeech = true; _doText = true; @@ -126,17 +121,27 @@ Common::Error ToltecsEngine::run() { _moviePlayer = new MoviePlayer(this); _music = new Music(_arc); _menuSystem = new MenuSystem(this); - + _sound = new Sound(this); + _cfgText = ConfMan.getBool("subtitles"); + _cfgVoices = !ConfMan.getBool("speech_mute"); + + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + + _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mute ? 0 : ConfMan.getInt("speech_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, mute ? 0 : ConfMan.getInt("music_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mute ? 0 : ConfMan.getInt("sfx_volume")); syncSoundSettings(); CursorMan.showMouse(true); setupSysStrings(); -//#define TEST_MENU -#ifdef TEST_MENU +#if 0 + // Menu test _screen->registerFont(0, 0x0D); _screen->registerFont(1, 0x0E); _screen->loadMouseCursor(12); @@ -181,7 +186,7 @@ Common::Error ToltecsEngine::run() { delete _music; delete _moviePlayer; delete _menuSystem; - + delete _sound; return Common::kNoError; @@ -245,7 +250,7 @@ void ToltecsEngine::loadScene(uint resIndex) { _screen->_fullRefresh = true; _screen->_renderQueue->clear(); - + } void ToltecsEngine::updateScreen() { @@ -306,6 +311,7 @@ void ToltecsEngine::drawScreen() { } _system->updateScreen(); + _system->delayMillis(10); updateCamera(); } @@ -321,15 +327,14 @@ void ToltecsEngine::updateInput() { //debug("key: flags = %02X; keycode = %d", _keyState.flags, _keyState.keycode); - // FIXME: This is just for debugging switch (event.kbd.keycode) { - case Common::KEYCODE_F7: - savegame("toltecs.001", "Quicksave"); + case Common::KEYCODE_F5: + showMenu(kMenuIdSave); break; - case Common::KEYCODE_F9: - loadgame("toltecs.001"); + case Common::KEYCODE_F7: + showMenu(kMenuIdLoad); break; - case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_SPACE: // Skip current dialog line, if a dialog is active if (_screen->getTalkTextDuration() > 0) { _sound->stopSpeech(); @@ -422,7 +427,7 @@ void ToltecsEngine::setCamera(int16 x, int16 y) { _screen->finishTalkTextItems(); _screen->clearSprites(); - + _cameraX = x; _newCameraX = x; @@ -491,9 +496,9 @@ void ToltecsEngine::updateCamera() { } void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) { - + byte *scanData = _script->getSlotData(slotIndex) + slotOffset; - + while (*scanData < 0xF0) { if (*scanData == 0x19) { scanData++; @@ -506,7 +511,7 @@ void ToltecsEngine::talk(int16 slotIndex, int16 slotOffset) { } scanData++; } - + if (*scanData == 0xFE) { if (_doSpeech) { int16 resIndex = READ_LE_UINT16(scanData + 1); @@ -540,7 +545,7 @@ void ToltecsEngine::walk(byte *walkData) { walkInfo.xerror = READ_LE_UINT16(walkData + 14); walkInfo.mulValue = READ_LE_UINT16(walkData + 16); walkInfo.scaling = READ_LE_UINT16(walkData + 18); - + walkInfo.scaling = -_segmap->getScalingAtPoint(walkInfo.x, walkInfo.y); if (walkInfo.y1 < walkInfo.y2) @@ -548,7 +553,7 @@ void ToltecsEngine::walk(byte *walkData) { else ystep = 1; ydelta = ABS(walkInfo.y1 - walkInfo.y2) * _walkSpeedY; - + if (walkInfo.x1 < walkInfo.x2) xstep = -1; else @@ -611,11 +616,11 @@ void ToltecsEngine::walk(byte *walkData) { } -int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, +int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, byte *rectDataEnd) { rectData += index * itemSize; - + while (rectData < rectDataEnd) { int16 rectY = READ_LE_UINT16(rectData); if (rectY == -10) @@ -633,9 +638,32 @@ int16 ToltecsEngine::findRectAtPoint(byte *rectData, int16 x, int16 y, int16 ind index++; rectData += itemSize; } - + return -1; +} + +void ToltecsEngine::showMenu(MenuID menuId) { + _screen->loadMouseCursor(12); + _palette->loadAddPalette(9, 224); + _palette->setDeltaPalette(_palette->getMainPalette(), 7, 0, 31, 224); + _screen->finishTalkTextItems(); + _screen->clearSprites(); + CursorMan.showMouse(true); + _menuSystem->run(menuId); + _keyState.reset(); + _script->setSwitchLocalDataNear(true); +} + +void ToltecsEngine::syncSoundSettings() { + Engine::syncSoundSettings(); + + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + _cfgVoicesVolume = (mute ? 0 : ConfMan.getInt("speech_volume")) * 20 / Audio::Mixer::kMaxChannelVolume; + _cfgMusicVolume = (mute ? 0 : ConfMan.getInt("music_volume")) * 20 / Audio::Mixer::kMaxChannelVolume; + _cfgSoundFXVolume = (mute ? 0 : ConfMan.getInt("sfx_volume")) * 20 / Audio::Mixer::kMaxChannelVolume; } } // End of namespace Toltecs diff --git a/engines/toltecs/toltecs.h b/engines/toltecs/toltecs.h index efa1f9d13a..b95a4f77cb 100644 --- a/engines/toltecs/toltecs.h +++ b/engines/toltecs/toltecs.h @@ -81,6 +81,14 @@ enum SysString { kSysStrCount }; +enum MenuID { + kMenuIdNone, + kMenuIdMain, + kMenuIdSave, + kMenuIdLoad, + kMenuIdVolumes +}; + class ToltecsEngine : public ::Engine { Common::KeyState _keyPressed; @@ -99,6 +107,7 @@ public: uint32 getFeatures() const; Common::Language getLanguage() const; const Common::String& getTargetName() const { return _targetName; } + void syncSoundSettings(); void setupSysStrings(); void requestSavegame(int slotNum, Common::String &description); @@ -119,14 +128,18 @@ public: void scrollCameraLeft(int16 delta); void scrollCameraRight(int16 delta); void updateCamera(); - + + void showMenu(MenuID menuId); + void talk(int16 slotIndex, int16 slotOffset); void walk(byte *walkData); - - int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, + + int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize, byte *rectDataEnd); + int _cfgVoicesVolume, _cfgMusicVolume, _cfgSoundFXVolume; + bool _cfgText, _cfgVoices; public: AnimationPlayer *_anim; @@ -150,7 +163,7 @@ public: uint _sceneResIndex; int16 _sceneWidth, _sceneHeight; - + int _counter01, _counter02; bool _movieSceneFlag; byte _flag01; @@ -161,7 +174,7 @@ public: int16 _guiHeight; bool _doSpeech, _doText; - + int16 _walkSpeedY, _walkSpeedX; Common::KeyState _keyState; diff --git a/engines/tony/custom.cpp b/engines/tony/custom.cpp index f0a9197c6d..23c655e35a 100644 --- a/engines/tony/custom.cpp +++ b/engines/tony/custom.cpp @@ -41,7 +41,7 @@ namespace Tony { -const char *ambianceFile[] = { +static const char *const kAmbianceFile[] = { "None", "1.ADP", // Grilli.WAV "2.ADP", // Grilli-Ovattati.WAV @@ -52,66 +52,38 @@ const char *ambianceFile[] = { "6.ADP" // Mare1.WAV half volume }; -struct MusicFileEntry { - const char *name; - int sync; -}; - -const MusicFileEntry musicFiles[] = { - {"00.ADP", 0}, {"01.ADP", 0}, - {"02.ADP", 0}, {"03.ADP", 0}, - {"04.ADP", 0}, {"05.ADP", 0}, - {"06.ADP", 0}, {"07.ADP", 0}, - {"08.ADP", 2450}, {"09.ADP", 0}, - {"10.ADP", 0}, {"11.ADP", 0}, - {"12.ADP", 0}, {"13.ADP", 0}, - {"14.ADP", 0}, {"15.ADP", 0}, - {"16.ADP", 0}, {"17.ADP", 0}, - {"18.ADP", 0}, {"19.ADP", 0}, - {"20.ADP", 0}, {"21.ADP", 0}, - {"22.ADP", 0}, {"23.ADP", 0}, - {"24.ADP", 0}, {"25.ADP", 0}, - {"26.ADP", 0}, {"27.ADP", 0}, - {"28.ADP", 1670}, {"29.ADP", 0}, - {"30.ADP", 0}, {"31.ADP", 0}, - {"32.ADP", 2900}, {"33.ADP", 0}, - {"34.ADP", 0}, {"35.ADP", 0}, - {"36.ADP", 0}, {"37.ADP", 0}, - {"38.ADP", 0}, {"39.ADP", 0}, - {"40.ADP", 0}, {"41.ADP", 1920}, - {"42.ADP", 1560}, {"43.ADP", 1920}, - {"44.ADP", 1920}, {"45.ADP", 1920}, - {"46.ADP", 1920}, {"47.ADP", 1920}, - {"48.ADP", 1920}, {"49.ADP", 1920}, - {"50.ADP", 1920}, {"51.ADP", 1920}, - {"52.ADP", 1920}, {"53.ADP", 0}, - {"54.ADP", 0}, {"55.ADP", 0}, - {"56.ADP", 0}, {"57.ADP", 0}, - {"58.ADP", 0}, {"59.ADP", 0} +static const MusicFileEntry kMusicFiles[] = { + {"00.ADP", 0}, {"01.ADP", 0}, {"02.ADP", 0}, {"03.ADP", 0}, + {"04.ADP", 0}, {"05.ADP", 0}, {"06.ADP", 0}, {"07.ADP", 0}, + {"08.ADP", 2450}, {"09.ADP", 0}, {"10.ADP", 0}, {"11.ADP", 0}, + {"12.ADP", 0}, {"13.ADP", 0}, {"14.ADP", 0}, {"15.ADP", 0}, + {"16.ADP", 0}, {"17.ADP", 0}, {"18.ADP", 0}, {"19.ADP", 0}, + {"20.ADP", 0}, {"21.ADP", 0}, {"22.ADP", 0}, {"23.ADP", 0}, + {"24.ADP", 0}, {"25.ADP", 0}, {"26.ADP", 0}, {"27.ADP", 0}, + {"28.ADP", 1670}, {"29.ADP", 0}, {"30.ADP", 0}, {"31.ADP", 0}, + {"32.ADP", 2900}, {"33.ADP", 0}, {"34.ADP", 0}, {"35.ADP", 0}, + {"36.ADP", 0}, {"37.ADP", 0}, {"38.ADP", 0}, {"39.ADP", 0}, + {"40.ADP", 0}, {"41.ADP", 1920}, {"42.ADP", 1560}, {"43.ADP", 1920}, + {"44.ADP", 1920}, {"45.ADP", 1920}, {"46.ADP", 1920}, {"47.ADP", 1920}, + {"48.ADP", 1920}, {"49.ADP", 1920}, {"50.ADP", 1920}, {"51.ADP", 1920}, + {"52.ADP", 1920}, {"53.ADP", 0}, {"54.ADP", 0}, {"55.ADP", 0}, + {"56.ADP", 0}, {"57.ADP", 0}, {"58.ADP", 0}, {"59.ADP", 0} }; -const char *jingleFileNames[] = { - "S00.ADP", "S01.ADP", - "S02.ADP", "S03.ADP", - "S04.ADP", "S05.ADP", - "S06.ADP", "S07.ADP", - "S08.ADP", "S09.ADP", - "S10.ADP", "S11.ADP", - "S12.ADP", "S13.ADP", - "S14.ADP", "S15.ADP", - "S16.ADP", "S17.ADP", - "S18.ADP" +static const char *const kJingleFileNames[] = { + "S00.ADP", "S01.ADP", "S02.ADP", "S03.ADP", "S04.ADP", + "S05.ADP", "S06.ADP", "S07.ADP", "S08.ADP", "S09.ADP", + "S10.ADP", "S11.ADP", "S12.ADP", "S13.ADP", "S14.ADP", + "S15.ADP", "S16.ADP", "S17.ADP", "S18.ADP" }; - -void ReapplyChangedHotspot() { - int i; - for (i = 0; i < GLOBALS._curChangedHotspot; i++) +void reapplyChangedHotspot() { + for (int i = 0; i < GLOBALS._curChangedHotspot; i++) GLOBALS._loc->getItemFromCode(GLOBALS._changedHotspot[i]._dwCode)->changeHotspot(RMPoint(GLOBALS._changedHotspot[i]._nX, GLOBALS._changedHotspot[i]._nY)); } -void SaveChangedHotspot(Common::OutSaveFile *f) { +void saveChangedHotspot(Common::OutSaveFile *f) { f->writeByte(GLOBALS._curChangedHotspot); if (GLOBALS._curChangedHotspot > 0) { for (int i = 0; i < GLOBALS._curChangedHotspot; ++i) @@ -119,7 +91,7 @@ void SaveChangedHotspot(Common::OutSaveFile *f) { } } -void LoadChangedHotspot(Common::InSaveFile *f) { +void loadChangedHotspot(Common::InSaveFile *f) { GLOBALS._curChangedHotspot = f->readByte(); if (GLOBALS._curChangedHotspot > 0) { @@ -128,7 +100,6 @@ void LoadChangedHotspot(Common::InSaveFile *f) { } } - /** * Classes required for custom functions * @@ -139,14 +110,14 @@ void LoadChangedHotspot(Common::InSaveFile *f) { * AddInventory -> theEngine.AddInventory() */ -void MCharResetCodes() { +void mCharResetCodes() { for (int i = 0; i < 10; i++) GLOBALS._mCharacter[i]._item = GLOBALS._loc->getItemFromCode(GLOBALS._mCharacter[i]._code); for (int i = 0; i < 10; i++) GLOBALS._character[i]._item = GLOBALS._loc->getItemFromCode(GLOBALS._character[i]._code); } -void CharsSaveAll(Common::OutSaveFile *f) { +void charsSaveAll(Common::OutSaveFile *f) { for (int i = 0; i < 10; i++) { f->writeByte(GLOBALS._isMChar[i]); if (GLOBALS._isMChar[i]) { @@ -157,7 +128,7 @@ void CharsSaveAll(Common::OutSaveFile *f) { } } -void CharsLoadAll(Common::InSaveFile *f) { +void charsLoadAll(Common::InSaveFile *f) { for (int i = 0; i < 10; i++) { GLOBALS._isMChar[i] = f->readByte(); if (GLOBALS._isMChar[i]) @@ -167,28 +138,28 @@ void CharsLoadAll(Common::InSaveFile *f) { } } -DECLARE_CUSTOM_FUNCTION(FaceToMe)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void faceToMe(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDDOWN); } -DECLARE_CUSTOM_FUNCTION(BackToMe)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void backToMe(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDUP); } -DECLARE_CUSTOM_FUNCTION(LeftToMe)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void leftToMe(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDLEFT); } -DECLARE_CUSTOM_FUNCTION(RightToMe)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void rightToMe(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._tony->setPattern(GLOBALS._tony->PAT_STANDRIGHT); } -DECLARE_CUSTOM_FUNCTION(TonySetPerorate)(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) { +void tonySetPerorate(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) { g_vm->getEngine()->setPerorate(bStatus); } -DECLARE_CUSTOM_FUNCTION(MySleep)(CORO_PARAM, uint32 dwTime, uint32, uint32, uint32) { +void mySleep(CORO_PARAM, uint32 dwTime, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; int i; CORO_END_CONTEXT(_ctx); @@ -201,12 +172,12 @@ DECLARE_CUSTOM_FUNCTION(MySleep)(CORO_PARAM, uint32 dwTime, uint32, uint32, uint CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(SetAlwaysDisplay)(CORO_PARAM, uint32 val, uint32, uint32, uint32) { +void setAlwaysDisplay(CORO_PARAM, uint32 val, uint32, uint32, uint32) { GLOBALS._bAlwaysDisplay = (val != 0); } -DECLARE_CUSTOM_FUNCTION(SetPointer)(CORO_PARAM, uint32 dwPointer, uint32, uint32, uint32) { +void setPointer(CORO_PARAM, uint32 dwPointer, uint32, uint32, uint32) { switch (dwPointer) { case 1: GLOBALS._pointer->setSpecialPointer(GLOBALS._pointer->PTR_ARROWUP); @@ -230,7 +201,7 @@ DECLARE_CUSTOM_FUNCTION(SetPointer)(CORO_PARAM, uint32 dwPointer, uint32, uint32 } } -VoiceHeader *SearchVoiceHeader(uint32 codehi, uint32 codelo) { +VoiceHeader *searchVoiceHeader(uint32 codehi, uint32 codelo) { int code = (codehi << 16) | codelo; if (g_vm->_voices.size() == 0) @@ -245,7 +216,7 @@ VoiceHeader *SearchVoiceHeader(uint32 codehi, uint32 codelo) { } -DECLARE_CUSTOM_FUNCTION(SendTonyMessage)(CORO_PARAM, uint32 dwMessage, uint32 nX, uint32 nY, uint32) { +void sendTonyMessage(CORO_PARAM, uint32 dwMessage, uint32 nX, uint32 nY, uint32) { CORO_BEGIN_CONTEXT; RMMessage msg; int i; @@ -266,7 +237,7 @@ DECLARE_CUSTOM_FUNCTION(SendTonyMessage)(CORO_PARAM, uint32 dwMessage, uint32 nX if (!_ctx->msg.isValid()) return; - _ctx->curVoc = SearchVoiceHeader(0, dwMessage); + _ctx->curVoc = searchVoiceHeader(0, dwMessage); _ctx->voice = NULL; if (_ctx->curVoc) { // Is positioned within the database of entries beginning at the first @@ -362,12 +333,12 @@ DECLARE_CUSTOM_FUNCTION(SendTonyMessage)(CORO_PARAM, uint32 dwMessage, uint32 nX CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(ChangeBoxStatus)(CORO_PARAM, uint32 nLoc, uint32 nBox, uint32 nStatus, uint32) { +void changeBoxStatus(CORO_PARAM, uint32 nLoc, uint32 nBox, uint32 nStatus, uint32) { GLOBALS._boxes->changeBoxStatus(nLoc, nBox, nStatus); } -DECLARE_CUSTOM_FUNCTION(CustLoadLocation)(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) { +void custLoadLocation(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) { CORO_BEGIN_CONTEXT; uint32 h; CORO_END_CONTEXT(_ctx); @@ -390,7 +361,7 @@ DECLARE_CUSTOM_FUNCTION(CustLoadLocation)(CORO_PARAM, uint32 nLoc, uint32 tX, ui } -DECLARE_CUSTOM_FUNCTION(SendFullscreenMsgStart)(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) { +void sendFullscreenMsgStart(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) { CORO_BEGIN_CONTEXT; RMMessage *msg; RMGfxClearTask clear; @@ -449,7 +420,7 @@ DECLARE_CUSTOM_FUNCTION(SendFullscreenMsgStart)(CORO_PARAM, uint32 nMsg, uint32 CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(ClearScreen)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void clearScreen(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; char buf[256]; RMGfxClearTask clear; @@ -469,33 +440,33 @@ DECLARE_CUSTOM_FUNCTION(ClearScreen)(CORO_PARAM, uint32, uint32, uint32, uint32) CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(SendFullscreenMsgEnd)(CORO_PARAM, uint32 bNotEnableTony, uint32, uint32, uint32) { +void sendFullscreenMsgEnd(CORO_PARAM, uint32 bNotEnableTony, uint32, uint32, uint32) { g_vm->getEngine()->loadLocation(GLOBALS._fullScreenMessageLoc, RMPoint(GLOBALS._fullScreenMessagePt._x, GLOBALS._fullScreenMessagePt._y), RMPoint(-1, -1)); if (!bNotEnableTony) GLOBALS._tony->show(); - MCharResetCodes(); - ReapplyChangedHotspot(); + mCharResetCodes(); + reapplyChangedHotspot(); } -DECLARE_CUSTOM_FUNCTION(SendFullscreenMessage)(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) { +void sendFullscreenMessage(CORO_PARAM, uint32 nMsg, uint32 nFont, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); - CORO_INVOKE_4(SendFullscreenMsgStart, nMsg, nFont, 0, 0); - CORO_INVOKE_4(SendFullscreenMsgEnd, 0, 0, 0, 0); + CORO_INVOKE_4(sendFullscreenMsgStart, nMsg, nFont, 0, 0); + CORO_INVOKE_4(sendFullscreenMsgEnd, 0, 0, 0, 0); CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(NoBullsEye)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void noBullsEye(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._bNoBullsEye = true; } -DECLARE_CUSTOM_FUNCTION(CloseLocation)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void closeLocation(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -515,7 +486,7 @@ DECLARE_CUSTOM_FUNCTION(CloseLocation)(CORO_PARAM, uint32, uint32, uint32, uint3 } -DECLARE_CUSTOM_FUNCTION(ChangeLocation)(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) { +void changeLocation(CORO_PARAM, uint32 nLoc, uint32 tX, uint32 tY, uint32 bUseStartPos) { CORO_BEGIN_CONTEXT; uint32 h; CORO_END_CONTEXT(_ctx); @@ -543,7 +514,7 @@ DECLARE_CUSTOM_FUNCTION(ChangeLocation)(CORO_PARAM, uint32 nLoc, uint32 tX, uint if (GLOBALS._lastTappeto != GLOBALS._ambiance[nLoc]) { GLOBALS._lastTappeto = GLOBALS._ambiance[nLoc]; if (GLOBALS._lastTappeto != 0) - g_vm->playMusic(4, ambianceFile[GLOBALS._lastTappeto], 0, true, 2000); + g_vm->playMusic(4, kAmbianceFile[GLOBALS._lastTappeto], 0, true, 2000); } if (!GLOBALS._bNoBullsEye) { @@ -566,51 +537,49 @@ DECLARE_CUSTOM_FUNCTION(ChangeLocation)(CORO_PARAM, uint32 nLoc, uint32 tX, uint CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(SetLocStartPosition)(CORO_PARAM, uint32 nLoc, uint32 lX, uint32 lY, uint32) { +void setLocStartPosition(CORO_PARAM, uint32 nLoc, uint32 lX, uint32 lY, uint32) { GLOBALS._startLocPos[nLoc].set(lX, lY); } -DECLARE_CUSTOM_FUNCTION(SaveTonyPosition)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void saveTonyPosition(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._saveTonyPos = GLOBALS._tony->position(); GLOBALS._saveTonyLoc = GLOBALS._loc->TEMPGetNumLoc(); } -DECLARE_CUSTOM_FUNCTION(RestoreTonyPosition)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void restoreTonyPosition(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); - CORO_INVOKE_4(ChangeLocation, GLOBALS._saveTonyLoc, GLOBALS._saveTonyPos._x, GLOBALS._saveTonyPos._y, 0); + CORO_INVOKE_4(changeLocation, GLOBALS._saveTonyLoc, GLOBALS._saveTonyPos._x, GLOBALS._saveTonyPos._y, 0); - MCharResetCodes(); + mCharResetCodes(); CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(DisableInput)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void disableInput(CORO_PARAM, uint32, uint32, uint32, uint32) { g_vm->getEngine()->disableInput(); } -DECLARE_CUSTOM_FUNCTION(EnableInput)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void enableInput(CORO_PARAM, uint32, uint32, uint32, uint32) { g_vm->getEngine()->enableInput(); } -DECLARE_CUSTOM_FUNCTION(StopTony)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void stopTony(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._tony->stopNoAction(coroParam); } -DECLARE_CUSTOM_FUNCTION(CustEnableGUI)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void custEnableGUI(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS.EnableGUI(); } -DECLARE_CUSTOM_FUNCTION(CustDisableGUI)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void custDisableGUI(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS.DisableGUI(); } - - -void TonyGenericTake1(CORO_PARAM, uint32 nDirection) { +void tonyGenericTake1(CORO_PARAM, uint32 nDirection) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -624,7 +593,7 @@ void TonyGenericTake1(CORO_PARAM, uint32 nDirection) { CORO_END_CODE; } -void TonyGenericTake2(CORO_PARAM, uint32 nDirection) { +void tonyGenericTake2(CORO_PARAM, uint32 nDirection) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -640,7 +609,7 @@ void TonyGenericTake2(CORO_PARAM, uint32 nDirection) { CORO_END_CODE; } -void TonyGenericPut1(CORO_PARAM, uint32 nDirection) { +void tonyGenericPut1(CORO_PARAM, uint32 nDirection) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -654,7 +623,7 @@ void TonyGenericPut1(CORO_PARAM, uint32 nDirection) { CORO_END_CODE; } -void TonyGenericPut2(CORO_PARAM, uint32 nDirection) { +void tonyGenericPut2(CORO_PARAM, uint32 nDirection) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -671,71 +640,65 @@ void TonyGenericPut2(CORO_PARAM, uint32 nDirection) { } -DECLARE_CUSTOM_FUNCTION(TonyTakeUp1)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericTake1(coroParam, 0); +void tonyTakeUp1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake1(coroParam, 0); } -DECLARE_CUSTOM_FUNCTION(TonyTakeMid1)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericTake1(coroParam, 1); +void tonyTakeMid1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake1(coroParam, 1); } -DECLARE_CUSTOM_FUNCTION(TonyTakeDown1)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericTake1(coroParam, 2); +void tonyTakeDown1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake1(coroParam, 2); } - - -DECLARE_CUSTOM_FUNCTION(TonyTakeUp2)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericTake2(coroParam, 0); +void tonyTakeUp2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake2(coroParam, 0); } -DECLARE_CUSTOM_FUNCTION(TonyTakeMid2)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericTake2(coroParam, 1); +void tonyTakeMid2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake2(coroParam, 1); } -DECLARE_CUSTOM_FUNCTION(TonyTakeDown2)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericTake2(coroParam, 2); +void tonyTakeDown2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericTake2(coroParam, 2); } - - -DECLARE_CUSTOM_FUNCTION(TonyPutUp1)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericPut1(coroParam, 0); +void tonyPutUp1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut1(coroParam, 0); } - -DECLARE_CUSTOM_FUNCTION(TonyPutMid1)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericPut1(coroParam, 1); +void tonyPutMid1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut1(coroParam, 1); } -DECLARE_CUSTOM_FUNCTION(TonyPutDown1)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericPut1(coroParam, 2); +void tonyPutDown1(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut1(coroParam, 2); } -DECLARE_CUSTOM_FUNCTION(TonyPutUp2)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericPut2(coroParam, 0); +void tonyPutUp2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut2(coroParam, 0); } - -DECLARE_CUSTOM_FUNCTION(TonyPutMid2)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericPut2(coroParam, 1); +void tonyPutMid2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut2(coroParam, 1); } -DECLARE_CUSTOM_FUNCTION(TonyPutDown2)(CORO_PARAM, uint32, uint32, uint32, uint32) { - TonyGenericPut2(coroParam, 2); +void tonyPutDown2(CORO_PARAM, uint32, uint32, uint32, uint32) { + tonyGenericPut2(coroParam, 2); } -DECLARE_CUSTOM_FUNCTION(TonyOnTheFloor)(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) { +void tonyOnTheFloor(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) { if (dwParte == 0) GLOBALS._tony->setPattern(GLOBALS._tony->PAT_ONTHEFLOORLEFT); else GLOBALS._tony->setPattern(GLOBALS._tony->PAT_ONTHEFLOORRIGHT); } -DECLARE_CUSTOM_FUNCTION(TonyGetUp)(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) { +void tonyGetUp(CORO_PARAM, uint32 dwParte, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -752,11 +715,11 @@ DECLARE_CUSTOM_FUNCTION(TonyGetUp)(CORO_PARAM, uint32 dwParte, uint32, uint32, u CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyShepherdess)(CORO_PARAM, uint32 bIsPast, uint32, uint32, uint32) { +void tonyShepherdess(CORO_PARAM, uint32 bIsPast, uint32, uint32, uint32) { GLOBALS._tony->setShepherdess(bIsPast); } -DECLARE_CUSTOM_FUNCTION(TonyWhistle)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWhistle(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -771,98 +734,97 @@ DECLARE_CUSTOM_FUNCTION(TonyWhistle)(CORO_PARAM, uint32, uint32, uint32, uint32) CORO_END_CODE; } - -void TonySetNumTexts(uint32 dwText) { +void tonySetNumTexts(uint32 dwText) { GLOBALS._dwTonyNumTexts = dwText; GLOBALS._bTonyInTexts = false; } -DECLARE_CUSTOM_FUNCTION(TonyLaugh)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyLaugh(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_LAUGH; } -DECLARE_CUSTOM_FUNCTION(TonyGiggle)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyGiggle(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_LAUGH2; } -DECLARE_CUSTOM_FUNCTION(TonyHips)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyHips(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_HIPS; } -DECLARE_CUSTOM_FUNCTION(TonySing)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonySing(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SING; } -DECLARE_CUSTOM_FUNCTION(TonyIndicate)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyIndicate(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_INDICATE; } -DECLARE_CUSTOM_FUNCTION(TonyScaredWithHands)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyScaredWithHands(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCARED; } -DECLARE_CUSTOM_FUNCTION(TonyScaredWithoutHands)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyScaredWithoutHands(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SCARED2; } -DECLARE_CUSTOM_FUNCTION(TonyWithHammer)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithHammer(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHHAMMER; GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHHAMMER); } -DECLARE_CUSTOM_FUNCTION(TonyWithGlasses)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithGlasses(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHGLASSES; GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHGLASSES); } -DECLARE_CUSTOM_FUNCTION(TonyWithWorm)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithWorm(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHWORM; GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHWORM); } -DECLARE_CUSTOM_FUNCTION(TonyWithRope)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithRope(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHROPE; GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHROPE); } -DECLARE_CUSTOM_FUNCTION(TonyWithSecretary)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithSecretary(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSECRETARY; GLOBALS._tony->setPattern(GLOBALS._tony->PAT_WITHSECRETARY); } -DECLARE_CUSTOM_FUNCTION(TonyWithRabbitANIM)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithRabbitANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRABBIT; } -DECLARE_CUSTOM_FUNCTION(TonyWithRecipeANIM)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithRecipeANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHRECIPE; } -DECLARE_CUSTOM_FUNCTION(TonyWithCardsANIM)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithCardsANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHCARDS; } -DECLARE_CUSTOM_FUNCTION(TonyWithSnowmanANIM)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyWithSnowmanANIM(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_WITHSNOWMAN; } -DECLARE_CUSTOM_FUNCTION(TonyWithSnowmanStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithSnowmanStart(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -875,7 +837,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithSnowmanStart)(CORO_PARAM, uint32, uint32, uint32 CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithSnowmanEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithSnowmanEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -888,7 +850,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithSnowmanEnd)(CORO_PARAM, uint32, uint32, uint32, CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithRabbitStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithRabbitStart(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -901,7 +863,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithRabbitStart)(CORO_PARAM, uint32, uint32, uint32, CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithRabbitEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithRabbitEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -914,7 +876,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithRabbitEnd)(CORO_PARAM, uint32, uint32, uint32, u CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithRecipeStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithRecipeStart(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -927,7 +889,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithRecipeStart)(CORO_PARAM, uint32, uint32, uint32, CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithRecipeEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithRecipeEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -940,7 +902,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithRecipeEnd)(CORO_PARAM, uint32, uint32, uint32, u CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithCardsStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithCardsStart(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -953,7 +915,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithCardsStart)(CORO_PARAM, uint32, uint32, uint32, CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithCardsEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithCardsEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -966,7 +928,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithCardsEnd)(CORO_PARAM, uint32, uint32, uint32, ui CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithNotebookStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithNotebookStart(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -979,7 +941,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithNotebookStart)(CORO_PARAM, uint32, uint32, uint3 CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithNotebookEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithNotebookEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -992,7 +954,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithNotebookEnd)(CORO_PARAM, uint32, uint32, uint32, CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithMegaphoneStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithMegaphoneStart(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1005,7 +967,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithMegaphoneStart)(CORO_PARAM, uint32, uint32, uint CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithMegaphoneEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithMegaphoneEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1018,7 +980,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithMegaphoneEnd)(CORO_PARAM, uint32, uint32, uint32 CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithBeardStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithBeardStart(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1031,7 +993,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithBeardStart)(CORO_PARAM, uint32, uint32, uint32, CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyWithBeardEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyWithBeardEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1044,7 +1006,7 @@ DECLARE_CUSTOM_FUNCTION(TonyWithBeardEnd)(CORO_PARAM, uint32, uint32, uint32, ui CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyScaredStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyScaredStart(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1057,7 +1019,7 @@ DECLARE_CUSTOM_FUNCTION(TonyScaredStart)(CORO_PARAM, uint32, uint32, uint32, uin CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonyScaredEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonyScaredEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1071,12 +1033,12 @@ DECLARE_CUSTOM_FUNCTION(TonyScaredEnd)(CORO_PARAM, uint32, uint32, uint32, uint3 } -DECLARE_CUSTOM_FUNCTION(TonyDisgusted)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonyDisgusted(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_DISGUSTED; } -DECLARE_CUSTOM_FUNCTION(TonySniffLeft)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonySniffLeft(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1084,12 +1046,12 @@ DECLARE_CUSTOM_FUNCTION(TonySniffLeft)(CORO_PARAM, uint32, uint32, uint32, uint3 GLOBALS._tony->setPattern(GLOBALS._tony->PAT_SNIFF_LEFT); CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); - CORO_INVOKE_4(LeftToMe, 0, 0, 0, 0); + CORO_INVOKE_4(leftToMe, 0, 0, 0, 0); CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonySniffRight)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void tonySniffRight(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1097,17 +1059,17 @@ DECLARE_CUSTOM_FUNCTION(TonySniffRight)(CORO_PARAM, uint32, uint32, uint32, uint GLOBALS._tony->setPattern(GLOBALS._tony->PAT_SNIFF_RIGHT); CORO_INVOKE_0(GLOBALS._tony->waitForEndPattern); - CORO_INVOKE_4(RightToMe, 0, 0, 0, 0); + CORO_INVOKE_4(rightToMe, 0, 0, 0, 0); CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(TonySarcastic)(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { - TonySetNumTexts(dwText); +void tonySarcastic(CORO_PARAM, uint32 dwText, uint32, uint32, uint32) { + tonySetNumTexts(dwText); GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_SARCASTIC; } -DECLARE_CUSTOM_FUNCTION(TonyMacbeth)(CORO_PARAM, uint32 nPos, uint32, uint32, uint32) { +void tonyMacbeth(CORO_PARAM, uint32 nPos, uint32, uint32, uint32) { switch (nPos) { case 1: GLOBALS._nTonyNextTalkType = GLOBALS._tony->TALK_MACBETH1; @@ -1140,15 +1102,15 @@ DECLARE_CUSTOM_FUNCTION(TonyMacbeth)(CORO_PARAM, uint32 nPos, uint32, uint32, ui } -DECLARE_CUSTOM_FUNCTION(EnableTony)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void enableTony(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._tony->show(); } -DECLARE_CUSTOM_FUNCTION(DisableTony)(CORO_PARAM, uint32 bShowShadow, uint32, uint32, uint32) { +void disableTony(CORO_PARAM, uint32 bShowShadow, uint32, uint32, uint32) { GLOBALS._tony->hide(bShowShadow); } -DECLARE_CUSTOM_FUNCTION(WaitForPatternEnd)(CORO_PARAM, uint32 nItem, uint32, uint32, uint32) { +void waitForPatternEnd(CORO_PARAM, uint32 nItem, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; RMItem *item; CORO_END_CONTEXT(_ctx); @@ -1164,11 +1126,11 @@ DECLARE_CUSTOM_FUNCTION(WaitForPatternEnd)(CORO_PARAM, uint32 nItem, uint32, uin } -DECLARE_CUSTOM_FUNCTION(SetTonyPosition)(CORO_PARAM, uint32 nX, uint32 nY, uint32 nLoc, uint32) { +void setTonyPosition(CORO_PARAM, uint32 nX, uint32 nY, uint32 nLoc, uint32) { GLOBALS._tony->setPosition(RMPoint(nX, nY), nLoc); } -DECLARE_CUSTOM_FUNCTION(MoveTonyAndWait)(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) { +void moveTonyAndWait(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -1187,11 +1149,11 @@ DECLARE_CUSTOM_FUNCTION(MoveTonyAndWait)(CORO_PARAM, uint32 nX, uint32 nY, uint3 CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(MoveTony)(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) { +void moveTony(CORO_PARAM, uint32 nX, uint32 nY, uint32, uint32) { GLOBALS._tony->move(coroParam, RMPoint(nX, nY)); } -DECLARE_CUSTOM_FUNCTION(ScrollLocation)(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) { +void scrollLocation(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) { CORO_BEGIN_CONTEXT; int lx, ly; RMPoint pt; @@ -1239,7 +1201,7 @@ DECLARE_CUSTOM_FUNCTION(ScrollLocation)(CORO_PARAM, uint32 nX, uint32 nY, uint32 CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(SyncScrollLocation)(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) { +void syncScrollLocation(CORO_PARAM, uint32 nX, uint32 nY, uint32 sX, uint32 sY) { CORO_BEGIN_CONTEXT; int lx, ly; RMPoint pt, startpt; @@ -1321,7 +1283,7 @@ DECLARE_CUSTOM_FUNCTION(SyncScrollLocation)(CORO_PARAM, uint32 nX, uint32 nY, ui } -DECLARE_CUSTOM_FUNCTION(ChangeHotspot)(CORO_PARAM, uint32 dwCode, uint32 nX, uint32 nY, uint32) { +void changeHotspot(CORO_PARAM, uint32 dwCode, uint32 nX, uint32 nY, uint32) { int i; for (i = 0; i < GLOBALS._curChangedHotspot; i++) { @@ -1343,15 +1305,15 @@ DECLARE_CUSTOM_FUNCTION(ChangeHotspot)(CORO_PARAM, uint32 dwCode, uint32 nX, uin } -DECLARE_CUSTOM_FUNCTION(AutoSave)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void autoSave(CORO_PARAM, uint32, uint32, uint32, uint32) { g_vm->autoSave(coroParam); } -DECLARE_CUSTOM_FUNCTION(AbortGame)(CORO_PARAM, uint32, uint32, uint32, uint32) { - g_vm->abortGame(); +void abortGame(CORO_PARAM, uint32, uint32, uint32, uint32) { + error("script called abortGame"); } -DECLARE_CUSTOM_FUNCTION(ShakeScreen)(CORO_PARAM, uint32 nScosse, uint32, uint32, uint32) { +void shakeScreen(CORO_PARAM, uint32 nScosse, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; uint32 i; uint32 curTime; @@ -1389,7 +1351,7 @@ DECLARE_CUSTOM_FUNCTION(ShakeScreen)(CORO_PARAM, uint32 nScosse, uint32, uint32, * Characters */ -DECLARE_CUSTOM_FUNCTION(CharSetCode)(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) { +void charSetCode(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) { assert(nChar < 16); GLOBALS._character[nChar]._code = nCode; GLOBALS._character[nChar]._item = GLOBALS._loc->getItemFromCode(nCode); @@ -1404,26 +1366,26 @@ DECLARE_CUSTOM_FUNCTION(CharSetCode)(CORO_PARAM, uint32 nChar, uint32 nCode, uin GLOBALS._isMChar[nChar] = false; } -DECLARE_CUSTOM_FUNCTION(CharSetColor)(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) { +void charSetColor(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) { assert(nChar < 16); GLOBALS._character[nChar]._r = r; GLOBALS._character[nChar]._g = g; GLOBALS._character[nChar]._b = b; } -DECLARE_CUSTOM_FUNCTION(CharSetTalkPattern)(CORO_PARAM, uint32 nChar, uint32 tp, uint32 sp, uint32) { +void charSetTalkPattern(CORO_PARAM, uint32 nChar, uint32 tp, uint32 sp, uint32) { assert(nChar < 16); GLOBALS._character[nChar]._talkPattern = tp; GLOBALS._character[nChar]._standPattern = sp; } -DECLARE_CUSTOM_FUNCTION(CharSetStartEndTalkPattern)(CORO_PARAM, uint32 nChar, uint32 sp, uint32 ep, uint32) { +void charSetStartEndTalkPattern(CORO_PARAM, uint32 nChar, uint32 sp, uint32 ep, uint32) { assert(nChar < 16); GLOBALS._character[nChar]._startTalkPattern = sp; GLOBALS._character[nChar]._endTalkPattern = ep; } -DECLARE_CUSTOM_FUNCTION(CharSendMessage)(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32) { +void charSendMessage(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32) { CORO_BEGIN_CONTEXT; RMMessage *msg; int i; @@ -1450,7 +1412,7 @@ DECLARE_CUSTOM_FUNCTION(CharSendMessage)(CORO_PARAM, uint32 nChar, uint32 dwMess GLOBALS._character[nChar]._item->setPattern(GLOBALS._character[nChar]._talkPattern); - _ctx->curVoc = SearchVoiceHeader(0, dwMessage); + _ctx->curVoc = searchVoiceHeader(0, dwMessage); _ctx->voice = NULL; if (_ctx->curVoc) { // Position within the database of entries, beginning at the first @@ -1530,15 +1492,15 @@ DECLARE_CUSTOM_FUNCTION(CharSendMessage)(CORO_PARAM, uint32 nChar, uint32 dwMess CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(AddInventory)(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) { +void addInventory(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) { GLOBALS._inventory->addItem(dwCode); } -DECLARE_CUSTOM_FUNCTION(RemoveInventory)(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) { +void removeInventory(CORO_PARAM, uint32 dwCode, uint32, uint32, uint32) { GLOBALS._inventory->removeItem(dwCode); } -DECLARE_CUSTOM_FUNCTION(ChangeInventoryStatus)(CORO_PARAM, uint32 dwCode, uint32 dwStatus, uint32, uint32) { +void changeInventoryStatus(CORO_PARAM, uint32 dwCode, uint32 dwStatus, uint32, uint32) { GLOBALS._inventory->changeItemStatus(dwCode, dwStatus); } @@ -1547,7 +1509,7 @@ DECLARE_CUSTOM_FUNCTION(ChangeInventoryStatus)(CORO_PARAM, uint32 dwCode, uint32 * Master Characters */ -DECLARE_CUSTOM_FUNCTION(MCharSetCode)(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) { +void mCharSetCode(CORO_PARAM, uint32 nChar, uint32 nCode, uint32, uint32) { assert(nChar < 10); GLOBALS._mCharacter[nChar]._code = nCode; if (nCode == 0) @@ -1569,56 +1531,52 @@ DECLARE_CUSTOM_FUNCTION(MCharSetCode)(CORO_PARAM, uint32 nChar, uint32 nCode, ui GLOBALS._isMChar[nChar] = true; } -DECLARE_CUSTOM_FUNCTION(MCharResetCode)(CORO_PARAM, uint32 nChar, uint32, uint32, uint32) { +void mCharResetCode(CORO_PARAM, uint32 nChar, uint32, uint32, uint32) { GLOBALS._mCharacter[nChar]._item = GLOBALS._loc->getItemFromCode(GLOBALS._mCharacter[nChar]._code); } -DECLARE_CUSTOM_FUNCTION(MCharSetPosition)(CORO_PARAM, uint32 nChar, uint32 nX, uint32 nY, uint32) { +void mCharSetPosition(CORO_PARAM, uint32 nChar, uint32 nX, uint32 nY, uint32) { assert(nChar < 10); GLOBALS._mCharacter[nChar]._x = nX; GLOBALS._mCharacter[nChar]._y = nY; } - -DECLARE_CUSTOM_FUNCTION(MCharSetColor)(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) { +void mCharSetColor(CORO_PARAM, uint32 nChar, uint32 r, uint32 g, uint32 b) { assert(nChar < 10); GLOBALS._mCharacter[nChar]._r = r; GLOBALS._mCharacter[nChar]._g = g; GLOBALS._mCharacter[nChar]._b = b; } - -DECLARE_CUSTOM_FUNCTION(MCharSetNumTalksInGroup)(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32 nTalks, uint32) { +void mCharSetNumTalksInGroup(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32 nTalks, uint32) { assert(nChar < 10); assert(nGroup < 10); GLOBALS._mCharacter[nChar]._numTalks[nGroup] = nTalks; } - -DECLARE_CUSTOM_FUNCTION(MCharSetCurrentGroup)(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32, uint32) { +void mCharSetCurrentGroup(CORO_PARAM, uint32 nChar, uint32 nGroup, uint32, uint32) { assert(nChar < 10); assert(nGroup < 10); GLOBALS._mCharacter[nChar]._curGroup = nGroup; } -DECLARE_CUSTOM_FUNCTION(MCharSetNumTexts)(CORO_PARAM, uint32 nChar, uint32 nTexts, uint32, uint32) { +void mCharSetNumTexts(CORO_PARAM, uint32 nChar, uint32 nTexts, uint32, uint32) { assert(nChar < 10); GLOBALS._mCharacter[nChar]._numTexts = nTexts - 1; GLOBALS._mCharacter[nChar]._bInTexts = false; } -DECLARE_CUSTOM_FUNCTION(MCharSetAlwaysBack)(CORO_PARAM, uint32 nChar, uint32 bAlwaysBack, uint32, uint32) { +void mCharSetAlwaysBack(CORO_PARAM, uint32 nChar, uint32 bAlwaysBack, uint32, uint32) { assert(nChar < 10); GLOBALS._mCharacter[nChar]._bAlwaysBack = bAlwaysBack; } - -DECLARE_CUSTOM_FUNCTION(MCharSendMessage)(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32 nFont) { +void mCharSendMessage(CORO_PARAM, uint32 nChar, uint32 dwMessage, uint32 bIsBack, uint32 nFont) { CORO_BEGIN_CONTEXT; RMMessage *msg; int i; @@ -1658,11 +1616,10 @@ DECLARE_CUSTOM_FUNCTION(MCharSendMessage)(CORO_PARAM, uint32 nChar, uint32 dwMes } } - _ctx->curVoc = SearchVoiceHeader(0, dwMessage); + _ctx->curVoc = searchVoiceHeader(0, dwMessage); _ctx->voice = NULL; if (_ctx->curVoc) { // Position within the database of entries, beginning at the first - // fseek(g_vm->m_vdbFP, curVoc->offset, SEEK_SET); g_vm->_vdbFP.seek(_ctx->curVoc->_offset); _ctx->curOffset = _ctx->curVoc->_offset; } @@ -1744,9 +1701,7 @@ DECLARE_CUSTOM_FUNCTION(MCharSendMessage)(CORO_PARAM, uint32 nChar, uint32 dwMes * Dialogs */ -int g_curDialog; - -DECLARE_CUSTOM_FUNCTION(SendDialogMessage)(CORO_PARAM, uint32 nPers, uint32 nMsg, uint32, uint32) { +void sendDialogMessage(CORO_PARAM, uint32 nPers, uint32 nMsg, uint32, uint32) { CORO_BEGIN_CONTEXT; char *string; RMTextDialog *text; @@ -1766,7 +1721,7 @@ DECLARE_CUSTOM_FUNCTION(SendDialogMessage)(CORO_PARAM, uint32 nPers, uint32 nMsg if (nPers != 0 && GLOBALS._isMChar[nPers] && GLOBALS._mCharacter[nPers]._bAlwaysBack) _ctx->bIsBack = true; - _ctx->curVoc = SearchVoiceHeader(g_curDialog, nMsg); + _ctx->curVoc = searchVoiceHeader(GLOBALS._curDialog, nMsg); _ctx->voice = NULL; if (_ctx->curVoc) { @@ -1927,7 +1882,7 @@ DECLARE_CUSTOM_FUNCTION(SendDialogMessage)(CORO_PARAM, uint32 nPers, uint32 nMsg // @@@@ This cannot be skipped!!!!!!!!!!!!!!!!!!! -DECLARE_CUSTOM_FUNCTION(StartDialog)(CORO_PARAM, uint32 nDialog, uint32 nStartGroup, uint32, uint32) { +void startDialog(CORO_PARAM, uint32 nDialog, uint32 nStartGroup, uint32, uint32) { CORO_BEGIN_CONTEXT; uint32 nChoice; uint32 *sl; @@ -1939,7 +1894,7 @@ DECLARE_CUSTOM_FUNCTION(StartDialog)(CORO_PARAM, uint32 nDialog, uint32 nStartGr CORO_BEGIN_CODE(_ctx); - g_curDialog = nDialog; + GLOBALS._curDialog = nDialog; // Call MPAL to start the dialog mpalQueryDoDialog(nDialog, nStartGroup); @@ -2010,7 +1965,7 @@ DECLARE_CUSTOM_FUNCTION(StartDialog)(CORO_PARAM, uint32 nDialog, uint32 nStartGr * Sync between idle and mpal */ -DECLARE_CUSTOM_FUNCTION(TakeOwnership)(CORO_PARAM, uint32 num, uint32, uint32, uint32) { +void takeOwnership(CORO_PARAM, uint32 num, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -2028,14 +1983,16 @@ DECLARE_CUSTOM_FUNCTION(TakeOwnership)(CORO_PARAM, uint32 num, uint32, uint32, u CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(ReleaseOwnership)(CORO_PARAM, uint32 num, uint32, uint32, uint32) { +void releaseOwnership(CORO_PARAM, uint32 num, uint32, uint32, uint32) { if (!GLOBALS._mut[num]._lockCount) { warning("ReleaseOwnership tried to release mutex %d, which isn't held", num); return; } - if (GLOBALS._mut[num]._ownerPid != (uint32)CoroScheduler.getCurrentPID()) - error("ReleaseOwnership tried to release mutex %d, which is held by a different process", num); + if (GLOBALS._mut[num]._ownerPid != (uint32)CoroScheduler.getCurrentPID()) { + warning("ReleaseOwnership tried to release mutex %d, which is held by a different process", num); + return; + } GLOBALS._mut[num]._lockCount--; if (!GLOBALS._mut[num]._lockCount) { @@ -2058,7 +2015,7 @@ DECLARE_CUSTOM_FUNCTION(ReleaseOwnership)(CORO_PARAM, uint32 num, uint32, uint32 * */ -void ThreadFadeInMusic(CORO_PARAM, const void *nMusic) { +void threadFadeInMusic(CORO_PARAM, const void *nMusic) { CORO_BEGIN_CONTEXT; int i; CORO_END_CONTEXT(_ctx); @@ -2067,7 +2024,7 @@ void ThreadFadeInMusic(CORO_PARAM, const void *nMusic) { CORO_BEGIN_CODE(_ctx); - debug("Start FadeIn Music"); + debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "Start FadeIn Music"); for (_ctx->i = 0; _ctx->i < 16; _ctx->i++) { g_vm->setMusicVolume(nChannel, _ctx->i * 4); @@ -2076,14 +2033,14 @@ void ThreadFadeInMusic(CORO_PARAM, const void *nMusic) { } g_vm->setMusicVolume(nChannel, 64); - debug("End FadeIn Music"); + debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "End FadeIn Music"); CORO_KILL_SELF(); CORO_END_CODE; } -void ThreadFadeOutMusic(CORO_PARAM, const void *nMusic) { +void threadFadeOutMusic(CORO_PARAM, const void *nMusic) { CORO_BEGIN_CONTEXT; int i; int startVolume; @@ -2114,52 +2071,52 @@ void ThreadFadeOutMusic(CORO_PARAM, const void *nMusic) { CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(FadeInSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) { - CoroScheduler.createProcess(ThreadFadeInMusic, &GLOBALS._curSoundEffect, sizeof(int)); +void fadeInSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { + CoroScheduler.createProcess(threadFadeInMusic, &GLOBALS._curSoundEffect, sizeof(int)); } -DECLARE_CUSTOM_FUNCTION(FadeOutSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void fadeOutSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._bFadeOutStop = false; - CoroScheduler.createProcess(ThreadFadeOutMusic, &GLOBALS._curSoundEffect, sizeof(int)); + CoroScheduler.createProcess(threadFadeOutMusic, &GLOBALS._curSoundEffect, sizeof(int)); } -DECLARE_CUSTOM_FUNCTION(FadeOutJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void fadeOutJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._bFadeOutStop = false; int channel = 2; - CoroScheduler.createProcess(ThreadFadeOutMusic, &channel, sizeof(int)); + CoroScheduler.createProcess(threadFadeOutMusic, &channel, sizeof(int)); } -DECLARE_CUSTOM_FUNCTION(FadeInJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void fadeInJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { int channel = 2; - CoroScheduler.createProcess(ThreadFadeInMusic, &channel, sizeof(int)); + CoroScheduler.createProcess(threadFadeInMusic, &channel, sizeof(int)); } -DECLARE_CUSTOM_FUNCTION(StopSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void stopSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { g_vm->stopMusic(GLOBALS._curSoundEffect); } -DECLARE_CUSTOM_FUNCTION(StopJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void stopJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { g_vm->stopMusic(2); } -DECLARE_CUSTOM_FUNCTION(MuteSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void muteSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { g_vm->setMusicVolume(GLOBALS._curSoundEffect, 0); } -DECLARE_CUSTOM_FUNCTION(DemuteSoundEffect)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void demuteSoundEffect(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._bFadeOutStop = true; g_vm->setMusicVolume(GLOBALS._curSoundEffect, 64); } -DECLARE_CUSTOM_FUNCTION(MuteJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void muteJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { g_vm->setMusicVolume(2, 0); } -DECLARE_CUSTOM_FUNCTION(DemuteJingle)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void demuteJingle(CORO_PARAM, uint32, uint32, uint32, uint32) { g_vm->setMusicVolume(2, 64); } -void CustPlayMusic(uint32 nChannel, const char *mFN, uint32 nFX, bool bLoop, int nSync = 0) { +void custPlayMusic(uint32 nChannel, const char *mFN, uint32 nFX, bool bLoop, int nSync = 0) { if (nSync == 0) nSync = 2000; debugC(DEBUG_INTERMEDIATE, kTonyDebugMusic, "Start CustPlayMusic"); @@ -2167,21 +2124,21 @@ void CustPlayMusic(uint32 nChannel, const char *mFN, uint32 nFX, bool bLoop, int debugC(DEBUG_INTERMEDIATE, kTonyDebugMusic, "End CustPlayMusic"); } -DECLARE_CUSTOM_FUNCTION(PlaySoundEffect)(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bNoLoop, uint32) { +void playSoundEffect(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bNoLoop, uint32) { if (nFX == 0 || nFX == 1 || nFX == 2) { debugC(DEBUG_INTERMEDIATE, kTonyDebugSound, "PlaySoundEffect stop fadeout"); GLOBALS._bFadeOutStop = true; } GLOBALS._lastMusic = nMusic; - CustPlayMusic(GLOBALS._curSoundEffect, musicFiles[nMusic].name, nFX, bNoLoop ? false : true, musicFiles[nMusic].sync); + custPlayMusic(GLOBALS._curSoundEffect, kMusicFiles[nMusic]._name, nFX, bNoLoop ? false : true, kMusicFiles[nMusic]._sync); } -DECLARE_CUSTOM_FUNCTION(PlayJingle)(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bLoop, uint32) { - CustPlayMusic(2, jingleFileNames[nMusic], nFX, bLoop); +void playJingle(CORO_PARAM, uint32 nMusic, uint32 nFX, uint32 bLoop, uint32) { + custPlayMusic(2, kJingleFileNames[nMusic], nFX, bLoop); } -DECLARE_CUSTOM_FUNCTION(PlayItemSfx)(CORO_PARAM, uint32 nItem, uint32 nSFX, uint32, uint32) { +void playItemSfx(CORO_PARAM, uint32 nItem, uint32 nSFX, uint32, uint32) { if (nItem == 0) { GLOBALS._tony->playSfx(nSFX); } else { @@ -2191,76 +2148,71 @@ DECLARE_CUSTOM_FUNCTION(PlayItemSfx)(CORO_PARAM, uint32 nItem, uint32 nSFX, uint } } - -void RestoreMusic(CORO_PARAM) { +void restoreMusic(CORO_PARAM) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); - CORO_INVOKE_4(PlaySoundEffect, GLOBALS._lastMusic, 0, 0, 0); + CORO_INVOKE_4(playSoundEffect, GLOBALS._lastMusic, 0, 0, 0); if (GLOBALS._lastTappeto != 0) - CustPlayMusic(4, ambianceFile[GLOBALS._lastTappeto], 0, true); + custPlayMusic(4, kAmbianceFile[GLOBALS._lastTappeto], 0, true); CORO_END_CODE; } -void SaveMusic(Common::OutSaveFile *f) { +void saveMusic(Common::OutSaveFile *f) { f->writeByte(GLOBALS._lastMusic); f->writeByte(GLOBALS._lastTappeto); } -void LoadMusic(Common::InSaveFile *f) { +void loadMusic(Common::InSaveFile *f) { GLOBALS._lastMusic = f->readByte(); GLOBALS._lastTappeto = f->readByte(); } - -DECLARE_CUSTOM_FUNCTION(JingleFadeStart)(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) { +void jingleFadeStart(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); - CORO_INVOKE_4(FadeOutSoundEffect, 0, 0, 0, 0); - CORO_INVOKE_4(MuteJingle, 0, 0, 0, 0); - CORO_INVOKE_4(PlayJingle, nJingle, 0, bLoop, 0); - CORO_INVOKE_4(FadeInJingle, 0, 0, 0, 0); + CORO_INVOKE_4(fadeOutSoundEffect, 0, 0, 0, 0); + CORO_INVOKE_4(muteJingle, 0, 0, 0, 0); + CORO_INVOKE_4(playJingle, nJingle, 0, bLoop, 0); + CORO_INVOKE_4(fadeInJingle, 0, 0, 0, 0); CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(JingleFadeEnd)(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) { +void jingleFadeEnd(CORO_PARAM, uint32 nJingle, uint32 bLoop, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); - CORO_INVOKE_4(FadeOutJingle, 0, 0, 0, 0); - CORO_INVOKE_4(FadeInSoundEffect, 0, 0, 0, 0); + CORO_INVOKE_4(fadeOutJingle, 0, 0, 0, 0); + CORO_INVOKE_4(fadeInSoundEffect, 0, 0, 0, 0); CORO_END_CODE; } - - - -DECLARE_CUSTOM_FUNCTION(MustSkipIdleStart)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void mustSkipIdleStart(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._bSkipIdle = true; CoroScheduler.setEvent(GLOBALS._hSkipIdle); } -DECLARE_CUSTOM_FUNCTION(MustSkipIdleEnd)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void mustSkipIdleEnd(CORO_PARAM, uint32, uint32, uint32, uint32) { GLOBALS._bSkipIdle = false; CoroScheduler.resetEvent(GLOBALS._hSkipIdle); } -DECLARE_CUSTOM_FUNCTION(PatIrqFreeze)(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) { +void patIrqFreeze(CORO_PARAM, uint32 bStatus, uint32, uint32, uint32) { // Unused in ScummVM. } -DECLARE_CUSTOM_FUNCTION(OpenInitLoadMenu)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void openInitLoadMenu(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -2271,7 +2223,7 @@ DECLARE_CUSTOM_FUNCTION(OpenInitLoadMenu)(CORO_PARAM, uint32, uint32, uint32, ui CORO_END_CODE; } -DECLARE_CUSTOM_FUNCTION(OpenInitOptions)(CORO_PARAM, uint32, uint32, uint32, uint32) { +void openInitOptions(CORO_PARAM, uint32, uint32, uint32, uint32) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -2282,8 +2234,7 @@ DECLARE_CUSTOM_FUNCTION(OpenInitOptions)(CORO_PARAM, uint32, uint32, uint32, uin CORO_END_CODE; } - -DECLARE_CUSTOM_FUNCTION(DoCredits)(CORO_PARAM, uint32 nMsg, uint32 dwTime, uint32, uint32) { +void doCredits(CORO_PARAM, uint32 nMsg, uint32 dwTime, uint32, uint32) { CORO_BEGIN_CONTEXT; RMMessage *msg; RMTextDialog *text; @@ -2355,159 +2306,157 @@ DECLARE_CUSTOM_FUNCTION(DoCredits)(CORO_PARAM, uint32 nMsg, uint32 dwTime, uint3 CORO_END_CODE; } - - BEGIN_CUSTOM_FUNCTION_MAP() -ASSIGN(1, CustLoadLocation) -ASSIGN(2, MySleep) -ASSIGN(3, SetPointer) -ASSIGN(5, MoveTony) -ASSIGN(6, FaceToMe) -ASSIGN(7, BackToMe) -ASSIGN(8, LeftToMe) -ASSIGN(9, RightToMe) -ASSIGN(10, SendTonyMessage) -ASSIGN(11, ChangeBoxStatus) -ASSIGN(12, ChangeLocation) -ASSIGN(13, DisableTony) -ASSIGN(14, EnableTony) -ASSIGN(15, WaitForPatternEnd) -ASSIGN(16, SetLocStartPosition) -ASSIGN(17, ScrollLocation) -ASSIGN(18, MoveTonyAndWait) -ASSIGN(19, ChangeHotspot) -ASSIGN(20, AddInventory) -ASSIGN(21, RemoveInventory) -ASSIGN(22, ChangeInventoryStatus) -ASSIGN(23, SetTonyPosition) -ASSIGN(24, SendFullscreenMessage) -ASSIGN(25, SaveTonyPosition) -ASSIGN(26, RestoreTonyPosition) -ASSIGN(27, DisableInput) -ASSIGN(28, EnableInput) -ASSIGN(29, StopTony) - -ASSIGN(30, TonyTakeUp1) -ASSIGN(31, TonyTakeMid1) -ASSIGN(32, TonyTakeDown1) -ASSIGN(33, TonyTakeUp2) -ASSIGN(34, TonyTakeMid2) -ASSIGN(35, TonyTakeDown2) - -ASSIGN(72, TonyPutUp1) -ASSIGN(73, TonyPutMid1) -ASSIGN(74, TonyPutDown1) -ASSIGN(75, TonyPutUp2) -ASSIGN(76, TonyPutMid2) -ASSIGN(77, TonyPutDown2) - -ASSIGN(36, TonyOnTheFloor) -ASSIGN(37, TonyGetUp) -ASSIGN(38, TonyShepherdess) -ASSIGN(39, TonyWhistle) - -ASSIGN(40, TonyLaugh) -ASSIGN(41, TonyHips) -ASSIGN(42, TonySing) -ASSIGN(43, TonyIndicate) -ASSIGN(44, TonyScaredWithHands) -ASSIGN(49, TonyScaredWithoutHands) -ASSIGN(45, TonyWithGlasses) -ASSIGN(46, TonyWithWorm) -ASSIGN(47, TonyWithHammer) -ASSIGN(48, TonyWithRope) -ASSIGN(90, TonyWithRabbitANIM) -ASSIGN(91, TonyWithRecipeANIM) -ASSIGN(92, TonyWithCardsANIM) -ASSIGN(93, TonyWithSnowmanANIM) -ASSIGN(94, TonyWithSnowmanStart) -ASSIGN(95, TonyWithSnowmanEnd) -ASSIGN(96, TonyWithRabbitStart) -ASSIGN(97, TonyWithRabbitEnd) -ASSIGN(98, TonyWithRecipeStart) -ASSIGN(99, TonyWithRecipeEnd) -ASSIGN(100, TonyWithCardsStart) -ASSIGN(101, TonyWithCardsEnd) -ASSIGN(102, TonyWithNotebookStart) -ASSIGN(103, TonyWithNotebookEnd) -ASSIGN(104, TonyWithMegaphoneStart) -ASSIGN(105, TonyWithMegaphoneEnd) -ASSIGN(106, TonyWithBeardStart) -ASSIGN(107, TonyWithBeardEnd) -ASSIGN(108, TonyGiggle) -ASSIGN(109, TonyDisgusted) -ASSIGN(110, TonySarcastic) -ASSIGN(111, TonyMacbeth) -ASSIGN(112, TonySniffLeft) -ASSIGN(113, TonySniffRight) -ASSIGN(114, TonyScaredStart) -ASSIGN(115, TonyScaredEnd) -ASSIGN(116, TonyWithSecretary) - -ASSIGN(50, CharSetCode) -ASSIGN(51, CharSetColor) -ASSIGN(52, CharSetTalkPattern) -ASSIGN(53, CharSendMessage) -ASSIGN(54, CharSetStartEndTalkPattern) - -ASSIGN(60, MCharSetCode) -ASSIGN(61, MCharSetColor) -ASSIGN(62, MCharSetCurrentGroup) -ASSIGN(63, MCharSetNumTalksInGroup) -ASSIGN(64, MCharSetNumTexts) -ASSIGN(65, MCharSendMessage) -ASSIGN(66, MCharSetPosition) -ASSIGN(67, MCharSetAlwaysBack) -ASSIGN(68, MCharResetCode) - -ASSIGN(70, StartDialog) -ASSIGN(71, SendDialogMessage) - -ASSIGN(80, TakeOwnership) -ASSIGN(81, ReleaseOwnership) - -ASSIGN(86, PlaySoundEffect) -ASSIGN(87, PlayJingle) -ASSIGN(88, FadeInSoundEffect) -ASSIGN(89, FadeOutSoundEffect) -ASSIGN(123, FadeInJingle) -ASSIGN(124, FadeOutJingle) -ASSIGN(125, MuteSoundEffect) -ASSIGN(126, DemuteSoundEffect) -ASSIGN(127, MuteJingle) -ASSIGN(128, DemuteJingle) -ASSIGN(84, StopSoundEffect) -ASSIGN(85, StopJingle) -ASSIGN(83, PlayItemSfx) -ASSIGN(129, JingleFadeStart) -ASSIGN(130, JingleFadeEnd) - -ASSIGN(120, ShakeScreen) -ASSIGN(121, AutoSave) -ASSIGN(122, AbortGame) -ASSIGN(131, NoBullsEye) -ASSIGN(132, SendFullscreenMsgStart) -ASSIGN(133, SendFullscreenMsgEnd) -ASSIGN(134, CustEnableGUI) -ASSIGN(135, CustDisableGUI) -ASSIGN(136, ClearScreen) -ASSIGN(137, PatIrqFreeze) -ASSIGN(138, TonySetPerorate) -ASSIGN(139, OpenInitLoadMenu) -ASSIGN(140, OpenInitOptions) -ASSIGN(141, SyncScrollLocation) -ASSIGN(142, CloseLocation) -ASSIGN(143, SetAlwaysDisplay) -ASSIGN(144, DoCredits) - -ASSIGN(200, MustSkipIdleStart); -ASSIGN(201, MustSkipIdleEnd); +ASSIGN(1, custLoadLocation) +ASSIGN(2, mySleep) +ASSIGN(3, setPointer) +ASSIGN(5, moveTony) +ASSIGN(6, faceToMe) +ASSIGN(7, backToMe) +ASSIGN(8, leftToMe) +ASSIGN(9, rightToMe) +ASSIGN(10, sendTonyMessage) +ASSIGN(11, changeBoxStatus) +ASSIGN(12, changeLocation) +ASSIGN(13, disableTony) +ASSIGN(14, enableTony) +ASSIGN(15, waitForPatternEnd) +ASSIGN(16, setLocStartPosition) +ASSIGN(17, scrollLocation) +ASSIGN(18, moveTonyAndWait) +ASSIGN(19, changeHotspot) +ASSIGN(20, addInventory) +ASSIGN(21, removeInventory) +ASSIGN(22, changeInventoryStatus) +ASSIGN(23, setTonyPosition) +ASSIGN(24, sendFullscreenMessage) +ASSIGN(25, saveTonyPosition) +ASSIGN(26, restoreTonyPosition) +ASSIGN(27, disableInput) +ASSIGN(28, enableInput) +ASSIGN(29, stopTony) + +ASSIGN(30, tonyTakeUp1) +ASSIGN(31, tonyTakeMid1) +ASSIGN(32, tonyTakeDown1) +ASSIGN(33, tonyTakeUp2) +ASSIGN(34, tonyTakeMid2) +ASSIGN(35, tonyTakeDown2) + +ASSIGN(72, tonyPutUp1) +ASSIGN(73, tonyPutMid1) +ASSIGN(74, tonyPutDown1) +ASSIGN(75, tonyPutUp2) +ASSIGN(76, tonyPutMid2) +ASSIGN(77, tonyPutDown2) + +ASSIGN(36, tonyOnTheFloor) +ASSIGN(37, tonyGetUp) +ASSIGN(38, tonyShepherdess) +ASSIGN(39, tonyWhistle) + +ASSIGN(40, tonyLaugh) +ASSIGN(41, tonyHips) +ASSIGN(42, tonySing) +ASSIGN(43, tonyIndicate) +ASSIGN(44, tonyScaredWithHands) +ASSIGN(49, tonyScaredWithoutHands) +ASSIGN(45, tonyWithGlasses) +ASSIGN(46, tonyWithWorm) +ASSIGN(47, tonyWithHammer) +ASSIGN(48, tonyWithRope) +ASSIGN(90, tonyWithRabbitANIM) +ASSIGN(91, tonyWithRecipeANIM) +ASSIGN(92, tonyWithCardsANIM) +ASSIGN(93, tonyWithSnowmanANIM) +ASSIGN(94, tonyWithSnowmanStart) +ASSIGN(95, tonyWithSnowmanEnd) +ASSIGN(96, tonyWithRabbitStart) +ASSIGN(97, tonyWithRabbitEnd) +ASSIGN(98, tonyWithRecipeStart) +ASSIGN(99, tonyWithRecipeEnd) +ASSIGN(100, tonyWithCardsStart) +ASSIGN(101, tonyWithCardsEnd) +ASSIGN(102, tonyWithNotebookStart) +ASSIGN(103, tonyWithNotebookEnd) +ASSIGN(104, tonyWithMegaphoneStart) +ASSIGN(105, tonyWithMegaphoneEnd) +ASSIGN(106, tonyWithBeardStart) +ASSIGN(107, tonyWithBeardEnd) +ASSIGN(108, tonyGiggle) +ASSIGN(109, tonyDisgusted) +ASSIGN(110, tonySarcastic) +ASSIGN(111, tonyMacbeth) +ASSIGN(112, tonySniffLeft) +ASSIGN(113, tonySniffRight) +ASSIGN(114, tonyScaredStart) +ASSIGN(115, tonyScaredEnd) +ASSIGN(116, tonyWithSecretary) + +ASSIGN(50, charSetCode) +ASSIGN(51, charSetColor) +ASSIGN(52, charSetTalkPattern) +ASSIGN(53, charSendMessage) +ASSIGN(54, charSetStartEndTalkPattern) + +ASSIGN(60, mCharSetCode) +ASSIGN(61, mCharSetColor) +ASSIGN(62, mCharSetCurrentGroup) +ASSIGN(63, mCharSetNumTalksInGroup) +ASSIGN(64, mCharSetNumTexts) +ASSIGN(65, mCharSendMessage) +ASSIGN(66, mCharSetPosition) +ASSIGN(67, mCharSetAlwaysBack) +ASSIGN(68, mCharResetCode) + +ASSIGN(70, startDialog) +ASSIGN(71, sendDialogMessage) + +ASSIGN(80, takeOwnership) +ASSIGN(81, releaseOwnership) + +ASSIGN(86, playSoundEffect) +ASSIGN(87, playJingle) +ASSIGN(88, fadeInSoundEffect) +ASSIGN(89, fadeOutSoundEffect) +ASSIGN(123, fadeInJingle) +ASSIGN(124, fadeOutJingle) +ASSIGN(125, muteSoundEffect) +ASSIGN(126, demuteSoundEffect) +ASSIGN(127, muteJingle) +ASSIGN(128, demuteJingle) +ASSIGN(84, stopSoundEffect) +ASSIGN(85, stopJingle) +ASSIGN(83, playItemSfx) +ASSIGN(129, jingleFadeStart) +ASSIGN(130, jingleFadeEnd) + +ASSIGN(120, shakeScreen) +ASSIGN(121, autoSave) +ASSIGN(122, abortGame) +ASSIGN(131, noBullsEye) +ASSIGN(132, sendFullscreenMsgStart) +ASSIGN(133, sendFullscreenMsgEnd) +ASSIGN(134, custEnableGUI) +ASSIGN(135, custDisableGUI) +ASSIGN(136, clearScreen) +ASSIGN(137, patIrqFreeze) +ASSIGN(138, tonySetPerorate) +ASSIGN(139, openInitLoadMenu) +ASSIGN(140, openInitOptions) +ASSIGN(141, syncScrollLocation) +ASSIGN(142, closeLocation) +ASSIGN(143, setAlwaysDisplay) +ASSIGN(144, doCredits) + +ASSIGN(200, mustSkipIdleStart); +ASSIGN(201, mustSkipIdleEnd); END_CUSTOM_FUNCTION_MAP() void processKilledCallback(Common::PROCESS *p) { - for (uint i = 0; i < 10; i++) + for (uint i = 0; i < 10; i++) { if (GLOBALS._mut[i]._ownerPid == p->pid) { // Handle scripts which don't call ReleaseOwnership, such as // the one in loc37's vEnter when Tony is chasing the mouse. @@ -2517,6 +2466,7 @@ void processKilledCallback(Common::PROCESS *p) { GLOBALS._mut[i]._lockCount = 0; CoroScheduler.setEvent(GLOBALS._mut[i]._eventId); } + } } void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input) { @@ -2531,13 +2481,12 @@ void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation GLOBALS.EnableGUI = mainEnableGUI; GLOBALS._bAlwaysDisplay = false; - int i; CoroScheduler.setResourceCallback(processKilledCallback); - for (i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) GLOBALS._mut[i]._eventId = CoroScheduler.createEvent(false, true); - for (i = 0; i < 200; i++) + for (int i = 0; i < 200; i++) GLOBALS._ambiance[i] = 0; GLOBALS._ambiance[6] = AMBIANCE_CRICKETS; diff --git a/engines/tony/custom.h b/engines/tony/custom.h index 524ab14aab..0f1061e8cd 100644 --- a/engines/tony/custom.h +++ b/engines/tony/custom.h @@ -36,9 +36,12 @@ namespace Tony { using namespace MPAL; -#define INIT_CUSTOM_FUNCTION MapCustomFunctions +struct MusicFileEntry { + const char *_name; + int _sync; +}; -#define DECLARE_CUSTOM_FUNCTION(x) void x +#define INIT_CUSTOM_FUNCTION MapCustomFunctions #define BEGIN_CUSTOM_FUNCTION_MAP() \ static void AssignError(int num) { \ @@ -63,6 +66,17 @@ class RMLocation; class RMInventory; class RMInput; +void charsSaveAll(Common::OutSaveFile *f); +void charsLoadAll(Common::InSaveFile *f); +void mCharResetCodes(); +void saveChangedHotspot(Common::OutSaveFile *f); +void loadChangedHotspot(Common::InSaveFile *f); +void reapplyChangedHotspot(); + +void restoreMusic(CORO_PARAM); +void saveMusic(Common::OutSaveFile *f); +void loadMusic(Common::InSaveFile *f); + void INIT_CUSTOM_FUNCTION(LPCUSTOMFUNCTION *lpMap, Common::String *lpStrMap); void setupGlobalVars(RMTony *tony, RMPointer *ptr, RMGameBoxes *box, RMLocation *loc, RMInventory *inv, RMInput *input); diff --git a/engines/tony/detection_tables.h b/engines/tony/detection_tables.h index f9881c29f2..ce3ab01a9f 100644 --- a/engines/tony/detection_tables.h +++ b/engines/tony/detection_tables.h @@ -124,6 +124,22 @@ static const TonyGameDescription gameDescriptions[] = { }, }, { + // Tony Tough Italian provided by Giovanni Bajo + { + "tony", + 0, + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "6202816f991b15af82aab84e3e4be011", 380183}, + AD_LISTEND + }, + Common::IT_ITA, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { // Tony Tough Polish provided by Fabio Barzagli { "tony", @@ -139,6 +155,39 @@ static const TonyGameDescription gameDescriptions[] = { GUIO1(GUIO_NONE) }, }, + { + // Tony Tough German "Gamestar" provided in bug #3566035 + { + "tony", + 0, + { + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "187de6f88f4083808cb66342ab55a7fd", 389904}, + AD_LISTEND + }, + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { + // Tony Tough Czech provided in bug #3565765 + { + "tony", + 0, + { + // {"data1.cab", 0, "c6d5dd8f0c1241a6e3f7861b7f27bf7b", 4350}, + {"roasted.mpr", 0, "06203dbbc85fdd1e6dc8fc211c1a6207", 135911071}, + {"roasted.mpc", 0, "a8283a101878f3ca105f1f83f07e2c40", 386491}, + AD_LISTEND + }, + Common::CZ_CZE, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, { AD_TABLE_END_MARKER } }; diff --git a/engines/tony/font.cpp b/engines/tony/font.cpp index 927adf9006..fa018b4464 100644 --- a/engines/tony/font.cpp +++ b/engines/tony/font.cpp @@ -84,10 +84,9 @@ void RMFont::unload() { RMGfxPrimitive *RMFont::makeLetterPrimitive(byte bChar, int &nLength) { RMFontPrimitive *prim; - int nLett; // Convert from character to glyph index - nLett = convertToLetter(bChar); + int nLett = convertToLetter(bChar); assert(nLett < _nLetters); // Create primitive font @@ -120,12 +119,11 @@ void RMFont::close() { } int RMFont::stringLen(const Common::String &text) { - uint len, i; - if (text.empty()) return letterLength('\0'); - len = 0; + uint len = 0; + uint i; for (i = 0; i < text.size() - 1; i++) len += letterLength(text[i], text[i + 1]); len += letterLength(text[i]); @@ -157,7 +155,6 @@ void RMFontColor::setBaseColor(byte r1, byte g1, byte b1) { int gstep = g / 14; int bstep = b / 14; - int i; byte pal[768 * 3]; // Check if we are already on the right color @@ -169,7 +166,7 @@ void RMFontColor::setBaseColor(byte r1, byte g1, byte b1) { _fontB = b1; // Constructs a new palette for the font - for (i = 1; i < 16; i++) { + for (int i = 1; i < 16; i++) { pal[i * 3 + 0] = r >> 16; pal[i * 3 + 1] = g >> 16; pal[i * 3 + 2] = b >> 16; @@ -184,7 +181,7 @@ void RMFontColor::setBaseColor(byte r1, byte g1, byte b1) { pal[15 * 3 + 2] += 8; // Puts in all the letters - for (i = 0; i < _nLetters; i++) + for (int i = 0; i < _nLetters; i++) _letter[i].loadPaletteWA(pal); } @@ -204,8 +201,6 @@ int RMFontWithTables::letterLength(int nChar, int nNext) { \****************************************************************************/ void RMFontDialog::init() { - int i; - // bernie: Number of characters in the font int nchars = 112 // base @@ -222,7 +217,7 @@ void RMFontDialog::init() { _hDefault = 18; Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0'); - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { _cTable[i] = g_vm->_cTableDialog[i]; _lTable[i] = g_vm->_lTableDialog[i]; } @@ -234,8 +229,6 @@ void RMFontDialog::init() { \****************************************************************************/ void RMFontMacc::init() { - int i; - // bernie: Number of characters in the font int nchars = 102 // base @@ -245,7 +238,6 @@ void RMFontMacc::init() { + 8 // francais + 5; // deutsch - load(RES_F_MACC, nchars, 11, 16); // Default @@ -253,7 +245,7 @@ void RMFontMacc::init() { _hDefault = 17; Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0'); - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { _cTable[i] = g_vm->_cTableMacc[i]; _lTable[i] = g_vm->_lTableMacc[i]; } @@ -264,8 +256,6 @@ void RMFontMacc::init() { \****************************************************************************/ void RMFontCredits::init() { - int i; - // bernie: Number of characters in the font int nchars = 112 // base @@ -275,7 +265,6 @@ void RMFontCredits::init() { + 8 // french + 2; // deutsch - load(RES_F_CREDITS, nchars, 27, 28, RES_F_CPAL); // Default @@ -283,7 +272,7 @@ void RMFontCredits::init() { _hDefault = 28; Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0'); - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { _cTable[i] = g_vm->_cTableCred[i]; _lTable[i] = g_vm->_lTableCred[i]; } @@ -305,10 +294,7 @@ void RMFontObj::setBothCase(int nChar, int nNext, signed char spiazz) { _l2Table[TOLOWER(nChar)][TOLOWER(nNext)] = spiazz; } - void RMFontObj::init() { - int i; - //bernie: Number of characters in the font (solo maiuscolo) int nchars = 85 // base @@ -318,7 +304,6 @@ void RMFontObj::init() { + 0 // francais (no uppercase chars) + 1; // deutsch - load(RES_F_OBJ, nchars, 25, 30); // Initialize the font table @@ -326,7 +311,7 @@ void RMFontObj::init() { _hDefault = 30; Common::fill(&_l2Table[0][0], &_l2Table[0][0] + (256 * 256), '\0'); - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { _cTable[i] = g_vm->_cTableObj[i]; _lTable[i] = g_vm->_lTableObj[i]; } @@ -345,7 +330,6 @@ void RMFontObj::init() { setBothCase('R', 'U', 3); } - /****************************************************************************\ * RMText Methods \****************************************************************************/ @@ -409,11 +393,6 @@ void RMText::writeText(const Common::String &text, int nFont, int *time) { void RMText::writeText(Common::String text, RMFontColor *font, int *time) { RMGfxPrimitive *prim; - uint p, old_p; - int j, x, y; - int len; - int numchar; - int width, height; // Set the base color font->setBaseColor(_textR, _textG, _textB); @@ -428,8 +407,9 @@ void RMText::writeText(Common::String text, RMFontColor *font, int *time) { // Divide the words into lines. In this cycle, X contains the maximum length reached by a line, // and the number of lines Common::Array<Common::String> lines; - p = 0; - j = x = 0; + uint p = 0; + int j = 0; + int x = 0; while (p < text.size()) { j += font->stringLen(text[p]); if (j > (((_aHorType == HLEFTPAR) && (lines.size() > 0)) ? _maxLineLength - 25 : _maxLineLength)) { @@ -443,7 +423,7 @@ void RMText::writeText(Common::String text, RMFontColor *font, int *time) { // width of a line caused discontinuation of the whole sentence. // This workaround has the partial word broken up so it will still display // - old_p = p; + uint old_p = p; while (text[p] != ' ' && text[p] != '-' && p > 0) p--; @@ -475,8 +455,8 @@ void RMText::writeText(Common::String text, RMFontColor *font, int *time) { x += 8; // Starting position for the surface: X1, Y - width = x; - height = (lines.size() - 1) * font->letterHeight() + font->_fontDimy; + int width = x; + int height = (lines.size() - 1) * font->letterHeight() + font->_fontDimy; // Create the surface create(width, height); @@ -484,8 +464,8 @@ void RMText::writeText(Common::String text, RMFontColor *font, int *time) { p = 0; - y = 0; - numchar = 0; + int y = 0; + int numchar = 0; for (uint i = 0; i < lines.size(); ++i) { const Common::String &line = lines[i]; @@ -522,6 +502,7 @@ void RMText::writeText(Common::String text, RMFontColor *font, int *time) { continue; } + int len; prim = font->makeLetterPrimitive(line[p], len); prim->getDst()._x1 = x; prim->getDst()._y1 = y; @@ -737,8 +718,8 @@ void RMTextDialog::removeThis(CORO_PARAM, bool &result) { CORO_END_CODE; } -void RMTextDialog::Unregister() { - RMGfxTask::Unregister(); +void RMTextDialog::unregister() { + RMGfxTask::unregister(); assert(_nInList == 0); CoroScheduler.setEvent(_hEndDisplay); } @@ -908,7 +889,7 @@ RMPoint RMTextItemName::getHotspot() { if (_item == NULL) return _mpos + _curscroll; else - return _item->hotspot(); + return _item->getHotspot(); } RMItem *RMTextItemName::getSelectedItem() { @@ -944,7 +925,7 @@ RMDialogChoice::RMDialogChoice() { _hUnreg = CoroScheduler.createEvent(false, false); _bRemoveFromOT = false; - + _curAdded = 0; _bShow = false; } @@ -953,8 +934,8 @@ RMDialogChoice::~RMDialogChoice() { CoroScheduler.closeEvent(_hUnreg); } -void RMDialogChoice::Unregister() { - RMGfxWoodyBuffer::Unregister(); +void RMDialogChoice::unregister() { + RMGfxWoodyBuffer::unregister(); assert(!_nInList); CoroScheduler.pulseEvent(_hUnreg); @@ -987,8 +968,6 @@ void RMDialogChoice::close() { } void RMDialogChoice::setNumChoices(int num) { - int i; - _numChoices = num; _curAdded = 0; @@ -997,7 +976,7 @@ void RMDialogChoice::setNumChoices(int num) { _ptDrawStrings = new RMPoint[num]; // Initialization - for (i = 0; i < _numChoices; i++) { + for (int i = 0; i < _numChoices; i++) { _drawedStrings[i].setColor(0, 255, 0); _drawedStrings[i].setAlignType(RMText::HLEFTPAR, RMText::VTOP); _drawedStrings[i].setMaxLineLength(600); diff --git a/engines/tony/font.h b/engines/tony/font.h index 99b20571b1..13c1ddf268 100644 --- a/engines/tony/font.h +++ b/engines/tony/font.h @@ -252,7 +252,7 @@ public: virtual void removeThis(CORO_PARAM, bool &result); // Overloaded de-registration - virtual void Unregister(); + virtual void unregister(); // Overloading of the Draw to center the text, if necessary virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); @@ -344,7 +344,7 @@ protected: public: virtual void removeThis(CORO_PARAM, bool &result); virtual void draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim); - void Unregister(); + void unregister(); public: // Initialization diff --git a/engines/tony/game.cpp b/engines/tony/game.cpp index 2bcfdc7fc2..42ace987b4 100644 --- a/engines/tony/game.cpp +++ b/engines/tony/game.cpp @@ -34,7 +34,6 @@ #include "tony/mpal/memory.h" #include "tony/mpal/mpal.h" #include "tony/mpal/mpalutils.h" -#include "tony/custom.h" #include "tony/game.h" #include "tony/gfxengine.h" #include "tony/tony.h" @@ -288,9 +287,7 @@ RMOptionScreen::RMOptionScreen() { _buttonSave_ArrowRight = NULL; _bEditSaveName = false; - int i; - - for (i = 0; i < 6; i++) { + for (int i = 0; i < 6; i++) { _curThumb[i] = NULL; _buttonSave_States[i] = NULL; } @@ -501,9 +498,7 @@ void RMOptionScreen::refreshAll(CORO_PARAM) { } void RMOptionScreen::refreshThumbnails() { - int i; - - for (i = 0; i < 6; i++) { + for (int i = 0; i < 6; i++) { if (_curThumb[i]) delete _curThumb[i]; @@ -691,9 +686,7 @@ void RMOptionScreen::closeState() { _buttonExit = NULL; if (_nState == MENULOAD || _nState == MENUSAVE) { - int i; - - for (i = 0; i < 6; i++) { + for (int i = 0; i < 6; i++) { if (_curThumb[i] != NULL) { delete _curThumb[i]; _curThumb[i] = NULL; @@ -981,7 +974,7 @@ void RMOptionScreen::doFrame(CORO_PARAM, RMInput *input) { CORO_BEGIN_CODE(_ctx); - // If it is fully open, do nothing + // If it is not fully open, do nothing if (_fadeStep != 6) return; @@ -1343,9 +1336,7 @@ void RMOptionScreen::removeThis(CORO_PARAM, bool &result) { bool RMOptionScreen::loadThumbnailFromSaveState(int nState, byte *lpDestBuf, Common::String &name, byte &diff) { - Common::String buf; char namebuf[256]; - int i; Common::InSaveFile *f; char id[4]; @@ -1355,7 +1346,7 @@ bool RMOptionScreen::loadThumbnailFromSaveState(int nState, byte *lpDestBuf, Com diff = 10; // Get the savegame filename for the given slot - buf = g_vm->getSaveStateFileName(nState); + Common::String buf = g_vm->getSaveStateFileName(nState); // Try and open the savegame f = g_system->getSavefileManager()->openForLoading(buf); @@ -1410,9 +1401,9 @@ bool RMOptionScreen::loadThumbnailFromSaveState(int nState, byte *lpDestBuf, Com return true; } - i = f->readByte(); - f->read(namebuf, i); - namebuf[i] = '\0'; + int bufSize = f->readByte(); + f->read(namebuf, bufSize); + namebuf[bufSize] = '\0'; name = namebuf; delete f; @@ -1436,9 +1427,7 @@ RMPointer::~RMPointer() { } void RMPointer::init() { - int i; - - for (i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { RMResRaw res(RES_P_GO + i); _pointer[i] = new RMGfxSourceBuffer8RLEByteAA; @@ -1446,7 +1435,7 @@ void RMPointer::init() { _pointer[i]->loadPaletteWA(RES_P_PAL); } - for (i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { RMRes res(RES_P_PAP1 + i); Common::SeekableReadStream *ds = res.getReadStream(); _specialPointer[i] = new RMItem; @@ -1467,9 +1456,7 @@ void RMPointer::init() { } void RMPointer::close() { - int i; - - for (i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { if (_pointer[i] != NULL) { delete _pointer[i]; _pointer[i] = NULL; diff --git a/engines/tony/gfxcore.cpp b/engines/tony/gfxcore.cpp index 71bf31396c..04ce01b0ed 100644 --- a/engines/tony/gfxcore.cpp +++ b/engines/tony/gfxcore.cpp @@ -56,7 +56,7 @@ void RMGfxTask::Register() { _nInList++; } -void RMGfxTask::Unregister() { +void RMGfxTask::unregister() { _nInList--; assert(_nInList >= 0); } @@ -269,7 +269,7 @@ void RMGfxTargetBuffer::clearOT() { cur = _otlist; while (cur != NULL) { - cur->_prim->_task->Unregister(); + cur->_prim->_task->unregister(); delete cur->_prim; n = cur->_next; delete cur; @@ -303,7 +303,7 @@ void RMGfxTargetBuffer::drawOT(CORO_PARAM) { CORO_INVOKE_1(_ctx->cur->_prim->_task->removeThis, _ctx->result); if (_ctx->result) { // De-register the task - _ctx->cur->_prim->_task->Unregister(); + _ctx->cur->_prim->_task->unregister(); // Delete task, freeing the memory delete _ctx->cur->_prim; @@ -361,7 +361,7 @@ void RMGfxTargetBuffer::addPrim(RMGfxPrimitive *prim) { void RMGfxTargetBuffer::addDirtyRect(const Common::Rect &r) { assert(r.isValidRect()); if (_trackDirtyRects && r.width() > 0 && r.height() > 0) - _currentDirtyRects.push_back(r); + _currentDirtyRects.push_back(r); } Common::List<Common::Rect> &RMGfxTargetBuffer::getDirtyRects() { @@ -507,9 +507,7 @@ int RMGfxSourceBufferPal::loadPaletteWA(const byte *buf, bool bSwapped) { } int RMGfxSourceBufferPal::loadPalette(const byte *buf) { - int i; - - for (i = 0; i < 256; i++) + for (int i = 0; i < 256; i++) memcpy(_pal + i * 3, buf + i * 4, 3); preparePalette(); @@ -518,9 +516,7 @@ int RMGfxSourceBufferPal::loadPalette(const byte *buf) { } void RMGfxSourceBufferPal::preparePalette() { - int i; - - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { _palFinal[i] = (((int)_pal[i * 3 + 0] >> 3) << 10) | (((int)_pal[i * 3 + 1] >> 3) << 5) | (((int)_pal[i * 3 + 2] >> 3) << 0); @@ -528,10 +524,8 @@ void RMGfxSourceBufferPal::preparePalette() { } int RMGfxSourceBufferPal::init(const byte *buf, int dimx, int dimy, bool bLoadPalette) { - int read; - // Load the RAW image - read = RMGfxSourceBuffer::init(buf, dimx, dimy); + int read = RMGfxSourceBuffer::init(buf, dimx, dimy); // Load the palette if necessary if (bLoadPalette) @@ -596,7 +590,7 @@ RMGfxSourceBuffer8::~RMGfxSourceBuffer8() { } void RMGfxSourceBuffer8::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { - int x, y, width, height, u, v; + int width, height, u, v; int bufx = bigBuf.getDimx(); uint16 *buf = bigBuf; byte *raw = _buf; @@ -623,10 +617,10 @@ void RMGfxSourceBuffer8::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimit // Normal step if (_bTrasp0) { - for (y = 0; y < height; y++) { + for (int y = 0; y < height; y++) { raw = _buf + (y + v) * _dimx + u; - for (x = 0; x < width; x++) { + for (int x = 0; x < width; x++) { if (*raw) *buf = _palFinal[*raw]; buf++; @@ -636,10 +630,10 @@ void RMGfxSourceBuffer8::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimit buf += bufx - width; } } else { - for (y = 0; y < height; y++) { + for (int y = 0; y < height; y++) { raw = _buf + (y + v) * _dimx + u; - for (x = 0; x < width; x += 2) { + for (int x = 0; x < width; x += 2) { buf[0] = _palFinal[raw[0]]; buf[1] = _palFinal[raw[1]]; @@ -693,11 +687,9 @@ RMGfxSourceBuffer8AB::~RMGfxSourceBuffer8AB() { } int RMGfxSourceBuffer8AB::calcTrasp(int fore, int back) { - int r, g, b; - - r = (GETRED(fore) >> 2) + (GETRED(back) >> 1); - g = (GETGREEN(fore) >> 2) + (GETGREEN(back) >> 1); - b = (GETBLUE(fore) >> 2) + (GETBLUE(back) >> 1); + int r = (GETRED(fore) >> 2) + (GETRED(back) >> 1); + int g = (GETGREEN(fore) >> 2) + (GETGREEN(back) >> 1); + int b = (GETBLUE(fore) >> 2) + (GETBLUE(back) >> 1); if (r > 0x1F) r = 0x1F; @@ -713,7 +705,7 @@ int RMGfxSourceBuffer8AB::calcTrasp(int fore, int back) { void RMGfxSourceBuffer8AB::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { - int x, y, width, height, u, v; + int width, height, u, v; int bufx = bigBuf.getDimx(); uint16 *buf = bigBuf; byte *raw = _buf; @@ -740,10 +732,10 @@ void RMGfxSourceBuffer8AB::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrim // Passaggio normale if (_bTrasp0) { - for (y = 0; y < height; y++) { + for (int y = 0; y < height; y++) { raw = _buf + (y + v) * _dimx + u; - for (x = 0; x < width; x++) { + for (int x = 0; x < width; x++) { if (*raw) *buf = calcTrasp(_palFinal[*raw], *buf); @@ -754,10 +746,10 @@ void RMGfxSourceBuffer8AB::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrim buf += bufx - width; } } else { - for (y = 0; y < height; y++) { + for (int y = 0; y < height; y++) { raw = _buf + (y + v) * _dimx + u; - for (x = 0; x < width; x += 2) { + for (int x = 0; x < width; x += 2) { buf[0] = calcTrasp(_palFinal[raw[0]], buf[0]); buf[1] = calcTrasp(_palFinal[raw[1]], buf[1]); @@ -809,9 +801,7 @@ void RMGfxSourceBuffer8RLE::init(Common::ReadStream &ds, int dimx, int dimy, boo if (_bNeedRLECompress) { RMGfxSourceBufferPal::init(ds, dimx, dimy, bLoadPalette); } else { - int size; - - size = ds.readSint32LE(); + int size = ds.readSint32LE(); _buf = new byte[size]; ds.read(_buf, size); @@ -845,7 +835,6 @@ void RMGfxSourceBuffer8RLE::setAlreadyCompressed() { } void RMGfxSourceBuffer8RLE::compressRLE() { - int x, y; byte *startline; byte *cur; byte curdata; @@ -856,7 +845,7 @@ void RMGfxSourceBuffer8RLE::compressRLE() { // Perform RLE compression for lines cur = _megaRLEBuf; src = _buf; - for (y = 0; y < _dimy; y++) { + for (int y = 0; y < _dimy; y++) { // Save the beginning of the line startline = cur; @@ -867,7 +856,7 @@ void RMGfxSourceBuffer8RLE::compressRLE() { curdata = 0; rep = 0; startsrc = src; - for (x = 0; x < _dimx;) { + for (int x = 0; x < _dimx;) { if ((curdata == 0 && *src == 0) || (curdata == 1 && *src == _alphaBlendColor) || (curdata == 2 && (*src != _alphaBlendColor && *src != 0))) { src++; @@ -875,13 +864,13 @@ void RMGfxSourceBuffer8RLE::compressRLE() { x++; } else { if (curdata == 0) { - RLEWriteTrasp(cur, rep); + rleWriteTrasp(cur, rep); curdata++; } else if (curdata == 1) { - RLEWriteAlphaBlend(cur, rep); + rleWriteAlphaBlend(cur, rep); curdata++; } else { - RLEWriteData(cur, rep, startsrc); + rleWriteData(cur, rep, startsrc); curdata = 0; } @@ -892,16 +881,16 @@ void RMGfxSourceBuffer8RLE::compressRLE() { // Pending data? if (curdata == 1) { - RLEWriteAlphaBlend(cur, rep); - RLEWriteData(cur, 0, NULL); + rleWriteAlphaBlend(cur, rep); + rleWriteData(cur, 0, NULL); } if (curdata == 2) { - RLEWriteData(cur, rep, startsrc); + rleWriteData(cur, rep, startsrc); } // End of line - RLEWriteEOL(cur); + rleWriteEOL(cur); // Write the length of the line WRITE_LE_UINT16(startline, (uint16)(cur - startline)); @@ -911,26 +900,25 @@ void RMGfxSourceBuffer8RLE::compressRLE() { delete[] _buf; // Copy the compressed image - x = cur - _megaRLEBuf; - _buf = new byte[x]; - Common::copy(_megaRLEBuf, _megaRLEBuf + x, _buf); + int bufSize = cur - _megaRLEBuf; + _buf = new byte[bufSize]; + Common::copy(_megaRLEBuf, _megaRLEBuf + bufSize, _buf); } void RMGfxSourceBuffer8RLE::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { - int y; byte *src; uint16 *buf = bigBuf; - int x1, y1, u, v, width, height; + int u, v, width, height; // Clipping - x1 = prim->getDst()._x1; - y1 = prim->getDst()._y1; + int x1 = prim->getDst()._x1; + int y1 = prim->getDst()._y1; if (!clip2D(x1, y1, u, v, width, height, false, &bigBuf)) return; // Go forward through the RLE lines src = _buf; - for (y = 0; y < v; y++) + for (int y = 0; y < v; y++) src += READ_LE_UINT16(src); // Calculate the position in the destination buffer @@ -952,9 +940,9 @@ void RMGfxSourceBuffer8RLE::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPri // Specify the drawn area bigBuf.addDirtyRect(Common::Rect(x1 - width, y1, x1 + 1, y1 + height)); - for (y = 0; y < height; y++) { + for (int y = 0; y < height; y++) { // Decompression - RLEDecompressLineFlipped(buf + x1, src + 2, u, width); + rleDecompressLineFlipped(buf + x1, src + 2, u, width); // Next line src += READ_LE_UINT16(src); @@ -966,9 +954,9 @@ void RMGfxSourceBuffer8RLE::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPri // Specify the drawn area bigBuf.addDirtyRect(Common::Rect(x1, y1, x1 + width, y1 + height)); - for (y = 0; y < height; y++) { + for (int y = 0; y < height; y++) { // Decompression - RLEDecompressLine(buf + x1, src + 2, u, width); + rleDecompressLine(buf + x1, src + 2, u, width); // Next line src += READ_LE_UINT16(src); @@ -985,20 +973,19 @@ void RMGfxSourceBuffer8RLE::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPri \****************************************************************************/ RMGfxSourceBuffer8RLEByte::~RMGfxSourceBuffer8RLEByte() { - } -void RMGfxSourceBuffer8RLEByte::RLEWriteTrasp(byte *&cur, int rep) { +void RMGfxSourceBuffer8RLEByte::rleWriteTrasp(byte *&cur, int rep) { assert(rep < 255); *cur ++ = rep; } -void RMGfxSourceBuffer8RLEByte::RLEWriteAlphaBlend(byte *&cur, int rep) { +void RMGfxSourceBuffer8RLEByte::rleWriteAlphaBlend(byte *&cur, int rep) { assert(rep < 255); *cur ++ = rep; } -void RMGfxSourceBuffer8RLEByte::RLEWriteData(byte *&cur, int rep, byte *src) { +void RMGfxSourceBuffer8RLEByte::rleWriteData(byte *&cur, int rep, byte *src) { assert(rep < 256); *cur ++ = rep; @@ -1011,13 +998,12 @@ void RMGfxSourceBuffer8RLEByte::RLEWriteData(byte *&cur, int rep, byte *src) { return; } -void RMGfxSourceBuffer8RLEByte::RLEWriteEOL(byte *&cur) { +void RMGfxSourceBuffer8RLEByte::rleWriteEOL(byte *&cur) { *cur ++ = 0xFF; } -void RMGfxSourceBuffer8RLEByte::RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { - int i, n; - int r, g, b; +void RMGfxSourceBuffer8RLEByte::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; if (nStartSkip == 0) goto RLEByteDoTrasp; @@ -1086,10 +1072,10 @@ RLEByteDoAlpha: RLEByteDoAlpha2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) { - r = (*dst >> 10) & 0x1F; - g = (*dst >> 5) & 0x1F; - b = *dst & 0x1F; + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); @@ -1111,7 +1097,7 @@ RLEByteDoCopy2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) + for (int i = 0; i < n; i++) *dst ++ = _palFinal[*src++]; nLength -= n; @@ -1121,9 +1107,8 @@ RLEByteDoCopy2: } } -void RMGfxSourceBuffer8RLEByte::RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) { - int i, n; - int r, g, b; +void RMGfxSourceBuffer8RLEByte::rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; if (nStartSkip == 0) goto RLEByteFlippedDoTrasp; @@ -1192,10 +1177,10 @@ RLEByteFlippedDoAlpha: RLEByteFlippedDoAlpha2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) { - r = (*dst >> 10) & 0x1F; - g = (*dst >> 5) & 0x1F; - b = *dst & 0x1F; + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); @@ -1217,7 +1202,7 @@ RLEByteFlippedDoCopy2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) + for (int i = 0; i < n; i++) *dst-- = _palFinal[*src++]; nLength -= n; @@ -1236,17 +1221,17 @@ RMGfxSourceBuffer8RLEWord::~RMGfxSourceBuffer8RLEWord() { } -void RMGfxSourceBuffer8RLEWord::RLEWriteTrasp(byte *&cur, int rep) { +void RMGfxSourceBuffer8RLEWord::rleWriteTrasp(byte *&cur, int rep) { WRITE_LE_UINT16(cur, rep); cur += 2; } -void RMGfxSourceBuffer8RLEWord::RLEWriteAlphaBlend(byte *&cur, int rep) { +void RMGfxSourceBuffer8RLEWord::rleWriteAlphaBlend(byte *&cur, int rep) { WRITE_LE_UINT16(cur, rep); cur += 2; } -void RMGfxSourceBuffer8RLEWord::RLEWriteData(byte *&cur, int rep, byte *src) { +void RMGfxSourceBuffer8RLEWord::rleWriteData(byte *&cur, int rep, byte *src) { WRITE_LE_UINT16(cur, rep); cur += 2; @@ -1257,14 +1242,13 @@ void RMGfxSourceBuffer8RLEWord::RLEWriteData(byte *&cur, int rep, byte *src) { } } -void RMGfxSourceBuffer8RLEWord::RLEWriteEOL(byte *&cur) { +void RMGfxSourceBuffer8RLEWord::rleWriteEOL(byte *&cur) { *cur ++ = 0xFF; *cur ++ = 0xFF; } -void RMGfxSourceBuffer8RLEWord::RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { - int i, n; - int r, g, b; +void RMGfxSourceBuffer8RLEWord::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; if (nStartSkip == 0) goto RLEWordDoTrasp; @@ -1341,10 +1325,10 @@ RLEWordDoAlpha2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) { - r = (*dst >> 10) & 0x1F; - g = (*dst >> 5) & 0x1F; - b = *dst & 0x1F; + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); @@ -1368,7 +1352,7 @@ RLEWordDoCopy2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) + for (int i = 0; i < n; i++) *dst++ = _palFinal[*src++]; nLength -= n; @@ -1380,9 +1364,8 @@ RLEWordDoCopy2: } } -void RMGfxSourceBuffer8RLEWord::RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) { - int i, n; - int r, g, b; +void RMGfxSourceBuffer8RLEWord::rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; if (nStartSkip == 0) goto RLEWordFlippedDoTrasp; @@ -1459,10 +1442,10 @@ RLEWordFlippedDoAlpha2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) { - r = (*dst >> 10) & 0x1F; - g = (*dst >> 5) & 0x1F; - b = *dst & 0x1F; + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); @@ -1486,7 +1469,7 @@ RLEWordFlippedDoCopy2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) + for (int i = 0; i < n; i++) *dst-- = _palFinal[*src++]; nLength -= n; @@ -1505,12 +1488,11 @@ RMGfxSourceBuffer8RLEWordAB::~RMGfxSourceBuffer8RLEWordAB() { } -void RMGfxSourceBuffer8RLEWordAB::RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { - int i, n; - int r, g, b, r2, g2, b2; +void RMGfxSourceBuffer8RLEWordAB::rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) { + int n; if (!GLOBALS._bCfgTransparence) { - RMGfxSourceBuffer8RLEWord::RLEDecompressLine(dst, src, nStartSkip, nLength); + RMGfxSourceBuffer8RLEWord::rleDecompressLine(dst, src, nStartSkip, nLength); return; } @@ -1590,10 +1572,10 @@ RLEWordDoAlpha2: n = nLength; // @@@ SHOULD NOT BE THERE !!!!! - for (i = 0; i < n; i++) { - r = (*dst >> 10) & 0x1F; - g = (*dst >> 5) & 0x1F; - b = *dst & 0x1F; + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; r = (r >> 2) + (_alphaR >> 1); g = (g >> 2) + (_alphaG >> 1); @@ -1617,14 +1599,14 @@ RLEWordDoCopy2: if (n > nLength) n = nLength; - for (i = 0; i < n; i++) { - r = (*dst >> 10) & 0x1F; - g = (*dst >> 5) & 0x1F; - b = *dst & 0x1F; + for (int i = 0; i < n; i++) { + int r = (*dst >> 10) & 0x1F; + int g = (*dst >> 5) & 0x1F; + int b = *dst & 0x1F; - r2 = (_palFinal[*src] >> 10) & 0x1F; - g2 = (_palFinal[*src] >> 5) & 0x1F; - b2 = _palFinal[*src] & 0x1F; + int r2 = (_palFinal[*src] >> 10) & 0x1F; + int g2 = (_palFinal[*src] >> 5) & 0x1F; + int b2 = _palFinal[*src] & 0x1F; r = (r >> 1) + (r2 >> 1); g = (g >> 1) + (g2 >> 1); @@ -1658,7 +1640,6 @@ void RMGfxSourceBuffer8AA::prepareImage() { } void RMGfxSourceBuffer8AA::calculateAA() { - int x, y; byte *src, *srcaa; // First pass: fill the edges @@ -1666,8 +1647,8 @@ void RMGfxSourceBuffer8AA::calculateAA() { src = _buf; srcaa = _megaAABuf; - for (y = 0; y < _dimy; y++) { - for (x = 0; x < _dimx; x++) { + for (int y = 0; y < _dimy; y++) { + for (int x = 0; x < _dimx; x++) { if (*src == 0) { if ((y > 0 && src[-_dimx] != 0) || (y < _dimy - 1 && src[_dimx] != 0) || @@ -1683,8 +1664,8 @@ void RMGfxSourceBuffer8AA::calculateAA() { src = _buf; srcaa = _megaAABuf; - for (y = 0; y < _dimy; y++) { - for (x = 0; x < _dimx; x++) { + for (int y = 0; y < _dimy; y++) { + for (int x = 0; x < _dimx; x++) { if (*src != 0) { if ((y > 0 && srcaa[-_dimx] == 1) || (y < _dimy - 1 && srcaa[_dimx] == 1) || @@ -1715,23 +1696,20 @@ RMGfxSourceBuffer8AA::~RMGfxSourceBuffer8AA() { } void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { - int x, y; byte *src; uint16 *mybuf; uint16 *buf; - int x1, y1, u, v, width, height; - int r, g, b; - int step; + int u, v, width, height; // Clip the sprite - x1 = prim->getDst()._x1; - y1 = prim->getDst()._y1; + int x1 = prim->getDst()._x1; + int y1 = prim->getDst()._y1; if (!clip2D(x1, y1, u, v, width, height, false, &bigBuf)) return; // Go forward through the RLE lines src = _buf; - for (y = 0; y < v; y++) + for (int y = 0; y < v; y++) src += READ_LE_UINT16(src); // Eliminate horizontal clipping @@ -1758,6 +1736,7 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri buf = bigBuf; buf += y1 * bigBuf.getDimx(); + int step; if (prim->isFlipped()) step = -1; else @@ -1765,17 +1744,17 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri // Loop buf += bigBuf.getDimx(); // Skip the first line - for (y = 1; y < height - 1; y++) { + for (int y = 1; y < height - 1; y++) { // if (prim->IsFlipped()) // mybuf=&buf[x1+m_dimx-1]; // else mybuf = &buf[x1]; - for (x = 0; x < width; x++, mybuf += step) { + for (int x = 0; x < width; x++, mybuf += step) { if (_aabuf[(y + v) * _dimx + x + u] == 2 && x != 0 && x != width - 1) { - r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]); - g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]); - b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]); + int r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]); + int g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]); + int b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]); r += GETRED(mybuf[0]); g += GETGREEN(mybuf[0]); @@ -1785,9 +1764,12 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri g /= 5; b /= 5; - if (r > 31) r = 31; - if (g > 31) g = 31; - if (b > 31) b = 31; + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; mybuf[0] = (r << 10) | (g << 5) | b; } @@ -1803,17 +1785,17 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri // Looppone buf += bigBuf.getDimx(); - for (y = 1; y < height - 1; y++) { + for (int y = 1; y < height - 1; y++) { // if (prim->IsFlipped()) // mybuf=&buf[x1+m_dimx-1]; // else mybuf = &buf[x1]; - for (x = 0; x < width; x++, mybuf += step) { + for (int x = 0; x < width; x++, mybuf += step) { if (_aabuf[(y + v) * _dimx + x + u] == 1 && x != 0 && x != width - 1) { - r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]); - g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]); - b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]); + int r = GETRED(mybuf[1]) + GETRED(mybuf[-1]) + GETRED(mybuf[-bigBuf.getDimx()]) + GETRED(mybuf[bigBuf.getDimx()]); + int g = GETGREEN(mybuf[1]) + GETGREEN(mybuf[-1]) + GETGREEN(mybuf[-bigBuf.getDimx()]) + GETGREEN(mybuf[bigBuf.getDimx()]); + int b = GETBLUE(mybuf[1]) + GETBLUE(mybuf[-1]) + GETBLUE(mybuf[-bigBuf.getDimx()]) + GETBLUE(mybuf[bigBuf.getDimx()]); r += GETRED(mybuf[0]) * 2; g += GETGREEN(mybuf[0]) * 2; @@ -1823,9 +1805,12 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri g /= 6; b /= 6; - if (r > 31) r = 31; - if (g > 31) g = 31; - if (b > 31) b = 31; + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; mybuf[0] = (r << 10) | (g << 5) | b; } @@ -1941,16 +1926,13 @@ RMGfxSourceBuffer16::~RMGfxSourceBuffer16() { void RMGfxSourceBuffer16::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { uint16 *buf = bigBuf; uint16 *raw = (uint16 *)_buf; - int dimx, dimy; - int u, v; - int x1, y1; - dimx = _dimx; - dimy = _dimy; - u = 0; - v = 0; - x1 = 0; - y1 = 0; + int dimx = _dimx; + int dimy = _dimy; + int u = 0; + int v = 0; + int x1 = 0; + int y1 = 0; if (prim->haveSrc()) { u = prim->getSrc()._x1; @@ -1999,10 +1981,9 @@ void RMGfxSourceBuffer16::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimi void RMGfxSourceBuffer16::prepareImage() { // Color space conversion if necessary! - int i; uint16 *buf = (uint16 *)_buf; - for (i = 0; i < _dimx * _dimy; i++) + for (int i = 0; i < _dimx * _dimy; i++) WRITE_LE_UINT16(&buf[i], FROM_LE_16(buf[i]) & 0x7FFF); } @@ -2041,7 +2022,6 @@ void RMGfxBox::setColor(byte r, byte g, byte b) { } void RMGfxBox::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { - int i, j; uint16 *buf = bigBuf; RMRect rcDst; @@ -2050,8 +2030,8 @@ void RMGfxBox::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) buf += rcDst._y1 * bigBuf.getDimx() + rcDst._x1; // Loop through the pixels - for (j = 0; j < rcDst.height(); j++) { - for (i = 0; i < rcDst.width(); i++) + for (int j = 0; j < rcDst.height(); j++) { + for (int i = 0; i < rcDst.width(); i++) *buf++ = _wFillColor; buf += bigBuf.getDimx() - rcDst.width(); diff --git a/engines/tony/gfxcore.h b/engines/tony/gfxcore.h index ac4eee05e4..f0deed83ee 100644 --- a/engines/tony/gfxcore.h +++ b/engines/tony/gfxcore.h @@ -146,7 +146,7 @@ public: // Registration virtual void Register(); - virtual void Unregister(); + virtual void unregister(); }; @@ -303,12 +303,12 @@ protected: protected: static byte _megaRLEBuf[]; - virtual void RLEWriteTrasp(byte *&cur, int rep) = 0; - virtual void RLEWriteData(byte *&cur, int rep, byte *src) = 0; - virtual void RLEWriteEOL(byte *&cur) = 0; - virtual void RLEWriteAlphaBlend(byte *&cur, int rep) = 0; - virtual void RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0; - virtual void RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0; + virtual void rleWriteTrasp(byte *&cur, int rep) = 0; + virtual void rleWriteData(byte *&cur, int rep, byte *src) = 0; + virtual void rleWriteEOL(byte *&cur) = 0; + virtual void rleWriteAlphaBlend(byte *&cur, int rep) = 0; + virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0; + virtual void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength) = 0; // Perform image compression in RLE void compressRLE(); @@ -338,12 +338,12 @@ public: class RMGfxSourceBuffer8RLEByte : public RMGfxSourceBuffer8RLE { protected: - void RLEWriteTrasp(byte * &cur, int rep); - void RLEWriteAlphaBlend(byte * &cur, int rep); - void RLEWriteData(byte * &cur, int rep, byte *src); - void RLEWriteEOL(byte * &cur); - void RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); - void RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength); + void rleWriteTrasp(byte * &cur, int rep); + void rleWriteAlphaBlend(byte * &cur, int rep); + void rleWriteData(byte * &cur, int rep, byte *src); + void rleWriteEOL(byte * &cur); + void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); + void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength); public: virtual ~RMGfxSourceBuffer8RLEByte(); @@ -351,12 +351,12 @@ public: class RMGfxSourceBuffer8RLEWord : public RMGfxSourceBuffer8RLE { protected: - void RLEWriteTrasp(byte * &cur, int rep); - void RLEWriteAlphaBlend(byte * &cur, int rep); - void RLEWriteData(byte * &cur, int rep, byte *src); - void RLEWriteEOL(byte * &cur); - virtual void RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); - virtual void RLEDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength); + void rleWriteTrasp(byte * &cur, int rep); + void rleWriteAlphaBlend(byte * &cur, int rep); + void rleWriteData(byte * &cur, int rep, byte *src); + void rleWriteEOL(byte * &cur); + virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); + virtual void rleDecompressLineFlipped(uint16 *dst, byte *src, int nStartSkip, int nLength); public: virtual ~RMGfxSourceBuffer8RLEWord(); @@ -364,7 +364,7 @@ public: class RMGfxSourceBuffer8RLEWordAB : public RMGfxSourceBuffer8RLEWord { protected: - virtual void RLEDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); + virtual void rleDecompressLine(uint16 *dst, byte *src, int nStartSkip, int nLength); public: virtual ~RMGfxSourceBuffer8RLEWordAB(); diff --git a/engines/tony/gfxengine.cpp b/engines/tony/gfxengine.cpp index 5c038e154d..c81e553770 100644 --- a/engines/tony/gfxengine.cpp +++ b/engines/tony/gfxengine.cpp @@ -40,7 +40,7 @@ namespace Tony { * RMGfxEngine Methods \****************************************************************************/ -void ExitAllIdles(CORO_PARAM, const void *param) { +void exitAllIdles(CORO_PARAM, const void *param) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -129,7 +129,7 @@ void RMGfxEngine::openOptionScreen(CORO_PARAM, int type) { GLOBALS._bIdleExited = false; - CoroScheduler.createProcess(ExitAllIdles, &_nCurLoc, sizeof(int)); + CoroScheduler.createProcess(exitAllIdles, &_nCurLoc, sizeof(int)); } } @@ -394,13 +394,10 @@ void RMGfxEngine::initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint star } uint32 RMGfxEngine::loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) { - bool bLoaded; - int i; - _nCurLoc = nLoc; - bLoaded = false; - for (i = 0; i < 5; i++) { + bool bLoaded = false; + for (int i = 0; i < 5; i++) { // Try the loading of the location RMRes res(_nCurLoc); if (!res.isValid()) @@ -532,36 +529,22 @@ void RMGfxEngine::disableMouse() { _bAlwaysDrawMouse = false; } -void CharsSaveAll(Common::OutSaveFile *f); -void CharsLoadAll(Common::InSaveFile *f); -void MCharResetCodes(); -void SaveChangedHotspot(Common::OutSaveFile *f); -void LoadChangedHotspot(Common::InSaveFile *f); -void ReapplyChangedHotspot(); - -void RestoreMusic(CORO_PARAM); -void SaveMusic(Common::OutSaveFile *f); -void LoadMusic(Common::InSaveFile *f); - #define TONY_SAVEGAME_VERSION 8 void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Common::String &name) { Common::OutSaveFile *f; byte *state; - uint thumbsize; - uint size; - int i; char buf[4]; RMPoint tp = _tony.position(); // Saving: MPAL variables, current location, and Tony inventory position // For now, we only save the MPAL state - size = mpalGetSaveStateSize(); + uint size = mpalGetSaveStateSize(); state = new byte[size]; mpalSaveState(state); - thumbsize = 160 * 120 * 2; + uint thumbsize = 160 * 120 * 2; buf[0] = 'R'; buf[1] = 'M'; @@ -577,7 +560,7 @@ void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Comm f->write(curThumb, thumbsize); // Difficulty level - i = mpalQueryGlobalVar("VERSIONEFACILE"); + int i = mpalQueryGlobalVar("VERSIONEFACILE"); f->writeByte(i); i = strlen(name.c_str()); @@ -608,16 +591,14 @@ void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Comm delete[] state; // New Ver5 - bool bStat; - // Saves the state of the shepherdess and show yourself - bStat = _tony.getShepherdess(); + bool bStat = _tony.getShepherdess(); f->writeByte(bStat); bStat = _inter.getPerorate(); f->writeByte(bStat); // Save the chars - CharsSaveAll(f); + charsSaveAll(f); // Save the options f->writeByte(GLOBALS._bCfgInvLocked); @@ -639,10 +620,10 @@ void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Comm f->writeByte(GLOBALS._nCfgSFXVolume); // Save the hotspots - SaveChangedHotspot(f); + saveChangedHotspot(f); // Save the music - SaveMusic(f); + saveMusic(f); f->finalize(); delete f; @@ -653,7 +634,7 @@ void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) { CORO_BEGIN_CONTEXT; Common::InSaveFile *f; byte *state, *statecmp; - uint size, sizecmp; + uint32 size, sizecmp; char buf[4]; RMPoint tp; int loc; @@ -734,7 +715,7 @@ void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) { _inv.loadState(_ctx->state); delete[] _ctx->state; - if (_ctx->ver >= 0x2) { // Versione 2: box please + if (_ctx->ver >= 0x2) { // Version 2: box please _ctx->size = _ctx->f->readUint32LE(); _ctx->state = new byte[_ctx->size]; _ctx->f->read(_ctx->state, _ctx->size); @@ -743,7 +724,7 @@ void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) { } if (_ctx->ver >= 5) { - // Versione 5 + // Version 5 bool bStat = false; bStat = _ctx->f->readByte(); @@ -751,7 +732,7 @@ void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) { bStat = _ctx->f->readByte(); _inter.setPerorate(bStat); - CharsLoadAll(_ctx->f); + charsLoadAll(_ctx->f); } if (_ctx->ver >= 6) { @@ -775,11 +756,11 @@ void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) { GLOBALS._nCfgSFXVolume = _ctx->f->readByte(); // Load hotspots - LoadChangedHotspot(_ctx->f); + loadChangedHotspot(_ctx->f); } if (_ctx->ver >= 7) { - LoadMusic(_ctx->f); + loadMusic(_ctx->f); } delete _ctx->f; @@ -793,13 +774,13 @@ void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) { mpalQueryDoAction(0, _ctx->loc, 0); else { // In the new ones, we just reset the mcode - MCharResetCodes(); + mCharResetCodes(); } if (_ctx->ver >= 6) - ReapplyChangedHotspot(); + reapplyChangedHotspot(); - CORO_INVOKE_0(RestoreMusic); + CORO_INVOKE_0(restoreMusic); _bGUIInterface = true; _bGUIInventory = true; diff --git a/engines/tony/inventory.cpp b/engines/tony/inventory.cpp index 81d62a035c..12540e5b7f 100644 --- a/engines/tony/inventory.cpp +++ b/engines/tony/inventory.cpp @@ -73,9 +73,6 @@ bool RMInventory::checkPointInside(const RMPoint &pt) { void RMInventory::init() { - int i, j; - int curres; - // Create the main buffer create(RM_SX, 68); setPriority(185); @@ -89,10 +86,10 @@ void RMInventory::init() { _nItems = 78; // @@@ Number of takeable items _items = new RMInventoryItem[_nItems + 1]; - curres = 10500; + int curres = 10500; // Loop through the items - for (i = 0; i <= _nItems; i++) { + for (int i = 0; i <= _nItems; i++) { // Load the items from the resource RMRes res(curres); assert(res.isValid()); @@ -115,7 +112,7 @@ void RMInventory::init() { _items[i]._pointer = new RMGfxSourceBuffer8RLEByteAA[_items[i]._icon.numPattern()]; - for (j = 0; j < _items[i]._icon.numPattern(); j++) { + for (int j = 0; j < _items[i]._icon.numPattern(); j++) { RMResRaw raw(curres); assert(raw.isValid()); @@ -232,9 +229,7 @@ void RMInventory::removeThis(CORO_PARAM, bool &result) { } void RMInventory::removeItem(int code) { - int i; - - for (i = 0; i < _nInv; i++) + for (int i = 0; i < _nInv; i++) { if (_inv[i] == code - 10000) { g_system->lockMutex(_csModifyInterface); @@ -247,6 +242,7 @@ void RMInventory::removeItem(int code) { g_system->unlockMutex(_csModifyInterface); return; } + } } void RMInventory::addItem(int code) { @@ -286,9 +282,7 @@ void RMInventory::changeItemStatus(uint32 code, uint32 dwStatus) { void RMInventory::prepare() { - int i; - - for (i = 1; i < RM_SX / 64 - 1; i++) { + for (int i = 1; i < RM_SX / 64 - 1; i++) { if (i - 1 + _curPos < _nInv) addPrim(new RMGfxPrimitive(&_items[_inv[i - 1 + _curPos]]._icon, RMPoint(i * 64, 0))); else @@ -325,10 +319,8 @@ void RMInventory::endCombine() { } bool RMInventory::leftClick(const RMPoint &mpos, int &nCombineObj) { - int n; - // The left click picks an item from your inventory to use it with the background - n = mpos._x / 64; + int n = mpos._x / 64; if (_state == OPENED) { if (n > 0 && n < RM_SX / 64 - 1 && _inv[n - 1 + _curPos] != 0) { @@ -679,18 +671,17 @@ RMItem *RMInventory::whichItemIsIn(const RMPoint &mpt) { int RMInventory::getSaveStateSize() { // m_inv pattern m_nInv - return 256 * 4 + 256 * 4 + 4 ; + return 256 * 4 + 256 * 4 + 4; } void RMInventory::saveState(byte *state) { - int i, x; - WRITE_LE_UINT32(state, _nInv); state += 4; Common::copy(_inv, _inv + 256, (uint32 *)state); state += 256 * 4; - for (i = 0; i < 256; i++) { + int x; + for (int i = 0; i < 256; i++) { if (i < _nItems) x = _items[i]._status; else @@ -702,14 +693,13 @@ void RMInventory::saveState(byte *state) { } int RMInventory::loadState(byte *state) { - int i, x; - _nInv = READ_LE_UINT32(state); state += 4; Common::copy((uint32 *)state, (uint32 *)state + 256, _inv); state += 256 * 4; - for (i = 0; i < 256; i++) { + int x; + for (int i = 0; i < 256; i++) { x = READ_LE_UINT32(state); state += 4; @@ -721,7 +711,7 @@ int RMInventory::loadState(byte *state) { _curPos = 0; _bCombining = false; - + _items[29]._icon.setPattern(1); if (_nInv > 8) @@ -768,17 +758,15 @@ bool RMInterface::active() { } int RMInterface::onWhichBox(RMPoint pt) { - int max, i; - pt -= _openStart; // Check how many verbs you have to consider - max = 4; + int max = 4; if (_bPerorate) max = 5; // Find the verb - for (i = 0; i < max; i++) { + for (int i = 0; i < max; i++) { if (_hotbbox[i].ptInRect(pt)) return i; } @@ -895,7 +883,6 @@ bool RMInterface::getPerorate() { } void RMInterface::init() { - int i; RMResRaw inter(RES_I_INTERFACE); RMRes pal(RES_I_INTERPPAL); @@ -904,7 +891,7 @@ void RMInterface::init() { RMGfxSourceBuffer::init(inter, inter.width(), inter.height()); loadPaletteWA(RES_I_INTERPAL); - for (i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { RMResRaw part(RES_I_INTERP1 + i); _hotzone[i].init(part, part.width(), part.height()); @@ -942,11 +929,9 @@ void RMInterface::init() { } void RMInterface::close() { - int i; - destroy(); - for (i = 0; i < 5; i++) + for (int i = 0; i < 5; i++) _hotzone[i].destroy(); } diff --git a/engines/tony/loc.cpp b/engines/tony/loc.cpp index 4fe19594f9..18470aa6fc 100644 --- a/engines/tony/loc.cpp +++ b/engines/tony/loc.cpp @@ -51,10 +51,8 @@ void RMPalette::readFromStream(Common::ReadStream &ds) { \****************************************************************************/ void RMPattern::RMSlot::readFromStream(Common::ReadStream &ds, bool bLOX) { - byte type; - // Type - type = ds.readByte(); + byte type = ds.readByte(); _type = (RMPattern::RMSlotType)type; // Data @@ -73,8 +71,6 @@ void RMPattern::RMSlot::readFromStream(Common::ReadStream &ds, bool bLOX) { \****************************************************************************/ void RMPattern::readFromStream(Common::ReadStream &ds, bool bLOX) { - int i; - // Pattern name if (!bLOX) _name = readString(ds); @@ -94,7 +90,7 @@ void RMPattern::readFromStream(Common::ReadStream &ds, bool bLOX) { // Create and read the slots _slots = new RMSlot[_nSlots]; - for (i = 0; i < _nSlots && !ds.err(); i++) { + for (int i = 0; i < _nSlots && !ds.err(); i++) { if (bLOX) _slots[i].readFromStream(ds, true); else @@ -118,14 +114,12 @@ void RMPattern::stopSfx(RMSfx *sfx) { } int RMPattern::init(RMSfx *sfx, bool bPlayP0, byte *bFlag) { - int i; - // Read the current time _nStartTime = g_vm->getTime(); _nCurSlot = 0; // Find the first frame of the pattern - i = 0; + int i = 0; while (_slots[i]._type != SPRITE) { assert(i + 1 < _nSlots); i++; @@ -297,15 +291,13 @@ void RMSprite::getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int } void RMSprite::readFromStream(Common::SeekableReadStream &ds, bool bLOX) { - int dimx, dimy; - // Sprite name if (!bLOX) _name = readString(ds); // Dimensions - dimx = ds.readSint32LE(); - dimy = ds.readSint32LE(); + int dimx = ds.readSint32LE(); + int dimy = ds.readSint32LE(); // Bounding box _rcBox.readFromStream(ds); @@ -343,12 +335,10 @@ RMSprite::~RMSprite() { \****************************************************************************/ void RMSfx::readFromStream(Common::ReadStream &ds, bool bLOX) { - int size; - // sfx name _name = readString(ds); - size = ds.readSint32LE(); + int size = ds.readSint32LE(); // Read the entire buffer into a MemoryReadStream byte *buffer = (byte *)malloc(size); @@ -390,7 +380,7 @@ void RMSfx::setVolume(int vol) { void RMSfx::pause(bool bPause) { if (_fx) { - _fx->pause(bPause); + _fx->setPause(bPause); } } @@ -463,9 +453,6 @@ bool RMItem::isIn(const RMPoint &pt, int *size) { } void RMItem::readFromStream(Common::SeekableReadStream &ds, bool bLOX) { - int i, dimx, dimy; - byte cm; - // MPAL code _mpalCode = ds.readSint32LE(); @@ -490,7 +477,7 @@ void RMItem::readFromStream(Common::SeekableReadStream &ds, bool bLOX) { _nPatterns = ds.readSint32LE(); // Color mode - cm = ds.readByte(); + byte cm = ds.readByte(); _cm = (RMColorMode)cm; // Flag for the presence of custom palette differences @@ -519,9 +506,10 @@ void RMItem::readFromStream(Common::SeekableReadStream &ds, bool bLOX) { _sfx = new RMSfx[_nSfx]; _patterns = new RMPattern[_nPatterns + 1]; + int dimx, dimy; // Read in class data - if (!ds.err()) - for (i = 0; i < _nSprites && !ds.err(); i++) { + if (!ds.err()) { + for (int i = 0; i < _nSprites && !ds.err(); i++) { // Download the sprites if (bLOX) { _sprites[i].LOXGetSizeFromStream(ds, &dimx, &dimy); @@ -536,23 +524,26 @@ void RMItem::readFromStream(Common::SeekableReadStream &ds, bool bLOX) { if (_cm == CM_256 && _bPal) _sprites[i].setPalette(_pal._data); } + } - if (!ds.err()) - for (i = 0; i < _nSfx && !ds.err(); i++) { + if (!ds.err()) { + for (int i = 0; i < _nSfx && !ds.err(); i++) { if (bLOX) _sfx[i].readFromStream(ds, true); else _sfx[i].readFromStream(ds, false); } + } // Read the pattern from pattern 1 - if (!ds.err()) - for (i = 1; i <= _nPatterns && !ds.err(); i++) { + if (!ds.err()) { + for (int i = 1; i <= _nPatterns && !ds.err(); i++) { if (bLOX) _patterns[i].readFromStream(ds, true); else _patterns[i].readFromStream(ds, false); } + } // Initialize the current pattern if (_bInitCurPattern) @@ -662,7 +653,7 @@ void RMItem::setStatus(int nStatus) { _bIsActive = (nStatus > 0); } -RMPoint RMItem::hotspot() { +RMPoint RMItem::getHotspot() { return _hot; } @@ -677,7 +668,7 @@ void RMItem::setPattern(int nPattern, bool bPlayP0) { if (_nCurPattern > 0) _patterns[_nCurPattern].stopSfx(_sfx); } - + // Remember the current pattern _nCurPattern = nPattern; @@ -789,14 +780,11 @@ void RMItem::playSfx(int nSfx) { } void RMItem::pauseSound(bool bPause) { - int i; - - for (i = 0; i < _nSfx; i++) + for (int i = 0; i < _nSfx; i++) _sfx[i].pause(bPause); } - /****************************************************************************\ * RMWipe Methods \****************************************************************************/ @@ -823,8 +811,8 @@ int RMWipe::priority() { return 200; } -void RMWipe::Unregister() { - RMGfxTask::Unregister(); +void RMWipe::unregister() { + RMGfxTask::unregister(); assert(_nInList == 0); CoroScheduler.setEvent(_hUnregistered); } @@ -952,7 +940,7 @@ short RMCharacter::findPath(short source, short destination) { error = 1; // Possible error // 1st cycle: explore possible new nodes - for (int i = 0; i < cur->_numbBox; i++) + for (int i = 0; i < cur->_numbBox; i++) { if (valid[i] == 1) { error = 0; // Failure de-bunked int j = 0; @@ -967,12 +955,13 @@ short RMCharacter::findPath(short source, short destination) { minCost = nodeCost[i] + 1; } } + } if (error) finish = true; // All nodes saturated // 2nd cycle: adding new nodes that were found, saturate old nodes - for (int i = 0; i < cur->_numbBox; i++) + for (int i = 0; i < cur->_numbBox; i++) { if ((valid[i] == 1) && ((nodeCost[i] + 1) == minCost)) { box[i]._adj[nextNode[i]] = 2; nodeCost[nextNode[i]] = minCost; @@ -984,6 +973,7 @@ short RMCharacter::findPath(short source, short destination) { if (nextNode[i] == destination) finish = true; } + } } // Remove the path from the adjacent modified matrixes @@ -1079,91 +1069,91 @@ void RMCharacter::goTo(CORO_PARAM, RMPoint destcoord, bool bReversed) { } -RMPoint RMCharacter::searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint punto) { - short passi, minimo; - RMPoint nuovo, trovato; - minimo = 32000; +RMPoint RMCharacter::searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint point) { + short steps; + RMPoint newPt, foundPt; + short minStep = 32000; if (UP) { - nuovo = punto; - passi = 0; - while ((inWhichBox(nuovo) == -1) && (nuovo._y >= 0)) { - nuovo._y--; - passi++; + newPt = point; + steps = 0; + while ((inWhichBox(newPt) == -1) && (newPt._y >= 0)) { + newPt._y--; + steps++; } - if ((inWhichBox(nuovo) != -1) && (passi < minimo) && - findPath(inWhichBox(_pos), inWhichBox(nuovo))) { - minimo = passi; - nuovo._y--; // to avoid error? - trovato = nuovo; + if ((inWhichBox(newPt) != -1) && (steps < minStep) && + findPath(inWhichBox(_pos), inWhichBox(newPt))) { + minStep = steps; + newPt._y--; // to avoid error? + foundPt = newPt; } } if (DOWN) { - nuovo = punto; - passi = 0; - while ((inWhichBox(nuovo) == -1) && (nuovo._y < 480)) { - nuovo._y++; - passi++; + newPt = point; + steps = 0; + while ((inWhichBox(newPt) == -1) && (newPt._y < 480)) { + newPt._y++; + steps++; } - if ((inWhichBox(nuovo) != -1) && (passi < minimo) && - findPath(inWhichBox(_pos), inWhichBox(nuovo))) { - minimo = passi; - nuovo._y++; // to avoid error? - trovato = nuovo; + if ((inWhichBox(newPt) != -1) && (steps < minStep) && + findPath(inWhichBox(_pos), inWhichBox(newPt))) { + minStep = steps; + newPt._y++; // to avoid error? + foundPt = newPt; } } if (RIGHT) { - nuovo = punto; - passi = 0; - while ((inWhichBox(nuovo) == -1) && (nuovo._x < 640)) { - nuovo._x++; - passi++; + newPt = point; + steps = 0; + while ((inWhichBox(newPt) == -1) && (newPt._x < 640)) { + newPt._x++; + steps++; } - if ((inWhichBox(nuovo) != -1) && (passi < minimo) && - findPath(inWhichBox(_pos), inWhichBox(nuovo))) { - minimo = passi; - nuovo._x++; // to avoid error? - trovato = nuovo; + if ((inWhichBox(newPt) != -1) && (steps < minStep) && + findPath(inWhichBox(_pos), inWhichBox(newPt))) { + minStep = steps; + newPt._x++; // to avoid error? + foundPt = newPt; } } if (LEFT) { - nuovo = punto; - passi = 0; - while ((inWhichBox(nuovo) == -1) && (nuovo._x >= 0)) { - nuovo._x--; - passi++; + newPt = point; + steps = 0; + while ((inWhichBox(newPt) == -1) && (newPt._x >= 0)) { + newPt._x--; + steps++; } - if ((inWhichBox(nuovo) != -1) && (passi < minimo) && - findPath(inWhichBox(_pos), inWhichBox(nuovo))) { - minimo = passi; - nuovo._x--; // to avoid error? - trovato = nuovo; + if ((inWhichBox(newPt) != -1) && (steps < minStep) && + findPath(inWhichBox(_pos), inWhichBox(newPt))) { + minStep = steps; + newPt._x--; // to avoid error? + foundPt = newPt; } } - if (minimo == 32000) - trovato = punto; + if (minStep == 32000) + foundPt = point; - return trovato; + return foundPt; } -RMPoint RMCharacter::nearestPoint(const RMPoint &punto) { - return searching(1, 1, 1, 1, punto); +RMPoint RMCharacter::nearestPoint(const RMPoint &point) { + return searching(1, 1, 1, 1, point); } -short RMCharacter::scanLine(const RMPoint &punto) { +short RMCharacter::scanLine(const RMPoint &point) { int Ldx, Ldy, Lcount; float Lfx, Lfy, Lslope; RMPoint Lstart, Lend, Lscan; signed char Lspeed, Lstatus; Lstart = _pos; - Lend = punto; + Lend = point; Ldx = Lstart._x - Lend._x; Ldy = Lstart._y - Lend._y; Lfx = Ldx; @@ -1209,60 +1199,60 @@ short RMCharacter::scanLine(const RMPoint &punto) { /** * Calculates intersections between the straight line and the closest BBOX */ -RMPoint RMCharacter::invScanLine(const RMPoint &punto) { - int Ldx, Ldy, Lcount; - float Lfx, Lfy, Lslope; - RMPoint Lstart, Lend, Lscan; - signed char Lspeed, Lstatus, Lbox = -1; - - Lstart = punto; // Exchange! - Lend = _pos; // :-) - Ldx = Lstart._x - Lend._x; - Ldy = Lstart._y - Lend._y; - Lfx = Ldx; - Lfy = Ldy; - Ldx = ABS(Ldx); - Ldy = ABS(Ldy); - Lspeed = 1; - Lcount = 0; - - if (Ldx > Ldy) { - Lslope = Lfy / Lfx; - if (Lend._x < Lstart._x) - Lspeed = -Lspeed; - Lstatus = 1; +RMPoint RMCharacter::invScanLine(const RMPoint &point) { + RMPoint lStart = point; // Exchange! + RMPoint lEnd = _pos; // :-) + int lDx = lStart._x - lEnd._x; + int lDy = lStart._y - lEnd._y; + float lFx = lDx; + float lFy = lDy; + lDx = ABS(lDx); + lDy = ABS(lDy); + signed char lSpeed = 1; + int lCount = 0; + + signed char lStatus; + float lSlope; + + if (lDx > lDy) { + lSlope = lFy / lFx; + if (lEnd._x < lStart._x) + lSpeed = -lSpeed; + lStatus = 1; } else { - Lslope = Lfx / Lfy; - if (Lend._y < Lstart._y) - Lspeed = -Lspeed; - Lstatus = 0; + lSlope = lFx / lFy; + if (lEnd._y < lStart._y) + lSpeed = -lSpeed; + lStatus = 0; } - Lscan = Lstart; + + RMPoint lScan = lStart; + signed char lBox = -1; for (;;) { - if (inWhichBox(Lscan) != -1) { - if (inWhichBox(Lscan) != Lbox) { - if (inWhichBox(_pos) == inWhichBox(Lscan) || findPath(inWhichBox(_pos), inWhichBox(Lscan))) - return Lscan; + if (inWhichBox(lScan) != -1) { + if (inWhichBox(lScan) != lBox) { + if (inWhichBox(_pos) == inWhichBox(lScan) || findPath(inWhichBox(_pos), inWhichBox(lScan))) + return lScan; else - Lbox = inWhichBox(Lscan); + lBox = inWhichBox(lScan); } } - Lcount++; - if (Lstatus) { - Ldx = Lspeed * Lcount; - Ldy = (int)(Lslope * Ldx); + lCount++; + if (lStatus) { + lDx = lSpeed * lCount; + lDy = (int)(lSlope * lDx); } else { - Ldy = Lspeed * Lcount; - Ldx = (int)(Lslope * Ldy); + lDy = lSpeed * lCount; + lDx = (int)(lSlope * lDy); } - Lscan._x = Lstart._x + Ldx; - Lscan._y = Lstart._y + Ldy; + lScan._x = lStart._x + lDx; + lScan._y = lStart._y + lDy; // WORKAROUND: Handles cases where the points never fall inside a bounding box - if (Lscan._x < -100 || Lscan._y < -100 || Lscan._x >= 1000 || Lscan._y >= 1000) - return punto; + if (lScan._x < -100 || lScan._y < -100 || lScan._x >= 1000 || lScan._y >= 1000) + return point; } } @@ -1272,25 +1262,24 @@ RMPoint RMCharacter::invScanLine(const RMPoint &punto) { */ RMPoint RMCharacter::nearestHotSpot(int sourcebox, int destbox) { - RMPoint puntocaldo; - short cc; - int x, y, distanzaminima; - distanzaminima = 10000000; + RMPoint hotspot; + int x, y; + int minDist = 10000000; RMBoxLoc *cur = _theBoxes->getBoxes(_curLocation); - for (cc = 0; cc < cur->_boxes[sourcebox]._numHotspot; cc++) + for (short cc = 0; cc < cur->_boxes[sourcebox]._numHotspot; cc++) if ((cur->_boxes[sourcebox]._hotspot[cc]._destination) == destbox) { x = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hotx - _pos._x); y = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hoty - _pos._y); - if ((x * x + y * y) < distanzaminima) { - distanzaminima = x * x + y * y; - puntocaldo._x = cur->_boxes[sourcebox]._hotspot[cc]._hotx; - puntocaldo._y = cur->_boxes[sourcebox]._hotspot[cc]._hoty; + if ((x * x + y * y) < minDist) { + minDist = x * x + y * y; + hotspot._x = cur->_boxes[sourcebox]._hotspot[cc]._hotx; + hotspot._y = cur->_boxes[sourcebox]._hotspot[cc]._hoty; } } - return puntocaldo; + return hotspot; } void RMCharacter::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) { @@ -1310,13 +1299,12 @@ void RMCharacter::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pr void RMCharacter::newBoxEntered(int nBox) { RMBoxLoc *cur; - bool bOldReverse; // Recall on ExitBox mpalQueryDoAction(3, _curLocation, _curBox); cur = _theBoxes->getBoxes(_curLocation); - bOldReverse = cur->_boxes[_curBox]._bReversed; + bool bOldReverse = cur->_boxes[_curBox]._bReversed; _curBox = nBox; // If Z is changed, we must remove it from the OT @@ -1673,7 +1661,7 @@ RMCharacter::RMCharacter() { _bMovingWithoutMinpath = false; _bDrawNow = false; _bNeedToStop = false; - + memset(_path, 0, sizeof(_path)); _pos.set(0, 0); @@ -1693,10 +1681,6 @@ void RMCharacter::linkToBoxes(RMGameBoxes *boxes) { \****************************************************************************/ void RMBox::readFromStream(Common::ReadStream &ds) { - uint16 w; - int i; - byte b; - // Bbox _left = ds.readSint32LE(); _top = ds.readSint32LE(); @@ -1704,24 +1688,25 @@ void RMBox::readFromStream(Common::ReadStream &ds) { _bottom = ds.readSint32LE(); // Adjacency - for (i = 0; i < MAXBOXES; i++) { + for (int i = 0; i < MAXBOXES; i++) { _adj[i] = ds.readSint32LE(); } // Misc _numHotspot = ds.readSint32LE(); _destZ = ds.readByte(); - b = ds.readByte(); + byte b = ds.readByte(); _bActive = b; b = ds.readByte(); _bReversed = b; // Reversed expansion space - for (i = 0; i < 30; i++) + for (int i = 0; i < 30; i++) ds.readByte(); + uint16 w; // Hotspots - for (i = 0; i < _numHotspot; i++) { + for (int i = 0; i < _numHotspot; i++) { w = ds.readUint16LE(); _hotspot[i]._hotx = w; w = ds.readUint16LE(); @@ -1745,14 +1730,12 @@ RMBoxLoc::~RMBoxLoc() { } void RMBoxLoc::readFromStream(Common::ReadStream &ds) { - int i; char buf[2]; - byte ver; // ID and version buf[0] = ds.readByte(); buf[1] = ds.readByte(); - ver = ds.readByte(); + byte ver = ds.readByte(); assert(buf[0] == 'B' && buf[1] == 'X'); assert(ver == 3); @@ -1763,19 +1746,18 @@ void RMBoxLoc::readFromStream(Common::ReadStream &ds) { _boxes = new RMBox[_numbBox]; // Read in boxes - for (i = 0; i < _numbBox; i++) + for (int i = 0; i < _numbBox; i++) _boxes[i].readFromStream(ds); } void RMBoxLoc::recalcAllAdj() { - int i, j; - - for (i = 0; i < _numbBox; i++) { + for (int i = 0; i < _numbBox; i++) { Common::fill(_boxes[i]._adj, _boxes[i]._adj + MAXBOXES, 0); - for (j = 0; j < _boxes[i]._numHotspot; j++) + for (int j = 0; j < _boxes[i]._numHotspot; j++) { if (_boxes[_boxes[i]._hotspot[j]._destination]._bActive) _boxes[i]._adj[_boxes[i]._hotspot[j]._destination] = 1; + } } } @@ -1794,11 +1776,9 @@ RMGameBoxes::~RMGameBoxes() { } void RMGameBoxes::init() { - int i; - // Load boxes from disk _nLocBoxes = 130; - for (i = 1; i <= _nLocBoxes; i++) { + for (int i = 1; i <= _nLocBoxes; i++) { RMRes res(10000 + i); Common::SeekableReadStream *ds = res.getReadStream(); @@ -1834,13 +1814,12 @@ bool RMGameBoxes::isInBox(int nLoc, int nBox, const RMPoint &pt) { } int RMGameBoxes::whichBox(int nLoc, const RMPoint &punto) { - int i; RMBoxLoc *cur = getBoxes(nLoc); if (!cur) return -1; - for (i = 0; i < cur->_numbBox; i++) { + for (int i = 0; i < cur->_numbBox; i++) { if (cur->_boxes[i]._bActive) { if ((punto._x >= cur->_boxes[i]._left) && (punto._x <= cur->_boxes[i]._right) && (punto._y >= cur->_boxes[i]._top) && (punto._y <= cur->_boxes[i]._bottom)) @@ -1857,12 +1836,9 @@ void RMGameBoxes::changeBoxStatus(int nLoc, int nBox, int status) { } int RMGameBoxes::getSaveStateSize() { - int size; - int i; + int size = 4; - size = 4; - - for (i = 1; i <= _nLocBoxes; i++) { + for (int i = 1; i <= _nLocBoxes; i++) { size += 4; size += _allBoxes[i]->_numbBox; } @@ -1871,38 +1847,34 @@ int RMGameBoxes::getSaveStateSize() { } void RMGameBoxes::saveState(byte *state) { - int i, j; - // Save the number of locations with boxes WRITE_LE_UINT32(state, _nLocBoxes); state += 4; // For each location, write out the number of boxes and their status - for (i = 1; i <= _nLocBoxes; i++) { + for (int i = 1; i <= _nLocBoxes; i++) { WRITE_LE_UINT32(state, _allBoxes[i]->_numbBox); state += 4; - for (j = 0; j < _allBoxes[i]->_numbBox; j++) + for (int j = 0; j < _allBoxes[i]->_numbBox; j++) *state++ = _allBoxes[i]->_boxes[j]._bActive; } } void RMGameBoxes::loadState(byte *state) { - int i, j; - int nloc, nbox; - // Load number of items - nloc = READ_LE_UINT32(state); + int nloc = READ_LE_UINT32(state); state += 4; assert(nloc <= _nLocBoxes); + int nbox; // For each location, read the number of boxes and their status - for (i = 1; i <= nloc; i++) { + for (int i = 1; i <= nloc; i++) { nbox = READ_LE_UINT32(state); state += 4; - for (j = 0; j < nbox ; j++) { + for (int j = 0; j < nbox ; j++) { if (j < _allBoxes[i]->_numbBox) _allBoxes[i]->_boxes[j]._bActive = *state; @@ -1944,10 +1916,6 @@ int RMLocation::TEMPGetNumLoc() { */ bool RMLocation::load(Common::SeekableReadStream &ds) { char id[3]; - int dimx, dimy; - byte ver; - byte cm; - int i; // Reset dirty rectangling _prevScroll.set(-1, -1); @@ -1965,7 +1933,7 @@ bool RMLocation::load(Common::SeekableReadStream &ds) { return false; // Version - ver = ds.readByte(); + byte ver = ds.readByte(); assert(ver == 6); // Location name @@ -1981,12 +1949,12 @@ bool RMLocation::load(Common::SeekableReadStream &ds) { ds.skip(1); // Location dimensions - dimx = ds.readSint32LE(); - dimy = ds.readSint32LE(); + int dimx = ds.readSint32LE(); + int dimy = ds.readSint32LE(); _curScroll.set(0, 0); // Read the color mode - cm = ds.readByte(); + byte cm = ds.readByte(); _cmode = (RMColorMode)cm; // Initialize the source buffer and read the location @@ -2019,7 +1987,7 @@ bool RMLocation::load(Common::SeekableReadStream &ds) { g_vm->freezeTime(); - for (i = 0; i < _nItems && !ds.err(); i++) + for (int i = 0; i < _nItems && !ds.err(); i++) _items[i].readFromStream(ds); g_vm->unfreezeTime(); @@ -2028,12 +1996,8 @@ bool RMLocation::load(Common::SeekableReadStream &ds) { bool RMLocation::loadLOX(Common::SeekableReadStream &ds) { - int dimx, dimy; - byte ver; - int i; - // Version - ver = ds.readByte(); + byte ver = ds.readByte(); assert(ver == 1); // Location name @@ -2045,8 +2009,8 @@ bool RMLocation::loadLOX(Common::SeekableReadStream &ds) { TEMPTonyStart._y = ds.readSint32LE(); // Dimensions - dimx = ds.readSint32LE(); - dimy = ds.readSint32LE(); + int dimx = ds.readSint32LE(); + int dimy = ds.readSint32LE(); _curScroll.set(0, 0); // It's always 65K (16-bit) mode @@ -2063,7 +2027,7 @@ bool RMLocation::loadLOX(Common::SeekableReadStream &ds) { if (_nItems > 0) _items = new RMItem[_nItems]; - for (i = 0; i < _nItems && !ds.err(); i++) + for (int i = 0; i < _nItems && !ds.err(); i++) _items[i].readFromStream(ds, true); return ds.err(); @@ -2111,22 +2075,18 @@ void RMLocation::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri * Prepare a frame, adding the location to the OT list, and all the items that have changed animation frame. */ void RMLocation::doFrame(RMGfxTargetBuffer *bigBuf) { - int i; - // If the location is not in the OT list, add it in if (!_nInList) bigBuf->addPrim(new RMGfxPrimitive(this)); // Process all the location items - for (i = 0; i < _nItems; i++) + for (int i = 0; i < _nItems; i++) _items[i].doFrame(bigBuf); } RMItem *RMLocation::getItemFromCode(uint32 dwCode) { - int i; - - for (i = 0; i < _nItems; i++) { + for (int i = 0; i < _nItems; i++) { if (_items[i].mpalCode() == (int)dwCode) return &_items[i]; } @@ -2239,9 +2199,7 @@ void RMLocation::setScrollPosition(const RMPoint &scroll) { void RMLocation::pauseSound(bool bPause) { - int i; - - for (i = 0; i < _nItems; i++) + for (int i = 0; i < _nItems; i++) _items[i].pauseSound(bPause); } diff --git a/engines/tony/loc.h b/engines/tony/loc.h index 61eece2440..04ba772458 100644 --- a/engines/tony/loc.h +++ b/engines/tony/loc.h @@ -256,7 +256,7 @@ public: void setStatus(int nStatus); bool isIn(const RMPoint &pt, int *size = NULL); - RMPoint hotspot(); + RMPoint getHotspot(); bool getName(Common::String &name); int mpalCode(); @@ -403,12 +403,12 @@ private: int inWhichBox(const RMPoint &pt); short findPath(short source, short destination); - RMPoint searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint punto); + RMPoint searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint point); RMPoint nearestPoint(const RMPoint &punto); void goTo(CORO_PARAM, RMPoint destcoord, bool bReversed = false); - short scanLine(const RMPoint &punto); - RMPoint invScanLine(const RMPoint &punto); + short scanLine(const RMPoint &point); + RMPoint invScanLine(const RMPoint &point); RMPoint nearestHotSpot(int sourcebox, int destbox); void newBoxEntered(int nBox); @@ -478,7 +478,7 @@ public: void closeFade(); void waitForFadeEnd(CORO_PARAM); - virtual void Unregister(); + virtual void unregister(); virtual void removeThis(CORO_PARAM, bool &result); virtual int priority(); }; diff --git a/engines/tony/mpal/expr.cpp b/engines/tony/mpal/expr.cpp index 7923d263c0..824cd91651 100644 --- a/engines/tony/mpal/expr.cpp +++ b/engines/tony/mpal/expr.cpp @@ -35,68 +35,6 @@ namespace Tony { namespace MPAL { -/** - * @defgroup Mathamatical operations - */ -//@{ - -#define OP_MUL ((1 << 4) | 0) -#define OP_DIV ((1 << 4) | 1) -#define OP_MODULE ((1 << 4) | 2) -#define OP_ADD ((2 << 4) | 0) -#define OP_SUB ((2 << 4) | 1) -#define OP_SHL ((3 << 4) | 0) -#define OP_SHR ((3 << 4) | 1) -#define OP_MINOR ((4 << 4) | 0) -#define OP_MAJOR ((4 << 4) | 1) -#define OP_MINEQ ((4 << 4) | 2) -#define OP_MAJEQ ((4 << 4) | 3) -#define OP_EQUAL ((5 << 4) | 0) -#define OP_NOEQUAL ((5 << 4) | 1) -#define OP_BITAND ((6 << 4) | 0) -#define OP_BITXOR ((7 << 4) | 0) -#define OP_BITOR ((8 << 4) | 0) -#define OP_AND ((9 << 4) | 0) -#define OP_OR ((10 << 4) | 0) - - -/** - * Object types that can be contained in an EXPRESSION structure - */ -enum ExprListTypes { - ELT_NUMBER = 1, - ELT_VAR = 2, - ELT_PARENTH = 3, - ELT_PARENTH2 = 4 -}; - -//@} - -/** - * @defgroup Structures - */ -//@{ - -/** - * Mathamatical framework to manage operations - */ -typedef struct { - byte _type; // Tipo di oggetto (vedi enum ExprListTypes) - byte _unary; // Unary operatore (NON SUPPORTATO) - - union { - int _num; // Numero (se type==ELT_NUMBER) - char *_name; // Nome variabile (se type==ELT_VAR) - MpalHandle _son; // Handle a espressione (type==ELT_PARENTH) - byte *_pson; // Handle lockato (type==ELT_PARENTH2) - } _val; - - byte _symbol; // Simbolo matematico (vedi #define OP_*) - -} Expression; -typedef Expression *LpExpression; - -//@} /** * Duplicate a mathematical expression. @@ -106,15 +44,14 @@ typedef Expression *LpExpression; */ static byte *duplicateExpression(MpalHandle h) { byte *orig, *clone; - LpExpression one, two; orig = (byte *)globalLock(h); int num = *(byte *)orig; - one = (LpExpression)(orig+1); + LpExpression one = (LpExpression)(orig+1); clone = (byte *)globalAlloc(GMEM_FIXED, sizeof(Expression) * num + 1); - two = (LpExpression)(clone + 1); + LpExpression two = (LpExpression)(clone + 1); memcpy(clone, orig, sizeof(Expression) * num + 1); @@ -180,7 +117,6 @@ static int Compute(int a, int b, byte symbol) { static void solve(LpExpression one, int num) { LpExpression two, three; - int j; while (num > 1) { two = one + 1; @@ -189,7 +125,7 @@ static void solve(LpExpression one, int num) { memmove(one, two, (num - 1) * sizeof(Expression)); --num; } else { - j = 1; + int j = 1; three = two + 1; while ((three->_symbol != 0) && (two->_symbol & 0xF0) > (three->_symbol & 0xF0)) { ++two; @@ -213,13 +149,11 @@ static void solve(LpExpression one, int num) { * @returns Value */ static int evaluateAndFreeExpression(byte *expr) { - LpExpression one, cur; - int num = *expr; - one = (LpExpression)(expr + 1); + LpExpression one = (LpExpression)(expr + 1); // 1) Substitutions of variables - cur = one; + LpExpression cur = one; for (int i = 0; i < num; i++, cur++) { if (cur->_type == ELT_VAR) { cur->_type = ELT_NUMBER; @@ -254,7 +188,6 @@ static int evaluateAndFreeExpression(byte *expr) { * @returns Pointer to the buffer immediately after the expression, or NULL if error. */ const byte *parseExpression(const byte *lpBuf, MpalHandle *h) { - LpExpression cur; byte *start; uint32 num = *lpBuf; @@ -270,12 +203,14 @@ const byte *parseExpression(const byte *lpBuf, MpalHandle *h) { start = (byte *)globalLock(*h); *start = (byte)num; - cur = (LpExpression)(start + 1); + LpExpression cur = (LpExpression)(start + 1); for (uint32 i = 0;i < num; i++) { cur->_type = *(lpBuf); - cur->_unary = *(lpBuf + 1); + + // *(lpBuf + 1) contains the unary operator, unused => skipped lpBuf += 2; + switch (cur->_type) { case ELT_NUMBER: cur->_val._num = (int32)READ_LE_UINT32(lpBuf); @@ -322,10 +257,8 @@ const byte *parseExpression(const byte *lpBuf, MpalHandle *h) { * @returns Numeric value */ int evaluateExpression(MpalHandle h) { - int ret; - lockVar(); - ret = evaluateAndFreeExpression(duplicateExpression(h)); + int ret = evaluateAndFreeExpression(duplicateExpression(h)); unlockVar(); return ret; @@ -339,7 +272,6 @@ int evaluateExpression(MpalHandle h) { */ bool compareExpressions(MpalHandle h1, MpalHandle h2) { byte *e1, *e2; - LpExpression one, two; e1 = (byte *)globalLock(h1); e2 = (byte *)globalLock(h2); @@ -353,8 +285,8 @@ bool compareExpressions(MpalHandle h1, MpalHandle h2) { return false; } - one = (LpExpression)(e1 + 1); - two = (LpExpression)(e2 + 1); + LpExpression one = (LpExpression)(e1 + 1); + LpExpression two = (LpExpression)(e2 + 1); for (int i = 0; i < num1; i++) { if (one->_type != two->_type || (i != num1 - 1 && one->_symbol != two->_symbol)) { diff --git a/engines/tony/mpal/expr.h b/engines/tony/mpal/expr.h index 9036099993..405624b4fe 100644 --- a/engines/tony/mpal/expr.h +++ b/engines/tony/mpal/expr.h @@ -35,6 +35,67 @@ namespace Tony { namespace MPAL { +/** + * @defgroup Mathamatical operations + */ +//@{ + +#define OP_MUL ((1 << 4) | 0) +#define OP_DIV ((1 << 4) | 1) +#define OP_MODULE ((1 << 4) | 2) +#define OP_ADD ((2 << 4) | 0) +#define OP_SUB ((2 << 4) | 1) +#define OP_SHL ((3 << 4) | 0) +#define OP_SHR ((3 << 4) | 1) +#define OP_MINOR ((4 << 4) | 0) +#define OP_MAJOR ((4 << 4) | 1) +#define OP_MINEQ ((4 << 4) | 2) +#define OP_MAJEQ ((4 << 4) | 3) +#define OP_EQUAL ((5 << 4) | 0) +#define OP_NOEQUAL ((5 << 4) | 1) +#define OP_BITAND ((6 << 4) | 0) +#define OP_BITXOR ((7 << 4) | 0) +#define OP_BITOR ((8 << 4) | 0) +#define OP_AND ((9 << 4) | 0) +#define OP_OR ((10 << 4) | 0) + +//@} + +/** + * @defgroup Structures + */ + +//@{ +/** + * Mathamatical framework to manage operations + */ +typedef struct { + byte _type; // Object Type (see enum ExprListTypes) + + union { + int _num; // Identifier (if type == ELT_NUMBER) + char *_name; // Variable name (if type == ELT_VAR) + MpalHandle _son; // Handle expressions (if type == ELT_PARENTH) + byte *_pson; // Handle lockato (if type == ELT_PARENTH2) + } _val; + + byte _symbol; // Mathematic symbols (see #define OP_*) + +} Expression; +typedef Expression *LpExpression; + +//@} + +/** + * Object types that can be contained in an EXPRESSION structure + */ +enum ExprListTypes { + ELT_NUMBER = 1, + ELT_VAR = 2, + ELT_PARENTH = 3, + ELT_PARENTH2 = 4 +}; + /****************************************************************************\ * Function Prototypes \****************************************************************************/ diff --git a/engines/tony/mpal/loadmpc.cpp b/engines/tony/mpal/loadmpc.cpp index 953820be74..9c45cdf982 100644 --- a/engines/tony/mpal/loadmpc.cpp +++ b/engines/tony/mpal/loadmpc.cpp @@ -139,7 +139,6 @@ static void FreeScript(LpMpalScript lpmsScript) { * @returns Pointer to the buffer after the item, or NULL on failure. */ static const byte *parseDialog(const byte *lpBuf, LpMpalDialog lpmdDialog) { - uint32 num2, num3; byte *lpLock; lpmdDialog->_nObj = READ_LE_UINT32(lpBuf); @@ -266,7 +265,7 @@ static const byte *parseDialog(const byte *lpBuf, LpMpalDialog lpmdDialog) { lpmdDialog->_choice[i]._nChoice = READ_LE_UINT16(lpBuf); lpBuf += 2; - num2 = *lpBuf++; + uint32 num2 = *lpBuf++; if (num2 >= MAX_SELECTS_PER_CHOICE) error("Too much selects in choice #%d in dialog #%d", lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj); @@ -296,7 +295,7 @@ static const byte *parseDialog(const byte *lpBuf, LpMpalDialog lpmdDialog) { lpBuf += 4; // PlayGroup - num3 = *lpBuf++; + uint32 num3 = *lpBuf++; if (num3 >= MAX_PLAYGROUPS_PER_SELECT) error("Too much playgroups in select #%d in choice #%d in dialog #%d", j, lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj); @@ -365,7 +364,6 @@ static const byte *parseItem(const byte *lpBuf, LpMpalItem lpmiItem) { lpBuf++; } - if (*lpBuf == 0) { lpBuf++; lpmiItem->_action[i]._when = NULL; diff --git a/engines/tony/mpal/memory.cpp b/engines/tony/mpal/memory.cpp index 428c07b3b7..dfbf16e789 100644 --- a/engines/tony/mpal/memory.cpp +++ b/engines/tony/mpal/memory.cpp @@ -33,8 +33,6 @@ namespace MPAL { * MemoryManager methods \****************************************************************************/ -const uint32 BLOCK_ID = 0x12345678; - /** * Allocates a new memory block * @return Returns a MemoryItem instance for the new block @@ -64,7 +62,7 @@ void *MemoryManager::alloc(uint32 size, uint flags) { return &item->_data[0]; } -#define OFFSETOF(type, field) ((unsigned long) &(((type *) 0)->field)) +#define OFFSETOF(type, field) ((size_t) &(((type *) 0)->field)) /** * Returns a reference to the MemoryItem for a gien byte pointer diff --git a/engines/tony/mpal/memory.h b/engines/tony/mpal/memory.h index ba7865938f..9c21cc20e6 100644 --- a/engines/tony/mpal/memory.h +++ b/engines/tony/mpal/memory.h @@ -69,6 +69,8 @@ public: #define GMEM_MOVEABLE 2 #define GMEM_ZEROINIT 4 +const uint32 BLOCK_ID = 0x12345678; + } // end of namespace MPAL } // end of namespace Tony diff --git a/engines/tony/mpal/mpal.cpp b/engines/tony/mpal/mpal.cpp index 10f5753540..8d83363c24 100644 --- a/engines/tony/mpal/mpal.cpp +++ b/engines/tony/mpal/mpal.cpp @@ -39,19 +39,6 @@ namespace Tony { namespace MPAL { -#define GETARG(type) va_arg(v, type) - -/****************************************************************************\ -* Copyright -\****************************************************************************/ - -const char *mpalCopyright = - "\n\nMPAL - MultiPurpose Adventure Language for Windows 95\n" - "Copyright 1997-98 Giovanni Bajo and Luca Giusti\n" - "ALL RIGHTS RESERVED\n" - "\n" - "\n"; - /****************************************************************************\ * Internal functions \****************************************************************************/ @@ -363,24 +350,22 @@ static char *duplicateDialogPeriod(uint32 nPeriod) { MpalHandle resLoad(uint32 dwId) { MpalHandle h; char head[4]; - uint32 nBytesRead; - uint32 nSizeComp, nSizeDecomp; byte *temp, *buf; for (int i = 0; i < GLOBALS._nResources; i++) if (GLOBALS._lpResources[i * 2] == dwId) { GLOBALS._hMpr.seek(GLOBALS._lpResources[i * 2 + 1]); - nBytesRead = GLOBALS._hMpr.read(head, 4); + uint32 nBytesRead = GLOBALS._hMpr.read(head, 4); if (nBytesRead != 4) return NULL; if (head[0] != 'R' || head[1] != 'E' || head[2] != 'S' || head[3] != 'D') return NULL; - nSizeDecomp = GLOBALS._hMpr.readUint32LE(); + uint32 nSizeDecomp = GLOBALS._hMpr.readUint32LE(); if (GLOBALS._hMpr.err()) return NULL; - nSizeComp = GLOBALS._hMpr.readUint32LE(); + uint32 nSizeComp = GLOBALS._hMpr.readUint32LE(); if (GLOBALS._hMpr.err()) return NULL; @@ -463,18 +448,16 @@ static uint32 *GetItemList(uint32 nLoc) { static LpItem getItemData(uint32 nOrdItem) { LpMpalItem curitem = GLOBALS._lpmiItems + nOrdItem; - LpItem ret; - MpalHandle hDat; char *dat; char *patlength; // Zeroing out the allocated memory is required!!! - ret = (LpItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(Item)); + LpItem ret = (LpItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(Item)); if (ret == NULL) return NULL; ret->_speed = 150; - hDat = resLoad(curitem->_dwRes); + MpalHandle hDat = resLoad(curitem->_dwRes); dat = (char *)globalLock(hDat); if (dat[0] == 'D' && dat[1] == 'A' && dat[2] == 'T') { @@ -659,6 +642,9 @@ void ScriptThread(CORO_PARAM, const void *param) { CORO_KILL_SELF(); return; } + + // WORKAROUND: Wait for events to pulse. + CORO_SLEEP(1); } } @@ -727,6 +713,9 @@ void ActionThread(CORO_PARAM, const void *param) { GLOBALS._mpalError = 1; break; } + + // WORKAROUND: Wait for events to pulse. + CORO_SLEEP(1); } globalDestroy(_ctx->item); @@ -1138,6 +1127,9 @@ void GroupThread(CORO_PARAM, const void *param) { CORO_KILL_SELF(); return; } + + // WORKAROUND: Wait for events to pulse. + CORO_SLEEP(1); } // The gruop is finished, so we can return to the calling function. @@ -1403,11 +1395,7 @@ bool doSelection(uint32 i, uint32 dwData) { */ bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName, LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings) { - Common::File hMpc; byte buf[5]; - uint32 nBytesRead; - bool bCompress; - uint32 dwSizeDecomp, dwSizeComp; byte *cmpbuf; // Save the array of custom functions @@ -1415,21 +1403,22 @@ bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName, GLOBALS._lplpFunctionStrings = lpcfStrings; // OPen the MPC file for reading + Common::File hMpc; if (!hMpc.open(lpszMpcFileName)) return false; // Read and check the header - nBytesRead = hMpc.read(buf, 5); + uint32 nBytesRead = hMpc.read(buf, 5); if (nBytesRead != 5) return false; if (buf[0] != 'M' || buf[1] != 'P' || buf[2] != 'C' || buf[3] != 0x20) return false; - bCompress = buf[4]; + bool bCompress = buf[4]; // Reads the size of the uncompressed file, and allocate memory - dwSizeDecomp = hMpc.readUint32LE(); + uint32 dwSizeDecomp = hMpc.readUint32LE(); if (hMpc.err()) return false; @@ -1439,7 +1428,7 @@ bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName, if (bCompress) { // Get the compressed size and read the data in - dwSizeComp = hMpc.readUint32LE(); + uint32 dwSizeComp = hMpc.readUint32LE(); if (hMpc.err()) return false; @@ -1480,7 +1469,7 @@ bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName, // Seek to the end of the file to read overall information GLOBALS._hMpr.seek(-12, SEEK_END); - dwSizeComp = GLOBALS._hMpr.readUint32LE(); + uint32 dwSizeComp = GLOBALS._hMpr.readUint32LE(); if (GLOBALS._hMpr.err()) return false; @@ -1958,11 +1947,9 @@ uint32 mpalGetError() { * @returns TRUE if the script 'was launched, FALSE on failure */ bool mpalExecuteScript(int nScript) { - LpMpalScript s; - LockScripts(); int n = scriptGetOrderFromNum(nScript); - s = (LpMpalScript)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalScript)); + LpMpalScript s = (LpMpalScript)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalScript)); if (s == NULL) return false; diff --git a/engines/tony/mpal/mpal.h b/engines/tony/mpal/mpal.h index c5f505063f..5e1b02b3fc 100644 --- a/engines/tony/mpal/mpal.h +++ b/engines/tony/mpal/mpal.h @@ -102,6 +102,8 @@ namespace MPAL { #define MAXPATTERN 40 // pattern of animation of an object #define MAXPOLLINGLOCATIONS 64 +#define GETARG(type) va_arg(v, type) + /** * Macro for use with queries that may refer to X and Y co-ordinates */ diff --git a/engines/tony/mpal/mpalutils.cpp b/engines/tony/mpal/mpalutils.cpp index 92d4af37fc..0919aed5ac 100644 --- a/engines/tony/mpal/mpalutils.cpp +++ b/engines/tony/mpal/mpalutils.cpp @@ -81,7 +81,7 @@ Common::SeekableReadStream *RMRes::getReadStream() { } bool RMRes::isValid() { - return _h != NULL; + return _h != NULL; } /****************************************************************************\ diff --git a/engines/tony/mpal/mpalutils.h b/engines/tony/mpal/mpalutils.h index 629e157e29..d92bb6f9a2 100644 --- a/engines/tony/mpal/mpalutils.h +++ b/engines/tony/mpal/mpalutils.h @@ -59,7 +59,7 @@ class RMResRaw : public RMRes { public: RMResRaw(uint32 resID); virtual ~RMResRaw(); - + const byte *dataPointer(); operator const byte*(); diff --git a/engines/tony/sound.cpp b/engines/tony/sound.cpp index 2c2c280eb2..20386d6353 100644 --- a/engines/tony/sound.cpp +++ b/engines/tony/sound.cpp @@ -45,7 +45,7 @@ namespace Tony { * */ FPSound::FPSound() { - _bSoundSupported = false; + _soundSupported = false; } /** @@ -54,8 +54,8 @@ FPSound::FPSound() { * @returns True is everything is OK, False otherwise */ bool FPSound::init() { - _bSoundSupported = g_system->getMixer()->isReady(); - return _bSoundSupported; + _soundSupported = g_system->getMixer()->isReady(); + return _soundSupported; } /** @@ -69,55 +69,55 @@ FPSound::~FPSound() { /** * Allocates an object of type FPStream, and return its pointer * - * @param lplpStream Will contain a pointer to the object you just created. + * @param streamPtr Will contain a pointer to the object you just created. * * @returns True is everything is OK, False otherwise */ -bool FPSound::createStream(FPStream **lplpStream) { - (*lplpStream) = new FPStream(_bSoundSupported); +bool FPSound::createStream(FPStream **streamPtr) { + (*streamPtr) = new FPStream(_soundSupported); - return (*lplpStream != NULL); + return (*streamPtr != NULL); } /** * Allocates an object of type FpSfx, and return its pointer * - * @param lplpSfx Will contain a pointer to the object you just created. + * @param soundPtr Will contain a pointer to the object you just created. * * @returns True is everything is OK, False otherwise */ -bool FPSound::createSfx(FPSfx **lplpSfx) { - (*lplpSfx) = new FPSfx(_bSoundSupported); +bool FPSound::createSfx(FPSfx **sfxPtr) { + (*sfxPtr) = new FPSfx(_soundSupported); - return (*lplpSfx != NULL); + return (*sfxPtr != NULL); } /** * Set the general volume * - * @param dwVolume Volume to set (0-63) + * @param volume Volume to set (0-63) */ -void FPSound::setMasterVolume(int dwVolume) { - if (!_bSoundSupported) +void FPSound::setMasterVolume(int volume) { + if (!_soundSupported) return; - g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, CLIP<int>(dwVolume, 0, 63) * Audio::Mixer::kMaxChannelVolume / 63); + g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, CLIP<int>(volume, 0, 63) * Audio::Mixer::kMaxChannelVolume / 63); } /** * Get the general volume * - * @param lpdwVolume Variable that will contain the volume (0-63) + * @param volumePtr Variable that will contain the volume (0-63) */ -void FPSound::getMasterVolume(int *lpdwVolume) { - if (!_bSoundSupported) +void FPSound::getMasterVolume(int *volumePtr) { + if (!_soundSupported) return; - *lpdwVolume = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 63 / Audio::Mixer::kMaxChannelVolume; + *volumePtr = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 63 / Audio::Mixer::kMaxChannelVolume; } /** @@ -128,15 +128,15 @@ void FPSound::getMasterVolume(int *lpdwVolume) { * */ -FPSfx::FPSfx(bool bSoundOn) { - _bSoundSupported = bSoundOn; - _bFileLoaded = false; +FPSfx::FPSfx(bool soundOn) { + _soundSupported = soundOn; + _fileLoaded = false; _lastVolume = 63; _hEndOfBuffer = CoroScheduler.createEvent(true, false); - _bIsVoice = false; + _isVoice = false; _loopStream = 0; _rewindableStream = 0; - _bPaused = false; + _paused = false; g_vm->_activeSfx.push_back(this); } @@ -150,7 +150,7 @@ FPSfx::FPSfx(bool bSoundOn) { */ FPSfx::~FPSfx() { - if (!_bSoundSupported) + if (!_soundSupported) return; g_system->getMixer()->stopHandle(_handle); @@ -187,22 +187,22 @@ bool FPSfx::loadWave(Common::SeekableReadStream *stream) { if (!_rewindableStream) return false; - _bFileLoaded = true; + _fileLoaded = true; setVolume(_lastVolume); return true; } bool FPSfx::loadVoiceFromVDB(Common::File &vdbFP) { - if (!_bSoundSupported) + if (!_soundSupported) return true; uint32 size = vdbFP.readUint32LE(); uint32 rate = vdbFP.readUint32LE(); - _bIsVoice = true; + _isVoice = true; _rewindableStream = Audio::makeADPCMStream(vdbFP.readStream(size), DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, 1); - _bFileLoaded = true; + _fileLoaded = true; setVolume(62); return true; } @@ -210,18 +210,18 @@ bool FPSfx::loadVoiceFromVDB(Common::File &vdbFP) { /** * Opens a file and loads a sound effect. * - * @param lpszFileName Sfx filename - * @param dwCodec CODEC used to uncompress the samples + * @param fileName Sfx filename + * @param codec CODEC used to uncompress the samples * * @returns True is everything is OK, False otherwise */ -bool FPSfx::loadFile(const char *lpszFileName, uint32 dwCodec) { - if (!_bSoundSupported) +bool FPSfx::loadFile(const char *fileName, uint32 codec) { + if (!_soundSupported) return true; Common::File file; - if (!file.open(lpszFileName)) { + if (!file.open(fileName)) { warning("FPSfx::LoadFile(): Cannot open sfx file!"); return false; } @@ -236,7 +236,7 @@ bool FPSfx::loadFile(const char *lpszFileName, uint32 dwCodec) { Common::SeekableReadStream *buffer = file.readStream(file.size() - file.pos()); - if (dwCodec == FPCODEC_ADPCM) { + if (codec == FPCODEC_ADPCM) { _rewindableStream = Audio::makeADPCMStream(buffer, DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, channels); } else { byte flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; @@ -247,7 +247,7 @@ bool FPSfx::loadFile(const char *lpszFileName, uint32 dwCodec) { _rewindableStream = Audio::makeRawStream(buffer, rate, flags, DisposeAfterUse::YES); } - _bFileLoaded = true; + _fileLoaded = true; return true; } @@ -260,14 +260,14 @@ bool FPSfx::loadFile(const char *lpszFileName, uint32 dwCodec) { bool FPSfx::play() { stop(); // sanity check - if (_bFileLoaded) { + if (_fileLoaded) { CoroScheduler.resetEvent(_hEndOfBuffer); _rewindableStream->rewind(); Audio::AudioStream *stream = _rewindableStream; - if (_bLoop) { + if (_loop) { if (!_loopStream) _loopStream = Audio::makeLoopingAudioStream(_rewindableStream, 0); @@ -279,7 +279,7 @@ bool FPSfx::play() { setVolume(_lastVolume); - if (_bPaused) + if (_paused) g_system->getMixer()->pauseHandle(_handle, true); } @@ -293,9 +293,9 @@ bool FPSfx::play() { */ bool FPSfx::stop() { - if (_bFileLoaded) { + if (_fileLoaded) { g_system->getMixer()->stopHandle(_handle); - _bPaused = false; + _paused = false; } return true; @@ -304,15 +304,15 @@ bool FPSfx::stop() { /** * Enables or disables the Sfx loop. * - * @param _bLoop True to enable the loop, False to disable + * @param loop True to enable the loop, False to disable * * @remarks The loop must be activated BEFORE the sfx starts * playing. Any changes made during the play will have * no effect until the sfx is stopped then played again. */ -void FPSfx::setLoop(bool bLop) { - _bLoop = bLop; +void FPSfx::setLoop(bool loop) { + _loop = loop; } /** @@ -320,65 +320,65 @@ void FPSfx::setLoop(bool bLop) { * */ -void FPSfx::pause(bool bPause) { - if (_bFileLoaded) { - if (g_system->getMixer()->isSoundHandleActive(_handle) && (bPause ^ _bPaused)) - g_system->getMixer()->pauseHandle(_handle, bPause); +void FPSfx::setPause(bool pause) { + if (_fileLoaded) { + if (g_system->getMixer()->isSoundHandleActive(_handle) && (pause ^ _paused)) + g_system->getMixer()->pauseHandle(_handle, pause); - _bPaused = bPause; + _paused = pause; } } /** * Change the volume of Sfx * - * @param dwVolume Volume to be set (0-63) + * @param volume Volume to be set (0-63) * */ -void FPSfx::setVolume(int dwVolume) { - if (dwVolume > 63) - dwVolume = 63; +void FPSfx::setVolume(int volume) { + if (volume > 63) + volume = 63; - if (dwVolume < 0) - dwVolume = 0; + if (volume < 0) + volume = 0; - _lastVolume = dwVolume; + _lastVolume = volume; - if (_bIsVoice) { + if (_isVoice) { if (!GLOBALS._bCfgDubbing) - dwVolume = 0; + volume = 0; else { - dwVolume -= (10 - GLOBALS._nCfgDubbingVolume) * 2; - if (dwVolume < 0) - dwVolume = 0; + volume -= (10 - GLOBALS._nCfgDubbingVolume) * 2; + if (volume < 0) + volume = 0; } } else { if (!GLOBALS._bCfgSFX) - dwVolume = 0; + volume = 0; else { - dwVolume -= (10 - GLOBALS._nCfgSFXVolume) * 2; - if (dwVolume < 0) - dwVolume = 0; + volume -= (10 - GLOBALS._nCfgSFXVolume) * 2; + if (volume < 0) + volume = 0; } } if (g_system->getMixer()->isSoundHandleActive(_handle)) - g_system->getMixer()->setChannelVolume(_handle, dwVolume * Audio::Mixer::kMaxChannelVolume / 63); + g_system->getMixer()->setChannelVolume(_handle, volume * Audio::Mixer::kMaxChannelVolume / 63); } /** * Gets the Sfx volume * - * @param lpdwVolume Will contain the current Sfx volume + * @param volumePtr Will contain the current Sfx volume * */ -void FPSfx::getVolume(int *lpdwVolume) { +void FPSfx::getVolume(int *volumePtr) { if (g_system->getMixer()->isSoundHandleActive(_handle)) - *lpdwVolume = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume; + *volumePtr = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume; else - *lpdwVolume = 0; + *volumePtr = 0; } /** @@ -421,14 +421,14 @@ void FPSfx::soundCheckProcess(CORO_PARAM, const void *param) { * @remarks Do *NOT* declare an object directly, but rather * create it using FPSound::CreateStream() */ -FPStream::FPStream(bool bSoundOn) { - _bSoundSupported = bSoundOn; - _bFileLoaded = false; - _bPaused = false; - _bLoop = false; - _bDoFadeOut = false; - _bSyncExit = false; - _dwBufferSize = _dwSize = 0; +FPStream::FPStream(bool soundOn) { + _soundSupported = soundOn; + _fileLoaded = false; + _paused = false; + _loop = false; + _doFadeOut = false; + _syncExit = false; + _bufferSize = _size = 0; _lastVolume = 0; _syncToPlay = NULL; _loopStream = NULL; @@ -442,13 +442,13 @@ FPStream::FPStream(bool bSoundOn) { */ FPStream::~FPStream() { - if (!_bSoundSupported) + if (!_soundSupported) return; if (g_system->getMixer()->isSoundHandleActive(_handle)) stop(); - if (_bFileLoaded) + if (_fileLoaded) unloadFile(); _syncToPlay = NULL; @@ -470,31 +470,32 @@ void FPStream::release() { * Opens a file stream * * @param fileName Filename to be opened - * @param dwCodec CODEC to be used to uncompress samples + * @param codec CODEC to be used to uncompress samples * * @returns True is everything is OK, False otherwise */ -bool FPStream::loadFile(const Common::String &fileName, uint32 dwCodType, int nBufSize) { - if (!_bSoundSupported) +bool FPStream::loadFile(const Common::String &fileName, uint32 codec, int bufSize) { + if (!_soundSupported) return true; - if (_bFileLoaded) + if (_fileLoaded) unloadFile(); // Save the codec type - _dwCodec = dwCodType; + _codec = codec; // Open the file stream for reading if (!_file.open(fileName)) { // Fallback: try with an extra '0' prefix if (!_file.open("0" + fileName)) return false; + warning("FPStream::loadFile(): Fallback from %s to %s", fileName.c_str(), _file.getName()); } // Save the size of the stream - _dwSize = _file.size(); + _size = _file.size(); - switch (_dwCodec) { + switch (_codec) { case FPCODEC_RAW: _rewindableStream = Audio::makeRawStream(&_file, 44100, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO, DisposeAfterUse::NO); break; @@ -509,8 +510,8 @@ bool FPStream::loadFile(const Common::String &fileName, uint32 dwCodType, int nB } // All done - _bFileLoaded = true; - _bPaused = false; + _fileLoaded = true; + _paused = false; setVolume(63); @@ -527,7 +528,7 @@ bool FPStream::loadFile(const Common::String &fileName, uint32 dwCodType, int nB * memory used by the stream. */ bool FPStream::unloadFile() { - if (!_bSoundSupported || !_bFileLoaded) + if (!_soundSupported || !_fileLoaded) return true; assert(!g_system->getMixer()->isSoundHandleActive(_handle)); @@ -540,7 +541,7 @@ bool FPStream::unloadFile() { _file.close(); // Flag that the file is no longer in memory - _bFileLoaded = false; + _fileLoaded = false; return true; } @@ -552,7 +553,7 @@ bool FPStream::unloadFile() { */ bool FPStream::play() { - if (!_bSoundSupported || !_bFileLoaded) + if (!_soundSupported || !_fileLoaded) return false; stop(); @@ -561,7 +562,7 @@ bool FPStream::play() { Audio::AudioStream *stream = _rewindableStream; - if (_bLoop) { + if (_loop) { if (!_loopStream) _loopStream = new Audio::LoopingAudioStream(_rewindableStream, 0, DisposeAfterUse::NO); @@ -571,7 +572,7 @@ bool FPStream::play() { // FIXME: Should this be kMusicSoundType or KPlainSoundType? g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO); setVolume(_lastVolume); - _bPaused = false; + _paused = false; return true; } @@ -584,10 +585,10 @@ bool FPStream::play() { */ bool FPStream::stop() { - if (!_bSoundSupported) + if (!_soundSupported) return true; - if (!_bFileLoaded) + if (!_fileLoaded) return false; if (!g_system->getMixer()->isSoundHandleActive(_handle)) @@ -595,49 +596,49 @@ bool FPStream::stop() { g_system->getMixer()->stopHandle(_handle); - _bPaused = false; + _paused = false; return true; } -void FPStream::waitForSync(FPStream *toplay) { +void FPStream::waitForSync(FPStream *toPlay) { // FIXME: The idea here is that you wait for this stream to reach // a buffer which is a multiple of nBufSize/nSync, and then the // thread stops it and immediately starts the 'toplay' stream. stop(); - toplay->play(); + toPlay->play(); } /** * Unables or disables stream loop. * - * @param _bLoop True enable loop, False disables it + * @param loop True enable loop, False disables it * * @remarks The loop must be activated BEFORE the stream starts * playing. Any changes made during the play will have no * effect until the stream is stopped then played again. */ void FPStream::setLoop(bool loop) { - _bLoop = loop; + _loop = loop; } /** * Pause sound effect * - * @param bPause True enables pause, False disables it + * @param pause True enables pause, False disables it */ -void FPStream::pause(bool bPause) { - if (!_bFileLoaded) +void FPStream::setPause(bool pause) { + if (!_fileLoaded) return; - if (bPause == _bPaused) + if (pause == _paused) return; if (g_system->getMixer()->isSoundHandleActive(_handle)) - g_system->getMixer()->pauseHandle(_handle, bPause); + g_system->getMixer()->pauseHandle(_handle, pause); - _bPaused = bPause; + _paused = pause; // Trick to reset the volume after a possible new sound configuration setVolume(_lastVolume); @@ -646,43 +647,43 @@ void FPStream::pause(bool bPause) { /** * Change the volume of the stream * - * @param dwVolume Volume to be set (0-63) + * @param volume Volume to be set (0-63) * */ -void FPStream::setVolume(int dwVolume) { - if (dwVolume > 63) - dwVolume = 63; +void FPStream::setVolume(int volume) { + if (volume > 63) + volume = 63; - if (dwVolume < 0) - dwVolume = 0; + if (volume < 0) + volume = 0; - _lastVolume = dwVolume; + _lastVolume = volume; if (!GLOBALS._bCfgMusic) - dwVolume = 0; + volume = 0; else { - dwVolume -= (10 - GLOBALS._nCfgMusicVolume) * 2; - if (dwVolume < 0) - dwVolume = 0; + volume -= (10 - GLOBALS._nCfgMusicVolume) * 2; + if (volume < 0) + volume = 0; } if (g_system->getMixer()->isSoundHandleActive(_handle)) - g_system->getMixer()->setChannelVolume(_handle, dwVolume * Audio::Mixer::kMaxChannelVolume / 63); + g_system->getMixer()->setChannelVolume(_handle, volume * Audio::Mixer::kMaxChannelVolume / 63); } /** * Gets the volume of the stream * - * @param lpdwVolume Variable that will contain the current volume + * @param volumePtr Variable that will contain the current volume * */ -void FPStream::getVolume(int *lpdwVolume) { +void FPStream::getVolume(int *volumePtr) { if (g_system->getMixer()->isSoundHandleActive(_handle)) - *lpdwVolume = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume; + *volumePtr = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume; else - *lpdwVolume = 0; + *volumePtr = 0; } } // End of namespace Tony diff --git a/engines/tony/sound.h b/engines/tony/sound.h index c859f781f4..7422de02b3 100644 --- a/engines/tony/sound.h +++ b/engines/tony/sound.h @@ -55,7 +55,7 @@ enum SoundCodecs { class FPSound { private: - bool _bSoundSupported; + bool _soundSupported; public: /** @@ -83,49 +83,49 @@ public: /** * Allocates an object of type FPStream, and return its pointer * - * @param lplpStream Will contain a pointer to the object you just created. + * @param streamPtr Will contain a pointer to the object you just created. * * @returns True is everything is OK, False otherwise */ - bool createStream(FPStream **lplpStream); + bool createStream(FPStream **streamPtr); /** * Allocates an object of type FpSfx, and return its pointer * - * @param lplpSfx Will contain a pointer to the object you just created. + * @param sfxPtr Will contain a pointer to the object you just created. * * @returns True is everything is OK, False otherwise */ - bool createSfx(FPSfx **lplpSfx); + bool createSfx(FPSfx **sfxPtr); /** * Set the general volume * - * @param dwVolume Volume to set (0-63) + * @param volume Volume to set (0-63) */ - void setMasterVolume(int dwVolume); + void setMasterVolume(int volume); /** * Get the general volume * - * @param lpdwVolume Variable that will contain the volume (0-63) + * @param volume Variable that will contain the volume (0-63) */ - void getMasterVolume(int *lpdwVolume); + void getMasterVolume(int *volume); }; class FPSfx { private: - bool _bSoundSupported; // True if the sound is active - bool _bFileLoaded; // True is a file is opened - bool _bLoop; // True is sound effect should loop + bool _soundSupported; // True if the sound is active + bool _fileLoaded; // True is a file is opened + bool _loop; // True is sound effect should loop int _lastVolume; - bool _bIsVoice; - bool _bPaused; + bool _isVoice; + bool _paused; Audio::AudioStream *_loopStream; Audio::RewindableAudioStream *_rewindableStream; @@ -147,7 +147,7 @@ public: * */ - FPSfx(bool bSoundOn); + FPSfx(bool soundOn); /** * Default Destructor. @@ -173,13 +173,13 @@ public: /** * Opens a file and loads a sound effect. * - * @param lpszFileName Sfx filename - * @param dwCodec CODEC used to uncompress the samples + * @param fileName Sfx filename + * @param codec CODEC used to uncompress the samples * * @returns True is everything is OK, False otherwise */ - bool loadFile(const char *lpszFileName, uint32 dwCodec = FPCODEC_RAW); + bool loadFile(const char *fileName, uint32 codec = FPCODEC_RAW); bool loadWave(Common::SeekableReadStream *stream); bool loadVoiceFromVDB(Common::File &vdbFP); @@ -204,37 +204,37 @@ public: * */ - void pause(bool bPause); + void setPause(bool pause); /** * Enables or disables the Sfx loop. * - * @param bLoop True to enable the loop, False to disable + * @param loop True to enable the loop, False to disable * * @remarks The loop must be activated BEFORE the sfx starts * playing. Any changes made during the play will have * no effect until the sfx is stopped then played again. */ - void setLoop(bool bLoop); + void setLoop(bool loop); /** * Change the volume of Sfx * - * @param dwVolume Volume to be set (0-63) + * @param volume Volume to be set (0-63) * */ - void setVolume(int dwVolume); + void setVolume(int volume); /** * Gets the Sfx volume * - * @param lpdwVolume Will contain the current Sfx volume + * @param volumePtr Will contain the current Sfx volume * */ - void getVolume(int *lpdwVolume); + void getVolume(int *volumePtr); /** * Returns true if the underlying sound has ended @@ -244,18 +244,18 @@ public: class FPStream { private: - uint32 _dwBufferSize; // Buffer size (bytes) - uint32 _dwSize; // Stream size (bytes) - uint32 _dwCodec; // CODEC used - - Common::File _file; // File handle used for the stream - - bool _bSoundSupported; // True if the sound is active - bool _bFileLoaded; // True if the file is open - bool _bLoop; // True if the stream should loop - bool _bDoFadeOut; // True if fade out is required - bool _bSyncExit; - bool _bPaused; + uint32 _bufferSize; // Buffer size (bytes) + uint32 _size; // Stream size (bytes) + uint32 _codec; // CODEC used + + Common::File _file; // File handle used for the stream + + bool _soundSupported; // True if the sound is active + bool _fileLoaded; // True if the file is open + bool _loop; // True if the stream should loop + bool _doFadeOut; // True if fade out is required + bool _syncExit; + bool _paused; int _lastVolume; FPStream *_syncToPlay; @@ -272,7 +272,7 @@ public: * create it using FPSound::CreateStream() */ - FPStream(bool bSoundOn); + FPStream(bool soundOn); /** * Default destructor. @@ -297,12 +297,12 @@ public: * Opens a file stream * * @param fileName Filename to be opened - * @param dwCodec CODEC to be used to uncompress samples + * @param codec CODEC to be used to uncompress samples * * @returns True is everything is OK, False otherwise */ - bool loadFile(const Common::String &fileName, uint32 dwCodec = FPCODEC_RAW, int nSync = 2000); + bool loadFile(const Common::String &fileName, uint32 codec = FPCODEC_RAW, int sync = 2000); /** * Closes a file stream (opened or not). @@ -332,44 +332,44 @@ public: */ bool stop(); - void waitForSync(FPStream *toplay); + void waitForSync(FPStream *toPlay); /** * Pause sound effect * - * @param bPause True enables pause, False disables it + * @param pause True enables pause, False disables it */ - void pause(bool bPause); + void setPause(bool pause); /** * Unables or disables stream loop. * - * @param bLoop True enable loop, False disables it + * @param loop True enable loop, False disables it * * @remarks The loop must be activated BEFORE the stream starts * playing. Any changes made during the play will have no * effect until the stream is stopped then played again. */ - void setLoop(bool bLoop); + void setLoop(bool loop); /** * Change the volume of the stream * - * @param dwVolume Volume to be set (0-63) + * @param volume Volume to be set (0-63) */ - void setVolume(int dwVolume); + void setVolume(int volume); /** * Gets the volume of the stream * - * @param lpdwVolume Variable that will contain the current volume + * @param volumePtr Variable that will contain the current volume * */ - void getVolume(int *lpdwVolume); + void getVolume(int *volumePtr); }; } // End of namespace Tony diff --git a/engines/tony/tony.cpp b/engines/tony/tony.cpp index 4ffb84ced8..86740c6fe5 100644 --- a/engines/tony/tony.cpp +++ b/engines/tony/tony.cpp @@ -55,6 +55,7 @@ TonyEngine::TonyEngine(OSystem *syst, const TonyGameDescription *gameDesc) : Eng SearchMan.addSubDirectoryMatching(gameDataDir, "Roasted"); SearchMan.addSubDirectoryMatching(gameDataDir, "Music"); SearchMan.addSubDirectoryMatching(gameDataDir, "Music/utilsfx"); + SearchMan.addSubDirectoryMatching(gameDataDir, "Music/Layer"); // Set up load slot number _initialLoadSlotNumber = -1; @@ -244,16 +245,16 @@ bool TonyEngine::loadTonyDat() { expectedLangVariant = 0; break; } - + int numVariant = in.readUint16BE(); - if (expectedLangVariant > numVariant) { + if (expectedLangVariant > numVariant - 1) { msg = Common::String::format("Font variant not present in 'tony.dat'. Get it from the ScummVM website"); GUIErrorMessage(msg); warning("%s", msg.c_str()); - + return false; } - + in.seek(in.pos() + (2 * 256 * 8 * expectedLangVariant)); for (int i = 0; i < 256; i++) { _cTableDialog[i] = in.readSint16BE(); @@ -323,7 +324,7 @@ void TonyEngine::playMusic(int nChannel, const Common::String &fname, int nFX, b if (!getIsDemo()) { if (!_stream[GLOBALS._nextChannel]->loadFile(fname, FPCODEC_ADPCM, nSync)) - g_vm->abortGame(); + error("failed to open music file '%s'", fname.c_str()); } else { _stream[GLOBALS._nextChannel]->loadFile(fname, FPCODEC_ADPCM, nSync); } @@ -335,7 +336,7 @@ void TonyEngine::playMusic(int nChannel, const Common::String &fname, int nFX, b } else { if (!getIsDemo()) { if (!_stream[nChannel]->loadFile(fname, FPCODEC_ADPCM, nSync)) - g_vm->abortGame(); + error("failed to open music file '%s'", fname.c_str()); } else { _stream[nChannel]->loadFile(fname, FPCODEC_ADPCM, nSync); } @@ -356,7 +357,7 @@ void TonyEngine::doNextMusic(CORO_PARAM, const void *param) { if (!g_vm->getIsDemo()) { if (!streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, FPCODEC_ADPCM, GLOBALS._nextSync)) - g_vm->abortGame(); + error("failed to open next music file '%s'", GLOBALS._nextMusic.c_str()); } else { streams[GLOBALS._nextChannel]->loadFile(GLOBALS._nextMusic, FPCODEC_ADPCM, GLOBALS._nextSync); } @@ -513,13 +514,13 @@ void TonyEngine::pauseSound(bool bPause) { for (uint i = 0; i < 6; i++) if (_stream[i]) - _stream[i]->pause(bPause); + _stream[i]->setPause(bPause); for (uint i = 0; i < MAX_SFX_CHANNELS; i++) { if (_sfx[i]) - _sfx[i]->pause(bPause); + _sfx[i]->setPause(bPause); if (_utilSfx[i]) - _utilSfx[i]->pause(bPause); + _utilSfx[i]->setPause(bPause); } } @@ -631,10 +632,6 @@ void TonyEngine::openInitOptions(CORO_PARAM) { _theEngine.openOptionScreen(coroParam, 2); } -void TonyEngine::abortGame() { - _bQuitNow = true; -} - /** * Main process for playing the game. * @@ -772,9 +769,9 @@ void TonyEngine::syncSoundSettings() { } void TonyEngine::saveSoundSettings() { - ConfMan.setBool("speech_mute", GLOBALS._bCfgDubbing); - ConfMan.setBool("sfx_mute", GLOBALS._bCfgSFX); - ConfMan.setBool("music_mute", GLOBALS._bCfgMusic); + ConfMan.setBool("speech_mute", !GLOBALS._bCfgDubbing); + ConfMan.setBool("sfx_mute", !GLOBALS._bCfgSFX); + ConfMan.setBool("music_mute", !GLOBALS._bCfgMusic); ConfMan.setInt("speech_volume", GLOBALS._nCfgDubbingVolume * 256 / 10); ConfMan.setInt("sfx_volume", GLOBALS._nCfgSFXVolume * 256 / 10); diff --git a/engines/tony/tony.h b/engines/tony/tony.h index 22090dfe51..332b122923 100644 --- a/engines/tony/tony.h +++ b/engines/tony/tony.h @@ -71,7 +71,7 @@ struct TonyGameDescription; #define MAX_SFX_CHANNELS 32 #define TONY_DAT_VER_MAJ 0 -#define TONY_DAT_VER_MIN 1 +#define TONY_DAT_VER_MIN 3 struct VoiceHeader { int _offset; @@ -169,7 +169,6 @@ public: void play(); void close(); - void abortGame(); void getDataDirectory(DataDir dir, char *path); diff --git a/engines/tony/utils.cpp b/engines/tony/utils.cpp index 3cc09a1454..81060146b7 100644 --- a/engines/tony/utils.cpp +++ b/engines/tony/utils.cpp @@ -383,7 +383,7 @@ void RMResUpdate::init(const Common::String &fileName) { // It doesn't exist, so exit immediately return; - uint8 version = _hFile.readByte(); + _hFile.readByte(); // Version, unused _numUpd = _hFile.readUint32LE(); diff --git a/engines/tony/window.cpp b/engines/tony/window.cpp index 013be84b4b..c9c450424f 100644 --- a/engines/tony/window.cpp +++ b/engines/tony/window.cpp @@ -256,7 +256,7 @@ void RMWindow::plotLines(const byte *lpBuf, const Common::Point ¢er, int x, } void RMWindow::showDirtyRects(bool v) { - _showDirtyRects = v; + _showDirtyRects = v; } /****************************************************************************\ diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp index 3877fa2a6c..38b1f4f6e1 100644 --- a/engines/toon/detection.cpp +++ b/engines/toon/detection.cpp @@ -84,7 +84,7 @@ static const ADGameDescription gameDescriptions[] = { {"study.svl", 0, "d4aff126ee27be3c3d25e2996369d7cb", 2324368}, }, Common::RU_RUS, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO0() - }, + }, { "toon", "", { diff --git a/engines/toon/movie.h b/engines/toon/movie.h index e795182cba..4dd6583bf6 100644 --- a/engines/toon/movie.h +++ b/engines/toon/movie.h @@ -40,7 +40,7 @@ protected: SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const; private: - bool _lowRes; + bool _lowRes; }; class Movie { diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp index 204b0fe576..f59cdca064 100644 --- a/engines/toon/picture.cpp +++ b/engines/toon/picture.cpp @@ -71,7 +71,7 @@ bool Picture::loadPicture(const Common::String &file) { _data = new uint8[decSize + 100]; _paletteEntries = READ_LE_UINT16(fileData + 14) / 3; _useFullPalette = (_paletteEntries == 256); - + if (_paletteEntries) { _palette = new uint8[_paletteEntries * 3]; memcpy(_palette, fileData + 16, _paletteEntries * 3); diff --git a/engines/touche/staticres.cpp b/engines/touche/staticres.cpp index c18a947358..23b76558e4 100644 --- a/engines/touche/staticres.cpp +++ b/engines/touche/staticres.cpp @@ -471,7 +471,10 @@ const uint16 Graphics::_freGerFontOffs[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x1920 + 0x0000, 0x0000, 0x0000, 0x1920, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 }; const int Graphics::_freGerFontSize = ARRAYSIZE(Graphics::_freGerFontOffs); diff --git a/engines/tsage/blue_force/blueforce_scenes3.cpp b/engines/tsage/blue_force/blueforce_scenes3.cpp index 22c831f531..81e4af6e97 100644 --- a/engines/tsage/blue_force/blueforce_scenes3.cpp +++ b/engines/tsage/blue_force/blueforce_scenes3.cpp @@ -346,6 +346,14 @@ void Scene300::postInit(SceneObjectList *OwnerList) { break; } + if (BF_GLOBALS.getFlag(onBike) && !BF_GLOBALS.getFlag(onDuty)) { + BF_GLOBALS._sound1.play(30); + } else if ((BF_GLOBALS._dayNumber == 2) && (BF_GLOBALS._bookmark < bEndDayOne)) { + BF_GLOBALS._sound1.changeSound(49); + } else if (BF_GLOBALS._sceneManager._previousScene != 190) { + BF_GLOBALS._sound1.changeSound(33); + } + _item10.setDetails(4, 300, 7, 13, 16, 1); _item11.setDetails(2, 300, 9, 13, 18, 1); _item12.setDetails(5, 300, 10, 13, 19, 1); diff --git a/engines/tsage/blue_force/blueforce_scenes7.cpp b/engines/tsage/blue_force/blueforce_scenes7.cpp index bb29ad1f34..4cdd2f3f15 100644 --- a/engines/tsage/blue_force/blueforce_scenes7.cpp +++ b/engines/tsage/blue_force/blueforce_scenes7.cpp @@ -159,7 +159,7 @@ void Scene710::postInit(SceneObjectList *OwnerList) { _stripManager.addSpeaker(&_skipSpeaker); _stripManager.addSpeaker(&_lauraSpeaker); _stripManager.addSpeaker(&_gameTextSpeaker); - + _kid.postInit(); _kid._moveDiff = Common::Point(4, 2); _laura.postInit(); diff --git a/engines/tsage/blue_force/blueforce_speakers.cpp b/engines/tsage/blue_force/blueforce_speakers.cpp index 8af18b43b8..2a57616640 100644 --- a/engines/tsage/blue_force/blueforce_speakers.cpp +++ b/engines/tsage/blue_force/blueforce_speakers.cpp @@ -809,7 +809,7 @@ void SpeakerGiggles::setText(const Common::String &msg) { SpeakerFBI::SpeakerFBI(): VisualSpeaker() { _color1 = 27; _color2 = 89; - + _speakerName = "FBI"; } @@ -832,7 +832,7 @@ void SpeakerFBI::setText(const Common::String &msg) { SpeakerNico::SpeakerNico(): VisualSpeaker() { _color1 = 105; _color2 = 102; - + _speakerName = "NICO"; } @@ -845,7 +845,7 @@ void SpeakerNico::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 262, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(905); _object2.setStrip2(1); @@ -862,7 +862,7 @@ void SpeakerNico::setText(const Common::String &msg) { SpeakerDA::SpeakerDA(): VisualSpeaker() { _color1 = 82; _color2 = 80; - + _speakerName = "DA"; } @@ -875,7 +875,7 @@ void SpeakerDA::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 84, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(915); _object2.setStrip2(1); @@ -892,7 +892,7 @@ void SpeakerDA::setText(const Common::String &msg) { SpeakerGrandma::SpeakerGrandma(): VisualSpeaker() { _color1 = 20; _color2 = 23; - + _speakerName = "GRANDMA"; } @@ -905,7 +905,7 @@ void SpeakerGrandma::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 43, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(274); _object2.setStrip2(3); @@ -922,7 +922,7 @@ void SpeakerGrandma::setText(const Common::String &msg) { SpeakerLyle::SpeakerLyle(): VisualSpeaker() { _color1 = 29; _color2 = 89; - + _speakerName = "LYLE"; } @@ -935,7 +935,7 @@ void SpeakerLyle::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 75, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(278); _object2.setStrip2(1); @@ -952,7 +952,7 @@ void SpeakerLyle::setText(const Common::String &msg) { SpeakerGranText::SpeakerGranText(): VisualSpeaker() { _color1 = 20; _color2 = 23; - + _speakerName = "GRANTEXT"; } @@ -961,7 +961,7 @@ SpeakerGranText::SpeakerGranText(): VisualSpeaker() { SpeakerLyleText::SpeakerLyleText(): VisualSpeaker() { _color1 = 29; _color2 = 89; - + _speakerName = "LYLETEXT"; } @@ -969,7 +969,7 @@ SpeakerLyleText::SpeakerLyleText(): VisualSpeaker() { SpeakerKate::SpeakerKate(): VisualSpeaker() { _color1 = 108; - + _speakerName = "KATE"; } @@ -982,7 +982,7 @@ void SpeakerKate::setText(const Common::String &msg) { _object1.fixPriority(254); _object1.setPosition(Common::Point(BF_GLOBALS._sceneManager._scene->_sceneBounds.left + 270, BF_GLOBALS._sceneManager._scene->_sceneBounds.top + 166)); - + _object2.postInit(); _object2.setVisage(122); _object2.setStrip2(1); @@ -1000,7 +1000,7 @@ void SpeakerKate::setText(const Common::String &msg) { SpeakerTony::SpeakerTony(): VisualSpeaker() { _color1 = 108; _color2 = 8; - + _speakerName = "TONY"; } diff --git a/engines/tsage/blue_force/blueforce_speakers.h b/engines/tsage/blue_force/blueforce_speakers.h index 508279a929..e406a50fbe 100644 --- a/engines/tsage/blue_force/blueforce_speakers.h +++ b/engines/tsage/blue_force/blueforce_speakers.h @@ -290,7 +290,7 @@ public: virtual Common::String getClassName() { return "FBI"; } virtual void setText(const Common::String &msg); }; - + class SpeakerNico: public VisualSpeaker { public: SpeakerNico(); @@ -340,7 +340,7 @@ public: class SpeakerKate: public VisualSpeaker { public: SpeakerKate(); - + virtual Common::String getClassName() { return "SpeakerKate"; } virtual void setText(const Common::String &msg); }; @@ -348,7 +348,7 @@ public: class SpeakerTony: public VisualSpeaker { public: SpeakerTony(); - + virtual Common::String getClassName() { return "SpeakerTony"; } virtual void setText(const Common::String &msg); }; diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp index bcadfdc201..a35d663b93 100644 --- a/engines/tsage/detection.cpp +++ b/engines/tsage/detection.cpp @@ -156,7 +156,7 @@ public: SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const { Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading( generateGameStateFileName(target, slot)); - + if (f) { TsAGE::tSageSavegameHeader header; TsAGE::Saver::readSavegameHeader(f, header); diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp index de9463268b..4589a926c9 100644 --- a/engines/tsage/globals.cpp +++ b/engines/tsage/globals.cpp @@ -205,7 +205,7 @@ void Globals::dispatchSounds() { void TsAGE2Globals::reset() { Globals::reset(); - + // Reset the inventory T2_GLOBALS._uiElements.updateInventory(); T2_GLOBALS._uiElements._scoreValue = 0; @@ -277,7 +277,7 @@ void BlueForceGlobals::synchronize(Serializer &s) { void BlueForceGlobals::reset() { TsAGE2Globals::reset(); _scenePalette.clearListeners(); - + _scrollFollower = &_player; _bookmark = bNone; @@ -368,7 +368,7 @@ namespace Ringworld2 { void Ringworld2Globals::reset() { Globals::reset(); - + // Reset the inventory R2_INVENTORY.reset(); T2_GLOBALS._uiElements.updateInventory(); @@ -526,7 +526,7 @@ void Ringworld2Globals::synchronize(Serializer &s) { for (i = 0; i < MAX_CHARACTERS; ++i) s.syncAsByte(_v565F1[i]); - + s.syncAsByte(_v565AE); s.syncAsByte(_v566A4); s.syncAsByte(_v566A5); diff --git a/engines/tsage/ringworld/ringworld_logic.cpp b/engines/tsage/ringworld/ringworld_logic.cpp index 7d571b40d7..0584570ac2 100644 --- a/engines/tsage/ringworld/ringworld_logic.cpp +++ b/engines/tsage/ringworld/ringworld_logic.cpp @@ -610,7 +610,7 @@ void NamedHotspot::doAction(int action) { case CURSOR_USE: if (_useLineNum == -1) break; - + SceneItem::display(_resNum, _useLineNum, SET_Y, 20, SET_WIDTH, 200, SET_EXT_BGCOLOR, 7, LIST_END); return; case CURSOR_TALK: diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp index a06899fe5a..97042cb621 100644 --- a/engines/tsage/ringworld2/ringworld2_logic.cpp +++ b/engines/tsage/ringworld2/ringworld2_logic.cpp @@ -1695,7 +1695,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) { _playbackTickPrior = -1; _playbackTick = 0; - // The final multiplication is used to deliberately slow down playback, since the original + // The final multiplication is used to deliberately slow down playback, since the original // was slowed down by the amount of time spent to decode and display the frames _frameDelay = (60 / _subData._frameRate) * 8; _gameFrame = R2_GLOBALS._events.getFrameNumber(); @@ -1706,7 +1706,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) { int v = (_subData._sliceSize + 2) * _subData._ySlices * _subData._framesPerSlices; _dataNeeded = (_subData._field16 / _subData._framesPerSlices) + v + 96; } - + debugC(1, ktSageDebugGraphics, "Data needed %d", _dataNeeded); // Set up animation data objects @@ -1760,7 +1760,7 @@ bool AnimationPlayer::load(int animId, Action *endAction) { byte r = _subData._palData[idx * 3]; byte g = _subData._palData[idx * 3 + 1]; byte b = _subData._palData[idx * 3 + 2]; - + int palIndex = R2_GLOBALS._scenePalette.indexOf(r, g, b); _palIndexes[idx] = palIndex; } diff --git a/engines/tsage/scenes.h b/engines/tsage/scenes.h index 2daa71ba98..d5ac88c692 100644 --- a/engines/tsage/scenes.h +++ b/engines/tsage/scenes.h @@ -67,7 +67,7 @@ public: void setZoomPercents(int yStart, int minPercent, int yEnd, int maxPercent); void loadBackground(int xAmount, int yAmount); - + void loadSceneData(int sceneNum); }; diff --git a/engines/tsage/tsage.cpp b/engines/tsage/tsage.cpp index 40f4dfcfd2..87697f950b 100644 --- a/engines/tsage/tsage.cpp +++ b/engines/tsage/tsage.cpp @@ -45,7 +45,7 @@ TSageEngine::TSageEngine(OSystem *system, const tSageGameDescription *gameDesc) else if (g_vm->getGameID() == GType_BlueForce) _debugger = new BlueForceDebugger(); else if (g_vm->getGameID() == GType_Ringworld2) - _debugger = new Ringworld2Debugger(); + _debugger = new Ringworld2Debugger(); } Common::Error TSageEngine::init() { @@ -92,7 +92,7 @@ void TSageEngine::initialize() { g_resourceManager->addLib("TSAGE.RLB"); } g_globals = new BlueForce::BlueForceGlobals(); - + // Setup the user interface T2_GLOBALS._uiElements.setup(Common::Point(0, UI_INTERFACE_Y - 2)); @@ -107,7 +107,7 @@ void TSageEngine::initialize() { // Reset all global variables R2_GLOBALS.reset(); - } + } g_globals->gfxManager().setDefaults(); diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp index bee09f7391..1b04f3fae9 100644 --- a/engines/tucker/resource.cpp +++ b/engines/tucker/resource.cpp @@ -29,6 +29,9 @@ #include "audio/decoders/vorbis.h" #include "audio/decoders/wave.h" +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" + #include "tucker/tucker.h" #include "tucker/graphics.h" @@ -298,23 +301,21 @@ void TuckerEngine::loadImage(const char *fname, uint8 *dst, int type) { return; } } - f.seek(128, SEEK_SET); - int size = 0; - while (size < 64000) { - int code = f.readByte(); - if (code >= 0xC0) { - const int sz = code - 0xC0; - code = f.readByte(); - memset(dst + size, code, sz); - size += sz; - } else { - dst[size++] = code; - } - } + + ::Graphics::PCXDecoder pcx; + if (!pcx.loadStream(f)) + error("Error while reading PCX image"); + + const ::Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + if (pcxSurface->w != 320 || pcxSurface->h != 200) + error("Invalid PCX surface size (%d x %d)", pcxSurface->w, pcxSurface->h); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(dst + y * 320, pcxSurface->getBasePtr(0, y), pcxSurface->w); + if (type != 0) { - if (f.readByte() != 12) - return; - f.read(_currentPalette, 768); + memcpy(_currentPalette, pcx.getPalette(), 3 * 256); setBlackPalette(); } } diff --git a/engines/wintermute/ad/ad_actor.cpp b/engines/wintermute/ad/ad_actor.cpp index 9087d66844..d175855d1e 100644 --- a/engines/wintermute/ad/ad_actor.cpp +++ b/engines/wintermute/ad/ad_actor.cpp @@ -602,13 +602,13 @@ bool AdActor::update() { } // finished playing animation? - if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->_finished) { + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { _state = _nextState; _nextState = STATE_READY; _currentSprite = _animSprite; } - if (_state == STATE_PLAYING_ANIM_SET && _animSprite2 != NULL && _animSprite2->_finished) { + if (_state == STATE_PLAYING_ANIM_SET && _animSprite2 != NULL && _animSprite2->isFinished()) { _state = _nextState; _nextState = STATE_READY; _currentSprite = _animSprite2; @@ -649,7 +649,7 @@ bool AdActor::update() { ////////////////////////////////////////////////////////////////////////// case STATE_TURNING_LEFT: - if (_tempSprite2 == NULL || _tempSprite2->_finished) { + if (_tempSprite2 == NULL || _tempSprite2->isFinished()) { if (_dir > 0) { _dir = (TDirection)(_dir - 1); } else { @@ -686,7 +686,7 @@ bool AdActor::update() { ////////////////////////////////////////////////////////////////////////// case STATE_TURNING_RIGHT: - if (_tempSprite2 == NULL || _tempSprite2->_finished) { + if (_tempSprite2 == NULL || _tempSprite2->isFinished()) { _dir = (TDirection)(_dir + 1); if ((int)_dir >= (int)NUM_DIRECTIONS) { @@ -753,7 +753,7 @@ bool AdActor::update() { } bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); - if (_tempSprite2 == NULL || _tempSprite2->_finished || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { if (timeIsUp) { _sentence->finish(); _tempSprite2 = NULL; @@ -798,7 +798,7 @@ bool AdActor::update() { if (_currentSprite && !already_moved) { _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); - if (_currentSprite->_changed) { + if (_currentSprite->isChanged()) { _posX += _currentSprite->_moveX; _posY += _currentSprite->_moveY; afterMove(); @@ -830,7 +830,7 @@ void AdActor::followPath() { // are there points to follow? if (_path->getCurrent() != NULL) { - _state = STATE_FOLLOWING_PATH;; + _state = STATE_FOLLOWING_PATH; initLine(BasePoint(_posX, _posY), *_path->getCurrent()); } else { if (_afterWalkDir != DI_NONE) { @@ -858,7 +858,7 @@ void AdActor::getNextStep() { } _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); - if (!_currentSprite->_changed) { + if (!_currentSprite->isChanged()) { return; } @@ -1075,27 +1075,27 @@ bool AdActor::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *AdActor::scGetProperty(const char *name) { +ScValue *AdActor::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Direction ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Direction") == 0) { + if (name == "Direction") { _scValue->setInt(_dir); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Type") == 0) { + else if (name == "Type") { _scValue->setString("actor"); return _scValue; } ////////////////////////////////////////////////////////////////////////// // TalkAnimName ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TalkAnimName") == 0) { + else if (name == "TalkAnimName") { _scValue->setString(_talkAnimName); return _scValue; } @@ -1103,7 +1103,7 @@ ScValue *AdActor::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // WalkAnimName ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "WalkAnimName") == 0) { + else if (name == "WalkAnimName") { _scValue->setString(_walkAnimName); return _scValue; } @@ -1111,7 +1111,7 @@ ScValue *AdActor::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // IdleAnimName ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "IdleAnimName") == 0) { + else if (name == "IdleAnimName") { _scValue->setString(_idleAnimName); return _scValue; } @@ -1119,7 +1119,7 @@ ScValue *AdActor::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TurnLeftAnimName ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TurnLeftAnimName") == 0) { + else if (name == "TurnLeftAnimName") { _scValue->setString(_turnLeftAnimName); return _scValue; } @@ -1127,7 +1127,7 @@ ScValue *AdActor::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TurnRightAnimName ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TurnRightAnimName") == 0) { + else if (name == "TurnRightAnimName") { _scValue->setString(_turnRightAnimName); return _scValue; } else { @@ -1351,7 +1351,7 @@ bool AdActor::persist(BasePersistenceManager *persistMgr) { ////////////////////////////////////////////////////////////////////////// TDirection AdActor::angleToDirection(int angle) { - TDirection ret = DI_DOWN;; + TDirection ret = DI_DOWN; if (angle > -112 && angle <= -67) { ret = DI_UP; diff --git a/engines/wintermute/ad/ad_actor.h b/engines/wintermute/ad/ad_actor.h index 271e57cb85..543c9d063a 100644 --- a/engines/wintermute/ad/ad_actor.h +++ b/engines/wintermute/ad/ad_actor.h @@ -83,7 +83,7 @@ private: AdSpriteSet *getAnimByName(const Common::String &animName); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ad/ad_entity.cpp b/engines/wintermute/ad/ad_entity.cpp index 234af1fffa..9af7e034ca 100644 --- a/engines/wintermute/ad/ad_entity.cpp +++ b/engines/wintermute/ad/ad_entity.cpp @@ -28,28 +28,29 @@ #include "engines/wintermute/ad/ad_entity.h" -#include "engines/wintermute/base/base_parser.h" -#include "engines/wintermute/base/base_dynamic_buffer.h" -#include "engines/wintermute/base/base_active_rect.h" -#include "engines/wintermute/base/base_surface_storage.h" -#include "engines/wintermute/base/base_game.h" #include "engines/wintermute/ad/ad_game.h" #include "engines/wintermute/ad/ad_scene.h" -#include "engines/wintermute/base/sound/base_sound.h" #include "engines/wintermute/ad/ad_waypoint_group.h" -#include "engines/wintermute/base/font/base_font_storage.h" -#include "engines/wintermute/base/font/base_font.h" #include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_region.h" #include "engines/wintermute/base/base_sprite.h" -#include "engines/wintermute/base/base_file_manager.h" -#include "engines/wintermute/platform_osystem.h" -#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/particles/part_emitter.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/sound/base_sound.h" #include "engines/wintermute/video/video_theora_player.h" -#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" #include "common/str.h" namespace Wintermute { @@ -577,7 +578,7 @@ bool AdEntity::update() { } // finished playing animation? - if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->_finished) { + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { _state = STATE_READY; _currentSprite = _animSprite; } @@ -612,7 +613,7 @@ bool AdEntity::update() { } bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); - if (_tempSprite2 == NULL || _tempSprite2->_finished || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { if (timeIsUp) { _sentence->finish(); _tempSprite2 = NULL; @@ -638,7 +639,7 @@ bool AdEntity::update() { if (_currentSprite) { _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); - if (_currentSprite->_changed) { + if (_currentSprite->isChanged()) { _posX += _currentSprite->_moveX; _posY += _currentSprite->_moveY; } @@ -828,13 +829,13 @@ bool AdEntity::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *AdEntity::scGetProperty(const char *name) { +ScValue *AdEntity::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("entity"); return _scValue; } @@ -842,7 +843,7 @@ ScValue *AdEntity::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Item ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Item") == 0) { + else if (name == "Item") { if (_item) { _scValue->setString(_item); } else { @@ -855,7 +856,7 @@ ScValue *AdEntity::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Subtype (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Subtype") == 0) { + else if (name == "Subtype") { if (_subtype == ENTITY_SOUND) { _scValue->setString("sound"); } else { @@ -868,7 +869,7 @@ ScValue *AdEntity::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // WalkToX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "WalkToX") == 0) { + else if (name == "WalkToX") { _scValue->setInt(_walkToX); return _scValue; } @@ -876,7 +877,7 @@ ScValue *AdEntity::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // WalkToY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "WalkToY") == 0) { + else if (name == "WalkToY") { _scValue->setInt(_walkToY); return _scValue; } @@ -884,7 +885,7 @@ ScValue *AdEntity::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // WalkToDirection ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "WalkToDirection") == 0) { + else if (name == "WalkToDirection") { _scValue->setInt((int)_walkToDir); return _scValue; } @@ -892,7 +893,7 @@ ScValue *AdEntity::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Region (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Region") == 0) { + else if (name == "Region") { if (_region) { _scValue->setNative(_region, true); } else { diff --git a/engines/wintermute/ad/ad_entity.h b/engines/wintermute/ad/ad_entity.h index 39dc133eef..415987e50a 100644 --- a/engines/wintermute/ad/ad_entity.h +++ b/engines/wintermute/ad/ad_entity.h @@ -56,7 +56,7 @@ public: TEntityType _subtype; // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ad/ad_game.cpp b/engines/wintermute/ad/ad_game.cpp index fe8a5991e2..4481b774c1 100644 --- a/engines/wintermute/ad/ad_game.cpp +++ b/engines/wintermute/ad/ad_game.cpp @@ -50,6 +50,7 @@ #include "engines/wintermute/base/base_viewport.h" #include "engines/wintermute/base/particles/part_emitter.h" #include "engines/wintermute/base/saveload.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script_engine.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" @@ -875,20 +876,20 @@ bool AdGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *AdGame::scGetProperty(const char *name) { +ScValue *AdGame::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("game"); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Scene ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Scene") == 0) { + else if (name == "Scene") { if (_scene) { _scValue->setNative(_scene, true); } else { @@ -900,7 +901,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SelectedItem ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SelectedItem") == 0) { + else if (name == "SelectedItem") { //if (_selectedItem) _scValue->setString(_selectedItem->_name); if (_selectedItem) { _scValue->setNative(_selectedItem, true); @@ -913,14 +914,14 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumItems ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumItems") == 0) { + else if (name == "NumItems") { return _invObject->scGetProperty(name); } ////////////////////////////////////////////////////////////////////////// // SmartItemCursor ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SmartItemCursor") == 0) { + else if (name == "SmartItemCursor") { _scValue->setBool(_smartItemCursor); return _scValue; } @@ -928,7 +929,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // InventoryVisible ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "InventoryVisible") == 0) { + else if (name == "InventoryVisible") { _scValue->setBool(_inventoryBox && _inventoryBox->_visible); return _scValue; } @@ -936,7 +937,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // InventoryScrollOffset ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "InventoryScrollOffset") == 0) { + else if (name == "InventoryScrollOffset") { if (_inventoryBox) { _scValue->setInt(_inventoryBox->_scrollOffset); } else { @@ -949,7 +950,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ResponsesVisible (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ResponsesVisible") == 0) { + else if (name == "ResponsesVisible") { _scValue->setBool(_stateEx == GAME_WAITING_RESPONSE); return _scValue; } @@ -957,7 +958,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // PrevScene / PreviousScene (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "PrevScene") == 0 || strcmp(name, "PreviousScene") == 0) { + else if (name == "PrevScene" || name == "PreviousScene") { if (!_prevSceneName) { _scValue->setString(""); } else { @@ -969,7 +970,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // PrevSceneFilename / PreviousSceneFilename (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "PrevSceneFilename") == 0 || strcmp(name, "PreviousSceneFilename") == 0) { + else if (name == "PrevSceneFilename" || name == "PreviousSceneFilename") { if (!_prevSceneFilename) { _scValue->setString(""); } else { @@ -981,7 +982,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // LastResponse (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "LastResponse") == 0) { + else if (name == "LastResponse") { if (!_responseBox || !_responseBox->_lastResponseText) { _scValue->setString(""); } else { @@ -993,7 +994,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // LastResponseOrig (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "LastResponseOrig") == 0) { + else if (name == "LastResponseOrig") { if (!_responseBox || !_responseBox->_lastResponseTextOrig) { _scValue->setString(""); } else { @@ -1005,7 +1006,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // InventoryObject ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "InventoryObject") == 0) { + else if (name == "InventoryObject") { if (_inventoryOwner == _invObject) { _scValue->setNative(this, true); } else { @@ -1018,7 +1019,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TotalNumItems ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TotalNumItems") == 0) { + else if (name == "TotalNumItems") { _scValue->setInt(_items.size()); return _scValue; } @@ -1026,7 +1027,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TalkSkipButton ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TalkSkipButton") == 0) { + else if (name == "TalkSkipButton") { _scValue->setInt(_talkSkipButton); return _scValue; } @@ -1034,7 +1035,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ChangingScene ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ChangingScene") == 0) { + else if (name == "ChangingScene") { _scValue->setBool(_scheduledScene != NULL); return _scValue; } @@ -1042,7 +1043,7 @@ ScValue *AdGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // StartupScene ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "StartupScene") == 0) { + else if (name == "StartupScene") { if (!_startupScene) { _scValue->setNULL(); } else { diff --git a/engines/wintermute/ad/ad_game.h b/engines/wintermute/ad/ad_game.h index 46427331bf..81c79a3da8 100644 --- a/engines/wintermute/ad/ad_game.h +++ b/engines/wintermute/ad/ad_game.h @@ -126,7 +126,7 @@ public: bool loadItemsBuffer(byte *buffer, bool merge = false); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); bool validMouse(); diff --git a/engines/wintermute/ad/ad_inventory_box.cpp b/engines/wintermute/ad/ad_inventory_box.cpp index 16b8e01ff3..7ae8ff8d69 100644 --- a/engines/wintermute/ad/ad_inventory_box.cpp +++ b/engines/wintermute/ad/ad_inventory_box.cpp @@ -35,6 +35,7 @@ #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/base/base_viewport.h" #include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/ui/ui_button.h" #include "engines/wintermute/ui/ui_window.h" #include "engines/wintermute/platform_osystem.h" diff --git a/engines/wintermute/ad/ad_item.cpp b/engines/wintermute/ad/ad_item.cpp index afd813933b..427b1c7db4 100644 --- a/engines/wintermute/ad/ad_item.cpp +++ b/engines/wintermute/ad/ad_item.cpp @@ -36,11 +36,11 @@ #include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/sound/base_sound.h" #include "engines/wintermute/base/base_sprite.h" -#include "engines/wintermute/utils/utils.h" -#include "engines/wintermute/platform_osystem.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" #include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" #include "common/str.h" namespace Wintermute { @@ -340,7 +340,7 @@ bool AdItem::update() { } // finished playing animation? - if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->_finished) { + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { _state = STATE_READY; _currentSprite = _animSprite; } @@ -379,7 +379,7 @@ bool AdItem::update() { } bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); - if (_tempSprite2 == NULL || _tempSprite2->_finished || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { if (timeIsUp) { _sentence->finish(); _tempSprite2 = NULL; @@ -614,13 +614,13 @@ bool AdItem::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *AdItem::scGetProperty(const char *name) { +ScValue *AdItem::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("item"); return _scValue; } @@ -628,7 +628,7 @@ ScValue *AdItem::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Name ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Name") == 0) { + else if (name == "Name") { _scValue->setString(getName()); return _scValue; } @@ -636,7 +636,7 @@ ScValue *AdItem::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // DisplayAmount ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "DisplayAmount") == 0) { + else if (name == "DisplayAmount") { _scValue->setBool(_displayAmount); return _scValue; } @@ -644,7 +644,7 @@ ScValue *AdItem::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Amount ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Amount") == 0) { + else if (name == "Amount") { _scValue->setInt(_amount); return _scValue; } @@ -652,7 +652,7 @@ ScValue *AdItem::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AmountOffsetX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AmountOffsetX") == 0) { + else if (name == "AmountOffsetX") { _scValue->setInt(_amountOffsetX); return _scValue; } @@ -660,7 +660,7 @@ ScValue *AdItem::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AmountOffsetY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AmountOffsetY") == 0) { + else if (name == "AmountOffsetY") { _scValue->setInt(_amountOffsetY); return _scValue; } @@ -668,7 +668,7 @@ ScValue *AdItem::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AmountAlign ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AmountAlign") == 0) { + else if (name == "AmountAlign") { _scValue->setInt(_amountAlign); return _scValue; } @@ -676,7 +676,7 @@ ScValue *AdItem::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AmountString ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AmountString") == 0) { + else if (name == "AmountString") { if (!_amountString) { _scValue->setNULL(); } else { @@ -688,7 +688,7 @@ ScValue *AdItem::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // CursorCombined ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "CursorCombined") == 0) { + else if (name == "CursorCombined") { _scValue->setBool(_cursorCombined); return _scValue; } else { diff --git a/engines/wintermute/ad/ad_item.h b/engines/wintermute/ad/ad_item.h index 6047c542c1..79978f9f72 100644 --- a/engines/wintermute/ad/ad_item.h +++ b/engines/wintermute/ad/ad_item.h @@ -51,7 +51,7 @@ public: bool loadBuffer(byte *buffer, bool complete = true); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ad/ad_layer.cpp b/engines/wintermute/ad/ad_layer.cpp index 46b75b8b21..209c12b7a2 100644 --- a/engines/wintermute/ad/ad_layer.cpp +++ b/engines/wintermute/ad/ad_layer.cpp @@ -29,12 +29,12 @@ #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/ad/ad_layer.h" #include "engines/wintermute/ad/ad_scene_node.h" -#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" -#include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/platform_osystem.h" #include "common/str.h" @@ -376,13 +376,13 @@ bool AdLayer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *AdLayer::scGetProperty(const char *name) { +ScValue *AdLayer::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("layer"); return _scValue; } @@ -390,7 +390,7 @@ ScValue *AdLayer::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumNodes (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumNodes") == 0) { + else if (name == "NumNodes") { _scValue->setInt(_nodes.size()); return _scValue; } @@ -398,7 +398,7 @@ ScValue *AdLayer::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Width ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Width") == 0) { + else if (name == "Width") { _scValue->setInt(_width); return _scValue; } @@ -406,7 +406,7 @@ ScValue *AdLayer::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Height ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Height") == 0) { + else if (name == "Height") { _scValue->setInt(_height); return _scValue; } @@ -414,7 +414,7 @@ ScValue *AdLayer::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Main (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Main") == 0) { + else if (name == "Main") { _scValue->setBool(_main); return _scValue; } @@ -422,7 +422,7 @@ ScValue *AdLayer::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // CloseUp ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "CloseUp") == 0) { + else if (name == "CloseUp") { _scValue->setBool(_closeUp); return _scValue; } @@ -430,7 +430,7 @@ ScValue *AdLayer::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Active ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Active") == 0) { + else if (name == "Active") { _scValue->setBool(_active); return _scValue; } else { diff --git a/engines/wintermute/ad/ad_layer.h b/engines/wintermute/ad/ad_layer.h index bb5f73b13a..de65e2822f 100644 --- a/engines/wintermute/ad/ad_layer.h +++ b/engines/wintermute/ad/ad_layer.h @@ -47,7 +47,7 @@ public: virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ad/ad_object.cpp b/engines/wintermute/ad/ad_object.cpp index 6c77917979..7b91daab2e 100644 --- a/engines/wintermute/ad/ad_object.cpp +++ b/engines/wintermute/ad/ad_object.cpp @@ -37,18 +37,19 @@ #include "engines/wintermute/ad/ad_waypoint_group.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_frame.h" -#include "engines/wintermute/base/sound/base_sound.h" -#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_string_table.h" #include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_surface_storage.h" #include "engines/wintermute/base/font/base_font.h" #include "engines/wintermute/base/font/base_font_storage.h" -#include "engines/wintermute/base/base_sprite.h" -#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/particles/part_emitter.h" #include "engines/wintermute/base/scriptables/script_engine.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" #include "engines/wintermute/base/scriptables/script_value.h" -#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/sound/base_sound.h" #include "common/str.h" #include "common/util.h" @@ -658,13 +659,13 @@ bool AdObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *AdObject::scGetProperty(const char *name) { +ScValue *AdObject::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("object"); return _scValue; } @@ -672,7 +673,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Active ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Active") == 0) { + else if (name == "Active") { _scValue->setBool(_active); return _scValue; } @@ -680,7 +681,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // IgnoreItems ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "IgnoreItems") == 0) { + else if (name == "IgnoreItems") { _scValue->setBool(_ignoreItems); return _scValue; } @@ -688,7 +689,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SceneIndependent ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SceneIndependent") == 0) { + else if (name == "SceneIndependent") { _scValue->setBool(_sceneIndependent); return _scValue; } @@ -696,7 +697,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SubtitlesWidth ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SubtitlesWidth") == 0) { + else if (name == "SubtitlesWidth") { _scValue->setInt(_subtitlesWidth); return _scValue; } @@ -704,7 +705,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SubtitlesPosRelative ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SubtitlesPosRelative") == 0) { + else if (name == "SubtitlesPosRelative") { _scValue->setBool(_subtitlesModRelative); return _scValue; } @@ -712,7 +713,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SubtitlesPosX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SubtitlesPosX") == 0) { + else if (name == "SubtitlesPosX") { _scValue->setInt(_subtitlesModX); return _scValue; } @@ -720,7 +721,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SubtitlesPosY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SubtitlesPosY") == 0) { + else if (name == "SubtitlesPosY") { _scValue->setInt(_subtitlesModY); return _scValue; } @@ -728,7 +729,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SubtitlesPosXCenter ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SubtitlesPosXCenter") == 0) { + else if (name == "SubtitlesPosXCenter") { _scValue->setBool(_subtitlesModXCenter); return _scValue; } @@ -736,7 +737,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumItems (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumItems") == 0) { + else if (name == "NumItems") { _scValue->setInt(getInventory()->_takenItems.size()); return _scValue; } @@ -744,7 +745,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ParticleEmitter (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ParticleEmitter") == 0) { + else if (name == "ParticleEmitter") { if (_partEmitter) { _scValue->setNative(_partEmitter, true); } else { @@ -757,7 +758,7 @@ ScValue *AdObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumAttachments (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumAttachments") == 0) { + else if (name == "NumAttachments") { _scValue->setInt(_attachmentsPre.size() + _attachmentsPost.size()); return _scValue; } else { diff --git a/engines/wintermute/ad/ad_object.h b/engines/wintermute/ad/ad_object.h index 8395f58cff..d1a20908e1 100644 --- a/engines/wintermute/ad/ad_object.h +++ b/engines/wintermute/ad/ad_object.h @@ -100,7 +100,7 @@ public: AdRegion *_currentRegions[MAX_NUM_REGIONS]; // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ad/ad_region.cpp b/engines/wintermute/ad/ad_region.cpp index 88bd8201a2..c9f1553c9a 100644 --- a/engines/wintermute/ad/ad_region.cpp +++ b/engines/wintermute/ad/ad_region.cpp @@ -27,12 +27,12 @@ */ #include "engines/wintermute/ad/ad_region.h" -#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_dynamic_buffer.h" -#include "engines/wintermute/base/scriptables/script_value.h" -#include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" namespace Wintermute { @@ -242,13 +242,13 @@ bool AdRegion::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *AdRegion::scGetProperty(const char *name) { +ScValue *AdRegion::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("ad region"); return _scValue; } @@ -256,7 +256,7 @@ ScValue *AdRegion::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Name ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Name") == 0) { + else if (name == "Name") { _scValue->setString(getName()); return _scValue; } @@ -264,7 +264,7 @@ ScValue *AdRegion::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Blocked ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Blocked") == 0) { + else if (name == "Blocked") { _scValue->setBool(_blocked); return _scValue; } @@ -272,7 +272,7 @@ ScValue *AdRegion::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Decoration ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Decoration") == 0) { + else if (name == "Decoration") { _scValue->setBool(_decoration); return _scValue; } @@ -280,7 +280,7 @@ ScValue *AdRegion::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Scale ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Scale") == 0) { + else if (name == "Scale") { _scValue->setFloat(_zoom); return _scValue; } @@ -288,7 +288,7 @@ ScValue *AdRegion::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AlphaColor ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AlphaColor") == 0) { + else if (name == "AlphaColor") { _scValue->setInt((int)_alpha); return _scValue; } else { diff --git a/engines/wintermute/ad/ad_region.h b/engines/wintermute/ad/ad_region.h index a60cb9a3f2..6112900361 100644 --- a/engines/wintermute/ad/ad_region.h +++ b/engines/wintermute/ad/ad_region.h @@ -47,7 +47,7 @@ public: virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ad/ad_response.cpp b/engines/wintermute/ad/ad_response.cpp index 37f46118bf..a2225f2632 100644 --- a/engines/wintermute/ad/ad_response.cpp +++ b/engines/wintermute/ad/ad_response.cpp @@ -28,8 +28,8 @@ #include "engines/wintermute/ad/ad_response.h" #include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/base/font/base_font_storage.h" #include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/font/base_font_storage.h" #include "engines/wintermute/utils/utils.h" namespace Wintermute { diff --git a/engines/wintermute/ad/ad_response_box.cpp b/engines/wintermute/ad/ad_response_box.cpp index a27f1ca54b..fb31aa0bb8 100644 --- a/engines/wintermute/ad/ad_response_box.cpp +++ b/engines/wintermute/ad/ad_response_box.cpp @@ -27,19 +27,20 @@ */ #include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_response.h" #include "engines/wintermute/ad/ad_response_box.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" #include "engines/wintermute/base/base_surface_storage.h" -#include "engines/wintermute/ui/ui_button.h" -#include "engines/wintermute/ui/ui_window.h" -#include "engines/wintermute/base/base_dynamic_buffer.h" #include "engines/wintermute/base/font/base_font_storage.h" #include "engines/wintermute/base/font/base_font.h" -#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" -#include "engines/wintermute/base/base_sprite.h" -#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_window.h" #include "engines/wintermute/utils/utils.h" #include "engines/wintermute/platform_osystem.h" #include "engines/wintermute/wintermute.h" diff --git a/engines/wintermute/ad/ad_rot_level.cpp b/engines/wintermute/ad/ad_rot_level.cpp index ca7ed693ad..fb9a4a47b9 100644 --- a/engines/wintermute/ad/ad_rot_level.cpp +++ b/engines/wintermute/ad/ad_rot_level.cpp @@ -27,11 +27,11 @@ */ #include "engines/wintermute/ad/ad_rot_level.h" -#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_sprite.h" -#include "engines/wintermute/base/base_file_manager.h" namespace Wintermute { diff --git a/engines/wintermute/ad/ad_scale_level.cpp b/engines/wintermute/ad/ad_scale_level.cpp index 8b68cc5d32..4e9293d875 100644 --- a/engines/wintermute/ad/ad_scale_level.cpp +++ b/engines/wintermute/ad/ad_scale_level.cpp @@ -28,9 +28,9 @@ #include "engines/wintermute/ad/ad_scale_level.h" #include "engines/wintermute/base/base_parser.h" -#include "engines/wintermute/base/base_dynamic_buffer.h" -#include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" namespace Wintermute { diff --git a/engines/wintermute/ad/ad_scene.cpp b/engines/wintermute/ad/ad_scene.cpp index e47acc63c9..8e9beca0c0 100644 --- a/engines/wintermute/ad/ad_scene.cpp +++ b/engines/wintermute/ad/ad_scene.cpp @@ -52,6 +52,7 @@ #include "engines/wintermute/base/base_scriptable.h" #include "engines/wintermute/base/base_sprite.h" #include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script_stack.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script.h" @@ -1808,13 +1809,13 @@ bool AdScene::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *AdScene::scGetProperty(const char *name) { +ScValue *AdScene::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("scene"); return _scValue; } @@ -1822,7 +1823,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumLayers (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumLayers") == 0) { + else if (name == "NumLayers") { _scValue->setInt(_layers.size()); return _scValue; } @@ -1830,7 +1831,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumWaypointGroups (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumWaypointGroups") == 0) { + else if (name == "NumWaypointGroups") { _scValue->setInt(_waypointGroups.size()); return _scValue; } @@ -1838,7 +1839,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MainLayer (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MainLayer") == 0) { + else if (name == "MainLayer") { if (_mainLayer) { _scValue->setNative(_mainLayer, true); } else { @@ -1851,7 +1852,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumFreeNodes (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumFreeNodes") == 0) { + else if (name == "NumFreeNodes") { _scValue->setInt(_objects.size()); return _scValue; } @@ -1859,7 +1860,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MouseX (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MouseX") == 0) { + else if (name == "MouseX") { int viewportX; getViewportOffset(&viewportX); @@ -1870,7 +1871,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MouseY (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MouseY") == 0) { + else if (name == "MouseY") { int viewportY; getViewportOffset(NULL, &viewportY); @@ -1881,7 +1882,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AutoScroll ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AutoScroll") == 0) { + else if (name == "AutoScroll") { _scValue->setBool(_autoScroll); return _scValue; } @@ -1889,7 +1890,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // PersistentState ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "PersistentState") == 0) { + else if (name == "PersistentState") { _scValue->setBool(_persistentState); return _scValue; } @@ -1897,7 +1898,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // PersistentStateSprites ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "PersistentStateSprites") == 0) { + else if (name == "PersistentStateSprites") { _scValue->setBool(_persistentStateSprites); return _scValue; } @@ -1905,7 +1906,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ScrollPixelsX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScrollPixelsX") == 0) { + else if (name == "ScrollPixelsX") { _scValue->setInt(_scrollPixelsH); return _scValue; } @@ -1913,7 +1914,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ScrollPixelsY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScrollPixelsY") == 0) { + else if (name == "ScrollPixelsY") { _scValue->setInt(_scrollPixelsV); return _scValue; } @@ -1922,7 +1923,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ScrollSpeedX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScrollSpeedX") == 0) { + else if (name == "ScrollSpeedX") { _scValue->setInt(_scrollTimeH); return _scValue; } @@ -1930,7 +1931,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ScrollSpeedY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScrollSpeedY") == 0) { + else if (name == "ScrollSpeedY") { _scValue->setInt(_scrollTimeV); return _scValue; } @@ -1938,7 +1939,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // OffsetX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "OffsetX") == 0) { + else if (name == "OffsetX") { _scValue->setInt(_offsetLeft); return _scValue; } @@ -1946,7 +1947,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // OffsetY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "OffsetY") == 0) { + else if (name == "OffsetY") { _scValue->setInt(_offsetTop); return _scValue; } @@ -1954,7 +1955,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Width (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Width") == 0) { + else if (name == "Width") { if (_mainLayer) { _scValue->setInt(_mainLayer->_width); } else { @@ -1966,7 +1967,7 @@ ScValue *AdScene::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Height (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Height") == 0) { + else if (name == "Height") { if (_mainLayer) { _scValue->setInt(_mainLayer->_height); } else { diff --git a/engines/wintermute/ad/ad_scene.h b/engines/wintermute/ad/ad_scene.h index c9c0e413bf..3b482403b5 100644 --- a/engines/wintermute/ad/ad_scene.h +++ b/engines/wintermute/ad/ad_scene.h @@ -156,7 +156,7 @@ public: int getPointsDist(BasePoint p1, BasePoint p2, BaseObject *requester = NULL); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ad/ad_scene_state.cpp b/engines/wintermute/ad/ad_scene_state.cpp index c09e6a259c..6b34f1af53 100644 --- a/engines/wintermute/ad/ad_scene_state.cpp +++ b/engines/wintermute/ad/ad_scene_state.cpp @@ -26,9 +26,9 @@ * Copyright (c) 2011 Jan Nedoma */ -#include "engines/wintermute/persistent.h" #include "engines/wintermute/ad/ad_scene_state.h" #include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/persistent.h" #include "engines/wintermute/platform_osystem.h" #include "common/str.h" diff --git a/engines/wintermute/ad/ad_sentence.cpp b/engines/wintermute/ad/ad_sentence.cpp index 1f09d3ae0f..cfe4191b07 100644 --- a/engines/wintermute/ad/ad_sentence.cpp +++ b/engines/wintermute/ad/ad_sentence.cpp @@ -26,17 +26,18 @@ * Copyright (c) 2011 Jan Nedoma */ +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_scene.h" #include "engines/wintermute/ad/ad_sentence.h" #include "engines/wintermute/ad/ad_talk_def.h" #include "engines/wintermute/ad/ad_talk_node.h" -#include "engines/wintermute/ad/ad_game.h" #include "engines/wintermute/utils/path_util.h" #include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/base/sound/base_sound.h" -#include "engines/wintermute/ad/ad_scene.h" -#include "engines/wintermute/base/font/base_font.h" #include "engines/wintermute/base/base_sprite.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound.h" namespace Wintermute { diff --git a/engines/wintermute/ad/ad_sprite_set.cpp b/engines/wintermute/ad/ad_sprite_set.cpp index c8cdec03c3..345b483a8f 100644 --- a/engines/wintermute/ad/ad_sprite_set.cpp +++ b/engines/wintermute/ad/ad_sprite_set.cpp @@ -27,10 +27,10 @@ */ #include "engines/wintermute/ad/ad_sprite_set.h" -#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_dynamic_buffer.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_sprite.h" namespace Wintermute { diff --git a/engines/wintermute/ad/ad_talk_def.cpp b/engines/wintermute/ad/ad_talk_def.cpp index 8cb489509b..a85cd7f986 100644 --- a/engines/wintermute/ad/ad_talk_def.cpp +++ b/engines/wintermute/ad/ad_talk_def.cpp @@ -26,13 +26,13 @@ * Copyright (c) 2011 Jan Nedoma */ +#include "engines/wintermute/ad/ad_sprite_set.h" #include "engines/wintermute/ad/ad_talk_def.h" #include "engines/wintermute/ad/ad_talk_node.h" #include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_dynamic_buffer.h" #include "engines/wintermute/base/base_sprite.h" -#include "engines/wintermute/ad/ad_sprite_set.h" #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/utils/utils.h" diff --git a/engines/wintermute/ad/ad_talk_holder.cpp b/engines/wintermute/ad/ad_talk_holder.cpp index 1e4ec26459..cca4fdc2cb 100644 --- a/engines/wintermute/ad/ad_talk_holder.cpp +++ b/engines/wintermute/ad/ad_talk_holder.cpp @@ -28,13 +28,13 @@ #include "engines/wintermute/ad/ad_talk_holder.h" #include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" -#include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/base/base_sprite.h" #include "engines/wintermute/platform_osystem.h" -#include "engines/wintermute/base/base_engine.h" #include "common/str.h" namespace Wintermute { @@ -334,13 +334,13 @@ bool AdTalkHolder::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisS ////////////////////////////////////////////////////////////////////////// -ScValue *AdTalkHolder::scGetProperty(const char *name) { +ScValue *AdTalkHolder::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("talk-holder"); return _scValue; } else { diff --git a/engines/wintermute/ad/ad_talk_holder.h b/engines/wintermute/ad/ad_talk_holder.h index ce10364b3d..d52ebf63c0 100644 --- a/engines/wintermute/ad/ad_talk_holder.h +++ b/engines/wintermute/ad/ad_talk_holder.h @@ -45,7 +45,7 @@ public: virtual ~AdTalkHolder(); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ad/ad_talk_node.cpp b/engines/wintermute/ad/ad_talk_node.cpp index b43a2b288e..c909ee27ff 100644 --- a/engines/wintermute/ad/ad_talk_node.cpp +++ b/engines/wintermute/ad/ad_talk_node.cpp @@ -26,12 +26,12 @@ * Copyright (c) 2011 Jan Nedoma */ +#include "engines/wintermute/ad/ad_sprite_set.h" #include "engines/wintermute/ad/ad_talk_node.h" #include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_dynamic_buffer.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_sprite.h" -#include "engines/wintermute/ad/ad_sprite_set.h" #include "engines/wintermute/utils/utils.h" namespace Wintermute { @@ -264,9 +264,9 @@ bool AdTalkNode::loadSprite() { bool AdTalkNode::isInTimeInterval(uint32 time, TDirection dir) { if (time >= _startTime) { if (_playToEnd) { - if ((_spriteFilename && _sprite == NULL) || (_sprite && _sprite->_finished == false)) { + if ((_spriteFilename && _sprite == NULL) || (_sprite && _sprite->isFinished() == false)) { return true; - } else if ((_spriteSetFilename && _spriteSet == NULL) || (_spriteSet && _spriteSet->getSprite(dir) && _spriteSet->getSprite(dir)->_finished == false)) { + } else if ((_spriteSetFilename && _spriteSet == NULL) || (_spriteSet && _spriteSet->getSprite(dir) && _spriteSet->getSprite(dir)->isFinished() == false)) { return true; } else { return false; diff --git a/engines/wintermute/ad/ad_waypoint_group.cpp b/engines/wintermute/ad/ad_waypoint_group.cpp index 984ed75aeb..81493ce769 100644 --- a/engines/wintermute/ad/ad_waypoint_group.cpp +++ b/engines/wintermute/ad/ad_waypoint_group.cpp @@ -27,12 +27,12 @@ */ #include "engines/wintermute/ad/ad_waypoint_group.h" -#include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_dynamic_buffer.h" -#include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/base/base_region.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/scriptables/script_value.h" #include <limits.h> namespace Wintermute { @@ -206,13 +206,13 @@ bool AdWaypointGroup::persist(BasePersistenceManager *persistMgr) { ////////////////////////////////////////////////////////////////////////// -ScValue *AdWaypointGroup::scGetProperty(const char *name) { +ScValue *AdWaypointGroup::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("waypoint-group"); return _scValue; } @@ -220,7 +220,7 @@ ScValue *AdWaypointGroup::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Active ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Active") == 0) { + else if (name == "Active") { _scValue->setBool(_active); return _scValue; } else { diff --git a/engines/wintermute/ad/ad_waypoint_group.h b/engines/wintermute/ad/ad_waypoint_group.h index 5cf6da1d1a..13d6bbadd7 100644 --- a/engines/wintermute/ad/ad_waypoint_group.h +++ b/engines/wintermute/ad/ad_waypoint_group.h @@ -49,7 +49,7 @@ public: virtual ~AdWaypointGroup(); BaseArray<BasePoint *> _points; int _editorSelectedPoint; - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); }; diff --git a/engines/wintermute/base/base_active_rect.cpp b/engines/wintermute/base/base_active_rect.cpp index d754cf0114..4addf15be8 100644 --- a/engines/wintermute/base/base_active_rect.cpp +++ b/engines/wintermute/base/base_active_rect.cpp @@ -29,6 +29,7 @@ #include "engines/wintermute/base/base_active_rect.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/platform_osystem.h" namespace Wintermute { diff --git a/engines/wintermute/base/base_dynamic_buffer.h b/engines/wintermute/base/base_dynamic_buffer.h index b6f3e12b9c..2d1a7fbe48 100644 --- a/engines/wintermute/base/base_dynamic_buffer.h +++ b/engines/wintermute/base/base_dynamic_buffer.h @@ -29,14 +29,12 @@ #ifndef WINTERMUTE_BASE_DYNAMIC_BUFFER_H #define WINTERMUTE_BASE_DYNAMIC_BUFFER_H - #include "engines/wintermute/base/base.h" namespace Wintermute { class BaseDynamicBuffer { public: - bool _initialized; void putText(const char *fmt, ...); void putTextIndent(int indent, const char *fmt, ...); uint32 getDWORD(); @@ -48,12 +46,13 @@ public: uint32 getSize(); bool init(uint32 initSize = 0); void cleanup(); - uint32 _size; - byte *_buffer; BaseDynamicBuffer(BaseGame *inGame, uint32 initSize = 1000, uint32 growBy = 1000); virtual ~BaseDynamicBuffer(); private: + uint32 _size; + byte *_buffer; + bool _initialized; uint32 _realSize; uint32 _growBy; uint32 _initSize; diff --git a/engines/wintermute/base/base_engine.cpp b/engines/wintermute/base/base_engine.cpp index 2368f8b106..8146d14beb 100644 --- a/engines/wintermute/base/base_engine.cpp +++ b/engines/wintermute/base/base_engine.cpp @@ -8,12 +8,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -25,7 +25,7 @@ * http://dead-code.org/redir.php?target=wmelite * Copyright (c) 2011 Jan Nedoma */ - + #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_engine.h" @@ -71,10 +71,10 @@ void BaseEngine::LOG(bool res, const char *fmt, ...) { secs = secs % 3600; uint32 mins = secs / 60; secs = secs % 60; - + char buff[512]; va_list va; - + va_start(va, fmt); vsprintf(buff, fmt, va); va_end(va); @@ -82,7 +82,7 @@ void BaseEngine::LOG(bool res, const char *fmt, ...) { if (instance()._gameRef) { instance()._gameRef->LOG("%s", buff); } else { - debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); + debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); } } diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h index 1cef7b33ba..1ed0e3ab01 100644 --- a/engines/wintermute/base/base_engine.h +++ b/engines/wintermute/base/base_engine.h @@ -8,12 +8,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -25,7 +25,7 @@ * http://dead-code.org/redir.php?target=wmelite * Copyright (c) 2011 Jan Nedoma */ - + #ifndef WINTERMUTE_BASE_ENGINE_H #define WINTERMUTE_BASE_ENGINE_H diff --git a/engines/wintermute/base/base_fader.cpp b/engines/wintermute/base/base_fader.cpp index 08e6f689ba..985718fcab 100644 --- a/engines/wintermute/base/base_fader.cpp +++ b/engines/wintermute/base/base_fader.cpp @@ -28,6 +28,7 @@ #include "engines/wintermute/base/base_fader.h" #include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "common/util.h" namespace Wintermute { diff --git a/engines/wintermute/base/base_fader.h b/engines/wintermute/base/base_fader.h index d3ced4aacc..116c8c963d 100644 --- a/engines/wintermute/base/base_fader.h +++ b/engines/wintermute/base/base_fader.h @@ -36,7 +36,6 @@ namespace Wintermute { class BaseFader : public BaseObject { public: - bool _system; uint32 getCurrentColor(); bool fadeOut(uint32 targetColor, uint32 duration, bool system = false); bool fadeIn(uint32 sourceColor, uint32 duration, bool system = false); @@ -47,6 +46,7 @@ public: BaseFader(BaseGame *inGame); virtual ~BaseFader(); private: + bool _system; bool _active; byte _red; byte _green; diff --git a/engines/wintermute/base/base_frame.cpp b/engines/wintermute/base/base_frame.cpp index e1b29a3a5c..7c64144480 100644 --- a/engines/wintermute/base/base_frame.cpp +++ b/engines/wintermute/base/base_frame.cpp @@ -87,6 +87,12 @@ bool BaseFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float return STATUS_OK; } +void BaseFrame::stopSound() { + if (_sound) { + _sound->stop(); + } +} + ////////////////////////////////////////////////////////////////////////// bool BaseFrame::oneTimeDisplay(BaseObject *owner, bool muted) { @@ -618,7 +624,7 @@ bool BaseFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStac ////////////////////////////////////////////////////////////////////////// -ScValue *BaseFrame::scGetProperty(const char *name) { +ScValue *BaseFrame::scGetProperty(const Common::String &name) { if (!_scValue) { _scValue = new ScValue(_gameRef); } @@ -627,7 +633,7 @@ ScValue *BaseFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("frame"); return _scValue; } @@ -635,7 +641,7 @@ ScValue *BaseFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Delay ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Delay") == 0) { + else if (name == "Delay") { _scValue->setInt(_delay); return _scValue; } @@ -643,7 +649,7 @@ ScValue *BaseFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Keyframe ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Keyframe") == 0) { + else if (name == "Keyframe") { _scValue->setBool(_keyframe); return _scValue; } @@ -651,7 +657,7 @@ ScValue *BaseFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // KillSounds ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "KillSounds") == 0) { + else if (name == "KillSounds") { _scValue->setBool(_killSound); return _scValue; } @@ -659,7 +665,7 @@ ScValue *BaseFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MoveX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MoveX") == 0) { + else if (name == "MoveX") { _scValue->setInt(_moveX); return _scValue; } @@ -667,7 +673,7 @@ ScValue *BaseFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MoveY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MoveY") == 0) { + else if (name == "MoveY") { _scValue->setInt(_moveY); return _scValue; } @@ -675,7 +681,7 @@ ScValue *BaseFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumSubframes (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumSubframes") == 0) { + else if (name == "NumSubframes") { _scValue->setInt(_subframes.size()); return _scValue; } @@ -683,7 +689,7 @@ ScValue *BaseFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumEvents (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumEvents") == 0) { + else if (name == "NumEvents") { _scValue->setInt(_applyEvent.size()); return _scValue; } diff --git a/engines/wintermute/base/base_frame.h b/engines/wintermute/base/base_frame.h index ea5467b6fe..7c5d893e70 100644 --- a/engines/wintermute/base/base_frame.h +++ b/engines/wintermute/base/base_frame.h @@ -41,11 +41,10 @@ class ScStack; class BaseFrame: public BaseScriptable { public: bool _killSound; - bool _keyframe; + void stopSound(); bool oneTimeDisplay(BaseObject *owner, bool muted = false); DECLARE_PERSISTENT(BaseFrame, BaseScriptable) - BaseSound *_sound; - bool _editorExpanded; + bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); bool saveAsText(BaseDynamicBuffer *buffer, int indent); int _moveY; @@ -61,11 +60,14 @@ public: BaseArray<const char *> _applyEvent; // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); - +private: + bool _keyframe; + bool _editorExpanded; + BaseSound *_sound; }; } // end of namespace Wintermute diff --git a/engines/wintermute/base/base_game.cpp b/engines/wintermute/base/base_game.cpp index 622e85cc11..b6886b7a33 100644 --- a/engines/wintermute/base/base_game.cpp +++ b/engines/wintermute/base/base_game.cpp @@ -64,6 +64,7 @@ #include "engines/wintermute/ui/ui_window.h" #include "engines/wintermute/wintermute.h" #include "engines/wintermute/platform_osystem.h" +#include "base/version.h" #include "common/config-manager.h" #include "common/savefile.h" #include "common/textconsole.h" @@ -489,7 +490,7 @@ void BaseGame::DEBUG_DebugEnable(const char *filename) { LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Release Build) *****************", hours, mins, secs); #endif - LOG(0, "%s ver %d.%d.%d%s, Compiled on " __DATE__ ", " __TIME__, DCGF_NAME, DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, DCGF_VER_SUFFIX); + LOG(0, "%s - %s ver %d.%d.%d%s ", gScummVMFullVersion, DCGF_NAME, DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, DCGF_VER_SUFFIX); AnsiString platform = BasePlatform::getPlatformName(); LOG(0, "Platform: %s", platform.c_str()); @@ -740,7 +741,7 @@ bool BaseGame::loadBuffer(byte *buffer, bool complete) { TOKEN_TABLE(LOCAL_SAVE_DIR) TOKEN_TABLE(COMPAT_KILL_METHOD_THREADS) TOKEN_TABLE_END - + // Declare a few variables necessary for moving data from these settings over to the renderer: // The values are the same as the defaults set in BaseRenderer. int loadImageX = 0; @@ -2241,27 +2242,27 @@ bool BaseGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *BaseGame::scGetProperty(const char *name) { +ScValue *BaseGame::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("game"); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Name ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Name") == 0) { + else if (name == "Name") { _scValue->setString(getName()); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Hwnd (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Hwnd") == 0) { + else if (name == "Hwnd") { _scValue->setInt((int)_renderer->_window); return _scValue; } @@ -2269,7 +2270,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // CurrentTime (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "CurrentTime") == 0) { + else if (name == "CurrentTime") { _scValue->setInt((int)_timer); return _scValue; } @@ -2277,7 +2278,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // WindowsTime (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "WindowsTime") == 0) { + else if (name == "WindowsTime") { _scValue->setInt((int)g_system->getMillis()); return _scValue; } @@ -2285,7 +2286,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // WindowedMode (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "WindowedMode") == 0) { + else if (name == "WindowedMode") { _scValue->setBool(_renderer->_windowed); return _scValue; } @@ -2293,7 +2294,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MouseX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MouseX") == 0) { + else if (name == "MouseX") { _scValue->setInt(_mousePos.x); return _scValue; } @@ -2301,7 +2302,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MouseY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MouseY") == 0) { + else if (name == "MouseY") { _scValue->setInt(_mousePos.y); return _scValue; } @@ -2309,7 +2310,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MainObject ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MainObject") == 0) { + else if (name == "MainObject") { _scValue->setNative(_mainObject, true); return _scValue; } @@ -2317,7 +2318,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ActiveObject (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ActiveObject") == 0) { + else if (name == "ActiveObject") { _scValue->setNative(_activeObject, true); return _scValue; } @@ -2325,7 +2326,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ScreenWidth (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScreenWidth") == 0) { + else if (name == "ScreenWidth") { _scValue->setInt(_renderer->_width); return _scValue; } @@ -2333,7 +2334,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ScreenHeight (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScreenHeight") == 0) { + else if (name == "ScreenHeight") { _scValue->setInt(_renderer->_height); return _scValue; } @@ -2341,7 +2342,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Interactive ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Interactive") == 0) { + else if (name == "Interactive") { _scValue->setBool(_interactive); return _scValue; } @@ -2349,7 +2350,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // DebugMode (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "DebugMode") == 0) { + else if (name == "DebugMode") { _scValue->setBool(_debugDebugMode); return _scValue; } @@ -2357,7 +2358,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SoundAvailable (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SoundAvailable") == 0) { + else if (name == "SoundAvailable") { _scValue->setBool(_soundMgr->_soundAvailable); return _scValue; } @@ -2365,7 +2366,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SFXVolume ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SFXVolume") == 0) { + else if (name == "SFXVolume") { _gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete"); _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType)); return _scValue; @@ -2374,7 +2375,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SpeechVolume ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SpeechVolume") == 0) { + else if (name == "SpeechVolume") { _gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete"); _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType)); return _scValue; @@ -2383,7 +2384,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MusicVolume ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MusicVolume") == 0) { + else if (name == "MusicVolume") { _gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete"); _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType)); return _scValue; @@ -2392,7 +2393,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MasterVolume ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MasterVolume") == 0) { + else if (name == "MasterVolume") { _gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete"); _scValue->setInt(_soundMgr->getMasterVolumePercent()); return _scValue; @@ -2401,7 +2402,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Keyboard (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Keyboard") == 0) { + else if (name == "Keyboard") { if (_keyboardState) { _scValue->setNative(_keyboardState, true); } else { @@ -2414,7 +2415,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Subtitles ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Subtitles") == 0) { + else if (name == "Subtitles") { _scValue->setBool(_subtitles); return _scValue; } @@ -2422,14 +2423,14 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SubtitlesSpeed ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SubtitlesSpeed") == 0) { + else if (name == "SubtitlesSpeed") { _scValue->setInt(_subtitlesSpeed); return _scValue; } ////////////////////////////////////////////////////////////////////////// // VideoSubtitles ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "VideoSubtitles") == 0) { + else if (name == "VideoSubtitles") { _scValue->setBool(_videoSubtitles); return _scValue; } @@ -2437,7 +2438,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // FPS (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "FPS") == 0) { + else if (name == "FPS") { _scValue->setInt(_fps); return _scValue; } @@ -2445,7 +2446,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AcceleratedMode / Accelerated (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AcceleratedMode") == 0 || strcmp(name, "Accelerated") == 0) { + else if (name == "AcceleratedMode" || name == "Accelerated") { _scValue->setBool(_useD3D); return _scValue; } @@ -2453,7 +2454,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TextEncoding ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TextEncoding") == 0) { + else if (name == "TextEncoding") { _scValue->setInt(_textEncoding); return _scValue; } @@ -2461,7 +2462,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TextRTL ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TextRTL") == 0) { + else if (name == "TextRTL") { _scValue->setBool(_textRTL); return _scValue; } @@ -2469,7 +2470,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SoundBufferSize ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SoundBufferSize") == 0) { + else if (name == "SoundBufferSize") { _scValue->setInt(_soundBufferSizeSec); return _scValue; } @@ -2477,7 +2478,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SuspendedRendering ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SuspendedRendering") == 0) { + else if (name == "SuspendedRendering") { _scValue->setBool(_suspendedRendering); return _scValue; } @@ -2485,7 +2486,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SuppressScriptErrors ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SuppressScriptErrors") == 0) { + else if (name == "SuppressScriptErrors") { _scValue->setBool(_suppressScriptErrors); return _scValue; } @@ -2494,7 +2495,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Frozen ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Frozen") == 0) { + else if (name == "Frozen") { _scValue->setBool(_state == GAME_FROZEN); return _scValue; } @@ -2502,7 +2503,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccTTSEnabled ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccTTSEnabled") == 0) { + else if (name == "AccTTSEnabled") { _scValue->setBool(false); return _scValue; } @@ -2510,7 +2511,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccTTSTalk ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccTTSTalk") == 0) { + else if (name == "AccTTSTalk") { _scValue->setBool(false); return _scValue; } @@ -2518,7 +2519,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccTTSCaptions ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccTTSCaptions") == 0) { + else if (name == "AccTTSCaptions") { _scValue->setBool(false); return _scValue; } @@ -2526,7 +2527,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccTTSKeypress ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccTTSKeypress") == 0) { + else if (name == "AccTTSKeypress") { _scValue->setBool(false); return _scValue; } @@ -2534,7 +2535,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccKeyboardEnabled ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccKeyboardEnabled") == 0) { + else if (name == "AccKeyboardEnabled") { _scValue->setBool(false); return _scValue; } @@ -2542,7 +2543,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccKeyboardCursorSkip ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccKeyboardCursorSkip") == 0) { + else if (name == "AccKeyboardCursorSkip") { _scValue->setBool(false); return _scValue; } @@ -2550,7 +2551,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccKeyboardPause ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccKeyboardPause") == 0) { + else if (name == "AccKeyboardPause") { _scValue->setBool(false); return _scValue; } @@ -2558,7 +2559,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AutorunDisabled ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AutorunDisabled") == 0) { + else if (name == "AutorunDisabled") { _scValue->setBool(_autorunDisabled); return _scValue; } @@ -2566,7 +2567,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SaveDirectory (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SaveDirectory") == 0) { + else if (name == "SaveDirectory") { AnsiString dataDir = "saves/"; // TODO: This is just to avoid telling the engine actual paths. _scValue->setString(dataDir.c_str()); return _scValue; @@ -2575,7 +2576,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AutoSaveOnExit ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AutoSaveOnExit") == 0) { + else if (name == "AutoSaveOnExit") { _scValue->setBool(_autoSaveOnExit); return _scValue; } @@ -2583,7 +2584,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AutoSaveSlot ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AutoSaveSlot") == 0) { + else if (name == "AutoSaveSlot") { _scValue->setInt(_autoSaveSlot); return _scValue; } @@ -2591,7 +2592,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // CursorHidden ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "CursorHidden") == 0) { + else if (name == "CursorHidden") { _scValue->setBool(_cursorHidden); return _scValue; } @@ -2599,7 +2600,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Platform (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Platform") == 0) { + else if (name == "Platform") { _scValue->setString(BasePlatform::getPlatformName().c_str()); return _scValue; } @@ -2607,7 +2608,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // DeviceType (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "DeviceType") == 0) { + else if (name == "DeviceType") { _scValue->setString(getDeviceType().c_str()); return _scValue; } @@ -2615,7 +2616,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MostRecentSaveSlot (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MostRecentSaveSlot") == 0) { + else if (name == "MostRecentSaveSlot") { if (!ConfMan.hasKey("most_recent_saveslot")) { _scValue->setInt(-1); } else { @@ -2627,7 +2628,7 @@ ScValue *BaseGame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Store (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Store") == 0) { + else if (name == "Store") { _scValue->setNULL(); error("Request for a SXStore-object, which is not supported by ScummVM"); @@ -3624,7 +3625,7 @@ bool BaseGame::persist(BasePersistenceManager *persistMgr) { persistMgr->transfer(TMEMBER(_musicCrossfadeChannel1)); persistMgr->transfer(TMEMBER(_musicCrossfadeChannel2)); persistMgr->transfer(TMEMBER(_musicCrossfadeSwap)); - + _renderer->persistSaveLoadImages(persistMgr); persistMgr->transfer(TMEMBER_INT(_textEncoding)); diff --git a/engines/wintermute/base/base_game.h b/engines/wintermute/base/base_game.h index 93cbc4536b..0f764b3d03 100644 --- a/engines/wintermute/base/base_game.h +++ b/engines/wintermute/base/base_game.h @@ -29,7 +29,6 @@ #ifndef WINTERMUTE_BASE_GAME_H #define WINTERMUTE_BASE_GAME_H -#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/base_object.h" #include "engines/wintermute/persistent.h" #include "engines/wintermute/coll_templ.h" @@ -165,7 +164,7 @@ public: virtual bool externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/base/base_keyboard_state.cpp b/engines/wintermute/base/base_keyboard_state.cpp index fd5f2b0e1d..da7baafd2d 100644 --- a/engines/wintermute/base/base_keyboard_state.cpp +++ b/engines/wintermute/base/base_keyboard_state.cpp @@ -103,13 +103,13 @@ bool BaseKeyboardState::scCallMethod(ScScript *script, ScStack *stack, ScStack * ////////////////////////////////////////////////////////////////////////// -ScValue *BaseKeyboardState::scGetProperty(const char *name) { +ScValue *BaseKeyboardState::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("keyboard"); return _scValue; } @@ -117,7 +117,7 @@ ScValue *BaseKeyboardState::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Key ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Key") == 0) { + else if (name == "Key") { if (_currentPrintable) { char key[2]; key[0] = (char)_currentCharCode; @@ -133,7 +133,7 @@ ScValue *BaseKeyboardState::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Printable ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Printable") == 0) { + else if (name == "Printable") { _scValue->setBool(_currentPrintable); return _scValue; } @@ -141,7 +141,7 @@ ScValue *BaseKeyboardState::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // KeyCode ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "KeyCode") == 0) { + else if (name == "KeyCode") { _scValue->setInt(_currentCharCode); return _scValue; } @@ -149,7 +149,7 @@ ScValue *BaseKeyboardState::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // IsShift ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "IsShift") == 0) { + else if (name == "IsShift") { _scValue->setBool(_currentShift); return _scValue; } @@ -157,7 +157,7 @@ ScValue *BaseKeyboardState::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // IsAlt ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "IsAlt") == 0) { + else if (name == "IsAlt") { _scValue->setBool(_currentAlt); return _scValue; } @@ -165,7 +165,7 @@ ScValue *BaseKeyboardState::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // IsControl ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "IsControl") == 0) { + else if (name == "IsControl") { _scValue->setBool(_currentControl); return _scValue; } else { diff --git a/engines/wintermute/base/base_keyboard_state.h b/engines/wintermute/base/base_keyboard_state.h index ebc0c83ee1..dfd0efdec0 100644 --- a/engines/wintermute/base/base_keyboard_state.h +++ b/engines/wintermute/base/base_keyboard_state.h @@ -59,7 +59,7 @@ public: static bool isAltDown(); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/base/base_object.cpp b/engines/wintermute/base/base_object.cpp index b6a6887624..eba8416485 100644 --- a/engines/wintermute/base/base_object.cpp +++ b/engines/wintermute/base/base_object.cpp @@ -531,13 +531,13 @@ bool BaseObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSta ////////////////////////////////////////////////////////////////////////// -ScValue *BaseObject::scGetProperty(const char *name) { +ScValue *BaseObject::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("object"); return _scValue; } @@ -545,7 +545,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Caption ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Caption") == 0) { + else if (name == "Caption") { _scValue->setString(getCaption(1)); return _scValue; } @@ -553,7 +553,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // X ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "X") == 0) { + else if (name == "X") { _scValue->setInt(_posX); return _scValue; } @@ -561,7 +561,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Y ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Y") == 0) { + else if (name == "Y") { _scValue->setInt(_posY); return _scValue; } @@ -569,7 +569,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Height (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Height") == 0) { + else if (name == "Height") { _scValue->setInt(getHeight()); return _scValue; } @@ -577,7 +577,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Ready (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Ready") == 0) { + else if (name == "Ready") { _scValue->setBool(_ready); return _scValue; } @@ -585,7 +585,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Movable ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Movable") == 0) { + else if (name == "Movable") { _scValue->setBool(_movable); return _scValue; } @@ -593,7 +593,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Registrable/Interactive ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Registrable") == 0 || strcmp(name, "Interactive") == 0) { + else if (name == "Registrable" || name == "Interactive") { _scValue->setBool(_registrable); return _scValue; } @@ -601,21 +601,21 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Zoomable/Scalable ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Zoomable") == 0 || strcmp(name, "Scalable") == 0) { + else if (name == "Zoomable" || name == "Scalable") { _scValue->setBool(_zoomable); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Rotatable ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Rotatable") == 0) { + else if (name == "Rotatable") { _scValue->setBool(_rotatable); return _scValue; } ////////////////////////////////////////////////////////////////////////// // AlphaColor ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AlphaColor") == 0) { + else if (name == "AlphaColor") { _scValue->setInt((int)_alphaColor); return _scValue; } @@ -623,7 +623,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // BlendMode ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "BlendMode") == 0) { + else if (name == "BlendMode") { _scValue->setInt((int)_blendMode); return _scValue; } @@ -631,7 +631,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Scale ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Scale") == 0) { + else if (name == "Scale") { if (_scale < 0) { _scValue->setNULL(); } else { @@ -643,7 +643,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ScaleX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScaleX") == 0) { + else if (name == "ScaleX") { if (_scaleX < 0) { _scValue->setNULL(); } else { @@ -655,7 +655,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ScaleY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScaleY") == 0) { + else if (name == "ScaleY") { if (_scaleY < 0) { _scValue->setNULL(); } else { @@ -667,7 +667,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // RelativeScale ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "RelativeScale") == 0) { + else if (name == "RelativeScale") { _scValue->setFloat((double)_relativeScale); return _scValue; } @@ -675,7 +675,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Rotate ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Rotate") == 0) { + else if (name == "Rotate") { if (!_rotateValid) { _scValue->setNULL(); } else { @@ -687,7 +687,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // RelativeRotate ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "RelativeRotate") == 0) { + else if (name == "RelativeRotate") { _scValue->setFloat((double)_relativeRotate); return _scValue; } @@ -695,14 +695,14 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Colorable ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Colorable") == 0) { + else if (name == "Colorable") { _scValue->setBool(_shadowable); return _scValue; } ////////////////////////////////////////////////////////////////////////// // SoundPanning ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SoundPanning") == 0) { + else if (name == "SoundPanning") { _scValue->setBool(_autoSoundPanning); return _scValue; } @@ -710,7 +710,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SaveState ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SaveState") == 0) { + else if (name == "SaveState") { _scValue->setBool(_saveState); return _scValue; } @@ -718,7 +718,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NonIntMouseEvents ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NonIntMouseEvents") == 0) { + else if (name == "NonIntMouseEvents") { _scValue->setBool(_nonIntMouseEvents); return _scValue; } @@ -726,7 +726,7 @@ ScValue *BaseObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccCaption ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccCaption") == 0) { + else if (name == "AccCaption") { _scValue->setNULL(); return _scValue; } else { diff --git a/engines/wintermute/base/base_object.h b/engines/wintermute/base/base_object.h index 34adbdb585..d7d91a25f6 100644 --- a/engines/wintermute/base/base_object.h +++ b/engines/wintermute/base/base_object.h @@ -136,7 +136,7 @@ public: public: // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/base/base_quick_msg.h b/engines/wintermute/base/base_quick_msg.h index 4fed5ffc2e..67f9613461 100644 --- a/engines/wintermute/base/base_quick_msg.h +++ b/engines/wintermute/base/base_quick_msg.h @@ -37,10 +37,10 @@ class BaseQuickMsg : public BaseClass { public: char *getText(); uint32 _startTime; - char *_text; - BaseQuickMsg(BaseGame *inGame, const char *Text); + BaseQuickMsg(BaseGame *inGame, const char *text); virtual ~BaseQuickMsg(); - +private: + char *_text; }; } // end of namespace Wintermute diff --git a/engines/wintermute/base/base_region.cpp b/engines/wintermute/base/base_region.cpp index e332ffe9ff..0bc5975e51 100644 --- a/engines/wintermute/base/base_region.cpp +++ b/engines/wintermute/base/base_region.cpp @@ -327,13 +327,13 @@ bool BaseRegion::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSta ////////////////////////////////////////////////////////////////////////// -ScValue *BaseRegion::scGetProperty(const char *name) { +ScValue *BaseRegion::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("region"); return _scValue; } @@ -341,7 +341,7 @@ ScValue *BaseRegion::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Name ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Name") == 0) { + else if (name == "Name") { _scValue->setString(getName()); return _scValue; } @@ -349,7 +349,7 @@ ScValue *BaseRegion::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Active ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Active") == 0) { + else if (name == "Active") { _scValue->setBool(_active); return _scValue; } @@ -357,7 +357,7 @@ ScValue *BaseRegion::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumPoints ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumPoints") == 0) { + else if (name == "NumPoints") { _scValue->setInt(_points.size()); return _scValue; } else { diff --git a/engines/wintermute/base/base_region.h b/engines/wintermute/base/base_region.h index 8dd02fe928..464f25be2f 100644 --- a/engines/wintermute/base/base_region.h +++ b/engines/wintermute/base/base_region.h @@ -36,9 +36,6 @@ namespace Wintermute { class BaseRegion : public BaseObject { public: - float _lastMimicScale; - int _lastMimicX; - int _lastMimicY; void cleanup(); bool mimic(BaseRegion *region, float scale = 100.0f, int x = 0, int y = 0); bool getBoundingRect(Rect32 *rect); @@ -58,10 +55,14 @@ public: virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent, const char *nameOverride); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); +private: + float _lastMimicScale; + int _lastMimicX; + int _lastMimicY; }; } // end of namespace Wintermute diff --git a/engines/wintermute/base/base_save_thumb_helper.cpp b/engines/wintermute/base/base_save_thumb_helper.cpp index 186e1234a8..b4205c21c4 100644 --- a/engines/wintermute/base/base_save_thumb_helper.cpp +++ b/engines/wintermute/base/base_save_thumb_helper.cpp @@ -28,6 +28,7 @@ #include "engines/wintermute/base/base_save_thumb_helper.h" #include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/base_game.h" namespace Wintermute { diff --git a/engines/wintermute/base/base_script_holder.cpp b/engines/wintermute/base/base_script_holder.cpp index d3e6078d43..c5d5e82f76 100644 --- a/engines/wintermute/base/base_script_holder.cpp +++ b/engines/wintermute/base/base_script_holder.cpp @@ -219,13 +219,13 @@ bool BaseScriptHolder::scCallMethod(ScScript *script, ScStack *stack, ScStack *t ////////////////////////////////////////////////////////////////////////// -ScValue *BaseScriptHolder::scGetProperty(const char *name) { +ScValue *BaseScriptHolder::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("script_holder"); return _scValue; } @@ -233,7 +233,7 @@ ScValue *BaseScriptHolder::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Name ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Name") == 0) { + else if (name == "Name") { _scValue->setString(getName()); return _scValue; } @@ -241,7 +241,7 @@ ScValue *BaseScriptHolder::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Filename (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Filename") == 0) { + else if (name == "Filename") { _scValue->setString(_filename); return _scValue; } else { diff --git a/engines/wintermute/base/base_script_holder.h b/engines/wintermute/base/base_script_holder.h index 0c3d7a1a70..5fd0dbec9c 100644 --- a/engines/wintermute/base/base_script_holder.h +++ b/engines/wintermute/base/base_script_holder.h @@ -59,7 +59,7 @@ public: BaseArray<ScScript *> _scripts; // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/base/base_scriptable.cpp b/engines/wintermute/base/base_scriptable.cpp index 143934402b..a2dd8b00e7 100644 --- a/engines/wintermute/base/base_scriptable.cpp +++ b/engines/wintermute/base/base_scriptable.cpp @@ -76,12 +76,12 @@ bool BaseScriptable::scCallMethod(ScScript *script, ScStack *stack, ScStack *thi ////////////////////////////////////////////////////////////////////////// -ScValue *BaseScriptable::scGetProperty(const char *name) { +ScValue *BaseScriptable::scGetProperty(const Common::String &name) { if (!_scProp) { _scProp = new ScValue(_gameRef); } if (_scProp) { - return _scProp->getProp(name); + return _scProp->getProp(name.c_str()); // TODO: Change to Common::String } else { return NULL; } diff --git a/engines/wintermute/base/base_scriptable.h b/engines/wintermute/base/base_scriptable.h index b006e6e07c..fbe14fc299 100644 --- a/engines/wintermute/base/base_scriptable.h +++ b/engines/wintermute/base/base_scriptable.h @@ -50,7 +50,7 @@ public: // high level scripting interface virtual bool canHandleMethod(const char *eventMethod); virtual bool scSetProperty(const char *name, ScValue *value); - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); virtual void *scToMemBuffer(); diff --git a/engines/wintermute/base/base_sprite.cpp b/engines/wintermute/base/base_sprite.cpp index e2dd8bbd39..468af1bd75 100644 --- a/engines/wintermute/base/base_sprite.cpp +++ b/engines/wintermute/base/base_sprite.cpp @@ -121,6 +121,13 @@ bool BaseSprite::draw(int x, int y, BaseObject *registerOwner, float zoomX, floa return display(x, y, registerOwner, zoomX, zoomY, alpha); } +bool BaseSprite::isChanged() { + return _changed; +} + +bool BaseSprite::isFinished() { + return _finished; +} ////////////////////////////////////////////////////////////////////// bool BaseSprite::loadFile(const Common::String &filename, int lifeTime, TSpriteCacheType cacheType) { @@ -686,13 +693,13 @@ bool BaseSprite::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSta ////////////////////////////////////////////////////////////////////////// -ScValue *BaseSprite::scGetProperty(const char *name) { +ScValue *BaseSprite::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("sprite"); return _scValue; } @@ -700,7 +707,7 @@ ScValue *BaseSprite::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumFrames (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumFrames") == 0) { + else if (name == "NumFrames") { _scValue->setInt(_frames.size()); return _scValue; } @@ -708,7 +715,7 @@ ScValue *BaseSprite::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // CurrentFrame ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "CurrentFrame") == 0) { + else if (name == "CurrentFrame") { _scValue->setInt(_currentFrame); return _scValue; } @@ -716,7 +723,7 @@ ScValue *BaseSprite::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // PixelPerfect ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "PixelPerfect") == 0) { + else if (name == "PixelPerfect") { _scValue->setBool(_precise); return _scValue; } @@ -724,7 +731,7 @@ ScValue *BaseSprite::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Looping ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Looping") == 0) { + else if (name == "Looping") { _scValue->setBool(_looping); return _scValue; } @@ -732,7 +739,7 @@ ScValue *BaseSprite::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Owner (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Owner") == 0) { + else if (name == "Owner") { if (_owner == NULL) { _scValue->setNULL(); } else { @@ -744,7 +751,7 @@ ScValue *BaseSprite::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Finished (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Finished") == 0) { + else if (name == "Finished") { _scValue->setBool(_finished); return _scValue; } @@ -752,7 +759,7 @@ ScValue *BaseSprite::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Paused (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Paused") == 0) { + else if (name == "Paused") { _scValue->setBool(_paused); return _scValue; } else { @@ -804,9 +811,7 @@ const char *BaseSprite::scToString() { ////////////////////////////////////////////////////////////////////////// bool BaseSprite::killAllSounds() { for (uint32 i = 0; i < _frames.size(); i++) { - if (_frames[i]->_sound) { - _frames[i]->_sound->stop(); - } + _frames[i]->stopSound(); } return STATUS_OK; } diff --git a/engines/wintermute/base/base_sprite.h b/engines/wintermute/base/base_sprite.h index c861ca9930..1d244c3a52 100644 --- a/engines/wintermute/base/base_sprite.h +++ b/engines/wintermute/base/base_sprite.h @@ -39,36 +39,21 @@ class BaseSurface; class BaseObject; class BaseSprite: public BaseScriptHolder { public: - bool killAllSounds(); BaseSurface *getSurface(); - char *_editorBgFile; - int _editorBgOffsetX; - int _editorBgOffsetY; - int _editorBgAlpha; - bool _streamed; - bool _streamedKeepLoaded; void cleanup(); void setDefaults(); - bool _precise; DECLARE_PERSISTENT(BaseSprite, BaseScriptHolder) - bool _editorAllFrames; bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); int _moveY; int _moveX; bool display(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, uint32 alpha = 0xFFFFFFFF, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL); bool getCurrentFrame(float zoomX = 100, float zoomY = 100); - bool _canBreak; - bool _editorMuted; - bool _continuous; void reset(); - BaseObject *_owner; - bool _changed; - bool _paused; - bool _finished; + bool isChanged(); + bool isFinished(); bool loadBuffer(byte *buffer, bool compete = true, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); bool loadFile(const Common::String &filename, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); - uint32 _lastFrameTime; bool draw(int x, int y, BaseObject *Register = NULL, float zoomX = 100, float zoomY = 100, uint32 alpha = 0xFFFFFFFF); bool _looping; int _currentFrame; @@ -79,10 +64,28 @@ public: bool saveAsText(BaseDynamicBuffer *buffer, int indent); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); +private: + BaseObject *_owner; + bool _canBreak; + bool _changed; + bool _editorAllFrames; + char *_editorBgFile; + int _editorBgOffsetX; + int _editorBgOffsetY; + int _editorBgAlpha; + bool _editorMuted; + bool _finished; + bool _continuous; + uint32 _lastFrameTime; + bool _precise; + bool _paused; + bool _streamed; + bool _streamedKeepLoaded; + bool killAllSounds(); }; } // end of namespace Wintermute diff --git a/engines/wintermute/base/base_sub_frame.cpp b/engines/wintermute/base/base_sub_frame.cpp index 6a9246efd4..77cc522ae7 100644 --- a/engines/wintermute/base/base_sub_frame.cpp +++ b/engines/wintermute/base/base_sub_frame.cpp @@ -34,6 +34,7 @@ #include "engines/wintermute/base/base_surface_storage.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script_stack.h" @@ -225,6 +226,9 @@ void BaseSubFrame::setRect(Rect32 rect) { _rect = rect; } +const char* BaseSubFrame::getSurfaceFilename() { + return _surfaceFilename; +} ////////////////////////////////////////////////////////////////////// bool BaseSubFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, bool precise, uint32 alpha, float rotate, TSpriteBlendMode blendMode) { @@ -442,7 +446,7 @@ bool BaseSubFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisS ////////////////////////////////////////////////////////////////////////// -ScValue *BaseSubFrame::scGetProperty(const char *name) { +ScValue *BaseSubFrame::scGetProperty(const Common::String &name) { if (!_scValue) { _scValue = new ScValue(_gameRef); } @@ -451,7 +455,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("subframe"); return _scValue; } @@ -459,7 +463,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AlphaColor ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AlphaColor") == 0) { + else if (name == "AlphaColor") { _scValue->setInt((int)_alpha); return _scValue; @@ -468,7 +472,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TransparentColor (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TransparentColor") == 0) { + else if (name == "TransparentColor") { _scValue->setInt((int)_transparent); return _scValue; } @@ -476,7 +480,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Is2DOnly ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Is2DOnly") == 0) { + else if (name == "Is2DOnly") { _scValue->setBool(_2DOnly); return _scValue; } @@ -484,7 +488,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Is3DOnly ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Is3DOnly") == 0) { + else if (name == "Is3DOnly") { _scValue->setBool(_3DOnly); return _scValue; } @@ -492,7 +496,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MirrorX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MirrorX") == 0) { + else if (name == "MirrorX") { _scValue->setBool(_mirrorX); return _scValue; } @@ -500,7 +504,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MirrorY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MirrorY") == 0) { + else if (name == "MirrorY") { _scValue->setBool(_mirrorY); return _scValue; } @@ -508,7 +512,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Decoration ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Decoration") == 0) { + else if (name == "Decoration") { _scValue->setBool(_decoration); return _scValue; } @@ -516,7 +520,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // HotspotX ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "HotspotX") == 0) { + else if (name == "HotspotX") { _scValue->setInt(_hotspotX); return _scValue; } @@ -524,7 +528,7 @@ ScValue *BaseSubFrame::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // HotspotY ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "HotspotY") == 0) { + else if (name == "HotspotY") { _scValue->setInt(_hotspotY); return _scValue; } else { diff --git a/engines/wintermute/base/base_sub_frame.h b/engines/wintermute/base/base_sub_frame.h index b174c6e5f0..c173ae69d1 100644 --- a/engines/wintermute/base/base_sub_frame.h +++ b/engines/wintermute/base/base_sub_frame.h @@ -54,6 +54,7 @@ public: bool loadBuffer(byte *buffer, int lifeTime, bool keepLoaded); bool draw(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, bool precise = true, uint32 alpha = 0xFFFFFFFF, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL); bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); + const char* getSurfaceFilename(); int _hotspotX; int _hotspotY; @@ -65,6 +66,7 @@ public: private: bool _wantsDefaultRect; Rect32 _rect; + char *_surfaceFilename; public: bool _cKDefault; byte _cKRed; @@ -72,7 +74,6 @@ public: byte _cKBlue; int _lifeTime; bool _keepLoaded; - char *_surfaceFilename; bool _2DOnly; bool _3DOnly; @@ -80,7 +81,7 @@ public: BaseSurface *_surface; // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/base/base_surface_storage.cpp b/engines/wintermute/base/base_surface_storage.cpp index 1dcebb0595..4e795ca813 100644 --- a/engines/wintermute/base/base_surface_storage.cpp +++ b/engines/wintermute/base/base_surface_storage.cpp @@ -28,6 +28,7 @@ #include "engines/wintermute/base/base_surface_storage.h" #include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/platform_osystem.h" diff --git a/engines/wintermute/base/base_transition_manager.cpp b/engines/wintermute/base/base_transition_manager.cpp index 5c28f36d30..7785f3d5af 100644 --- a/engines/wintermute/base/base_transition_manager.cpp +++ b/engines/wintermute/base/base_transition_manager.cpp @@ -28,6 +28,7 @@ #include "engines/wintermute/base/base_transition_manager.h" #include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" namespace Wintermute { diff --git a/engines/wintermute/base/base_viewport.cpp b/engines/wintermute/base/base_viewport.cpp index 3b003e1c49..7ec995449f 100644 --- a/engines/wintermute/base/base_viewport.cpp +++ b/engines/wintermute/base/base_viewport.cpp @@ -29,6 +29,7 @@ #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/platform_osystem.h" #include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/gfx/base_renderer.h" namespace Wintermute { diff --git a/engines/wintermute/base/file/base_disk_file.cpp b/engines/wintermute/base/file/base_disk_file.cpp index 2d3951b026..25be3dad2d 100644 --- a/engines/wintermute/base/file/base_disk_file.cpp +++ b/engines/wintermute/base/file/base_disk_file.cpp @@ -178,10 +178,8 @@ Common::SeekableReadStream *openDiskFile(const Common::String &filename) { } delete[] compBuffer; - - return new Common::MemoryReadStream(data, uncompSize, DisposeAfterUse::YES); delete file; - file = NULL; + return new Common::MemoryReadStream(data, uncompSize, DisposeAfterUse::YES); } else { file->seek(0, SEEK_SET); return file; diff --git a/engines/wintermute/base/file/base_package.cpp b/engines/wintermute/base/file/base_package.cpp index 9780992652..51a1558a7c 100644 --- a/engines/wintermute/base/file/base_package.cpp +++ b/engines/wintermute/base/file/base_package.cpp @@ -86,16 +86,16 @@ void TPackageHeader::readFromStream(Common::ReadStream *stream) { _magic1 = stream->readUint32LE(); _magic2 = stream->readUint32LE(); _packageVersion = stream->readUint32LE(); - + _gameVersion = stream->readUint32LE(); - + _priority = stream->readByte(); _cd = stream->readByte(); _masterIndex = stream->readByte(); stream->readByte(); // To align the next byte... - + _creationTime = stream->readUint32LE(); - + stream->read(_desc, 100); _numDirs = stream->readUint32LE(); } diff --git a/engines/wintermute/base/file/base_save_thumb_file.cpp b/engines/wintermute/base/file/base_save_thumb_file.cpp index 5bdab0853e..94d3e5a94e 100644 --- a/engines/wintermute/base/file/base_save_thumb_file.cpp +++ b/engines/wintermute/base/file/base_save_thumb_file.cpp @@ -28,7 +28,6 @@ #include "engines/wintermute/base/base_persistence_manager.h" #include "engines/wintermute/base/file/base_save_thumb_file.h" -#include "engines/wintermute/platform_osystem.h" namespace Wintermute { diff --git a/engines/wintermute/base/font/base_font_bitmap.cpp b/engines/wintermute/base/font/base_font_bitmap.cpp index fced08c7e2..55f46c476b 100644 --- a/engines/wintermute/base/font/base_font_bitmap.cpp +++ b/engines/wintermute/base/font/base_font_bitmap.cpp @@ -31,6 +31,7 @@ #include "engines/wintermute/base/base_parser.h" #include "engines/wintermute/base/base_frame.h" #include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_sub_frame.h" #include "engines/wintermute/base/base_frame.h" diff --git a/engines/wintermute/base/font/base_font_storage.cpp b/engines/wintermute/base/font/base_font_storage.cpp index d26fa1d593..8128ffe897 100644 --- a/engines/wintermute/base/font/base_font_storage.cpp +++ b/engines/wintermute/base/font/base_font_storage.cpp @@ -29,7 +29,6 @@ #include "engines/wintermute/base/font/base_font_storage.h" #include "engines/wintermute/base/font/base_font.h" #include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/platform_osystem.h" #include "common/str.h" namespace Wintermute { diff --git a/engines/wintermute/base/font/base_font_truetype.cpp b/engines/wintermute/base/font/base_font_truetype.cpp index 599010bbd5..f236329fcd 100644 --- a/engines/wintermute/base/font/base_font_truetype.cpp +++ b/engines/wintermute/base/font/base_font_truetype.cpp @@ -26,11 +26,8 @@ * Copyright (c) 2011 Jan Nedoma */ -#include "engines/wintermute/base/file/base_file.h" #include "engines/wintermute/base/font/base_font_truetype.h" -#include "engines/wintermute/utils/path_util.h" #include "engines/wintermute/utils/string_util.h" -#include "engines/wintermute/math/math_util.h" #include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/gfx/base_surface.h" #include "engines/wintermute/base/base_parser.h" @@ -162,6 +159,11 @@ void BaseFontTT::drawText(const byte *text, int x, int y, int width, TTextAlign // TODO: Why do we still insist on Widestrings everywhere? /* if (_gameRef->_textEncoding == TEXT_UTF8) text = StringUtil::Utf8ToWide((char *)Text); else text = StringUtil::AnsiToWide((char *)Text);*/ + // HACK: J.U.L.I.A. uses CP1252, we need to fix that, + // And we still don't have any UTF8-support. + if (_gameRef->_textEncoding != TEXT_UTF8) { + textStr = StringUtil::ansiToWide((char *)text); + } if (maxLength >= 0 && textStr.size() > (uint32)maxLength) { textStr = Common::String(textStr.c_str(), (uint32)maxLength); diff --git a/engines/wintermute/base/gfx/base_renderer.cpp b/engines/wintermute/base/gfx/base_renderer.cpp index 9205438a5b..e7ffc14c25 100644 --- a/engines/wintermute/base/gfx/base_renderer.cpp +++ b/engines/wintermute/base/gfx/base_renderer.cpp @@ -129,13 +129,13 @@ void BaseRenderer::initSaveLoad(bool isSaving, bool quickSave) { _indicatorDisplay = true; _indicatorProgress = 0; _hasDrawnSaveLoadImage = false; - + if (isSaving && !quickSave) { delete _saveLoadImage; _saveLoadImage = NULL; if (_saveImageName.size()) { _saveLoadImage = createSurface(); - + if (!_saveLoadImage || DID_FAIL(_saveLoadImage->create(_saveImageName, true, 0, 0, 0))) { delete _saveLoadImage; _saveLoadImage = NULL; @@ -146,7 +146,7 @@ void BaseRenderer::initSaveLoad(bool isSaving, bool quickSave) { _saveLoadImage = NULL; if (_loadImageName.size()) { _saveLoadImage = createSurface(); - + if (!_saveLoadImage || DID_FAIL(_saveLoadImage->create(_loadImageName, true, 0, 0, 0))) { delete _saveLoadImage; _saveLoadImage = NULL; @@ -360,7 +360,7 @@ bool BaseRenderer::displayIndicator() { flip(); _hasDrawnSaveLoadImage = true; } - + if ((!_indicatorDisplay && _indicatorWidth <= 0) || _indicatorHeight <= 0) { return STATUS_OK; } @@ -369,7 +369,7 @@ bool BaseRenderer::displayIndicator() { for (int i = 0; i < _indicatorHeight; i++) { drawLine(_indicatorX, _indicatorY + i, _indicatorX + curWidth, _indicatorY + i, _indicatorColor); } - + setup2D(); _indicatorWidthDrawn = curWidth; if (_indicatorWidthDrawn) { diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp index 6d67253038..7970a25300 100644 --- a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp +++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp @@ -261,7 +261,7 @@ void BaseRenderOSystem::fade(uint16 alpha) { void BaseRenderOSystem::fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect) { // This particular warning is rather messy, as this function is called a ton, // thus we avoid printing it more than once. - + // TODO: Add fading with dirty rects. if (!_disableDirtyRects) { warning("BaseRenderOSystem::FadeToColor - Breaks when using dirty rects"); @@ -573,6 +573,11 @@ Rect32 BaseRenderOSystem::getViewPort() { ////////////////////////////////////////////////////////////////////////// void BaseRenderOSystem::modTargetRect(Common::Rect *rect) { + // FIXME: This is wrong in quite a few ways right now, and ends up + // breaking the notebook in Dirty Split, so we disable the correction + // for now, this will need fixing when a game with odd aspect-ratios + // show up. + return; rect->left = (int16)MathUtil::round(rect->left * _ratioX + _borderLeft - _renderRect.left); rect->top = (int16)MathUtil::round(rect->top * _ratioY + _borderTop - _renderRect.top); rect->setWidth((int16)MathUtil::roundUp(rect->width() * _ratioX)); diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp index d5464782a3..bee876bb65 100644 --- a/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp +++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp @@ -26,7 +26,6 @@ * Copyright (c) 2011 Jan Nedoma */ -#include "engines/wintermute/base/file/base_file.h" #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" diff --git a/engines/wintermute/base/particles/part_emitter.cpp b/engines/wintermute/base/particles/part_emitter.cpp index 3655b89131..bab4d4609e 100644 --- a/engines/wintermute/base/particles/part_emitter.cpp +++ b/engines/wintermute/base/particles/part_emitter.cpp @@ -35,6 +35,7 @@ #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_region.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/utils/utils.h" #include "engines/wintermute/platform_osystem.h" #include "common/str.h" @@ -603,41 +604,41 @@ bool PartEmitter::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSt } ////////////////////////////////////////////////////////////////////////// -ScValue *PartEmitter::scGetProperty(const char *name) { +ScValue *PartEmitter::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("particle-emitter"); return _scValue; } ////////////////////////////////////////////////////////////////////////// // X ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "X") == 0) { + else if (name == "X") { _scValue->setInt(_posX); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Y ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Y") == 0) { + else if (name == "Y") { _scValue->setInt(_posY); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Width ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Width") == 0) { + else if (name == "Width") { _scValue->setInt(_width); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Height ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Height") == 0) { + else if (name == "Height") { _scValue->setInt(_height); return _scValue; } @@ -645,21 +646,21 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Scale1 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Scale1") == 0) { + else if (name == "Scale1") { _scValue->setFloat(_scale1); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Scale2 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Scale2") == 0) { + else if (name == "Scale2") { _scValue->setFloat(_scale2); return _scValue; } ////////////////////////////////////////////////////////////////////////// // ScaleZBased ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ScaleZBased") == 0) { + else if (name == "ScaleZBased") { _scValue->setBool(_scaleZBased); return _scValue; } @@ -667,21 +668,21 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Velocity1 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Velocity1") == 0) { + else if (name == "Velocity1") { _scValue->setFloat(_velocity1); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Velocity2 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Velocity2") == 0) { + else if (name == "Velocity2") { _scValue->setFloat(_velocity2); return _scValue; } ////////////////////////////////////////////////////////////////////////// // VelocityZBased ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "VelocityZBased") == 0) { + else if (name == "VelocityZBased") { _scValue->setBool(_velocityZBased); return _scValue; } @@ -689,21 +690,21 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // LifeTime1 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "LifeTime1") == 0) { + else if (name == "LifeTime1") { _scValue->setInt(_lifeTime1); return _scValue; } ////////////////////////////////////////////////////////////////////////// // LifeTime2 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "LifeTime2") == 0) { + else if (name == "LifeTime2") { _scValue->setInt(_lifeTime2); return _scValue; } ////////////////////////////////////////////////////////////////////////// // LifeTimeZBased ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "LifeTimeZBased") == 0) { + else if (name == "LifeTimeZBased") { _scValue->setBool(_lifeTimeZBased); return _scValue; } @@ -711,14 +712,14 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Angle1 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Angle1") == 0) { + else if (name == "Angle1") { _scValue->setInt(_angle1); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Angle2 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Angle2") == 0) { + else if (name == "Angle2") { _scValue->setInt(_angle2); return _scValue; } @@ -726,14 +727,14 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AngVelocity1 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AngVelocity1") == 0) { + else if (name == "AngVelocity1") { _scValue->setFloat(_angVelocity1); return _scValue; } ////////////////////////////////////////////////////////////////////////// // AngVelocity2 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AngVelocity2") == 0) { + else if (name == "AngVelocity2") { _scValue->setFloat(_angVelocity2); return _scValue; } @@ -741,14 +742,14 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Rotation1 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Rotation1") == 0) { + else if (name == "Rotation1") { _scValue->setFloat(_rotation1); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Rotation2 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Rotation2") == 0) { + else if (name == "Rotation2") { _scValue->setFloat(_rotation2); return _scValue; } @@ -756,21 +757,21 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Alpha1 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Alpha1") == 0) { + else if (name == "Alpha1") { _scValue->setInt(_alpha1); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Alpha2 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Alpha2") == 0) { + else if (name == "Alpha2") { _scValue->setInt(_alpha2); return _scValue; } ////////////////////////////////////////////////////////////////////////// // AlphaTimeBased ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AlphaTimeBased") == 0) { + else if (name == "AlphaTimeBased") { _scValue->setBool(_alphaTimeBased); return _scValue; } @@ -778,14 +779,14 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MaxParticles ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MaxParticles") == 0) { + else if (name == "MaxParticles") { _scValue->setInt(_maxParticles); return _scValue; } ////////////////////////////////////////////////////////////////////////// // NumLiveParticles (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumLiveParticles") == 0) { + else if (name == "NumLiveParticles") { int numAlive = 0; for (uint32 i = 0; i < _particles.size(); i++) { if (_particles[i] && !_particles[i]->_isDead) { @@ -799,21 +800,21 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // GenerationInterval ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "GenerationInterval") == 0) { + else if (name == "GenerationInterval") { _scValue->setInt(_genInterval); return _scValue; } ////////////////////////////////////////////////////////////////////////// // GenerationAmount ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "GenerationAmount") == 0) { + else if (name == "GenerationAmount") { _scValue->setInt(_genAmount); return _scValue; } ////////////////////////////////////////////////////////////////////////// // MaxBatches ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MaxBatches") == 0) { + else if (name == "MaxBatches") { _scValue->setInt(_maxBatches); return _scValue; } @@ -821,14 +822,14 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // FadeInTime ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "FadeInTime") == 0) { + else if (name == "FadeInTime") { _scValue->setInt(_fadeInTime); return _scValue; } ////////////////////////////////////////////////////////////////////////// // FadeOutTime ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "FadeOutTime") == 0) { + else if (name == "FadeOutTime") { _scValue->setInt(_fadeOutTime); return _scValue; } @@ -836,21 +837,21 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // GrowthRate1 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "GrowthRate1") == 0) { + else if (name == "GrowthRate1") { _scValue->setFloat(_growthRate1); return _scValue; } ////////////////////////////////////////////////////////////////////////// // GrowthRate2 ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "GrowthRate2") == 0) { + else if (name == "GrowthRate2") { _scValue->setFloat(_growthRate2); return _scValue; } ////////////////////////////////////////////////////////////////////////// // ExponentialGrowth ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ExponentialGrowth") == 0) { + else if (name == "ExponentialGrowth") { _scValue->setBool(_exponentialGrowth); return _scValue; } @@ -858,7 +859,7 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // UseRegion ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "UseRegion") == 0) { + else if (name == "UseRegion") { _scValue->setBool(_useRegion); return _scValue; } @@ -866,7 +867,7 @@ ScValue *PartEmitter::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // EmitEvent ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "EmitEvent") == 0) { + else if (name == "EmitEvent") { if (!_emitEvent) { _scValue->setNULL(); } else { diff --git a/engines/wintermute/base/particles/part_emitter.h b/engines/wintermute/base/particles/part_emitter.h index 9a35cd9bbc..f2c8f139f1 100644 --- a/engines/wintermute/base/particles/part_emitter.h +++ b/engines/wintermute/base/particles/part_emitter.h @@ -63,7 +63,7 @@ public: BaseArray<PartForce *> _forces; // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/base/saveload.cpp b/engines/wintermute/base/saveload.cpp index 06e9fd2565..12204e1b35 100644 --- a/engines/wintermute/base/saveload.cpp +++ b/engines/wintermute/base/saveload.cpp @@ -8,12 +8,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -35,6 +35,7 @@ #include "engines/wintermute/base/base_region.h" #include "engines/wintermute/base/base_sub_frame.h" #include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/sound/base_sound.h" #include "engines/wintermute/base/scriptables/script.h" #include "common/savefile.h" @@ -44,11 +45,11 @@ namespace Wintermute { bool SaveLoad::loadGame(const Common::String &filename, BaseGame *gameRef) { gameRef->LOG(0, "Loading game '%s'...", filename.c_str()); - + bool ret; - + gameRef->_renderer->initSaveLoad(false); - + gameRef->_loadInProgress = true; BasePersistenceManager *pm = new BasePersistenceManager(); if (DID_SUCCEED(ret = pm->initLoad(filename))) { @@ -60,20 +61,20 @@ bool SaveLoad::loadGame(const Common::String &filename, BaseGame *gameRef) { // data initialization after load SaveLoad::initAfterLoad(); - + gameRef->applyEvent("AfterLoad", true); - + gameRef->displayContent(true, false); //_renderer->flip(); } } } - + delete pm; gameRef->_loadInProgress = false; - + gameRef->_renderer->endSaveLoad(); - + //_gameRef->LOG(0, "Load end %d", BaseUtils::GetUsedMemMB()); // AdGame: if (DID_SUCCEED(ret)) { @@ -84,13 +85,13 @@ bool SaveLoad::loadGame(const Common::String &filename, BaseGame *gameRef) { bool SaveLoad::saveGame(int slot, const char *desc, bool quickSave, BaseGame *gameRef) { Common::String filename = SaveLoad::getSaveSlotFilename(slot); - + gameRef->LOG(0, "Saving game '%s'...", filename.c_str()); - + gameRef->applyEvent("BeforeSave", true); - + bool ret; - + BasePersistenceManager *pm = new BasePersistenceManager(); if (DID_SUCCEED(ret = pm->initSave(desc))) { gameRef->_renderer->initSaveLoad(true, quickSave); // TODO: The original code inited the indicator before the conditionals @@ -103,11 +104,11 @@ bool SaveLoad::saveGame(int slot, const char *desc, bool quickSave, BaseGame *ga } } } - + delete pm; - + gameRef->_renderer->endSaveLoad(); - + return ret; } @@ -165,21 +166,21 @@ Common::String SaveLoad::getSaveSlotFilename(int slot) { bool SaveLoad::getSaveSlotDescription(int slot, char *buffer) { buffer[0] = '\0'; - + Common::String filename = getSaveSlotFilename(slot); BasePersistenceManager *pm = new BasePersistenceManager(); if (!pm) { return false; } - + if (!(pm->initLoad(filename))) { delete pm; return false; } - + strcpy(buffer, pm->_savedDescription); delete pm; - + return true; } @@ -198,6 +199,6 @@ bool SaveLoad::emptySaveSlot(int slot) { delete pm; return true; } - - + + } // end of namespace Wintermute diff --git a/engines/wintermute/base/saveload.h b/engines/wintermute/base/saveload.h index e448cc8814..722f7a89b6 100644 --- a/engines/wintermute/base/saveload.h +++ b/engines/wintermute/base/saveload.h @@ -8,12 +8,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -25,7 +25,7 @@ * http://dead-code.org/redir.php?target=wmelite * Copyright (c) 2011 Jan Nedoma */ - + #ifndef WINTERMUTE_SAVEGAME_H #define WINTERMUTE_SAVEGAME_H @@ -39,7 +39,7 @@ public: static bool isSaveSlotUsed(int slot); static bool getSaveSlotDescription(int slot, char *buffer); static Common::String getSaveSlotFilename(int slot); - + static bool loadGame(const Common::String &filename, BaseGame *gameRef); static bool saveGame(int slot, const char *desc, bool quickSave, BaseGame *gameRef); static bool initAfterLoad(); diff --git a/engines/wintermute/base/scriptables/script_engine.cpp b/engines/wintermute/base/scriptables/script_engine.cpp index 20e2ccadd1..3d1863946e 100644 --- a/engines/wintermute/base/scriptables/script_engine.cpp +++ b/engines/wintermute/base/scriptables/script_engine.cpp @@ -33,7 +33,6 @@ #include "engines/wintermute/base/scriptables/script_ext_math.h" #include "engines/wintermute/base/base_engine.h" #include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/base/sound/base_sound.h" #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/utils/utils.h" diff --git a/engines/wintermute/base/scriptables/script_ext_array.cpp b/engines/wintermute/base/scriptables/script_ext_array.cpp index 5ed07f0da6..613cbd0758 100644 --- a/engines/wintermute/base/scriptables/script_ext_array.cpp +++ b/engines/wintermute/base/scriptables/script_ext_array.cpp @@ -140,13 +140,13 @@ bool SXArray::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *SXArray::scGetProperty(const char *name) { +ScValue *SXArray::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("array"); return _scValue; } @@ -154,7 +154,7 @@ ScValue *SXArray::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Length ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Length") == 0) { + else if (name == "Length") { _scValue->setInt(_length); return _scValue; } @@ -164,7 +164,7 @@ ScValue *SXArray::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// else { char paramName[20]; - if (validNumber(name, paramName)) { + if (validNumber(name.c_str(), paramName)) { // TODO: Change to Common::String return _values->getProp(paramName); } else { return _scValue; diff --git a/engines/wintermute/base/scriptables/script_ext_array.h b/engines/wintermute/base/scriptables/script_ext_array.h index d9805ef94f..284c547a27 100644 --- a/engines/wintermute/base/scriptables/script_ext_array.h +++ b/engines/wintermute/base/scriptables/script_ext_array.h @@ -41,7 +41,7 @@ public: SXArray(BaseGame *inGame, ScStack *stack); SXArray(BaseGame *inGame); virtual ~SXArray(); - ScValue *scGetProperty(const char *name); + ScValue *scGetProperty(const Common::String &name); bool scSetProperty(const char *name, ScValue *value); bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); const char *scToString(); diff --git a/engines/wintermute/base/scriptables/script_ext_date.cpp b/engines/wintermute/base/scriptables/script_ext_date.cpp index 11eead3b9c..5aa069d0b2 100644 --- a/engines/wintermute/base/scriptables/script_ext_date.cpp +++ b/engines/wintermute/base/scriptables/script_ext_date.cpp @@ -203,13 +203,13 @@ bool SXDate::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *SXDate::scGetProperty(const char *name) { +ScValue *SXDate::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("date"); return _scValue; } else { @@ -224,7 +224,7 @@ bool SXDate::scSetProperty(const char *name, ScValue *value) { ////////////////////////////////////////////////////////////////////////// // Name ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Name")==0){ + if (name == "Name")==0){ setName(value->getString()); return STATUS_OK; } diff --git a/engines/wintermute/base/scriptables/script_ext_date.h b/engines/wintermute/base/scriptables/script_ext_date.h index f6f04dd7e6..062b7c55c7 100644 --- a/engines/wintermute/base/scriptables/script_ext_date.h +++ b/engines/wintermute/base/scriptables/script_ext_date.h @@ -40,7 +40,7 @@ public: DECLARE_PERSISTENT(SXDate, BaseScriptable) SXDate(BaseGame *inGame, ScStack *Stack); virtual ~SXDate(); - ScValue *scGetProperty(const char *name); + ScValue *scGetProperty(const Common::String &name); bool scSetProperty(const char *name, ScValue *value); bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); const char *scToString(); diff --git a/engines/wintermute/base/scriptables/script_ext_file.cpp b/engines/wintermute/base/scriptables/script_ext_file.cpp index ab574d464b..a1d39c5d0a 100644 --- a/engines/wintermute/base/scriptables/script_ext_file.cpp +++ b/engines/wintermute/base/scriptables/script_ext_file.cpp @@ -33,7 +33,6 @@ #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/utils/utils.h" #include "engines/wintermute/base/base_game.h" -#include "engines/wintermute/base/file/base_file.h" #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/platform_osystem.h" #include "engines/wintermute/base/scriptables/script_ext_file.h" @@ -641,13 +640,13 @@ bool SXFile::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *SXFile::scGetProperty(const char *name) { +ScValue *SXFile::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("file"); return _scValue; } @@ -655,7 +654,7 @@ ScValue *SXFile::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Filename (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Filename") == 0) { + if (name == "Filename") { _scValue->setString(_filename); return _scValue; } @@ -663,7 +662,7 @@ ScValue *SXFile::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Position (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Position") == 0) { + else if (name == "Position") { _scValue->setInt(getPos()); return _scValue; } @@ -671,7 +670,7 @@ ScValue *SXFile::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Length (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Length") == 0) { + else if (name == "Length") { _scValue->setInt(getLength()); return _scValue; } @@ -679,7 +678,7 @@ ScValue *SXFile::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TextMode (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TextMode") == 0) { + else if (name == "TextMode") { _scValue->setBool(_textMode); return _scValue; } @@ -687,7 +686,7 @@ ScValue *SXFile::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // AccessMode (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "AccessMode") == 0) { + else if (name == "AccessMode") { _scValue->setInt(_mode); return _scValue; } else { diff --git a/engines/wintermute/base/scriptables/script_ext_file.h b/engines/wintermute/base/scriptables/script_ext_file.h index b91a53e695..f7c72fcfb3 100644 --- a/engines/wintermute/base/scriptables/script_ext_file.h +++ b/engines/wintermute/base/scriptables/script_ext_file.h @@ -40,7 +40,7 @@ class BaseFile; class SXFile : public BaseScriptable { public: DECLARE_PERSISTENT(SXFile, BaseScriptable) - ScValue *scGetProperty(const char *name); + ScValue *scGetProperty(const Common::String &name); bool scSetProperty(const char *name, ScValue *value); bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); const char *scToString(); diff --git a/engines/wintermute/base/scriptables/script_ext_math.cpp b/engines/wintermute/base/scriptables/script_ext_math.cpp index 598b80cff3..d816fbec65 100644 --- a/engines/wintermute/base/scriptables/script_ext_math.cpp +++ b/engines/wintermute/base/scriptables/script_ext_math.cpp @@ -250,13 +250,13 @@ bool SXMath::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *SXMath::scGetProperty(const char *name) { +ScValue *SXMath::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("math"); return _scValue; } @@ -264,7 +264,7 @@ ScValue *SXMath::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // PI ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "PI") == 0) { + else if (name == "PI") { _scValue->setFloat(M_PI); return _scValue; } else { diff --git a/engines/wintermute/base/scriptables/script_ext_math.h b/engines/wintermute/base/scriptables/script_ext_math.h index f86d59fe7b..48c43ea7e8 100644 --- a/engines/wintermute/base/scriptables/script_ext_math.h +++ b/engines/wintermute/base/scriptables/script_ext_math.h @@ -39,7 +39,7 @@ public: DECLARE_PERSISTENT(SXMath, BaseScriptable) SXMath(BaseGame *inGame); virtual ~SXMath(); - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); private: diff --git a/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp b/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp index 5ed9bd5313..8f05b7bff6 100644 --- a/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp +++ b/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp @@ -447,13 +447,13 @@ bool SXMemBuffer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisSt ////////////////////////////////////////////////////////////////////////// -ScValue *SXMemBuffer::scGetProperty(const char *name) { +ScValue *SXMemBuffer::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("membuffer"); return _scValue; } @@ -461,7 +461,7 @@ ScValue *SXMemBuffer::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Size (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Size") == 0) { + if (name == "Size") { _scValue->setInt(_size); return _scValue; } else { diff --git a/engines/wintermute/base/scriptables/script_ext_mem_buffer.h b/engines/wintermute/base/scriptables/script_ext_mem_buffer.h index d2662b3036..1527a323dc 100644 --- a/engines/wintermute/base/scriptables/script_ext_mem_buffer.h +++ b/engines/wintermute/base/scriptables/script_ext_mem_buffer.h @@ -38,7 +38,7 @@ class SXMemBuffer : public BaseScriptable { public: virtual int scCompare(BaseScriptable *Val); DECLARE_PERSISTENT(SXMemBuffer, BaseScriptable) - ScValue *scGetProperty(const char *name); + ScValue *scGetProperty(const Common::String &name); bool scSetProperty(const char *name, ScValue *value); bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); const char *scToString(); diff --git a/engines/wintermute/base/scriptables/script_ext_string.cpp b/engines/wintermute/base/scriptables/script_ext_string.cpp index 8d87a92dc1..5f7da1c2dd 100644 --- a/engines/wintermute/base/scriptables/script_ext_string.cpp +++ b/engines/wintermute/base/scriptables/script_ext_string.cpp @@ -343,20 +343,20 @@ bool SXString::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *SXString::scGetProperty(const char *name) { +ScValue *SXString::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("string"); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Length (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Length") == 0) { + else if (name == "Length") { if (_gameRef->_textEncoding == TEXT_UTF8) { WideString wstr = StringUtil::utf8ToWide(_string); _scValue->setInt(wstr.size()); @@ -369,7 +369,7 @@ ScValue *SXString::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Capacity ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Capacity") == 0) { + else if (name == "Capacity") { _scValue->setInt(_capacity); return _scValue; } else { diff --git a/engines/wintermute/base/scriptables/script_ext_string.h b/engines/wintermute/base/scriptables/script_ext_string.h index 255b9c57eb..00bffab3a9 100644 --- a/engines/wintermute/base/scriptables/script_ext_string.h +++ b/engines/wintermute/base/scriptables/script_ext_string.h @@ -38,7 +38,7 @@ class SXString : public BaseScriptable { public: virtual int scCompare(BaseScriptable *Val); DECLARE_PERSISTENT(SXString, BaseScriptable) - ScValue *scGetProperty(const char *name); + ScValue *scGetProperty(const Common::String &name); bool scSetProperty(const char *name, ScValue *value); bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); void scSetString(const char *val); diff --git a/engines/wintermute/base/sound/base_sound_buffer.cpp b/engines/wintermute/base/sound/base_sound_buffer.cpp index 3fd6c4d5f2..250570f2b8 100644 --- a/engines/wintermute/base/sound/base_sound_buffer.cpp +++ b/engines/wintermute/base/sound/base_sound_buffer.cpp @@ -26,12 +26,10 @@ * Copyright (c) 2011 Jan Nedoma */ -#include "engines/wintermute/base/file/base_file.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/sound/base_sound_manager.h" #include "engines/wintermute/base/sound/base_sound_buffer.h" #include "engines/wintermute/base/base_file_manager.h" -#include "engines/wintermute/utils/utils.h" #include "engines/wintermute/wintermute.h" #include "audio/audiostream.h" #include "audio/mixer.h" diff --git a/engines/wintermute/base/sound/base_sound_manager.cpp b/engines/wintermute/base/sound/base_sound_manager.cpp index f7788cd255..441793144d 100644 --- a/engines/wintermute/base/sound/base_sound_manager.cpp +++ b/engines/wintermute/base/sound/base_sound_manager.cpp @@ -32,6 +32,7 @@ #include "engines/wintermute/utils/string_util.h" #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/sound/base_sound_buffer.h" #include "engines/wintermute/wintermute.h" #include "common/config-manager.h" diff --git a/engines/wintermute/graphics/transparent_surface.cpp b/engines/wintermute/graphics/transparent_surface.cpp index da01259b1b..9319899495 100644 --- a/engines/wintermute/graphics/transparent_surface.cpp +++ b/engines/wintermute/graphics/transparent_surface.cpp @@ -52,13 +52,13 @@ TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Sur void doBlitOpaque(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) { byte *in, *out; - + #ifdef SCUMM_LITTLE_ENDIAN const int aIndex = 3; #else const int aIndex = 0; #endif - + for (uint32 i = 0; i < height; i++) { out = outo; in = ino; @@ -121,7 +121,7 @@ void TransparentSurface::doBlitAlpha(byte *ino, byte* outo, uint32 width, uint32 int a = (pix >> aShift) & 0xff; int outb, outg, outr, outa; in += inStep; - + switch (a) { case 0: // Full transparency out += 4; diff --git a/engines/wintermute/persistent.h b/engines/wintermute/persistent.h index 5bcf30787d..c862df5d6b 100644 --- a/engines/wintermute/persistent.h +++ b/engines/wintermute/persistent.h @@ -52,7 +52,7 @@ namespace Wintermute { virtual bool persist(BasePersistenceManager* PersistMgr);\ void* operator new (size_t size);\ void operator delete(void* p);\ - + #define IMPLEMENT_PERSISTENT(class_name, persistent_class)\ const char class_name::_className[] = #class_name;\ @@ -80,7 +80,7 @@ namespace Wintermute { SystemClassRegistry::getInstance()->unregisterInstance(#class_name, p);\ ::operator delete(p);\ }\ - + #define TMEMBER(member_name) #member_name, &member_name #define TMEMBER_INT(member_name) #member_name, (int*)&member_name diff --git a/engines/wintermute/readme.txt b/engines/wintermute/readme.txt deleted file mode 100644 index 57592dac2a..0000000000 --- a/engines/wintermute/readme.txt +++ /dev/null @@ -1,155 +0,0 @@ -Wintermute Engine - Copyright (c) 2011 Jan Nedoma -ScummVM port by Einar Johan Trøan Sømåen (somaen) - -*************************** -*** General information *** -*************************** -The Wintermute Engine is a game engine mainly aimed at creating adventure -games, it supports both 3D, 2.5D, 2D and First-Person games, and has numerous -games both free, open-source and commercial created with it. This port was -created by somaen as part of Google Summer of Code 2012. - -**************** -*** Features *** -**************** -This port of the Wintermute Engine (WME) is based on WME Lite, which lacks the following functionality -originally found in WME: (from http://res.dead-code.org/doku.php/wmelite:start) - -The following features of WME 1.x are NOT supported by WME Lite: -* 3D characters. WME Lite only supports 2D games. -* Sprite frame mirroring. -* Sprite rotations. -* Sprite blending modes. -* Video playback. -* Plugins. -* Calling external functions from DLL libraries from scripts. -* Game Explorer support. -* 'Directory' script object. - -This port does reimplement a few of these features, currently: -* Sprite frame mirroring - WORKS. -* Video playback - Theora PARTIALLY WORKING. (Slow, and doesn't support seeking) - -In addition, this port removes a few additional features that were never/rarely used: -* 'File' script object - ScummVM doesn't have any easy way to write/read arbitrary files. -* Debugger/Compiler - weren't properly accessible in WME Lite anyhow. -* CD-numbering support in .dcp-files - was never used. -* 'SaveDirectory'-property of 'Game' will not return anything usefull to the game-scripts (saving is handled through SaveFileMan) - -******************************* -*** Additional limitations: *** -******************************* -* Only .OGG and RAW-.WAV sounds are supported at this point -* TTF-fonts might behave a bit differently, owing to both the change to FreeType in WME Lite - and the change in dpi in this port of WME. -* The window-caption-setting in-game will be ignored, for the sake of concistency with ScummVM. -* Most VKey-combinations might still be missing (as they already were in WME Lite) -* Since we don't use FreeImage, some games might use odd files that weren't expected when the - image-decoders in ScummVM were written. One example here is interlaced-PNGs. -* UTF8-support is not ported, which means only games with western charsets will work for now. -* Games that select language by moving .dcp-files around still need a bit more handling on detection/load - adding support for those languages on a language-by-language basis. -* Most games assume the availability of the Windows-fonts (particularly arial.ttf) - at this point no fallback has been put in place for using FreeFonts as replacements, - simply for lack of having them easily accessible to the engines at this point. So, at least - arial.ttf should be put in either the game-folder or made available through the extras-folder - for now, otherwise kGUIBigFont will be used as a replacement. - - -********************************* -*** Advanced engine-features: *** -********************************* -At this point the engine implements the following "advanced engine features": -* RTL ("Return to Launcher") support -* Global options dialog support -* Listing savestates via command line or Launcher -* Loading savestates via command line or Launcher -* Deleting savestates via the Launcher and GMM -* Savestate metadata support -* Loading/Saving during run time - -and NOT the following: -* Enhanced debug/error messages - -***************** -*** Detection *** -***************** -Since Wintermute has authoring tools available, there will at any point in -time be atleast a few games that are works-in-progress, and as the authors -of these games might want to test their games in ScummVM, the engine has -to be able to detect arbitrary Wintermute-games, to this end the detector -code in this engine will check any folder containing "data.dcp", and try to -read "startup.settings" and "default.game" (or optionally any other .game-file -defined in startup.settings), the Name/Caption fields in the .game-file will -be used as gameid/title (prefixing the gameid with "wmefan-" to avoid confusion -with any other WME game that might happen to have taken that id. - -All COMPLETED games should have their md5s and gameid's properly added, IFF -they don't require 3D. - -3D games may also be added, for the purpose of giving the user feedback -as to why their game won't run, but at this point, any such MD5 should -be added as a comment only, to avoid confusion, as no mechanism for giving -the user feedback about 3D-games not being supported is currently added. - -************************************* -*** Games targeted by the engine: *** -************************************* -This engine potentially targets a very large amount of games: -http://res.dead-code.org/doku.php/games:start - -Since the feature-set of WME Lite differs from that of the full Wintermute Engine, -games will need to be targeted on a case-by-case, feature-by-feature basis, this is -a list of the games that are currently known to work (although perhaps with minor -issues) through to completion: - -* Dirty Split (dirtysplit) -* the white chamber (twc) -* Chivalry is NOT dead (chivalry) -* Rosemary (rosemary) -* The Box (thebox) -* J.U.L.I.A. (Demo) (julia) -* Pigeons in the park (pigeons) - -Untested, but starts: -* East Side Story (Demo) (eastside) -* Actual Destination (actualdest) -* Ghost in the sheet (ghostsheet) - -******************************** -*** Games with known issues: *** -******************************** -Certain games will work mostly fine with this engine, but can still -be impossible to complete for various reasons, this is a list of games -that technically qualify (as in they do not require the 3D-parts of the engine) -but have issues that make them problematic or not completable: - -Won't start: -* Five Lethal Demons (5ld) - Requires support for interlaced PNGs -* Five Magical Amulets (5ma) - Requires support for interlaced PNGs -* Kulivoeko - Requires support for interlaced PNGs -* Reversion (reversion) - Requires support for Non-V1.1 JPEGs and interlaced PNGs -* Mirage (mirage) - Tries to seek in a vorbis-stream inside a ZipStream -* Hamlet or the last game without MMORPS features, shaders and product placement (hamlet) - - Requires support for interlaced PNGs - -Gameplay broken: -* J.U.L.I.A. (Full game) (julia) - Requires sprite-rotation for a puzzle. - -Non-critical: -* Ghost in the sheet (ghostsheet) - uses Non-V1.1-JPEGs -* East Side Story (eastside) - wants "framd.ttf" - -***************************** -*** General known issues: *** -***************************** - -Mostly a TODO-section, to not forget fixing outstanding general issues: -* Save/Load-screens are not shown during save/load - this is probably a result of reducing the amount of redrawing done - during save/load, and I'm not sure it should be put back, if that means - making saves slower again. -* Font-sizes are wrong enough to allow Dirty Split to draw text that is hidden in - the original game (most visible on the coin-interface) -* Alpha-masks for Theora-videos are broken on big-endian platforms - diff --git a/engines/wintermute/system/sys_class_registry.cpp b/engines/wintermute/system/sys_class_registry.cpp index 5e3b968c5c..7c1911c2bf 100644 --- a/engines/wintermute/system/sys_class_registry.cpp +++ b/engines/wintermute/system/sys_class_registry.cpp @@ -29,6 +29,7 @@ #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/platform_osystem.h" #include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/system/sys_instance.h" #include "engines/wintermute/system/sys_class_registry.h" #include "engines/wintermute/system/sys_class.h" diff --git a/engines/wintermute/ui/ui_button.cpp b/engines/wintermute/ui/ui_button.cpp index f2ac5b2fdd..7967d566f9 100644 --- a/engines/wintermute/ui/ui_button.cpp +++ b/engines/wintermute/ui/ui_button.cpp @@ -37,6 +37,7 @@ #include "engines/wintermute/base/base_string_table.h" #include "engines/wintermute/base/base_sprite.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" @@ -1081,13 +1082,13 @@ bool UIButton::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *UIButton::scGetProperty(const char *name) { +ScValue *UIButton::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("button"); return _scValue; } @@ -1095,7 +1096,7 @@ ScValue *UIButton::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TextAlign ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TextAlign") == 0) { + else if (name == "TextAlign") { _scValue->setInt(_align); return _scValue; } @@ -1103,21 +1104,21 @@ ScValue *UIButton::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Focusable ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Focusable") == 0) { + else if (name == "Focusable") { _scValue->setBool(_canFocus); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Pressed ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Pressed") == 0) { + else if (name == "Pressed") { _scValue->setBool(_stayPressed); return _scValue; } ////////////////////////////////////////////////////////////////////////// // PixelPerfect ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "PixelPerfect") == 0) { + else if (name == "PixelPerfect") { _scValue->setBool(_pixelPerfect); return _scValue; } else { diff --git a/engines/wintermute/ui/ui_button.h b/engines/wintermute/ui/ui_button.h index 9342f766cc..93333a2534 100644 --- a/engines/wintermute/ui/ui_button.h +++ b/engines/wintermute/ui/ui_button.h @@ -69,7 +69,7 @@ public: virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ui/ui_edit.cpp b/engines/wintermute/ui/ui_edit.cpp index 94d11255ce..a3283d5a01 100644 --- a/engines/wintermute/ui/ui_edit.cpp +++ b/engines/wintermute/ui/ui_edit.cpp @@ -40,6 +40,7 @@ #include "engines/wintermute/base/base_sprite.h" #include "engines/wintermute/base/base_string_table.h" #include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script_stack.h" #include "engines/wintermute/base/scriptables/script.h" @@ -397,13 +398,13 @@ bool UIEdit::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *UIEdit::scGetProperty(const char *name) { +ScValue *UIEdit::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("editor"); return _scValue; } @@ -411,7 +412,7 @@ ScValue *UIEdit::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SelStart ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SelStart") == 0) { + else if (name == "SelStart") { _scValue->setInt(_selStart); return _scValue; } @@ -419,7 +420,7 @@ ScValue *UIEdit::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SelEnd ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SelEnd") == 0) { + else if (name == "SelEnd") { _scValue->setInt(_selEnd); return _scValue; } @@ -427,7 +428,7 @@ ScValue *UIEdit::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // CursorBlinkRate ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "CursorBlinkRate") == 0) { + else if (name == "CursorBlinkRate") { _scValue->setInt(_cursorBlinkRate); return _scValue; } @@ -435,7 +436,7 @@ ScValue *UIEdit::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // CursorChar ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "CursorChar") == 0) { + else if (name == "CursorChar") { _scValue->setString(_cursorChar); return _scValue; } @@ -443,7 +444,7 @@ ScValue *UIEdit::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // FrameWidth ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "FrameWidth") == 0) { + else if (name == "FrameWidth") { _scValue->setInt(_frameWidth); return _scValue; } @@ -451,7 +452,7 @@ ScValue *UIEdit::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // MaxLength ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "MaxLength") == 0) { + else if (name == "MaxLength") { _scValue->setInt(_maxLength); return _scValue; } @@ -459,7 +460,7 @@ ScValue *UIEdit::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Text ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Text") == 0) { + else if (name == "Text") { if (_gameRef->_textEncoding == TEXT_UTF8) { WideString wstr = StringUtil::ansiToWide(_text); _scValue->setString(StringUtil::wideToUtf8(wstr).c_str()); diff --git a/engines/wintermute/ui/ui_edit.h b/engines/wintermute/ui/ui_edit.h index 610629afb3..5bb31422b6 100644 --- a/engines/wintermute/ui/ui_edit.h +++ b/engines/wintermute/ui/ui_edit.h @@ -61,7 +61,7 @@ public: virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ui/ui_entity.cpp b/engines/wintermute/ui/ui_entity.cpp index c49cb5a240..1cb4e0926b 100644 --- a/engines/wintermute/ui/ui_entity.cpp +++ b/engines/wintermute/ui/ui_entity.cpp @@ -305,13 +305,13 @@ bool UIEntity::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *UIEntity::scGetProperty(const char *name) { +ScValue *UIEntity::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("entity container"); return _scValue; } @@ -319,7 +319,7 @@ ScValue *UIEntity::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Freezable ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Freezable") == 0) { + else if (name == "Freezable") { if (_entity) { _scValue->setBool(_entity->_freezable); } else { diff --git a/engines/wintermute/ui/ui_entity.h b/engines/wintermute/ui/ui_entity.h index 3bf8068fd5..b5f4450071 100644 --- a/engines/wintermute/ui/ui_entity.h +++ b/engines/wintermute/ui/ui_entity.h @@ -48,7 +48,7 @@ public: bool setEntity(const char *filename); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ui/ui_object.cpp b/engines/wintermute/ui/ui_object.cpp index 85e381c55b..8e5bae993c 100644 --- a/engines/wintermute/ui/ui_object.cpp +++ b/engines/wintermute/ui/ui_object.cpp @@ -32,6 +32,7 @@ #include "engines/wintermute/ui/ui_tiled_image.h" #include "engines/wintermute/ui/ui_window.h" #include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/base/scriptables/script_stack.h" #include "engines/wintermute/base/font/base_font_storage.h" @@ -359,13 +360,13 @@ bool UIObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *UIObject::scGetProperty(const char *name) { +ScValue *UIObject::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("ui_object"); return _scValue; } @@ -373,7 +374,7 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Name ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Name") == 0) { + else if (name == "Name") { _scValue->setString(getName()); return _scValue; } @@ -381,7 +382,7 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Parent (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Parent") == 0) { + else if (name == "Parent") { _scValue->setNative(_parent, true); return _scValue; } @@ -389,7 +390,7 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ParentNotify ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ParentNotify") == 0) { + else if (name == "ParentNotify") { _scValue->setBool(_parentNotify); return _scValue; } @@ -397,7 +398,7 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Width ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Width") == 0) { + else if (name == "Width") { _scValue->setInt(_width); return _scValue; } @@ -405,7 +406,7 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Height ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Height") == 0) { + else if (name == "Height") { _scValue->setInt(_height); return _scValue; } @@ -413,7 +414,7 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Visible ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Visible") == 0) { + else if (name == "Visible") { _scValue->setBool(_visible); return _scValue; } @@ -421,7 +422,7 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Disabled ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Disabled") == 0) { + else if (name == "Disabled") { _scValue->setBool(_disable); return _scValue; } @@ -429,7 +430,7 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Text ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Text") == 0) { + else if (name == "Text") { _scValue->setString(_text); return _scValue; } @@ -437,13 +438,13 @@ ScValue *UIObject::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NextSibling (RO) / PrevSibling (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NextSibling") == 0 || strcmp(name, "PrevSibling") == 0) { + else if (name == "NextSibling" || name == "PrevSibling") { _scValue->setNULL(); if (_parent && _parent->_type == UI_WINDOW) { UIWindow *win = (UIWindow *)_parent; for (uint32 i = 0; i < win->_widgets.size(); i++) { if (win->_widgets[i] == this) { - if (strcmp(name, "NextSibling") == 0) { + if (name == "NextSibling") { if (i < win->_widgets.size() - 1) { _scValue->setNative(win->_widgets[i + 1], true); } diff --git a/engines/wintermute/ui/ui_object.h b/engines/wintermute/ui/ui_object.h index 81c025d33b..ec2ea33de1 100644 --- a/engines/wintermute/ui/ui_object.h +++ b/engines/wintermute/ui/ui_object.h @@ -74,7 +74,7 @@ public: virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ui/ui_text.cpp b/engines/wintermute/ui/ui_text.cpp index 1844b640d0..2c10f176c7 100644 --- a/engines/wintermute/ui/ui_text.cpp +++ b/engines/wintermute/ui/ui_text.cpp @@ -431,13 +431,13 @@ bool UIText::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, ////////////////////////////////////////////////////////////////////////// -ScValue *UIText::scGetProperty(const char *name) { +ScValue *UIText::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("static"); return _scValue; } @@ -445,7 +445,7 @@ ScValue *UIText::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // TextAlign ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "TextAlign") == 0) { + else if (name == "TextAlign") { _scValue->setInt(_textAlign); return _scValue; } @@ -453,7 +453,7 @@ ScValue *UIText::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // VerticalAlign ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "VerticalAlign") == 0) { + else if (name == "VerticalAlign") { _scValue->setInt(_verticalAlign); return _scValue; } else { diff --git a/engines/wintermute/ui/ui_text.h b/engines/wintermute/ui/ui_text.h index d2f116b44b..da4d113500 100644 --- a/engines/wintermute/ui/ui_text.h +++ b/engines/wintermute/ui/ui_text.h @@ -49,7 +49,7 @@ public: virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/ui/ui_tiled_image.cpp b/engines/wintermute/ui/ui_tiled_image.cpp index cec23cf67e..2b337330c7 100644 --- a/engines/wintermute/ui/ui_tiled_image.cpp +++ b/engines/wintermute/ui/ui_tiled_image.cpp @@ -33,6 +33,7 @@ #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/base_sub_frame.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/platform_osystem.h" namespace Wintermute { @@ -333,8 +334,8 @@ bool UITiledImage::saveAsText(BaseDynamicBuffer *buffer, int indent) { buffer->putTextIndent(indent, "TILED_IMAGE\n"); buffer->putTextIndent(indent, "{\n"); - if (_image && _image->_surfaceFilename) { - buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->_surfaceFilename); + if (_image && _image->getSurfaceFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getSurfaceFilename()); } int h1, h2, h3; diff --git a/engines/wintermute/ui/ui_window.cpp b/engines/wintermute/ui/ui_window.cpp index 65af62141d..9606486efb 100644 --- a/engines/wintermute/ui/ui_window.cpp +++ b/engines/wintermute/ui/ui_window.cpp @@ -41,6 +41,7 @@ #include "engines/wintermute/base/font/base_font_storage.h" #include "engines/wintermute/base/font/base_font.h" #include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script.h" #include "engines/wintermute/base/scriptables/script_stack.h" #include "engines/wintermute/base/base_sprite.h" @@ -1013,13 +1014,13 @@ bool UIWindow::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack ////////////////////////////////////////////////////////////////////////// -ScValue *UIWindow::scGetProperty(const char *name) { +ScValue *UIWindow::scGetProperty(const Common::String &name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type ////////////////////////////////////////////////////////////////////////// - if (strcmp(name, "Type") == 0) { + if (name == "Type") { _scValue->setString("window"); return _scValue; } @@ -1027,7 +1028,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // NumWidgets / NumControls (RO) ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "NumWidgets") == 0 || strcmp(name, "NumControls") == 0) { + else if (name == "NumWidgets" || name == "NumControls") { _scValue->setInt(_widgets.size()); return _scValue; } @@ -1035,7 +1036,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Exclusive ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Exclusive") == 0) { + else if (name == "Exclusive") { _scValue->setBool(_mode == WINDOW_EXCLUSIVE); return _scValue; } @@ -1043,7 +1044,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // SystemExclusive ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "SystemExclusive") == 0) { + else if (name == "SystemExclusive") { _scValue->setBool(_mode == WINDOW_SYSTEM_EXCLUSIVE); return _scValue; } @@ -1051,7 +1052,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Menu ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Menu") == 0) { + else if (name == "Menu") { _scValue->setBool(_isMenu); return _scValue; } @@ -1059,7 +1060,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // InGame ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "InGame") == 0) { + else if (name == "InGame") { _scValue->setBool(_inGame); return _scValue; } @@ -1067,7 +1068,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // PauseMusic ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "PauseMusic") == 0) { + else if (name == "PauseMusic") { _scValue->setBool(_pauseMusic); return _scValue; } @@ -1075,7 +1076,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // ClipContents ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "ClipContents") == 0) { + else if (name == "ClipContents") { _scValue->setBool(_clipContents); return _scValue; } @@ -1083,7 +1084,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // Transparent ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "Transparent") == 0) { + else if (name == "Transparent") { _scValue->setBool(_transparent); return _scValue; } @@ -1091,7 +1092,7 @@ ScValue *UIWindow::scGetProperty(const char *name) { ////////////////////////////////////////////////////////////////////////// // FadeColor ////////////////////////////////////////////////////////////////////////// - else if (strcmp(name, "FadeColor") == 0) { + else if (name == "FadeColor") { _scValue->setInt((int)_fadeColor); return _scValue; } else { diff --git a/engines/wintermute/ui/ui_window.h b/engines/wintermute/ui/ui_window.h index cbd417a7d9..ae035c65c7 100644 --- a/engines/wintermute/ui/ui_window.h +++ b/engines/wintermute/ui/ui_window.h @@ -83,7 +83,7 @@ public: virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); // scripting interface - virtual ScValue *scGetProperty(const char *name); + virtual ScValue *scGetProperty(const Common::String &name); virtual bool scSetProperty(const char *name, ScValue *value); virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); virtual const char *scToString(); diff --git a/engines/wintermute/utils/string_util.cpp b/engines/wintermute/utils/string_util.cpp index 2c3be8c2f5..7b3b0e1297 100644 --- a/engines/wintermute/utils/string_util.cpp +++ b/engines/wintermute/utils/string_util.cpp @@ -148,11 +148,11 @@ Utf8String StringUtil::wideToUtf8(const WideString &WideStr) { // Currently this only does Ansi->ISO 8859, and only for carets. char simpleAnsiToWide(const AnsiString &str, uint32 &offset) { - char c = str[offset]; + byte c = str[offset]; - if (c == 92) { + if (c == 146) { offset++; - return '\''; + return 39; // Replace right-quote with apostrophe } else { offset++; return c; @@ -162,11 +162,11 @@ char simpleAnsiToWide(const AnsiString &str, uint32 &offset) { ////////////////////////////////////////////////////////////////////////// WideString StringUtil::ansiToWide(const AnsiString &str) { // TODO: This function gets called a lot, so warnings like these drown out the usefull information - /*Common::String converted = ""; + Common::String converted = ""; uint32 index = 0; while (index != str.size()) { converted += simpleAnsiToWide(str, index); - }*/ + } // using default os locale! /* setlocale(LC_CTYPE, ""); @@ -176,7 +176,7 @@ WideString StringUtil::ansiToWide(const AnsiString &str) { WideString ResultString(wstr); delete[] wstr; return ResultString;*/ - return WideString(str); + return WideString(converted); } ////////////////////////////////////////////////////////////////////////// diff --git a/engines/wintermute/video/video_theora_player.cpp b/engines/wintermute/video/video_theora_player.cpp index 0d23a04af4..d14c807e11 100644 --- a/engines/wintermute/video/video_theora_player.cpp +++ b/engines/wintermute/video/video_theora_player.cpp @@ -32,8 +32,8 @@ #include "engines/wintermute/base/base_file_manager.h" #include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" #include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/sound/base_sound_manager.h" -#include "engines/wintermute/utils/utils.h" #include "engines/wintermute/platform_osystem.h" #include "video/theora_decoder.h" #include "engines/wintermute/wintermute.h" @@ -151,7 +151,32 @@ bool VideoTheoraPlayer::initialize(const Common::String &filename, const Common: ////////////////////////////////////////////////////////////////////////// bool VideoTheoraPlayer::resetStream() { - warning("VidTheoraPlayer::resetStream - stubbed"); + warning("VidTheoraPlayer::resetStream - hacked"); + // HACK: Just reopen the same file again. + if (_theoraDecoder) { + _theoraDecoder->close(); + } + delete _theoraDecoder; + _theoraDecoder = NULL; + + _file = BaseFileManager::getEngineInstance()->openFile(_filename, true, false); + if (!_file) { + return STATUS_FAILED; + } + +#if defined (USE_THEORADEC) + _theoraDecoder = new Video::TheoraDecoder(); +#else + return STATUS_FAILED; +#endif + _theoraDecoder->loadStream(_file); + + if (!_theoraDecoder->isVideoLoaded()) { + return STATUS_FAILED; + } + + return play(_playbackType, _posX, _posY, false, false, _looping, 0, _playZoom); + // End of hack. #if 0 // Stubbed for now, as theora isn't seekable if (_sound) { _sound->Stop(); @@ -265,8 +290,10 @@ bool VideoTheoraPlayer::update() { if (_theoraDecoder) { if (_theoraDecoder->endOfVideo() && _looping) { - warning("Should loop movie %s", _filename.c_str()); + warning("Should loop movie %s, hacked for now", _filename.c_str()); _theoraDecoder->rewind(); + //HACK: Just reinitialize the same video again: + return resetStream(); } else if (_theoraDecoder->endOfVideo() && !_looping) { debugC(kWintermuteDebugLog, "Finished movie %s", _filename.c_str()); _state = THEORA_STATE_FINISHED; diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp index 9ada07293f..c9726e150a 100644 --- a/engines/wintermute/wintermute.cpp +++ b/engines/wintermute/wintermute.cpp @@ -39,6 +39,7 @@ #include "engines/wintermute/base/sound/base_sound_manager.h" #include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" #include "engines/wintermute/base/scriptables/script_engine.h" namespace Wintermute { |