[Scummvm-git-logs] scummvm branch-2-5 -> 643e2985bf55954f0328cc3834056745bc0d9abd

mgerhardy martin.gerhardy at gmail.com
Mon Nov 1 17:12:56 UTC 2021


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

Summary:
bbc8430908 TWINE: merged the changes from master
039f700113 TWINE: Add LBA Preview Version dated August 15 1994
7aa3ad2067 TWINE: Mark later Preview Version as unstable
265853d849 TWINE: track index of -1 means to stop the music
d237f772de TWINE: fixed extra flag names
701c1de7dc TWINE: replaced magic numbers and named enum constants
c19e28b379 TWINE: merged with latest master
b6e441fe36 TWINE: Add French demo dated 21 October 1994
d13f9178f4 TWINE: renamed enum value
2f1204482d TWINE: added isDotEmuEnhanced() helper method
643e2985bf TWINE: the demo of lba1 has three scenes


Commit: bbc8430908cfc4df5654367a143c3ff5c2d2bd1f
    https://github.com/scummvm/scummvm/commit/bbc8430908cfc4df5654367a143c3ff5c2d2bd1f
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: merged the changes from master

Changed paths:
  A engines/twine/movies.cpp
  A engines/twine/movies.h
  R engines/twine/flamovies.cpp
  R engines/twine/flamovies.h
    engines/twine/audio/music.cpp
    engines/twine/audio/sound.cpp
    engines/twine/audio/sound.h
    engines/twine/holomap.cpp
    engines/twine/holomap.h
    engines/twine/menu/menu.cpp
    engines/twine/menu/menuoptions.cpp
    engines/twine/module.mk
    engines/twine/parser/body.cpp
    engines/twine/parser/bodytypes.h
    engines/twine/parser/parser.h
    engines/twine/parser/text.cpp
    engines/twine/renderer/redraw.cpp
    engines/twine/renderer/renderer.cpp
    engines/twine/renderer/renderer.h
    engines/twine/renderer/screens.cpp
    engines/twine/renderer/screens.h
    engines/twine/resources/hqr.h
    engines/twine/resources/resources.cpp
    engines/twine/resources/resources.h
    engines/twine/scene/actor.h
    engines/twine/scene/animations.cpp
    engines/twine/scene/gamestate.cpp
    engines/twine/scene/grid.h
    engines/twine/scene/scene.cpp
    engines/twine/script/script_life_v1.cpp
    engines/twine/script/script_move_v1.cpp
    engines/twine/shared.h
    engines/twine/twine.cpp
    engines/twine/twine.h


