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

alexbevi noreply at scummvm.org
Fri Oct 16 00:22:25 UTC 2026


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

Summary:
f2d9adcbba HARVESTER: add cursor handling to Other dialogue entry
e464552720 HARVESTER: handle back-to-front player turn when at room min depth
b31124723e HARVESTER: properly restore animated room elements on re-entry


Commit: f2d9adcbbac8b48ec5d0a90d3f1e0a80d25923c4
    https://github.com/scummvm/scummvm/commit/f2d9adcbbac8b48ec5d0a90d3f1e0a80d25923c4
Author: Alex Bevilacqua (alex at alexbevi.com)
Date: 2026-10-15T20:22:11-04:00

Commit Message:
HARVESTER: add cursor handling to Other dialogue entry

Assisted-by: Codex:gpt-6.1-sol

Changed paths:
    engines/harvester/cft_font.cpp
    engines/harvester/cft_font.h
    engines/harvester/dialogue.cpp


diff --git a/engines/harvester/cft_font.cpp b/engines/harvester/cft_font.cpp
index 7b3630aef2d..5f7743f5a12 100644
--- a/engines/harvester/cft_font.cpp
+++ b/engines/harvester/cft_font.cpp
@@ -121,6 +121,10 @@ HarvesterCftFont::HarvesterCftFont(const CftFontResource &resource) : _resource(
 		_spaceWidth = MAX<int>(1, _maxCharWidth);
 }
 
+bool HarvesterCftFont::hasGlyph(uint32 chr) const {
+	return chr == ' ' || chr == '_' || findGlyph(chr) != nullptr;
+}
+
 int HarvesterCftFont::getCharWidth(uint32 chr) const {
 	if (chr == ' ' || chr == '_')
 		return _spaceWidth;
diff --git a/engines/harvester/cft_font.h b/engines/harvester/cft_font.h
index a7cd0d34241..e6588c1a485 100644
--- a/engines/harvester/cft_font.h
+++ b/engines/harvester/cft_font.h
@@ -40,6 +40,7 @@ public:
 	Common::String getFontName() const override { return _resource.name; }
 	int getMaxCharWidth() const override { return _maxCharWidth; }
 	int getCharWidth(uint32 chr) const override;
+	bool hasGlyph(uint32 chr) const;
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 
 private:
diff --git a/engines/harvester/dialogue.cpp b/engines/harvester/dialogue.cpp
index a343fe3d54d..8787db2c63b 100644
--- a/engines/harvester/dialogue.cpp
+++ b/engines/harvester/dialogue.cpp
@@ -133,6 +133,9 @@ static const int kDialogueOtherX = 171;
 static const int kDialogueOtherY = 183;
 static const int kDialogueTextEntryX = 208;
 static const int kDialogueTextEntryY = 184;
+static const uint kDialogueTextEntryMaxLength = 26;
+static const int kDialogueTextEntryMaxWidth = 550;
+static const uint32 kDialogueTextEntryBlinkTicks = 0x30;
 static const int kDialogueOtherWidth = 120;
 static const int kDialogueTopicStartX = 168;
 static const int kDialogueTopicEndX = 482;
@@ -766,6 +769,8 @@ public:
 		topics.push_back(_genericByeTopic);
 		debugC(1, kDebugDialogue, "Harvester: keyword menu default topic='%s'", _genericByeTopic.c_str());
 		bool editingOther = false;
+		bool textEntryCursorVisible = false;
+		uint32 textEntryBlinkTimestamp = 0;
 		Common::String typedTopic;
 		Common::Error releaseError = waitForPointerRelease();
 		if (releaseError.getCode() != Common::kNoError)
@@ -773,43 +778,66 @@ public:
 
 		Graphics::FrameLimiter limiter(g_system, 60);
 		for (;;) {
+			const uint32 now = getRuntimeClockTicks();
+			if (editingOther && now - textEntryBlinkTimestamp > kDialogueTextEntryBlinkTicks) {
+				textEntryCursorVisible = !textEntryCursorVisible;
+				textEntryBlinkTimestamp = now;
+			}
 			const int hoveredTopicIndex = editingOther ? -1 : getDialogueMenuItemAt(*_menuFont, topics.size(), _mousePos);
-			const bool hoverOther = !editingOther && isDialogueOtherHit(_mousePos);
+			const bool hoverOther = editingOther || isDialogueOtherHit(_mousePos);
 			drawDialogueOverlay(&_keywordBitmap, nullptr, &topics, hoveredTopicIndex, hoverOther,
-				editingOther ? &typedTopic : nullptr);
+				editingOther ? &typedTopic : nullptr, textEntryCursorVisible);
 
 			Common::Event event;
 			while (g_system->getEventManager()->pollEvent(event)) {
 				Common::Error result = Common::kNoError;
-				if (DialogueFlowAccess::handleSystemEvent(_flow, event, result))
+				if (DialogueFlowAccess::handleSystemEvent(_flow, event, result)) {
+					if (editingOther && _entityManager)
+						_entityManager->showCursor();
 					return result;
+				}
 
 				if (editingOther) {
+					bool acceptInput = false;
+					bool cancelInput = false;
 					if (event.type == Common::EVENT_KEYDOWN) {
 						if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
-							typedTopic.clear();
-							editingOther = false;
-							debugC(1, kDebugDialogue, "Harvester: keyword menu cancelled Other input");
-							if (_entityManager)
-								_entityManager->showCursor();
-						} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
+							cancelInput = true;
+						} else if (event.kbd.keycode == Common::KEYCODE_BACKSPACE ||
+								event.kbd.keycode == Common::KEYCODE_LEFT) {
 							if (!typedTopic.empty())
 								typedTopic.deleteLastChar();
+						} else if (event.kbd.keycode == Common::KEYCODE_HOME) {
+							typedTopic.clear();
 						} else if (event.kbd.keycode == Common::KEYCODE_RETURN ||
 								event.kbd.keycode == Common::KEYCODE_KP_ENTER) {
-							if (!typedTopic.empty()) {
-								selectedTopic = typedTopic;
-								selection.fromTypedInput = true;
-								debugC(1, kDebugDialogue, "Harvester: keyword menu typed topic='%s'",
-									selectedTopic.c_str());
-								if (_entityManager)
-									_entityManager->showCursor();
-								return Common::kNoError;
-							}
-						} else if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7e &&
-								typedTopic.size() < 79) {
+							acceptInput = true;
+						} else if (event.kbd.ascii >= 0x1f && event.kbd.ascii <= 0x80 &&
+								event.kbd.ascii != '~' && typedTopic.size() < kDialogueTextEntryMaxLength &&
+								(!_subtitleCftFont || _subtitleCftFont->hasGlyph(event.kbd.ascii))) {
 							typedTopic += (char)event.kbd.ascii;
+							if (_highlightFont->getStringWidth(typedTopic) > kDialogueTextEntryMaxWidth)
+								typedTopic.deleteLastChar();
+						}
+					} else if (event.type == Common::EVENT_LBUTTONDOWN) {
+						acceptInput = Common::Rect(3, 460, 318, 477).contains(event.mouse);
+						cancelInput = Common::Rect(321, 460, 636, 477).contains(event.mouse);
+						if (cancelInput)
+							g_system->delayMillis(400);
+					}
+					if (acceptInput || cancelInput) {
+						editingOther = false;
+						if (_entityManager)
+							_entityManager->showCursor();
+						if (acceptInput && !typedTopic.empty()) {
+							selectedTopic = typedTopic;
+							selection.fromTypedInput = true;
+							debugC(1, kDebugDialogue, "Harvester: keyword menu typed topic='%s'",
+								selectedTopic.c_str());
+							return Common::kNoError;
 						}
+						typedTopic.clear();
+						debugC(1, kDebugDialogue, "Harvester: keyword menu cancelled Other input");
 					}
 					continue;
 				}
