[Scummvm-git-logs] scummvm master -> 979207a3afb81f8ccebc5ca93c25a843bffe69f8

scemino noreply at scummvm.org
Tue Dec 10 20:56:39 UTC 2024


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
979207a3af TWP: Fix cutscene regressions


Commit: 979207a3afb81f8ccebc5ca93c25a843bffe69f8
    https://github.com/scummvm/scummvm/commit/979207a3afb81f8ccebc5ca93c25a843bffe69f8
Author: scemino (scemino74 at gmail.com)
Date: 2024-12-10T21:52:42+01:00

Commit Message:
TWP: Fix cutscene regressions

Fix #15574, #15576 and #15577

Changed paths:
    engines/twp/debugtools.cpp
    engines/twp/genlib.cpp
    engines/twp/squtil.cpp
    engines/twp/squtil.h
    engines/twp/syslib.cpp
    engines/twp/task.h
    engines/twp/thread.cpp
    engines/twp/thread.h
    engines/twp/twp.cpp
    engines/twp/twp.h


diff --git a/engines/twp/debugtools.cpp b/engines/twp/debugtools.cpp
index ddd2c77d091..bbcfc2a2b0f 100644
--- a/engines/twp/debugtools.cpp
+++ b/engines/twp/debugtools.cpp
@@ -90,7 +90,7 @@ static void drawThreads() {
 				ImGui::TableNextColumn();
 				ImGui::Text("%-56s", thread->getName().c_str());
 				ImGui::TableNextColumn();
-				if (thread->getId() != g_twp->_cutscene.id) {
+				if(!g_twp->_cutscene || (thread->getId() != g_twp->_cutscene->getId())) {
 					ImGui::Text("%-6s", thread->isGlobal() ? "global" : "local");
 				} else {
 					ImGui::Text("%-6s", "cutscene");
@@ -369,8 +369,8 @@ static void drawGeneral() {
 	ImGui::Text("%lld", size);
 	ImGui::TextColored(gray, "Cutscene:");
 	ImGui::SameLine();
-	if (g_twp->_cutscene.id) {
-		Common::SharedPtr<Thread> cutscene(sqthread(g_twp->_cutscene.id));
+	if (g_twp->_cutscene) {
+		Common::SharedPtr<ThreadBase> cutscene(sqthread(g_twp->_cutscene->getId()));
 		ImGui::Text("%s", cutscene->getName().c_str());
 	} else {
 		ImGui::Text("no");
diff --git a/engines/twp/genlib.cpp b/engines/twp/genlib.cpp
index 416926a4dfc..a943ea7e69c 100644
--- a/engines/twp/genlib.cpp
+++ b/engines/twp/genlib.cpp
@@ -460,7 +460,7 @@ static SQInteger getPrivatePref(HSQUIRRELVM v) {
 }
 
 static SQInteger incutscene(HSQUIRRELVM v) {
-	sqpush(v, g_twp->_cutscene.id != 0);
+	sqpush(v, g_twp->_cutscene != nullptr);
 	return 1;
 }
 
diff --git a/engines/twp/squtil.cpp b/engines/twp/squtil.cpp
index 6bf2485cafa..c99d4dbd202 100644
--- a/engines/twp/squtil.cpp
+++ b/engines/twp/squtil.cpp
@@ -361,16 +361,22 @@ void sqexec(HSQUIRRELVM v, const char *code, const char *filename) {
 	sq_settop(v, top);
 }
 
-Common::SharedPtr<Thread> sqthread(HSQUIRRELVM v, int i) {
+Common::SharedPtr<ThreadBase> sqthread(HSQUIRRELVM v, int i) {
 	SQInteger id;
 	if (SQ_SUCCEEDED(sqget(v, i, id)))
 		return sqthread(id);
 	return nullptr;
 }
 
-Common::SharedPtr<Thread> sqthread(int id) {
+Common::SharedPtr<ThreadBase> sqthread(int id) {
+	if (g_twp->_cutscene) {
+		if (g_twp->_cutscene->getId() == id) {
+			return g_twp->_cutscene;
+		}
+	}
+
 	for (size_t i = 0; i < g_twp->_threads.size(); i++) {
-		Common::SharedPtr<Thread> t = g_twp->_threads[i];
+		Common::SharedPtr<ThreadBase> t = g_twp->_threads[i];
 		if (t->getId() == id) {
 			return t;
 		}
@@ -400,7 +406,7 @@ Light *sqlight(HSQUIRRELVM v, int i) {
 
 struct GetThread {
 	explicit GetThread(HSQUIRRELVM v) : _v(v) {}
-	bool operator()(Common::SharedPtr<Thread> t) {
+	bool operator()(Common::SharedPtr<ThreadBase> t) {
 		return t->getThread() == _v;
 	}
 
@@ -408,7 +414,13 @@ private:
 	HSQUIRRELVM _v;
 };
 
-Common::SharedPtr<Thread> sqthread(HSQUIRRELVM v) {
+Common::SharedPtr<ThreadBase> sqthread(HSQUIRRELVM v) {
+	if (g_twp->_cutscene) {
+		if (g_twp->_cutscene->getThread() == v) {
+			return g_twp->_cutscene;
+		}
+	}
+
 	auto it = Common::find_if(g_twp->_threads.begin(), g_twp->_threads.end(), GetThread(v));
 	if (it != g_twp->_threads.end())
 		return *it;
diff --git a/engines/twp/squtil.h b/engines/twp/squtil.h
index b6f9dc80e68..d41b7c1bf07 100644
--- a/engines/twp/squtil.h
+++ b/engines/twp/squtil.h
@@ -179,9 +179,9 @@ Common::SharedPtr<Object> sqactor(HSQOBJECT table);
 Common::SharedPtr<Object> sqactor(HSQUIRRELVM v, int i);
 Common::SharedPtr<SoundDefinition> sqsounddef(HSQUIRRELVM v, int i);
 Common::SharedPtr<SoundDefinition> sqsounddef(int id);
-Common::SharedPtr<Thread> sqthread(HSQUIRRELVM v);
-Common::SharedPtr<Thread> sqthread(HSQUIRRELVM v, int id);
-Common::SharedPtr<Thread> sqthread(int id);
+Common::SharedPtr<ThreadBase> sqthread(HSQUIRRELVM v);
+Common::SharedPtr<ThreadBase> sqthread(HSQUIRRELVM v, int id);
+Common::SharedPtr<ThreadBase> sqthread(int id);
 Light *sqlight(int id);
 Light *sqlight(HSQUIRRELVM v, int i);
 
diff --git a/engines/twp/syslib.cpp b/engines/twp/syslib.cpp
index 7f3310dd70d..fd898f7c671 100644
--- a/engines/twp/syslib.cpp
+++ b/engines/twp/syslib.cpp
@@ -93,8 +93,8 @@ static SQInteger _startthread(HSQUIRRELVM v, bool global) {
 	return 1;
 }
 
-static SQInteger breakfunc(HSQUIRRELVM v, void func(Common::SharedPtr<Thread> t, void *data), void *data) {
-	Common::SharedPtr<Thread> thread(sqthread(v));
+static SQInteger breakfunc(HSQUIRRELVM v, void func(Common::SharedPtr<ThreadBase> t, void *data), void *data) {
+	Common::SharedPtr<ThreadBase> thread(sqthread(v));
 	if (!thread)
 		return sq_throwerror(v, "failed to get thread");
 	thread->suspend();
@@ -164,12 +164,12 @@ static SQInteger addFolder(HSQUIRRELVM v) {
 	return 0;
 }
 
-static void threadFrames(Common::SharedPtr<Thread> tb, void *data) {
+static void threadFrames(Common::SharedPtr<ThreadBase> tb, void *data) {
 	int numFrames = *(int *)data;
 	tb->_numFrames = numFrames;
 }
 
-static void threadTime(Common::SharedPtr<Thread> tb, void *data) {
+static void threadTime(Common::SharedPtr<ThreadBase> tb, void *data) {
 	float time = *(float *)data;
 	tb->_waitTime = time;
 }
@@ -224,7 +224,7 @@ static SQInteger breakwhilecond(HSQUIRRELVM v, Predicate pred, const char *fmt,
 	Common::String name = Common::String::format(fmt, va);
 	va_end(va);
 
-	Common::SharedPtr<Thread> curThread = sqthread(v);
+	Common::SharedPtr<ThreadBase> curThread = sqthread(v);
 	if (!curThread)
 		return sq_throwerror(v, "Current thread should be created with startthread");
 
@@ -279,7 +279,7 @@ static SQInteger breakwhilecamera(HSQUIRRELVM v) {
 
 struct CutsceneRunning {
 	bool operator()() {
-		return g_twp->_cutscene.id != 0;
+		return g_twp->_cutscene != nullptr;
 	}
 };
 
@@ -337,7 +337,7 @@ static SQInteger breakwhilerunning(HSQUIRRELVM v) {
 		return sq_throwerror(v, "failed to get id");
 	debugC(kDebugSysScript, "breakwhilerunning: %lld", id);
 
-	Common::SharedPtr<Thread> t = sqthread(id);
+	Common::SharedPtr<ThreadBase> t = sqthread(id);
 	if (!t) {
 		if (!g_twp->_resManager->isSound(id)) {
 			warning("thread and sound not found: %lld", id);
@@ -471,35 +471,21 @@ static SQInteger cutscene(HSQUIRRELVM v) {
 		if (SQ_FAILED(sq_getstackobj(v, 3, &closureOverride)))
 			return sq_throwerror(v, "failed to get cutscene override closure");
 	}
-	sq_addref(v, &closureOverride);
 
-	Common::SharedPtr<Thread> parentThread = sqthread(v);
+	Common::SharedPtr<ThreadBase> parentThread = sqthread(v);
 	Common::String cutsceneName = Common::String::format("%s (%lld)", _stringval(_closure(closure)->_function->_sourcename), _closure(closure)->_function->_lineinfos->_line);
-	Common::SharedPtr<Thread> cutscene(new Thread(cutsceneName, true, threadObj, envObj, closure, {}));
-	g_twp->_threads.push_back(cutscene);
-	if (!g_twp->_cutscene.id) {
-		g_twp->_cutscene.inputState = g_twp->_inputState.getState();
-		g_twp->_cutscene.showCursor = g_twp->_inputState.getShowCursor();
-		g_twp->_inputState.setInputActive(false);
-		g_twp->_inputState.setShowCursor(false);
-	}
-	g_twp->_cutscene.inOverride = false;
-	g_twp->_cutscene.envObj = envObj;
-	g_twp->_cutscene.closureOverride = closureOverride;
-	g_twp->_cutscene.id = cutscene->getId();
-
-	debugC(kDebugSysScript, "create cutscene: %s", cutsceneName.c_str());
+	Common::SharedPtr<Cutscene> cutscene(new Cutscene(cutsceneName, parentThread->getId(), threadObj, closure, closureOverride, envObj));
+	g_twp->_cutscene = cutscene;
 
 	// call the closure in the thread
-	if (!cutscene->call())
-		return sq_throwerror(v, "call failed");
-
+	cutscene->update(0.f);
 	return breakwhilecutscene(v);
 }
 
 static SQInteger cutsceneOverride(HSQUIRRELVM v) {
 	debugC(kDebugSysScript, "cutsceneOverride");
-	return g_twp->skipCutscene();
+	g_twp->_cutscene->cutsceneOverride();
+	return 0;
 }
 
 static SQInteger dumpvar(HSQUIRRELVM v) {
@@ -606,7 +592,7 @@ static SQInteger inputHUD(HSQUIRRELVM v) {
 }
 
 static SQInteger inputOff(HSQUIRRELVM v) {
-	if (!g_twp->_cutscene.id) {
+	if (!g_twp->_cutscene || g_twp->_cutscene->isStopped()) {
 		g_twp->_inputState.setInputActive(false);
 		g_twp->_inputState.setShowCursor(false);
 	}
@@ -614,7 +600,8 @@ static SQInteger inputOff(HSQUIRRELVM v) {
 }
 
 static SQInteger inputOn(HSQUIRRELVM v) {
-	if (!g_twp->_cutscene.id) {
+	Common::SharedPtr<Cutscene> scene(g_twp->_cutscene);
+	if (!scene || scene->isStopped()) {
 		g_twp->_inputState.setInputActive(true);
 		g_twp->_inputState.setShowCursor(true);
 	} else {
@@ -623,8 +610,8 @@ static SQInteger inputOn(HSQUIRRELVM v) {
 		state &= (~UI_INPUT_OFF);
 		state |= UI_CURSOR_ON;
 		state &= (~UI_CURSOR_OFF);
-		g_twp->_cutscene.inputState = (InputStateFlag)state;
-		g_twp->_cutscene.showCursor = true;
+		scene->setInputState((InputStateFlag)state);
+		scene->setShowCursor(true);
 	}
 	return 0;
 }
@@ -767,7 +754,7 @@ static SQInteger stopthread(HSQUIRRELVM v) {
 		return 1;
 	}
 
-	Common::SharedPtr<Thread> t = sqthread(id);
+	Common::SharedPtr<ThreadBase> t = sqthread(id);
 	if (t) {
 		t->stop();
 	}
@@ -795,7 +782,7 @@ static SQInteger stopthread(HSQUIRRELVM v) {
 //     }
 // }
 static SQInteger threadid(HSQUIRRELVM v) {
-	Common::SharedPtr<Thread> t = sqthread(v);
+	Common::SharedPtr<ThreadBase> t = sqthread(v);
 	if (t)
 		sqpush(v, t->getId());
 	else
@@ -806,7 +793,7 @@ static SQInteger threadid(HSQUIRRELVM v) {
 // 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<Thread> t = sqthread(v, 2);
+	Common::SharedPtr<ThreadBase> t = sqthread(v, 2);
 	if (!t)
 		return sq_throwerror(v, "failed to get thread");
 	SQInteger pauseable = 0;
diff --git a/engines/twp/task.h b/engines/twp/task.h
index d62ed14791a..832aedbe104 100644
--- a/engines/twp/task.h
+++ b/engines/twp/task.h
@@ -50,7 +50,7 @@ public:
 	virtual bool update(float elapsed) override final {
 		if (_cond())
 			return false;
-		Common::SharedPtr<Thread> pt(sqthread(_parentId));
+		Common::SharedPtr<ThreadBase> pt = sqthread(_parentId);
 		if (pt) {
 			debugC(kDebugGame, "Resume task: %d, %s", _parentId, pt->getName().c_str());
 			pt->resume();
diff --git a/engines/twp/thread.cpp b/engines/twp/thread.cpp
index f387cba58f2..0655a514183 100644
--- a/engines/twp/thread.cpp
+++ b/engines/twp/thread.cpp
@@ -28,6 +28,28 @@
 
 namespace Twp {
 
+bool ThreadBase::isDead() {
+	SQInteger state = sq_getvmstate(getThread());
+	return _stopRequest || state == SQ_VMSTATE_IDLE;
+}
+
+bool ThreadBase::isSuspended() {
+	SQInteger state = sq_getvmstate(getThread());
+	return state != SQ_VMSTATE_RUNNING;
+}
+
+void ThreadBase::suspend() {
+	if (_pauseable && !isSuspended()) {
+		sq_suspendvm(getThread());
+	}
+}
+
+void ThreadBase::resume() {
+	if (!isDead() && isSuspended()) {
+		sq_wakeupvm(getThread(), SQFalse, SQFalse, SQTrue, SQFalse);
+	}
+}
+
 Thread::Thread(const Common::String &name, bool global, HSQOBJECT threadObj, HSQOBJECT envObj, HSQOBJECT closureObj, const Common::Array<HSQOBJECT> args) {
 	_id = g_twp->_resManager->newThreadId();
 	_name = name;
@@ -58,40 +80,6 @@ Thread::~Thread() {
 	sq_release(v, &_closureObj);
 }
 
-void Thread::pause() {
-	if (_pauseable) {
-		_paused = true;
-		suspend();
-	}
-}
-
-void Thread::unpause() {
-	_paused = false;
-	resume();
-}
-
-bool Thread::isDead() const {
-	SQInteger state = sq_getvmstate(getThread());
-	return _stopRequest || state == SQ_VMSTATE_IDLE;
-}
-
-bool Thread::isSuspended() const {
-	SQInteger state = sq_getvmstate(getThread());
-	return state != SQ_VMSTATE_RUNNING;
-}
-
-void Thread::suspend() {
-	if (_pauseable && !isSuspended()) {
-		sq_suspendvm(getThread());
-	}
-}
-
-void Thread::resume() {
-	if (!isDead() && isSuspended()) {
-		sq_wakeupvm(getThread(), SQFalse, SQFalse, SQTrue, SQFalse);
-	}
-}
-
 bool Thread::call() {
 	HSQUIRRELVM v = _threadObj._unVal.pThread;
 	// call the closure in the thread
@@ -133,4 +121,243 @@ void Thread::stop() {
 	suspend();
 }
 
+Cutscene::Cutscene(const Common::String &name, int parentThreadId, HSQOBJECT threadObj, HSQOBJECT closure, HSQOBJECT closureOverride, HSQOBJECT envObj)
+	: _parentThreadId(parentThreadId),
+	  _threadObj(threadObj),
+	  _closure(closure),
+	  _closureOverride(closureOverride),
+	  _envObj(envObj) {
+
+	_name = name;
+	_id = g_twp->_resManager->newThreadId();
+	_inputState = g_twp->_inputState.getState();
+	_actor = g_twp->_followActor;
+	_showCursor = g_twp->_inputState.getShowCursor();
+	_state = csStart;
+	debugC(kDebugGame, "Create cutscene %d with input: 0x%X from parent thread: %d", _id, _inputState, _parentThreadId);
+	g_twp->_inputState.setInputActive(false);
+	g_twp->_inputState.setShowCursor(false);
+	for (auto thread : g_twp->_threads) {
+		if (thread->isGlobal())
+			thread->pause();
+	}
+	HSQUIRRELVM vm = g_twp->getVm();
+	sq_addref(vm, &_threadObj);
+	sq_addref(vm, &_closure);
+	sq_addref(vm, &_closureOverride);
+	sq_addref(vm, &_envObj);
+}
+
+Cutscene::~Cutscene() {
+	debugC(kDebugGame, "destroy cutscene %d", _id);
+	HSQUIRRELVM vm = g_twp->getVm();
+	sq_release(vm, &_threadObj);
+	sq_release(vm, &_closure);
+	sq_release(vm, &_closureOverride);
+	sq_release(vm, &_envObj);
+}
+
+void Cutscene::start() {
+	_state = csCheckEnd;
+	HSQUIRRELVM thread = getThread();
+	// call the closure in the thread
+	SQInteger top = sq_gettop(thread);
+	sq_pushobject(thread, _closure);
+	sq_pushobject(thread, _envObj);
+	if (SQ_FAILED(sq_call(thread, 1, SQFalse, SQTrue))) {
+		sq_settop(thread, top);
+		error("Couldn't call cutscene");
+	}
+}
+
+void Cutscene::stop() {
+	_state = csQuit;
+	debugC(kDebugGame, "End cutscene: %d", getId());
+	g_twp->_inputState.setState(_inputState);
+	g_twp->_inputState.setShowCursor(_showCursor);
+	if (_showCursor)
+		g_twp->_inputState.setInputActive(true);
+	debugC(kDebugGame, "Restore cutscene input: %X", _inputState);
+	g_twp->follow(g_twp->_actor);
+	Common::Array<Common::SharedPtr<ThreadBase> > threads(g_twp->_threads);
+	for (auto thread : threads) {
+		if (thread->isGlobal())
+			thread->unpause();
+	}
+	sqcall("onCutsceneEnded");
+
+	Common::SharedPtr<ThreadBase> t = sqthread(_parentThreadId);
+	if (t && t->getId())
+		t->unpause();
+	HSQUIRRELVM thread = getThread();
+	if (thread)
+		sq_suspendvm(thread);
+}
+
+HSQUIRRELVM Cutscene::getThread() {
+	if (_threadObj._type != OT_THREAD)
+		return nullptr;
+	return _threadObj._unVal.pThread;
+}
+
+void Cutscene::checkEndCutsceneOverride() {
+	if (isStopped()) {
+		_state = csEnd;
+		debugC(kDebugGame, "end checkEndCutsceneOverride");
+	}
+}
+
+bool Cutscene::update(float elapsed) {
+	if (_waitTime > 0) {
+		_waitTime -= elapsed;
+		if (_waitTime <= 0) {
+			_waitTime = 0;
+			resume();
+		}
+	} else if (_numFrames > 0) {
+		_numFrames--;
+		if (_numFrames <= 0) {
+			_numFrames = 0;
+			resume();
+		}
+	}
+
+	switch (_state) {
+	case csStart:
+		debugC(kDebugGame, "startCutscene");
+		start();
+		return false;
+	case csCheckEnd:
+		checkEndCutscene();
+		return false;
+	case csOverride:
+		debugC(kDebugGame, "doCutsceneOverride");
+		doCutsceneOverride();
+		return false;
+	case csCheckOverride:
+		debugC(kDebugGame, "checkEndCutsceneOverride");
+		checkEndCutsceneOverride();
+		return false;
+	case csEnd:
+		stop();
+		return false;
+	case csQuit:
+		return true;
+	}
+
+	return false;
+}
+
+bool Cutscene::hasOverride() const {
+	return !sq_isnull(_closureOverride);
+}
+
+void Cutscene::doCutsceneOverride() {
+	if (hasOverride()) {
+		// first quit current thread
+		HSQUIRRELVM vm = g_twp->getVm();
+		sq_release(vm, &_threadObj);
+
+		// create thread and store it on the stack
+		sq_newthread(vm, 1024);
+		sq_resetobject(&_threadObj);
+		if (SQ_FAILED(sq_getstackobj(vm, -1, &_threadObj))) {
+			error("failed to get coroutine thread from stack");
+		}
+		sq_addref(vm, &_threadObj);
+
+		_state = csCheckOverride;
+		debugC(kDebugGame, "start cutsceneOverride");
+		sq_pushobject(getThread(), _closureOverride);
+		sq_pushobject(getThread(), _envObj);
+		if (SQ_FAILED(sq_call(getThread(), 1, SQFalse, SQTrue))) {
+			error("Couldn't call cutsceneOverride");
+		}
+		return;
+	}
+	_state = csEnd;
+}
+
+void Cutscene::checkEndCutscene() {
+	if (isStopped()) {
+		_state = csEnd;
+	}
+}
+
+bool Cutscene::isStopped() {
+	if (_stopped || (_state == csQuit))
+		return true;
+	return sq_getvmstate(getThread()) == 0;
+}
+
+void Cutscene::cutsceneOverride() {
+	if (_state == csCheckEnd) {
+		_state = csOverride;
+	}
+}
+
+// CutsceneOverride::CutsceneOverride(const Common::String &name, int parentThreadId, HSQOBJECT threadObj, HSQOBJECT closureOverride, HSQOBJECT envObj)
+// 	: _parentThreadId(parentThreadId),
+// 	  _threadObj(threadObj),
+// 	//   _closure(closure),
+// 	  _closureOverride(closureOverride),
+// 	  _envObj(envObj) {
+
+// 	_name = name;
+// 	_id = g_twp->_resManager->newThreadId();
+// 	_inputState = g_twp->_inputState.getState();
+// 	_actor = g_twp->_followActor;
+// 	_showCursor = g_twp->_inputState.getShowCursor();
+// 	_state = csStart;
+// 	debugC(kDebugGame, "Create cutscene %d with input: 0x%X from parent thread: %d", _id, _inputState, _parentThreadId);
+// 	g_twp->_inputState.setInputActive(false);
+// 	g_twp->_inputState.setShowCursor(false);
+// 	for (auto thread : g_twp->_threads) {
+// 		if (thread->isGlobal())
+// 			thread->pause();
+// 	}
+// 	HSQUIRRELVM vm = g_twp->getVm();
+// 	sq_addref(vm, &_threadObj);
+// 	sq_addref(vm, &_closure);
+// 	sq_addref(vm, &_closureOverride);
+// 	sq_addref(vm, &_envObj);
+// }
+
+// CutsceneOverride::~CutsceneOverride() {
+// 	debugC(kDebugGame, "destroy cutscene override %d", _id);
+// 	HSQUIRRELVM vm = g_twp->getVm();
+// 	sq_release(vm, &_threadObj);
+// 	sq_release(vm, &_closure);
+// 	sq_release(vm, &_closureOverride);
+// 	sq_release(vm, &_envObj);
+// }
+
+// bool CutsceneOverride::update(float elapsed) {
+// 	switch (_state) {
+// 	case csStart:
+// 		debugC(kDebugGame, "start cutsceneOverride");
+// 		sq_pushobject(getThread(), _closureOverride);
+// 		sq_pushobject(getThread(), _envObj);
+// 		if (SQ_FAILED(sq_call(getThread(), 1, SQFalse, SQTrue)))
+// 			error("Couldn't call cutsceneOverride");
+// 			break;
+// 	case csCheckOverride:
+// 		debugC(kDebugGame, "checkEndCutsceneOverride");
+// 		checkEndCutsceneOverride();
+// 		return false;
+// 	case csEnd:
+// 		stop();
+// 		return false;
+// 	case csQuit:
+// 		return true;
+// 	}
+// }
+
+// void CutsceneOverride::checkEndCutsceneOverride() {
+// 	if (isStopped()) {
+// 		_state = csEnd;
+// 		debugC(kDebugGame, "end checkEndCutsceneOverride");
+// 	}
+// }
+
 } // namespace Twp
diff --git a/engines/twp/thread.h b/engines/twp/thread.h
index b66661f9fb8..c050529c69c 100644
--- a/engines/twp/thread.h
+++ b/engines/twp/thread.h
@@ -28,45 +28,113 @@
 
 namespace Twp {
 
-class Thread {
+class ThreadBase {
 public:
-	Thread(const Common::String &name, bool global, HSQOBJECT threadObj, HSQOBJECT envObj, HSQOBJECT closureObj, const Common::Array<HSQOBJECT> args);
-	~Thread();
+	virtual ~ThreadBase() {}
 
-	int getId() const { return _id; }
-	bool isGlobal() const { return _global; }
-	HSQUIRRELVM getThread() const { return _threadObj._unVal.pThread; }
-	const Common::String &getName() const { return _name; }
+	void pause() {
+		if (_pauseable) {
+			_paused = true;
+			suspend();
+		}
+	}
 
-	bool call();
-	bool update(float elapsed);
+	void unpause() {
+		_paused = false;
+		resume();
+	}
+
+	void setName(const Common::String &name) { _name = name; }
+	Common::String getName() const { return _name; }
 
-	void pause();
-	void unpause();
-	void stop();
+	int getId() const { return _id; }
+	virtual HSQUIRRELVM getThread() = 0;
 
-	bool isSuspended() const;
-	bool isDead() const;
+	virtual bool isGlobal() = 0;
+	bool isSuspended();
+	bool isDead();
 
 	void suspend();
 	void resume();
 
+	virtual bool update(float elapsed) = 0;
+	virtual void stop() = 0;
+
+protected:
 public:
 	float _waitTime = 0.f;
 	int _numFrames = 0;
+	bool _paused = false;
 	bool _pauseable = false;
 	uint32 _lastUpdateTime = 0;
 
-private:
+protected:
 	int _id = 0;
 	Common::String _name;
 	bool _stopRequest = false;
-	bool _paused = false;
+	bool _stopped = false;
+};
+
+class Thread final : public ThreadBase {
+public:
+	Thread(const Common::String &name, bool global, HSQOBJECT threadObj, HSQOBJECT envObj, HSQOBJECT closureObj, const Common::Array<HSQOBJECT> args);
+	virtual ~Thread() override final;
+
+	virtual bool isGlobal() override final { return _global; }
+	virtual HSQUIRRELVM getThread() override final { return _threadObj._unVal.pThread; }
+
+	bool call();
+	virtual bool update(float elapsed) override final;
+	virtual void stop() override final;
+
+public:
 	bool _global = false;
 	HSQOBJECT _threadObj, _envObj, _closureObj;
 	Common::Array<HSQOBJECT> _args;
 };
 
+enum CutsceneState {
+	csStart,
+	csCheckEnd,
+	csOverride,
+	csCheckOverride,
+	csEnd,
+	csQuit
+};
+
+class Object;
+class Cutscene final : public ThreadBase {
+public:
+	Cutscene(const Common::String &name, int parentThreadId, HSQOBJECT threadObj, HSQOBJECT closure, HSQOBJECT closureOverride, HSQOBJECT envObj);
+	~Cutscene() override final;
+
+	void start();
+	bool isGlobal() override final { return false; }
+	HSQUIRRELVM getThread() override final;
+	bool update(float elapsed) override final;
+	void stop() override final;
+
+	bool hasOverride() const;
+	void cutsceneOverride();
+	bool isStopped();
+
+	void setInputState(InputStateFlag state) { _inputState = state; }
+	void setShowCursor(bool state) { _showCursor = state; }
+
+private:
+	void checkEndCutscene();
+	void checkEndCutsceneOverride();
+	void doCutsceneOverride();
+
+private:
+	int _parentThreadId = 0;
+	HSQOBJECT _threadObj, _closure, _closureOverride, _envObj;
+	CutsceneState _state;
+	bool _showCursor = false;
+	InputStateFlag _inputState = (InputStateFlag)0;
+	Common::SharedPtr<Object> _actor;
+};
+
 } // namespace Twp
 
 #endif
diff --git a/engines/twp/twp.cpp b/engines/twp/twp.cpp
index 3d6740fa760..f03d6260cc0 100644
--- a/engines/twp/twp.cpp
+++ b/engines/twp/twp.cpp
@@ -181,8 +181,6 @@ TwpEngine::TwpEngine(OSystem *syst, const TwpGameDescription *gameDesc)
 	_dialog.reset(new Dialog());
 	_dialog->_tgt.reset(new EngineDialogTarget());
 	sq_resetobject(&_defaultObj);
-	sq_resetobject(&_cutscene.closureOverride);
-	sq_resetobject(&_cutscene.envObj);
 
 	_audio.reset(new AudioSystem());
 	_scene.reset(new Scene());
@@ -555,11 +553,11 @@ void TwpEngine::update(float elapsed) {
 			}
 
 			_inputState.setHotspot(_noun1 != nullptr);
-			bool hudVisible = _inputState.getInputActive() && _inputState.getInputVerbsActive() && _dialog->getState() == DialogState::None && !_cutscene.id;
+			bool hudVisible = _inputState.getInputActive() && _inputState.getInputVerbsActive() && _dialog->getState() == DialogState::None && !_cutscene;
 			_hud->setVisible(hudVisible);
 			_sentence.setVisible(_hud->isVisible());
 			_uiInv.setVisible(hudVisible);
-			_actorSwitcher.setVisible((_dialog->getState() == DialogState::None) && !_cutscene.id);
+			_actorSwitcher.setVisible((_dialog->getState() == DialogState::None) && !_cutscene);
 			// Common::String cursortxt = Common::String::format("%s (%d, %d) - (%d, %d)", cursorText().c_str(), (int)roomPos.getX(), (int)roomPos.getY(), (int)scrPos.getX(), (int)scrPos.getY());
 			//_sentence.setText(cursortxt.c_str());
 			_sentence.setText(cursorText());
@@ -604,32 +602,30 @@ void TwpEngine::update(float elapsed) {
 	_actorSwitcher.update(actorSwitcherSlots(), elapsed);
 	const uint32 endMiscTime = _system->getMillis();
 
+	// update cutscene
+	if (_cutscene) {
+		if (_cutscene->update(elapsed)) {
+			_cutscene.reset();
+		}
+	}
 	const uint32 endUpdateCutsceneTime = _system->getMillis();
 
 	// update threads: make a copy of the threads because during threads update, new threads can be added
-	Common::Array<Common::SharedPtr<Thread> > threads(_threads);
-	Common::Array<Common::SharedPtr<Thread> > threadsToRemove;
+	Common::Array<Common::SharedPtr<ThreadBase> > threads(_threads);
+	Common::Array<Common::SharedPtr<ThreadBase> > threadsToRemove;
 
 	bool isNotInDialog = _dialog->getState() == DialogState::None;
 	for (auto it = threads.begin(); it != threads.end(); it++) {
-		Common::SharedPtr<Thread> thread(*it);
-		if ((isNotInDialog || !thread->isGlobal() || !thread->_pauseable) && thread->update(elapsed)) {
+		Common::SharedPtr<ThreadBase> thread(*it);
+		if ((isNotInDialog || !thread->isGlobal()) && thread->update(elapsed)) {
 			threadsToRemove.push_back(thread);
 		}
 	}
 	// remove threads that are terminated
 	for (auto it = threadsToRemove.begin(); it != threadsToRemove.end(); it++) {
-		Common::SharedPtr<Thread> thread(*it);
+		Common::SharedPtr<ThreadBase> thread(*it);
 		size_t i = find(_threads, *it);
 		if (i != (size_t)-1) {
-			// if cutscene reset information
-			if (it->get()->getId() == _cutscene.id) {
-				_cutscene.id = 0;
-				g_twp->_inputState.setState(_cutscene.inputState);
-				g_twp->_inputState.setShowCursor(_cutscene.showCursor);
-				if (_cutscene.showCursor)
-					g_twp->_inputState.setInputActive(true);
-			}
 			_threads.remove_at(i);
 		}
 	}
@@ -1350,7 +1346,7 @@ Common::Error TwpEngine::loadGameStream(Common::SeekableReadStream *stream) {
 }
 
 bool TwpEngine::canSaveGameStateCurrently(Common::U32String *msg) {
-	return _saveGameManager->_allowSaveGame && !_cutscene.id;
+	return _saveGameManager->_allowSaveGame && !_cutscene;
 }
 
 Common::Error TwpEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
@@ -1702,7 +1698,7 @@ void TwpEngine::exitRoom(Common::SharedPtr<Room> nextRoom) {
 
 		// stop all local threads
 		for (size_t i = 0; i < _threads.size(); i++) {
-			Common::SharedPtr<Thread> thread = _threads[i];
+			Common::SharedPtr<ThreadBase> thread = _threads[i];
 			if (!thread->isGlobal()) {
 				thread->stop();
 			}
@@ -1723,9 +1719,6 @@ void TwpEngine::actorExit(Common::SharedPtr<Object> actor) {
 		if (sqrawexists(_room->_table, "actorExit")) {
 			sqcall(_room->_table, "actorExit", actor->_table);
 		}
-		if (_followActor == actor) {
-			_followActor = _actor;
-		}
 	}
 }
 
@@ -2070,42 +2063,14 @@ float TwpEngine::getRandom(float min, float max) const {
 	return min + scale * (max - min);
 }
 
-SQRESULT TwpEngine::skipCutscene() {
-	if (!_cutscene.id)
-		return 0;
-
-	if ((_dialog->getState() != DialogState::None) || _cutscene.inOverride || (_cutscene.closureOverride._type == OT_NULL)) {
-		_noOverride->reset();
-		return 0;
+void TwpEngine::skipCutscene() {
+	if (!_cutscene)
+		return;
+	if (_cutscene->hasOverride()) {
+		_cutscene->cutsceneOverride();
+		return;
 	}
-
-	_cutscene.inOverride = true;
-	HSQUIRRELVM vm = getVm();
-	HSQUIRRELVM v = vm;
-
-	// stop cutscene
-	sq_addref(v, &_cutscene.envObj);
-	Common::SharedPtr<Thread> thread(sqthread(_cutscene.id));
-	thread->stop();
-
-	// create thread and store it on the stack
-	HSQOBJECT threadObj;
-	sq_resetobject(&threadObj);
-	sq_newthread(vm, 1024);
-	if (SQ_FAILED(sq_getstackobj(vm, -1, &threadObj)))
-		return sq_throwerror(v, "Couldn't get coroutine thread from stack");
-
-	Common::String name(Common::String::format("cutscene override: %s", thread->getName().c_str()));
-	Common::SharedPtr<Thread> t(new Thread(name, true, threadObj, _cutscene.envObj, _cutscene.closureOverride, {}));
-	_threads.push_back(t);
-	_cutscene.id = t->getId();
-
-	debugC(kDebugSysScript, "create cutscene override");
-
-	// call the closure in the thread
-	if (!t->call())
-		return sq_throwerror(v, "call failed");
-	return 0;
+	_noOverride->reset();
 }
 
 Scaling *TwpEngine::getScaling(const Common::String &name) {
diff --git a/engines/twp/twp.h b/engines/twp/twp.h
index a98ee61ed7c..f74c852f119 100644
--- a/engines/twp/twp.h
+++ b/engines/twp/twp.h
@@ -70,7 +70,7 @@ class Scene;
 struct ShaderParams;
 class Task;
 class TextDb;
-class Thread;
+class ThreadBase;
 struct TwpGameDescription;
 struct Verb;
 struct VerbId;
@@ -120,7 +120,7 @@ public:
 	void updateSettingVars();
 
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
-		return _cutscene.id == 0;
+		return !_cutscene;
 	}
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
 
@@ -167,8 +167,6 @@ public:
 
 	int runDialog(GUI::Dialog &dialog) override;
 
-	SQRESULT skipCutscene();
-
 private:
 	void update(float elapsedMs);
 	void draw(RenderTexture *texture = nullptr);
@@ -187,6 +185,7 @@ private:
 	Common::Array<ActorSwitcherSlot> actorSwitcherSlots();
 	ActorSwitcherSlot actorSwitcherSlot(ActorSlot *slot);
 	Scaling *getScaling(const Common::String &name);
+	void skipCutscene();
 
 private:
 	unique_ptr<Vm> _vm;
@@ -196,7 +195,7 @@ public:
 	unique_ptr<ResManager> _resManager;
 	Common::Array<Common::SharedPtr<Room> > _rooms;
 	Common::Array<Common::SharedPtr<Object> > _actors;
-	Common::Array<Common::SharedPtr<Thread> > _threads;
+	Common::Array<Common::SharedPtr<ThreadBase> > _threads;
 	Common::Array<Common::SharedPtr<Task> > _tasks;
 	Common::Array<Common::SharedPtr<Callback> > _callbacks;
 	Common::SharedPtr<Object> _actor;
@@ -212,14 +211,7 @@ public:
 	float _nextHoldToMoveTime = 0.f;
 	int _frameCounter = 0;
 	Common::SharedPtr<Lighting> _lighting;
-	struct {
-		int id = 0;
-		InputStateFlag inputState = (InputStateFlag)0;
-		bool showCursor = false;
-		bool inOverride = false;
-		HSQOBJECT envObj;
-		HSQOBJECT closureOverride;
-	} _cutscene;
+	Common::SharedPtr<Cutscene> _cutscene;
 	unique_ptr<Scene> _scene;
 	unique_ptr<Scene> _screenScene;
 	unique_ptr<NoOverrideNode> _noOverride;




More information about the Scummvm-git-logs mailing list