void GuestAdditions::syncPhant1UI(const int16 oldMusicVolume, const int16 musicVolume, reg_t &musicGlobal, const int16 oldDacVolume, const int16 dacVolume, reg_t &dacGlobal) const { const reg_t buttonId = _segMan->findObjectByName("dacVolUp"); if (buttonId.isNull() || !_segMan->getObject(buttonId)->isInserted()) { // No inserted dacVolUp button means the control panel with the // volume controls is not visible and we can just update the values // and leave musicGlobal.setOffset(musicVolume); dacGlobal.setOffset(dacVolume); return; } reg_t thermo = _segMan->findObjectByName("midiVolThermo"); if (!thermo.isNull()) { int count = ABS(musicVolume - oldMusicVolume); const int stepSize = (musicVolume > oldMusicVolume ? 1 : -1); while (count--) { musicGlobal.incOffset(stepSize); invokeSelector(thermo, SELECTOR(doit)); } } thermo = _segMan->findObjectByName("dacVolThermo"); if (!thermo.isNull()) { int count = ABS(dacVolume - oldDacVolume) / 8; const int stepSize = (dacVolume > oldDacVolume ? 8 : -8); while (count--) { dacGlobal.incOffset(stepSize); invokeSelector(thermo, SELECTOR(doit)); } } }
reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { List *list = s->_segMan->lookupList(argv[0]); Node *curNode = s->_segMan->lookupNode(list->first); reg_t curObject; Selector slc = argv[1].toUint16(); ObjVarRef address; while (curNode) { // We get the next node here as the current node might be gone after the invoke reg_t nextNode = curNode->succ; curObject = curNode->value; // First, check if the target selector is a variable if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // This can only happen with 3 params (list, target selector, variable) if (argc != 3) { error("kListEachElementDo: Attempted to modify a variable selector with %d params", argc); } else { writeSelector(s->_segMan, curObject, slc, argv[2]); } } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); } curNode = s->_segMan->lookupNode(nextNode); } return s->r_acc; }
reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) { List *list = s->_segMan->lookupList(argv[0]); Node *curNode = s->_segMan->lookupNode(list->first); reg_t curObject; Selector slc = argv[1].toUint16(); ObjVarRef address; s->r_acc = make_reg(0, 1); // reset the accumulator while (curNode) { reg_t nextNode = curNode->succ; curObject = curNode->value; // First, check if the target selector is a variable if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // Can this happen with variable selectors? error("kListAllTrue: Attempted to access a variable selector"); } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); // Check if the result isn't true if (s->r_acc.isNull()) break; } curNode = s->_segMan->lookupNode(nextNode); } return s->r_acc; }
void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) { AnimateList::iterator it; const AnimateList::iterator end = _list.end(); // This has to be done in a separate loop. At least in sq1 some .dispose // modifies FIXEDLOOP flag in signal for another object. In that case we // would overwrite the new signal with our version of the old signal. for (it = _list.begin(); it != end; ++it) { // Finally update signal writeSelectorValue(_s->_segMan, it->object, SELECTOR(signal), it->signal); } for (it = _list.legacy_reverse_begin(); it != end; --it) { // We read out signal here again, this is not by accident but to ensure // that we got an up-to-date signal it->signal = readSelectorValue(_s->_segMan, it->object, SELECTOR(signal)); if ((it->signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) { _paint16->bitsRestore(readSelector(_s->_segMan, it->object, SELECTOR(underBits))); writeSelectorValue(_s->_segMan, it->object, SELECTOR(underBits), 0); } if (it->signal & kSignalDisposeMe) { // Call .delete_ method of that object invokeSelector(_s, it->object, SELECTOR(delete_), argc, argv, 0); } } }
void GuestAdditions::syncMessageTypeFromScummVMUsingLSL6HiresStrategy() const { // LSL6hires synchronisation happens in send_selector, except when // restoring a game, where it happens here if (_state->variables[VAR_GLOBAL][kGlobalVarLSL6HiresGameFlags].isNull()) { return; } reg_t params[] = { make_reg(0, kLSL6HiresSubtitleFlag) }; Selector selector; reg_t restore; if (ConfMan.getBool("subtitles")) { restore = TRUE_REG; selector = SELECTOR(clear); } else { restore = NULL_REG; selector = SELECTOR(set); } // Attempting to show or hide the ScrollWindow used for subtitles // directly (by invoking `show` or `hide`) causes the game to crash with // an error about passing an invalid ScrollWindow ID. Fortunately, the // game scripts store a flag that restores the window when a game is // restored _state->variables[VAR_GLOBAL][kGlobalVarLSL6HiresRestoreTextWindow] = restore; invokeSelector(_state->variables[VAR_GLOBAL][kGlobalVarLSL6HiresGameFlags], selector, 1, params); }
reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) { const reg_t listReg = argv[0]; List *list = s->_segMan->lookupList(listReg); Node *curNode = s->_segMan->lookupNode(list->first); Selector slc = argv[1].toUint16(); ObjVarRef address; s->r_acc = NULL_REG; ++list->numRecursions; if (list->numRecursions >= ARRAYSIZE(list->nextNodes)) { error("Too much recursion in kListFirstTrue"); } while (curNode) { // We get the next node here as the current node might be deleted by the // invoke. In the case that the next node is also deleted, kDeleteKey // needs to be able to adjust the location of the next node, which is // why it is stored on the list instead of on the stack list->nextNodes[list->numRecursions] = curNode->succ; reg_t curObject = curNode->value; // First, check if the target selector is a variable if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // If it's a variable selector, check its value. // Example: script 64893 in Torin, MenuHandler::isHilited checks // all children for variable selector 0x03ba (bHilited). if (!readSelector(s->_segMan, curObject, slc).isNull()) { s->r_acc = curObject; break; } } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); // Check if the call above leads to a game restore, in which case // the segment manager will be reset, and the original list will // be invalidated if (s->abortScriptProcessing == kAbortLoadGame) return s->r_acc; // Check if the result is true if (!s->r_acc.isNull()) { s->r_acc = curObject; break; } } curNode = s->_segMan->lookupNode(list->nextNodes[list->numRecursions]); } if (s->_segMan->isValidAddr(listReg, SEG_TYPE_LISTS)) { --list->numRecursions; } return s->r_acc; }
void GuestAdditions::syncGK2UI() const { const reg_t sliderId = _segMan->findObjectByName("soundSlider"); if (!sliderId.isNull() && _segMan->getObject(sliderId)->isInserted()) { const reg_t oldAcc = _state->r_acc; invokeSelector(sliderId, SELECTOR(initialOff)); writeSelector(_segMan, sliderId, SELECTOR(x), _state->r_acc); _state->r_acc = oldAcc; } }
void GuestAdditions::syncSQ6UI() const { const reg_t bars[] = { _segMan->findObjectByName("musicBar"), _segMan->findObjectByName("soundBar") }; for (int i = 0; i < ARRAYSIZE(bars); ++i) { const reg_t barId = bars[i]; if (!barId.isNull()) { invokeSelector(barId, SELECTOR(show)); } } }
void GuestAdditions::syncPQ4UI(const int16 musicVolume) const { const SegmentId segment = _segMan->getScriptSegment(9, SCRIPT_GET_DONT_LOAD); if (segment != 0 && _segMan->getScript(segment)->getLocalsCount() > 2) { const reg_t barId = _segMan->getScript(segment)->getLocalsBegin()[2]; if (!barId.isNull()) { reg_t params[] = { make_reg(0, musicVolume) }; invokeSelector(barId, SELECTOR(setSize), 1, params); } } }
void GuestAdditions::syncLSL6HiresUI(const int16 musicVolume) const { const reg_t musicDialId = _segMan->findObjectByName("volumeDial"); if (!musicDialId.isNull()) { writeSelectorValue(_segMan, musicDialId, SELECTOR(curPos), musicVolume); writeSelectorValue(_segMan, musicDialId, SELECTOR(cel), musicVolume); reg_t params[] = { make_reg(0, musicVolume) }; invokeSelector(musicDialId, SELECTOR(update), 1, params); if (_segMan->getObject(musicDialId)->isInserted()) { g_sci->_gfxFrameout->kernelUpdateScreenItem(musicDialId); } } }
void GuestAdditions::syncTorinUI(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const { const reg_t sliders[] = { _segMan->findObjectByName("oMusicScroll"), _segMan->findObjectByName("oSFXScroll"), _segMan->findObjectByName("oAudioScroll") }; const int16 values[] = { musicVolume, sfxVolume, speechVolume }; for (int i = 0; i < ARRAYSIZE(sliders); ++i) { const reg_t sliderId = sliders[i]; if (!sliderId.isNull()) { reg_t params[] = { make_reg(0, values[i]) }; invokeSelector(sliderId, SELECTOR(setPos), 1, params); } } }
reg_t kSort(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t source = argv[0]; reg_t dest = argv[1]; reg_t order_func = argv[2]; int input_size = (int16)readSelectorValue(segMan, source, SELECTOR(size)); reg_t input_data = readSelector(segMan, source, SELECTOR(elements)); reg_t output_data = readSelector(segMan, dest, SELECTOR(elements)); List *list; Node *node; if (!input_size) return s->r_acc; if (output_data.isNull()) { list = s->_segMan->allocateList(&output_data); list->first = list->last = NULL_REG; writeSelector(segMan, dest, SELECTOR(elements), output_data); } writeSelectorValue(segMan, dest, SELECTOR(size), input_size); list = s->_segMan->lookupList(input_data); node = s->_segMan->lookupNode(list->first); sort_temp_t *temp_array = (sort_temp_t *)malloc(sizeof(sort_temp_t) * input_size); int i = 0; while (node) { reg_t params[1] = { node->value }; invokeSelector(s, order_func, SELECTOR(doit), argc, argv, 1, params); temp_array[i].key = node->key; temp_array[i].value = node->value; temp_array[i].order = s->r_acc; i++; node = s->_segMan->lookupNode(node->succ); } qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp); for (i = 0;i < input_size;i++) { reg_t lNode = s->_segMan->newNode(temp_array[i].value, temp_array[i].key); addToEnd(s, output_data, lNode); } free(temp_array); return s->r_acc; }
reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { const reg_t listReg = argv[0]; List *list = s->_segMan->lookupList(listReg); Node *curNode = s->_segMan->lookupNode(list->first); Selector slc = argv[1].toUint16(); ObjVarRef address; ++list->numRecursions; if (list->numRecursions >= ARRAYSIZE(list->nextNodes)) { error("Too much recursion in kListEachElementDo"); } while (curNode) { // We get the next node here as the current node might be deleted by the // invoke. In the case that the next node is also deleted, kDeleteKey // needs to be able to adjust the location of the next node, which is // why it is stored on the list instead of on the stack list->nextNodes[list->numRecursions] = curNode->succ; reg_t curObject = curNode->value; // First, check if the target selector is a variable if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // This can only happen with 3 params (list, target selector, variable) if (argc != 3) { error("kListEachElementDo: Attempted to modify a variable selector with %d params", argc); } else { writeSelector(s->_segMan, curObject, slc, argv[2]); } } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); // Check if the call above leads to a game restore, in which case // the segment manager will be reset, and the original list will // be invalidated if (s->abortScriptProcessing == kAbortLoadGame) return s->r_acc; } curNode = s->_segMan->lookupNode(list->nextNodes[list->numRecursions]); } if (s->_segMan->isValidAddr(listReg, SEG_TYPE_LISTS)) { --list->numRecursions; } return s->r_acc; }
void GuestAdditions::syncGK2VolumeFromScummVM(const int16 musicVolume) const { _state->variables[VAR_GLOBAL][kGlobalVarGK2MusicVolume] = make_reg(0, musicVolume); // Calling `setVol` on all sounds is necessary to propagate the volume // change to existing sounds, and matches how game scripts propagate // volume changes when the in-game music slider is moved const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds]; if (!soundsId.isNull()) { List *sounds = _segMan->lookupList(readSelector(_segMan, soundsId, SELECTOR(elements))); reg_t soundId = sounds->first; while (!soundId.isNull()) { Node *sound = _segMan->lookupNode(soundId); reg_t params[] = { make_reg(0, musicVolume) }; invokeSelector(sound->value, SELECTOR(setVol), 1, params); soundId = sound->succ; } } }
reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) { List *list = s->_segMan->lookupList(argv[0]); Node *curNode = s->_segMan->lookupNode(list->first); reg_t curObject; Selector slc = argv[1].toUint16(); ObjVarRef address; s->r_acc = TRUE_REG; ++list->numRecursions; if (list->numRecursions >= ARRAYSIZE(list->nextNodes)) { error("Too much recursion in kListAllTrue"); } while (curNode) { // We get the next node here as the current node might be deleted by the // invoke. In the case that the next node is also deleted, kDeleteKey // needs to be able to adjust the location of the next node, which is // why it is stored on the list instead of on the stack list->nextNodes[list->numRecursions] = curNode->succ; curObject = curNode->value; // First, check if the target selector is a variable if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // If it's a variable selector, check its value s->r_acc = readSelector(s->_segMan, curObject, slc); } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); } // Check if the result isn't true if (s->r_acc.isNull()) break; curNode = s->_segMan->lookupNode(list->nextNodes[list->numRecursions]); } --list->numRecursions; return s->r_acc; }
bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) { reg_t curAddress = list->first; Node *curNode = _s->_segMan->lookupNode(curAddress); reg_t curObject; uint16 signal; while (curNode) { curObject = curNode->value; if (_fastCastEnabled) { // Check if the game has a fastCast object set // if we don't abort kAnimate processing, at least in kq5 there will be animation cels drawn into speech boxes. if (!_s->variables[VAR_GLOBAL][84].isNull()) { // This normally points to an object called "fastCast", // but for example in Eco Quest 1 it may also point to an object called "EventHandler" (see bug #5170) // Original SCI only checked, if this global was not 0. return false; } } signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal)); if (!(signal & kSignalFrozen)) { // Call .doit method of that object invokeSelector(_s, curObject, SELECTOR(doit), argc, argv, 0); // If a game is being loaded, stop processing if (_s->abortScriptProcessing != kAbortNone) return true; // Stop processing // Lookup node again, since the nodetable it was in may have been reallocated. // The node might have been deallocated at this point (e.g. LSL2, room 42), // in which case the node reference will be null and the loop will stop below. // If the node is deleted from kDeleteKey, it won't have a successor node, thus // list processing will stop here (which is what SSCI does). curNode = _s->_segMan->lookupNode(curAddress, false); } if (curNode) { curAddress = curNode->succ; curNode = _s->_segMan->lookupNode(curAddress); } } return true; }
void GuestAdditions::syncGK1UI() const { const reg_t bars[] = { _segMan->findObjectByName("musicBar"), _segMan->findObjectByName("soundBar") }; for (int i = 0; i < ARRAYSIZE(bars); ++i) { const reg_t barId = bars[i]; if (!barId.isNull()) { // Resetting the position to 0 causes the bar to refresh its // position when it next draws writeSelectorValue(_segMan, barId, SELECTOR(position), 0); // The `signal` property indicates bar visibility (for some // reason, the normal `-info-` flag is not used) if (readSelectorValue(_segMan, barId, SELECTOR(signal)) & 0x20) { // `show` pulls a new value from the underlying sound object // and refreshes the bar rendering invokeSelector(barId, SELECTOR(show)); } } } }
reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) { List *list = s->_segMan->lookupList(argv[0]); Node *curNode = s->_segMan->lookupNode(list->first); reg_t curObject; Selector slc = argv[1].toUint16(); ObjVarRef address; s->r_acc = NULL_REG; // reset the accumulator while (curNode) { reg_t nextNode = curNode->succ; curObject = curNode->value; // First, check if the target selector is a variable if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // If it's a variable selector, check its value. // Example: script 64893 in Torin, MenuHandler::isHilited checks // all children for variable selector 0x03ba (bHilited). if (!readSelector(s->_segMan, curObject, slc).isNull()) return curObject; } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); // Check if the result is true if (!s->r_acc.isNull()) return curObject; } curNode = s->_segMan->lookupNode(nextNode); } // No selector returned true return NULL_REG; }
void GuestAdditions::syncTorinVolumeFromScummVM(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const { _state->variables[VAR_GLOBAL][kGlobalVarTorinMusicVolume] = make_reg(0, musicVolume); _state->variables[VAR_GLOBAL][kGlobalVarTorinSFXVolume] = make_reg(0, sfxVolume); _state->variables[VAR_GLOBAL][kGlobalVarTorinSpeechVolume] = make_reg(0, speechVolume); // Calling `reSyncVol` on all sounds is necessary to propagate the // volume change to existing sounds, and matches how game scripts // propagate volume changes when the in-game volume sliders are moved const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds]; if (!soundsId.isNull()) { const Selector selector = SELECTOR(reSyncVol); List *sounds = _segMan->lookupList(readSelector(_segMan, soundsId, SELECTOR(elements))); reg_t soundId = sounds->first; while (!soundId.isNull()) { Node *sound = _segMan->lookupNode(soundId); const reg_t &soundObj = sound->value; if (_segMan->isHeapObject(soundObj) && lookupSelector(_segMan, soundObj, selector, nullptr, nullptr) != kSelectorNone) { invokeSelector(sound->value, SELECTOR(reSyncVol)); } soundId = sound->succ; } } }
reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { int mask = argv[0].toUint16(); reg_t obj = argv[1]; SciEvent curEvent; int modifier_mask = getSciVersion() <= SCI_VERSION_01 ? SCI_KEYMOD_ALL : SCI_KEYMOD_NO_FOOLOCK; uint16 modifiers = 0; SegManager *segMan = s->_segMan; Common::Point mousePos; // For Mac games with an icon bar, handle possible icon bar events first if (g_sci->hasMacIconBar()) { reg_t iconObj = g_sci->_gfxMacIconBar->handleEvents(); if (!iconObj.isNull()) invokeSelector(s, iconObj, SELECTOR(select), argc, argv, 0, NULL); } // If there's a simkey pending, and the game wants a keyboard event, use the // simkey instead of a normal event if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) { // In case we use a simulated event we query the current mouse position mousePos = g_sci->_gfxCursor->getPosition(); #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2_1_EARLY) g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); #endif // Limit the mouse cursor position, if necessary g_sci->_gfxCursor->refreshPosition(); writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key); writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x); writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y); g_debug_simulated_key = 0; return make_reg(0, 1); } curEvent = g_sci->getEventManager()->getSciEvent(mask); if (s->_delayedRestoreGame) { // delayed restore game from ScummVM menu got triggered gamestate_delayedrestore(s); return NULL_REG; } // For a real event we use its associated mouse position mousePos = curEvent.mousePos; #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2_1_EARLY) g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); #endif // Limit the mouse cursor position, if necessary g_sci->_gfxCursor->refreshPosition(); if (g_sci->getVocabulary()) g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event if (s->_cursorWorkaroundActive) { // We check if the actual cursor position is inside specific rectangles // where the cursor itself should be moved to. If this is the case, we // set the mouse cursor's position to be within the rectangle in // question. Check GfxCursor::setPosition(), for a more detailed // explanation and a list of cursor position workarounds. if (s->_cursorWorkaroundRect.contains(mousePos.x, mousePos.y)) { s->_cursorWorkaroundActive = false; } else { mousePos.x = s->_cursorWorkaroundPoint.x; mousePos.y = s->_cursorWorkaroundPoint.y; } } writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x); writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y); // Get current keyboard modifiers, only keep relevant bits modifiers = curEvent.modifiers & modifier_mask; if (g_sci->getPlatform() == Common::kPlatformDOS) { // We are supposed to emulate SCI running in DOS // We set the higher byte of the modifiers to 02h // Original SCI also did that indirectly, because it asked BIOS for shift status // via AH=0x02 INT16, which then sets the shift flags in AL // AH is supposed to be destroyed in that case and it's not defined that 0x02 // is still in it on return. The value of AX was then set into the modifiers selector. // At least one fan-made game (Betrayed Alliance) requires 0x02 to be in the upper byte, // otherwise the darts game (script 111) will not work properly. // It seems Sierra fixed this behaviour (effectively bug) in the SCI1 keyboard driver. // SCI32 also resets the upper byte. if (getSciVersion() <= SCI_VERSION_01) { modifiers |= 0x0200; } } //s->_gui->moveCursor(s->gfx_state->pointer_pos.x, s->gfx_state->pointer_pos.y); switch (curEvent.type) { case SCI_EVENT_QUIT: s->abortScriptProcessing = kAbortQuitGame; // Terminate VM g_sci->_debugState.seeking = kDebugSeekNothing; g_sci->_debugState.runningStep = 0; break; case SCI_EVENT_KEYBOARD: writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event s->r_acc = make_reg(0, 1); writeSelectorValue(segMan, obj, SELECTOR(message), curEvent.character); // We only care about the translated character writeSelectorValue(segMan, obj, SELECTOR(modifiers), modifiers); break; case SCI_EVENT_MOUSE_RELEASE: case SCI_EVENT_MOUSE_PRESS: // track left buttton clicks, if requested if (curEvent.type == SCI_EVENT_MOUSE_PRESS && curEvent.data == 1 && g_debug_track_mouse_clicks) { g_sci->getSciDebugger()->debugPrintf("Mouse clicked at %d, %d\n", mousePos.x, mousePos.y); } if (mask & curEvent.type) { int extra_bits = 0; switch (curEvent.data) { case 2: extra_bits = SCI_KEYMOD_LSHIFT | SCI_KEYMOD_RSHIFT; break; case 3: extra_bits = SCI_KEYMOD_CTRL; default: break; } modifiers |= extra_bits; // add these additional bits to the mix writeSelectorValue(segMan, obj, SELECTOR(type), curEvent.type); writeSelectorValue(segMan, obj, SELECTOR(message), 0); writeSelectorValue(segMan, obj, SELECTOR(modifiers), modifiers); s->r_acc = make_reg(0, 1); } break; default: // Return a null event writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_NONE); writeSelectorValue(segMan, obj, SELECTOR(message), 0); writeSelectorValue(segMan, obj, SELECTOR(modifiers), modifiers); s->r_acc = NULL_REG; } if ((s->r_acc.getOffset()) && (g_sci->_debugState.stopOnEvent)) { g_sci->_debugState.stopOnEvent = false; // A SCI event occurred, and we have been asked to stop, so open the debug console Console *con = g_sci->getSciDebugger(); con->debugPrintf("SCI event occurred: "); switch (curEvent.type) { case SCI_EVENT_QUIT: con->debugPrintf("quit event\n"); break; case SCI_EVENT_KEYBOARD: con->debugPrintf("keyboard event\n"); break; case SCI_EVENT_MOUSE_RELEASE: case SCI_EVENT_MOUSE_PRESS: con->debugPrintf("mouse click event\n"); break; default: con->debugPrintf("unknown or no event (event type %d)\n", curEvent.type); } con->attach(); con->onFrame(); } if (g_sci->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) { // If we're running a sound-SCI0 game, update the sound cues, to // compensate for the fact that sound-SCI0 does not poll to update // the sound cues itself, like sound-SCI1 and later do with // cmdUpdateSoundCues. kGetEvent is called quite often, so emulate // the sound-SCI1 behavior of cmdUpdateSoundCues with this call g_sci->_soundCmd->updateSci0Cues(); } // Wait a bit here, so that the CPU isn't maxed out when the game // is waiting for user input (e.g. when showing text boxes) - bug // #3037874. Make sure that we're not delaying while the game is // benchmarking, as that will affect the final benchmarked result - // check bugs #3058865 and #3127824 if (s->_gameIsBenchmarking) { // Game is benchmarking, don't add a delay } else { g_system->delayMillis(10); } return s->r_acc; }
reg_t kParse(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t stringpos = argv[0]; Common::String string = s->_segMan->getString(stringpos); char *error; reg_t event = argv[1]; g_sci->checkVocabularySwitch(); Vocabulary *voc = g_sci->getVocabulary(); voc->parser_event = event; reg_t params[2] = { s->_segMan->getParserPtr(), stringpos }; ResultWordListList words; bool res = voc->tokenizeString(words, string.c_str(), &error); voc->parserIsValid = false; /* not valid */ if (res && !words.empty()) { voc->synonymizeTokens(words); s->r_acc = make_reg(0, 1); #ifdef DEBUG_PARSER debugC(kDebugLevelParser, "Parsed to the following blocks:"); for (ResultWordListList::const_iterator i = words.begin(); i != words.end(); ++i) { debugCN(2, kDebugLevelParser, " "); for (ResultWordList::const_iterator j = i->begin(); j != i->end(); ++j) { debugCN(2, kDebugLevelParser, "%sType[%04x] Group[%04x]", j == i->begin() ? "" : " / ", j->_class, j->_group); } debugCN(2, kDebugLevelParser, "\n"); } #endif voc->replacePronouns(words); int syntax_fail = voc->parseGNF(words); if (syntax_fail) { s->r_acc = make_reg(0, 1); writeSelectorValue(segMan, event, SELECTOR(claimed), 1); invokeSelector(s, g_sci->getGameObject(), SELECTOR(syntaxFail), argc, argv, 2, params); /* Issue warning */ debugC(kDebugLevelParser, "Tree building failed"); } else { voc->parserIsValid = true; voc->storePronounReference(); writeSelectorValue(segMan, event, SELECTOR(claimed), 0); #ifdef DEBUG_PARSER voc->dumpParseTree(); #endif } } else { s->r_acc = make_reg(0, 0); writeSelectorValue(segMan, event, SELECTOR(claimed), 1); if (error) { s->_segMan->strcpy(s->_segMan->getParserPtr(), error); debugC(kDebugLevelParser, "Word unknown: %s", error); /* Issue warning: */ invokeSelector(s, g_sci->getGameObject(), SELECTOR(wordFail), argc, argv, 2, params); free(error); return make_reg(0, 1); /* Tell them that it didn't work */ } } return s->r_acc; }
void GuestAdditions::syncPQSWATUI() const { const reg_t barId = _segMan->findObjectByName("volumeLed"); if (!barId.isNull() && _segMan->getObject(barId)->isInserted()) { invokeSelector(barId, SELECTOR(displayValue)); } }
bool GuestAdditions::restoreFromLauncher() const { assert(_state->_delayedRestoreGameId != -1); #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { if (_restoring) { // Recursion will occur if a restore fails, as // _delayedRestoreGameId will not be reset so the kernel will try // to keep restoring forever _state->_delayedRestoreGameId = -1; _restoring = false; return false; } // Delayed restore should not happen until after the benchmarking room. // In particular, in SQ6, delayed restore must not happen until room 100 // (the Sierra logo & main menu room), otherwise the game scripts will // try to make calls to the subtitles ScrollWindow, which does not // exist. In other games, restoring early either breaks benchmarking, // or, when trying to load an invalid save game, makes the dialog // telling the user that the game is invalid impossible to read if (strcmp(_segMan->getObjectName(_state->variables[VAR_GLOBAL][kGlobalVarCurrentRoom]), "speedRoom") == 0) { return false; } _restoring = true; if (g_sci->getGameId() == GID_SHIVERS) { // Shivers accepts the save game number as a parameter to // `SHIVERS::restore` reg_t args[] = { make_reg(0, _state->_delayedRestoreGameId - kSaveIdShift) }; invokeSelector(g_sci->getGameObject(), SELECTOR(restore), 1, args); } else { // When `Game::restore` is invoked, it will call to `Restore::doit` // which will automatically return the `_delayedRestoreGameId` instead // of prompting the user for a save game invokeSelector(g_sci->getGameObject(), SELECTOR(restore)); } _restoring = false; return true; } else { #else { #endif int savegameId = _state->_delayedRestoreGameId; // delayedRestoreGameId gets destroyed within gamestate_restore()! Common::String fileName = g_sci->getSavegameName(savegameId); Common::SeekableReadStream *in = g_sci->getSaveFileManager()->openForLoading(fileName); if (in) { // found a savegame file gamestate_restore(_state, in); delete in; if (_state->r_acc != make_reg(0, 1)) { gamestate_afterRestoreFixUp(_state, savegameId); return true; } } error("Restoring gamestate '%s' failed", fileName.c_str()); } } #pragma mark - #pragma mark Message type sync void GuestAdditions::syncMessageTypeFromScummVM() const { switch (_features->getMessageTypeSyncStrategy()) { case kMessageTypeSyncStrategyDefault: syncMessageTypeFromScummVMUsingDefaultStrategy(); break; #ifdef ENABLE_SCI32 case kMessageTypeSyncStrategyShivers: syncMessageTypeFromScummVMUsingShiversStrategy(); break; case kMessageTypeSyncStrategyLSL6Hires: syncMessageTypeFromScummVMUsingLSL6HiresStrategy(); break; #endif case kMessageTypeSyncStrategyNone: break; } } void GuestAdditions::syncMessageTypeFromScummVMUsingDefaultStrategy() const { uint8 value = 0; if (ConfMan.getBool("subtitles")) { value |= kMessageTypeSubtitles; } if (!ConfMan.getBool(("speech_mute"))) { value |= kMessageTypeSpeech; } if (value == kMessageTypeSubtitles + kMessageTypeSpeech && !_features->supportsSpeechWithSubtitles()) { value &= ~kMessageTypeSubtitles; } if (value) { _state->variables[VAR_GLOBAL][kGlobalVarMessageType] = make_reg(0, value); } if (g_sci->getGameId() == GID_GK1) { if (value == kMessageTypeSubtitles) { _state->variables[VAR_GLOBAL][kGlobalVarGK1NarratorMode] = NULL_REG; } else if (value == kMessageTypeSpeech) { _state->variables[VAR_GLOBAL][kGlobalVarGK1NarratorMode] = TRUE_REG; } } }