@@ -838,6 +866,8 @@ public:
 					if (isDialogueOtherHit(_mousePos)) {
 						typedTopic.clear();
 						editingOther = true;
+						textEntryCursorVisible = false;
+						textEntryBlinkTimestamp = getRuntimeClockTicks();
 						debugC(1, kDebugDialogue, "Harvester: keyword menu entered Other input");
 						if (_entityManager)
 							_entityManager->hideCursor();
@@ -998,7 +1028,8 @@ private:
 
 	void drawDialogueOverlay(const IndexedBitmap *overlayBitmap,
 			const Common::Array<Common::String> *subtitleLines, const Common::Array<Common::String> *topics,
-			int hoveredTopicIndex, bool hoverOther, const Common::String *textEntryValue) {
+			int hoveredTopicIndex, bool hoverOther, const Common::String *textEntryValue,
+			bool textEntryCursorVisible = false) {
 		Graphics::Screen *activeScreen = getActiveScreen();
 		if (!activeScreen)
 			return;
@@ -1057,8 +1088,9 @@ private:
 
 		if (textEntryValue) {
 			drawFontString(*_highlightFont, _highlightFontUsesCft,
-				*textEntryValue + "_", kDialogueTextEntryX, kDialogueTextEntryY,
-				kDialogueKeywordTextWidth - 30, kTextColorNormal);
+				*textEntryValue + (textEntryCursorVisible ? "~" : ""),
+				kDialogueTextEntryX, kDialogueTextEntryY, kDialogueTextEntryMaxWidth,
+				kTextColorNormal);
 		}
 
 		if (_entityManager)


Commit: e4645527209e4a956f1e18a9e55e5a7be392fe0b
    https://github.com/scummvm/scummvm/commit/e4645527209e4a956f1e18a9e55e5a7be392fe0b
Author: Alex Bevilacqua (alex at alexbevi.com)
Date: 2026-10-15T20:22:11-04:00

Commit Message:
HARVESTER: handle back-to-front player turn when at room min depth

Fixes #17370

Assisted-by: Codex:gpt-6.1-sol

Changed paths:
    engines/harvester/player.cpp


diff --git a/engines/harvester/player.cpp b/engines/harvester/player.cpp
index 8d838aa2f48..0136cc01933 100644
--- a/engines/harvester/player.cpp
+++ b/engines/harvester/player.cpp
@@ -1696,13 +1696,19 @@ bool Player::startTurnAnimation(RoomPlayerState &playerState, int targetFacing)
 			playerState.facing < 0 || playerState.facing == targetFacing)
 		return false;
 