diff --git a/engines/twine/audio/music.cpp b/engines/twine/audio/music.cpp
index 34a1e811cd..5dcd71bf9f 100644
--- a/engines/twine/audio/music.cpp
+++ b/engines/twine/audio/music.cpp
@@ -171,10 +171,12 @@ void Music::stopTrackMusic() {
 
 bool Music::playMidiMusic(int32 midiIdx, int32 loop) {
 	if (!_engine->_cfgfile.Sound || _engine->_cfgfile.MidiType == MIDIFILE_NONE) {
+		debug("midi disabled - skip playing %i", midiIdx);
 		return false;
 	}
 
 	if (midiIdx == currentMusic) {
+		debug("already playing %i", midiIdx);
 		return true;
 	}
 
@@ -206,8 +208,10 @@ bool Music::playMidiMusic(int32 midiIdx, int32 loop) {
 
 	int32 midiSize = HQR::getAllocEntry(&midiPtr, filename, midiIdx);
 	if (midiSize == 0) {
+		debug("Could not find midi file for index %i", midiIdx);
 		return false;
 	}
+	debug("Play midi file for index %i", midiIdx);
 	_midiPlayer.play(midiPtr, midiSize);
 	return true;
 }
diff --git a/engines/twine/audio/sound.cpp b/engines/twine/audio/sound.cpp
index 87af4466d5..a162b3bf7c 100644
--- a/engines/twine/audio/sound.cpp
+++ b/engines/twine/audio/sound.cpp
@@ -30,7 +30,7 @@
 #include "common/util.h"
 #include "twine/parser/text.h"
 #include "twine/scene/collision.h"
-#include "twine/flamovies.h"
+#include "twine/movies.h"
 #include "twine/scene/grid.h"
 #include "twine/resources/hqr.h"
 #include "twine/scene/movements.h"
@@ -60,7 +60,7 @@ void Sound::setSamplePosition(int32 channelIdx, int32 x, int32 y, int32 z) {
 	_engine->_system->getMixer()->setChannelVolume(samplesPlaying[channelIdx], targetVolume);
 }
 
-void Sound::playFlaSample(int32 index, int32 repeat, int32 x, int32 y) {
+void Sound::playFlaSample(int32 index, int32 repeat, uint8 balance, int32 volumeLeft, int32 volumeRight) {
 	if (!_engine->_cfgfile.Sound) {
 		return;
 	}
@@ -158,7 +158,11 @@ bool Sound::playSample(int channelIdx, int index, uint8 *sampPtr, int32 sampSize
 	if (loop == -1) {
 		loop = 0;
 	}
-	_engine->_system->getMixer()->playStream(soundType, &samplesPlaying[channelIdx], Audio::makeLoopingAudioStream(audioStream, loop), index);
+	Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(audioStream, loop);
+	Audio::SoundHandle *handle = &samplesPlaying[channelIdx];
+	const byte volume = Audio::Mixer::kMaxChannelVolume;
+	// TODO: implement balance
+	_engine->_system->getMixer()->playStream(soundType, handle, loopStream, index, volume);
 	return true;
 }
 
diff --git a/engines/twine/audio/sound.h b/engines/twine/audio/sound.h
index a28d13f688..74a3771d0d 100644
--- a/engines/twine/audio/sound.h
+++ b/engines/twine/audio/sound.h
@@ -44,7 +44,8 @@ enum _Samples {
 	TaskCompleted = 41,
 	Hit = 86,
 	ItemFound = 97,
-	WalkFloorBegin = 126
+	WalkFloorBegin = 126,
+	WalkFloorRightBegin = 141
 };
 }
 
@@ -80,10 +81,8 @@ public:
 	 * Play FLA movie samples
 	 * @param index sample index under flasamp.hqr file
 	 * @param repeat number of times to repeat the sample
-	 * @param x unknown x variable
-	 * @param y unknown y variable
 	 */
-	void playFlaSample(int32 index, int32 repeat, int32 x, int32 y);
+	void playFlaSample(int32 index, int32 repeat, uint8 balance, int32 volumeLeft, int32 volumeRight);
 
 	/** Update sample position in channel */
 	void setSamplePosition(int32 channelIdx, int32 x, int32 y, int32 z);
diff --git a/engines/twine/holomap.cpp b/engines/twine/holomap.cpp
index f63728c605..069e986df3 100644
--- a/engines/twine/holomap.cpp
+++ b/engines/twine/holomap.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "twine/holomap.h"
+#include "common/algorithm.h"
 #include "common/debug.h"
 #include "common/memstream.h"
 #include "common/stream.h"
@@ -61,18 +62,18 @@ static const float zDistanceTrajectory = 5300.0f;
 
 Holomap::Holomap(TwinEEngine *engine) : _engine(engine) {}
 
-int32 Holomap::distance(int32 distance) const {
+int32 Holomap::distance(float distance) const {
 	const float w = (float)_engine->width() / (float)ORIGINAL_WIDTH;
 	const float h = (float)_engine->height() / (float)ORIGINAL_HEIGHT;
 	const float f = MIN<float>(w, h);
-	return (int32)((float)distance / f);
+	return (int32)(distance / f);
 }
 
-int32 Holomap::scale(int32 val) const {
+int32 Holomap::scale(float val) const {
 	const float w = (float)_engine->width() / (float)ORIGINAL_WIDTH;
 	const float h = (float)_engine->height() / (float)ORIGINAL_HEIGHT;
 	const float f = MIN<float>(w, h);
-	return (int32)((float)val * f);
+	return (int32)(val * f);
 }
 
 bool Holomap::loadLocations() {
@@ -121,40 +122,43 @@ void Holomap::clearHolomapPosition(int32 locationIdx) {
 }
 
 void Holomap::loadHolomapGFX() {
-	_engine->_screens->loadCustomPalette(RESSHQR_HOLOPAL);
+	constexpr TwineResource resource(Resources::HQR_RESS_FILE, RESSHQR_HOLOPAL);
+	_engine->_screens->loadCustomPalette(resource);
 
-	int32 j = 576;
-	for (int32 i = 0; i < 96; i += 3, j += 3) {
+	int32 j = HOLOMAP_PALETTE_INDEX * 3;
+	const int32 n = NUM_HOLOMAPCOLORS * 3;
+	for (int32 i = 0; i < n; i += 3, j += 3) {
 		_paletteHolomap[i + 0] = _engine->_screens->_palette[j + 0];
 		_paletteHolomap[i + 1] = _engine->_screens->_palette[j + 1];
 		_paletteHolomap[i + 2] = _engine->_screens->_palette[j + 2];
 	}
 
-	j = 576;
-	for (int32 i = 96; i < 189; i += 3, j += 3) {
+	j = HOLOMAP_PALETTE_INDEX * 3;
+	for (int32 i = n; i < 2 * n - 3; i += 3, j += 3) {
 		_paletteHolomap[i + 0] = _engine->_screens->_palette[j + 0];
 		_paletteHolomap[i + 1] = _engine->_screens->_palette[j + 1];
 		_paletteHolomap[i + 2] = _engine->_screens->_palette[j + 2];
 	}
 
 	prepareHolomapProjectedPositions();
-	prepareHolomapSurface();
-	_holomapPaletteIndex = 0;
-}
 
-static int sortHolomapSurfaceCoordsByDepth(const void *a1, const void *a2) {
-	return (int)*(const int16 *)a1 - (int)*(const int16 *)a2;
+	Common::SeekableReadStream *surfaceStream = HQR::makeReadStream(TwineResource(Resources::HQR_RESS_FILE, RESSHQR_HOLOSURFACE));
+	if (surfaceStream == nullptr) {
+		error("Failed to load holomap surface");
+	}
+	prepareHolomapSurface(surfaceStream);
+	delete surfaceStream;
+	_holomapPaletteIndex = 0;
 }
 
-void Holomap::prepareHolomapSurface() {
-	Common::MemoryReadStream stream(_engine->_resources->_holomapSurfacePtr, _engine->_resources->_holomapSurfaceSize);
+void Holomap::prepareHolomapSurface(Common::SeekableReadStream *holomapSurfaceStream) {
 	int holomapSurfaceArrayIdx = 0;
 	_engine->_renderer->setBaseRotation(0, 0, 0);
 	for (int alpha = -ANGLE_90; alpha <= ANGLE_90; alpha += ANGLE_11_25) {
-		const int32 rot = stream.readByte();
-		stream.seek(-1, SEEK_CUR);
+		const int32 rot = holomapSurfaceStream->readByte();
+		holomapSurfaceStream->seek(-1, SEEK_CUR);
 		for (int beta = 0; beta < ANGLE_360; beta += ANGLE_11_25) {
-			const int32 rotX = stream.readByte();
+			const int32 rotX = holomapSurfaceStream->readByte();
 			const IVec3 &rotVec = _engine->_renderer->getHolomapRotation(rotX, alpha, beta);
 			_holomapSurface[holomapSurfaceArrayIdx].x = rotVec.x;
 			_holomapSurface[holomapSurfaceArrayIdx].y = rotVec.y;
@@ -167,44 +171,41 @@ void Holomap::prepareHolomapSurface() {
 		_holomapSurface[holomapSurfaceArrayIdx].z = rotVec.z;
 		++holomapSurfaceArrayIdx;
 	}
+	assert(holomapSurfaceStream->eos());
 }
 
-// verified with disassembly
 void Holomap::prepareHolomapProjectedPositions() {
 	int projectedIndex = 0;
-	for (int32 angle = -ANGLE_90; angle <= ANGLE_90; angle += ANGLE_11_25) {
-		int rotation = 0;
-		for (int32 i = 0; i < ANGLE_11_25; ++i) {
-			_projectedSurfacePositions[projectedIndex].x2 = _engine->_screens->crossDot(0, 0xffff, ANGLE_360 - 1, rotation);
-			if (angle == ANGLE_90) {
+	for (int32 alpha = -ANGLE_90; alpha <= ANGLE_90; alpha += ANGLE_11_25) {
+		for (int32 beta = 0; beta < ANGLE_360; beta += ANGLE_11_25) {
+			_projectedSurfacePositions[projectedIndex].x2 = _engine->_screens->lerp(0, 0xffff, ANGLE_360 - 1, beta);
+			if (alpha == ANGLE_90) {
 				_projectedSurfacePositions[projectedIndex].y2 = -1;
 			} else {
-				_projectedSurfacePositions[projectedIndex].y2 = ((angle + ANGLE_90) * ANGLE_90) / 2;
+				_projectedSurfacePositions[projectedIndex].y2 = ((alpha + ANGLE_90) * ANGLE_90) / 2;
 			}
-			rotation += ANGLE_11_25;
 			++projectedIndex;
 		}
 		_projectedSurfacePositions[projectedIndex].x2 = -1;
-		if (angle == ANGLE_90) {
+		if (alpha == ANGLE_90) {
 			_projectedSurfacePositions[projectedIndex].y2 = -1;
 		} else {
-			_projectedSurfacePositions[projectedIndex].y2 = ((angle + ANGLE_90) * ANGLE_90) / 2;
+			_projectedSurfacePositions[projectedIndex].y2 = ((alpha + ANGLE_90) * ANGLE_90) / 2;
 		}
 		++projectedIndex;
 	}
 }
 
-// verified with disassembly
 void Holomap::prepareHolomapPolygons() {
 	int holomapSortArrayIdx = 0;
 	int holomapSurfaceArrayIdx = 0;
 	_projectedSurfaceIndex = 0;
-	for (int32 angle = -ANGLE_90; angle <= ANGLE_90; angle += ANGLE_11_25) {
-		int rotation = 0;
-		for (int32 stepWidth = 0; stepWidth < ANGLE_11_25; ++stepWidth) {
-			IVec3* vec = &_holomapSurface[holomapSurfaceArrayIdx++];
+	for (int32 alpha = -ANGLE_90; alpha <= ANGLE_90; alpha += ANGLE_11_25) {
+		int rotation = ANGLE_0;
+		for (int32 beta = 0; beta < ANGLE_11_25; ++beta) {
+			IVec3 *vec = &_holomapSurface[holomapSurfaceArrayIdx++];
 			const IVec3 &destPos = _engine->_renderer->getBaseRotationPosition(vec->x, vec->y, vec->z);
-			if (angle != ANGLE_90) {
+			if (alpha != ANGLE_90) {
 				_holomapSort[holomapSortArrayIdx].z = destPos.z;
 				_holomapSort[holomapSortArrayIdx].projectedPosIdx = _projectedSurfaceIndex;
 				++holomapSortArrayIdx;
@@ -215,7 +216,7 @@ void Holomap::prepareHolomapPolygons() {
 			rotation += ANGLE_11_25;
 			++_projectedSurfaceIndex;
 		}
-		IVec3* vec = &_holomapSurface[holomapSurfaceArrayIdx++];
+		IVec3 *vec = &_holomapSurface[holomapSurfaceArrayIdx++];
 		const IVec3 &destPos = _engine->_renderer->getBaseRotationPosition(vec->x, vec->y, vec->z);
 		const IVec3 &projPos = _engine->_renderer->projectPositionOnScreen(destPos);
 		_projectedSurfacePositions[_projectedSurfaceIndex].x1 = projPos.x;
@@ -226,7 +227,7 @@ void Holomap::prepareHolomapPolygons() {
 	assert(holomapSortArrayIdx == ARRAYSIZE(_holomapSort));
 	assert(holomapSurfaceArrayIdx == ARRAYSIZE(_holomapSurface));
 	assert(_projectedSurfaceIndex == ARRAYSIZE(_projectedSurfacePositions));
-	qsort(_holomapSort, ARRAYSIZE(_holomapSort), sizeof(HolomapSort), sortHolomapSurfaceCoordsByDepth);
+	Common::sort(_holomapSort, _holomapSort + ARRAYSIZE(_holomapSort), [](const HolomapSort &a, const HolomapSort &b) { return a.z < b.z; });
 }
 
 bool Holomap::isTriangleVisible(const Vertex *vertices) const {
@@ -237,12 +238,13 @@ bool Holomap::isTriangleVisible(const Vertex *vertices) const {
 	return iVar2 - iVar1 != 0 && iVar1 <= iVar2;
 }
 
-void Holomap::renderHolomapSurfacePolygons() {
+#define SURFACE_POS_OFFSET ((ANGLE_360 / ANGLE_11_25) + 1)
+void Holomap::renderHolomapSurfacePolygons(uint8 *holomapImage, uint32 holomapImageSize) {
 	prepareHolomapPolygons();
 	for (int32 i = 0; i < ARRAYSIZE(_holomapSort); ++i) {
 		assert(_holomapSort[i].projectedPosIdx + 34 < _projectedSurfaceIndex);
 		const HolomapProjectedPos &pos1 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 0];
-		const HolomapProjectedPos &pos2 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 33];
+		const HolomapProjectedPos &pos2 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 0 + SURFACE_POS_OFFSET];
 		const HolomapProjectedPos &pos3 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 1];
 		Vertex vertexCoordinates[3];
 		vertexCoordinates[0].x = pos1.x1;
@@ -252,17 +254,17 @@ void Holomap::renderHolomapSurfacePolygons() {
 		vertexCoordinates[2].x = pos3.x1;
 		vertexCoordinates[2].y = pos3.y1;
 		if (isTriangleVisible(vertexCoordinates)) {
-			Vertex vertexCoordinates2[3];
-			vertexCoordinates2[0].x = pos1.x2;
-			vertexCoordinates2[0].y = pos1.y2;
-			vertexCoordinates2[1].x = pos2.x2;
-			vertexCoordinates2[1].y = pos2.y2;
-			vertexCoordinates2[2].x = pos3.x2;
-			vertexCoordinates2[2].y = pos3.y2;
-			_engine->_renderer->renderHolomapVertices(vertexCoordinates, vertexCoordinates2);
+			Vertex textureCoordinates[3];
+			textureCoordinates[0].x = pos1.x2;
+			textureCoordinates[0].y = pos1.y2;
+			textureCoordinates[1].x = pos2.x2;
+			textureCoordinates[1].y = pos2.y2;
+			textureCoordinates[2].x = pos3.x2;
+			textureCoordinates[2].y = pos3.y2;
+			_engine->_renderer->renderHolomapVertices(vertexCoordinates, textureCoordinates, holomapImage, holomapImageSize);
 		}
-		const HolomapProjectedPos &pos4 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 33];
-		const HolomapProjectedPos &pos5 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 34];
+		const HolomapProjectedPos &pos4 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 0 + SURFACE_POS_OFFSET];
+		const HolomapProjectedPos &pos5 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 1 + SURFACE_POS_OFFSET];
 		const HolomapProjectedPos &pos6 = _projectedSurfacePositions[_holomapSort[i].projectedPosIdx + 1];
 		vertexCoordinates[0].x = pos4.x1;
 		vertexCoordinates[0].y = pos4.y1;
@@ -271,14 +273,14 @@ void Holomap::renderHolomapSurfacePolygons() {
 		vertexCoordinates[2].x = pos6.x1;
 		vertexCoordinates[2].y = pos6.y1;
 		if (isTriangleVisible(vertexCoordinates)) {
-			Vertex vertexCoordinates2[3];
-			vertexCoordinates2[0].x = pos4.x2;
-			vertexCoordinates2[0].y = pos4.y2;
-			vertexCoordinates2[1].x = pos5.x2;
-			vertexCoordinates2[1].y = pos5.y2;
-			vertexCoordinates2[2].x = pos6.x2;
-			vertexCoordinates2[2].y = pos6.y2;
-			_engine->_renderer->renderHolomapVertices(vertexCoordinates, vertexCoordinates2);
+			Vertex textureCoordinates[3];
+			textureCoordinates[0].x = pos4.x2;
+			textureCoordinates[0].y = pos4.y2;
+			textureCoordinates[1].x = pos5.x2;
+			textureCoordinates[1].y = pos5.y2;
+			textureCoordinates[2].x = pos6.x2;
+			textureCoordinates[2].y = pos6.y2;
+			_engine->_renderer->renderHolomapVertices(vertexCoordinates, textureCoordinates, holomapImage, holomapImageSize);
 		}
 	}
 }
@@ -348,7 +350,13 @@ void Holomap::drawHolomapTrajectory(int32 trajectoryIndex) {
 	_engine->_renderer->setCameraPosition(cameraPosX, cameraPosY, 128, 1024, 1024);
 	_engine->_renderer->setCameraAngle(0, 0, 0, data->pos.x, data->pos.y, data->pos.z, distance(zDistanceTrajectory));
 
-	renderHolomapSurfacePolygons();
+	constexpr TwineResource holomapImageRes(Resources::HQR_RESS_FILE, RESSHQR_HOLOIMG);
+	uint8 *holomapImagePtr = nullptr;
+	const int32 holomapImageSize = HQR::getAllocEntry(&holomapImagePtr, holomapImageRes);
+	if (holomapImageSize == 0) {
+		error("Failed to load holomap image");
+	}
+	renderHolomapSurfacePolygons(holomapImagePtr, holomapImageSize);
 
 	const Location &loc = _locations[data->locationIdx];
 	renderHolomapPointModel(data->pos, loc.angle.x, loc.angle.y);
@@ -375,8 +383,8 @@ void Holomap::drawHolomapTrajectory(int32 trajectoryIndex) {
 
 		if (!fadeInPalette && waterPaletteChangeTimer < _engine->_lbaTime) {
 			// animate the water surface
-			_engine->setPalette(192, 32, &_paletteHolomap[3 * _holomapPaletteIndex++]);
-			if (_holomapPaletteIndex == 32) {
+			_engine->setPalette(HOLOMAP_PALETTE_INDEX, NUM_HOLOMAPCOLORS, &_paletteHolomap[3 * _holomapPaletteIndex++]);
+			if (_holomapPaletteIndex == NUM_HOLOMAPCOLORS) {
 				_holomapPaletteIndex = 0;
 			}
 			waterPaletteChangeTimer = _engine->_lbaTime + 3;
@@ -423,6 +431,8 @@ void Holomap::drawHolomapTrajectory(int32 trajectoryIndex) {
 
 	_engine->_text->initSceneTextBank();
 	_engine->_input->enableKeyMap(mainKeyMapId);
+
+	free(holomapImagePtr);
 }
 
 int32 Holomap::getNextHolomapLocation(int32 currentLocation, int32 dir) const {
@@ -521,6 +531,13 @@ void Holomap::processHolomap() {
 	const int32 cameraPosY = scale(190);
 	_engine->_renderer->setCameraPosition(cameraPosX, cameraPosY, 128, 1024, 1024);
 
+	constexpr TwineResource holomapImageRes(Resources::HQR_RESS_FILE, RESSHQR_HOLOIMG);
+	uint8 *holomapImagePtr = nullptr;
+	const int32 holomapImageSize = HQR::getAllocEntry(&holomapImagePtr, holomapImageRes);
+	if (holomapImageSize == 0) {
+		error("Failed to load holomap image");
+	}
+
 	int32 currentLocation = _engine->_scene->_currentSceneIdx;
 	_engine->_text->drawHolomapLocation(_locations[currentLocation].textIndex);
 
@@ -586,8 +603,8 @@ void Holomap::processHolomap() {
 
 		if (!fadeInPalette && waterPaletteChangeTimer < _engine->_lbaTime) {
 			// animate the water surface
-			_engine->setPalette(192, 32, &_paletteHolomap[3 * _holomapPaletteIndex++]);
-			if (_holomapPaletteIndex == 32) {
+			_engine->setPalette(HOLOMAP_PALETTE_INDEX, NUM_HOLOMAPCOLORS, &_paletteHolomap[3 * _holomapPaletteIndex++]);
+			if (_holomapPaletteIndex == NUM_HOLOMAPCOLORS) {
 				_holomapPaletteIndex = 0;
 			}
 			waterPaletteChangeTimer = _engine->_lbaTime + 3;
@@ -603,7 +620,7 @@ void Holomap::processHolomap() {
 			renderLocations(xRot, yRot, 0, false);
 			_engine->_renderer->setBaseRotation(xRot, yRot, 0, true);
 			_engine->_renderer->setBaseRotationPos(0, 0, distance(zDistanceHolomap));
-			renderHolomapSurfacePolygons();
+			renderHolomapSurfacePolygons(holomapImagePtr, holomapImageSize);
 			renderLocations(xRot, yRot, 0, true);
 			drawHolomapText(_engine->width() / 2, 25, "HoloMap");
 			if (rotate) {
@@ -618,10 +635,6 @@ void Holomap::processHolomap() {
 
 		++_engine->_lbaTime;
 
-		// TODO: text afterwards on top (not before as it is currently implemented)?
-		// pos 0x140,0x19?
-
-		//_engine->restoreFrontBuffer();
 		if (fadeInPalette) {
 			fadeInPalette = false;
 			_engine->_screens->fadeToPal(_engine->_screens->_paletteRGBACustom);
@@ -639,6 +652,8 @@ void Holomap::processHolomap() {
 
 	_engine->_input->enableKeyMap(mainKeyMapId);
 	_engine->_text->initSceneTextBank();
+
+	free(holomapImagePtr);
 }
 
 const char *Holomap::getLocationName(int index) const {
diff --git a/engines/twine/holomap.h b/engines/twine/holomap.h
index 5b4f6d1bb2..972146e348 100644
--- a/engines/twine/holomap.h
+++ b/engines/twine/holomap.h
@@ -26,6 +26,13 @@
 #include "twine/shared.h"
 #include "common/scummsys.h"
 
+#define NUM_HOLOMAPCOLORS 32
+#define HOLOMAP_PALETTE_INDEX 192
+
+namespace Common {
+class SeekableReadStream;
+}
+
 namespace TwinE {
 
 class TwinEEngine;
@@ -86,17 +93,17 @@ private:
 	 * Renders a holomap path with single path points appearing slowly one after another
 	 */
 	void renderHolomapPointModel(const IVec3 &angle, int32 x, int32 y);
-	void prepareHolomapSurface();
+	void prepareHolomapSurface(Common::SeekableReadStream *holomapSurfaceStream);
 	void prepareHolomapProjectedPositions();
 	void prepareHolomapPolygons();
-	void renderHolomapSurfacePolygons();
+	void renderHolomapSurfacePolygons(uint8 *holomapImage, uint32 holomapImageSize);
 	void renderHolomapVehicle(uint &frameNumber, ActorMoveStruct &move, AnimTimerDataStruct &animTimerData, BodyData &bodyData, AnimData &animData);
 
 	/**
 	 * Controls the size/zoom of the holomap planet
 	 */
-	int32 distance(int32 distance) const;
-	int32 scale(int32 val) const;
+	int32 distance(float distance) const;
+	int32 scale(float val) const;
 
 public:
 	Holomap(TwinEEngine *engine);
diff --git a/engines/twine/menu/menu.cpp b/engines/twine/menu/menu.cpp
index 795489ba06..2a24e6ad3d 100644
--- a/engines/twine/menu/menu.cpp
+++ b/engines/twine/menu/menu.cpp
@@ -256,22 +256,22 @@ void Menu::drawButtonGfx(const MenuSettings *menuSettings, const Common::Rect &r
 			switch (buttonId) {
 			case MenuButtonTypes::kMusicVolume: {
 				const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
-				newWidth = _engine->_screens->crossDot(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
+				newWidth = _engine->_screens->lerp(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
 				break;
 			}
 			case MenuButtonTypes::kSoundVolume: {
 				const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kSFXSoundType);
-				newWidth = _engine->_screens->crossDot(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
+				newWidth = _engine->_screens->lerp(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
 				break;
 			}
 			case MenuButtonTypes::kCDVolume: {
 				const AudioCDManager::Status status = _engine->_system->getAudioCDManager()->getStatus();
-				newWidth = _engine->_screens->crossDot(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, status.volume);
+				newWidth = _engine->_screens->lerp(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, status.volume);
 				break;
 			}
 			case MenuButtonTypes::kSpeechVolume: {
 				const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType);
-				newWidth = _engine->_screens->crossDot(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
+				newWidth = _engine->_screens->lerp(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
 				break;
 			}
 			}
@@ -591,6 +591,8 @@ int32 Menu::processMenu(MenuSettings *menuSettings, bool showCredits) {
 		}
 		if (showCredits && loopMillis - startMillis > 11650) {
 			_engine->_menuOptions->showCredits();
+			_engine->_text->initTextBank(TextBankId::Options_and_menus);
+
 			// TODO the original game also performs these actions:
 			// play FLA_DRAGON3 fla
 			// display RESSHQR_INTROSCREEN1IMG
@@ -832,7 +834,7 @@ int32 Menu::giveupMenu() {
 void Menu::drawHealthBar(int32 left, int32 right, int32 top, int32 barLeftPadding, int32 barHeight) {
 	_engine->_grid->drawSprite(left, top + 3, _engine->_resources->_spriteData[SPRITEHQR_LIFEPOINTS]);
 	const int32 barLeft = left + barLeftPadding;
-	const int32 healthBarRight = _engine->_screens->crossDot(barLeft, right, 50, _engine->_scene->_sceneHero->_life);
+	const int32 healthBarRight = _engine->_screens->lerp(barLeft, right, 50, _engine->_scene->_sceneHero->_life);
 	const int32 barBottom = top + barHeight;
 	_engine->_interface->drawFilledRect(Common::Rect(barLeft, top, healthBarRight, barBottom), COLOR_91);
 	drawRectBorders(Common::Rect(barLeft, top, right, barBottom));
@@ -841,13 +843,13 @@ void Menu::drawHealthBar(int32 left, int32 right, int32 top, int32 barLeftPaddin
 void Menu::drawCloverLeafs(int32 newBoxLeft, int32 boxRight, int32 top) {
 	// Clover leaf boxes
 	for (int32 i = 0; i < _engine->_gameState->_inventoryNumLeafsBox; i++) {
-		const int32 leftSpritePos = _engine->_screens->crossDot(newBoxLeft, boxRight, 10, i);
+		const int32 leftSpritePos = _engine->_screens->lerp(newBoxLeft, boxRight, 10, i);
 		_engine->_grid->drawSprite(leftSpritePos, top + 58, _engine->_resources->_spriteData[SPRITEHQR_CLOVERLEAFBOX]);
 	}
 
 	// Clover leafs
 	for (int32 i = 0; i < _engine->_gameState->_inventoryNumLeafs; i++) {
-		const int32 leftSpritePos = _engine->_screens->crossDot(newBoxLeft, boxRight, 10, i);
+		const int32 leftSpritePos = _engine->_screens->lerp(newBoxLeft, boxRight, 10, i);
 		_engine->_grid->drawSprite(leftSpritePos + 2, top + 60, _engine->_resources->_spriteData[SPRITEHQR_CLOVERLEAF]);
 	}
 }
@@ -865,7 +867,7 @@ void Menu::drawMagicPointsBar(int32 left, int32 right, int32 top, int32 barLeftP
 	}
 	const int32 barLeft = left + barLeftPadding;
 	const int32 barBottom = top + barHeight;
-	const int32 barRight = _engine->_screens->crossDot(barLeft, right, 80, _engine->_gameState->_inventoryMagicPoints);
+	const int32 barRight = _engine->_screens->lerp(barLeft, right, 80, _engine->_gameState->_inventoryMagicPoints);
 	const Common::Rect pointsRect(barLeft, top, barRight, barBottom);
 	_engine->_interface->drawFilledRect(pointsRect, COLOR_75);
 	drawRectBorders(barLeft, top, barLeft + _engine->_gameState->_magicLevelIdx * 80, barBottom);
diff --git a/engines/twine/menu/menuoptions.cpp b/engines/twine/menu/menuoptions.cpp
index 62b108c083..31cb62cb5a 100644
--- a/engines/twine/menu/menuoptions.cpp
+++ b/engines/twine/menu/menuoptions.cpp
@@ -29,7 +29,7 @@
 #include "savestate.h"
 #include "twine/audio/music.h"
 #include "twine/audio/sound.h"
-#include "twine/flamovies.h"
+#include "twine/movies.h"
 #include "twine/scene/gamestate.h"
 #include "twine/input.h"
 #include "twine/menu/interface.h"
@@ -53,7 +53,7 @@ void MenuOptions::newGame() {
 	_engine->_cfgfile.FlagDisplayText = true;
 
 	// intro screen 1 - twinsun
-	_engine->_screens->loadImage(RESSHQR_INTROSCREEN1IMG, RESSHQR_INTROSCREEN1PAL);
+	_engine->_screens->loadImage(TwineImage(Resources::HQR_RESS_FILE, 15, 16));
 
 	_engine->_text->_drawTextBoxBackground = false;
 	_engine->_text->_renderTextTriangle = true;
@@ -66,11 +66,11 @@ void MenuOptions::newGame() {
 
 	// intro screen 2
 	if (!aborted) {
-		_engine->_screens->loadImage(RESSHQR_INTROSCREEN2IMG, RESSHQR_INTROSCREEN2PAL);
+		_engine->_screens->loadImage(TwineImage(Resources::HQR_RESS_FILE, 17, 18));
 		aborted |= _engine->_text->drawTextProgressive(TextId::kIntroText2);
 
 		if (!aborted) {
-			_engine->_screens->loadImage(RESSHQR_INTROSCREEN3IMG, RESSHQR_INTROSCREEN3PAL);
+			_engine->_screens->loadImage(TwineImage(Resources::HQR_RESS_FILE, 19, 20));
 			aborted |= _engine->_text->drawTextProgressive(TextId::kIntroText3);
 		}
 	}
@@ -80,7 +80,7 @@ void MenuOptions::newGame() {
 	_engine->_screens->clearScreen();
 
 	if (!aborted) {
-		// _engine->_music->playMidiMusic(1);
+		_engine->_music->playMidiMusic(1);
 		_engine->_flaMovies->playFlaMovie(FLA_INTROD);
 	}
 
diff --git a/engines/twine/module.mk b/engines/twine/module.mk
index c2f59a66d0..b7ea4919a4 100644
--- a/engines/twine/module.mk
+++ b/engines/twine/module.mk
@@ -42,7 +42,7 @@ MODULE_OBJS := \
 	resources/lzss.o \
 	resources/resources.o \
 	\
-	flamovies.o \
+	movies.o \
 	holomap.o \
 	input.o \
 	metaengine.o \
diff --git a/engines/twine/flamovies.cpp b/engines/twine/movies.cpp
similarity index 82%
rename from engines/twine/flamovies.cpp
rename to engines/twine/movies.cpp
index f9cc6eb858..ef70aa0664 100644
--- a/engines/twine/flamovies.cpp
+++ b/engines/twine/movies.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "twine/flamovies.h"
+#include "twine/movies.h"
 #include "common/endian.h"
 #include "common/file.h"
 #include "common/system.h"
@@ -34,7 +34,9 @@
 #include "twine/resources/hqr.h"
 #include "twine/resources/resources.h"
 #include "twine/scene/grid.h"
+#include "twine/shared.h"
 #include "twine/twine.h"
+#include "video/smk_decoder.h"
 
 namespace TwinE {
 
@@ -43,13 +45,13 @@ enum FlaFrameOpcode {
 	kLoadPalette = 1,
 	kFade = 2,
 	kPlaySample = 3,
-	kFlaUnknown4 = 4,
+	kSampleBalance = 4,
 	kStopSample = 5,
 	kDeltaFrame = 6,
-	kFlaUnknown7 = 7,
+	kBlackFrame = 7,
 	kKeyFrame = 8,
-	kFlaUnknown9 = 9,
-	kFlaUnknown16SameAs9 = 16
+	kCopy = 9,
+	kCopy2 = 16
 };
 
 /** FLA movie sample structure */
@@ -60,18 +62,15 @@ struct FLASampleStruct {
 	int16 freq = 0;
 	/** Numbers of time to repeat */
 	int16 repeat = 0;
-	/** Dummy variable */
-	int8 dummy = 0;
-	/** Unknown x */
-	uint8 x = 0;
-	/** Unknown y */
-	uint8 y = 0;
+	uint8 balance = 0;
+	uint8 volumeLeft = 0;
+	uint8 volumeRight = 0;
 };
 
 /** FLA movie extension */
 #define FLA_EXT ".fla"
 
-void FlaMovies::drawKeyFrame(Common::MemoryReadStream &stream, int32 width, int32 height) {
+void Movies::drawKeyFrame(Common::MemoryReadStream &stream, int32 width, int32 height) {
 	uint8 *destPtr = (uint8 *)_flaBuffer;
 	uint8 *startOfLine = destPtr;
 
@@ -97,7 +96,7 @@ void FlaMovies::drawKeyFrame(Common::MemoryReadStream &stream, int32 width, int3
 	}
 }
 
-void FlaMovies::drawDeltaFrame(Common::MemoryReadStream &stream, int32 width) {
+void Movies::drawDeltaFrame(Common::MemoryReadStream &stream, int32 width) {
 	const uint16 skip = stream.readUint16LE() * width;
 	const int32 height = stream.readSint16LE();
 
@@ -126,7 +125,7 @@ void FlaMovies::drawDeltaFrame(Common::MemoryReadStream &stream, int32 width) {
 	}
 }
 
-void FlaMovies::scaleFla2x() {
+void Movies::scaleFla2x() {
 	uint8 *source = (uint8 *)_flaBuffer;
 	uint8 *dest = (uint8 *)_engine->_imageBuffer.getPixels();
 
@@ -160,7 +159,7 @@ void FlaMovies::scaleFla2x() {
 	}
 }
 
-void FlaMovies::processFrame() {
+void Movies::processFrame() {
 	FLASampleStruct sample;
 
 	_frameData.videoSize = _file.readSint16LE();
@@ -194,7 +193,7 @@ void FlaMovies::processFrame() {
 		case kFade: {
 			int16 innerOpcpde = stream.readSint16LE();
 			switch (innerOpcpde) {
-			case 1:
+			case 1: // fla flute
 				_engine->_music->playMidiMusic(26);
 				break;
 			case 2:
@@ -219,14 +218,19 @@ void FlaMovies::processFrame() {
 			sample.sampleNum = stream.readSint16LE();
 			sample.freq = stream.readSint16LE();
 			sample.repeat = stream.readSint16LE();
-			sample.dummy = stream.readSByte();
-			sample.x = stream.readByte();
-			sample.y = stream.readByte();
-			_engine->_sound->playFlaSample(sample.sampleNum, sample.repeat, sample.x, sample.y);
+			sample.balance = stream.readByte();
+			sample.volumeLeft = stream.readByte();
+			sample.volumeRight = stream.readByte();
+			_engine->_sound->playFlaSample(sample.sampleNum, sample.repeat, sample.balance, sample.volumeLeft, sample.volumeRight);
 			break;
 		}
 		case kStopSample: {
-			_engine->_sound->stopSample(sample.sampleNum);
+			const int16 sampleNum = stream.readSint16LE();
+			if (sampleNum == -1) {
+				_engine->_sound->stopSamples();
+			} else {
+				_engine->_sound->stopSample(sampleNum);
+			}
 			break;
 		}
 		case kDeltaFrame: {
@@ -240,13 +244,13 @@ void FlaMovies::processFrame() {
 			drawKeyFrame(stream, FLASCREEN_WIDTH, FLASCREEN_HEIGHT);
 			break;
 		}
-		case kFlaUnknown7: {
+		case kBlackFrame: {
 			const Common::Rect rect(0, 0, 79, 199);
 			_engine->_interface->drawFilledRect(rect, 0);
 			break;
 		}
-		case kFlaUnknown9:
-		case kFlaUnknown16SameAs9: {
+		case kCopy:
+		case kCopy2: {
 			const Common::Rect rect(0, 0, 80, 200);
 			byte *ptr = (byte *)_engine->_frontVideoBuffer.getPixels();
 			for (int y = rect.top; y < rect.bottom; ++y) {
@@ -258,7 +262,15 @@ void FlaMovies::processFrame() {
 			_engine->_frontVideoBuffer.addDirtyRect(rect);
 			break;
 		}
-		case kFlaUnknown4:
+		case kSampleBalance: {
+			/* int16 num = */ stream.readSint16LE();
+			/* uint8 offset = */ stream.readByte();
+			/* int16 balance = */ stream.readSint16LE();
+			/* uint8 volumeLeft = */ stream.readByte();
+			/* uint8 volumeRight = */ stream.readByte();
+			// TODO: change balance
+			break;
+		}
 		default: {
 			break;
 		}
@@ -268,9 +280,9 @@ void FlaMovies::processFrame() {
 	}
 }
 
-FlaMovies::FlaMovies(TwinEEngine *engine) : _engine(engine) {}
+Movies::Movies(TwinEEngine *engine) : _engine(engine) {}
 
-void FlaMovies::prepareGIF(int index) {
+void Movies::prepareGIF(int index) {
 	Image::GIFDecoder decoder;
 	Common::SeekableReadStream *stream = HQR::makeReadStream(Resources::HQR_FLAGIF_FILE, index);
 	if (stream == nullptr) {
@@ -293,7 +305,7 @@ void FlaMovies::prepareGIF(int index) {
 	_engine->setPalette(_engine->_screens->_paletteRGBA);
 }
 
-void FlaMovies::playGIFMovie(const char *flaName) {
+void Movies::playGIFMovie(const char *flaName) {
 	if (!Common::File::exists(Resources::HQR_FLAGIF_FILE)) {
 		warning("%s file doesn't exist", Resources::HQR_FLAGIF_FILE);
 		return;
@@ -341,7 +353,8 @@ void FlaMovies::playGIFMovie(const char *flaName) {
 	}
 }
 
-void FlaMovies::playFlaMovie(const char *flaName) {
+void Movies::playFlaMovie(const char *flaName) {
+	assert(_engine->isLBA1());
 	_engine->_sound->stopSamples();
 
 	Common::String fileNamePath = Common::String::format("%s", flaName);
@@ -435,4 +448,46 @@ void FlaMovies::playFlaMovie(const char *flaName) {
 	_engine->_sound->stopSamples();
 }
 
+void Movies::playSmkMovie(int index) {
+	assert(_engine->isLBA2());
+	Video::SmackerDecoder decoder;
+	Common::SeekableReadStream *stream = HQR::makeReadStream(TwineResource(Resources::HQR_VIDEO_FILE, index));
+	if (stream == nullptr) {
+		warning("Failed to find smacker video %i", index);
+		return;
+	}
+	if (!decoder.loadStream(stream)) {
+		warning("Failed to load smacker video %i", index);
+		return;
+	}
+	decoder.start();
+
+	for (;;) {
+		if (decoder.endOfVideo()) {
+			break;
+		}
+		FrameMarker frame(_engine);
+		_engine->_input->readKeys();
+		if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
+			break;
+		}
+
+		if (decoder.needsUpdate()) {
+			const Graphics::Surface *frameSurf = decoder.decodeNextFrame();
+			if (!frameSurf) {
+				continue;
+			}
+			if (decoder.hasDirtyPalette()) {
+				_engine->setPalette(0, 256, decoder.getPalette());
+			}
+
+			Graphics::ManagedSurface& target = _engine->_frontVideoBuffer;
+			const Common::Rect frameBounds(0, 0, frameSurf->w, frameSurf->h);
+			target.transBlitFrom(*frameSurf, frameBounds, target.getBounds(), 0, false, 0, 0xff, nullptr, true);
+		}
+	}
+
+	decoder.close();
+}
+
 } // namespace TwinE
diff --git a/engines/twine/flamovies.h b/engines/twine/movies.h
similarity index 97%
rename from engines/twine/flamovies.h
rename to engines/twine/movies.h
index a761076c49..eb409e584d 100644
--- a/engines/twine/flamovies.h
+++ b/engines/twine/movies.h
@@ -60,7 +60,7 @@ struct FLAFrameDataStruct {
 
 class TwinEEngine;
 
-class FlaMovies {
+class Movies {
 private:
 	TwinEEngine *_engine;
 
@@ -96,13 +96,15 @@ private:
 	void playGIFMovie(const char *flaName);
 
 public:
-	FlaMovies(TwinEEngine *engine);
+	Movies(TwinEEngine *engine);
 
 	/**
 	 * Play FLA movies
 	 * @param flaName FLA movie name
 	 */
 	void playFlaMovie(const char *flaName);
+
+	void playSmkMovie(int index);
 };
 
 } // namespace TwinE
diff --git a/engines/twine/parser/body.cpp b/engines/twine/parser/body.cpp
index 9edab0143e..02ad85ebee 100644
--- a/engines/twine/parser/body.cpp
+++ b/engines/twine/parser/body.cpp
@@ -148,9 +148,9 @@ void BodyData::loadLines(Common::SeekableReadStream &stream) {
 	_lines.reserve(numLines);
 	for (uint16 i = 0; i < numLines; ++i) {
 		BodyLine line;
-		line.unk1 = stream.readByte();
+		stream.skip(1);
 		line.color = stream.readByte();
-		line.unk2 = stream.readUint16LE();
+		stream.skip(2);
 		line.vertex1 = stream.readUint16LE() / 6;
 		line.vertex2 = stream.readUint16LE() / 6;
 		_lines.push_back(line);
@@ -165,9 +165,9 @@ void BodyData::loadSpheres(Common::SeekableReadStream &stream) {
 	_spheres.reserve(numSpheres);
 	for (uint16 i = 0; i < numSpheres; ++i) {
 		BodySphere sphere;
-		sphere.unk1 = stream.readByte();
-		sphere.color = stream.readByte();
-		sphere.unk2 = stream.readUint16LE();
+		sphere.fillType = stream.readByte();
+		sphere.color = stream.readUint16LE();
+		stream.readByte();
 		sphere.radius = stream.readUint16LE();
 		sphere.vertex = stream.readUint16LE() / 6;
 		_spheres.push_back(sphere);
diff --git a/engines/twine/parser/bodytypes.h b/engines/twine/parser/bodytypes.h
index edf077310e..b2fec04bbf 100644
--- a/engines/twine/parser/bodytypes.h
+++ b/engines/twine/parser/bodytypes.h
@@ -37,19 +37,17 @@ struct BodyVertex {
 };
 
 struct BodyLine {
-	// TODO: intensity??
-	uint8 unk1;
+	// fill byte here
 	uint8 color;
-	uint16 unk2;
+	// 2 fill bytes here
 	uint16 vertex1;
 	uint16 vertex2;
 };
 
 struct BodySphere {
-	// TODO: intensity??
-	uint8 unk1;
-	uint8 color;
-	uint16 unk2;
+	uint8 fillType;
+	uint16 color; // start and end color index
+	// fill byte here
 	uint16 radius;
 	uint16 vertex;
 };
diff --git a/engines/twine/parser/parser.h b/engines/twine/parser/parser.h
index ae4ca3c759..521cb97e94 100644
--- a/engines/twine/parser/parser.h
+++ b/engines/twine/parser/parser.h
@@ -41,6 +41,10 @@ public:
 
 	bool loadFromBuffer(const uint8 *buf, uint32 size, bool lba1);
 	bool loadFromHQR(const char *name, int index, bool lba1);
+
+	inline bool loadFromHQR(const TwineResource &resource, bool lba1) {
+		return loadFromHQR(resource.hqr, resource.index, lba1);
+	}
 };
 
 } // End of namespace TwinE
diff --git a/engines/twine/parser/text.cpp b/engines/twine/parser/text.cpp
index 3a56ab93a1..8592c60ecb 100644
--- a/engines/twine/parser/text.cpp
+++ b/engines/twine/parser/text.cpp
@@ -57,13 +57,20 @@ bool TextData::loadFromHQR(const char *name, TextBankId textBankId, int language
 
 	for (int entry = 0; entry < numIdxEntries; ++entry) {
 		const TextId textIdx = (TextId)indexStream->readUint16LE();
-		const uint16 start = offsetStream->readUint16LE();
+		uint16 start = offsetStream->readUint16LE();
 		const int32 offsetPos = offsetStream->pos();
 		const uint16 end = offsetStream->readUint16LE();
+
+		if (!lba1) {
+			++start;
+		}
 		offsetStream->seek(start);
 		Common::String result;
 		for (int16 i = start; i < end - 1; ++i) {
 			const char c = (char)offsetStream->readByte();
+			if (c == '\0') {
+				break;
+			}
 			result += c;
 		}
 		add(textBankId, TextEntry{result, entry, textIdx});
diff --git a/engines/twine/renderer/redraw.cpp b/engines/twine/renderer/redraw.cpp
index 6d108bc746..8e87a05cfe 100644
--- a/engines/twine/renderer/redraw.cpp
+++ b/engines/twine/renderer/redraw.cpp
@@ -493,7 +493,7 @@ void Redraw::processDrawList(DrawListStruct *drawList, int32 drawListPos, bool b
 		// Drawing unknown
 		else if (flags < DrawListType::DrawActorSprites) {
 			// TODO reverse this part of the code
-			warning("Not yet reversed part of the rendering code");
+			warning("Not yet reversed part of the rendering code: %u", flags);
 		}
 		// Drawing sprite actors, doors and entities
 		else if (flags == DrawListType::DrawActorSprites) {
diff --git a/engines/twine/renderer/renderer.cpp b/engines/twine/renderer/renderer.cpp
index 3c0a8a3b9b..fcb11c5bee 100644
--- a/engines/twine/renderer/renderer.cpp
+++ b/engines/twine/renderer/renderer.cpp
@@ -87,19 +87,19 @@ IVec3 &Renderer::projectPositionOnScreen(int32 cX, int32 cY, int32 cZ) {
 		posZ = 0x7FFF;
 	}
 
-	_projPos.x = (cX * _cameraScaleY) / posZ + _orthoProjPos.x;
-	_projPos.y = (-cY * _cameraScaleZ) / posZ + _orthoProjPos.y;
+	_projPos.x = (cX * _cameraScaleX) / posZ + _orthoProjPos.x;
+	_projPos.y = (-cY * _cameraScaleY) / posZ + _orthoProjPos.y;
 	_projPos.z = posZ;
 	return _projPos;
 }
 
-void Renderer::setCameraPosition(int32 x, int32 y, int32 depthOffset, int32 scaleY, int32 scaleZ) {
+void Renderer::setCameraPosition(int32 x, int32 y, int32 depthOffset, int32 scaleX, int32 scaleY) {
 	_orthoProjPos.x = x;
 	_orthoProjPos.y = y;
 
 	_cameraDepthOffset = depthOffset;
+	_cameraScaleX = scaleX;
 	_cameraScaleY = scaleY;
-	_cameraScaleZ = scaleZ;
 
 	_isUsingOrthoProjection = false;
 }
@@ -459,7 +459,7 @@ void Renderer::computePolygons(int16 polyRenderType, const Vertex *vertices, int
 	}
 }
 
-void Renderer::renderPolygonsCopper(int vtop, int32 vsize, uint8 color) const {
+void Renderer::renderPolygonsCopper(int vtop, int32 vsize, uint16 color) const {
 	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
 	const int16 *ptr1 = &_polyTab[vtop];
 	const int screenWidth = _engine->width();
@@ -473,79 +473,31 @@ void Renderer::renderPolygonsCopper(int vtop, int32 vsize, uint8 color) const {
 	if (renderLoop > screenHeight) {
 		renderLoop = screenHeight;
 	}
+	int32 sens = 1;
+
 	for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
-		int16 start = ptr1[0];
-		int16 stop = ptr1[screenHeight];
+		int16 xMin = ptr1[0];
+		int16 xMax = ptr1[screenHeight];
 
 		ptr1++;
-		int32 hsize = stop - start;
+		uint8 *pDest = out + xMin;
 
-		if (hsize >= 0) {
-			uint16 mask = 0x43DB;
+		for (; xMin <= xMax; xMin++) {
+			*pDest++ = (uint8)color;
+		}
 
-			hsize++;
-			const int32 startCopy = start;
-
-			for (int32 j = startCopy; j < hsize + startCopy; j++) {
-				start += mask;
-				start = (start & 0xFF00) | ((start & 0xFF) & 3U);
-				start = (start & 0xFF00) | ((start & 0xFF) + color);
-				if (j >= 0 && j < screenWidth) {
-					out[j] = start & 0xFF;
-				}
-				mask = (mask * 4) | (mask / SCENE_SIZE_HALF);
-				mask++;
+		color += sens;
+		if (!(color & 0xF)) {
+			sens = -sens;
+			if (sens < 0) {
+				color += sens;
 			}
 		}
-		out += screenWidth;
+		pDest += screenWidth;
 	}
 }
 
-void Renderer::renderPolygonsBopper(int vtop, int32 vsize, uint8 color) const {
-#if 0
-	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
-	const int16 *ptr1 = &_polyTab[vtop];
-
-	const int screenWidth = _engine->width();
-	const int screenHeight = _engine->height();
-
-	int32 j = 0;
-
-	do {
-		uint16 stop = *(const uint16 *)(ptr1 + screenHeight);
-		uint16 start = *(const uint16 *)ptr1;
-		++ptr1;
-		if (stop >= start) {
-			++j;
-			uint8 *out2 = out + start;
-			memset(out2, color, j);
-			++color;
-			if (!(color & 0xF)) {
-				while (1) {
-					--color;
-					if (!(color & 0xF)) {
-						break;
-					}
-					out += screenWidth;
-					--vsize;
-					if (!vsize) {
-						return;
-					}
-					stop = *(const uint16 *)(ptr1 + screenHeight);
-					start = *(const uint16 *)ptr1;
-					++ptr1;
-					if (stop >= start) {
-						++j;
-						out2 = out + start;
-						memset(out2, color, j);
-					}
-				}
-			}
-		}
-		out += screenWidth;
-		--vsize;
-	} while (vsize);
-#else
+void Renderer::renderPolygonsBopper(int vtop, int32 vsize, uint16 color) const {
 	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
 	const int16 *ptr1 = &_polyTab[vtop];
 	const int screenWidth = _engine->width();
@@ -558,27 +510,35 @@ void Renderer::renderPolygonsBopper(int vtop, int32 vsize, uint8 color) const {
 	if (renderLoop > screenHeight) {
 		renderLoop = screenHeight;
 	}
+	int32 sens = 1;
+	int32 line = 2;
 	for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
-		int16 start = ptr1[0];
-		int16 stop = ptr1[screenHeight];
+		int16 xMin = ptr1[0];
+		int16 xMax = ptr1[screenHeight];
 		ptr1++;
-		const int32 hsize = stop - start;
 
-		if (start & 1) {
-			if (hsize >= 0) {
-				for (int32 j = start; j <= hsize + start; j++) {
-					if (j >= 0 && j < screenWidth) {
-						out[j] = color;
-					}
+		uint8 *pDest = out + xMin;
+
+		for (; xMin <= xMax; xMin++) {
+			*pDest++ = (uint8)color;
+		}
+
+		line--;
+		if (!line) {
+			line = 2;
+			color += sens;
+			if (!(color & 0xF)) {
+				sens = -sens;
+				if (sens < 0) {
+					color += sens;
 				}
 			}
 		}
 		out += screenWidth;
 	}
-#endif
 }
 
-void Renderer::renderPolygonsFlat(int vtop, int32 vsize, uint8 color) const {
+void Renderer::renderPolygonsFlat(int vtop, int32 vsize, uint16 color) const {
 	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
 	const int16 *ptr1 = &_polyTab[vtop];
 	const int screenWidth = _engine->width();
@@ -597,104 +557,59 @@ void Renderer::renderPolygonsFlat(int vtop, int32 vsize, uint8 color) const {
 		ptr1++;
 		const int32 hsize = stop - start;
 
-		if (hsize >= 0) {
-			for (int32 j = start; j <= hsize + start; j++) {
-				if (j >= 0 && j < screenWidth) {
-					out[j] = color;
-				}
+		for (int32 j = start; j <= hsize + start; j++) {
+			if (j >= 0 && j < screenWidth) {
+				out[j] = color;
 			}
 		}
 		out += screenWidth;
 	}
 }
 
-void Renderer::renderPolygonsTele(int vtop, int32 vsize, uint8 color) const {
+#define ROL16(x, b) (((x) << (b)) | ((x) >> (16 - (b))))
+
+void Renderer::renderPolygonsTele(int vtop, int32 vsize, uint16 color) const {
 	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
 	const int16 *ptr1 = &_polyTab[vtop];
-	int bx = (uint16)color << 16;
 	const int screenWidth = _engine->width();
 	const int screenHeight = _engine->height();
 
 	int32 renderLoop = vsize;
-	do {
-		int16 start;
-		int16 stop;
-		int32 hsize;
-		while (1) {
-			start = ptr1[0];
-			stop = ptr1[screenHeight];
-			ptr1++;
-			hsize = stop - start;
-
-			if (hsize) {
-				break;
-			}
-
-			uint8 *out2 = start + out;
-			*out2 = ((uint16)(bx / 24)) & 0x0F;
-
-			color = *(out2 + 1);
-
-			out += screenWidth;
-
-			--renderLoop;
-			if (!renderLoop) {
-				return;
-			}
-		}
-
-		if (stop >= start) {
-			hsize++;
-			bx = (uint16)(color / 16);
-			uint8 *out2 = start + out;
-
-			int ax = (bx & 0xF0) * 256;
-			bx = bx * 256;
-			ax += (bx & 0x0F);
-			ax -= bx;
-			ax++;
-			ax = ax >> 16;
-
-			ax = ax / hsize;
-			uint16 temp = (ax & 0xF0);
-			temp = temp / 256;
-			temp += (ax & 0x0F);
-			ax = temp;
-
-			uint16 dx = ax;
-
-			ax = (ax & 0x0F) + (bx & 0xF0);
-			hsize++;
+	if (vtop < 0) {
+		out += screenWidth * ABS(vtop);
+		renderLoop -= ABS(vtop);
+	}
+	if (renderLoop > screenHeight) {
+		renderLoop = screenHeight;
+	}
 
-			if (hsize & 1) {
-				ax = 0; // not sure about this
-			}
+	uint16 acc = 17371;
+	color &= 0xFF;
+	uint16 col;
+	for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
+		int16 xMin = ptr1[0];
+		int16 xMax = ptr1[screenHeight];
+		++ptr1;
+		uint8 *pDest = out + xMin;
+		col = xMin;
 
-			for (int32 j = hsize >> 1; j > 0; --j) {
-				*(out2++) = ax & 0x0F;
-				ax += dx;
+		for (; xMin <= xMax; xMin++) {
+			col = ((col + acc) & 0xFF03) + (uint16)color;
+			acc = ROL16(acc, 2) + 1;
 
-				*(out2++) = ax & 0x0F;
-				ax += dx;
-			}
+			*pDest++ = (uint8)col;
 		}
-
 		out += screenWidth;
-		--renderLoop;
-
-	} while (renderLoop);
+	}
 }
 
-// FIXME: buggy
-void Renderer::renderPolygonsTras(int vtop, int32 vsize, uint8 color) const {
+void Renderer::renderPolygonsTrans(int vtop, int32 vsize, uint16 color) const {
 	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
 	const int16 *ptr1 = &_polyTab[vtop];
 	const int screenWidth = _engine->width();
 	const int screenHeight = _engine->height();
 
 	do {
-		unsigned short int bx;
-
 		int16 start = ptr1[0];
 		int16 stop = ptr1[screenHeight];
 
@@ -704,30 +619,17 @@ void Renderer::renderPolygonsTras(int vtop, int32 vsize, uint8 color) const {
 		if (hsize >= 0) {
 			hsize++;
 			uint8 *out2 = start + out;
-
-			if (hsize / 2 < 0) {
-				bx = color;
-				bx = bx * 256;
-				bx += color;
-				for (int32 j = 0; j < hsize; j++) {
-					*(out2) = (*(out2)&0x0F0F) | bx;
-					// TODO: check for potential out2++ here
-				}
-			} else {
-				*out2 = (*(out2)&0x0F) | color;
-				out2++;
-			}
+			*out2 = (*(out2)&0x0F) | color;
+			out2++;
 		}
 		out += screenWidth;
 	} while (--vsize);
 }
 
-// FIXME: buggy
 // Used e.g for the legs of the horse or the ears of most characters
-void Renderer::renderPolygonsTrame(int vtop, int32 vsize, uint8 color) const {
+void Renderer::renderPolygonsTrame(int vtop, int32 vsize, uint16 color) const {
 	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
 	const int16 *ptr1 = &_polyTab[vtop];
-	unsigned char bh = 0;
 	const int screenWidth = _engine->width();
 	const int screenHeight = _engine->height();
 
@@ -739,32 +641,25 @@ void Renderer::renderPolygonsTrame(int vtop, int32 vsize, uint8 color) const {
 	if (renderLoop > screenHeight) {
 		renderLoop = screenHeight;
 	}
+	int32 pair = 0;
 	for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
 		int16 start = ptr1[0];
 		int16 stop = ptr1[screenHeight];
 		ptr1++;
-		int32 hsize = stop - start;
-
-		if (hsize >= 0) {
-			hsize++;
-			uint8 *out2 = start + out;
-
-			hsize /= 2;
-			if (hsize > 1) {
-				uint16 ax;
-				bh ^= 1;
-				ax = (uint16)(*out2);
-				ax &= 1;
-				if (ax ^ bh) {
-					out2++;
-				}
+		uint8 *out2 = start + out;
+		stop = ((stop - start) + 1) / 2;
+		if (stop > 0) {
+			pair ^= 1; // paire/impair
+			if ((start & 1) ^ pair) {
+				out2++;
+			}
 
-				for (int32 j = 0; j < hsize; j++) {
-					*out2 = color;
-					out2 += 2;
-				}
+			for (; stop > 0; stop--) {
+				*out2 = color;
+				out2 += 2;
 			}
 		}
+
 		out += screenWidth;
 	}
 }
@@ -986,105 +881,75 @@ void Renderer::renderPolygonsDither(int vtop, int32 vsize) const {
 	}
 }
 
-void Renderer::renderPolygonsMarble(int vtop, int32 vsize, uint8 color) const {
+void Renderer::renderPolygonsMarble(int vtop, int32 vsize, uint16 color) const {
 	const int screenWidth = _engine->width();
 	const int screenHeight = _engine->height();
 
 	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
-	const int16 *ptr1 = &_polyTab[vtop];
-	int height = vsize;
-
-	uint16 color2 = color;
-	uint16 v29 = 2;
-	while (2) {
-		const uint16 stop = *(const uint16 *)(ptr1 + screenHeight);
-		const uint16 start = *(const uint16 *)ptr1;
-		++ptr1;
-		if (stop < start) {
-			out += screenWidth;
-			--height;
-			if (!height) {
-				return;
-			}
-			continue;
-		}
-		const uint16 hsize = stop - start;
-		uint16 width = hsize + 1;
-		uint8 *out2 = start + out;
-		if ((uintptr)out2 & 1) {
-			*out2++ = color2;
-			--width;
-		}
-		for (uint16 k = width / 2; k; --k) {
-			*out2++ = color2;
-			*out2++ = color2;
-		}
-		const uint16 v34 = width & 1;
-		for (uint16 l = v34; l; --l) {
-			*out2++ = color2;
-		}
-		--v29;
-		if (v29 || (v29 = 2, ++color2, color2 & 0xF)) {
-			out += screenWidth;
-			--height;
-			if (!height) {
-				return;
-			}
-			continue;
-		}
-		v29 = 2;
-		--color2;
-		if (!(color2 & 0xF)) {
-			out += screenWidth;
-			--height;
-			if (!height) {
-				return;
+	int16 *ptr1 = &_polyTab[vtop];
+
+	int16 xMin, xMax;
+	int16 y = vtop;
+	uint8 *pDestLine = out;
+	uint8 *pDest;
+	int16 *pVerticG = ptr1;
+	int16 *pVerticD = &ptr1[screenHeight];
+
+	uint16 start = (color & 0xFF) << 8;
+	uint16 end = color & 0xFF00;
+	uint16 delta = end - start + 1; // delta intensity
+	int32 step, dc;
+
+	for (; y <= vsize; y++) {
+		xMin = *pVerticG++;
+		xMax = *pVerticD++;
+		pDest = pDestLine + xMin;
+
+		dc = xMax - xMin;
+		if (dc == 0) {
+			// just one
+			*pDest++ = (uint8)(end >> 8);
+		} else if (dc > 0) {
+			step = delta / (dc + 1);
+			color = start;
+
+			for (; xMin <= xMax; xMin++) {
+				*pDest++ = (uint8)(color >> 8);
+				color += step;
 			}
-			continue;
 		}
-		break;
+
+		pDestLine += screenWidth;
 	}
-	while (1) {
-		out += screenWidth;
-		--height;
-		if (!height) {
-			return;
+}
+
+void Renderer::renderPolygonsSimplified(int vtop, int32 vsize, uint16 color) const {
+	uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
+	const int16 *ptr1 = &_polyTab[vtop];
+	const int16 *ptr2 = &_colorProgressionBuffer[vtop];
+	const int screenWidth = _engine->width();
+	const int screenHeight = _engine->height();
+
+	int32 renderLoop = vsize;
+	if (vtop < 0) {
+		out += screenWidth * ABS(vtop);
+		renderLoop -= ABS(vtop);
+	}
+	if (renderLoop > screenHeight) {
+		renderLoop = screenHeight;
+	}
+	for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
+		int16 xMin = MAX<int16>(0, ptr1[0]);
+		const int16 xMax = MIN<int16>((int16)(screenWidth - 1), ptr1[screenHeight]);
+		uint8 *pDest = out + xMin;
+
+		color = (*ptr2++) >> 8;
+		for (; xMin <= xMax; xMin++) {
+			*pDest++ = color;
 		}
-		const uint16 stop = *(const uint16 *)(ptr1 + screenHeight);
-		const uint16 start = *(const uint16 *)ptr1;
 		++ptr1;
-		if (stop >= start) {
-			const uint16 hsize = stop - start;
-			uint16 width = hsize + 1;
-			uint8 *out2 = start + out;
-			if ((uintptr)out2 & 1) {
-				*out2++ = color2;
-				--width;
-			}
-			for (uint16 m = width / 2; m; --m) {
-				*out2++ = color2;
-				*out2++ = color2;
-			}
-			const uint16 v41 = width & 1;
-			for (uint16 n = v41; n; --n) {
-				*out2++ = color2;
-			}
-			--v29;
-			if (v29) {
-				continue;
-			}
-		}
-		v29 = 2;
-		--color2;
-		if (!(color2 & 0xF)) {
-			out += screenWidth;
-			--height;
-			if (!height) {
-				return;
-			}
-			continue;
-		}
-		break;
+
+		out += screenWidth;
 	}
 }
 
@@ -1092,67 +957,159 @@ void Renderer::renderPolygons(const CmdRenderPolygon &polygon, Vertex *vertices,
 	computePolygons(polygon.renderType, vertices, polygon.numVertices);
 
 	const int32 vsize = vbottom - vtop + 1;
+	fillVertices(vtop, vsize, polygon.renderType, polygon.colorIndex);
+}
 
-	switch (polygon.renderType) {
+void Renderer::fillVertices(int vtop, int32 vsize, uint8 renderType, uint16 color) {
+	switch (renderType) {
 	case POLYGONTYPE_FLAT:
-		renderPolygonsFlat(vtop, vsize, polygon.colorIndex);
+		renderPolygonsFlat(vtop, vsize, color);
+		break;
+	case POLYGONTYPE_TELE:
+		if (_engine->_cfgfile.PolygonDetails == 0) {
+			renderPolygonsFlat(vtop, vsize, color);
+		} else {
+			renderPolygonsTele(vtop, vsize, color);
+		}
 		break;
 	case POLYGONTYPE_COPPER:
-		// TODO: activate again after POLYGONTYPE_BOPPER is fixed
-		//renderPolygonsCopper(vtop, vsize, polygon.colorIndex);
-		//break;
-	case POLYGONTYPE_BOPPER:
-		renderPolygonsCopper(vtop, vsize, polygon.colorIndex);
-		// TODO: fix this render method:
-		// renderPolygonsBopper(vtop, vsize, polygon.colorIndex);
+		renderPolygonsCopper(vtop, vsize, color);
 		break;
-	case POLYGONTYPE_TELE:
-		renderPolygonsTele(vtop, vsize, polygon.colorIndex);
+	case POLYGONTYPE_BOPPER:
+		renderPolygonsBopper(vtop, vsize, color);
 		break;
-	case POLYGONTYPE_TRAS:
-		renderPolygonsTras(vtop, vsize, polygon.colorIndex);
+	case POLYGONTYPE_TRANS:
+		renderPolygonsTrans(vtop, vsize, color);
 		break;
-	case POLYGONTYPE_TRAME:
-		renderPolygonsTrame(vtop, vsize, polygon.colorIndex);
+	case POLYGONTYPE_TRAME: // raster
+		renderPolygonsTrame(vtop, vsize, color);
 		break;
 	case POLYGONTYPE_GOURAUD:
-		renderPolygonsGouraud(vtop, vsize);
+		if (_engine->_cfgfile.PolygonDetails == 0) {
+			renderPolygonsSimplified(vtop, vsize, color);
+		} else {
+			renderPolygonsGouraud(vtop, vsize);
+		}
 		break;
 	case POLYGONTYPE_DITHER:
-		renderPolygonsDither(vtop, vsize);
+		if (_engine->_cfgfile.PolygonDetails == 0) {
+			renderPolygonsSimplified(vtop, vsize, color);
+		} else if (_engine->_cfgfile.PolygonDetails == 1) {
+			renderPolygonsGouraud(vtop, vsize);
+		} else {
+			renderPolygonsDither(vtop, vsize);
+		}
 		break;
 	case POLYGONTYPE_MARBLE:
-		renderPolygonsMarble(vtop, vsize, polygon.colorIndex);
+		renderPolygonsMarble(vtop, vsize, color);
 		break;
 	default:
-		warning("RENDER WARNING: Unsupported render type %d", polygon.renderType);
+		warning("RENDER WARNING: Unsupported render type %d", renderType);
 		break;
 	}
 }
 
-void Renderer::circleFill(int32 x, int32 y, int32 radius, uint8 color) {
+bool Renderer::prepareCircle(int32 x, int32 y, int32 radius) {
 	if (radius <= 0) {
-		return;
+		return false;
 	}
-	radius += 1;
+	int16 left = (int16)(x - radius);
+	int16 right = (int16)(y - radius);
+	int16 bottom = (int16)(x + radius);
+	int16 top = (int16)(y + radius);
+	const Common::Rect &clip = _engine->_interface->_clip;
+	int16 cleft = clip.left;
+	int16 cright = clip.right;
+	int16 ctop = clip.top;
+	int16 cbottom = clip.bottom;
+
+	if (left <= cright && bottom >= cleft && right <= cbottom && top >= ctop) {
+		if (left < cleft) {
+			left = cleft;
+		}
+		if (bottom > cright) {
+			bottom = cright;
+		}
+		if (right < ctop) {
+			right = ctop;
+		}
+		if (top > cbottom) {
+			top = cbottom;
+		}
 
-	for (int32 currentLine = -radius; currentLine <= radius; currentLine++) {
-		double width;
+		int32 r = 0;
+		int32 acc = -radius;
 
-		if (ABS(currentLine) != radius) {
-			width = ABS(sin(acos((float)currentLine / (float)radius)) * radius);
-		} else {
-			width = 0.0;
+		int16 *start = _polyTab;
+		int16 *end = &_polyTab[_engine->height()];
+
+		while (r <= radius) {
+			int32 x1 = x - radius;
+			if (x1 < cleft) {
+				x1 = cleft;
+			}
+
+			int32 x2 = x + radius;
+			if (x2 > cright) {
+				x2 = cright;
+			}
+
+			int32 ny = y - r;
+			if ((ny >= ctop) && (ny <= cbottom)) {
+				start[ny] = (int16)x1;
+				end[ny] = (int16)x2;
+			}
+
+			ny = y + r;
+			if ((ny >= ctop) && (ny <= cbottom)) {
+				start[ny] = (int16)x1;
+				end[ny] = (int16)x2;
+			}
+
+			if (acc < 0) {
+				acc += r;
+				if (acc >= 0) {
+					x1 = x - r;
+					if (x1 < cleft) {
+						x1 = cleft;
+					}
+
+					x2 = x + r;
+					if (x2 > cright) {
+						x2 = cright;
+					}
+
+					ny = y - radius;
+					if ((ny >= ctop) && (ny <= cbottom)) {
+						start[ny] = (int16)x1;
+						end[ny] = (int16)x2;
+					}
+
+					ny = y + radius;
+					if ((ny >= ctop) && (ny <= cbottom)) {
+						start[ny] = (int16)x1;
+						end[ny] = (int16)x2;
+					}
+
+					--radius;
+					acc -= radius;
+				}
+			}
+
+			++r;
 		}
 
-		_engine->_interface->drawLine((int32)(x - width), currentLine + y, (int32)(x + width), currentLine + y, color);
+		return true;
 	}
+
+	return false;
 }
 
 uint8 *Renderer::prepareSpheres(const Common::Array<BodySphere> &spheres, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData) {
 	for (const BodySphere &sphere : spheres) {
 		CmdRenderSphere *cmd = (CmdRenderSphere *)renderBufferPtr;
-		cmd->colorIndex = sphere.color;
+		cmd->color = sphere.color;
+		cmd->polyRenderType = sphere.fillType;
 		cmd->radius = sphere.radius;
 		const int16 centerIndex = sphere.vertex;
 		cmd->x = modelData->flattenPoints[centerIndex].x;
@@ -1315,14 +1272,14 @@ bool Renderer::renderModelElements(int32 numOfPrimitives, const BodyData &bodyDa
 			int32 radius = sphere->radius;
 
 			if (_isUsingOrthoProjection) {
+				// * sqrt(sx+sy) / 512 (isometric scale)
 				radius = (radius * 34) / 512;
 			} else {
 				int32 delta = _cameraDepthOffset + sphere->z;
-				if (delta <= 0) {
-					radius = 0;
-				} else {
-					radius = ((sphere->radius * _cameraScaleY) / delta) & 0xFFFF;
+				if (delta == 0) {
+					break;
 				}
+				radius = (sphere->radius * _cameraScaleX) / delta;
 			}
 
 			radius += 3;
@@ -1345,7 +1302,10 @@ bool Renderer::renderModelElements(int32 numOfPrimitives, const BodyData &bodyDa
 
 			radius -= 3;
 
-			circleFill(sphere->x, sphere->y, radius, sphere->colorIndex);
+			if (prepareCircle(sphere->x, sphere->y, radius)) {
+				const int32 vsize = 2 * radius;
+				fillVertices(sphere->y - radius, vsize, sphere->polyRenderType, sphere->color);
+			}
 			break;
 		}
 		default:
@@ -1437,7 +1397,7 @@ bool Renderer::renderAnimatedModel(ModelData *modelData, const BodyData &bodyDat
 
 			// X projection
 			{
-				coX = _orthoProjPos.x + ((coX * _cameraScaleY) / coZ);
+				coX = _orthoProjPos.x + ((coX * _cameraScaleX) / coZ);
 
 				if (coX > 0xFFFF) {
 					coX = 0x7FFF;
@@ -1456,7 +1416,7 @@ bool Renderer::renderAnimatedModel(ModelData *modelData, const BodyData &bodyDat
 
 			// Y projection
 			{
-				coY = _orthoProjPos.y + ((-coY * _cameraScaleZ) / coZ);
+				coY = _orthoProjPos.y + ((-coY * _cameraScaleY) / coZ);
 
 				if (coY > 0xFFFF) {
 					coY = 0x7FFF;
@@ -1660,7 +1620,7 @@ void Renderer::computeHolomapPolygon(int32 top, int32 x1, int32 bottom, int32 x2
 	}
 }
 
-void Renderer::fillHolomapPolygons(const Vertex &vertex1, const Vertex &vertex2, const Vertex &angles1, const Vertex &angles2, int32 &top, int32 &bottom) {
+void Renderer::fillHolomapPolygons(const Vertex &vertex1, const Vertex &vertex2, const Vertex &texCoord1, const Vertex &texCoord2, int32 &top, int32 &bottom) {
 	const int32 yBottom = vertex1.y;
 	const int32 yTop = vertex2.y;
 	if (yBottom == yTop) {
@@ -1676,7 +1636,7 @@ void Renderer::fillHolomapPolygons(const Vertex &vertex1, const Vertex &vertex2,
 			bottom = yTop;
 		}
 		computeHolomapPolygon(yTop, vertex2.x, yBottom, vertex1.x, _holomap_polytab_1_1);
-		computeHolomapPolygon(yTop, (uint32)(uint16)angles2.x, yBottom, (uint32)(uint16)angles1.x, _holomap_polytab_1_2);
+		computeHolomapPolygon(yTop, (uint32)(uint16)texCoord2.x, yBottom, (uint32)(uint16)texCoord1.x, _holomap_polytab_1_2);
 		polygonTabPtr = _holomap_polytab_1_3;
 	} else {
 		if (bottom < yBottom) {
@@ -1686,24 +1646,22 @@ void Renderer::fillHolomapPolygons(const Vertex &vertex1, const Vertex &vertex2,
 			top = yTop;
 		}
 		computeHolomapPolygon(yTop, vertex2.x, yBottom, vertex1.x, _holomap_polytab_2_1);
-		computeHolomapPolygon(yTop, (uint32)(uint16)angles2.x, yBottom, (uint32)(uint16)angles1.x, _holomap_polytab_2_2);
+		computeHolomapPolygon(yTop, (uint32)(uint16)texCoord2.x, yBottom, (uint32)(uint16)texCoord1.x, _holomap_polytab_2_2);
 		polygonTabPtr = _holomap_polytab_2_3;
 	}
-	computeHolomapPolygon(yTop, (uint32)(uint16)angles2.y, yBottom, (uint32)(uint16)angles1.y, polygonTabPtr);
+	computeHolomapPolygon(yTop, (uint32)(uint16)texCoord2.y, yBottom, (uint32)(uint16)texCoord1.y, polygonTabPtr);
 }
 
-void Renderer::renderHolomapVertices(const Vertex vertexCoordinates[3], const Vertex vertexCoordinates2[3]) {
+void Renderer::renderHolomapVertices(const Vertex vertexCoordinates[3], const Vertex textureCoordinates[3], uint8 *holomapImage, uint32 holomapImageSize) {
 	int32 top = SCENE_SIZE_MAX;
 	int32 bottom = SCENE_SIZE_MIN;
-	fillHolomapPolygons(vertexCoordinates[0], vertexCoordinates[1], vertexCoordinates2[0], vertexCoordinates2[1], top, bottom);
-	fillHolomapPolygons(vertexCoordinates[1], vertexCoordinates[2], vertexCoordinates2[1], vertexCoordinates2[2], top, bottom);
-	fillHolomapPolygons(vertexCoordinates[2], vertexCoordinates[0], vertexCoordinates2[2], vertexCoordinates2[0], top, bottom);
-	renderHolomapPolygons(top, bottom);
+	fillHolomapPolygons(vertexCoordinates[0], vertexCoordinates[1], textureCoordinates[0], textureCoordinates[1], top, bottom);
+	fillHolomapPolygons(vertexCoordinates[1], vertexCoordinates[2], textureCoordinates[1], textureCoordinates[2], top, bottom);
+	fillHolomapPolygons(vertexCoordinates[2], vertexCoordinates[0], textureCoordinates[2], textureCoordinates[0], top, bottom);
+	renderHolomapPolygons(top, bottom, holomapImage, holomapImageSize);
 }
 
-void Renderer::renderHolomapPolygons(int32 top, int32 bottom) {
-	const void *pixelBegin = _engine->_frontVideoBuffer.getBasePtr(0, 0);
-	const void *pixelEnd = _engine->_frontVideoBuffer.getBasePtr(_engine->_frontVideoBuffer.w - 1, _engine->_frontVideoBuffer.h - 1);
+void Renderer::renderHolomapPolygons(int32 top, int32 bottom, uint8 *holomapImage, uint32 holomapImageSize) {
 	if (top < 0 || top >= _engine->_frontVideoBuffer.h) {
 		return;
 	}
@@ -1718,29 +1676,29 @@ void Renderer::renderHolomapPolygons(int32 top, int32 bottom) {
 
 	int32 yHeight = bottom - top;
 	while (yHeight > -1) {
+		int32 u;
+		int32 v;
 		const int16 left = *lholomap_polytab_1_1++;
 		const int16 right = *lholomap_polytab_2_1++;
-		const uint16 x_1_2 = *lholomap_polytab_1_2++;
-		const uint16 x_1_3 = *lholomap_polytab_1_3++;
-		const uint16 x_2_2 = *lholomap_polytab_2_2++;
-		const uint16 x_2_3 = *lholomap_polytab_2_3++;
+		const uint32 u0 = u = *lholomap_polytab_1_2++;
+		const uint32 v0 = v = *lholomap_polytab_1_3++;
+		const uint32 u1 = *lholomap_polytab_2_2++;
+		const uint32 v1 = *lholomap_polytab_2_3++;
 		const int16 width = right - left;
 		if (width > 0) {
 			uint8 *pixelBufPtr = screenBufPtr + left;
-			const int32 iWidth = (int32)width;
-			uint32 uVar1 = (uint32)x_1_3;
-			uint32 uVar3 = (uint32)x_1_2;
+
+			int32 ustep = ((int32)u1 - (int32)u0 + 1) / width;
+			int32 vstep = ((int32)v1 - (int32)v0 + 1) / width;
+
 			for (int16 i = 0; i < width; ++i) {
-				const uint32 holomapImageOffset = (uint32)((int32)uVar3 >> 8 & 0xffU) | (uVar1 & 0xff00);
-				assert(holomapImageOffset < _engine->_resources->_holomapImageSize);
-				if (pixelBufPtr < pixelBegin || pixelBufPtr > pixelEnd) {
-					++pixelBufPtr;
-				} else {
-					//debug("holomapImageOffset: %i", holomapImageOffset);
-					*pixelBufPtr++ = _engine->_resources->_holomapImagePtr[holomapImageOffset];
-				}
-				uVar1 += (int32)(((uint32)x_2_3 - (uint32)x_1_3) + 1) / iWidth;
-				uVar3 += (int32)(((uint32)x_2_2 - (uint32)x_1_2) + 1) / iWidth;
+				// u0 & 0xFF00 is the x position on the image * 256
+				// v0 & 0xFF00 is the y position on the image * 256
+				const uint32 idx = ((u >> 8) & 0xff) | (v & 0xff00);
+				assert(idx < holomapImageSize);
+				*pixelBufPtr++ = holomapImage[idx];
+				u += ustep;
+				v += vstep;
 			}
 		}
 		screenBufPtr += _engine->_frontVideoBuffer.pitch;
diff --git a/engines/twine/renderer/renderer.h b/engines/twine/renderer/renderer.h
index 1e3ff1c62d..ac19d0bfed 100644
--- a/engines/twine/renderer/renderer.h
+++ b/engines/twine/renderer/renderer.h
@@ -30,12 +30,12 @@
 #include "twine/twine.h"
 
 #define POLYGONTYPE_FLAT 0
+#define POLYGONTYPE_TELE 1
 // horizontal color adjustment with changing pattern over the polygon
-#define POLYGONTYPE_COPPER 1
-#define POLYGONTYPE_BOPPER 2
-#define POLYGONTYPE_MARBLE 3
-#define POLYGONTYPE_TELE 4
-#define POLYGONTYPE_TRAS 5
+#define POLYGONTYPE_COPPER 2
+#define POLYGONTYPE_BOPPER 3
+#define POLYGONTYPE_MARBLE 4
+#define POLYGONTYPE_TRANS 5
 #define POLYGONTYPE_TRAME 6
 #define POLYGONTYPE_GOURAUD 7
 #define POLYGONTYPE_DITHER 8
@@ -122,7 +122,8 @@ private:
 	 * @sa RenderCommand
 	 */
 	struct CmdRenderSphere {
-		int8 colorIndex = 0;
+		uint16 color = 0; // color start and end values
+		uint8 polyRenderType = 0;
 		int16 radius = 0;
 		int16 x = 0;
 		int16 y = 0;
@@ -138,7 +139,7 @@ private:
 	ModelData _modelData;
 
 	bool renderAnimatedModel(ModelData *modelData, const BodyData &bodyData, RenderCommand *renderCmds, const IVec3 &angleVec, const IVec3 &renderPos, Common::Rect &modelRect);
-	void circleFill(int32 x, int32 y, int32 radius, uint8 color);
+	bool prepareCircle(int32 x, int32 y, int32 radius);
 	bool renderModelElements(int32 numOfPrimitives, const BodyData &bodyData, RenderCommand **renderCmds, ModelData *modelData, Common::Rect &modelRect);
 	IVec3 getCameraAnglePositions(int32 x, int32 y, int32 z);
 	inline IVec3 getCameraAnglePositions(const IVec3 &vec) {
@@ -155,8 +156,8 @@ private:
 	IVec3 _orthoProjPos;
 
 	int32 _cameraDepthOffset = 0;
+	int32 _cameraScaleX = 0;
 	int32 _cameraScaleY = 0;
-	int32 _cameraScaleZ = 0;
 
 	IMatrix3x3 _baseMatrix;
 	IMatrix3x3 _matricesTable[30 + 1];
@@ -183,16 +184,16 @@ private:
 
 	bool _isUsingOrthoProjection = false;
 
-	void renderPolygonsCopper(int vtop, int32 vsize, uint8 color) const;
-	void renderPolygonsBopper(int vtop, int32 vsize, uint8 color) const;
-	void renderPolygonsFlat(int vtop, int32 vsize, uint8 color) const;
-	void renderPolygonsTele(int vtop, int32 vsize, uint8 color) const;
-	void renderPolygonsTras(int vtop, int32 vsize, uint8 color) const;
-	void renderPolygonsTrame(int vtop, int32 vsize, uint8 color) const;
+	void renderPolygonsCopper(int vtop, int32 vsize, uint16 color) const;
+	void renderPolygonsBopper(int vtop, int32 vsize, uint16 color) const;
+	void renderPolygonsFlat(int vtop, int32 vsize, uint16 color) const;
+	void renderPolygonsTele(int vtop, int32 vsize, uint16 color) const;
+	void renderPolygonsTrans(int vtop, int32 vsize, uint16 color) const;
+	void renderPolygonsTrame(int vtop, int32 vsize, uint16 color) const;
 	void renderPolygonsGouraud(int vtop, int32 vsize) const;
 	void renderPolygonsDither(int vtop, int32 vsize) const;
-	void renderPolygonsMarble(int vtop, int32 vsize, uint8 color) const;
-
+	void renderPolygonsMarble(int vtop, int32 vsize, uint16 color) const;
+	void renderPolygonsSimplified(int vtop, int32 vsize, uint16 color) const;
 	void computePolygons(int16 polyRenderType, const Vertex *vertices, int32 numVertices);
 
 	const RenderCommand *depthSortRenderCommands(int32 numOfPrimitives);
@@ -202,9 +203,9 @@ private:
 
 	void baseMatrixTranspose();
 
-	void renderHolomapPolygons(int32 top, int32 bottom);
+	void renderHolomapPolygons(int32 top, int32 bottom, uint8 *holomapImage, uint32 holomapImageSize);
 	void computeHolomapPolygon(int32 y1, int32 x1, int32 y2, int32 x2, int16 *polygonTabPtr);
-	void fillHolomapPolygons(const Vertex &vertex1, const Vertex &vertex2, const Vertex &vertex3, const Vertex &vertex4, int32 &top, int32 &bottom);
+	void fillHolomapPolygons(const Vertex &vertex1, const Vertex &vertex2, const Vertex &texCoord1, const Vertex &texCoord2, int32 &top, int32 &bottom);
 
 public:
 	Renderer(TwinEEngine *engine);
@@ -224,6 +225,7 @@ public:
 		return getBaseRotationPosition(vec.x, vec.y, vec.z);
 	}
 
+	void fillVertices(int vtop, int32 vsize, uint8 renderType, uint16 color);
 	void renderPolygons(const CmdRenderPolygon &polygon, Vertex *vertices, int vtop, int vbottom);
 
 	inline IVec3 &projectPositionOnScreen(const IVec3& pos) {
@@ -232,7 +234,7 @@ public:
 
 	IVec3 &projectPositionOnScreen(int32 cX, int32 cY, int32 cZ);
 
-	void setCameraPosition(int32 x, int32 y, int32 depthOffset, int32 scaleY, int32 scaleZ);
+	void setCameraPosition(int32 x, int32 y, int32 depthOffset, int32 scaleX, int32 scaleY);
 	void setCameraAngle(int32 transPosX, int32 transPosY, int32 transPosZ, int32 rotPosX, int32 rotPosY, int32 rotPosZ, int32 param6);
 	IVec3 updateCameraAnglePositions(int zShift = 0);
 	void setBaseTranslation(int32 x, int32 y, int32 z);
@@ -261,7 +263,7 @@ public:
 
 	void renderInventoryItem(int32 x, int32 y, const BodyData &bodyData, int32 angle, int32 param);
 
-	void renderHolomapVertices(const Vertex vertexCoordinates[3], const Vertex vertexCoordinates2[3]);
+	void renderHolomapVertices(const Vertex vertexCoordinates[3], const Vertex textureCoordinates[3], uint8 *holomapImage, uint32 holomapImageSize);
 };
 
 inline void Renderer::setBaseRotationPos(int32 x, int32 y, int32 z) {
diff --git a/engines/twine/renderer/screens.cpp b/engines/twine/renderer/screens.cpp
index 329ab15034..80c12ff2bf 100644
--- a/engines/twine/renderer/screens.cpp
+++ b/engines/twine/renderer/screens.cpp
@@ -34,19 +34,24 @@ namespace TwinE {
 bool Screens::adelineLogo() {
 	_engine->_music->playMidiMusic(31);
 
-	return loadImageDelay(RESSHQR_ADELINEIMG, RESSHQR_ADELINEPAL, 7);
+	return loadImageDelay(_engine->_resources->adelineLogo(), 7);
 }
 
 void Screens::loadMenuImage(bool fadeIn) {
-	loadImage(RESSHQR_MENUIMG, -1, fadeIn);
+	loadImage(_engine->_resources->menuBackground(), fadeIn);
 	_engine->_workVideoBuffer.blitFrom(_engine->_frontVideoBuffer);
 }
 
-void Screens::loadCustomPalette(int32 index) {
-	if (HQR::getEntry(_palette, Resources::HQR_RESS_FILE, index) == 0) {
-		warning("Failed to load custom palette %i", index);
+void Screens::loadCustomPalette(const TwineResource &resource) {
+	const int32 size = HQR::getEntry(_palette, resource.hqr, resource.index);
+	if (size == 0) {
+		warning("Failed to load custom palette %s:%i", resource.hqr, resource.index);
 		return;
 	}
+	if (size != (int32)sizeof(_palette)) {
+		warning("Unexpected palette size %s:%i", resource.hqr, resource.index);
+	}
+	debug(3, "palette %s:%i with size %i", resource.hqr, resource.index, size);
 	convertPalToRGBA(_palette, _paletteRGBACustom);
 }
 
@@ -62,18 +67,18 @@ void Screens::convertPalToRGBA(const uint8 *in, uint32 *out) {
 	}
 }
 
-void Screens::loadImage(int32 index, int32 paletteIndex, bool fadeIn) {
+void Screens::loadImage(TwineImage image, bool fadeIn) {
 	Graphics::ManagedSurface& src = _engine->_imageBuffer;
-	if (HQR::getEntry((uint8 *)src.getPixels(), Resources::HQR_RESS_FILE, index) == 0) {
-		warning("Failed to load image with index %i", index);
+	if (HQR::getEntry((uint8 *)src.getPixels(), image.image) == 0) {
+		warning("Failed to load image with index %i", image.image.index);
 		return;
 	}
-	debug(0, "Load image: %i", index);
+	debug(0, "Load image: %i", image.image.index);
 	Graphics::ManagedSurface& target = _engine->_frontVideoBuffer;
 	target.transBlitFrom(src, src.getBounds(), target.getBounds(), 0, false, 0, 0xff, nullptr, true);
 	const uint32 *pal = _paletteRGBA;
-	if (paletteIndex != -1) {
-		loadCustomPalette(paletteIndex);
+	if (image.palette.index != -1) {
+		loadCustomPalette(image.palette);
 		pal = _paletteRGBACustom;
 	}
 	if (fadeIn) {
@@ -83,8 +88,8 @@ void Screens::loadImage(int32 index, int32 paletteIndex, bool fadeIn) {
 	}
 }
 
-bool Screens::loadImageDelay(int32 index, int32 paletteIndex, int32 seconds) {
-	loadImage(index, paletteIndex);
+bool Screens::loadImageDelay(TwineImage image, int32 seconds) {
+	loadImage(image);
 	if (_engine->delaySkip(1000 * seconds)) {
 		adjustPalette(0, 0, 0, _paletteRGBACustom, 100);
 		return true;
@@ -117,11 +122,11 @@ void Screens::fadeOut(const uint32 *pal) {
 #endif
 }
 
-int32 Screens::crossDot(int32 modifier, int32 color, int32 param, int32 intensity) {
-	if (!param) {
-		return color;
+int32 Screens::lerp(int32 value, int32 start, int32 end, int32 t) {
+	if (!end) {
+		return start;
 	}
-	return (((color - modifier) * intensity) / param) + modifier;
+	return (((start - value) * t) / end) + value;
 }
 
 void Screens::adjustPalette(uint8 r, uint8 g, uint8 b, const uint32 *rgbaPal, int32 intensity) {
@@ -137,9 +142,9 @@ void Screens::adjustPalette(uint8 r, uint8 g, uint8 b, const uint32 *rgbaPal, in
 	uint8 *newA = &paletteOut[3];
 
 	for (int32 i = 0; i < NUMOFCOLORS; i++) {
-		*newR = crossDot(r, paletteIn[counter], 100, intensity);
-		*newG = crossDot(g, paletteIn[counter + 1], 100, intensity);
-		*newB = crossDot(b, paletteIn[counter + 2], 100, intensity);
+		*newR = lerp(r, paletteIn[counter], 100, intensity);
+		*newG = lerp(g, paletteIn[counter + 1], 100, intensity);
+		*newB = lerp(b, paletteIn[counter + 2], 100, intensity);
 		*newA = 0xFF;
 
 		newR += 4;
@@ -173,9 +178,9 @@ void Screens::adjustCrossPalette(const uint32 *pal1, const uint32 *pal2) {
 		uint8 *newA = &paletteOut[counter + 3];
 
 		for (int32 i = 0; i < NUMOFCOLORS; i++) {
-			*newR = crossDot(pal1p[counter + 0], pal2p[counter + 0], 100, intensity);
-			*newG = crossDot(pal1p[counter + 1], pal2p[counter + 1], 100, intensity);
-			*newB = crossDot(pal1p[counter + 2], pal2p[counter + 2], 100, intensity);
+			*newR = lerp(pal1p[counter + 0], pal2p[counter + 0], 100, intensity);
+			*newG = lerp(pal1p[counter + 1], pal2p[counter + 1], 100, intensity);
+			*newB = lerp(pal1p[counter + 2], pal2p[counter + 2], 100, intensity);
 			*newA = 0xFF;
 
 			newR += 4;
diff --git a/engines/twine/renderer/screens.h b/engines/twine/renderer/screens.h
index e97b228249..afc0eb9350 100644
--- a/engines/twine/renderer/screens.h
+++ b/engines/twine/renderer/screens.h
@@ -78,7 +78,7 @@ public:
 	 * Load a custom palette
 	 * @param index \a RESS.HQR entry index (starting from 0)
 	 */
-	void loadCustomPalette(int32 index);
+	void loadCustomPalette(const TwineResource &resource);
 
 	/** Load and display Main Menu image */
 	void loadMenuImage(bool fadeIn = true);
@@ -89,7 +89,7 @@ public:
 	 * @param paletteIndex \a RESS.HQR entry index of the palette for the given image. This is often the @c index + 1
 	 * @param fadeIn if we fade in before using the palette
 	 */
-	void loadImage(int32 index, int32 paletteIndex, bool fadeIn = true);
+	void loadImage(TwineImage image, bool fadeIn = true);
 
 	/**
 	 * Load and display a particulary image on \a RESS.HQR file with cross fade effect and delay
@@ -97,7 +97,7 @@ public:
 	 * @param paletteIndex \a RESS.HQR entry index of the palette for the given image. This is often the @c index + 1
 	 * @param seconds number of seconds to delay
 	 */
-	bool loadImageDelay(int32 index, int32 paletteIndex, int32 seconds);
+	bool loadImageDelay(TwineImage image, int32 seconds);
 
 	/**
 	 * Fade image in
@@ -112,14 +112,15 @@ public:
 	void fadeOut(const uint32 *palette);
 
 	/**
-	 * Calculate a new color component according with an intensity
-	 * @param modifier color compenent
-	 * @param color color value
-	 * @param param unknown
-	 * @param intensity intensity value to adjust
-	 * @return new color component
+	 * Linear interpolation of the given value between start and end
+	 * @param value color component
+	 * @param start lower range
+	 * @param end upper range
+	 * @param t the location in given range
+	 * @return the lerped value
+	 * @note Doesn't clamp
 	 */
-	int32 crossDot(int32 modifier, int32 color, int32 param, int32 intensity);
+	int32 lerp(int32 value, int32 start, int32 end, int32 t);
 
 	/**
 	 * Adjust between two palettes
diff --git a/engines/twine/resources/hqr.h b/engines/twine/resources/hqr.h
index bf75fa894c..886393044b 100644
--- a/engines/twine/resources/hqr.h
+++ b/engines/twine/resources/hqr.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "common/stream.h"
+#include "twine/shared.h"
 
 namespace TwinE {
 
@@ -37,6 +38,7 @@ class TwinEEngine;
  */
 namespace HQR {
 
+
 /**
  * Get a HQR entry pointer
  * @param ptr pointer to save the entry
@@ -45,6 +47,9 @@ namespace HQR {
  * @return entry real size
  */
 int32 getEntry(uint8 *ptr, const char *filename, int32 index);
+inline int32 getEntry(uint8 *ptr, const TwineResource &resource) {
+	return getEntry(ptr, resource.hqr, resource.index);
+}
 
 /**
  * Get a HQR entry pointer
@@ -53,6 +58,9 @@ int32 getEntry(uint8 *ptr, const char *filename, int32 index);
  * @return entry real size
  */
 int32 entrySize(const char *filename, int32 index);
+inline int32 entrySize(const TwineResource &resource) {
+	return entrySize(resource.hqr, resource.index);
+}
 
 /**
  * Get a HQR total number of entries
@@ -70,11 +78,17 @@ int32 numEntries(const char *filename);
  * @return entry real size
  */
 int32 getAllocEntry(uint8 **ptr, const char *filename, int32 index);
+inline int32 getAllocEntry(uint8 **ptr, const TwineResource &resource) {
+	return getAllocEntry(ptr, resource.hqr, resource.index);
+}
 
 /**
  * @brief Helper method to dump the content of the given hqr index to a file
  */
 bool dumpEntry(const char *filename, int32 index, const char *targetFileName);
+inline bool dumpEntry(const TwineResource &resource, const char *targetFileName) {
+	return dumpEntry(resource.hqr, resource.index, targetFileName);
+}
 
 /**
  * Get a HQR entry pointer
@@ -95,7 +109,9 @@ int32 getVoxEntry(uint8 *ptr, const char *filename, int32 index, int32 hiddenInd
 int32 getAllocVoxEntry(uint8 **ptr, const char *filename, int32 index, int32 hiddenIndex);
 
 Common::SeekableReadStream *makeReadStream(const char *filename, int index);
-
+inline Common::SeekableReadStream *makeReadStream(const TwineResource &resource) {
+	return makeReadStream(resource.hqr, resource.index);
+}
 } // namespace HQR
 
 } // namespace TwinE
diff --git a/engines/twine/resources/resources.cpp b/engines/twine/resources/resources.cpp
index e40196d6cd..dca05b661c 100644
--- a/engines/twine/resources/resources.cpp
+++ b/engines/twine/resources/resources.cpp
@@ -41,8 +41,6 @@ Resources::~Resources() {
 		free(_samplesTable[i]);
 	}
 	free(_fontPtr);
-	free(_holomapSurfacePtr);
-	free(_holomapImagePtr);
 }
 
 void Resources::initPalettes() {
@@ -147,46 +145,36 @@ void Resources::initResources() {
 	_engine->_text->setFontColor(COLOR_14);
 	_engine->_text->setTextCrossColor(136, 143, 2);
 
-	if (!_spriteShadowPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_SPRITESHADOW, _engine->isLBA1())) {
-		error("Failed to load shadow sprites");
-	}
-
 	if (_engine->isLBA1()) {
-		if (!_spriteBoundingBox.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_SPRITEBOXDATA, _engine->isLBA1())) {
-			error("Failed to load sprite bounding box data");
+		if (!_spriteShadowPtr.loadFromHQR(TwineResource(Resources::HQR_RESS_FILE, RESSHQR_SPRITESHADOW), _engine->isLBA1())) {
+			error("Failed to load shadow sprites");
 		}
-	}
-
-	_holomapSurfaceSize = HQR::getAllocEntry(&_holomapSurfacePtr, Resources::HQR_RESS_FILE, RESSHQR_HOLOSURFACE);
-	if (_holomapSurfaceSize == 0) {
-		error("Failed to load holomap surface");
-	}
 
-	_holomapImageSize = HQR::getAllocEntry(&_holomapImagePtr, Resources::HQR_RESS_FILE, RESSHQR_HOLOIMG);
-	if (_holomapImageSize == 0) {
-		error("Failed to load holomap image");
-	}
+		if (!_spriteBoundingBox.loadFromHQR(TwineResource(Resources::HQR_RESS_FILE, RESSHQR_SPRITEBOXDATA), _engine->isLBA1())) {
+			error("Failed to load sprite bounding box data");
+		}
 
-	if (!_holomapTwinsenModelPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_HOLOTWINMDL, _engine->isLBA1())) {
-		error("Failed to load holomap twinsen model");
-	}
+		if (!_holomapTwinsenModelPtr.loadFromHQR(TwineResource(Resources::HQR_RESS_FILE, RESSHQR_HOLOTWINMDL), _engine->isLBA1())) {
+			error("Failed to load holomap twinsen model");
+		}
 
-	if (!_holomapPointModelPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_HOLOPOINTMDL, _engine->isLBA1())) {
-		error("Failed to load holomap point model");
-	}
+		if (!_holomapPointModelPtr.loadFromHQR(TwineResource(Resources::HQR_RESS_FILE, RESSHQR_HOLOPOINTMDL), _engine->isLBA1())) {
+			error("Failed to load holomap point model");
+		}
 
-	if (!_holomapArrowPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_HOLOARROWMDL, _engine->isLBA1())) {
-		error("Failed to load holomap arrow model");
-	}
+		if (!_holomapArrowPtr.loadFromHQR(TwineResource(Resources::HQR_RESS_FILE, RESSHQR_HOLOARROWMDL), _engine->isLBA1())) {
+			error("Failed to load holomap arrow model");
+		}
 
-	if (!_holomapTwinsenArrowPtr.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_HOLOTWINARROWMDL, _engine->isLBA1())) {
-		error("Failed to load holomap twinsen arrow model");
-	}
+		if (!_holomapTwinsenArrowPtr.loadFromHQR(TwineResource(Resources::HQR_RESS_FILE, RESSHQR_HOLOTWINARROWMDL), _engine->isLBA1())) {
+			error("Failed to load holomap twinsen arrow model");
+		}
 
-	if (!_trajectories.loadFromHQR(Resources::HQR_RESS_FILE, RESSHQR_HOLOPOINTANIM, _engine->isLBA1())) {
-		error("Failed to parse trajectory data");
+		if (!_trajectories.loadFromHQR(TwineResource(Resources::HQR_RESS_FILE, RESSHQR_HOLOPOINTANIM), _engine->isLBA1())) {
+			error("Failed to parse trajectory data");
+		}
+		debug("preload %i trajectories", (int)_trajectories.getTrajectories().size());
 	}
-	debug("preload %i trajectories", (int)_trajectories.getTrajectories().size());
 
 	preloadSprites();
 	preloadAnimations();
@@ -194,13 +182,17 @@ void Resources::initResources() {
 	preloadInventoryItems();
 
 	const int32 bodyCount = HQR::numEntries(Resources::HQR_BODY_FILE);
+	const int32 maxBodies = _engine->isLBA1() ? 200 : NUM_BODIES;
+	if (bodyCount > maxBodies) {
+		error("Max body count exceeded: %i", bodyCount);
+	}
 	for (int32 i = 0; i < bodyCount; ++i) {
-		if (!_bodyData[i].loadFromHQR(Resources::HQR_BODY_FILE, i, _engine->isLBA1())) {
+		if (!_bodyData[i].loadFromHQR(TwineResource(Resources::HQR_BODY_FILE, i), _engine->isLBA1())) {
 			error("HQR ERROR: Parsing body entity for model %i failed", i);
 		}
 	}
 
-	loadFlaInfo();
+	loadMovieInfo();
 
 	const int32 textEntryCount = _engine->isLBA1() ? 28 : 30;
 	for (int32 i = 0; i < textEntryCount / 2; ++i) {
@@ -219,35 +211,46 @@ const Trajectory *Resources::getTrajectory(int index) const {
 	return _trajectories.getTrajectory(index);
 }
 
-void Resources::loadFlaInfo() {
+void Resources::loadMovieInfo() {
 	uint8 *content = nullptr;
-	const int32 size = HQR::getAllocEntry(&content, Resources::HQR_RESS_FILE, RESSHQR_FLAINFO);
+	int32 size;
+	if (_engine->isLBA1()) {
+		size = HQR::getAllocEntry(&content, Resources::HQR_RESS_FILE, RESSHQR_FLAINFO);
+	} else {
+		size = HQR::getAllocEntry(&content, Resources::HQR_RESS_FILE, 48);
+	}
 	if (size == 0) {
 		return;
 	}
 	const Common::String str((const char *)content, size);
 	free(content);
-
+	debug(3, "movie info:\n%s", str.c_str());
 	Common::StringTokenizer tok(str, "\r\n");
+	int videoIndex = 0;
 	while (!tok.empty()) {
 		const Common::String &line = tok.nextToken();
-		Common::StringTokenizer lineTok(line);
-		if (lineTok.empty()) {
-			continue;
-		}
-		const Common::String &name = lineTok.nextToken();
-		Common::Array<int32> frames;
-		while (!lineTok.empty()) {
-			const Common::String &frame = lineTok.nextToken();
-			const int32 frameIdx = atoi(frame.c_str());
-			frames.push_back(frameIdx);
+		if (_engine->isLBA1()) {
+			Common::StringTokenizer lineTok(line);
+			if (lineTok.empty()) {
+				continue;
+			}
+			const Common::String &name = lineTok.nextToken();
+			Common::Array<int32> frames;
+			while (!lineTok.empty()) {
+				const Common::String &frame = lineTok.nextToken();
+				const int32 frameIdx = atoi(frame.c_str());
+				frames.push_back(frameIdx);
+			}
+			_movieInfo.setVal(name, frames);
+		} else {
+			Common::Array<int32> info(videoIndex);
+			_movieInfo.setVal(line, info);
 		}
-		_flaMovieFrames.setVal(name, frames);
 	}
 }
 
-const Common::Array<int32> &Resources::getFlaMovieInfo(const Common::String &name) const {
-	return _flaMovieFrames.getVal(name);
+const Common::Array<int32> &Resources::getMovieInfo(const Common::String &name) const {
+	return _movieInfo.getVal(name);
 }
 
 } // namespace TwinE
diff --git a/engines/twine/resources/resources.h b/engines/twine/resources/resources.h
index bec9bf9717..1194a8b961 100644
--- a/engines/twine/resources/resources.h
+++ b/engines/twine/resources/resources.h
@@ -48,33 +48,17 @@ namespace TwinE {
 #define RESSHQR_HOLOTWINMDL 9
 #define RESSHQR_HOLOARROWMDL 10
 #define RESSHQR_HOLOTWINARROWMDL 11
-#define RESSHQR_RELLENTIMG 12
-#define RESSHQR_RELLENTPAL 13
-#define RESSHQR_MENUIMG 14
-#define RESSHQR_INTROSCREEN1IMG 15
-#define RESSHQR_INTROSCREEN1PAL 16
-#define RESSHQR_INTROSCREEN2IMG 17
-#define RESSHQR_INTROSCREEN2PAL 18
-#define RESSHQR_INTROSCREEN3IMG 19
-#define RESSHQR_INTROSCREEN3PAL 20
+
 #define RESSHQR_GAMEOVERMDL 21
 
 #define RESSHQR_ALARMREDPAL 22
 #define RESSHQR_FLAINFO 23
 #define RESSHQR_DARKPAL 24
-#define RESSHQR_TWINSEN_ZOE_SENDELLIMG 25
-#define RESSHQR_TWINSEN_ZOE_SENDELLPAL 26
-#define RESSHQR_ADELINEIMG 27
-#define RESSHQR_ADELINEPAL 28
 
 #define RESSHQR_HOLOPOINTMDL 29
 #define RESSHQR_HOLOPOINTANIM 30
 
-#define RESSHQR_LBAIMG 49
-#define RESSHQR_LBAPAL 50
 #define RESSHQR_PLASMAEFFECT 51
-#define RESSHQR_EAIMG 52
-#define RESSHQR_EAPAL 53
 
 #define FLA_DRAGON3 "dragon3"
 #define FLA_INTROD "introd"
@@ -148,10 +132,10 @@ private:
 	/** Preload all animations */
 	void preloadAnimations();
 	void preloadSamples();
-	void loadFlaInfo();
+	void loadMovieInfo();
 
 	using MovieInfoMap = Common::HashMap<Common::String, Common::Array<int32> >;
-	MovieInfoMap _flaMovieFrames;
+	MovieInfoMap _movieInfo;
 
 	TrajectoryData _trajectories;
 
@@ -161,7 +145,11 @@ public:
 	Resources(TwinEEngine *engine) : _engine(engine) {}
 	~Resources();
 
-	const Common::Array<int32> &getFlaMovieInfo(const Common::String &name) const;
+	/**
+	 * For lba1 this is returning the gif images that are used as a placeholder for the fla movies
+	 * For lba2 this is the list of videos that are mapped by their entry index
+	 */
+	const Common::Array<int32> &getMovieInfo(const Common::String &name) const;
 
 	/** Table with all loaded samples */
 	BodyData _inventoryTable[NUM_INVENTORY_ITEMS];
@@ -189,11 +177,6 @@ public:
 	SpriteData _spriteShadowPtr;
 	SpriteBoundingBoxData _spriteBoundingBox;
 
-	uint32 _holomapSurfaceSize = 0;
-	uint8 *_holomapSurfacePtr = nullptr;
-	uint32 _holomapImageSize = 0;
-	uint8 *_holomapImagePtr = nullptr;
-
 	BodyData _holomapPointModelPtr;
 	BodyData _holomapTwinsenModelPtr;
 	BodyData _holomapTwinsenArrowPtr;
@@ -229,6 +212,8 @@ public:
 	static constexpr const char *HQR_LBA_BRK_FILE = "lba_brk.hqr";
 	// scenes (active area content (actors, scripts, etc.))
 	static constexpr const char *HQR_SCENE_FILE = "scene.hqr";
+	// full screen images (lba2)
+	static constexpr const char *HQR_SCREEN_FILE = "screen.hqr";
 	// sprites
 	static constexpr const char *HQR_SPRITES_FILE = "sprites.hqr";
 	/**
@@ -250,6 +235,51 @@ public:
 	static constexpr const char *HQR_FLASAMP_FILE = "flasamp.hqr";
 	static constexpr const char *HQR_MIDI_MI_DOS_FILE = "midi_mi.hqr";
 	static constexpr const char *HQR_MIDI_MI_WIN_FILE = "midi_mi_win.hqr";
+
+	static constexpr const char *HQR_VIDEO_FILE = "video.hqr"; // lba2 - smk files
+
+	TwineImage adelineLogo() const {
+		if (_engine->isLBA1()) {
+			return TwineImage(Resources::HQR_RESS_FILE, 27, 28);
+		}
+		return TwineImage(Resources::HQR_SCREEN_FILE, 0, 1);
+	}
+
+	TwineImage lbaLogo() const {
+		if (_engine->isLBA1()) {
+			return TwineImage(Resources::HQR_RESS_FILE, 49, 50);
+		}
+		return TwineImage(Resources::HQR_SCREEN_FILE, 60, 61);
+	}
+
+	TwineImage eaLogo() const {
+		if (_engine->isLBA1()) {
+			return TwineImage(Resources::HQR_RESS_FILE, 52, 53);
+		}
+		return TwineImage(Resources::HQR_SCREEN_FILE, 74, 75);
+	}
+
+	TwineImage activisionLogo() const {
+		assert(_engine->isLBA2());
+		return TwineImage(Resources::HQR_SCREEN_FILE, 72, 73);
+	}
+
+	TwineImage virginLogo() const {
+		assert(_engine->isLBA2());
+		return TwineImage(Resources::HQR_SCREEN_FILE, 76, 77);
+	}
+
+	TwineImage relentLogo() const {
+		assert(_engine->isLBA1());
+		return TwineImage(Resources::HQR_RESS_FILE, 12, 13);
+	}
+
+	TwineImage menuBackground() const {
+		if (_engine->isLBA1()) {
+			return TwineImage(Resources::HQR_RESS_FILE, 14, -1);
+		}
+		return TwineImage(Resources::HQR_SCREEN_FILE, 4, 5);
+	}
 };
 
 } // namespace TwinE
diff --git a/engines/twine/scene/actor.h b/engines/twine/scene/actor.h
index feacdf5c8c..a8edb49e78 100644
--- a/engines/twine/scene/actor.h
+++ b/engines/twine/scene/actor.h
@@ -34,7 +34,7 @@ namespace TwinE {
 #define NUM_SPRITES 425 // 200 for lba1
 
 /** Total number of bodies allowed in the game */
-#define NUM_BODIES 200
+#define NUM_BODIES 469 // 131 for lba1
 
 /** Actors move structure */
 struct ActorMoveStruct {
diff --git a/engines/twine/scene/animations.cpp b/engines/twine/scene/animations.cpp
index 521510f03e..bce4fd5c45 100644
--- a/engines/twine/scene/animations.cpp
+++ b/engines/twine/scene/animations.cpp
@@ -318,12 +318,17 @@ void Animations::processAnimActions(int32 actorIdx) {
 			}
 			break;
 		case ActionType::ACTION_LEFT_STEP:
-		case ActionType::ACTION_RIGHT_STEP:
 			if (action.animFrame == actor->_animPosition && (actor->_brickSound & 0xF0U) != 0xF0U) {
 				const int16 sampleIdx = (actor->_brickSound & 0x0FU) + Samples::WalkFloorBegin;
 				_engine->_sound->playSample(sampleIdx, 1, actor->pos(), actorIdx);
 			}
 			break;
+		case ActionType::ACTION_RIGHT_STEP:
+			if (action.animFrame == actor->_animPosition && (actor->_brickSound & 0xF0U) != 0xF0U) {
+				const int16 sampleIdx = (actor->_brickSound & 0x0FU) + Samples::WalkFloorRightBegin;
+				_engine->_sound->playSample(sampleIdx, 1, actor->pos(), actorIdx);
+			}
+			break;
 		case ActionType::ACTION_HERO_HITTING:
 			if (action.animFrame - 1 == actor->_animPosition) {
 				actor->_strengthOfHit = magicLevelStrengthOfHit[_engine->_gameState->_magicLevelIdx];
diff --git a/engines/twine/scene/gamestate.cpp b/engines/twine/scene/gamestate.cpp
index 18255cca4a..c2e2531764 100644
--- a/engines/twine/scene/gamestate.cpp
+++ b/engines/twine/scene/gamestate.cpp
@@ -512,7 +512,7 @@ void GameState::processGameoverAnimation() {
 		}
 
 		const int32 avg = _engine->_collision->getAverageValue(40000, 3200, 500, _engine->_lbaTime - startLbaTime);
-		const int32 cdot = _engine->_screens->crossDot(1, 1024, 100, (_engine->_lbaTime - startLbaTime) % 100);
+		const int32 cdot = _engine->_screens->lerp(1, 1024, 100, (_engine->_lbaTime - startLbaTime) % 100);
 
 		_engine->blitWorkToFront(rect);
 		_engine->_renderer->setCameraAngle(0, 0, 0, 0, -cdot, 0, avg);
diff --git a/engines/twine/scene/grid.h b/engines/twine/scene/grid.h
index 4ab519b653..c5a54a6760 100644
--- a/engines/twine/scene/grid.h
+++ b/engines/twine/scene/grid.h
@@ -300,10 +300,6 @@ public:
 	inline ShapeType getBrickShapeFull(const IVec3 &pos, int32 y2) {
 		return getBrickShapeFull(pos.x, pos.y, pos.z, y2);
 	}
-
-	inline uint8 getBrickSoundType(const IVec3 &pos) {
-		return getBrickSoundType(pos.x, pos.y, pos.z);
-	}
 };
 
 } // namespace TwinE
diff --git a/engines/twine/scene/scene.cpp b/engines/twine/scene/scene.cpp
index a07fed9230..ab777ff874 100644
--- a/engines/twine/scene/scene.cpp
+++ b/engines/twine/scene/scene.cpp
@@ -468,23 +468,24 @@ void Scene::reloadCurrentScene() {
 }
 
 void Scene::changeScene() {
-	if (_useScenePatches) {
-		if (_currentSceneIdx == LBA1SceneId::Citadel_Island_Harbor && _needChangeScene == LBA1SceneId::Principal_Island_Harbor && _sceneNumZones > 14) {
-			const ZoneStruct *zone = &_sceneZones[15];
-			const IVec3 &track = _sceneTracks[8];
-			IVec3 &pos = _zoneHeroPos;
-			pos.x = zone->infoData.ChangeScene.x - zone->mins.x + track.x;
-			pos.y = zone->infoData.ChangeScene.y - zone->mins.y + track.y;
-			pos.z = zone->infoData.ChangeScene.z - zone->mins.z + track.z;
-			_engine->_scene->_heroPositionType = ScenePositionType::kZone;
-			// otherActorIdx = lactorIdx;
-			debug(3, "Using zone position %i:%i:%i", pos.x, pos.y, pos.z);
+	if (_engine->isLBA1()) {
+		if (_useScenePatches) {
+			if (_currentSceneIdx == LBA1SceneId::Citadel_Island_Harbor && _needChangeScene == LBA1SceneId::Principal_Island_Harbor && _sceneNumZones > 14) {
+				const ZoneStruct *zone = &_sceneZones[15];
+				const IVec3 &track = _sceneTracks[8];
+				IVec3 &pos = _zoneHeroPos;
+				pos.x = zone->infoData.ChangeScene.x - zone->mins.x + track.x;
+				pos.y = zone->infoData.ChangeScene.y - zone->mins.y + track.y;
+				pos.z = zone->infoData.ChangeScene.z - zone->mins.z + track.z;
+				_engine->_scene->_heroPositionType = ScenePositionType::kZone;
+				debug(3, "Using zone position %i:%i:%i", pos.x, pos.y, pos.z);
+			}
 		}
-	}
 
-	// change twinsen house destroyed hard-coded
-	if (_needChangeScene == LBA1SceneId::Citadel_Island_near_twinsens_house && _engine->_gameState->hasOpenedFunfrocksSafe()) {
-		_needChangeScene = LBA1SceneId::Citadel_Island_Twinsens_house_destroyed;
+		// change twinsen house destroyed hard-coded
+		if (_needChangeScene == LBA1SceneId::Citadel_Island_near_twinsens_house && _engine->_gameState->hasOpenedFunfrocksSafe()) {
+			_needChangeScene = LBA1SceneId::Citadel_Island_Twinsens_house_destroyed;
+		}
 	}
 
 	// local backup previous scene
@@ -498,14 +499,16 @@ void Scene::changeScene() {
 	}
 	debug(2, "Entering scene %s (came from %i)", _engine->_gameState->_sceneName, _previousSceneIdx);
 
-	if (_needChangeScene == LBA1SceneId::Polar_Island_end_scene) {
-		_engine->unlockAchievement("LBA_ACH_001");
-		// if you finish the game in less than 4 hours
-		if (_engine->getTotalPlayTime() <= 1000 * 60 * 60 * 4) {
-			_engine->unlockAchievement("LBA_ACH_005");
+	if (_engine->isLBA1()) {
+		if (_needChangeScene == LBA1SceneId::Polar_Island_end_scene) {
+			_engine->unlockAchievement("LBA_ACH_001");
+			// if you finish the game in less than 4 hours
+			if (_engine->getTotalPlayTime() <= 1000 * 60 * 60 * 4) {
+				_engine->unlockAchievement("LBA_ACH_005");
+			}
+		} else if (_needChangeScene == LBA1SceneId::Brundle_Island_Secret_room) {
+			_engine->unlockAchievement("LBA_ACH_006");
 		}
-	} else if (_needChangeScene == LBA1SceneId::Brundle_Island_Secret_room) {
-		_engine->unlockAchievement("LBA_ACH_006");
 	}
 
 	_engine->_sound->stopSamples();
@@ -535,9 +538,7 @@ void Scene::changeScene() {
 
 	if (_heroPositionType == ScenePositionType::kZone) {
 		_newHeroPos = _zoneHeroPos;
-	}
-
-	if (_heroPositionType == ScenePositionType::kScene || _heroPositionType == ScenePositionType::kNoPosition) {
+	} else if (_heroPositionType == ScenePositionType::kScene || _heroPositionType == ScenePositionType::kNoPosition) {
 		_newHeroPos = _sceneHeroPos;
 	}
 
diff --git a/engines/twine/script/script_life_v1.cpp b/engines/twine/script/script_life_v1.cpp
index de778d7fb4..6e82d1920c 100644
--- a/engines/twine/script/script_life_v1.cpp
+++ b/engines/twine/script/script_life_v1.cpp
@@ -29,7 +29,7 @@
 #include "twine/audio/music.h"
 #include "twine/audio/sound.h"
 #include "twine/scene/collision.h"
-#include "twine/flamovies.h"
+#include "twine/movies.h"
 #include "twine/scene/gamestate.h"
 #include "twine/scene/grid.h"
 #include "twine/holomap.h"
@@ -1578,7 +1578,7 @@ static int32 lSET_NORMAL_PAL(TwinEEngine *engine, LifeScriptContext &ctx) {
 static int32 lMESSAGE_SENDELL(TwinEEngine *engine, LifeScriptContext &ctx) {
 	ScopedEngineFreeze scoped(engine);
 	engine->_screens->fadeToBlack(engine->_screens->_paletteRGBA);
-	engine->_screens->loadImage(RESSHQR_TWINSEN_ZOE_SENDELLIMG, RESSHQR_TWINSEN_ZOE_SENDELLPAL);
+	engine->_screens->loadImage(TwineImage(Resources::HQR_RESS_FILE, 25, 26));
 	engine->_text->textClipFull();
 	engine->_text->setFontCrossColor(COLOR_WHITE);
 	engine->_text->_drawTextBoxBackground = false;
diff --git a/engines/twine/script/script_move_v1.cpp b/engines/twine/script/script_move_v1.cpp
index 7f187490fb..88b90014d5 100644
--- a/engines/twine/script/script_move_v1.cpp
+++ b/engines/twine/script/script_move_v1.cpp
@@ -26,7 +26,7 @@
 #include "common/util.h"
 #include "twine/scene/animations.h"
 #include "twine/audio/sound.h"
-#include "twine/flamovies.h"
+#include "twine/movies.h"
 #include "twine/scene/movements.h"
 #include "twine/renderer/redraw.h"
 #include "twine/renderer/renderer.h"
diff --git a/engines/twine/shared.h b/engines/twine/shared.h
index 0413874398..4fb34c434f 100644
--- a/engines/twine/shared.h
+++ b/engines/twine/shared.h
@@ -568,6 +568,22 @@ enum InventoryItems {
 	MaxInventoryItems = 28
 };
 
+struct TwineResource {
+	const char *hqr;
+	const int32 index;
+
+	constexpr TwineResource(const char *_hqr, int32 _index) : hqr(_hqr), index(_index) {
+	}
+};
+
+struct TwineImage {
+	TwineResource image;
+	TwineResource palette;
+
+	constexpr TwineImage(const char *hqr, int32 index, int32 paletteIndex = -1) : image(hqr, index), palette(hqr, paletteIndex) {
+	}
+};
+
 // lba2 does from 0 to 0x1000
 // lba1 angles
 // TODO: wrap in a class to be able to handle lba1 and lba2
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index b6beeac15b..1d97acb503 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -49,7 +49,7 @@
 #include "twine/debugger/debug_grid.h"
 #include "twine/debugger/debug_scene.h"
 #include "twine/detection.h"
-#include "twine/flamovies.h"
+#include "twine/movies.h"
 #include "twine/holomap.h"
 #include "twine/input.h"
 #include "twine/menu/interface.h"
@@ -129,6 +129,9 @@ TwinEEngine::TwinEEngine(OSystem *system, Common::Language language, uint32 flag
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 	SearchMan.addSubDirectoryMatching(gameDataDir, "fla");
 	SearchMan.addSubDirectoryMatching(gameDataDir, "vox");
+	if (isLBA2()) {
+		SearchMan.addSubDirectoryMatching(gameDataDir, "video");
+	}
 	if (flags & TF_DOTEMU_ENHANCED) {
 		SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/hqr");
 		SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/fla");
@@ -160,7 +163,7 @@ TwinEEngine::TwinEEngine(OSystem *system, Common::Language language, uint32 flag
 	_movements = new Movements(this);
 	_interface = new Interface(this);
 	_menu = new Menu(this);
-	_flaMovies = new FlaMovies(this);
+	_flaMovies = new Movies(this);
 	_menuOptions = new MenuOptions(this);
 	_music = new Music(this);
 	_redraw = new Redraw(this);
@@ -483,30 +486,42 @@ void TwinEEngine::initEngine() {
 	_input->enableKeyMap(cutsceneKeyMapId);
 	// Display company logo
 	bool abort = false;
+
+	if (isLBA2()) {
+		//abort |= _screens->loadImageDelay(_resources->activisionLogo(), 7);
+		abort |= _screens->loadImageDelay(_resources->eaLogo(), 7);
+	}
+
 	abort |= _screens->adelineLogo();
 
-	// verify game version screens
-	if (!abort && _cfgfile.Version == EUROPE_VERSION) {
-		// Little Big Adventure screen
-		abort |= _screens->loadImageDelay(RESSHQR_LBAIMG, RESSHQR_LBAPAL, 3);
-		if (!abort) {
-			// Electronic Arts Logo
-			abort |= _screens->loadImageDelay(RESSHQR_EAIMG, RESSHQR_EAPAL, 2);
-		}
-	} else if (!abort && _cfgfile.Version == USA_VERSION) {
-		// Relentless screen
-		abort |= _screens->loadImageDelay(RESSHQR_RELLENTIMG, RESSHQR_RELLENTPAL, 3);
-		if (!abort) {
-			// Electronic Arts Logo
-			abort |= _screens->loadImageDelay(RESSHQR_EAIMG, RESSHQR_EAPAL, 2);
+	if (isLBA1()) {
+		// verify game version screens
+		if (!abort && _cfgfile.Version == EUROPE_VERSION) {
+			// Little Big Adventure screen
+			abort |= _screens->loadImageDelay(_resources->lbaLogo(), 3);
+			if (!abort) {
+				// Electronic Arts Logo
+				abort |= _screens->loadImageDelay(_resources->eaLogo(), 2);
+			}
+		} else if (!abort && _cfgfile.Version == USA_VERSION) {
+			// Relentless screen
+			abort |= _screens->loadImageDelay(_resources->relentLogo(), 3);
+			if (!abort) {
+				// Electronic Arts Logo
+				abort |= _screens->loadImageDelay(_resources->eaLogo(), 2);
+			}
+		} else if (!abort && _cfgfile.Version == MODIFICATION_VERSION) {
+			// Modification screen
+			abort |= _screens->loadImageDelay(_resources->relentLogo(), 2);
 		}
-	} else if (!abort && _cfgfile.Version == MODIFICATION_VERSION) {
-		// Modification screen
-		abort |= _screens->loadImageDelay(RESSHQR_RELLENTIMG, RESSHQR_RELLENTPAL, 2);
 	}
 
 	if (!abort) {
-		_flaMovies->playFlaMovie(FLA_DRAGON3);
+		if (isLBA1()) {
+			_flaMovies->playFlaMovie(FLA_DRAGON3);
+		} else {
+			_flaMovies->playSmkMovie(16);
+		}
 	}
 	_input->enableKeyMap(uiKeyMapId);
 
@@ -566,7 +581,7 @@ void TwinEEngine::processActorSamplePosition(int32 actorIdx) {
 
 void TwinEEngine::processBookOfBu() {
 	_screens->fadeToBlack(_screens->_paletteRGBA);
-	_screens->loadImage(RESSHQR_INTROSCREEN1IMG, RESSHQR_INTROSCREEN1PAL);
+	_screens->loadImage(TwineImage(Resources::HQR_RESS_FILE, 15, 16));
 	_text->initTextBank(TextBankId::Inventory_Intro_and_Holomap);
 	_text->_drawTextBoxBackground = false;
 	_text->textClipFull();
@@ -903,28 +918,26 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 			const uint8 brickSound = _grid->getBrickSoundType(actor->_pos.x, actor->_pos.y - 1, actor->_pos.z);
 			actor->_brickSound = brickSound;
 
-			if ((brickSound & 0xF0U) == 0xF0U) {
-				if ((brickSound & 0x0FU) == 1) {
-					if (IS_HERO(a)) {
-						// we are dying if we aren't using the protopack to fly over water
-						if (_actor->_heroBehaviour != HeroBehaviourType::kProtoPack || actor->_anim != AnimationTypes::kForward) {
-							if (!_actor->_cropBottomScreen) {
-								_animations->initAnim(AnimationTypes::kDrawn, AnimType::kAnimationType_4, AnimationTypes::kStanding, 0);
-							}
-							const IVec3 &projPos = _renderer->projectPositionOnScreen(actor->pos() - _grid->_camera);
-							actor->_controlMode = ControlMode::kNoMove;
-							actor->setLife(-1);
-							_actor->_cropBottomScreen = projPos.y;
-							actor->_staticFlags.bDoesntCastShadow = 1;
+			if (brickSound == 0xF1U) {
+				if (IS_HERO(a)) {
+					// we are dying if we aren't using the protopack to fly over water
+					if (_actor->_heroBehaviour != HeroBehaviourType::kProtoPack || actor->_anim != AnimationTypes::kForward) {
+						if (!_actor->_cropBottomScreen) {
+							_animations->initAnim(AnimationTypes::kDrawn, AnimType::kAnimationType_4, AnimationTypes::kStanding, 0);
 						}
-					} else {
-						_sound->playSample(Samples::Explode, 1, actor->pos(), a);
-						if (actor->_bonusParameter.cloverleaf || actor->_bonusParameter.kashes || actor->_bonusParameter.key || actor->_bonusParameter.lifepoints || actor->_bonusParameter.magicpoints) {
-							if (!actor->_bonusParameter.unk1) {
-								_actor->processActorExtraBonus(a);
-							}
-							actor->setLife(0);
+						const IVec3 &projPos = _renderer->projectPositionOnScreen(actor->pos() - _grid->_camera);
+						actor->_controlMode = ControlMode::kNoMove;
+						actor->setLife(-1);
+						_actor->_cropBottomScreen = projPos.y;
+						actor->_staticFlags.bDoesntCastShadow = 1;
+					}
+				} else {
+					_sound->playSample(Samples::Explode, 1, actor->pos(), a);
+					if (actor->_bonusParameter.cloverleaf || actor->_bonusParameter.kashes || actor->_bonusParameter.key || actor->_bonusParameter.lifepoints || actor->_bonusParameter.magicpoints) {
+						if (!actor->_bonusParameter.unk1) {
+							_actor->processActorExtraBonus(a);
 						}
+						actor->setLife(0);
 					}
 				}
 			}
diff --git a/engines/twine/twine.h b/engines/twine/twine.h
index 643acc1992..c6e6cf0fb5 100644
--- a/engines/twine/twine.h
+++ b/engines/twine/twine.h
@@ -115,7 +115,6 @@ struct ConfigFile {
 	// these settings can be changed in-game - and must be persisted
 	/** Shadow mode type, value: all, character only, none */
 	int32 ShadowMode = 0;
-	// TODO: currently unused
 	int32 PolygonDetails = 2;
 	/** Scenery Zoom */
 	bool SceZoom = false;
@@ -130,7 +129,7 @@ class Grid;
 class Movements;
 class Interface;
 class Menu;
-class FlaMovies;
+class Movies;
 class MenuOptions;
 class Music;
 class Redraw;
@@ -260,7 +259,7 @@ public:
 	Movements *_movements;
 	Interface *_interface;
 	Menu *_menu;
-	FlaMovies *_flaMovies;
+	Movies *_flaMovies;
 	MenuOptions *_menuOptions;
 	Music *_music;
 	Redraw *_redraw;


Commit: 039f7001133cff410ee6712087000fb7c6720022
    https://github.com/scummvm/scummvm/commit/039f7001133cff410ee6712087000fb7c6720022
Author: eientei (log.butt at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: Add LBA Preview Version dated August 15 1994

Changed paths:
    engines/twine/detection.cpp


diff --git a/engines/twine/detection.cpp b/engines/twine/detection.cpp
index 1b80eaf605..ad28a75c60 100644
--- a/engines/twine/detection.cpp
+++ b/engines/twine/detection.cpp
@@ -56,6 +56,28 @@ static const ADGameDescription twineGameDescriptions[] = {
 		GUIO1(GUIO_NONE)
 	},
 
+	// Little Big Adventure - Preview Version (EN, FR)
+	// LBA.EXE
+	// 15 August 1994 at 18:28
+	{
+		"lba",
+		"Preview Version",
+		AD_ENTRY1s("LBA.EXE", "c1a887e38283d43f271249ad9f2a73ef", 298697),
+		Common::EN_ANY,
+		Common::kPlatformDOS,
+		ADGF_NO_FLAGS,
+		GUIO1(GUIO_NONE)
+	},
+	{
+		"lba",
+		"Preview Version",
+		AD_ENTRY1s("LBA.EXE", "c1a887e38283d43f271249ad9f2a73ef", 298697),
+		Common::FR_FRA,
+		Common::kPlatformDOS,
+		ADGF_NO_FLAGS,
+		GUIO1(GUIO_NONE)
+	},
+
 	// Little Big Adventure - Demo Version (EN, FR, DE, IT, ES)
 	// RELENT.EXE
 	// 14 October 1994 at 10:18


Commit: 7aa3ad20677b362ad99c413abdca9cf877a87ad9
    https://github.com/scummvm/scummvm/commit/7aa3ad20677b362ad99c413abdca9cf877a87ad9
Author: eientei (log.butt at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: Mark later Preview Version as unstable

Changed paths:
    engines/twine/detection.cpp


diff --git a/engines/twine/detection.cpp b/engines/twine/detection.cpp
index ad28a75c60..dc281c07e3 100644
--- a/engines/twine/detection.cpp
+++ b/engines/twine/detection.cpp
@@ -65,7 +65,7 @@ static const ADGameDescription twineGameDescriptions[] = {
 		AD_ENTRY1s("LBA.EXE", "c1a887e38283d43f271249ad9f2a73ef", 298697),
 		Common::EN_ANY,
 		Common::kPlatformDOS,
-		ADGF_NO_FLAGS,
+		ADGF_UNSTABLE,
 		GUIO1(GUIO_NONE)
 	},
 	{
@@ -74,7 +74,7 @@ static const ADGameDescription twineGameDescriptions[] = {
 		AD_ENTRY1s("LBA.EXE", "c1a887e38283d43f271249ad9f2a73ef", 298697),
 		Common::FR_FRA,
 		Common::kPlatformDOS,
-		ADGF_NO_FLAGS,
+		ADGF_UNSTABLE,
 		GUIO1(GUIO_NONE)
 	},
 


Commit: 265853d8497ae78174736184e8e9119828e12cbc
    https://github.com/scummvm/scummvm/commit/265853d8497ae78174736184e8e9119828e12cbc
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: track index of -1 means to stop the music

Changed paths:
    engines/twine/audio/music.cpp


diff --git a/engines/twine/audio/music.cpp b/engines/twine/audio/music.cpp
index 5dcd71bf9f..7e6373cf5f 100644
--- a/engines/twine/audio/music.cpp
+++ b/engines/twine/audio/music.cpp
@@ -137,6 +137,10 @@ void Music::stopTrackMusicCd() {
 }
 
 bool Music::playTrackMusic(int32 track) {
+	if (track == -1) {
+		stopMusic();
+		return true;
+	}
 	if (!_engine->_cfgfile.Sound) {
 		return false;
 	}


Commit: d237f772dec27e89e97290c570b9f205c6b23f2b
    https://github.com/scummvm/scummvm/commit/d237f772dec27e89e97290c570b9f205c6b23f2b
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: fixed extra flag names

Changed paths:
    engines/twine/scene/extra.cpp
    engines/twine/scene/extra.h


diff --git a/engines/twine/scene/extra.cpp b/engines/twine/scene/extra.cpp
index 1b1019f75a..3bbe0a139a 100644
--- a/engines/twine/scene/extra.cpp
+++ b/engines/twine/scene/extra.cpp
@@ -85,7 +85,7 @@ int32 Extra::addExtra(int32 actorIdx, int32 x, int32 y, int32 z, int32 spriteIdx
 			continue;
 		}
 		extra->info0 = spriteIdx;
-		extra->type = ExtraType::UNK7;
+		extra->type = ExtraType::SEARCH_OBJ;
 		extra->info1 = 0;
 		extra->pos.x = x;
 		extra->pos.y = y;
@@ -161,7 +161,7 @@ int32 Extra::addExtraSpecial(int32 x, int32 y, int32 z, ExtraSpecialType type) {
 		extra->info1 = 0;
 
 		if (type == ExtraSpecialType::kHitStars) {
-			extra->type = ExtraType::TIME_OUT | ExtraType::UNK3;
+			extra->type = ExtraType::TIME_OUT | ExtraType::END_COL;
 
 			extra->pos.x = x;
 			extra->pos.y = y;
@@ -229,10 +229,10 @@ int32 Extra::addExtraBonus(int32 x, int32 y, int32 z, int32 xAngle, int32 yAngle
 			continue;
 		}
 		extra->info0 = type;
-		extra->type = ExtraType::STOP_COL | ExtraType::TAKABLE | ExtraType::BONUS;
+		extra->type = ExtraType::STOP_COL | ExtraType::TAKABLE | ExtraType::WAIT_SOME_TIME;
 
 		if (type != SPRITEHQR_KEY) {
-			extra->type = ExtraType::TIME_OUT | ExtraType::TAKABLE | ExtraType::FLASH | ExtraType::STOP_COL | ExtraType::BONUS;
+			extra->type = ExtraType::TIME_OUT | ExtraType::TAKABLE | ExtraType::FLASH | ExtraType::STOP_COL | ExtraType::WAIT_SOME_TIME;
 		}
 
 		extra->pos.x = x;
@@ -257,7 +257,7 @@ int32 Extra::addExtraThrow(int32 actorIdx, int32 x, int32 y, int32 z, int32 spri
 			continue;
 		}
 		extra->info0 = spriteIdx;
-		extra->type = ExtraType::UNK2 | ExtraType::UNK3 | ExtraType::UNK8 | ExtraType::WAIT_NO_COL;
+		extra->type = ExtraType::END_OBJ | ExtraType::END_COL | ExtraType::IMPACT | ExtraType::WAIT_NO_COL;
 		extra->pos.x = x;
 		extra->pos.y = y;
 		extra->pos.z = z;
@@ -282,7 +282,7 @@ int32 Extra::addExtraAiming(int32 actorIdx, int32 x, int32 y, int32 z, int32 spr
 			continue;
 		}
 		extra->info0 = spriteIdx;
-		extra->type = ExtraType::UNK7;
+		extra->type = ExtraType::SEARCH_OBJ;
 		extra->info1 = 0;
 		extra->pos.x = x;
 		extra->pos.y = y;
@@ -538,7 +538,7 @@ void Extra::processExtras() {
 			}
 		}
 		// reset extra
-		if (extra->type & ExtraType::RESET_EXTRA) {
+		if (extra->type & ExtraType::ONE_FRAME) {
 			extra->info0 = -1;
 			continue;
 		}
@@ -589,14 +589,14 @@ void Extra::processExtras() {
 			}
 		}
 
-		if (extra->type & ExtraType::BONUS) {
+		if (extra->type & ExtraType::WAIT_SOME_TIME) {
 			if (_engine->_lbaTime - extra->spawnTime > 40) {
-				extra->type &= ~ExtraType::BONUS;
+				extra->type &= ~ExtraType::WAIT_SOME_TIME;
 			}
 			continue;
 		}
 		// process actor target hit
-		if (extra->type & ExtraType::UNK7) {
+		if (extra->type & ExtraType::SEARCH_OBJ) {
 			int32 actorIdxAttacked = extra->spawnTime;
 			int32 actorIdx = extra->payload.actorIdx;
 
@@ -719,7 +719,7 @@ void Extra::processExtras() {
 			}
 		}
 		// process extra collision with actors
-		if (extra->type & ExtraType::UNK2) {
+		if (extra->type & ExtraType::END_OBJ) {
 			if (_engine->_collision->checkExtraCollisionWithActors(extra, extra->payload.actorIdx) != -1) {
 				// if extra is Magic Ball
 				if (i == _engine->_gameState->_magicBallIdx) {
@@ -740,7 +740,7 @@ void Extra::processExtras() {
 			}
 		}
 		// process extra collision with scene ground
-		if (extra->type & ExtraType::UNK3) {
+		if (extra->type & ExtraType::END_COL) {
 			bool process = false;
 
 			if (_engine->_collision->checkExtraCollisionWithBricks(currentExtraX, currentExtraY, currentExtraZ, extra->pos)) {
@@ -757,7 +757,7 @@ void Extra::processExtras() {
 
 			if (process) {
 				// show explode cloud
-				if (extra->type & ExtraType::UNK8) {
+				if (extra->type & ExtraType::IMPACT) {
 					addExtraSpecial(currentExtraX, currentExtraY, currentExtraZ, ExtraSpecialType::kExplodeCloud);
 				}
 				// if extra is magic ball
diff --git a/engines/twine/scene/extra.h b/engines/twine/scene/extra.h
index 960b5a5be6..72cb1a61d3 100644
--- a/engines/twine/scene/extra.h
+++ b/engines/twine/scene/extra.h
@@ -44,22 +44,22 @@ struct ExtraShape {
 };
 
 enum ExtraType {
-	TIME_OUT = 1 << 0,       // 0x0001
-	FLY = 1 << 1,            // 0x0002
-	UNK2 = 1 << 2,           // 0x0004
-	UNK3 = 1 << 3,           // 0x0008
-	STOP_COL = 1 << 4,       // 0x0010
-	TAKABLE = 1 << 5,        // 0x0020
-	FLASH = 1 << 6,          // 0x0040
-	UNK7 = 1 << 7,           // 0x0080
-	UNK8 = 1 << 8,           // 0x0100
-	MAGIC_BALL_KEY = 1 << 9, // 0x0200
-	TIME_IN = 1 << 10,       // 0x0400
-	RESET_EXTRA = 1 << 11,   // 0x0800
-	EXPLOSION = 1 << 12,     // 0x1000
-	WAIT_NO_COL = 1 << 13,   // 0x2000
-	BONUS = 1 << 14,         // 0x4000
-	UNK15 = 1 << 15          // 0x8000
+	TIME_OUT = 1 << 0,        // 0x0001
+	FLY = 1 << 1,             // 0x0002
+	END_OBJ = 1 << 2,         // 0x0004
+	END_COL = 1 << 3,         // 0x0008
+	STOP_COL = 1 << 4,        // 0x0010
+	TAKABLE = 1 << 5,         // 0x0020
+	FLASH = 1 << 6,           // 0x0040
+	SEARCH_OBJ = 1 << 7,      // 0x0080
+	IMPACT = 1 << 8,          // 0x0100
+	MAGIC_BALL_KEY = 1 << 9,  // 0x0200
+	TIME_IN = 1 << 10,        // 0x0400
+	ONE_FRAME = 1 << 11,      // 0x0800
+	EXPLOSION = 1 << 12,      // 0x1000
+	WAIT_NO_COL = 1 << 13,    // 0x2000
+	WAIT_SOME_TIME = 1 << 14, // 0x4000
+	COMPUTE_TRAJ = 1 << 15    // 0x8000 used in dotemu enhanced to render the magic ball trajectories
 };
 
 struct ExtraListStruct {


Commit: 701c1de7dc581404c0ce01a63af1bc22e88b72e9
    https://github.com/scummvm/scummvm/commit/701c1de7dc581404c0ce01a63af1bc22e88b72e9
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: replaced magic numbers and named enum constants

Changed paths:
    engines/twine/scene/actor.cpp
    engines/twine/scene/animations.cpp
    engines/twine/scene/collision.cpp
    engines/twine/scene/grid.h
    engines/twine/scene/movements.cpp
    engines/twine/scene/scene.cpp
    engines/twine/script/script_life_v1.cpp
    engines/twine/shared.h
    engines/twine/twine.cpp


diff --git a/engines/twine/scene/actor.cpp b/engines/twine/scene/actor.cpp
index 172fc4439a..aecae0cbcd 100644
--- a/engines/twine/scene/actor.cpp
+++ b/engines/twine/scene/actor.cpp
@@ -129,7 +129,7 @@ void Actor::setBehaviour(HeroBehaviourType behaviour) {
 	sceneHero->_anim = AnimationTypes::kAnimNone;
 	sceneHero->_animType = AnimType::kAnimationTypeLoop;
 
-	_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, 0);
+	_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, OWN_ACTOR_SCENE_INDEX);
 }
 
 void Actor::initSpriteActor(int32 actorIdx) {
@@ -329,9 +329,9 @@ void Actor::hitActor(int32 actorIdx, int32 actorIdxAttacked, int32 strengthOfHit
 			}
 
 			if (_engine->getRandomNumber() & 1) {
-				_engine->_animations->initAnim(AnimationTypes::kHit2, AnimType::kAnimationType_3, AnimationTypes::kAnimInvalid, actorIdxAttacked);
+				_engine->_animations->initAnim(AnimationTypes::kHit2, AnimType::kAnimationInsert, AnimationTypes::kAnimInvalid, actorIdxAttacked);
 			} else {
-				_engine->_animations->initAnim(AnimationTypes::kBigHit, AnimType::kAnimationType_3, AnimationTypes::kAnimInvalid, actorIdxAttacked);
+				_engine->_animations->initAnim(AnimationTypes::kBigHit, AnimType::kAnimationInsert, AnimationTypes::kAnimInvalid, actorIdxAttacked);
 			}
 		}
 
@@ -346,7 +346,7 @@ void Actor::hitActor(int32 actorIdx, int32 actorIdxAttacked, int32 strengthOfHit
 			actor->_life = 0;
 		}
 	} else {
-		_engine->_animations->initAnim(AnimationTypes::kHit, AnimType::kAnimationType_3, AnimationTypes::kAnimInvalid, actorIdxAttacked);
+		_engine->_animations->initAnim(AnimationTypes::kHit, AnimType::kAnimationInsert, AnimationTypes::kAnimInvalid, actorIdxAttacked);
 	}
 }
 
diff --git a/engines/twine/scene/animations.cpp b/engines/twine/scene/animations.cpp
index bce4fd5c45..30f3e6a4ed 100644
--- a/engines/twine/scene/animations.cpp
+++ b/engines/twine/scene/animations.cpp
@@ -402,7 +402,7 @@ bool Animations::initAnim(AnimationTypes newAnim, AnimType animType, AnimationTy
 		return true;
 	}
 
-	if (animExtra == AnimationTypes::kAnimInvalid && actor->_animType != AnimType::kAnimationType_2) {
+	if (animExtra == AnimationTypes::kAnimInvalid && actor->_animType != AnimType::kAnimationAllThen) {
 		animExtra = actor->_anim;
 	}
 
@@ -412,13 +412,13 @@ bool Animations::initAnim(AnimationTypes newAnim, AnimType animType, AnimationTy
 		animIndex = getBodyAnimIndex(AnimationTypes::kStanding, actorIdx);
 	}
 
-	if (animType != AnimType::kAnimationType_4 && actor->_animType == AnimType::kAnimationType_2) {
+	if (animType != AnimType::kAnimationSet && actor->_animType == AnimType::kAnimationAllThen) {
 		actor->_animExtra = newAnim;
 		return false;
 	}
 
-	if (animType == AnimType::kAnimationType_3) {
-		animType = AnimType::kAnimationType_2;
+	if (animType == AnimType::kAnimationInsert) {
+		animType = AnimType::kAnimationAllThen;
 
 		animExtra = actor->_anim;
 
@@ -427,8 +427,8 @@ bool Animations::initAnim(AnimationTypes newAnim, AnimType animType, AnimationTy
 		}
 	}
 
-	if (animType == AnimType::kAnimationType_4) {
-		animType = AnimType::kAnimationType_2;
+	if (animType == AnimType::kAnimationSet) {
+		animType = AnimType::kAnimationAllThen;
 	}
 
 	if (actor->_previousAnimIdx == -1) {
@@ -695,7 +695,7 @@ void Animations::processActorAnimations(int32 actorIdx) {
 			if (destPos.x >= 0 && destPos.z >= 0 && destPos.x <= 0x7E00 && destPos.z <= 0x7E00) { // SCENE_SIZE_MAX
 				if (_engine->_grid->getBrickShape(destPos.x, processActor.y + BRICK_HEIGHT, destPos.z) != ShapeType::kNone && _engine->_cfgfile.WallCollision) { // avoid wall hit damage
 					_engine->_extra->addExtraSpecial(actor->_pos.x, actor->_pos.y + 1000, actor->_pos.z, ExtraSpecialType::kHitStars);
-					initAnim(AnimationTypes::kBigHit, AnimType::kAnimationType_2, AnimationTypes::kStanding, _currentlyProcessedActorIdx);
+					initAnim(AnimationTypes::kBigHit, AnimType::kAnimationAllThen, AnimationTypes::kStanding, _currentlyProcessedActorIdx);
 
 					if (IS_HERO(_currentlyProcessedActorIdx)) {
 						_engine->_movements->_heroMoved = true;
@@ -717,7 +717,7 @@ void Animations::processActorAnimations(int32 actorIdx) {
 				} else {
 					if (IS_HERO(actorIdx) && _engine->_actor->_heroBehaviour == HeroBehaviourType::kAthletic && actor->_anim == AnimationTypes::kForward && _engine->_cfgfile.WallCollision) { // avoid wall hit damage
 						_engine->_extra->addExtraSpecial(actor->_pos.x, actor->_pos.y + 1000, actor->_pos.z, ExtraSpecialType::kHitStars);
-						initAnim(AnimationTypes::kBigHit, AnimType::kAnimationType_2, AnimationTypes::kStanding, _currentlyProcessedActorIdx);
+						initAnim(AnimationTypes::kBigHit, AnimType::kAnimationAllThen, AnimationTypes::kStanding, _currentlyProcessedActorIdx);
 						_engine->_movements->_heroMoved = true;
 						actor->addLife(-1);
 					}
diff --git a/engines/twine/scene/collision.cpp b/engines/twine/scene/collision.cpp
index 9ec6e61c72..714c7e4a53 100644
--- a/engines/twine/scene/collision.cpp
+++ b/engines/twine/scene/collision.cpp
@@ -383,16 +383,16 @@ void Collision::stopFalling() { // ReceptionObj()
 			} else {
 				_engine->_actor->_processActorPtr->addLife(-1);
 			}
-			_engine->_animations->initAnim(AnimationTypes::kLandingHit, AnimType::kAnimationType_2, AnimationTypes::kStanding, _engine->_animations->_currentlyProcessedActorIdx);
+			_engine->_animations->initAnim(AnimationTypes::kLandingHit, AnimType::kAnimationAllThen, AnimationTypes::kStanding, _engine->_animations->_currentlyProcessedActorIdx);
 		} else if (fall > 10) {
-			_engine->_animations->initAnim(AnimationTypes::kLanding, AnimType::kAnimationType_2, AnimationTypes::kStanding, _engine->_animations->_currentlyProcessedActorIdx);
+			_engine->_animations->initAnim(AnimationTypes::kLanding, AnimType::kAnimationAllThen, AnimationTypes::kStanding, _engine->_animations->_currentlyProcessedActorIdx);
 		} else {
 			_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeLoop, AnimationTypes::kStanding, _engine->_animations->_currentlyProcessedActorIdx);
 		}
 
 		_engine->_scene->_heroYBeforeFall = 0;
 	} else {
-		_engine->_animations->initAnim(AnimationTypes::kLanding, AnimType::kAnimationType_2, _engine->_actor->_processActorPtr->_animExtra, _engine->_animations->_currentlyProcessedActorIdx);
+		_engine->_animations->initAnim(AnimationTypes::kLanding, AnimType::kAnimationAllThen, _engine->_actor->_processActorPtr->_animExtra, _engine->_animations->_currentlyProcessedActorIdx);
 	}
 
 	_engine->_actor->_processActorPtr->_dynamicFlags.bIsFalling = 0;
diff --git a/engines/twine/scene/grid.h b/engines/twine/scene/grid.h
index c5a54a6760..014fcabe68 100644
--- a/engines/twine/scene/grid.h
+++ b/engines/twine/scene/grid.h
@@ -23,6 +23,8 @@
 #ifndef TWINE_SCENE_GRID_H
 #define TWINE_SCENE_GRID_H
 
+#define WATER_BRICK (0xF1)
+
 #include "common/scummsys.h"
 #include "twine/parser/blocklibrary.h"
 #include "twine/parser/sprite.h"
diff --git a/engines/twine/scene/movements.cpp b/engines/twine/scene/movements.cpp
index 1f8739621c..d740f48927 100644
--- a/engines/twine/scene/movements.cpp
+++ b/engines/twine/scene/movements.cpp
@@ -218,7 +218,7 @@ bool Movements::processBehaviourExecution(int actorIdx) {
 		executeAction = true;
 		break;
 	case HeroBehaviourType::kAthletic:
-		_engine->_animations->initAnim(AnimationTypes::kJump, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+		_engine->_animations->initAnim(AnimationTypes::kJump, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 		break;
 	case HeroBehaviourType::kAggressive:
 		if (_engine->_actor->_autoAggressive) {
@@ -231,25 +231,25 @@ bool Movements::processBehaviourExecution(int actorIdx) {
 
 				switch (aggresiveMode) {
 				case 0:
-					_engine->_animations->initAnim(AnimationTypes::kKick, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+					_engine->_animations->initAnim(AnimationTypes::kKick, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 					break;
 				case 1:
-					_engine->_animations->initAnim(AnimationTypes::kRightPunch, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+					_engine->_animations->initAnim(AnimationTypes::kRightPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 					break;
 				case 2:
-					_engine->_animations->initAnim(AnimationTypes::kLeftPunch, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+					_engine->_animations->initAnim(AnimationTypes::kLeftPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 					break;
 				}
 			}
 		} else {
 			if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
-				_engine->_animations->initAnim(AnimationTypes::kLeftPunch, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+				_engine->_animations->initAnim(AnimationTypes::kLeftPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 				_heroMoved = true;
 			} else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
-				_engine->_animations->initAnim(AnimationTypes::kRightPunch, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+				_engine->_animations->initAnim(AnimationTypes::kRightPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 				_heroMoved = true;
 			} else if (_engine->_input->isActionActive(TwinEActionType::MoveForward)) {
-				_engine->_animations->initAnim(AnimationTypes::kKick, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+				_engine->_animations->initAnim(AnimationTypes::kKick, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 				_heroMoved = true;
 			}
 		}
@@ -269,7 +269,7 @@ bool Movements::processAttackExecution(int actorIdx) {
 		// Use Magic Ball
 		if (_engine->_gameState->hasItem(InventoryItems::kiMagicBall)) {
 			if (_engine->_gameState->_magicBallIdx == -1) {
-				_engine->_animations->initAnim(AnimationTypes::kThrowBall, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+				_engine->_animations->initAnim(AnimationTypes::kThrowBall, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 			}
 
 			actor->_angle = actor->_move.getRealAngle(_engine->_lbaTime);
@@ -280,7 +280,7 @@ bool Movements::processAttackExecution(int actorIdx) {
 			_engine->_actor->initModelActor(BodyType::btSabre, actorIdx);
 		}
 
-		_engine->_animations->initAnim(AnimationTypes::kSabreAttack, AnimType::kAnimationType_1, AnimationTypes::kStanding, actorIdx);
+		_engine->_animations->initAnim(AnimationTypes::kSabreAttack, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
 
 		actor->_angle = actor->_move.getRealAngle(_engine->_lbaTime);
 		return true;
diff --git a/engines/twine/scene/scene.cpp b/engines/twine/scene/scene.cpp
index ab777ff874..4bd28e9d68 100644
--- a/engines/twine/scene/scene.cpp
+++ b/engines/twine/scene/scene.cpp
@@ -733,7 +733,7 @@ void Scene::processActorZones(int32 actorIdx) {
 				break;
 			case ZoneType::kObject:
 				if (IS_HERO(actorIdx) && _engine->_movements->shouldTriggerZoneAction()) {
-					_engine->_animations->initAnim(AnimationTypes::kAction, AnimType::kAnimationType_1, AnimationTypes::kStanding, 0);
+					_engine->_animations->initAnim(AnimationTypes::kAction, AnimType::kAnimationThen, AnimationTypes::kStanding, OWN_ACTOR_SCENE_INDEX);
 					processZoneExtraBonus(zone);
 				}
 				break;
@@ -757,7 +757,7 @@ void Scene::processActorZones(int32 actorIdx) {
 						if (_engine->_grid->getBrickShape(destPos.x, actor->_pos.y + ANGLE_90, destPos.z) != ShapeType::kNone) {
 							_currentActorInZone = true;
 							if (actor->_pos.y >= ABS(zone->mins.y + zone->maxs.y) / 2) {
-								_engine->_animations->initAnim(AnimationTypes::kTopLadder, AnimType::kAnimationType_2, AnimationTypes::kStanding, actorIdx); // reached end of ladder
+								_engine->_animations->initAnim(AnimationTypes::kTopLadder, AnimType::kAnimationAllThen, AnimationTypes::kStanding, actorIdx); // reached end of ladder
 							} else {
 								_engine->_animations->initAnim(AnimationTypes::kClimbLadder, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx); // go up in ladder
 							}
diff --git a/engines/twine/script/script_life_v1.cpp b/engines/twine/script/script_life_v1.cpp
index 6e82d1920c..1b50c73d42 100644
--- a/engines/twine/script/script_life_v1.cpp
+++ b/engines/twine/script/script_life_v1.cpp
@@ -733,7 +733,7 @@ static int32 lCAM_FOLLOW(TwinEEngine *engine, LifeScriptContext &ctx) {
 static int32 lSET_BEHAVIOUR(TwinEEngine *engine, LifeScriptContext &ctx) {
 	const HeroBehaviourType behavior = (HeroBehaviourType)ctx.stream.readByte();
 
-	engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, 0);
+	engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, OWN_ACTOR_SCENE_INDEX);
 	engine->_actor->setBehaviour(behavior);
 
 	return 0;
diff --git a/engines/twine/shared.h b/engines/twine/shared.h
index 4fb34c434f..da2eb9b8ab 100644
--- a/engines/twine/shared.h
+++ b/engines/twine/shared.h
@@ -262,15 +262,15 @@ enum class AnimationTypes {
 
 enum class AnimType {
 	kAnimationTypeLoop = 0,
-	kAnimationType_1 = 1,
+	kAnimationThen = 1,
 	// play animation and let animExtra follow as next animation
 	// if there is already a next animation set - replace the value
-	kAnimationType_2 = 2,
+	kAnimationAllThen = 2,
 	// replace animation and let the current animation follow
-	kAnimationType_3 = 3,
+	kAnimationInsert = 3,
 	// play animation and let animExtra follow as next animation
 	// but don't take the current state in account
-	kAnimationType_4 = 4
+	kAnimationSet = 4
 };
 
 /** Hero behaviour
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index 1d97acb503..7fde7de197 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -633,7 +633,7 @@ void TwinEEngine::processInventoryAction() {
 				_actor->setBehaviour(HeroBehaviourType::kNormal);
 			}
 			_actor->initModelActor(BodyType::btSabre, OWN_ACTOR_SCENE_INDEX);
-			_animations->initAnim(AnimationTypes::kSabreUnknown, AnimType::kAnimationType_1, AnimationTypes::kStanding, OWN_ACTOR_SCENE_INDEX);
+			_animations->initAnim(AnimationTypes::kSabreUnknown, AnimType::kAnimationThen, AnimationTypes::kStanding, OWN_ACTOR_SCENE_INDEX);
 
 			_gameState->_usingSabre = true;
 		}
@@ -875,7 +875,7 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 
 		if (actor->_life == 0) {
 			if (IS_HERO(a)) {
-				_animations->initAnim(AnimationTypes::kLandDeath, AnimType::kAnimationType_4, AnimationTypes::kStanding, 0);
+				_animations->initAnim(AnimationTypes::kLandDeath, AnimType::kAnimationSet, AnimationTypes::kStanding, OWN_ACTOR_SCENE_INDEX);
 				actor->_controlMode = ControlMode::kNoMove;
 			} else {
 				_sound->playSample(Samples::Explode, 1, actor->pos(), a);
@@ -918,12 +918,12 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 			const uint8 brickSound = _grid->getBrickSoundType(actor->_pos.x, actor->_pos.y - 1, actor->_pos.z);
 			actor->_brickSound = brickSound;
 
-			if (brickSound == 0xF1U) {
+			if (brickSound == WATER_BRICK) {
 				if (IS_HERO(a)) {
 					// we are dying if we aren't using the protopack to fly over water
 					if (_actor->_heroBehaviour != HeroBehaviourType::kProtoPack || actor->_anim != AnimationTypes::kForward) {
 						if (!_actor->_cropBottomScreen) {
-							_animations->initAnim(AnimationTypes::kDrawn, AnimType::kAnimationType_4, AnimationTypes::kStanding, 0);
+							_animations->initAnim(AnimationTypes::kDrawn, AnimType::kAnimationSet, AnimationTypes::kStanding, OWN_ACTOR_SCENE_INDEX);
 						}
 						const IVec3 &projPos = _renderer->projectPositionOnScreen(actor->pos() - _grid->_camera);
 						actor->_controlMode = ControlMode::kNoMove;


Commit: c19e28b37957b598e9ab3e9ea4d63cb6eb76a0db
    https://github.com/scummvm/scummvm/commit/c19e28b37957b598e9ab3e9ea4d63cb6eb76a0db
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: merged with latest master

Changed paths:
    engines/twine/menu/menu.cpp
    engines/twine/movies.cpp
    engines/twine/movies.h
    engines/twine/renderer/redraw.cpp
    engines/twine/renderer/redraw.h
    engines/twine/renderer/renderer.cpp
    engines/twine/renderer/renderer.h
    engines/twine/renderer/screens.h
    engines/twine/scene/gamestate.cpp
    engines/twine/twine.h


diff --git a/engines/twine/menu/menu.cpp b/engines/twine/menu/menu.cpp
index 2a24e6ad3d..1df4c50c90 100644
--- a/engines/twine/menu/menu.cpp
+++ b/engines/twine/menu/menu.cpp
@@ -35,6 +35,7 @@
 #include "twine/scene/animations.h"
 #include "twine/audio/music.h"
 #include "twine/audio/sound.h"
+#include "twine/movies.h"
 #include "twine/scene/gamestate.h"
 #include "twine/scene/grid.h"
 #include "twine/resources/hqr.h"
@@ -591,17 +592,23 @@ int32 Menu::processMenu(MenuSettings *menuSettings, bool showCredits) {
 		}
 		if (showCredits && loopMillis - startMillis > 11650) {
 			_engine->_menuOptions->showCredits();
+			if (_engine->_flaMovies->playFlaMovie(FLA_DRAGON3)) {
+				if (!_engine->_screens->loadImageDelay(TwineImage(Resources::HQR_RESS_FILE, 15, 16), 3)) {
+					if (!_engine->_screens->loadImageDelay(TwineImage(Resources::HQR_RESS_FILE, 17, 18), 3)) {
+						if (!_engine->_screens->loadImageDelay(TwineImage(Resources::HQR_RESS_FILE, 19, 20), 3)) {
+							if (_engine->_flaMovies->playFlaMovie(FLA_BATEAU)) {
+								if (_engine->_cfgfile.Version == USA_VERSION) {
+									_engine->_screens->loadImageDelay(_engine->_resources->relentLogo(), 3);
+								} else {
+									_engine->_screens->loadImageDelay(_engine->_resources->lbaLogo(), 3);
+								}
+								_engine->_screens->adelineLogo();
+							}
+						}
+					}
+				}
+			}
 			_engine->_text->initTextBank(TextBankId::Options_and_menus);
-
-			// TODO the original game also performs these actions:
-			// play FLA_DRAGON3 fla
-			// display RESSHQR_INTROSCREEN1IMG
-			// display RESSHQR_INTROSCREEN2IMG
-			// display RESSHQR_INTROSCREEN3IMG
-			// play FLA_BATEAU fla
-			// if version == EUROPE_VERSION display RESSHQR_LBAIMG else display RESSHQR_RELLENTIMG
-			// display adeline logo
-			// pressing any key during these actions will abort everything and return to the menu
 			startMillis = _engine->_system->getMillis();
 			_engine->_screens->loadMenuImage(false);
 		}
@@ -1148,6 +1155,7 @@ void Menu::drawInventoryItems(int32 left, int32 top) {
 	for (int32 item = 0; item < NUM_INVENTORY_ITEMS; item++) {
 		drawItem(left, top, item);
 	}
+	_engine->_interface->resetClip();
 }
 
 void Menu::processInventoryMenu() {
diff --git a/engines/twine/movies.cpp b/engines/twine/movies.cpp
index ef70aa0664..98c23ca8d5 100644
--- a/engines/twine/movies.cpp
+++ b/engines/twine/movies.cpp
@@ -353,7 +353,7 @@ void Movies::playGIFMovie(const char *flaName) {
 	}
 }
 
-void Movies::playFlaMovie(const char *flaName) {
+bool Movies::playFlaMovie(const char *flaName) {
 	assert(_engine->isLBA1());
 	_engine->_sound->stopSamples();
 
@@ -365,7 +365,7 @@ void Movies::playFlaMovie(const char *flaName) {
 
 	if (_engine->_cfgfile.Movie == CONF_MOVIE_FLAGIF) {
 		playGIFMovie(fileNamePath.c_str());
-		return;
+		return true;
 	}
 
 	_engine->_music->stopMusic();
@@ -377,7 +377,7 @@ void Movies::playFlaMovie(const char *flaName) {
 	if (!_file.open(fileNamePath + FLA_EXT)) {
 		warning("Failed to open fla movie '%s'", fileNamePath.c_str());
 		playGIFMovie(fileNamePath.c_str());
-		return;
+		return true;
 	}
 
 	const uint32 version = _file.readUint32LE();
@@ -395,6 +395,7 @@ void Movies::playFlaMovie(const char *flaName) {
 
 	_file.skip(4 * _samplesInFla);
 
+	bool finished = false;
 	if (version != MKTAG('V', '1', '.', '3')) {
 		int32 currentFrame = 0;
 
@@ -410,6 +411,7 @@ void Movies::playFlaMovie(const char *flaName) {
 				break;
 			}
 			if (currentFrame == _flaHeaderData.numOfFrames) {
+				finished = true;
 				break;
 			}
 			processFrame();
@@ -446,6 +448,7 @@ void Movies::playFlaMovie(const char *flaName) {
 	}
 
 	_engine->_sound->stopSamples();
+	return finished;
 }
 
 void Movies::playSmkMovie(int index) {
diff --git a/engines/twine/movies.h b/engines/twine/movies.h
index eb409e584d..f02198b856 100644
--- a/engines/twine/movies.h
+++ b/engines/twine/movies.h
@@ -101,8 +101,9 @@ public:
 	/**
 	 * Play FLA movies
 	 * @param flaName FLA movie name
+	 * @return @c true if finished. @c false if aborted.
 	 */
-	void playFlaMovie(const char *flaName);
+	bool playFlaMovie(const char *flaName);
 
 	void playSmkMovie(int index);
 };
diff --git a/engines/twine/renderer/redraw.cpp b/engines/twine/renderer/redraw.cpp
index 8e87a05cfe..5bf6c2a066 100644
--- a/engines/twine/renderer/redraw.cpp
+++ b/engines/twine/renderer/redraw.cpp
@@ -336,6 +336,7 @@ void Redraw::processDrawListShadows(const DrawListStruct &drawCmd) {
 	addRedrawArea(_engine->_interface->_clip);
 
 	_engine->_debugScene->drawClip(renderRect);
+	_engine->_interface->resetClip();
 }
 
 void Redraw::processDrawListActors(const DrawListStruct &drawCmd, bool bgRedraw) {
@@ -348,7 +349,15 @@ void Redraw::processDrawListActors(const DrawListStruct &drawCmd, bool bgRedraw)
 
 	const IVec3 &delta = actor->pos() - _engine->_grid->_camera;
 	Common::Rect renderRect;
+
+	if (actorIdx == OWN_ACTOR_SCENE_INDEX) {
+		if (_engine->_actor->_cropBottomScreen) {
+			_engine->_interface->_clip.bottom = _engine->_actor->_cropBottomScreen;
+		}
+	}
+
 	if (!_engine->_renderer->renderIsoModel(delta.x, delta.y, delta.z, ANGLE_0, actor->_angle, ANGLE_0, _engine->_resources->_bodyData[actor->_entity], renderRect)) {
+		_engine->_interface->resetClip();
 		return;
 	}
 
@@ -364,10 +373,6 @@ void Redraw::processDrawListActors(const DrawListStruct &drawCmd, bool bgRedraw)
 
 		_engine->_grid->drawOverModelActor(tempX, tempY, tempZ);
 
-		if (_engine->_actor->_cropBottomScreen) {
-			_engine->_interface->_clip.bottom = _engine->_actor->_cropBottomScreen + 10;
-		}
-
 		addRedrawArea(_engine->_interface->_clip);
 
 		if (actor->_staticFlags.bIsBackgrounded && bgRedraw) {
@@ -376,6 +381,7 @@ void Redraw::processDrawListActors(const DrawListStruct &drawCmd, bool bgRedraw)
 
 		_engine->_debugScene->drawClip(_engine->_interface->_clip);
 	}
+	_engine->_interface->resetClip();
 }
 
 void Redraw::processDrawListActorSprites(const DrawListStruct &drawCmd, bool bgRedraw) {
@@ -399,14 +405,15 @@ void Redraw::processDrawListActorSprites(const DrawListStruct &drawCmd, bool bgR
 	renderRect.right = renderRect.left + spriteWidth;
 	renderRect.bottom = renderRect.top + spriteHeight;
 
+	bool validClip;
 	if (actor->_staticFlags.bUsesClipping) {
 		const Common::Rect rect(_projPosScreen.x + actor->_cropLeft, _projPosScreen.y + actor->_cropTop, _projPosScreen.x + actor->_cropRight, _projPosScreen.y + actor->_cropBottom);
-		_engine->_interface->setClip(rect);
+		validClip = _engine->_interface->setClip(rect);
 	} else {
-		_engine->_interface->setClip(renderRect);
+		validClip = _engine->_interface->setClip(renderRect);
 	}
 
-	if (_engine->_interface->_clip.isValidRect()) {
+	if (validClip) {
 		_engine->_grid->drawSprite(0, renderRect.left, renderRect.top, spritePtr);
 
 		actor->_dynamicFlags.bIsVisible = 1;
@@ -434,6 +441,7 @@ void Redraw::processDrawListActorSprites(const DrawListStruct &drawCmd, bool bgR
 		}
 
 		_engine->_debugScene->drawClip(renderRect);
+		_engine->_interface->resetClip();
 	}
 }
 
@@ -461,9 +469,7 @@ void Redraw::processDrawListExtras(const DrawListStruct &drawCmd) {
 		_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
 	}
 
-	_engine->_interface->setClip(renderRect);
-
-	if (_engine->_interface->_clip.isValidRect()) {
+	if (_engine->_interface->setClip(renderRect)) {
 		const int32 tmpX = (extra->pos.x + BRICK_HEIGHT) / BRICK_SIZE;
 		const int32 tmpY = extra->pos.y / BRICK_HEIGHT;
 		const int32 tmpZ = (extra->pos.z + BRICK_HEIGHT) / BRICK_SIZE;
@@ -473,6 +479,7 @@ void Redraw::processDrawListExtras(const DrawListStruct &drawCmd) {
 
 		// show clipping area
 		//drawRectBorders(renderRect);
+		_engine->_interface->resetClip();
 	}
 }
 
@@ -480,27 +487,13 @@ void Redraw::processDrawList(DrawListStruct *drawList, int32 drawListPos, bool b
 	for (int32 pos = 0; pos < drawListPos; ++pos) {
 		const DrawListStruct &drawCmd = drawList[pos];
 		const uint32 flags = drawCmd.type;
-		// Drawing actors
-		if (flags < DrawListType::DrawShadows) {
-			if (flags == 0) {
-				processDrawListActors(drawCmd, bgRedraw);
-			}
-		}
-		// Drawing shadows
-		else if (flags == DrawListType::DrawShadows && !_engine->_actor->_cropBottomScreen) {
+		if (flags == DrawListType::DrawObject3D) {
+			processDrawListActors(drawCmd, bgRedraw);
+		} else if (flags == DrawListType::DrawShadows && !_engine->_actor->_cropBottomScreen) {
 			processDrawListShadows(drawCmd);
-		}
-		// Drawing unknown
-		else if (flags < DrawListType::DrawActorSprites) {
-			// TODO reverse this part of the code
-			warning("Not yet reversed part of the rendering code: %u", flags);
-		}
-		// Drawing sprite actors, doors and entities
-		else if (flags == DrawListType::DrawActorSprites) {
+		} else if (flags == DrawListType::DrawActorSprites) {
 			processDrawListActorSprites(drawCmd, bgRedraw);
-		}
-		// Drawing extras
-		else if (flags == DrawListType::DrawExtras) {
+		} else if (flags == DrawListType::DrawExtras) {
 			processDrawListExtras(drawCmd);
 		}
 
@@ -575,6 +568,8 @@ void Redraw::renderOverlays() {
 				_engine->_text->drawText(renderRect.left, renderRect.top, text);
 
 				addRedrawArea(_engine->_interface->_clip);
+
+				_engine->_interface->resetClip();
 				break;
 			}
 			case OverlayType::koNumberRange: {
@@ -599,6 +594,7 @@ void Redraw::renderOverlays() {
 				_engine->_text->drawText(renderRect.left, renderRect.top, text);
 
 				addRedrawArea(_engine->_interface->_clip);
+				_engine->_interface->resetClip();
 				break;
 			}
 			case OverlayType::koInventoryItem: {
@@ -614,6 +610,7 @@ void Redraw::renderOverlays() {
 				_engine->_menu->drawRectBorders(rect);
 				addRedrawArea(rect);
 				_engine->_gameState->initEngineProjections();
+				_engine->_interface->resetClip();
 				break;
 			}
 			case OverlayType::koText: {
@@ -638,6 +635,7 @@ void Redraw::renderOverlays() {
 				_engine->_text->drawText(renderRect.left, renderRect.top, text);
 
 				addRedrawArea(_engine->_interface->_clip);
+				_engine->_interface->resetClip();
 				break;
 			}
 			}
@@ -743,9 +741,10 @@ void Redraw::drawBubble(int32 actorIdx) {
 	renderRect.right = spriteWidth + renderRect.left - 1;
 	renderRect.bottom = spriteHeight + renderRect.top - 1;
 
-	_engine->_interface->setClip(renderRect);
-	_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
-	_engine->_interface->resetClip();
+	if (_engine->_interface->setClip(renderRect)) {
+		_engine->_grid->drawSprite(renderRect.left, renderRect.top, spritePtr);
+		_engine->_interface->resetClip();
+	}
 }
 
 void Redraw::zoomScreenScale() {
diff --git a/engines/twine/renderer/redraw.h b/engines/twine/renderer/redraw.h
index 1ca7a43a7d..e34397d5c6 100644
--- a/engines/twine/renderer/redraw.h
+++ b/engines/twine/renderer/redraw.h
@@ -80,14 +80,23 @@ struct DrawListStruct {
 	}
 };
 
+#define TYPE_OBJ_SHIFT (10)
+#define TYPE_OBJ_FIRST (1 << TYPE_OBJ_SHIFT) // 1024
+#define NUM_OBJ_MASK (TYPE_OBJ_FIRST - 1)
+
 class TwinEEngine;
 class Redraw {
 private:
 	TwinEEngine *_engine;
 	enum DrawListType {
-		DrawActorSprites = 0x1000,
-		DrawExtras = 0x1800,
-		DrawShadows = 0xC00
+		DrawObject3D = (0 << TYPE_OBJ_SHIFT),
+		DrawFlagRed = (1 << TYPE_OBJ_SHIFT),
+		DrawFlagYellow = (2 << TYPE_OBJ_SHIFT),
+		DrawShadows = (3 << TYPE_OBJ_SHIFT),
+		DrawActorSprites = (4 << TYPE_OBJ_SHIFT),
+		DrawZoneDec = (5 << TYPE_OBJ_SHIFT),
+		DrawExtras = (6 << TYPE_OBJ_SHIFT),
+		DrawPrimitive = (7 << TYPE_OBJ_SHIFT)
 	};
 
 	Common::Rect _currentRedrawList[300];
diff --git a/engines/twine/renderer/renderer.cpp b/engines/twine/renderer/renderer.cpp
index fcb11c5bee..bd4cbf2b5d 100644
--- a/engines/twine/renderer/renderer.cpp
+++ b/engines/twine/renderer/renderer.cpp
@@ -383,16 +383,46 @@ static FORCEINLINE int16 clamp(int16 x, int16 a, int16 b) {
 	return x < a ? a : (x > b ? b : x);
 }
 
-void Renderer::computePolygons(int16 polyRenderType, const Vertex *vertices, int32 numVertices) {
+bool Renderer::computePolygons(int16 polyRenderType, const Vertex *vertices, int32 numVertices) {
 	uint8 vertexParam1 = vertices[numVertices - 1].colorIndex;
 	int16 currentVertexX = vertices[numVertices - 1].x;
 	int16 currentVertexY = vertices[numVertices - 1].y;
 	const int16 *polyTabBegin = _polyTab;
 	const int16 *polyTabEnd = &_polyTab[_polyTabSize - 1];
-	const int16 *polyTab2Begin = _colorProgressionBuffer;
-	const int16 *polyTab2End = &_colorProgressionBuffer[_polyTabSize - 1];
+	const int16 *colProgressBufStart = _colorProgressionBuffer;
+	const int16 *colProgressBufEnd = &_colorProgressionBuffer[_polyTabSize - 1];
 	const int screenHeight = _engine->height();
 
+	const Common::Rect &clip = _engine->_interface->_clip;
+	if (!clip.isEmpty()) {
+		int32 vleft;
+		int32 vright;
+		int32 vtop;
+		int32 vbottom;
+
+		vleft = vtop = SCENE_SIZE_MAX;
+		vright = vbottom = SCENE_SIZE_MIN;
+
+		for (int32 i = 0; i < numVertices; i++) {
+			if (vertices[i].x < vleft)
+				vleft = vertices[i].x;
+			if (vertices[i].x > vright)
+				vright = vertices[i].x;
+			if (vertices[i].y < vtop)
+				vtop = vertices[i].y;
+			if (vertices[i].y > vbottom)
+				vbottom = vertices[i].y;
+		}
+		// no vertices
+		if (vtop > vbottom) {
+			return false;
+		}
+		if (vright < clip.left - 1 || vleft > clip.right + 1 || vbottom < clip.top - 1 || vtop > clip.bottom + 1) {
+			debug(10, "Clipped %i:%i:%i:%i, clip rect(%i:%i:%i:%i)", vleft, vtop, vright, vbottom, clip.left, clip.top, clip.right, clip.bottom);
+			return false;
+		}
+	}
+
 	for (int32 nVertex = 0; nVertex < numVertices; nVertex++) {
 		const int16 oldVertexY = currentVertexY;
 		const int16 oldVertexX = currentVertexX;
@@ -449,7 +479,7 @@ void Renderer::computePolygons(int16 polyRenderType, const Vertex *vertices, int
 			int16 *outPtr2 = &_colorProgressionBuffer[polyTabIndex];
 
 			for (int16 i = 0; i < vsize + 2; i++) {
-				if (outPtr2 >= polyTab2Begin && outPtr2 <= polyTab2End) {
+				if (outPtr2 >= colProgressBufStart && outPtr2 <= colProgressBufEnd) {
 					*outPtr2 = cvalue;
 				}
 				outPtr2 += direction;
@@ -457,6 +487,7 @@ void Renderer::computePolygons(int16 polyRenderType, const Vertex *vertices, int
 			}
 		}
 	}
+	return true;
 }
 
 void Renderer::renderPolygonsCopper(int vtop, int32 vsize, uint16 color) const {
@@ -954,10 +985,10 @@ void Renderer::renderPolygonsSimplified(int vtop, int32 vsize, uint16 color) con
 }
 
 void Renderer::renderPolygons(const CmdRenderPolygon &polygon, Vertex *vertices, int vtop, int vbottom) {
-	computePolygons(polygon.renderType, vertices, polygon.numVertices);
-
-	const int32 vsize = vbottom - vtop + 1;
-	fillVertices(vtop, vsize, polygon.renderType, polygon.colorIndex);
+	if (computePolygons(polygon.renderType, vertices, polygon.numVertices)) {
+		const int32 vsize = vbottom - vtop + 1;
+		fillVertices(vtop, vsize, polygon.renderType, polygon.colorIndex);
+	}
 }
 
 void Renderer::fillVertices(int vtop, int32 vsize, uint8 renderType, uint16 color) {
@@ -1562,6 +1593,7 @@ void Renderer::renderBehaviourModel(const Common::Rect &rect, int32 y, int32 ang
 	} else {
 		renderIsoModel(0, y, 0, ANGLE_0, angle, ANGLE_0, bodyData, dummy);
 	}
+	_engine->_interface->resetClip();
 }
 
 void Renderer::renderInventoryItem(int32 x, int32 y, const BodyData &bodyData, int32 angle, int32 param) {
diff --git a/engines/twine/renderer/renderer.h b/engines/twine/renderer/renderer.h
index ac19d0bfed..7760d3a007 100644
--- a/engines/twine/renderer/renderer.h
+++ b/engines/twine/renderer/renderer.h
@@ -194,7 +194,7 @@ private:
 	void renderPolygonsDither(int vtop, int32 vsize) const;
 	void renderPolygonsMarble(int vtop, int32 vsize, uint16 color) const;
 	void renderPolygonsSimplified(int vtop, int32 vsize, uint16 color) const;
-	void computePolygons(int16 polyRenderType, const Vertex *vertices, int32 numVertices);
+	bool computePolygons(int16 polyRenderType, const Vertex *vertices, int32 numVertices);
 
 	const RenderCommand *depthSortRenderCommands(int32 numOfPrimitives);
 	uint8 *preparePolygons(const Common::Array<BodyPolygon>& polygons, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData);
diff --git a/engines/twine/renderer/screens.h b/engines/twine/renderer/screens.h
index afc0eb9350..b2e01526cb 100644
--- a/engines/twine/renderer/screens.h
+++ b/engines/twine/renderer/screens.h
@@ -96,6 +96,7 @@ public:
 	 * @param index \a RESS.HQR entry index (starting from 0)
 	 * @param paletteIndex \a RESS.HQR entry index of the palette for the given image. This is often the @c index + 1
 	 * @param seconds number of seconds to delay
+	 * @return @c true if aborted
 	 */
 	bool loadImageDelay(TwineImage image, int32 seconds);
 
diff --git a/engines/twine/scene/gamestate.cpp b/engines/twine/scene/gamestate.cpp
index c2e2531764..1a6f00c41e 100644
--- a/engines/twine/scene/gamestate.cpp
+++ b/engines/twine/scene/gamestate.cpp
@@ -445,6 +445,7 @@ void GameState::processFoundItem(InventoryItems item) {
 	_engine->_text->stopVox(_engine->_text->_currDialTextEntry);
 
 	_engine->_scene->_sceneHero->_animTimerData = tmpAnimTimer;
+	_engine->_interface->resetClip();
 }
 
 void GameState::processGameChoices(TextId choiceIdx) {
diff --git a/engines/twine/twine.h b/engines/twine/twine.h
index c6e6cf0fb5..a3841c358e 100644
--- a/engines/twine/twine.h
+++ b/engines/twine/twine.h
@@ -92,7 +92,7 @@ struct ConfigFile {
 	bool Debug = false;
 	/** Type of music file to be used */
 	MidiFileType MidiType = MIDIFILE_NONE;
-	/** *Game version */
+	/** Game version */
 	int32 Version = EUROPE_VERSION;
 	/** If you want to use the LBA CD or not */
 	int32 UseCD = 0;


Commit: b6e441fe36dc668d3b1ca5b10b88fabf7fa72a71
    https://github.com/scummvm/scummvm/commit/b6e441fe36dc668d3b1ca5b10b88fabf7fa72a71
Author: eientei (log.butt at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: Add French demo dated 21 October 1994

Changed paths:
    engines/twine/detection.cpp


diff --git a/engines/twine/detection.cpp b/engines/twine/detection.cpp
index dc281c07e3..e2d0fe5607 100644
--- a/engines/twine/detection.cpp
+++ b/engines/twine/detection.cpp
@@ -225,6 +225,19 @@ static const ADGameDescription twineGameDescriptions[] = {
 		GUIO1(GUIO_NONE)
 	},
 
+	// Little Big Adventure - Demo Version (FR)
+	// LBA.EXE
+	// 21 October 1994 at 15:25
+	{
+		"lba",
+		"Demo Version",
+		AD_ENTRY1s("LBA.EXE", "c1a887e38283d43f271249ad9f2a73ef", 273281),
+		Common::FR_FRA,
+		Common::kPlatformDOS,
+		ADGF_DEMO,
+		GUIO1(GUIO_NONE)
+	},
+
 	// Little Big Adventure - Floppy Disk Version
 	// FLA_GIF.HQR
 	// 11 August 1995 at 23:28


Commit: d13f9178f47de0acb25ff5344127b2d5c18cce64
    https://github.com/scummvm/scummvm/commit/d13f9178f47de0acb25ff5344127b2d5c18cce64
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: renamed enum value

Changed paths:
    engines/twine/parser/entity.cpp
    engines/twine/scene/animations.cpp
    engines/twine/shared.h


diff --git a/engines/twine/parser/entity.cpp b/engines/twine/parser/entity.cpp
index 0be257112a..002c3628c4 100644
--- a/engines/twine/parser/entity.cpp
+++ b/engines/twine/parser/entity.cpp
@@ -135,7 +135,7 @@ bool EntityData::loadAnim(Common::SeekableReadStream &stream) {
 			action.finalAngle = ToAngle(stream.readSint16LE());
 			action.strength = stream.readByte();
 			break;
-		case ActionType::ACTION_UNKNOWN_21:
+		case ActionType::ACTION_THROW_3D_MAGIC:
 			action.animFrame = stream.readByte();
 			action.distanceX = stream.readSint16LE();
 			action.distanceY = stream.readSint16LE();
diff --git a/engines/twine/scene/animations.cpp b/engines/twine/scene/animations.cpp
index 30f3e6a4ed..af6baf0637 100644
--- a/engines/twine/scene/animations.cpp
+++ b/engines/twine/scene/animations.cpp
@@ -372,7 +372,7 @@ void Animations::processAnimActions(int32 actorIdx) {
 				                                action.targetActor, action.finalAngle, action.strength);
 			}
 			break;
-		case ActionType::ACTION_UNKNOWN_21:
+		case ActionType::ACTION_THROW_3D_MAGIC:
 			if (_engine->_gameState->_magicBallIdx == -1 && action.animFrame == actor->_animPosition) {
 				const IVec3 &destPos = _engine->_movements->rotateActor(action.distanceX, action.distanceZ, actor->_angle);
 				const int32 x = actor->_pos.x + destPos.x;
diff --git a/engines/twine/shared.h b/engines/twine/shared.h
index da2eb9b8ab..fd6e531f85 100644
--- a/engines/twine/shared.h
+++ b/engines/twine/shared.h
@@ -190,7 +190,7 @@ enum class ActionType : uint8 {
 	ACTION_THROW_3D = 18,
 	ACTION_THROW_3D_ALPHA = 19,
 	ACTION_THROW_3D_SEARCH = 20,
-	ACTION_UNKNOWN_21 = 21,
+	ACTION_THROW_3D_MAGIC = 21,
 	ACTION_LAST
 };
 


Commit: 2f1204482d8329cec66affe945bf3850bb7ae7ae
    https://github.com/scummvm/scummvm/commit/2f1204482d8329cec66affe945bf3850bb7ae7ae
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: added isDotEmuEnhanced() helper method

Changed paths:
    engines/twine/twine.cpp
    engines/twine/twine.h


diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index 7fde7de197..fdfe204a23 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -132,18 +132,17 @@ TwinEEngine::TwinEEngine(OSystem *system, Common::Language language, uint32 flag
 	if (isLBA2()) {
 		SearchMan.addSubDirectoryMatching(gameDataDir, "video");
 	}
-	if (flags & TF_DOTEMU_ENHANCED) {
+	if (isDotEmuEnhanced()) {
 		SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/hqr");
 		SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/fla");
 		if (_gameLang == Common::Language::DE_DEU) {
 			SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/vox/de_voice");
-		}
-		if (_gameLang == Common::Language::EN_ANY || _gameLang == Common::Language::EN_GRB || _gameLang == Common::Language::EN_USA) {
+		} else if (_gameLang == Common::Language::EN_ANY || _gameLang == Common::Language::EN_GRB || _gameLang == Common::Language::EN_USA) {
 			SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/vox/en_voice");
-		}
-		if (_gameLang == Common::Language::FR_FRA) {
+		} else if (_gameLang == Common::Language::FR_FRA) {
 			SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/vox/fr_voice");
 		}
+		SearchMan.addSubDirectoryMatching(gameDataDir, "resources");
 #ifdef USE_MAD
 		SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/music");
 		SearchMan.addSubDirectoryMatching(gameDataDir, "resources/lba_files/midi_mp3");
diff --git a/engines/twine/twine.h b/engines/twine/twine.h
index a3841c358e..6efe3c949b 100644
--- a/engines/twine/twine.h
+++ b/engines/twine/twine.h
@@ -245,6 +245,7 @@ public:
 	bool isLBA1() const { return _gameType == TwineGameType::GType_LBA; }
 	bool isLBA2() const { return _gameType == TwineGameType::GType_LBA2; }
 	bool isMod() const { return (_gameFlags & TwinE::TF_MOD) != 0; }
+	bool isDotEmuEnhanced() const { return (_gameFlags & TwinE::TF_DOTEMU_ENHANCED) != 0; }
 	bool isDemo() const { return (_gameFlags & ADGF_DEMO) != 0; };
 	const char *getGameId() const;
 


Commit: 643e2985bf55954f0328cc3834056745bc0d9abd
    https://github.com/scummvm/scummvm/commit/643e2985bf55954f0328cc3834056745bc0d9abd
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2021-11-01T18:12:48+01:00

Commit Message:
TWINE: the demo of lba1 has three scenes

Changed paths:
    engines/twine/twine.cpp


diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index fdfe204a23..d56ff0f26c 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -720,8 +720,12 @@ int32 TwinEEngine::runGameEngine() { // mainLoopInteration
 
 	if (_scene->_needChangeScene > -1) {
 		if (!isMod() && isDemo() && isLBA1()) {
-			// the demo only has these two scenes
-			if (_scene->_needChangeScene != LBA1SceneId::Citadel_Island_Prison && _scene->_needChangeScene != LBA1SceneId::Citadel_Island_outside_the_citadel) {
+			// the demo only has these scenes
+			if (_scene->_needChangeScene != LBA1SceneId::Citadel_Island_Prison
+			 && _scene->_needChangeScene != LBA1SceneId::Citadel_Island_outside_the_citadel
+			 && _scene->_needChangeScene != LBA1SceneId::Citadel_Island_near_the_tavern) {
+				// TODO: PlayMidiFile(6);
+				// TODO: Credits();
 				return 1;
 			}
 		}




More information about the Scummvm-git-logs mailing list