/* 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 3 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, see . * */ #include "common/config-manager.h" #include "twp/twp.h" #include "twp/callback.h" #include "twp/dialog.h" #include "twp/object.h" #include "twp/resmanager.h" #include "twp/room.h" #include "twp/savegame.h" #include "twp/sqgame.h" #include "twp/squtil.h" #include "twp/task.h" #include "twp/thread.h" #include "twp/squirrel/sqvm.h" #include "twp/squirrel/sqstring.h" #include "twp/squirrel/sqstate.h" #include "twp/squirrel/sqtable.h" #include "twp/squirrel/sqfuncproto.h" #include "twp/squirrel/sqclosure.h" namespace Twp { static SQInteger _startthread(HSQUIRRELVM v, bool global) { HSQUIRRELVM vm = g_twp->getVm(); SQInteger size = sq_gettop(v); HSQOBJECT envObj, threadObj, closureObj; sq_resetobject(&envObj); if (SQ_FAILED(sq_getstackobj(v, 1, &envObj))) return sq_throwerror(v, "Couldn't get environment from stack"); // create thread and store it on the stack sq_newthread(vm, 1024); sq_resetobject(&threadObj); if (SQ_FAILED(sq_getstackobj(vm, -1, &threadObj))) return sq_throwerror(v, "Couldn't get coroutine thread from stack"); Common::Array args; for (int i = 0; i < size - 2; i++) { HSQOBJECT arg; sq_resetobject(&arg); if (SQ_FAILED(sq_getstackobj(v, 3 + i, &arg))) return sq_throwerror(v, "Couldn't get coroutine args from stack"); args.push_back(arg); } // get the closure sq_resetobject(&closureObj); if (SQ_FAILED(sq_getstackobj(v, 2, &closureObj))) return sq_throwerror(v, "Couldn't get coroutine thread from stack"); const SQChar *name = nullptr; if (SQ_SUCCEEDED(sq_getclosurename(v, 2))) sq_getstring(v, -1, &name); Common::String threadName = Common::String::format("%s %s (%lld)", name == nullptr ? "" : name, _stringval(_closure(closureObj)->_function->_sourcename), _closure(closureObj)->_function->_lineinfos->_line); Common::SharedPtr t(new Thread(threadName, global, threadObj, envObj, closureObj, Common::move(args))); sq_pop(vm, 1); if (name) sq_pop(v, 1); // pop name sq_pop(v, 1); // pop closure g_twp->_threads.push_back(t); debugC(kDebugSysScript, "create thread %s", t->getName().c_str()); // call the closure in the thread if (!t->call()) return sq_throwerror(v, "call failed"); sqpush(v, t->getId()); return 1; } static SQInteger breakfunc(HSQUIRRELVM v, void func(Common::SharedPtr t, void *data), void *data) { Common::SharedPtr thread(sqthread(v)); if (!thread) return sq_throwerror(v, "failed to get thread"); thread->suspend(); func(thread, data); return -666; } static SQInteger activeController(HSQUIRRELVM v) { warning("TODO: activeController: not implemented"); // TODO: change harcoded mouse sq_pushinteger(v, 1); return 1; } // Sets a timer of duration seconds. // // When the timer is up, method will be executed. // Use this method so that the callback will get saved. // That is, if you set a callback to call method in 30 minutes, play the game for 10 minutes, save and quit; // when you return to the game, it will remember that it needs to wait 20 minutes before calling method. // If the game is paused, all callback timers are paused. // Note, method cannot be code, it must be a defined script or function (otherwise, the game wouldn't be able to save what it needs to do when the timer is up). // .. code-block:: Squirrel // if (actorTalking()) { // addCallback(30, doADance) // Wait another 30 seconds // return //} static SQInteger addCallback(HSQUIRRELVM v) { SQInteger count = sq_gettop(v); float duration; if (SQ_FAILED(sqget(v, 2, duration))) return sq_throwerror(v, "failed to get duration"); HSQOBJECT meth; sq_resetobject(&meth); if (SQ_FAILED(sq_getstackobj(v, 3, &meth)) || !sq_isclosure(meth)) return sq_throwerror(v, "failed to get method"); Common::String methodName; if (SQ_SUCCEEDED(sq_getclosurename(v, 3))) { const SQChar *tmpMethodName; sq_getstring(v, -1, &tmpMethodName); methodName = tmpMethodName; } Common::Array args; for (int i = 4; i <= count; i++) { HSQOBJECT arg; sq_resetobject(&arg); if (SQ_FAILED(sq_getstackobj(v, i, &arg))) return sq_throwerror(v, Common::String::format("failed to get argument %d", i).c_str()); args.push_back(arg); } Common::SharedPtr callback(new Callback(g_twp->_resManager->newCallbackId(), duration, methodName, args)); g_twp->_callbacks.push_back(callback); sqpush(v, callback->getId()); return 1; } // Registers a folder that assets can appear in. // // Only used for development builds where the assets are not bundled up. // Use in the Boot.nut process. // Not necessary for release. static SQInteger addFolder(HSQUIRRELVM v) { return 0; } static void threadFrames(Common::SharedPtr tb, void *data) { int numFrames = *(int *)data; tb->_numFrames = numFrames; } static void threadTime(Common::SharedPtr tb, void *data) { float time = *(float *)data; tb->_waitTime = time; } // When called in a function started with startthread, execution is suspended for count frames. // It is an error to call breakhere in a function that was not started with startthread. // Particularly useful instead of breaktime if you just want to wait 1 frame, since not all machines run at the same speed. // . code-block:: Squirrel // while(isSoundPlaying(soundPhoneBusy)) { // breakhere(5) //} static SQInteger breakhere(HSQUIRRELVM v) { SQObjectType t = sq_gettype(v, 2); if (t == OT_INTEGER) { SQInteger numFrames; if (SQ_FAILED(sqget(v, 2, numFrames))) return sq_throwerror(v, "failed to get numFrames"); return breakfunc(v, threadFrames, &numFrames); } if (t == OT_FLOAT) { float time; if (SQ_FAILED(sqget(v, 2, time))) return sq_throwerror(v, "failed to get time"); return breakfunc(v, threadTime, &time); } return sq_throwerror(v, Common::String::format("failed to get numFrames (wrong type = {%d})", t).c_str()); } // When called in a function started with startthread, execution is suspended for time seconds. // It is an error to call breaktime in a function that was not started with startthread. // // . code-block:: Squirrel // for (local x = 1; x < 4; x += 1) { // playSound(soundPhoneRinging) // breaktime(5.0) // } static SQInteger breaktime(HSQUIRRELVM v) { SQFloat time; if (SQ_FAILED(sq_getfloat(v, 2, &time))) return sq_throwerror(v, "failed to get time"); // it can happen to have a negative time, in this case wait for 1 frame if (time <= 0.f) { int frame = 1; return breakfunc(v, threadFrames, &frame); } return breakfunc(v, threadTime, &time); } template static SQInteger breakwhilecond(HSQUIRRELVM v, Predicate pred, const char *fmt, ...) { va_list va; va_start(va, fmt); Common::String name = Common::String::format(fmt, va); va_end(va); Common::SharedPtr curThread = sqthread(v); if (!curThread) return sq_throwerror(v, "Current thread should be created with startthread"); debugC(kDebugSysScript, "add breakwhilecond name=%s pid=%d, %s", name.c_str(), curThread->getId(), curThread->getName().c_str()); g_twp->_tasks.push_back(Common::SharedPtr(new BreakWhileCond(curThread->getId(), name, Common::move(pred)))); return SQ_SUSPEND_FLAG; } static bool isAnimating(Common::SharedPtr obj) { return obj->_nodeAnim->_anim && !obj->_nodeAnim->_disabled && obj->_animName != obj->getAnimName(STAND_ANIMNAME); } struct ObjAnimating { explicit ObjAnimating(Common::SharedPtr obj) : _obj(obj) {} bool operator()() { return isAnimating(_obj); } private: Common::SharedPtr _obj; }; // When called in a function started with startthread, execution is suspended until animatingItem has completed its animation. // Note, animatingItem can be an actor or an object. // It is an error to call breakwhileanimating in a function that was not started with `startthread`. // // . code-block:: Squirrel // actorFace(ray, FACE_LEFT) // actorCostume(ray, "RayVomit") // actorPlayAnimation(ray, "vomit") // breakwhileanimating(ray) // actorCostume(ray, "RayAnimation") static SQInteger breakwhileanimating(HSQUIRRELVM v) { Common::SharedPtr obj = sqobj(v, 2); if (!obj) return sq_throwerror(v, "failed to get object"); return breakwhilecond(v, ObjAnimating(obj), "breakwhileanimating(%s)", obj->_key.c_str()); } struct CameraMoving { bool operator()() { return g_twp->_camera->isMoving(); } }; // Breaks while a camera is moving. // Once the thread finishes execution, the method will continue running. // It is an error to call breakwhilecamera in a function that was not started with startthread. static SQInteger breakwhilecamera(HSQUIRRELVM v) { return breakwhilecond(v, CameraMoving(), "breakwhilecamera()"); } struct CutsceneRunning { bool operator()() { return g_twp->_cutscene != nullptr; } }; // Breaks while a cutscene is running. // Once the thread finishes execution, the method will continue running. // It is an error to call breakwhilecutscene in a function that was not started with startthread. static SQInteger breakwhilecutscene(HSQUIRRELVM v) { return breakwhilecond(v, CutsceneRunning(), "breakwhilecutscene()"); } struct DialogRunning { bool operator()() { return g_twp->_dialog->getState() != DialogState::None; } }; // Breaks while a dialog is running. // Once the thread finishes execution, the method will continue running. // It is an error to call breakwhiledialog in a function that was not started with startthread. static SQInteger breakwhiledialog(HSQUIRRELVM v) { return breakwhilecond(v, DialogRunning(), "breakwhiledialog()"); } struct InputOff { bool operator()() { return !g_twp->_inputState.getInputActive(); } }; // Breaks while input is not active. // Once the thread finishes execution, the method will continue running. // It is an error to call breakwhileinputoff in a function that was not started with startthread. static SQInteger breakwhileinputoff(HSQUIRRELVM v) { return breakwhilecond(v, InputOff(), "breakwhileinputoff()"); } // Breaks while the thread referenced by threadId is running. // Once the thread finishes execution, the method will continue running. // It is an error to call breakwhilerunning in a function that was not started with startthread. // // . code-block:: Squirrel // local waitTID = 0 // // if ( g.in_flashback && HotelElevator.requestedFloor == 13 ) { // waitTID = startthread(HotelElevator.avoidPenthouse) // breakwhilerunning(waitTID) // } // waitTID = 0 // if (HotelElevator.requestedFloor >= 0) { // // Continue executing other code // } static SQInteger breakwhilerunning(HSQUIRRELVM v) { SQInteger id = 0; if ((sq_gettype(v, 2) == OT_INTEGER) && SQ_FAILED(sqget(v, 2, id))) return sq_throwerror(v, "failed to get id"); debugC(kDebugSysScript, "breakwhilerunning: %lld", id); Common::SharedPtr t = sqthread(id); if (!t) { if (!g_twp->_resManager->isSound(id)) { warning("thread and sound not found: %lld", id); return 0; } return breakwhilecond( v, [id] { return g_twp->_audio->playing(id); }, "breakwhilerunningsound(%d)", id); } return breakwhilecond( v, [id] { return sqthread(id) != nullptr; }, "breakwhilerunning(%d)", id); } struct SomeoneTalking { bool operator()() { return g_twp->isSomeoneTalking(); } }; struct ActorTalking { explicit ActorTalking(Common::SharedPtr obj) : _obj(obj) {} bool operator()() { return _obj->getTalking() && _obj->getTalking()->isEnabled() && _obj->_room == g_twp->_room; } private: Common::SharedPtr _obj; }; // If an actor is specified, breaks until actor has finished talking. // If no actor is specified, breaks until ALL actors have finished talking. // Once talking finishes, the method will continue running. // It is an error to call breakwhiletalking in a function that was not started with startthread. // // . code-block:: Squirrel // while(closeToWillie()) { // local line = randomfrom(lines) // breakwhiletalking(willie) // mumbleLine(willie, line) // breakwhiletalking(willie) // } static SQInteger breakwhiletalking(HSQUIRRELVM v) { SQInteger nArgs = sq_gettop(v); if (nArgs == 1) { return breakwhilecond(v, SomeoneTalking(), "breakwhiletalking(all)"); } if (nArgs == 2) { Common::SharedPtr obj = sqobj(v, 2); if (!obj) return sq_throwerror(v, "failed to get object"); return breakwhilecond(v, ActorTalking(obj), "breakwhiletalking(%s)", obj->_name.c_str()); } return sq_throwerror(v, "Invalid number of arguments for breakwhiletalking"); } struct ActorWalking { explicit ActorWalking(Common::SharedPtr obj) : _obj(obj) {} bool operator()() { return _obj->getWalkTo() && _obj->getWalkTo()->isEnabled(); } private: Common::SharedPtr _obj; }; // If an actor is specified, breaks until actor has finished walking. // Once arrived at destination, the method will continue running. // It is an error to call breakwhilewalking in a function that was not started with `startthread`. // // . code-block:: Squirrel // startthread(@(){ // actorWalkTo(currentActor, Nickel.copyTron) // breakwhilewalking(currentActor) // pushSentence(VERB_USE, nickel, Nickel.copyTron) //}) static SQInteger breakwhilewalking(HSQUIRRELVM v) { Common::SharedPtr obj = sqobj(v, 2); if (!obj) return sq_throwerror(v, "failed to get object"); return breakwhilecond(v, ActorWalking(obj), "breakwhilewalking(%s)", obj->_name.c_str()); } struct SoundPlaying { explicit SoundPlaying(int soundId) : _soundId(soundId) {} bool operator()() { return g_twp->_audio->playing(_soundId); } private: int _soundId; }; // Breaks until specified sound has finished playing. // Once sound finishes, the method will continue running. static SQInteger breakwhilesound(HSQUIRRELVM v) { SQInteger soundId = 0; if (SQ_FAILED(sqget(v, 2, soundId))) return sq_throwerror(v, "failed to get sound"); return breakwhilecond(v, SoundPlaying(soundId), "breakwhilesound(%d)", soundId); } static SQInteger cutscene(HSQUIRRELVM v) { HSQUIRRELVM vm = g_twp->getVm(); SQInteger nArgs = sq_gettop(v); HSQOBJECT envObj; sq_resetobject(&envObj); if (SQ_FAILED(sq_getstackobj(v, 1, &envObj))) return sq_throwerror(v, "failed to get environment from stack"); // create thread and store it on the stack sq_newthread(vm, 1024); HSQOBJECT threadObj; sq_resetobject(&threadObj); if (SQ_FAILED(sq_getstackobj(vm, -1, &threadObj))) return sq_throwerror(v, "failed to get coroutine thread from stack"); // get the closure HSQOBJECT closure; sq_resetobject(&closure); if (SQ_FAILED(sq_getstackobj(v, 2, &closure))) return sq_throwerror(v, "failed to get cutscene closure"); // get the cutscene override closure HSQOBJECT closureOverride; sq_resetobject(&closureOverride); if (nArgs == 3) { if (SQ_FAILED(sq_getstackobj(v, 3, &closureOverride))) return sq_throwerror(v, "failed to get cutscene override closure"); } Common::SharedPtr parentThread = sqthread(v); Common::String cutsceneName = Common::String::format("%s (%lld)", _stringval(_closure(closure)->_function->_sourcename), _closure(closure)->_function->_lineinfos->_line); Common::SharedPtr cutscene(new Cutscene(cutsceneName, parentThread->getId(), threadObj, closure, closureOverride, envObj)); g_twp->_cutscene = cutscene; // call the closure in the thread cutscene->update(0.f); return breakwhilecutscene(v); } static SQInteger cutsceneOverride(HSQUIRRELVM v) { debugC(kDebugSysScript, "cutsceneOverride"); g_twp->_cutscene->cutsceneOverride(); return 0; } static SQInteger dumpvar(HSQUIRRELVM v) { warning("TODO: dumpvar: not implemented"); return 0; } static SQInteger exCommand(HSQUIRRELVM v) { SQInteger cmd; if (SQ_FAILED(sqget(v, 2, cmd))) return sq_throwerror(v, "Failed to get command"); switch (cmd) { case EX_AUTOSAVE_STATE: { SQInteger enabled; if (SQ_FAILED(sqget(v, 3, enabled))) return sq_throwerror(v, "Failed to get enabled"); g_twp->_saveGameManager->_autoSave = enabled != 0; } break; case EX_AUTOSAVE: { if (g_twp->_saveGameManager->_autoSave && g_twp->_saveGameManager->_allowSaveGame) { g_twp->saveGameState(0, "", true); } } break; case EX_ALLOW_SAVEGAMES: { SQInteger enabled; if (SQ_FAILED(sqget(v, 3, enabled))) return sq_throwerror(v, "Failed to get enabled"); g_twp->_saveGameManager->_allowSaveGame = enabled != 0; } break; case EX_POP_CHARACTER_SELECTION: // seems not to be used warning("exCommand EX_POP_CHARACTER_SELECTION: not implemented"); break; case EX_CAMERA_TRACKING: warning("TODO: exCommand EX_CAMERA_TRACKING: not implemented"); break; case EX_BUTTON_HOVER_SOUND: { Common::SharedPtr sound = sqsounddef(v, 3); if (!sound) return sq_throwerror(v, "failed to get sound for EX_BUTTON_HOVER_SOUND"); g_twp->_audio->_soundHover = sound; } break; case EX_RESTART: warning("TODO: exCommand EX_RESTART: not implemented"); break; case EX_IDLE_TIME: // used in demo only in watchForIdle warning("TODO: exCommand EX_IDLE_TIME: not implemented"); break; case EX_DISABLE_SAVESYSTEM: // seems not to be used warning("exCommand EX_DISABLE_SAVESYSTEM: not implemented"); break; case EX_SHOW_OPTIONS: g_twp->openMainMenuDialog(); break; case EX_OPTIONS_MUSIC: warning("TODO: exCommand EX_OPTIONS_MUSIC: not implemented"); break; case EX_FORCE_TALKIE_TEXT: // seems not to be used warning("exCommand EX_FORCE_TALKIE_TEXT: not implemented"); break; case EX_SCREEN_SIZE: // only on mobile, used in Bridge.nut: objectScale(text, (exCommand(EX_SCREEN_SIZE) == 3) ? 0.75 : 0.5) sqpush(v, 0); return 1; break; default: warning("exCommand(%lld) not implemented", cmd); break; } return 0; } // Returns how long (in seconds) the game has been played for in total (not just this session). //// Saved when the game is saved. // Also used for testing. // The value is a float, so 1 = 1 second, 0.5 = half a second. // // . code-block:: Squirrel // if (gameTime() > (time+testerTronTimeOut)) { // Do something // } static SQInteger gameTime(HSQUIRRELVM v) { sqpush(v, g_twp->_time); return 1; } static SQInteger sysInclude(HSQUIRRELVM v) { const SQChar *filename; if (SQ_FAILED(sqget(v, 2, filename))) { return sq_throwerror(v, "failed to get filename"); } g_twp->execNutEntry(v, filename); return 0; } static SQInteger inputController(HSQUIRRELVM v) { warning("TODO: inputController: not implemented"); return 0; } static SQInteger inputHUD(HSQUIRRELVM v) { bool on; if (SQ_FAILED(sqget(v, 2, on))) return sq_throwerror(v, "failed to get on"); g_twp->_inputState.setInputHUD(on); return 0; } static SQInteger inputOff(HSQUIRRELVM v) { if (!g_twp->_cutscene || g_twp->_cutscene->isStopped()) { g_twp->_inputState.setInputActive(false); g_twp->_inputState.setShowCursor(false); } return 0; } static SQInteger inputOn(HSQUIRRELVM v) { Common::SharedPtr scene(g_twp->_cutscene); if (!scene || scene->isStopped()) { g_twp->_inputState.setInputActive(true); g_twp->_inputState.setShowCursor(true); } else { int state = g_twp->_inputState.getState(); state |= UI_INPUT_ON; state &= (~UI_INPUT_OFF); state |= UI_CURSOR_ON; state &= (~UI_CURSOR_OFF); scene->setInputState((InputStateFlag)state); scene->setShowCursor(true); } return 0; } static SQInteger inputSilentOff(HSQUIRRELVM v) { g_twp->_inputState.setInputActive(false); return 0; } static SQInteger sysInputState(HSQUIRRELVM v) { SQInteger numArgs = sq_gettop(v); if (numArgs == 1) { int state = (int)g_twp->_inputState.getState(); sqpush(v, state); return 1; } if (numArgs == 2) { SQInteger state; if (SQ_FAILED(sqget(v, 2, state))) return sq_throwerror(v, "failed to get state"); g_twp->_inputState.setState((InputStateFlag)state); return 0; } return sq_throwerror(v, Common::String::format("inputState with %lld arguments not implemented", numArgs).c_str()); } static SQInteger inputVerbs(HSQUIRRELVM v) { bool on; if (SQ_FAILED(sqget(v, 2, on))) return sq_throwerror(v, "failed to get isActive"); debugC(kDebugSysScript, "inputVerbs: %s", on ? "yes" : "no"); g_twp->_inputState.setInputVerbsActive(on); return 1; } static SQInteger isInputOn(HSQUIRRELVM v) { bool isActive = g_twp->_inputState.getInputActive(); sqpush(v, isActive); return 1; } static SQInteger logEvent(HSQUIRRELVM v) { SQInteger numArgs = sq_gettop(v); Common::String msg, event; if (SQ_FAILED(sqget(v, 2, event))) return sq_throwerror(v, "failed to get event"); if (numArgs == 3) { if (SQ_FAILED(sqget(v, 3, event))) return sq_throwerror(v, "failed to get message"); msg = event + ": " + msg; } debugC(kDebugSysScript, "%s", msg.c_str()); return 0; } // Like a print statement, but gets sent to the output log file instead. // Useful for testing. static SQInteger logInfo(HSQUIRRELVM v) { Common::String msg; if (SQ_FAILED(sqget(v, 2, msg))) return sq_throwerror(v, "failed to get message"); debugC(kDebugSysScript, "%s", msg.c_str()); return 0; } // Sends a warning message to the output log file. static SQInteger logWarning(HSQUIRRELVM v) { Common::String msg; if (SQ_FAILED(sqget(v, 2, msg))) return sq_throwerror(v, "failed to get message"); warning("%s", msg.c_str()); return 0; } // Returns game time in milliseconds. // Based on when the machine is booted and runs all the time (not paused or saved). // See also gameTime, which is in seconds. static SQInteger microTime(HSQUIRRELVM v) { sqpush(v, g_twp->_time * 1000.0f); return 1; } static SQInteger moveCursorTo(HSQUIRRELVM v) { SQInteger x, y; if (SQ_FAILED(sqget(v, 2, x))) return sq_throwerror(v, "Failed to get x"); if (SQ_FAILED(sqget(v, 3, y))) return sq_throwerror(v, "Failed to get y"); float t; if (SQ_FAILED(sqget(v, 4, t))) return sq_throwerror(v, "Failed to get time"); Math::Vector2d pos; if (g_twp->_room) { pos = g_twp->roomToScreen(Math::Vector2d(x, y)); } else { pos = g_twp->screenToWin(Math::Vector2d(x, y)); } pos.setX(CLIP(pos.getX(), 0.f, (float)SCREEN_WIDTH)); pos.setY(CLIP(pos.getY(), 0.f, (float)SCREEN_HEIGHT)); pos = g_twp->screenToWin(pos); g_twp->_moveCursorTo = Common::ScopedPtr(new MoveCursorTo(pos, t)); return 0; } // removeCallback(id: int) remove the given callback static SQInteger removeCallback(HSQUIRRELVM v) { SQInteger id = 0; if (SQ_FAILED(sqget(v, 2, id))) return sq_throwerror(v, "failed to get callback"); for (size_t i = 0; i < g_twp->_callbacks.size(); i++) { Common::SharedPtr cb = g_twp->_callbacks[i]; if (cb->getId() == id) { cb->remove(); return 0; } } return 0; } static SQInteger startthread(HSQUIRRELVM v) { return _startthread(v, false); } static SQInteger startglobalthread(HSQUIRRELVM v) { return _startthread(v, true); } // Stops a thread specified by threadid. // // If the thread is not running, the command does nothing. // // See also: // * `startthread` // * `startglobalthread` static SQInteger stopthread(HSQUIRRELVM v) { SQInteger id = 0; if (SQ_FAILED(sqget(v, 2, id))) { sqpush(v, 0); return 1; } Common::SharedPtr t = sqthread(id); if (t) { t->stop(); } sqpush(v, 0); return 1; } // Returns the thread ID of the currently running script/thread. // // If no thread is running, it will return 0. // // . code-block:: Squirrel // Phone <- // { // phoneRingingTID = 0 // script phoneRinging(number) { // phoneRingingTID = threadid() // ... // } // function clickedButton(...) { // if (!phoneRingingTID) { // ... // } // } // } static SQInteger threadid(HSQUIRRELVM v) { Common::SharedPtr t = sqthread(v); if (t) sqpush(v, t->getId()); else sqpush(v, 0); return 1; } // Specify whether a thread should be pauseable or not. // If a thread is not pauseable, it won't be possible to pause this thread. static SQInteger threadpauseable(HSQUIRRELVM v) { Common::SharedPtr t = sqthread(v, 2); if (!t) return sq_throwerror(v, "failed to get thread"); SQInteger pauseable = 0; if (SQ_FAILED(sqget(v, 3, pauseable))) return sq_throwerror(v, "failed to get pauseable"); t->_pauseable = pauseable != 0; return 0; } void sqgame_register_syslib(HSQUIRRELVM v) { regFunc(v, startthread, _SC("startthread")); regFunc(v, startglobalthread, _SC("startglobalthread")); regFunc(v, breaktime, _SC("breaktime")); regFunc(v, activeController, _SC("activeController")); regFunc(v, addCallback, _SC("addCallback")); regFunc(v, addFolder, _SC("addFolder")); regFunc(v, breakhere, _SC("breakhere")); regFunc(v, breaktime, _SC("breaktime")); regFunc(v, breakwhileanimating, _SC("breakwhileanimating")); regFunc(v, breakwhilecamera, _SC("breakwhilecamera")); regFunc(v, breakwhilecutscene, _SC("breakwhilecutscene")); regFunc(v, breakwhiledialog, _SC("breakwhiledialog")); regFunc(v, breakwhileinputoff, _SC("breakwhileinputoff")); regFunc(v, breakwhilerunning, _SC("breakwhilerunning")); regFunc(v, breakwhilesound, _SC("breakwhilesound")); regFunc(v, breakwhiletalking, _SC("breakwhiletalking")); regFunc(v, breakwhilewalking, _SC("breakwhilewalking")); regFunc(v, cutscene, _SC("cutscene")); regFunc(v, cutsceneOverride, _SC("cutsceneOverride")); regFunc(v, dumpvar, _SC("dumpvar")); regFunc(v, exCommand, _SC("exCommand")); regFunc(v, gameTime, _SC("gameTime")); regFunc(v, sysInclude, _SC("include")); regFunc(v, inputController, _SC("inputController")); regFunc(v, inputHUD, _SC("inputHUD")); regFunc(v, inputOff, _SC("inputOff")); regFunc(v, inputOn, _SC("inputOn")); regFunc(v, inputSilentOff, _SC("inputSilentOff")); regFunc(v, sysInputState, _SC("inputState")); regFunc(v, inputVerbs, _SC("inputVerbs")); regFunc(v, isInputOn, _SC("isInputOn")); regFunc(v, logEvent, _SC("logEvent")); regFunc(v, logInfo, _SC("logInfo")); regFunc(v, logWarning, _SC("logWarning")); regFunc(v, microTime, _SC("microTime")); regFunc(v, moveCursorTo, _SC("moveCursorTo")); regFunc(v, removeCallback, _SC("removeCallback")); regFunc(v, startglobalthread, _SC("startglobalthread")); regFunc(v, startthread, _SC("startthread")); regFunc(v, stopthread, _SC("stopthread")); regFunc(v, threadid, _SC("threadid")); regFunc(v, threadpauseable, _SC("threadpauseable")); } static void regConst(HSQUIRRELVM v, const char *name, SQInteger value) { SQObject obj = sqtoobj(v, value); _table(v->_roottable)->NewSlot(sqtoobj(v, name), SQObjectPtr(obj)); } void sqgame_register_constants(HSQUIRRELVM v) { regConst(v, "ALL", ALL); regConst(v, "HERE", HERE); regConst(v, "GONE", GONE); regConst(v, "OFF", OFF); regConst(v, "ON", ON); regConst(v, "FULL", FULL); regConst(v, "EMPTY", EMPTY); regConst(v, "OPEN", OPEN); regConst(v, "CLOSED", CLOSED); regConst(v, "FALSE", FALSE); regConst(v, "TRUE", TRUE); regConst(v, "MOUSE", MOUSE); regConst(v, "CONTROLLER", CONTROLLER); regConst(v, "DIRECTDRIVE", DIRECTDRIVE); regConst(v, "TOUCH", TOUCH); regConst(v, "REMOTE", REMOTE); regConst(v, "FADE_IN", FADE_IN); regConst(v, "FADE_OUT", FADE_OUT); regConst(v, "FADE_WOBBLE", FADE_WOBBLE); regConst(v, "FADE_WOBBLE_TO_SEPIA", FADE_WOBBLE_TO_SEPIA); regConst(v, "FACE_FRONT", static_cast(Facing::FACE_FRONT)); regConst(v, "FACE_BACK", static_cast(Facing::FACE_BACK)); regConst(v, "FACE_LEFT", static_cast(Facing::FACE_LEFT)); regConst(v, "FACE_RIGHT", static_cast(Facing::FACE_RIGHT)); regConst(v, "FACE_FLIP", FACE_FLIP); regConst(v, "DIR_FRONT", DIR_FRONT); regConst(v, "DIR_BACK", DIR_BACK); regConst(v, "DIR_LEFT", DIR_LEFT); regConst(v, "DIR_RIGHT", DIR_RIGHT); regConst(v, "LINEAR", LINEAR); regConst(v, "EASE_IN", EASE_IN); regConst(v, "EASE_INOUT", EASE_INOUT); regConst(v, "EASE_OUT", EASE_OUT); regConst(v, "SLOW_EASE_IN", SLOW_EASE_IN); regConst(v, "SLOW_EASE_OUT", SLOW_EASE_OUT); regConst(v, "LOOPING", LOOPING); regConst(v, "SWING", SWING); regConst(v, "STOP_LOOPING", STOP_LOOPING); regConst(v, "ALIGN_LEFT", ALIGN_LEFT); regConst(v, "ALIGN_CENTER", ALIGN_CENTER); regConst(v, "ALIGN_RIGHT", ALIGN_RIGHT); regConst(v, "ALIGN_TOP", ALIGN_TOP); regConst(v, "ALIGN_BOTTOM", ALIGN_BOTTOM); regConst(v, "LESS_SPACING", LESS_SPACING); regConst(v, "EX_ALLOW_SAVEGAMES", EX_ALLOW_SAVEGAMES); regConst(v, "EX_POP_CHARACTER_SELECTION", EX_POP_CHARACTER_SELECTION); regConst(v, "EX_CAMERA_TRACKING", EX_CAMERA_TRACKING); regConst(v, "EX_BUTTON_HOVER_SOUND", EX_BUTTON_HOVER_SOUND); regConst(v, "EX_RESTART", EX_RESTART); regConst(v, "EX_IDLE_TIME", EX_IDLE_TIME); regConst(v, "EX_AUTOSAVE", EX_AUTOSAVE); regConst(v, "EX_AUTOSAVE_STATE", EX_AUTOSAVE_STATE); regConst(v, "EX_DISABLE_SAVESYSTEM", EX_DISABLE_SAVESYSTEM); regConst(v, "EX_SHOW_OPTIONS", EX_SHOW_OPTIONS); regConst(v, "EX_OPTIONS_MUSIC", EX_OPTIONS_MUSIC); regConst(v, "EX_FORCE_TALKIE_TEXT", EX_FORCE_TALKIE_TEXT); regConst(v, "EX_SCREEN_SIZE", EX_SCREEN_SIZE); regConst(v, "GRASS_BACKANDFORTH", GRASS_BACKANDFORTH); regConst(v, "EFFECT_NONE", EFFECT_NONE); regConst(v, "DOOR", DOOR); regConst(v, "DOOR_LEFT", DOOR_LEFT); regConst(v, "DOOR_RIGHT", DOOR_RIGHT); regConst(v, "DOOR_BACK", DOOR_BACK); regConst(v, "DOOR_FRONT", DOOR_FRONT); regConst(v, "FAR_LOOK", FAR_LOOK); regConst(v, "USE_WITH", USE_WITH); regConst(v, "USE_ON", USE_ON); regConst(v, "USE_IN", USE_IN); regConst(v, "GIVEABLE", GIVEABLE); regConst(v, "TALKABLE", TALKABLE); regConst(v, "IMMEDIATE", IMMEDIATE); regConst(v, "FEMALE", FEMALE); regConst(v, "MALE", MALE); regConst(v, "PERSON", PERSON); regConst(v, "REACH_HIGH", REACH_HIGH); regConst(v, "REACH_MED", REACH_MED); regConst(v, "REACH_LOW", REACH_LOW); regConst(v, "REACH_NONE", REACH_NONE); regConst(v, "VERB_WALKTO", VERB_WALKTO); regConst(v, "VERB_LOOKAT", VERB_LOOKAT); regConst(v, "VERB_TALKTO", VERB_TALKTO); regConst(v, "VERB_PICKUP", VERB_PICKUP); regConst(v, "VERB_OPEN", VERB_OPEN); regConst(v, "VERB_CLOSE", VERB_CLOSE); regConst(v, "VERB_PUSH", VERB_PUSH); regConst(v, "VERB_PULL", VERB_PULL); regConst(v, "VERB_GIVE", VERB_GIVE); regConst(v, "VERB_USE", VERB_USE); regConst(v, "VERB_DIALOG", VERB_DIALOG); regConst(v, "VERBFLAG_INSTANT", VERBFLAG_INSTANT); regConst(v, "NO", TWP_NO); regConst(v, "YES", TWP_YES); regConst(v, "TEMP_UNSELECTABLE", TEMP_UNSELECTABLE); regConst(v, "TEMP_SELECTABLE", TEMP_SELECTABLE); regConst(v, "MAC", MAC); regConst(v, "WIN", WIN); regConst(v, "LINUX", LINUX); regConst(v, "XBOX", XBOX); regConst(v, "IOS", IOS); regConst(v, "ANDROID", ANDROID); regConst(v, "SWITCH", SWITCH); regConst(v, "PS4", PS4); regConst(v, "EFFECT_NONE", EFFECT_NONE); regConst(v, "EFFECT_SEPIA", EFFECT_SEPIA); regConst(v, "EFFECT_EGA", EFFECT_EGA); regConst(v, "EFFECT_VHS", EFFECT_VHS); regConst(v, "EFFECT_GHOST", EFFECT_GHOST); regConst(v, "EFFECT_BLACKANDWHITE", EFFECT_BLACKANDWHITE); regConst(v, "UI_INPUT_ON", UI_INPUT_ON); regConst(v, "UI_INPUT_OFF", UI_INPUT_OFF); regConst(v, "UI_VERBS_ON", UI_VERBS_ON); regConst(v, "UI_VERBS_OFF", UI_VERBS_OFF); regConst(v, "UI_HUDOBJECTS_ON", UI_HUDOBJECTS_ON); regConst(v, "UI_HUDOBJECTS_OFF", UI_HUDOBJECTS_OFF); regConst(v, "UI_CURSOR_ON", UI_CURSOR_ON); regConst(v, "UI_CURSOR_OFF", UI_CURSOR_OFF); regConst(v, "KEY_UP", KEY_UP); regConst(v, "KEY_RIGHT", KEY_RIGHT); regConst(v, "KEY_DOWN", KEY_DOWN); regConst(v, "KEY_LEFT", KEY_LEFT); regConst(v, "KEY_PAD1", KEY_PAD1); regConst(v, "KEY_PAD2", KEY_PAD2); regConst(v, "KEY_PAD3", KEY_PAD3); regConst(v, "KEY_PAD4", KEY_PAD4); regConst(v, "KEY_PAD5", KEY_PAD5); regConst(v, "KEY_PAD6", KEY_PAD6); regConst(v, "KEY_PAD7", KEY_PAD7); regConst(v, "KEY_PAD8", KEY_PAD8); regConst(v, "KEY_PAD9", KEY_PAD9); regConst(v, "KEY_ESCAPE", KEY_ESCAPE); regConst(v, "KEY_TAB", KEY_TAB); regConst(v, "KEY_RETURN", KEY_RETURN); regConst(v, "KEY_BACKSPACE", KEY_BACKSPACE); regConst(v, "KEY_SPACE", KEY_SPACE); regConst(v, "KEY_A", KEY_A); regConst(v, "KEY_B", KEY_B); regConst(v, "KEY_C", KEY_C); regConst(v, "KEY_D", KEY_D); regConst(v, "KEY_E", KEY_E); regConst(v, "KEY_F", KEY_F); regConst(v, "KEY_G", KEY_G); regConst(v, "KEY_H", KEY_H); regConst(v, "KEY_I", KEY_I); regConst(v, "KEY_J", KEY_J); regConst(v, "KEY_K", KEY_K); regConst(v, "KEY_L", KEY_L); regConst(v, "KEY_M", KEY_M); regConst(v, "KEY_N", KEY_N); regConst(v, "KEY_O", KEY_O); regConst(v, "KEY_P", KEY_P); regConst(v, "KEY_Q", KEY_Q); regConst(v, "KEY_R", KEY_R); regConst(v, "KEY_S", KEY_S); regConst(v, "KEY_T", KEY_T); regConst(v, "KEY_U", KEY_U); regConst(v, "KEY_V", KEY_V); regConst(v, "KEY_W", KEY_W); regConst(v, "KEY_X", KEY_X); regConst(v, "KEY_Y", KEY_Y); regConst(v, "KEY_Z", KEY_Z); regConst(v, "KEY_0", KEY_0); regConst(v, "KEY_1", KEY_1); regConst(v, "KEY_2", KEY_2); regConst(v, "KEY_3", KEY_3); regConst(v, "KEY_4", KEY_4); regConst(v, "KEY_5", KEY_5); regConst(v, "KEY_6", KEY_6); regConst(v, "KEY_7", KEY_7); regConst(v, "KEY_8", KEY_8); regConst(v, "KEY_9", KEY_9); regConst(v, "KEY_F1", KEY_F1); regConst(v, "KEY_F2", KEY_F2); regConst(v, "KEY_F3", KEY_F3); regConst(v, "KEY_F4", KEY_F4); regConst(v, "KEY_F5", KEY_F5); regConst(v, "KEY_F6", KEY_F6); regConst(v, "KEY_F7", KEY_F7); regConst(v, "KEY_F8", KEY_F8); regConst(v, "KEY_F9", KEY_F9); regConst(v, "KEY_F10", KEY_F10); regConst(v, "KEY_F11", KEY_F11); regConst(v, "KEY_F12", KEY_F12); regConst(v, "BUTTON_A", BUTTON_A); regConst(v, "BUTTON_B", BUTTON_B); regConst(v, "BUTTON_X", BUTTON_X); regConst(v, "BUTTON_Y", BUTTON_Y); regConst(v, "BUTTON_START", BUTTON_START); regConst(v, "BUTTON_BACK", BUTTON_BACK); regConst(v, "BUTTON_MOUSE_LEFT", BUTTON_MOUSE_LEFT); regConst(v, "BUTTON_MOUSE_RIGHT", BUTTON_MOUSE_RIGHT); regConst(v, "WAITING_FOR_CHOICE", 2); // convert ScummVM platform to the one expected SQInteger platform = MAC; const char *platformsSrc[] = {"mac", "windows", "linux", "xbox", "ios", "android", "switch"}; const SQInteger platformsDst[] = {MAC, WIN, LINUX, XBOX, IOS, ANDROID, SWITCH}; Common::String platformName(ConfMan.get("platform")); for (int i = 0; i < ARRAYSIZE(platformsSrc); i++) { if (platformName == platformsSrc[i]) { platform = platformsDst[i]; } } regConst(v, "PLATFORM", platform); } } // namespace Twp