-	// update_actor_runtime_state (0x4d750) routes desired states 1/2 from the
-	// opposite horizontal facing through front-facing state 0.
-	const int nextFacing = (playerState.facing == 1 && targetFacing == 2) ||
-			(playerState.facing == 2 && targetFacing == 1) ? 0 : targetFacing;
+	int nextFacing = targetFacing;
+	if ((playerState.facing == 1 && targetFacing == 2) ||
+			(playerState.facing == 2 && targetFacing == 1))
+		nextFacing = 0;
+	else if ((playerState.facing == 0 && targetFacing == 3) ||
+			(playerState.facing == 3 && targetFacing == 0))
+		nextFacing = 2;
 	PlayerTurnAnimationRange range;
-	if (!resolvePlayerTurnAnimationRange(playerState.facing, nextFacing, range))
+	if (playerState.facing == 3 && targetFacing == 0) {
+		range = PlayerTurnAnimationRange{ 0x28, 0x28, false };
+	} else if (!resolvePlayerTurnAnimationRange(playerState.facing, nextFacing, range)) {
 		return false;
+	}
 
 	playerState.turnActive = true;
 	playerState.turnTargetFacing = nextFacing;


Commit: b31124723e689dfdbd6242bac3cd6a66730fe626
    https://github.com/scummvm/scummvm/commit/b31124723e689dfdbd6242bac3cd6a66730fe626
Author: Alex Bevilacqua (alex at alexbevi.com)
Date: 2026-10-15T20:22:11-04:00

Commit Message:
HARVESTER: properly restore animated room elements on re-entry

Fixes #17360

Assisted-by: Codex:gpt-6.1-sol

Changed paths:
    engines/harvester/entity.cpp
    engines/harvester/entity.h


diff --git a/engines/harvester/entity.cpp b/engines/harvester/entity.cpp
index ebd3839a251..0a4a2e13de6 100644
--- a/engines/harvester/entity.cpp
+++ b/engines/harvester/entity.cpp
@@ -241,12 +241,13 @@ void Entity::setAnimationEnabled(bool enabled) {
 	}
 }
 
-void Entity::setCurrentFrame(int frame) {
+void Entity::setCurrentFrame(int frame, bool preserveScreenBase) {
 	if (_frames.empty())
 		return;
 
 	advanceAnimationFrame(frame);
-	updateScreenBaseFromCurrentFrame();
+	if (!preserveScreenBase)
+		updateScreenBaseFromCurrentFrame();
 }
 
 void Entity::setAnimationFrameRange(int firstFrame, int lastFrame, bool looping) {
@@ -900,10 +901,6 @@ Entity *EntityManager::spawnSceneAnimationEntity(const Common::String &name,
 	if (!entity)
 		return nullptr;
 
-	// Native room ANIM entities center the initially shown frame on the record
-	// x/y pivot, lower the render-list depth anchor by half z_extent, and then
-	// keep that cached screen base while later frame ABM x/y offsets shift the
-	// final draw rect.
 	entity->setAnchorMode(kEntityAnchorCentered);
 	entity->setZExtent(3.0f);
 	entity->setPosition(position.x, position.y, z - floorf(MAX<float>(entity->getZExtent(), 0.0f) * 0.5f));
@@ -913,7 +910,7 @@ Entity *EntityManager::spawnSceneAnimationEntity(const Common::String &name,
 	int startFrame = playBackwards ? entity->getLastFrame() : 0;
 	if (!playBackwards && initialFrame >= 0)
 		startFrame = CLIP<int>(initialFrame, 0, entity->getLastFrame());
-	entity->setCurrentFrame(startFrame);
+	entity->setCurrentFrame(startFrame, true);
 	// Non-looping room ANIM records can be authored visible but are started by SET_ANIM.
 	const bool shouldAdvance = active && (looping || initialFrame >= 0);
 	entity->setAnimationEnabled(shouldAdvance);
diff --git a/engines/harvester/entity.h b/engines/harvester/entity.h
index b14011749a3..b561f15c02c 100644
--- a/engines/harvester/entity.h
+++ b/engines/harvester/entity.h
@@ -92,7 +92,7 @@ public:
 	int getAnimationRate() const { return _animationRate; }
 	void setAnimationEnabled(bool enabled);
 	bool isAnimationEnabled() const { return _animationEnabled; }
-	void setCurrentFrame(int frame);
+	void setCurrentFrame(int frame, bool preserveScreenBase = false);
 	int getCurrentFrame() const { return _currentFrame; }
 	int getLastFrame() const { return _lastFrame; }
 	uint getFrameCount() const { return _frames.size(); }




More information about the Scummvm-git-logs mailing list