[Scummvm-git-logs] scummvm master -> a259e6e2e6443c10080be10c7695cb9b44f90948

scemino noreply at scummvm.org
Sun Apr 21 11:58:26 UTC 2024


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

Summary:
30fc9cbb73 TWP: Fix invalid original savegame
a259e6e2e6 TWP: Implement missing sayLineAt function


Commit: 30fc9cbb73e4eed007daa6975e5e037386b5c006
    https://github.com/scummvm/scummvm/commit/30fc9cbb73e4eed007daa6975e5e037386b5c006
Author: scemino (scemino74 at gmail.com)
Date: 2024-04-21T13:57:34+02:00

Commit Message:
TWP: Fix invalid original savegame

Changed paths:
    engines/twp/twp.cpp


diff --git a/engines/twp/twp.cpp b/engines/twp/twp.cpp
index fc9e3f809b1..17612d4cfa8 100644
--- a/engines/twp/twp.cpp
+++ b/engines/twp/twp.cpp
@@ -1080,15 +1080,13 @@ Common::Error TwpEngine::saveGameState(int slot, const Common::String &desc, boo
 		if (!saveFile)
 			return Common::kWritingFailed;
 
-		result = saveGameStream(saveFile, isAutosave);
-		if (result.getCode() == Common::kNoError) {
-			Common::OutSaveFile *thumbnail = _saveFileMan->openForSaving(Common::String::format("Savegame%d.png", slot), false);
-			Graphics::Surface surface;
-			capture(surface, 320, 180);
-			Image::writePNG(*thumbnail, surface);
-			thumbnail->finalize();
-			delete thumbnail;
-		}
+		_saveGameManager->saveGame(saveFile);
+		Common::OutSaveFile *thumbnail = _saveFileMan->openForSaving(Common::String::format("Savegame%d.png", slot), false);
+		Graphics::Surface surface;
+		capture(surface, 320, 180);
+		Image::writePNG(*thumbnail, surface);
+		thumbnail->finalize();
+		delete thumbnail;
 
 		saveFile->finalize();
 		delete saveFile;


Commit: a259e6e2e6443c10080be10c7695cb9b44f90948
    https://github.com/scummvm/scummvm/commit/a259e6e2e6443c10080be10c7695cb9b44f90948
Author: scemino (scemino74 at gmail.com)
Date: 2024-04-21T13:57:34+02:00

Commit Message:
TWP: Implement missing sayLineAt function

Changed paths:
    engines/twp/actorlib.cpp
    engines/twp/motor.cpp
    engines/twp/motor.h
    engines/twp/twp.cpp
    engines/twp/twp.h


diff --git a/engines/twp/actorlib.cpp b/engines/twp/actorlib.cpp
index c4d4c40d237..896030193b9 100644
--- a/engines/twp/actorlib.cpp
+++ b/engines/twp/actorlib.cpp
@@ -899,6 +899,7 @@ static SQInteger sayLineAt(HSQUIRRELVM v) {
 	SQInteger x, y;
 	Common::String text;
 	float duration = -1.0f;
+	Common::SharedPtr<Object> actor;
 	if (SQ_FAILED(sqget(v, 2, x)))
 		return sq_throwerror(v, "failed to get x");
 	if (SQ_FAILED(sqget(v, 3, y)))
@@ -909,23 +910,20 @@ static SQInteger sayLineAt(HSQUIRRELVM v) {
 		if (SQ_FAILED(sqget(v, 4, c)))
 			return sq_throwerror(v, "failed to get color");
 		color = Color::rgb(c);
-		if (SQ_FAILED(sqget(v, 5, duration)))
-			return sq_throwerror(v, "failed to get duration");
-		if (SQ_FAILED(sqget(v, 6, text)))
-			return sq_throwerror(v, "failed to get text");
 	} else {
-		Common::SharedPtr<Object> actor = sqactor(v, 4);
+		actor = sqactor(v, 4);
 		if (!actor)
 			return sq_throwerror(v, "failed to get actor");
-		Math::Vector2d pos = g_twp->roomToScreen(actor->_node->getAbsPos());
-		x = pos.getX();
-		y = pos.getY();
 		color = actor->_talkColor;
-		if (SQ_FAILED(sqget(v, 6, text)))
-			return sq_throwerror(v, "failed to get text");
 	}
 
-	warning("TODO: saylineAt: (%lld,%lld) text=%s color=%s duration=%f", x, y, text.c_str(), color.toStr().c_str(), duration);
+	if (SQ_FAILED(sqget(v, 5, duration)))
+		return sq_throwerror(v, "failed to get duration");
+	if (SQ_FAILED(sqget(v, 6, text)))
+		return sq_throwerror(v, "failed to get text");
+
+	debugC(kDebugActScript, "saylineAt: (%lld,%lld) text=%s color=%s duration=%f", x, y, text.c_str(), color.toStr().c_str(), duration);
+	g_twp->sayLineAt(Math::Vector2d(x, y), color, actor, duration, text);
 	return 0;
 }
 
diff --git a/engines/twp/motor.cpp b/engines/twp/motor.cpp
index 86e323f8579..a42fe08ce64 100644
--- a/engines/twp/motor.cpp
+++ b/engines/twp/motor.cpp
@@ -307,8 +307,71 @@ void WalkTo::onUpdate(float elapsed) {
 	}
 }
 
-Talking::Talking(Common::SharedPtr<Object> obj, const Common::StringArray &texts, const Color &color) {
-	_obj = obj;
+TalkingBase::TalkingBase(Common::SharedPtr<Object> actor, float duration)
+	: _actor(actor), _duration(duration) {
+}
+
+int TalkingBase::loadActorSpeech(const Common::String &name) {
+	if (ConfMan.getBool("speech_mute")) {
+		debugC(kDebugGame, "talking %s: speech_mute: true", _actor->_key.c_str());
+		return 0;
+	}
+
+	debugC(kDebugGame, "loadActorSpeech %s.ogg", name.c_str());
+	Common::String filename(name);
+	filename.toUppercase();
+	filename += ".ogg";
+	if (g_twp->_pack->assetExists(filename.c_str())) {
+		Common::SharedPtr<SoundDefinition> soundDefinition(new SoundDefinition(filename));
+		if (!soundDefinition) {
+			debugC(kDebugGame, "File %s.ogg not found", name.c_str());
+		} else {
+			g_twp->_audio->_soundDefs.push_back(soundDefinition);
+			int id = g_twp->_audio->play(soundDefinition, Audio::Mixer::SoundType::kSpeechSoundType, 0, 0, 1.f);
+			int duration = g_twp->_audio->getDuration(id);
+			debugC(kDebugGame, "talking %s audio id: %d, dur: %d", _actor->_key.c_str(), id, duration);
+			if (duration)
+				_duration = static_cast<float>(duration) / 1000.f;
+			return id;
+		}
+	}
+	return 0;
+}
+
+int TalkingBase::onTalkieId(int id) {
+	SQInteger result = 0;
+	sqcallfunc(result, "onTalkieID", _actor->_table, id);
+	if (result == 0)
+		result = id;
+	return result;
+}
+
+Common::String TalkingBase::talkieKey() {
+	Common::String result;
+	if (sqrawexists(_actor->_table, "_talkieKey") && SQ_FAILED(sqgetf(_actor->_table, "_talkieKey", result))) {
+		error("Failed to get talkie key");
+	}
+	if (sqrawexists(_actor->_table, "_key") && SQ_FAILED(sqgetf(_actor->_table, "_key", result))) {
+		error("Failed to get talkie key (2)");
+	}
+	return result;
+}
+
+void TalkingBase::setDuration(const Common::String &text) {
+	_elapsed = 0;
+	// let sayLineBaseTime = prefs(SayLineBaseTime);
+	float sayLineBaseTime = 1.5f;
+	//   let sayLineCharTime = prefs(SayLineCharTime);
+	float sayLineCharTime = 0.025f;
+	// let sayLineMinTime = prefs(SayLineMinTime);
+	float sayLineMinTime = 0.2f;
+	//   let sayLineSpeed = prefs(SayLineSpeed);
+	float sayLineSpeed = 0.5f;
+	float duration = (sayLineBaseTime + sayLineCharTime * static_cast<float>(text.size())) / (0.2f + sayLineSpeed);
+	_duration = MAX(duration, sayLineMinTime);
+}
+
+Talking::Talking(Common::SharedPtr<Object> obj, const Common::StringArray &texts, const Color &color) : TalkingBase(obj, 0.f) {
 	_color = color;
 	_texts.assign(texts.begin() + 1, texts.end());
 	say(texts[0]);
@@ -350,55 +413,28 @@ void Talking::onUpdate(float elapsed) {
 		return;
 
 	_elapsed += elapsed;
-	if (_obj->_sound) {
-		if (!g_twp->_audio->playing(_obj->_sound)) {
-			debugC(kDebugGame, "talking %s audio stopped", _obj->_key.c_str());
-			_obj->_sound = 0;
+	if (_actor->_sound) {
+		if (!g_twp->_audio->playing(_actor->_sound)) {
+			debugC(kDebugGame, "talking %s audio stopped", _actor->_key.c_str());
+			_actor->_sound = 0;
 		} else {
-			float e = static_cast<float>(g_twp->_audio->getElapsed(_obj->_sound)) / 1000.f;
+			float e = static_cast<float>(g_twp->_audio->getElapsed(_actor->_sound)) / 1000.f;
 			char letter = _lip.letter(e);
-			_obj->setHeadIndex(letterToIndex(letter));
+			_actor->setHeadIndex(letterToIndex(letter));
 		}
 	} else if (_elapsed < _duration) {
 		char letter = _lip.letter(_elapsed);
-		_obj->setHeadIndex(letterToIndex(letter));
+		_actor->setHeadIndex(letterToIndex(letter));
 	} else if (!_texts.empty()) {
-		debugC(kDebugGame, "talking %s: %s", _obj->_key.c_str(), _texts[0].c_str());
+		debugC(kDebugGame, "talking %s: %s", _actor->_key.c_str(), _texts[0].c_str());
 		say(_texts[0]);
 		_texts.remove_at(0);
 	} else {
-		debugC(kDebugGame, "talking %s: ended", _obj->_key.c_str());
+		debugC(kDebugGame, "talking %s: ended", _actor->_key.c_str());
 		disable();
 	}
 }
 
-int Talking::loadActorSpeech(const Common::String &name) {
-	if (ConfMan.getBool("speech_mute")) {
-		debugC(kDebugGame, "talking %s: speech_mute: true", _obj->_key.c_str());
-		return 0;
-	}
-
-	debugC(kDebugGame, "loadActorSpeech %s.ogg", name.c_str());
-	Common::String filename(name);
-	filename.toUppercase();
-	filename += ".ogg";
-	if (g_twp->_pack->assetExists(filename.c_str())) {
-		Common::SharedPtr<SoundDefinition> soundDefinition(new SoundDefinition(filename));
-		if (!soundDefinition) {
-			debugC(kDebugGame, "File %s.ogg not found", name.c_str());
-		} else {
-			g_twp->_audio->_soundDefs.push_back(soundDefinition);
-			int id = g_twp->_audio->play(soundDefinition, Audio::Mixer::SoundType::kSpeechSoundType, 0, 0, 1.f);
-			int duration = g_twp->_audio->getDuration(id);
-			debugC(kDebugGame, "talking %s audio id: %d, dur: %d", _obj->_key.c_str(), id, duration);
-			if (duration)
-				_duration = static_cast<float>(duration) / 1000.f;
-			return id;
-		}
-	}
-	return 0;
-}
-
 void Talking::say(const Common::String &text) {
 	if (text.empty())
 		return;
@@ -442,11 +478,11 @@ void Talking::say(const Common::String &text) {
 			debugC(kDebugGame, "Lip %s loaded", path.c_str());
 		}
 
-		if (_obj->_sound) {
-			g_twp->_audio->stop(_obj->_sound);
+		if (_actor->_sound) {
+			g_twp->_audio->stop(_actor->_sound);
 		}
 
-		_obj->_sound = loadActorSpeech(name);
+		_actor->_sound = loadActorSpeech(name);
 	} else if (txt[0] == '^') {
 		txt = txt.substr(1);
 	}
@@ -460,9 +496,9 @@ void Talking::say(const Common::String &text) {
 
 	debugC(kDebugGame, "sayLine '%s'", txt.c_str());
 
-	if (sqrawexists(_obj->_table, "sayingLine")) {
-		const char *anim = _obj->_animName.empty() ? nullptr : _obj->_animName.c_str();
-		sqcall(_obj->_table, "sayingLine", anim, txt);
+	if (sqrawexists(_actor->_table, "sayingLine")) {
+		const char *anim = _actor->_animName.empty() ? nullptr : _actor->_animName.c_str();
+		sqcall(_actor->_table, "sayingLine", anim, txt);
 	}
 
 	// modify state ?
@@ -473,34 +509,34 @@ void Talking::say(const Common::String &text) {
 			state = txt.substr(1, i - 1);
 			debugC(kDebugGame, "Set state from anim '%s'", state.c_str());
 			if (state != "notalk") {
-				_obj->play(state);
+				_actor->play(state);
 			}
 			txt = txt.substr(i + 1);
 		}
 	}
 
-	if (!_obj->_sound)
+	if (!_actor->_sound)
 		setDuration(txt);
 
-	if (_obj->_sayNode) {
-		_obj->_sayNode->remove();
+	if (_actor->_sayNode) {
+		_actor->_sayNode->remove();
 	}
 
 	if (ConfMan.getBool("subtitles")) {
 		Text text2("sayline", txt, thCenter, tvTop, SCREEN_WIDTH * 3.f / 4.f, _color);
-		_obj->_sayNode = Common::SharedPtr<TextNode>(new TextNode());
-		_obj->_sayNode->setText(text2);
-		_obj->_sayNode->setColor(_color);
-		_node = _obj->_sayNode;
-		Math::Vector2d pos = g_twp->roomToScreen(_obj->_node->getAbsPos() + _obj->_talkOffset);
+		_actor->_sayNode = Common::SharedPtr<TextNode>(new TextNode());
+		_actor->_sayNode->setText(text2);
+		_actor->_sayNode->setColor(_color);
+		_node = _actor->_sayNode;
+		Math::Vector2d pos = g_twp->roomToScreen(_actor->_node->getAbsPos() + _actor->_talkOffset);
 
 		// clamp position to keep it on screen
 		pos.setX(CLIP(pos.getX(), 10.f + text2.getBounds().getX() / 2.f, SCREEN_WIDTH - text2.getBounds().getX() / 2.f));
 		pos.setY(CLIP(pos.getY(), 10.f + text2.getBounds().getY(), SCREEN_HEIGHT - text2.getBounds().getY()));
 
-		_obj->_sayNode->setPos(pos);
-		_obj->_sayNode->setAnchorNorm(Math::Vector2d(0.5f, 0.0f));
-		g_twp->_screenScene->addChild(_obj->_sayNode.get());
+		_actor->_sayNode->setPos(pos);
+		_actor->_sayNode->setAnchorNorm(Math::Vector2d(0.5f, 0.0f));
+		g_twp->_screenScene->addChild(_actor->_sayNode.get());
 	}
 
 	_elapsed = 0.f;
@@ -508,48 +544,121 @@ void Talking::say(const Common::String &text) {
 
 void Talking::disable() {
 	Motor::disable();
-	if (_obj->_sound) {
-		g_twp->_audio->stop(_obj->_sound);
+	if (_actor->_sound) {
+		g_twp->_audio->stop(_actor->_sound);
 	}
 	_texts.clear();
-	_obj->setHeadIndex(1);
+	_actor->setHeadIndex(1);
 	if (_node)
 		_node->remove();
 	_elapsed = 0.f;
 	_duration = 0.f;
 }
 
-int Talking::onTalkieId(int id) {
-	SQInteger result = 0;
-	sqcallfunc(result, "onTalkieID", _obj->_table, id);
-	if (result == 0)
-		result = id;
-	return result;
+SayLineAt::SayLineAt(const Math::Vector2d &pos, const Color &color, Common::SharedPtr<Object> actor, float duration, const Common::String &text)
+	: TalkingBase(actor, duration), _pos(pos), _color(color), _text(text) {
+	say(text);
 }
 
-void Talking::setDuration(const Common::String &text) {
-	_elapsed = 0;
-	// let sayLineBaseTime = prefs(SayLineBaseTime);
-	float sayLineBaseTime = 1.5f;
-	//   let sayLineCharTime = prefs(SayLineCharTime);
-	float sayLineCharTime = 0.025f;
-	// let sayLineMinTime = prefs(SayLineMinTime);
-	float sayLineMinTime = 0.2f;
-	//   let sayLineSpeed = prefs(SayLineSpeed);
-	float sayLineSpeed = 0.5f;
-	float duration = (sayLineBaseTime + sayLineCharTime * static_cast<float>(text.size())) / (0.2f + sayLineSpeed);
-	_duration = MAX(duration, sayLineMinTime);
-}
+void SayLineAt::say(const Common::String &text) {
+	Common::String txt(text);
+	if (txt[0] == '$') {
+		HSQUIRRELVM v = g_twp->getVm();
+		SQInteger top = sq_gettop(v);
+		sq_pushroottable(v);
+		Common::String code(Common::String::format("return %s", text.substr(1, text.size() - 1).c_str()));
+		if (SQ_FAILED(sq_compilebuffer(v, code.c_str(), code.size(), "execCode", SQTrue))) {
+			error("Error executing code %s", code.c_str());
+		} else {
+			sq_push(v, -2);
+			// call
+			if (SQ_FAILED(sq_call(v, 1, SQTrue, SQTrue))) {
+				error("Error calling code %s", code.c_str());
+			} else {
+				if (SQ_FAILED(sqget(v, -1, txt))) {
+					error("Error getting call result %s", code.c_str());
+				}
+				sq_settop(v, top);
+			}
+		}
+	}
 
-Common::String Talking::talkieKey() {
-	Common::String result;
-	if (sqrawexists(_obj->_table, "_talkieKey") && SQ_FAILED(sqgetf(_obj->_table, "_talkieKey", result))) {
-		error("Failed to get talkie key");
+	if (txt[0] == '@') {
+		int id = atoi(txt.c_str() + 1);
+		txt = g_twp->_textDb->getText(id);
+
+		if (_actor) {
+			id = onTalkieId(id);
+			Common::String key(talkieKey());
+			key.toUppercase();
+			Common::String name = Common::String::format("%s_%d", key.c_str(), id);
+			Common::String path(name + ".lip");
+
+			debugC(kDebugGame, "Load lip %s", path.c_str());
+			if (g_twp->_pack->assetExists(path.c_str())) {
+				GGPackEntryReader entry;
+				entry.open(*g_twp->_pack, path);
+				Lip lip;
+				lip.load(&entry);
+				debugC(kDebugGame, "Lip %s loaded", path.c_str());
+			}
+
+			if (_actor->_sound) {
+				g_twp->_audio->stop(_actor->_sound);
+			}
+
+			_actor->_sound = loadActorSpeech(name);
+		}
+	} else if (txt[0] == '^') {
+		txt = txt.substr(1);
 	}
-	if (sqrawexists(_obj->_table, "_key") && SQ_FAILED(sqgetf(_obj->_table, "_key", result))) {
-		error("Failed to get talkie key (2)");
+
+	// remove text in parentheses
+	if (txt[0] == '(') {
+		uint32 i = txt.find(')');
+		if (i != Common::String::npos)
+			txt = txt.substr(i + 1);
 	}
-	return result;
+
+	if (_actor && !_actor->_sound)
+		setDuration(txt);
+
+	debugC(kDebugGame, "sayLine '%s'", txt.c_str());
+
+	// transform talking position to screen pos
+	Math::Vector2d talkingSize(320.f, 180.f);
+	Math::Vector2d pos(Math::Vector2d(SCREEN_WIDTH, SCREEN_HEIGHT) * _pos / talkingSize);
+
+	Text text2("sayline", txt, thCenter, tvTop, 0.f, _color);
+	_node = Common::SharedPtr<TextNode>(new TextNode());
+	_node->setText(text2);
+	_node->setPos(pos);
+	_node->setColor(_color);
+	_node->setAnchorNorm(Math::Vector2d(0.5f, 0.0f));
+	g_twp->_screenScene->addChild(_node.get());
+
+	_elapsed = 0.f;
+}
+
+void SayLineAt::onUpdate(float elapsed) {
+	if (!isEnabled())
+		return;
+
+	_elapsed += elapsed;
+	if (_actor && _actor->_sound) {
+		if (!g_twp->_audio->playing(_actor->_sound)) {
+			debugC(kDebugGame, "talking %s audio stopped", _actor->_key.c_str());
+			_actor->_sound = 0;
+		}
+	} else if (_elapsed >= _duration) {
+		debugC(kDebugGame, "talking %s: ended", _text.c_str());
+		disable();
+	}
+}
+
+void SayLineAt::disable() {
+	Motor::disable();
+	_node->remove();
 }
 
 Jiggle::Jiggle(Node *node, float amount) : _amount(amount), _node(node) {
diff --git a/engines/twp/motor.h b/engines/twp/motor.h
index 86594b9d2a1..93f1e2cd0f0 100644
--- a/engines/twp/motor.h
+++ b/engines/twp/motor.h
@@ -245,8 +245,28 @@ private:
 };
 
 class TextNode;
+
+class TalkingBase : public Motor {
+protected:
+	TalkingBase(Common::SharedPtr<Object> actor, float duration);
+
+public:
+	virtual ~TalkingBase() {}
+
+protected:
+	Common::String talkieKey();
+	int onTalkieId(int id);
+	int loadActorSpeech(const Common::String &name);
+	void setDuration(const Common::String &text);
+
+protected:
+	Common::SharedPtr<Object> _actor;
+	float _duration = 0.f;
+	float _elapsed = 0.f;
+};
+
 // Creates a talking animation for a specified object.
-class Talking : public Motor {
+class Talking : public TalkingBase {
 public:
 	Talking(Common::SharedPtr<Object> obj, const Common::StringArray &texts, const Color &color);
 	virtual ~Talking() {}
@@ -258,21 +278,32 @@ public:
 
 private:
 	void say(const Common::String &text);
-	int onTalkieId(int id);
-	Common::String talkieKey();
-	void setDuration(const Common::String &text);
-	int loadActorSpeech(const Common::String &name);
 
 private:
-	Common::SharedPtr<Object> _obj;
 	Common::SharedPtr<TextNode> _node;
 	Lip _lip;
-	float _elapsed = 0.f;
-	float _duration = 0.f;
 	Color _color;
 	Common::StringArray _texts;
 };
 
+class SayLineAt : public TalkingBase {
+public:
+	SayLineAt(const Math::Vector2d &pos, const Color &color, Common::SharedPtr<Object> actor, float duration, const Common::String &text);
+	virtual ~SayLineAt() {}
+
+	virtual void onUpdate(float elapsed) override;
+	virtual void disable() override;
+
+private:
+	void say(const Common::String &text);
+
+private:
+	const Math::Vector2d _pos;
+	Color _color;
+	Common::String _text;
+	Common::SharedPtr<TextNode> _node;
+};
+
 class Jiggle : public Motor {
 public:
 	Jiggle(Node *node, float amount);
diff --git a/engines/twp/twp.cpp b/engines/twp/twp.cpp
index 17612d4cfa8..a8b75b8c016 100644
--- a/engines/twp/twp.cpp
+++ b/engines/twp/twp.cpp
@@ -454,6 +454,8 @@ void TwpEngine::update(float elapsed) {
 
 	_audio->update(elapsed);
 	_noOverride->update(elapsed);
+	if (_talking)
+		_talking->update(elapsed);
 
 	// update mouse pos
 	Math::Vector2d scrPos = winToScreen(_cursor.pos);
@@ -1756,6 +1758,10 @@ void TwpEngine::updateTriggers() {
 	}
 }
 
+void TwpEngine::sayLineAt(const Math::Vector2d &pos, const Color &color, Common::SharedPtr<Object> actor, float duration, const Common::String &text) {
+	_talking = Common::ScopedPtr<SayLineAt>(new SayLineAt(pos, color, actor, duration, text));
+}
+
 void TwpEngine::stopTalking() {
 	if (!_room)
 		return;
@@ -1768,6 +1774,8 @@ void TwpEngine::stopTalking() {
 }
 
 bool TwpEngine::isSomeoneTalking() const {
+	if (_talking && _talking->isEnabled())
+		return true;
 	if (!_room)
 		return false;
 	for (auto it = _actors.begin(); it != _actors.end(); it++) {
diff --git a/engines/twp/twp.h b/engines/twp/twp.h
index 35635813e7f..834d1720cfc 100644
--- a/engines/twp/twp.h
+++ b/engines/twp/twp.h
@@ -58,6 +58,7 @@ class InputState;
 struct Light;
 class Lighting;
 class LightingNode;
+class Motor;
 class NoOverrideNode;
 class Object;
 class PathNode;
@@ -138,6 +139,7 @@ public:
 	void setActor(Common::SharedPtr<Object> actor, bool userSelected = false);
 	Common::SharedPtr<Object> objAt(const Math::Vector2d &pos);
 	void flashSelectableActor(int flash);
+	void sayLineAt(const Math::Vector2d &pos, const Color &color, Common::SharedPtr<Object> actor, float duration, const Common::String &text);
 	void stopTalking();
 	bool isSomeoneTalking() const;
 	void walkFast(bool state = true);
@@ -272,6 +274,7 @@ private:
 	unique_ptr<Shader> _sepiaShader;
 	int _speed = 1;
 	bool _control = false;
+	unique_ptr<Motor> _talking;
 };
 
 extern TwpEngine *g_twp;




More information about the Scummvm-git-logs mailing list