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

mduggan noreply at scummvm.org
Mon Feb 20 09:45:44 UTC 2023


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

Summary:
550f2e276d TETRAEDGE: Inventory fixes for Syberia 2
787bffdd2f TETRAEDGE: Update scene loading for Syberia 2
a559a0c7c9 TETRAEDGE: Support converting image formats on load
4dde2c98f4 TETRAEDGE: More features to support Syberia 2
50d4766109 TETRAEDGE: Implement some empty lua binds for Syberia 2
ae5d052ce2 TETRAEDGE: Implement updateScroll and updateViewport for Syberia 2
ed71db3403 TETRAEDGE: Avoid compiler warning in TeParticle
eb871c93ad TETRAEDGE: Complete updateScroll for Syberia 2
ebfa1e91cc TETRAEDGE: Add Charater::Water class for Syberia 2
8e057462db TETRAEDGE: Handle invertNormals for Syberia 2 characters
3cc29bcfc5 TETRAEDGE: Clean up some TODOs
fcc44b34c0 TETRAEDGE: Fix some Coverity issues (null checks, initialization)


Commit: 550f2e276d459a0db638e18799b9671d30f85790
    https://github.com/scummvm/scummvm/commit/550f2e276d459a0db638e18799b9671d30f85790
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:11+09:00

Commit Message:
TETRAEDGE: Inventory fixes for Syberia 2

Changed paths:
    engines/tetraedge/game/inventory.cpp


diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index dd499915b73..0286f101e53 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -349,7 +349,14 @@ void Inventory::unPauseAnims() {
 	error("TODO: implement Inventory::unPauseAnims");
 }
 
-void Inventory::removeObject(const Common::String &objname) {
+void Inventory::removeObject(const Common::String &name) {
+	if (!name.size()) {
+		warning("Reqeust to remove an object with no name?");
+		return;
+	}
+
+	// Take a copy of the name to be sure as we will be deleting the object
+	const Common::String objname = name;
 	int pageNo = 0;
 	bool finished = false;
 	while (!finished) {
@@ -374,6 +381,7 @@ void Inventory::removeObject(const Common::String &objname) {
 							break;
 						}
 					}
+					slotLayout->removeChild(child);
 					delete childObj;
 					updateLayout();
 					return;
@@ -397,7 +405,32 @@ InventoryObject *Inventory::selectedInventoryObject() {
 }
 
 void Inventory::selectedObject(const Common::String &objname) {
-	error("TODO: implement Inventory::selectedObject('%s')", objname.c_str());
+	int pageNo = 0;
+	bool finished = false;
+	while (!finished) {
+		TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
+		if (!page)
+			break;
+		int slotNo = 0;
+		while (true) {
+			const Common::String slotStr = Common::String::format("page%dSlot%d", pageNo, slotNo);
+			TeLayout *slotLayout = _gui.layout(slotStr);
+			if (!slotLayout)
+				break;
+
+			for (Te3DObject2 *child : slotLayout->childList()) {
+				InventoryObject *invObj = dynamic_cast<InventoryObject *>(child);
+				if (invObj && invObj->name() == objname) {
+					selectedObject(invObj);
+					// NOTE: Original then iterates _invObjects here..
+					// why double iterate like that?
+					return;
+				}
+			}
+			slotNo++;
+		}
+		pageNo++;
+	}
 }
 
 void Inventory::selectedObject(InventoryObject *obj) {
@@ -450,8 +483,7 @@ const Common::String &Inventory::selectedObject() {
 
 bool Inventory::updateLayout() {
 	int pageNo = 0;
-	bool finished = false;
-	while (!finished) {
+	while (true) {
 		TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
 		if (!page)
 			break;
@@ -475,9 +507,13 @@ bool Inventory::updateLayout() {
 		pageNo++;
 	}
 
+	// If list is empty, we're done.
+	if (_invObjects.size() == 0)
+		return true;
+
 	pageNo = 0;
-	auto invObjIter = _invObjects.begin();
-	while (!finished) {
+	Common::List<InventoryObject *>::iterator invObjIter = _invObjects.begin();
+	while (true) {
 		TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
 		if (!page)
 			break;


Commit: 787bffdd2fd88fb06cee8b443f46e14e994bd8ad
    https://github.com/scummvm/scummvm/commit/787bffdd2fd88fb06cee8b443f46e14e994bd8ad
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:11+09:00

Commit Message:
TETRAEDGE: Update scene loading for Syberia 2

Changed paths:
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/in_game_scene_xml_parser.cpp
    engines/tetraedge/game/particle_xml_parser.cpp
    engines/tetraedge/te/te_particle.cpp
    engines/tetraedge/te/te_particle.h


diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 7a6a08b1d84..828e2e97052 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -44,7 +44,7 @@
 #include "tetraedge/te/te_lua_script.h"
 #include "tetraedge/te/te_lua_thread.h"
 
-//#define TETRAEDGE_DEBUG_PATHFINDING
+#define TETRAEDGE_DEBUG_PATHFINDING
 //#define TETRAEDGE_DEBUG_LIGHTS
 
 namespace Tetraedge {
@@ -52,6 +52,21 @@ namespace Tetraedge {
 /*static*/
 bool InGameScene::_collisionSlide = false;
 
+/*static*/
+const int InGameScene::MAX_FIRE = 50;
+const int InGameScene::MAX_SNOW = 250;
+const int InGameScene::MAX_SMOKE = 350;
+const float InGameScene::DUREE_MAX_FIRE = 32000.0;
+const float InGameScene::SCALE_FIRE = 0.1;
+const int InGameScene::MAX_FLAKE = 10;
+const float InGameScene::DUREE_MIN_FLAKE = 3000.0;
+const float InGameScene::DUREE_MAX_FLAKE = 5000.0;
+const float InGameScene::SCALE_FLAKE = 0.1;
+const float InGameScene::DEPTH_MAX_FLAKE = 0.1;
+
+
+
+
 InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr),
 _shadowLightNo(-1), _waitTime(-1.0f), _shadowColor(0, 0, 0, 0x80), _shadowFov(20.0f),
 _shadowFarPlane(1000), _shadowNearPlane(1), _maskAlpha(false) {
@@ -696,6 +711,23 @@ bool InGameScene::loadXml(const Common::String &zone, const Common::String &scen
 	if (!parser.parse())
 		error("InGameScene::loadXml: Can't parse %s", node.getPath().c_str());
 
+	// loadFlamme and loadSnowCustom are handled by the above.
+
+	_charactersShadow = CharactersShadow::makeInstance();
+	_charactersShadow->create(this);
+
+	for (uint i = 0; i < _lights.size(); i++)
+		_lights[i]->disable(i);
+	_lights.clear();
+
+	const Common::Path lightspath = getLightsFileName();
+	TeCore *core = g_engine->getCore();
+	const Common::FSNode lightsNode(core->findFile(lightspath));
+	if (lightsNode.isReadable())
+		loadLights(lightsNode);
+
+	// TODO: Should we set particle matrix to current cam matrix here?
+	// If we are loading a new scene it seems redundant..
 	Common::Path pxmlpath = _sceneFileNameBase(zone, scene).joinInPlace("particles.xml");
 	Common::FSNode pnode = g_engine->getCore()->findFile(pxmlpath);
 	if (pnode.isReadable()) {
@@ -882,8 +914,9 @@ bool InGameScene::loadCurve(const Common::String &name) {
 		warning("[InGameScene::loadCurve] Can't open file : %s.", path.toString().c_str());
 		return false;
 	}
-	TeBezierCurve *curve = new TeBezierCurve();
+	TeIntrusivePtr<TeBezierCurve> curve = new TeBezierCurve();
 	curve->loadBin(node);
+	_bezierCurves.push_back(curve);
 	return true;
 }
 
@@ -1400,6 +1433,7 @@ void InGameScene::update() {
 	}
 
 	TeScene::update();
+	// TODO: YoukiManager::update();
 
 	float waitTime = _waitTimeTimer.timeFromLastTimeElapsed();
 	if (_waitTime != -1.0 && waitTime > _waitTime) {
@@ -1419,6 +1453,14 @@ void InGameScene::update() {
 			game->luaScript().execute("OnWaitFinished");
 	}
 
+	// TODO: Update Flammes
+
+	// Original does this, but snowCustoms are never actually created?
+	//for (auto snow : _snowCustoms)
+	//	snow->addFlake();
+
+	TeParticle::updateAll(1);
+
 	for (Object3D *obj : _object3Ds) {
 		if (obj->_translateTime >= 0) {
 			float time = MIN((float)(obj->_translateTimer.getTimeFromStart() / 1000000.0), obj->_translateTime);
@@ -1462,4 +1504,18 @@ bool InGameScene::AnimObject::onFinished() {
 	return false;
 }
 
+void InGameScene::Flamme::initFire() {
+	_needsFires = true;
+	_fires.resize(MAX_FIRE);
+}
+
+InGameScene::Flamme::~Flamme() {
+	for (auto fire : _fires) {
+		if (fire) {
+			delete fire;
+		}
+	}
+	_fires.clear();
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 26897c20d1f..37d059a56a0 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -91,12 +91,35 @@ public:
 		TeVector3f32 _scale;
 	};
 
+	static const int MAX_FIRE;
+	static const int MAX_SNOW;
+	static const int MAX_SMOKE;
+	static const float DUREE_MAX_FIRE;
+	static const float SCALE_FIRE;
+	static const int MAX_FLAKE;
+	static const float DUREE_MIN_FLAKE;
+	static const float DUREE_MAX_FLAKE;
+	static const float SCALE_FLAKE;
+	static const float DEPTH_MAX_FLAKE;
+
+	struct Fire {
+		TeCurveAnim2<TeModel, TeVector3f32> _positionAnim;
+		TeCurveAnim2<TeModel, TeColor> _colorAnim;
+		TeCurveAnim2<TeModel, TeVector3f32> _scaleAnim;
+	};
+
 	struct Flamme {
+		Flamme() : _needsFires(false), _addFireOnUpdate(false) {};
+		~Flamme();
+		Common::Array<Fire*> _fires;
 		Common::String _name;
 		TeVector3f32 _center;
 		TeVector3f32 _yMax;
 		TeVector3f32 _offsetMin;
 		TeVector3f32 _offsetMax;
+		bool _needsFires;
+		bool _addFireOnUpdate;
+		void initFire();
 	};
 
 	void activateAnchorZone(const Common::String &name, bool val);
diff --git a/engines/tetraedge/game/in_game_scene_xml_parser.cpp b/engines/tetraedge/game/in_game_scene_xml_parser.cpp
index 3adc56e9f2c..78b742c4499 100644
--- a/engines/tetraedge/game/in_game_scene_xml_parser.cpp
+++ b/engines/tetraedge/game/in_game_scene_xml_parser.cpp
@@ -88,6 +88,7 @@ bool InGameSceneXmlParser::parserCallback_shadowMask(ParserNode *node) {
 }
 
 bool InGameSceneXmlParser::parserCallback_shadowReceivingObject(ParserNode *node) {
+	_scene->loadShadowReceivingObject(node->values["name"], _scene->getZoneName(), _scene->getSceneName());
 	return true;
 }
 
diff --git a/engines/tetraedge/game/particle_xml_parser.cpp b/engines/tetraedge/game/particle_xml_parser.cpp
index e3d2ac10a53..36854f6bacc 100644
--- a/engines/tetraedge/game/particle_xml_parser.cpp
+++ b/engines/tetraedge/game/particle_xml_parser.cpp
@@ -122,5 +122,4 @@ bool ParticleXmlParser::parserCallback_orientation(ParserNode *node) {
 }
 
 
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_particle.cpp b/engines/tetraedge/te/te_particle.cpp
index 58b44ec4e7d..be8904ec7b7 100644
--- a/engines/tetraedge/te/te_particle.cpp
+++ b/engines/tetraedge/te/te_particle.cpp
@@ -50,6 +50,10 @@ bool TeParticle::loadTexture(const Common::String &filename) {
 	return _texture->load(texnode);
 }
 
+void TeParticle::update(int val) {
+	// TODO: Implement me.
+}
+
 /*static*/
 Common::Array<TeParticle *> *TeParticle::_indexedParticles = nullptr;
 
@@ -74,6 +78,13 @@ TeParticle *TeParticle::getIndexedParticle(int idx) {
 	return (*parts)[idx];
 }
 
+/*static*/
+void TeParticle::updateAll(int val) {
+	Common::Array<TeParticle *> *parts = indexedParticles();
+	for (uint i = 0; i < parts->size(); i++)
+		(*parts)[i]->update(val);
+}
+
 /*static*/
 Common::Array<TeParticle *> *TeParticle::indexedParticles() {
 	if (_indexedParticles == nullptr)
diff --git a/engines/tetraedge/te/te_particle.h b/engines/tetraedge/te/te_particle.h
index 512a3e64568..69e91861480 100644
--- a/engines/tetraedge/te/te_particle.h
+++ b/engines/tetraedge/te/te_particle.h
@@ -56,10 +56,14 @@ public:
 	void setRandomDir(bool val) { _randomDir = val; }
 	void setOrientation(const TeVector3f32 &orientation) { _orientation = orientation; }
 
+	void update(int val);
+
 	static int getIndex(const Common::String &name);
 	static TeParticle *getIndexedParticle(int idx);
 	static void cleanup();
 
+	static void updateAll(int val);
+
 private:
 	Common::Array<TeIntrusivePtr<TeElement>> _elements;
 	TeScene *_scene;


Commit: a559a0c7c9904a325cac1d6782152a439ce7b600
    https://github.com/scummvm/scummvm/commit/a559a0c7c9904a325cac1d6782152a439ce7b600
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:11+09:00

Commit Message:
TETRAEDGE: Support converting image formats on load

Changed paths:
    engines/tetraedge/te/te_scummvm_codec.cpp


diff --git a/engines/tetraedge/te/te_scummvm_codec.cpp b/engines/tetraedge/te/te_scummvm_codec.cpp
index fe1a0121b24..9bac0a054fe 100644
--- a/engines/tetraedge/te/te_scummvm_codec.cpp
+++ b/engines/tetraedge/te/te_scummvm_codec.cpp
@@ -72,6 +72,11 @@ bool TeScummvmCodec::update(uint i, TeImage &imgout) {
 	if (imgout.w == _loadedSurface->w && imgout.h == _loadedSurface->h && imgout.format == _loadedSurface->format) {
 		imgout.copyFrom(*_loadedSurface);
 		return true;
+	} else if (imgout.w == _loadedSurface->w && imgout.h == _loadedSurface->h) {
+		Graphics::PixelFormat destfmt = imgout.format;
+		imgout.copyFrom(*_loadedSurface);
+		imgout.convertToInPlace(destfmt);
+		return true;
 	}
 
 	error("TODO: Implement TeScummvmCodec::update for different sizes");


Commit: 4dde2c98f4829da0d9ce9a17259e45a955a1456d
    https://github.com/scummvm/scummvm/commit/4dde2c98f4829da0d9ce9a17259e45a955a1456d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:11+09:00

Commit Message:
TETRAEDGE: More features to support Syberia 2

Changed paths:
  A engines/tetraedge/game/youki_manager.cpp
  A engines/tetraedge/game/youki_manager.h
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/object3d.cpp
    engines/tetraedge/game/object3d.h
    engines/tetraedge/module.mk
    engines/tetraedge/te/te_3d_texture_opengl.cpp
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_image.h
    engines/tetraedge/te/te_png.cpp
    engines/tetraedge/te/te_renderer.h
    engines/tetraedge/te/te_renderer_opengl.cpp
    engines/tetraedge/te/te_renderer_opengl.h
    engines/tetraedge/te/te_renderer_tinygl.cpp
    engines/tetraedge/te/te_renderer_tinygl.h


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 7edb7be1396..b2c21cb97e6 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -84,6 +84,7 @@ void Application::create() {
 
 	const int winWidth = g_engine->getDefaultScreenWidth();
 	const int winHeight = g_engine->getDefaultScreenHeight();
+
 	// See TeMainWindowBase::initCamera
 	_mainWindowCamera.reset(new TeCamera());
 	_mainWindowCamera->setName("_mainWinCam");
@@ -96,6 +97,7 @@ void Application::create() {
 	_mainWindow.setSizeType(TeILayout::ABSOLUTE);
 	_mainWindow.setPositionType(TeILayout::ABSOLUTE);
 	_mainWindow.setPosition(TeVector3f32(0.0f, 0.0f, 0.0f));
+	_mainWindow.setName("TeEngine Application");
 
 	TeResourceManager *resmgr = g_engine->getResourceManager();
 	TeCore *core = g_engine->getCore();
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 95045ff665f..a4f1bc3b334 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -70,7 +70,7 @@ _recallageY(true), _walkToFlag(false), _walkCurveEnd(0.0f), _walkCurveLast(0.0f)
 _walkCurveLen(0.0f), _walkCurveIncrement(0.0f), _walkEndAnimG(false), _walkTotalFrames(0),
 _walkCurveNextLength(0.0f), _walkedLength(0.0f), _walkLoopAnimLen(0.0f), _walkEndGAnimLen(0.0f),
 _walkStartAnimLen(0.0f), _walkStartAnimFrameCount(0), _walkLoopAnimFrameCount(0),
-_walkEndGAnimFrameCount(0), _hasAnchor(false) {
+_walkEndGAnimFrameCount(0), _hasAnchor(false), _charLookingAtFloat(0.0f) {
 	_curModelAnim.setDeleteFn(&TeModelAnimation::deleteLaterStatic);
 }
 
@@ -210,7 +210,7 @@ float Character::animLengthFromFile(const Common::String &animname, uint32 *pfra
 	}
 
 	// The "Pere" or "father" bone is the root.
-	float animLen = animLength(*anim, anim->findBone("Pere"), lastframe);
+	float animLen = animLength(*anim, anim->findBone(rootBone()), lastframe);
 	int frameCount = anim->lastFrame() + 1 - anim->firstFrame();
 	*pframeCount = frameCount;
 
@@ -462,7 +462,7 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 		return false;
 
 	Game *game = g_engine->getGame();
-	if (boneName == "Pere") {
+	if (boneName == rootBone()) {
 		const Common::String animfile = _model->anim()->loadedPath().getLastComponent().toString();
 		bool resetX = false;
 		if (game->scene()._character == this) {
@@ -593,7 +593,7 @@ bool Character::onModelAnimationFinished() {
 	}
 
 	if (!isWalkAnim && shouldAdjust) {
-		int pereBone = _curModelAnim->findBone("Pere");
+		int pereBone = _curModelAnim->findBone(rootBone());
 		const TeTRS endTRS = trsFromAnim(*_curModelAnim, pereBone, _curModelAnim->lastFrame());
 		TeVector3f32 trans = endTRS.getTranslation();
 		trans.x() = -trans.x();
@@ -697,6 +697,13 @@ void Character::removeFromCurve() {
 	_curve.release();
 }
 
+Common::String Character::rootBone() const {
+	if (g_engine->gameType() != TetraedgeEngine::kSyberia2 || _model->name() != "Youki")
+		return "Pere";
+	else
+		return "Bip01";
+}
+
 bool Character::setAnimation(const Common::String &aname, bool repeat, bool returnToIdle, bool unused, int startFrame, int endFrame) {
 	if (aname.empty())
 		return false;
@@ -770,7 +777,7 @@ float Character::speedFromAnim(double msFromStart) {
 	if (!modelAnim)
 		return 0.0f;
 
-	const int pereBone = modelAnim->findBone("Pere");
+	const int pereBone = modelAnim->findBone(rootBone());
 	int curFrame = modelAnim->calcCurrentFrame(msFromStart);
 
 	float result;
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index a5065199677..5ab2b71ba30 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -127,7 +127,7 @@ public:
 	//void play() // just called TeAnimation::play();
 	void removeAnim();
 	void removeFromCurve();
-	static Common::String rootBone() { return "Pere"; }
+	Common::String rootBone() const;
 
 	bool setAnimation(const Common::String &name, bool repeat, bool returnToIdle = false, bool unused = false, int startFrame = -1, int endFrame = 9999);
 	void setAnimationSound(const Common::String &name, uint offset);
@@ -161,6 +161,7 @@ public:
 	bool needsSomeUpdate() const { return _needsSomeUpdate; }
 	void setNeedsSomeUpdate(bool val) { _needsSomeUpdate = val; }
 	void setCharLookingAt(Character *other) { _charLookingAt = other; }
+	void setCharLookingAtFloat(float f) { _charLookingAtFloat = f; }
 	const TeVector3f32 &positionCharacter() const { return _positionCharacter; }
 	void setPositionCharacter(const TeVector3f32 &val) { _positionCharacter = val; }
 	bool positionFlag() const { return _positionFlag; }
@@ -202,6 +203,7 @@ private:
 	Common::String _animSound;
 
 	Character *_charLookingAt;
+	float _charLookingAtFloat; // TODO: what is this?
 
 	uint _animSoundOffset;
 
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 828e2e97052..d50451d2817 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -44,7 +44,7 @@
 #include "tetraedge/te/te_lua_script.h"
 #include "tetraedge/te/te_lua_thread.h"
 
-#define TETRAEDGE_DEBUG_PATHFINDING
+//#define TETRAEDGE_DEBUG_PATHFINDING
 //#define TETRAEDGE_DEBUG_LIGHTS
 
 namespace Tetraedge {
@@ -65,8 +65,6 @@ const float InGameScene::SCALE_FLAKE = 0.1;
 const float InGameScene::DEPTH_MAX_FLAKE = 0.1;
 
 
-
-
 InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr),
 _shadowLightNo(-1), _waitTime(-1.0f), _shadowColor(0, 0, 0, 0x80), _shadowFov(20.0f),
 _shadowFarPlane(1000), _shadowNearPlane(1), _maskAlpha(false) {
@@ -366,13 +364,14 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 }
 
 void InGameScene::draw() {
-	TeScene::draw();
-
 	if (currentCameraIndex() >= (int)cameras().size())
 		return;
 
 	currentCamera()->apply();
 
+	drawMask();
+	drawReflection();
+
 #ifdef TETRAEDGE_DEBUG_PATHFINDING
 	if (_character && _character->curve()) {
 		_character->curve()->setVisible(true);
@@ -395,6 +394,41 @@ void InGameScene::draw() {
 		_lights[i]->update(i);
 
 	TeCamera::restore();
+
+	drawKate();
+
+	TeScene::draw();
+}
+
+void InGameScene::drawKate() {
+	if (_rippleMasks.size())
+		error("TODO: Implement InGameScene::drawKate");
+}
+
+void InGameScene::drawMask() {
+	if (_masks.empty())
+		return;
+
+	TeIntrusivePtr<TeCamera> cam = currentCamera();
+	if (!cam)
+		return;
+
+	cam->apply();
+
+	TeRenderer *rend = g_engine->getRenderer();
+	if (!_maskAlpha)
+		rend->colorMask(false, false, false, false);
+
+	for (auto mask : _masks)
+		mask->draw();
+
+	if (!_maskAlpha)
+		rend->colorMask(true, true, true, true);
+}
+
+void InGameScene::drawReflection() {
+	if (_rippleMasks.size())
+		error("TODO: Implement InGameScene::drawReflection");
 }
 
 void InGameScene::drawPath() {
@@ -452,7 +486,8 @@ InGameScene::SoundStep InGameScene::findSoundStep(const Common::String &name) {
 
 void InGameScene::freeGeometry() {
 	_loadedPath.set("");
-
+	_youkiManager.reset();
+	freeSceneObjects();
 	for (TeFreeMoveZone *zone : _freeMoveZones)
 		delete zone;
 	_freeMoveZones.clear();
@@ -461,6 +496,8 @@ void InGameScene::freeGeometry() {
 	cameras().clear();
 	_zoneModels.clear();
 	_masks.clear();
+	_shadowReceivingObjects.clear();
+	// TODO: _sceneLights.clear();
 	if (_charactersShadow) {
 		delete _charactersShadow;
 		_charactersShadow = nullptr;
@@ -496,9 +533,18 @@ void InGameScene::freeSceneObjects() {
 	}
 	_sprites.clear();
 
+	// TODO: Clean up snows, waterCones, smokes, snowCones
+
 	deleteAllCallback();
 	_markers.clear();
 
+	// TODO: Clean up randomAnims
+
+	for (RippleMask *rmask : _rippleMasks) {
+		delete rmask;
+	}
+	_rippleMasks.clear();
+
 	for (InGameScene::AnchorZone *zone : _anchorZones) {
 		delete zone;
 	}
@@ -719,6 +765,7 @@ bool InGameScene::loadXml(const Common::String &zone, const Common::String &scen
 	for (uint i = 0; i < _lights.size(); i++)
 		_lights[i]->disable(i);
 	_lights.clear();
+	_shadowLightNo = -1;
 
 	const Common::Path lightspath = getLightsFileName();
 	TeCore *core = g_engine->getCore();
@@ -948,16 +995,16 @@ bool InGameScene::loadDynamicLightBloc(const Common::String &name, const Common:
 	TeMesh *mesh = model->meshes()[0].get();
 	mesh->setConf(verts, tricount * 3, TeMesh::MeshMode_Triangles, 0, 0);
 
-	TeVector3f32 vec;
-	TeVector2f32 vec2;
 	for (uint i = 0; i < verts; i++) {
+		TeVector3f32 vec;
 		TeVector3f32::deserialize(file, vec);
 		mesh->setVertex(i, vec);
 		mesh->setNormal(i, TeVector3f32(0, 0, 1));
 	}
 	for (uint i = 0; i < verts; i++) {
+		TeVector2f32 vec2;
 		TeVector2f32::deserialize(file, vec2);
-		vec.y() = 1.0 - vec.y();
+		vec2.setY(1.0 - vec2.getY());
 		mesh->setTextureUV(i, vec2);
 	}
 
@@ -1012,19 +1059,20 @@ bool InGameScene::loadMask(const Common::String &name, const Common::String &tex
 	TeMesh *mesh = model->meshes()[0].get();
 	mesh->setConf(verts, tricount * 3, TeMesh::MeshMode_Triangles, 0, 0);
 
-	TeVector3f32 vec;
-	TeVector2f32 vec2;
 	for (uint i = 0; i < verts; i++) {
+		TeVector3f32 vec;
 		TeVector3f32::deserialize(file, vec);
 		mesh->setVertex(i, vec);
 		mesh->setNormal(i, TeVector3f32(0, 0, 1));
 		if (_maskAlpha) {
-			mesh->setColor(TeColor(255, 255, 255, 128));
+			mesh->setColor(i, TeColor(255, 255, 255, 128));
 		}
 	}
+
 	for (uint i = 0; i < verts; i++) {
+		TeVector2f32 vec2;
 		TeVector2f32::deserialize(file, vec2);
-		vec.y() = 1.0 - vec.y();
+		vec2.setY(1.0 - vec2.getY());
 		mesh->setTextureUV(i, vec2);
 	}
 
@@ -1037,16 +1085,20 @@ bool InGameScene::loadMask(const Common::String &name, const Common::String &tex
 
 	file.close();
 	Common::FSNode texnode = core->findFile(texpath);
-	TeIntrusivePtr<Te3DTexture> tex = Te3DTexture::makeInstance();
-	tex->load2(texnode, !_maskAlpha);
-	mesh->defaultMaterial(tex);
+	TeIntrusivePtr<Te3DTexture> tex = Te3DTexture::load2(texnode, !_maskAlpha);
 
-	if (!_maskAlpha) {
-		mesh->materials()[0]._mode = TeMaterial::MaterialMode2;
-	}
+	if (tex) {
+		mesh->defaultMaterial(tex);
+		if (!_maskAlpha) {
+			mesh->materials()[0]._mode = TeMaterial::MaterialMode2;
+		}
 
-	_masks.push_back(model);
-	return true;
+		_masks.push_back(model);
+		return true;
+	} else {
+		warning("Failed to load mask texture %s", texture.c_str());
+		return false;
+	}
 }
 
 bool InGameScene::loadRBB(const Common::String &fname, const Common::String &zone, const Common::String &scene) {
@@ -1069,8 +1121,46 @@ bool InGameScene::loadShadowMask(const Common::String &name, const Common::Strin
 	return true;
 }
 
-bool InGameScene::loadShadowReceivingObject(const Common::String &fname, const Common::String &zone, const Common::String &scene) {
-	warning("TODO: Implement InGameScene::loadShadowReceivingObject");
+bool InGameScene::loadShadowReceivingObject(const Common::String &name, const Common::String &zone, const Common::String &scene) {
+	Common::Path datpath = _sceneFileNameBase(zone, scene).joinInPlace(name).appendInPlace(".bin");
+	Common::FSNode datnode = g_engine->getCore()->findFile(datpath);
+	if (!datnode.isReadable()) {
+		warning("[InGameScene::loadShadowReceivingObject] Can't open file : %s.", datpath.toString().c_str());
+		return false;
+	}
+	TeModel *model = new TeModel();
+	model->setMeshCount(1);
+	model->setName(name);
+
+	Common::File file;
+	file.open(datnode);
+
+	// Load position, rotation, size.
+	Te3DObject2::deserialize(file, *model, false);
+
+	uint32 verts = file.readUint32LE();
+	uint32 tricount = file.readUint32LE();
+	if (verts > 100000 || tricount > 10000)
+		error("Improbable number of verts (%d) or triangles (%d)", verts, tricount);
+
+	TeMesh *mesh = model->meshes()[0].get();
+	mesh->setConf(verts, tricount * 3, TeMesh::MeshMode_Triangles, 0, 0);
+
+	for (uint i = 0; i < verts; i++) {
+		TeVector3f32 vec;
+		TeVector3f32::deserialize(file, vec);
+		mesh->setVertex(i, vec);
+		mesh->setNormal(i, TeVector3f32(0, 0, 1));
+	}
+
+	// Indexes in reverse order :(
+	for (uint i = 0; i < tricount * 3; i += 3) {
+		mesh->setIndex(i + 2, file.readUint16LE());
+		mesh->setIndex(i + 1, file.readUint16LE());
+		mesh->setIndex(i, file.readUint16LE());
+	}
+
+	_shadowReceivingObjects.push_back(model);
 	return true;
 }
 
@@ -1135,6 +1225,7 @@ void InGameScene::loadBlockers() {
 }
 
 void InGameScene::loadBackground(const Common::FSNode &node) {
+	_youkiManager.reset();
 	_bgGui.load(node);
 	TeLayout *bg = _bgGui.layout("background");
 	TeLayout *root = _bgGui.layout("root");
@@ -1232,6 +1323,9 @@ TeFreeMoveZone *InGameScene::pathZone(const Common::String &name) {
 }
 
 void InGameScene::reset() {
+	for (auto *character : _characters)
+		character->setFreeMoveZone(nullptr);
+	_youkiManager.reset();
 	if (_character)
 		_character->setFreeMoveZone(nullptr);
 	freeSceneObjects();
@@ -1433,7 +1527,7 @@ void InGameScene::update() {
 	}
 
 	TeScene::update();
-	// TODO: YoukiManager::update();
+	_youkiManager.update();
 
 	float waitTime = _waitTimeTimer.timeFromLastTimeElapsed();
 	if (_waitTime != -1.0 && waitTime > _waitTime) {
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 37d059a56a0..95c20258fe0 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -28,6 +28,7 @@
 
 #include "tetraedge/game/object3d.h"
 #include "tetraedge/game/billboard.h"
+#include "tetraedge/game/youki_manager.h"
 
 #include "tetraedge/te/te_act_zone.h"
 #include "tetraedge/te/te_bezier_curve.h"
@@ -122,6 +123,11 @@ public:
 		void initFire();
 	};
 
+	// TODO: 
+	struct RippleMask {
+
+	};
+
 	void activateAnchorZone(const Common::String &name, bool val);
 	void addAnchorZone(const Common::String &s1, const Common::String &name, float radius);
 	void addBlockingObject(const Common::String &obj) {
@@ -148,6 +154,9 @@ public:
 	void deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam);
 	void deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<TeModel> &model, TePickMesh2 *pickmesh);
 	virtual void draw() override;
+	void drawKate();
+	void drawMask();
+	void drawReflection();
 	void drawPath();
 	Dummy dummy(const Common::String &name);
 	bool findKate();
@@ -260,6 +269,7 @@ public:
 
 	void setCollisionSlide(bool val) { _collisionSlide = val; }
 	void activateMask(const Common::String &name, bool val);
+	YoukiManager &youkiManager() { return _youkiManager; }
 
 private:
 	int _shadowLightNo;
@@ -283,6 +293,7 @@ private:
 	Common::Array<Billboard *> _billboards;
 	Common::Array<TeSpriteLayout *> _sprites;
 	Common::Array<TePickMesh2 *> _clickMeshes;
+	Common::Array<RippleMask *> _rippleMasks;
 
 	Common::HashMap<Common::String, SoundStep> _soundSteps;
 	Common::HashMap<Common::String, Common::Array<Callback*>> _callbacks;
@@ -295,6 +306,7 @@ private:
 	Common::Array<TeIntrusivePtr<TeModel>> _zoneModels;
 	Common::Array<TeIntrusivePtr<TeModel>> _masks;
 	Common::Array<TeIntrusivePtr<TeParticle>> _particles;
+	Common::Array<TeIntrusivePtr<TeModel>> _shadowReceivingObjects;
 
 	TeIntrusivePtr<TeModel> _playerCharacterModel;
 	TeIntrusivePtr<TeBezierCurve> _curve;
@@ -315,6 +327,7 @@ private:
 	Common::String _sceneName;
 	Common::String _zoneName;
 	bool _maskAlpha;
+	YoukiManager _youkiManager;
 
 };
 
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 3dcb45b095e..bd2c9ad65c9 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -1666,8 +1666,11 @@ static void SetCharacterLookChar(const Common::String &charname, const Common::S
 		return;
 	}
 	character->setLookingAtTallThing(tall);
+
 	if (f != 0.0)
 		warning("TODO: Use float param %f in SetCharacterLookChar", f);
+	character->setCharLookingAtFloat(f);
+
 	if (destname.empty()) {
 		character->setCharLookingAt(nullptr);
 	} else {
@@ -2372,7 +2375,12 @@ static int tolua_ExportedFunctions_AddUnlockedAnim00(lua_State *L) {
 }
 
 static void SetObjectMoveDest(const Common::String &obj, float x, float y, float z) {
-	warning("TODO: SetObjectMoveDest(%s, %f, %f, %f)", obj.c_str(), x, y, z);
+	Object3D *obj3d = g_engine->getGame()->scene().object3D(obj);
+	if (obj3d) {
+		obj3d->setObjectMoveDest(TeVector3f32(x, y, z));
+	} else {
+		warning("[SetObjectMoveDest] Object not found %s", obj.c_str());
+	}
 }
 
 static int tolua_ExportedFunctions_SetObjectMoveDest00(lua_State *L) {
@@ -2391,7 +2399,12 @@ static int tolua_ExportedFunctions_SetObjectMoveDest00(lua_State *L) {
 }
 
 static void SetObjectMoveTime(const Common::String &obj, float f) {
-	warning("TODO: SetObjectMoveTime(%s, %f)", obj.c_str(), f);
+	Object3D *obj3d = g_engine->getGame()->scene().object3D(obj);
+	if (obj3d) {
+		obj3d->setObjectMoveTime(f);
+	} else {
+		warning("[SetObjectMoveTime] Object not found %s", obj.c_str());
+	}
 }
 
 static int tolua_ExportedFunctions_SetObjectMoveTime00(lua_State *L) {
@@ -2423,6 +2436,52 @@ static int tolua_ExportedFunctions_ActivateMask00(lua_State *L) {
 	error("#ferror in function 'ActivateMask': %d %d %s", err.index, err.array, err.type);
 }
 
+static void SetYoukiFollowKate(bool val) {
+	g_engine->getGame()->scene().youkiManager().setFollowKate(val);
+}
+
+static int tolua_ExportedFunctions_SetYoukiFollowKate00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isboolean(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		bool b1 = tolua_toboolean(L, 1, 0.0);
+		SetYoukiFollowKate(b1);
+		return 0;
+	}
+	error("#ferror in function 'SetYoukiFollowKate': %d %d %s", err.index, err.array, err.type);
+}
+
+static void AddRandomAnimation(const Common::String &character, const Common::String &anim, float f) {
+	// This exists in the game, but does nothing.
+}
+
+static int tolua_ExportedFunctions_AddRandomAnimation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) &&
+		tolua_isboolean(L, 3, 0, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		bool b1 = tolua_toboolean(L, 3, 0.0);
+		AddRandomAnimation(s1, s2, b1);
+		return 0;
+	}
+	error("#ferror in function 'AddRandomAnimation': %d %d %s", err.index, err.array, err.type);
+}
+
+static void PlayRandomAnimation(const Common::String &character) {
+	// This exists in the game, but does nothing.
+}
+
+static int tolua_ExportedFunctions_PlayRandomAnimation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		PlayRandomAnimation(s1);
+		return 0;
+	}
+	error("#ferror in function 'PlayRandomAnimation': %d %d %s", err.index, err.array, err.type);
+}
+
+
 // Not your imagination, the implementation of these two is quite different to the others.
 static int tolua_GetParticleIndex(lua_State *L) {
 	Common::String s1(tolua_tostring(L, 1, nullptr));
@@ -2445,7 +2504,6 @@ static int tolua_EnableParticle(lua_State *L) {
 	return 0;
 }
 
-
 // ////////////////////////////////////////////////////////////////////////
 
 
@@ -2608,6 +2666,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetObjectMoveDest", tolua_ExportedFunctions_SetObjectMoveDest00);
 	tolua_function(L, "SetObjectMoveTime", tolua_ExportedFunctions_SetObjectMoveTime00);
 	tolua_function(L, "ActivateMask", tolua_ExportedFunctions_ActivateMask00);
+	tolua_function(L, "SetYoukiFollowKate", tolua_ExportedFunctions_SetYoukiFollowKate00);
+	tolua_function(L, "AddRandomAnimation", tolua_ExportedFunctions_AddRandomAnimation00);
+	tolua_function(L, "PlayRandomAnimation", tolua_ExportedFunctions_PlayRandomAnimation00);
 	tolua_function(L, "GetParticleIndex", tolua_GetParticleIndex);
 	tolua_function(L, "EnableParticle", tolua_EnableParticle);
 
@@ -2616,11 +2677,8 @@ void LuaOpenBinds(lua_State *L) {
 	//tolua_function(L, "PlaySnowCustom", tolua_ExportedFunctions_PlaySnowCustom00);
 	//tolua_function(L, "SnowCustomVisible", tolua_ExportedFunctions_SnowCustomVisible00);
 	//tolua_function(L, "RemoveRandomSound", tolua_ExportedFunctions_RemoveRandomSound00);
-	//tolua_function(L, "SetYoukiFollowKate", tolua_ExportedFunctions_SetYoukiFollowKate00);
 	//tolua_function(L, "PlaySmoke", tolua_ExportedFunctions_PlaySmoke00);
 	//tolua_function(L, "SmokeVisible", tolua_ExportedFunctions_SmokeVisible00);
-	//tolua_function(L, "AddRandomAnimation", tolua_ExportedFunctions_AddRandomAnimation00);
-	//tolua_function(L, "PlayRandomAnimation", tolua_ExportedFunctions_PlayRandomAnimation00);
 	//tolua_function(L, "PlayVerticalScrolling", tolua_ExportedFunctions_PlayVerticalScrolling00);
 
 	tolua_endmodule(L);
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index 95ba54dbf17..cd0bca59362 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -20,9 +20,14 @@
  */
 
 #include "common/textconsole.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/game.h"
 #include "tetraedge/game/object3d.h"
 #include "tetraedge/game/object_settings_xml_parser.h"
 
+#include "tetraedge/te/te_lua_script.h"
+
 namespace Tetraedge {
 
 /*static*/
@@ -59,6 +64,33 @@ bool Object3D::loadModel(const Common::String &name) {
 	return false;
 }
 
+void Object3D::setObjectMoveDest(const TeVector3f32 &vec) {
+	_moveAnim._startVal = TeVector3f32();
+	_moveAnim._endVal = vec;
+}
+
+void Object3D::setObjectMoveTime(float time) {
+	_moveAnim._duration = time * 1000;
+	_moveAnim._callbackObj = this;
+	Common::Array<float> curve;
+	curve.push_back(0.0f);
+	curve.push_back(1.0f);
+	_moveAnim.setCurve(curve);
+	_moveAnim.onFinished().remove(this, &Object3D::onMoveAnimFinished);
+	_moveAnim.onFinished().add(this, &Object3D::onMoveAnimFinished);
+	_moveAnim.play();
+}
+
+bool Object3D::onMoveAnimFinished() {
+	g_engine->getGame()->luaScript().execute("OnObjectMoveFinished", _modelPtr->name());
+	_moveAnim.onFinished().remove(this, &Object3D::onMoveAnimFinished);
+	return false;
+}
+
+void Object3D::setCurMovePos(const TeVector3f32 &vec) {
+	_curMovePos = vec;
+}
+
 /*static*/
 bool Object3D::loadSettings(const Common::String &path) {
 	ObjectSettingsXmlParser parser;
diff --git a/engines/tetraedge/game/object3d.h b/engines/tetraedge/game/object3d.h
index c3bff423aef..4963ce802ab 100644
--- a/engines/tetraedge/game/object3d.h
+++ b/engines/tetraedge/game/object3d.h
@@ -25,6 +25,7 @@
 #include "common/str.h"
 #include "common/hashmap.h"
 
+#include "tetraedge/te/te_curve_anim2.h"
 #include "tetraedge/te/te_object.h"
 #include "tetraedge/te/te_model.h"
 #include "tetraedge/te/te_vector3f32.h"
@@ -52,6 +53,11 @@ public:
 
 	TeIntrusivePtr<TeModel> model() { return _modelPtr; }
 
+	void setObjectMoveDest(const TeVector3f32 &vec);
+	void setObjectMoveTime(float f);
+	bool onMoveAnimFinished();
+	void setCurMovePos(const TeVector3f32 &pos);
+
 	float _rotateTime;
 	TeTimer _rotateTimer;
 	TeQuaternion _rotateStart;
@@ -62,6 +68,9 @@ public:
 	TeVector3f32 _translateStart;
 	TeVector3f32 _translateAmount;
 
+	TeCurveAnim2<Object3D,TeVector3f32> _moveAnim;
+	TeVector3f32 _curMovePos;
+
 	Common::String _onCharName;
 	Common::String _onCharBone;
 
diff --git a/engines/tetraedge/game/youki_manager.cpp b/engines/tetraedge/game/youki_manager.cpp
new file mode 100644
index 00000000000..8db1c4fbbfd
--- /dev/null
+++ b/engines/tetraedge/game/youki_manager.cpp
@@ -0,0 +1,48 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/youki_manager.h"
+
+namespace Tetraedge {
+
+YoukiManager::YoukiManager() : _followKate(false) {
+}
+
+void YoukiManager::reset() {
+	_followKate = false;
+}
+
+void YoukiManager::update() {
+	if (g_engine->gameType() != TetraedgeEngine::kSyberia2)
+		return;
+	// TODO: Implement me.
+}
+
+bool YoukiManager::onAnimFinished(const Common::String &anim) {
+	return false;
+}
+
+bool YoukiManager::onMoveFinished() {
+	return false;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/youki_manager.h b/engines/tetraedge/game/youki_manager.h
new file mode 100644
index 00000000000..65c53a2572b
--- /dev/null
+++ b/engines/tetraedge/game/youki_manager.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef TETRAEDGE_GAME_YOUKI_MANAGER_H
+#define TETRAEDGE_GAME_YOUKI_MANAGER_H
+
+#include "common/str.h"
+#include "tetraedge/te/te_timer.h"
+
+
+namespace Tetraedge {
+
+class YoukiManager {
+public:
+	YoukiManager();
+
+	void setFollowKate(bool val) { _followKate = val; }
+	void reset();
+	void update();
+
+private:
+	bool onAnimFinished(const Common::String &anim);
+	bool onMoveFinished();
+
+	TeTimer _timer;
+	bool _followKate;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_YOUKI_MANAGER_H
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index b600b0fedcf..3b0a957f65c 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -41,6 +41,7 @@ MODULE_OBJS := \
 	game/question2.o \
 	game/scene_lights_xml_parser.o \
 	game/splash_screens.o \
+	game/youki_manager.o \
 	te/micropather.o \
 	te/te_3d_object2.o \
 	te/te_3d_texture.o \
diff --git a/engines/tetraedge/te/te_3d_texture_opengl.cpp b/engines/tetraedge/te/te_3d_texture_opengl.cpp
index 9f48205b4a4..890a603e876 100644
--- a/engines/tetraedge/te/te_3d_texture_opengl.cpp
+++ b/engines/tetraedge/te/te_3d_texture_opengl.cpp
@@ -139,12 +139,28 @@ bool Te3DTextureOpenGL::load(const TeImage &img) {
 
 	const void *imgdata = img.getPixels();
 	if (_format == TeImage::RGB8) {
-		GLenum destfmt = _alphaOnly ? GL_ALPHA : GL_RGB8;
-		glTexImage2D(GL_TEXTURE_2D, 0, destfmt, _texWidth, _texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+		if (_alphaOnly)
+			warning("Te3DTexture::load can't load RGB as alpha-only");
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, _texWidth, _texHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
 		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.pitch / 3, img.h, GL_RGB, GL_UNSIGNED_BYTE, imgdata);
 	} else if (_format == TeImage::RGBA8) {
-		GLenum destfmt = _alphaOnly ? GL_ALPHA : GL_RGBA8;
-		glTexImage2D(GL_TEXTURE_2D, 0, destfmt, _texWidth, _texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+		Graphics::Surface surf;
+		if (_alphaOnly) {
+			surf.copyFrom(img);
+			// Slight hack: Move R data to A channel.  Our image reader
+			// only reads data as RGB, so use red for alpha-only values.
+			uint32 *p = (uint32 *)surf.getPixels();
+			for (int y = 0; y < img.h; y++) {
+				for (int x = 0; x < img.w; x++) {
+					byte a, r, g, b;
+					img.format.colorToARGB(p[x], a, r, g ,b);
+					p[x] = img.format.ARGBToColor(r, r, r, r);
+				}
+				p += img.pitch / 4;
+			}
+			imgdata = surf.getPixels();
+		}
+		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _texWidth, _texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.w, img.h, GL_RGBA, GL_UNSIGNED_BYTE, imgdata);
 		// FIXME: Slight hack.. sometimes artifacts appear because we draw
 		// a (half?)pixel outside the original texture. Clear one more row
@@ -155,6 +171,8 @@ bool Te3DTextureOpenGL::load(const TeImage &img) {
 			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, img.h, img.w, 1, GL_RGBA, GL_UNSIGNED_BYTE, buf);
 			delete [] buf;
 		}
+		if (_alphaOnly)
+			surf.free();
 	} else {
 		warning("Te3DTexture::load can't send image format %d to GL.", _format);
 	}
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index 3b7e3c75eb3..2aa8d6b1ebf 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -115,7 +115,7 @@ bool TeImage::load(Common::ReadStream &stream, const Common::Path &path) {
 	error("TODO: Implement TeImage::load");
 }
 
-bool TeImage::save(const Common::Path &path, enum Type type) {
+bool TeImage::save(const Common::Path &path, enum SaveType type) {
 	error("TODO: Implement TeImage::save");
 }
 
diff --git a/engines/tetraedge/te/te_image.h b/engines/tetraedge/te/te_image.h
index 4417169e525..46aa02fa05d 100644
--- a/engines/tetraedge/te/te_image.h
+++ b/engines/tetraedge/te/te_image.h
@@ -48,16 +48,17 @@ public:
 	enum Format {
 		RGB8 = 5,
 		RGBA8 = 6,
+		// GREY8 = 0xd,
 		INVALID = 0xe
 	};
-	enum Type {
-		PNG,
+	enum SaveType {
+		PNG
 	};
 
 	void copy(TeImage &dest, const TeVector2s32 &vec1, const TeVector2s32 &vec2,
 			  const TeVector2s32 &vec3) const;
 	uint64 countPixelsOfColor(const TeColor &col) const;
-	//void create(); // never used?
+	// void create(); // never used?
 	void createImg(uint xsize, uint ysize, Common::SharedPtr<TePalette> &palette, Format newformat) {
 		createImg(xsize, ysize, palette, newformat, xsize, ysize);
 	}
@@ -75,7 +76,7 @@ public:
 	bool isExtensionSupported(const Common::Path &path);
 	bool load(const Common::FSNode &node);
 	bool load(Common::ReadStream &stream, const Common::Path &path);
-	bool save(const Common::Path &path, enum Type type);
+	bool save(const Common::Path &path, enum SaveType type);
 	int serialize(Common::WriteStream &stream);
 	TeVector2s32 bufSize() const {
 		return TeVector2s32(pitch / format.bytesPerPixel, h);
diff --git a/engines/tetraedge/te/te_png.cpp b/engines/tetraedge/te/te_png.cpp
index 613200e16e7..d1332a18a81 100644
--- a/engines/tetraedge/te/te_png.cpp
+++ b/engines/tetraedge/te/te_png.cpp
@@ -47,11 +47,7 @@ bool TePng::load(Common::SeekableReadStream &stream) {
 	if (!png.loadStream(stream))
 		return false;
 
-	//if (png.getTransparentColor() == -1) {
-	//	_loadedSurface = png.getSurface()->convertTo(Graphics::createPixelFormat<888>());
-	//} else {
-		_loadedSurface = png.getSurface()->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
-	//}
+	_loadedSurface = png.getSurface()->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
 	return true;
 }
 
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index edb46d14c5d..a6a93cd0203 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -83,6 +83,7 @@ public:
 	void addTransparentMesh(const TeMesh &mesh, uint i1, uint i2, uint i3);
 	void checkError(const Common::String &str) {};
 	virtual void clearBuffer(Buffer buf) = 0;
+	virtual void colorMask(bool r, bool g, bool b, bool a) = 0;
 	void create();
 	TeMatrix4x4 currentMatrix();
 	virtual void disableAllLights() = 0;
diff --git a/engines/tetraedge/te/te_renderer_opengl.cpp b/engines/tetraedge/te/te_renderer_opengl.cpp
index 4a14b3dc2f2..c21b0fb4162 100644
--- a/engines/tetraedge/te/te_renderer_opengl.cpp
+++ b/engines/tetraedge/te/te_renderer_opengl.cpp
@@ -48,6 +48,10 @@ void TeRendererOpenGL::clearBuffer(TeRenderer::Buffer buf) {
 	glClear(glBuf);
 }
 
+void TeRendererOpenGL::colorMask(bool r, bool g, bool b, bool a) {
+	glColorMask(r, g, b, a);
+}
+
 void TeRendererOpenGL::disableAllLights() {
 	TeLightOpenGL::disableAll();
 }
diff --git a/engines/tetraedge/te/te_renderer_opengl.h b/engines/tetraedge/te/te_renderer_opengl.h
index 128bce82663..3f0996a969d 100644
--- a/engines/tetraedge/te/te_renderer_opengl.h
+++ b/engines/tetraedge/te/te_renderer_opengl.h
@@ -32,6 +32,7 @@ class TeRendererOpenGL : public TeRenderer {
 public:
 	TeRendererOpenGL();
 	void clearBuffer(TeRenderer::Buffer buf) override;
+	void colorMask(bool r, bool g, bool b, bool a) override;
 	void disableAllLights() override;
 	void disableTexture() override;
 	void disableWireFrame() override;
diff --git a/engines/tetraedge/te/te_renderer_tinygl.cpp b/engines/tetraedge/te/te_renderer_tinygl.cpp
index a82b25a1055..68cb085638a 100644
--- a/engines/tetraedge/te/te_renderer_tinygl.cpp
+++ b/engines/tetraedge/te/te_renderer_tinygl.cpp
@@ -49,6 +49,10 @@ void TeRendererTinyGL::clearBuffer(TeRenderer::Buffer buf) {
 	tglClear(glBuf);
 }
 
+void TeRendererTinyGL::colorMask(bool r, bool g, bool b, bool a) {
+	tglColorMask(r, g, b, a);
+}
+
 void TeRendererTinyGL::disableAllLights() {
 	TeLightTinyGL::disableAll();
 }
diff --git a/engines/tetraedge/te/te_renderer_tinygl.h b/engines/tetraedge/te/te_renderer_tinygl.h
index 209591e5224..3d424801e4d 100644
--- a/engines/tetraedge/te/te_renderer_tinygl.h
+++ b/engines/tetraedge/te/te_renderer_tinygl.h
@@ -33,6 +33,7 @@ public:
 	TeRendererTinyGL();
 
 	void clearBuffer(TeRenderer::Buffer buf) override;
+	void colorMask(bool r, bool g, bool b, bool a) override;
 	void disableAllLights() override;
 	void disableTexture() override;
 	void disableWireFrame() override;


Commit: 50d476610960db50d4b018c74a582953a106b4a7
    https://github.com/scummvm/scummvm/commit/50d476610960db50d4b018c74a582953a106b4a7
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:11+09:00

Commit Message:
TETRAEDGE: Implement some empty lua binds for Syberia 2

Changed paths:
    engines/tetraedge/game/lua_binds.cpp


diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index bd2c9ad65c9..dcfa39ab0f1 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -2481,6 +2481,35 @@ static int tolua_ExportedFunctions_PlayRandomAnimation00(lua_State *L) {
 	error("#ferror in function 'PlayRandomAnimation': %d %d %s", err.index, err.array, err.type);
 }
 
+static int tolua_ExportedFunctions_PlaySmoke00(lua_State *L) {
+	// This exists in the game, but does nothing.
+	return 0;
+}
+
+static int tolua_ExportedFunctions_SmokeVisible00(lua_State *L) {
+	// This exists in the game, but does nothing.
+	return 0;
+}
+
+static int tolua_ExportedFunctions_PlaySnow00(lua_State *L) {
+	// This exists in the game, but does nothing.
+	return 0;
+}
+
+static int tolua_ExportedFunctions_PlaySnowCustom00(lua_State *L) {
+	// This exists in the game, but does nothing.
+	return 0;
+}
+
+static int tolua_ExportedFunctions_SnowCustomVisible00(lua_State *L) {
+	// This exists in the game, but does nothing.
+	return 0;
+}
+
+static int tolua_ExportedFunctions_RemoveRandomSound00(lua_State *L) {
+	// This exists in the game, but does nothing.
+	return 0;
+}
 
 // Not your imagination, the implementation of these two is quite different to the others.
 static int tolua_GetParticleIndex(lua_State *L) {
@@ -2669,16 +2698,16 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetYoukiFollowKate", tolua_ExportedFunctions_SetYoukiFollowKate00);
 	tolua_function(L, "AddRandomAnimation", tolua_ExportedFunctions_AddRandomAnimation00);
 	tolua_function(L, "PlayRandomAnimation", tolua_ExportedFunctions_PlayRandomAnimation00);
+	tolua_function(L, "PlaySmoke", tolua_ExportedFunctions_PlaySmoke00);
+	tolua_function(L, "SmokeVisible", tolua_ExportedFunctions_SmokeVisible00);
+	tolua_function(L, "PlaySnow", tolua_ExportedFunctions_PlaySnow00);
+	tolua_function(L, "PlaySnowCustom", tolua_ExportedFunctions_PlaySnowCustom00);
+	tolua_function(L, "SnowCustomVisible", tolua_ExportedFunctions_SnowCustomVisible00);
+	tolua_function(L, "RemoveRandomSound", tolua_ExportedFunctions_RemoveRandomSound00);
 	tolua_function(L, "GetParticleIndex", tolua_GetParticleIndex);
 	tolua_function(L, "EnableParticle", tolua_EnableParticle);
 
 	// TODO Syberia 2 functions..
-	//tolua_function(L, "PlaySnow", tolua_ExportedFunctions_PlaySnow00);
-	//tolua_function(L, "PlaySnowCustom", tolua_ExportedFunctions_PlaySnowCustom00);
-	//tolua_function(L, "SnowCustomVisible", tolua_ExportedFunctions_SnowCustomVisible00);
-	//tolua_function(L, "RemoveRandomSound", tolua_ExportedFunctions_RemoveRandomSound00);
-	//tolua_function(L, "PlaySmoke", tolua_ExportedFunctions_PlaySmoke00);
-	//tolua_function(L, "SmokeVisible", tolua_ExportedFunctions_SmokeVisible00);
 	//tolua_function(L, "PlayVerticalScrolling", tolua_ExportedFunctions_PlayVerticalScrolling00);
 
 	tolua_endmodule(L);


Commit: ae5d052ce2817369aa5699fc27cfa75947d17985
    https://github.com/scummvm/scummvm/commit/ae5d052ce2817369aa5699fc27cfa75947d17985
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:11+09:00

Commit Message:
TETRAEDGE: Implement updateScroll and updateViewport for Syberia 2

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/lua_binds.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index b2c21cb97e6..a356ba044be 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -328,6 +328,7 @@ bool Application::run() {
 
 		renderer->reset();
 		game->update();
+		game->scene().updateScroll();
 		g_engine->getSoundManager()->update();
 		performRender();
 		if (game->_returnToMainMenu) {
@@ -352,7 +353,6 @@ bool Application::run() {
 			}
 			_finishedGame = false;
 		}
-		InGameScene::updateScroll();
 		TeObject::deleteNow();
 	}
 	return true;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index d50451d2817..963781d5983 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -67,7 +67,8 @@ const float InGameScene::DEPTH_MAX_FLAKE = 0.1;
 
 InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr),
 _shadowLightNo(-1), _waitTime(-1.0f), _shadowColor(0, 0, 0, 0x80), _shadowFov(20.0f),
-_shadowFarPlane(1000), _shadowNearPlane(1), _maskAlpha(false) {
+_shadowFarPlane(1000), _shadowNearPlane(1), _maskAlpha(false),
+_verticalScrollTime(1000000.0f), _verticalScrollPlaying(false) {
 }
 
 void InGameScene::activateAnchorZone(const Common::String &name, bool val) {
@@ -588,7 +589,7 @@ Common::String InGameScene::imagePathMarker(const Common::String &name) {
 }
 
 void InGameScene::initScroll() {
-	_someScrollVector = TeVector2f32(0.5f, 0.0f);
+	_scrollOffset = TeVector2f32(0.5f, 0.0f);
 }
 
 bool InGameScene::isMarker(const Common::String &name) {
@@ -607,6 +608,21 @@ bool InGameScene::isObjectBlocking(const Common::String &name) {
 	return false;
 }
 
+TeVector2f32 InGameScene::layerSize() {
+	TeLayout *bglayout = _bgGui.layout("background");
+	TeVector3f32 sz;
+	if (bglayout) {
+		TeLayout *rootlayout = Game::findSpriteLayoutByName(bglayout, "root");
+		if (!rootlayout)
+			error("InGameScene::layerSize: No root layout inside the background");
+		sz = rootlayout->size();
+		_scrollScale = TeVector2f32(sz.x(), sz.y());
+	} else {
+		sz = g_engine->getApplication()->getMainWindow().size();
+	}
+	return TeVector2f32(sz.x(), sz.y());
+}
+
 bool InGameScene::load(const Common::FSNode &sceneNode) {
 	// Syberia 1 has loadActZones function contents inline.
 	loadActZones();
@@ -1322,6 +1338,14 @@ TeFreeMoveZone *InGameScene::pathZone(const Common::String &name) {
 	return nullptr;
 }
 
+void InGameScene::playVerticalScrolling(float time) {
+	_verticalScrollTimer.start();
+	_verticalScrollTimer.stop();
+	_verticalScrollTimer.start();
+	_verticalScrollTime = time * 1000000.0f;
+	_verticalScrollPlaying = true;
+}
+
 void InGameScene::reset() {
 	for (auto *character : _characters)
 		character->setFreeMoveZone(nullptr);
@@ -1422,7 +1446,7 @@ void InGameScene::unloadCharacter(const Common::String &name) {
 		_character->deleteAnim();
 		_character->deleteAllCallback();
 		if (_character->_model->anim())
-		_character->_model->anim()->stop(); // TODO: added this
+			_character->_model->anim()->stop(); // TODO: added this
 		_character->setFreeMoveZone(nullptr); // TODO: added this
 		// TODO: deleteLater() something here..
 		_character = nullptr;
@@ -1570,6 +1594,52 @@ void InGameScene::update() {
 	}
 }
 
+void InGameScene::updateScroll() {
+	if (g_engine->gameType() != TetraedgeEngine::kSyberia2)
+		return;
+
+	TeLayout *bg = _bgGui.layout("background");
+	if (!bg)
+		return;
+
+	TeSpriteLayout *root = Game::findSpriteLayoutByName(bg, "root");
+	if (!root)
+		error("No root layout in the background");
+	_scrollOffset = TeVector2f32();
+	TeVector2s32 texSize = root->_tiledSurfacePtr->tiledTexture()->totalSize();
+	if (texSize._x < 801) {
+		if (texSize._y < 601) {
+			_scrollOffset = TeVector2f32(0.5f, 0.0f);
+			updateViewport(0);
+		} else {
+			// TODO: update stuff here.
+		}
+	} else {
+		// TODO: update stuff here.
+	}
+}
+
+void InGameScene::updateViewport(int ival) {
+	TeVector2f32 lsize = layerSize();
+	TeVector2f32 offset((0.5f - _scrollOffset.getX()) * _scrollScale.getX(),
+						_scrollOffset.getY() * _scrollScale.getY());
+	TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
+	for (auto cam : cameras()) {
+		//cam->setSomething(ival);
+		int x = (winSize.x() - lsize.getX()) / 2.0f + offset.getX();
+		int y = (winSize.y() - lsize.getY()) / 2.0f;
+		int width = lsize.getX();
+		int height = lsize.getY();
+		cam->viewport(x, y, width, height);
+		float aspectRatio = lsize.getX() / lsize.getY();
+		/* TODO: Handle ratioStretched
+		if (g_engine->getApplication()->_ratioStretched) {
+			aspectRatio = (aspectRatio / (winSize.x() / winSize.y())) * 1.333333f;
+		} */
+		cam->setAspectRatio(aspectRatio);
+	}
+}
+
 void InGameScene::activateMask(const Common::String &name, bool val) {
 	for (auto mask : _masks) {
 		if (mask->name() == name) {
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 95c20258fe0..85233076de0 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -215,6 +215,7 @@ public:
 	Object3D *object3D(const Common::String &oname);
 	void onMainWindowSizeChanged();
 	TeFreeMoveZone *pathZone(const Common::String &zname);
+	void playVerticalScrolling(float time);
 	TeVector3f32 positionMarker(const Common::String &mname);
 	void removeBlockingObject(const Common::String &oname);
 
@@ -233,8 +234,8 @@ public:
 	void update() override;
 
 	// Does nothing, but to keep calls from original..
-	static void updateScroll() {};
-	static void updateViewport() {};
+	void updateScroll();
+	void updateViewport(int ival);
 
 	Character *_character;
 	Common::Array<Character *> _characters;
@@ -317,7 +318,8 @@ private:
 
 	Common::Array<Common::SharedPtr<TeLight>> _lights;
 
-	TeVector2f32 _someScrollVector;
+	TeVector2f32 _scrollOffset;
+	TeVector2f32 _scrollScale;
 	TeVector2f32 _viewportSize;
 
 	Common::Path _loadedPath;
@@ -328,7 +330,9 @@ private:
 	Common::String _zoneName;
 	bool _maskAlpha;
 	YoukiManager _youkiManager;
-
+	TeTimer _verticalScrollTimer;
+	float _verticalScrollTime;
+	bool _verticalScrollPlaying;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index dcfa39ab0f1..a3d734ebfe4 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -2511,6 +2511,20 @@ static int tolua_ExportedFunctions_RemoveRandomSound00(lua_State *L) {
 	return 0;
 }
 
+static void PlayVerticalScrolling(float time) {
+	g_engine->getGame()->scene().playVerticalScrolling(time);
+}
+
+static int tolua_ExportedFunctions_PlayVerticalScrolling00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		float f1 = tolua_tonumber(L, 1, 0.0);
+		PlayVerticalScrolling(f1);
+		return 0;
+	}
+	error("#ferror in function 'SetObjectMoveTime': %d %d %s", err.index, err.array, err.type);
+}
+
 // Not your imagination, the implementation of these two is quite different to the others.
 static int tolua_GetParticleIndex(lua_State *L) {
 	Common::String s1(tolua_tostring(L, 1, nullptr));
@@ -2704,12 +2718,10 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "PlaySnowCustom", tolua_ExportedFunctions_PlaySnowCustom00);
 	tolua_function(L, "SnowCustomVisible", tolua_ExportedFunctions_SnowCustomVisible00);
 	tolua_function(L, "RemoveRandomSound", tolua_ExportedFunctions_RemoveRandomSound00);
+	tolua_function(L, "PlayVerticalScrolling", tolua_ExportedFunctions_PlayVerticalScrolling00);
 	tolua_function(L, "GetParticleIndex", tolua_GetParticleIndex);
 	tolua_function(L, "EnableParticle", tolua_EnableParticle);
 
-	// TODO Syberia 2 functions..
-	//tolua_function(L, "PlayVerticalScrolling", tolua_ExportedFunctions_PlayVerticalScrolling00);
-
 	tolua_endmodule(L);
 }
 


Commit: ed71db34038525a212ce3fc31a257946314aa559
    https://github.com/scummvm/scummvm/commit/ed71db34038525a212ce3fc31a257946314aa559
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:11+09:00

Commit Message:
TETRAEDGE: Avoid compiler warning in TeParticle

Changed paths:
    engines/tetraedge/te/te_particle.cpp
    engines/tetraedge/te/te_particle.h


diff --git a/engines/tetraedge/te/te_particle.cpp b/engines/tetraedge/te/te_particle.cpp
index be8904ec7b7..89878b2edda 100644
--- a/engines/tetraedge/te/te_particle.cpp
+++ b/engines/tetraedge/te/te_particle.cpp
@@ -25,7 +25,10 @@
 
 namespace Tetraedge {
 
-TeParticle::TeParticle(TeScene *scene) : _scene(scene), _size(0),
+//static const char *TE_PARTICLE_RANDOM_TABLE = "http://www.arkham-development.com/";
+
+
+TeParticle::TeParticle(TeScene *scene) : /*_scene(scene),*/ _size(0),
 _colorTime(0), _time(0), _period(0), _particlePerPeriod(0),
 _enabled(false), _startLoop(0), _gravity(0), _randomDir(false) {
 	indexedParticles()->push_back(this);
@@ -51,7 +54,13 @@ bool TeParticle::loadTexture(const Common::String &filename) {
 }
 
 void TeParticle::update(int val) {
-	// TODO: Implement me.
+	if (val <= 0) {
+		_realTimer.timeElapsed();
+		return;
+	}
+	for (int i = 0; i < val; i++) {
+		// TODO: Finish me.
+	}
 }
 
 /*static*/
diff --git a/engines/tetraedge/te/te_particle.h b/engines/tetraedge/te/te_particle.h
index 69e91861480..39e45cddae7 100644
--- a/engines/tetraedge/te/te_particle.h
+++ b/engines/tetraedge/te/te_particle.h
@@ -66,7 +66,7 @@ public:
 
 private:
 	Common::Array<TeIntrusivePtr<TeElement>> _elements;
-	TeScene *_scene;
+	//TeScene *_scene;
 	TeRealTimer _realTimer;
 	Common::String _name;
 	TeIntrusivePtr<Te3DTexture> _texture;


Commit: eb871c93ada23752d9d3688e160b43dc021ebdc4
    https://github.com/scummvm/scummvm/commit/eb871c93ada23752d9d3688e160b43dc021ebdc4
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:11+09:00

Commit Message:
TETRAEDGE: Complete updateScroll for Syberia 2

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/application.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index a356ba044be..73e4f3a976c 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -48,7 +48,7 @@ bool Application::_dontUpdateWhenApplicationPaused = false;
 
 Application::Application() : _finishedGame(false), _finishedFremium(false),
 _captureFade(false), _difficulty(1), _created(false), _tutoActivated(false),
-_drawShadows(true) {
+_drawShadows(true), _ratioStretched(false) {
 	TeCore *core = g_engine->getCore();
 	core->_coreNotReady = true;
 	core->fileFlagSystemSetFlag("platform", "MacOSX");
diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
index 37afa2d40e0..a37b6e3056e 100644
--- a/engines/tetraedge/game/application.h
+++ b/engines/tetraedge/game/application.h
@@ -113,6 +113,7 @@ public:
 	TeLayout &frontOrientationLayout() { return _frontOrientationLayout; }
 	TeLayout &backLayout() { return _backLayout; }
 	LocFile &loc() { return _loc; }
+	bool ratioStretched() const { return _ratioStretched; }
 
 private:
 	bool _finishedGame;
@@ -166,6 +167,7 @@ private:
 	bool _created;
 	bool _tutoActivated;
 	bool _drawShadows;
+	bool _ratioStretched;
 
 	int _difficulty;
 
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 963781d5983..08ec38013eb 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -428,8 +428,19 @@ void InGameScene::drawMask() {
 }
 
 void InGameScene::drawReflection() {
-	if (_rippleMasks.size())
-		error("TODO: Implement InGameScene::drawReflection");
+	if (_rippleMasks.empty() || currentCameraIndex() >= (int)cameras().size())
+		return;
+
+	currentCamera()->apply();
+	if (!_maskAlpha)
+		g_engine->getRenderer()->colorMask(false, false, false, false);
+
+	for (uint i = _rippleMasks.size() - 1; i > 0; i--) {
+		_rippleMasks[i]->draw();
+	}
+
+	if (!_maskAlpha)
+		g_engine->getRenderer()->colorMask(true, true, true, true);
 }
 
 void InGameScene::drawPath() {
@@ -498,7 +509,9 @@ void InGameScene::freeGeometry() {
 	_zoneModels.clear();
 	_masks.clear();
 	_shadowReceivingObjects.clear();
-	// TODO: _sceneLights.clear();
+	if (_charactersShadow)
+		_charactersShadow->destroy();
+	_sceneLights.clear();
 	if (_charactersShadow) {
 		delete _charactersShadow;
 		_charactersShadow = nullptr;
@@ -945,7 +958,8 @@ bool InGameScene::loadObjectMaterials(const Common::String &name) {
 }
 
 bool InGameScene::loadObjectMaterials(const Common::String &path, const Common::String &name) {
-	error("TODO: InGameScene::loadObjectMaterials(%s, %s)", path.c_str(), name.c_str());
+	// Seems like this is never used?
+	error("InGameScene::loadObjectMaterials(%s, %s)", path.c_str(), name.c_str());
 }
 
 bool InGameScene::loadPlayerCharacter(const Common::String &name) {
@@ -1043,8 +1057,24 @@ bool InGameScene::loadDynamicLightBloc(const Common::String &name, const Common:
 	return true;
 }
 
-bool InGameScene::loadLight(const Common::String &fname, const Common::String &zone, const Common::String &scene) {
-	warning("TODO: Implement InGameScene::loadLight");
+bool InGameScene::loadLight(const Common::String &name, const Common::String &zone, const Common::String &scene) {
+	Common::Path datpath = _sceneFileNameBase(zone, scene).joinInPlace(name).appendInPlace(".bin");
+	Common::FSNode datnode = g_engine->getCore()->findFile(datpath);
+	if (!datnode.isReadable()) {
+		warning("[InGameScene::loadLight] Can't open file : %s.", datpath.toString().c_str());
+		return false;
+	}
+
+	Common::File file;
+	file.open(datnode);
+	SceneLight light;
+	light._name = name;
+	TeVector3f32::deserialize(file, light._v1);
+	TeVector3f32::deserialize(file, light._v2);
+	light._color.deserialize(file);
+	light._f = file.readFloatLE();
+
+	_sceneLights.push_back(light);
 	return true;
 }
 
@@ -1180,8 +1210,44 @@ bool InGameScene::loadShadowReceivingObject(const Common::String &name, const Co
 	return true;
 }
 
-bool InGameScene::loadZBufferObject(const Common::String &fname, const Common::String &zone, const Common::String &scene) {
-	warning("TODO: Implement InGameScene::loadZBufferObject");
+bool InGameScene::loadZBufferObject(const Common::String &name, const Common::String &zone, const Common::String &scene) {
+	Common::Path datpath = _sceneFileNameBase(zone, scene).joinInPlace(name).appendInPlace(".bin");
+	Common::FSNode datnode = g_engine->getCore()->findFile(datpath);
+	if (!datnode.isReadable()) {
+		warning("[InGameScene::loadZBufferObject] Can't open file : %s.", datpath.toString().c_str());
+		return false;
+	}
+	TeModel *model = new TeModel();
+	model->setMeshCount(1);
+	model->setName(name);
+
+	Common::File file;
+	file.open(datnode);
+
+	// Load position, rotation, size.
+	Te3DObject2::deserialize(file, *model, false);
+
+	uint32 verts = file.readUint32LE();
+	uint32 tricount = file.readUint32LE();
+	if (verts > 100000 || tricount > 10000)
+		error("Improbable number of verts (%d) or triangles (%d)", verts, tricount);
+
+	TeMesh *mesh = model->meshes()[0].get();
+	mesh->setConf(verts, tricount * 3, TeMesh::MeshMode_Triangles, 0, 0);
+
+	for (uint i = 0; i < verts; i++) {
+		TeVector3f32 vec;
+		TeVector3f32::deserialize(file, vec);
+		mesh->setVertex(i, vec);
+		mesh->setNormal(i, TeVector3f32(0, 0, 1));
+		mesh->setColor(i, TeColor(128, 0, 255, 128));
+	}
+
+	for (uint i = 0; i < tricount * 3; i++) {
+		mesh->setIndex(i, file.readUint16LE());
+	}
+
+	_zoneModels.push_back(model);
 	return true;
 }
 
@@ -1606,36 +1672,92 @@ void InGameScene::updateScroll() {
 	if (!root)
 		error("No root layout in the background");
 	_scrollOffset = TeVector2f32();
-	TeVector2s32 texSize = root->_tiledSurfacePtr->tiledTexture()->totalSize();
+	TeIntrusivePtr<TeTiledTexture> rootTex = root->_tiledSurfacePtr->tiledTexture();
+	TeVector2s32 texSize = rootTex->totalSize();
 	if (texSize._x < 801) {
 		if (texSize._y < 601) {
 			_scrollOffset = TeVector2f32(0.5f, 0.0f);
 			updateViewport(0);
 		} else {
-			// TODO: update stuff here.
+			TeVector3f32 usersz = bg->userSize();
+			usersz.y() = 2.333333f;
+			bg->setSize(usersz);
+			//TeVector2f32 boundLayerSz = boundLayerSize();
+			layerSize();
+
+			float y1 = 300.0f / texSize._y;
+			float y2 = (texSize._y - 300.0f) / texSize._y;
+
+			if (_verticalScrollPlaying) {
+				float elapsed = _verticalScrollTimer.timeFromLastTimeElapsed();
+				_scrollOffset.setY(elapsed * (y2 - y1) / _verticalScrollTime + y1);
+			} else if (_character && _character->_model) {
+				TeIntrusivePtr<TeCamera> cam = currentCamera();
+				TeMatrix4x4 camProjMatrix = cam->projectionMatrix();
+				TeMatrix4x4 camWorldMatrix = cam->worldTransformationMatrix();
+				camWorldMatrix.inverse();
+				TeMatrix4x4 camProjWorld = camProjMatrix * camWorldMatrix;
+				TeVector3f32 charPos = camProjWorld * _character->_model->position();
+				_scrollOffset.setY(1.0f - (charPos.y() + 1.0f));
+			}
+			_scrollOffset.setX(0.5f);
+			_scrollOffset.setY(CLIP(_scrollOffset.getY(), y1, y2));
+			if (_scrollOffset.getY() >= y2 && _verticalScrollPlaying) {
+				_verticalScrollTimer.stop();
+				_verticalScrollPlaying = false;
+			}
+			root->setAnchor(TeVector3f32(0.5f, _scrollOffset.getY(), 0.5f));
+			updateViewport(2);
 		}
 	} else {
-		// TODO: update stuff here.
+		TeVector3f32 usersz = bg->userSize();
+		usersz.x() = texSize._x / 800.0f;
+		bg->setSize(usersz);
+		//TeVector2f32 boundLayerSz = boundLayerSize();
+		TeVector2f32 layerSz = layerSize();
+		float x1, x2;
+		if (g_engine->getApplication()->ratioStretched()) {
+			x1 = layerSz.getX() * 2;
+			const TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
+			x2 = winSize.x();
+		} else {
+			x1 = layerSz.getX() * 2;
+			x2 = layerSz.getX();
+		}
+		TeIntrusivePtr<TeCamera> cam = currentCamera();
+		if (cam) {
+			TeMatrix4x4 camProjMatrix = cam->projectionMatrix();
+			TeMatrix4x4 camWorldMatrix = cam->worldTransformationMatrix();
+			camWorldMatrix.inverse();
+			TeMatrix4x4 camProjWorld = camProjMatrix * camWorldMatrix;
+			TeVector3f32 charPos = camProjWorld * _character->_model->position();
+			_scrollOffset.setX((charPos.x() + 1.0f) / 2.0f);
+			float xmin = x2 / x1;
+			float xmax = 1.0f - (x2 / x1);
+			_scrollOffset.setX(CLIP(_scrollOffset.getX(), xmin, xmax));
+			root->setAnchor(TeVector3f32(_scrollOffset.getX(), 0.5f, 0.5f));
+			TeLayout *forbg = g_engine->getGame()->forGui().layoutChecked("background");
+			forbg->setAnchor(TeVector3f32(_scrollOffset.getX(), 0.5f, 0.5f));
+			updateViewport(1);
+			// _globalScrollingType = 1;  // This gets set but never used?
+		}
 	}
 }
 
 void InGameScene::updateViewport(int ival) {
-	TeVector2f32 lsize = layerSize();
-	TeVector2f32 offset((0.5f - _scrollOffset.getX()) * _scrollScale.getX(),
-						_scrollOffset.getY() * _scrollScale.getY());
-	TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
+	const TeVector2f32 lsize = layerSize();
+	const TeVector2f32 offset((0.5f - _scrollOffset.getX()) * _scrollScale.getX(),
+							_scrollOffset.getY() * _scrollScale.getY());
+	const TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
+	float aspectRatio = lsize.getX() / lsize.getY();
 	for (auto cam : cameras()) {
 		//cam->setSomething(ival);
 		int x = (winSize.x() - lsize.getX()) / 2.0f + offset.getX();
 		int y = (winSize.y() - lsize.getY()) / 2.0f;
-		int width = lsize.getX();
-		int height = lsize.getY();
-		cam->viewport(x, y, width, height);
-		float aspectRatio = lsize.getX() / lsize.getY();
-		/* TODO: Handle ratioStretched
-		if (g_engine->getApplication()->_ratioStretched) {
+		cam->viewport(x, y, lsize.getX(), lsize.getY());
+		if (g_engine->getApplication()->ratioStretched()) {
 			aspectRatio = (aspectRatio / (winSize.x() / winSize.y())) * 1.333333f;
-		} */
+		}
 		cam->setAspectRatio(aspectRatio);
 	}
 }
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 85233076de0..4b8b4200159 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -123,11 +123,19 @@ public:
 		void initFire();
 	};
 
-	// TODO: 
-	struct RippleMask {
+	// TODO: Any other members of RippleMask?
+	class RippleMask : public TeModel {
 
 	};
 
+	struct SceneLight {
+		Common::String _name;
+		TeVector3f32 _v1;
+		TeVector3f32 _v2;
+		TeColor _color;
+		float _f;
+	};
+
 	void activateAnchorZone(const Common::String &name, bool val);
 	void addAnchorZone(const Common::String &s1, const Common::String &name, float radius);
 	void addBlockingObject(const Common::String &obj) {
@@ -304,6 +312,7 @@ private:
 	Common::Array<TeIntrusivePtr<TeBezierCurve>> _bezierCurves;
 	Common::Array<Dummy> _dummies;
 	Common::Array<Flamme> _flammes;
+	Common::Array<SceneLight> _sceneLights;
 	Common::Array<TeIntrusivePtr<TeModel>> _zoneModels;
 	Common::Array<TeIntrusivePtr<TeModel>> _masks;
 	Common::Array<TeIntrusivePtr<TeParticle>> _particles;


Commit: ebfa1e91cc4d8a430f6d3c194a768559fbc5ce3d
    https://github.com/scummvm/scummvm/commit/ebfa1e91cc4d8a430f6d3c194a768559fbc5ce3d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:12+09:00

Commit Message:
TETRAEDGE: Add Charater::Water class for Syberia 2

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index a4f1bc3b334..d72608c5cfa 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -1054,4 +1054,44 @@ void Character::walkTo(float curveEnd, bool walkFlag) {
 	play();
 }
 
+Character::Water::Water() {
+	_model = new TeModel();
+	_model->setName("Water");
+	TeIntrusivePtr<TeCamera> cam = g_engine->getGame()->scene().currentCamera();
+	if (!cam)
+		error("No active camera when constructing water");
+	TeMatrix4x4 camMatrix = cam->worldTransformationMatrix();
+	Common::Array<TeVector3f32> quad;
+	quad.resize(4);
+	quad[0] = camMatrix.mult3x3(TeVector3f32(-0.1, 0.0,  0.1));
+	quad[1] = camMatrix.mult3x3(TeVector3f32( 0.1, 0.0,  0.1));
+	quad[2] = camMatrix.mult3x3(TeVector3f32(-0.1, 0.0, -0.1));
+	quad[3] = camMatrix.mult3x3(TeVector3f32( 0.1, 0.0, -0.1));
+	TeQuaternion rot = TeQuaternion::fromEuler(TeVector3f32(0, 0, 0));
+	TeIntrusivePtr<Te3DTexture> tex = Te3DTexture::makeInstance();
+	tex->load(g_engine->getCore()->findFile("texturesIngame/EauOndine1.tga"));
+	_model->setQuad(tex, quad, TeColor(255, 0, 0, 0));
+	_model->setRotation(rot);
+	_model->setScale(TeVector3f32(0.5, 0.5, 0.5));
+	_colorAnim._duration = 2000.0f;
+	TeColor col = _model->color();
+	col.a() = 100;
+	_colorAnim._startVal = col;
+	col.a() = 0;
+	_colorAnim._endVal = col;
+	Common::Array<float> curve;
+	curve.push_back(0);
+	curve.push_back(1);
+	_colorAnim.setCurve(curve);
+	_colorAnim._callbackObj = _model.get();
+	_colorAnim._callbackMethod = &TeModel::setColor;
+	_colorAnim.play();
+	_scaleAnim._duration = 2000.0f;
+	_scaleAnim._startVal = _model->scale();
+	_scaleAnim._endVal = TeVector3f32(3.0f, 3.0f, 3.0f);
+	_scaleAnim.setCurve(curve);
+	_scaleAnim._callbackObj = _model.get();
+	_scaleAnim._callbackMethod = &TeModel::setScale;
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 5ab2b71ba30..2e697516797 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -34,6 +34,7 @@
 #include "tetraedge/te/te_bezier_curve.h"
 #include "tetraedge/te/te_free_move_zone.h"
 #include "tetraedge/te/te_trs.h"
+#include "tetraedge/te/te_curve_anim2.h"
 
 namespace Tetraedge {
 
@@ -95,6 +96,13 @@ public:
 		float _callsMade;
 	};
 
+	class Water {
+		Water();
+		TeIntrusivePtr<TeModel> _model;
+		TeCurveAnim2<TeModel,TeColor> _colorAnim;
+		TeCurveAnim2<TeModel,TeVector3f32> _scaleAnim;
+	};
+
 	void addCallback(const Common::String &s1, const Common::String &s2, float f1, float f2);
 
 	static void animCacheFreeAll();


Commit: 8e057462db3ee367e3059f383a990c2ba31a4045
    https://github.com/scummvm/scummvm/commit/8e057462db3ee367e3059f383a990c2ba31a4045
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:12+09:00

Commit Message:
TETRAEDGE: Handle invertNormals for Syberia 2 characters

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_vector3f32.h


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index d72608c5cfa..3dae16c93fa 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -371,14 +371,17 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 
 	_model->setName(mname);
 	_model->setScale(_characterSettings._defaultScale);
+	if (_characterSettings._invertNormals)
+		_model->invertNormals();
 
 	for (auto &mesh : _model->meshes())
 		mesh->setVisible(true);
 
-	// Set all mouthes not visible by default
+	// Set all mouthes, eyes, etc not visible by default
 	_model->setVisibleByName("_B_", false);
-	// Set all eyes not visible by default
 	_model->setVisibleByName("_Y_", false);
+	_model->setVisibleByName("_M_", false);
+	_model->setVisibleByName("_E_", false);
 
 	// Note: game loops through "faces" here, but it only ever uses the default ones.
 	_model->setVisibleByName(_characterSettings._defaultEyes, true);
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 793c2c1502c..da2a2d24b16 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -137,6 +137,20 @@ TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, uint num) {
 	return _bones[num]._trs;
 }
 
+void TeModel::invertNormals() {
+	for (auto mesh : meshes()) {
+		for (uint i = 0; i < mesh->numIndexes() / 3; i++) {
+			ushort idx0 = mesh->index(i);
+			ushort idx2 = mesh->index(i + 2);
+			mesh->setIndex(i, idx2);
+			mesh->setIndex(i, idx0);
+		}
+		for (uint i = 0; i < mesh->numVerticies(); i++) {
+			mesh->setNormal(i, -mesh->normal(i));
+		}
+	}
+}
+
 TeMatrix4x4 TeModel::lerpElementsMatrix(uint weightsNum, const Common::Array<TeMatrix4x4> &matricies) {
 	TeMatrix4x4 retval;
 	// Start with a 0 matrix.
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index e4b4f68d830..df74b9d3e57 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -98,6 +98,7 @@ public:
 	int findOrAddWeights(const Common::Array<weightElement> &weights);
 	void forceMatrix(const TeMatrix4x4 &matrix);
 	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, uint num);
+	void invertNormals();
 
 	/* Align the stream to the nearest 4 byte boudary*/
 	static void loadAlign(Common::SeekableReadStream &stream);
diff --git a/engines/tetraedge/te/te_vector3f32.h b/engines/tetraedge/te/te_vector3f32.h
index eeb839f54be..e1e740fcc67 100644
--- a/engines/tetraedge/te/te_vector3f32.h
+++ b/engines/tetraedge/te/te_vector3f32.h
@@ -76,6 +76,8 @@ public:
 	}
 
 	void rotate(const TeQuaternion &rot);
+
+	TeVector3f32 operator-() { return TeVector3f32(-x(), -y(), -z()); }
 };
 
 TeVector3f32 operator^(const TeVector3f32 &left, const TeVector3f32 &right);


Commit: 3cc29bcfc574a3c1db4a148fa0a4acf7624e344c
    https://github.com/scummvm/scummvm/commit/3cc29bcfc574a3c1db4a148fa0a4acf7624e344c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:12+09:00

Commit Message:
TETRAEDGE: Clean up some TODOs

Changed paths:
    engines/tetraedge/game/application.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 73e4f3a976c..e73a84abdcd 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -309,7 +309,8 @@ void Application::startGame(bool newGame, int difficulty) {
 }
 
 void Application::resume() {
-	error("TODO: Implement Application::resume");
+	// Probably not needed.
+	error("Implement Application::resume");
 }
 
 bool Application::run() {
@@ -347,8 +348,8 @@ bool Application::run() {
 				TeLuaGUI finalGui;
 				finalGui.load("finalURL.lua");
 				/*TeVariant finalVal =*/ finalGui.value("finalURL");
+				// Not clear if this variant is ever used in original.
 				debug("TODO: use final URL??");
-				// TODO: Not clear if this variant is ever used in original.
 				finalGui.unload();
 			}
 			_finishedGame = false;
@@ -359,7 +360,8 @@ bool Application::run() {
 }
 
 void Application::suspend() {
-	error("TODO: Implement Application::suspend");
+	// Probably not needed.
+	error("Implement Application::suspend");
 }
 
 void Application::showNoCellIcon(bool show) {
@@ -385,7 +387,8 @@ void Application::showLoadingIcon(bool show) {
 }
 
 void Application::saveCorrupted(const Common::String &fname) {
-	error("TODO: Implement Application::showLoadingIcon");
+	// Probably not needed.
+	error("Implement Application::saveCorrupted");
 }
 
 void Application::drawBack() {
@@ -504,7 +507,8 @@ bool Application::isFading() {
 }
 
 bool Application::onBlackFadeAnimationFinished() {
-	error("TODO: Implement Application::onBlackFadeAnimationFinished");
+	_visFade._blackFadeSprite.setVisible(false);
+	_visFade._buttonLayout.setVisible(false);
 	return false;
 }
 
@@ -545,13 +549,13 @@ void Application::lockCursorFromAction(bool lock) {
 }
 
 void Application::loadOptions(const Common::String &fname) {
-	// TODO: Maybe load options here - original uses an
-	// xml file but we would want confman.
-	debug("TODO: Implement Application::loadOptions %s", fname.c_str());
+	// Probably not needed.  We sync confman in addArtworkUnlocked.
+	debug("Application::loadOptions %s", fname.c_str());
 }
 
 void Application::saveOptions(const Common::String &fname) {
-	debug("TODO: Implement Application::saveOptions %s", fname.c_str());
+	// Probably not needed.  We sync confman in addArtworkUnlocked.
+	debug("Application::saveOptions %s", fname.c_str());
 }
 
 Common::String Application::getHelpText(const Common::String &key) {
@@ -559,7 +563,8 @@ Common::String Application::getHelpText(const Common::String &key) {
 }
 
 const char *Application::inAppUnlockFullVersionID() {
-	error("TODO: Implement Application::inAppUnlockFullVersionID");
+	// Probably not needed.
+	error("Implement Application::inAppUnlockFullVersionID");
 }
 
 } // end namespace Tetraedge


Commit: fcc44b34c01971dd43726758c564d62daa5efeb0
    https://github.com/scummvm/scummvm/commit/fcc44b34c01971dd43726758c564d62daa5efeb0
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-02-20T17:08:12+09:00

Commit Message:
TETRAEDGE: Fix some Coverity issues (null checks, initialization)

Changed paths:
    engines/tetraedge/game/bonus_menu.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/documents_browser.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/splash_screens.cpp


diff --git a/engines/tetraedge/game/bonus_menu.cpp b/engines/tetraedge/game/bonus_menu.cpp
index 1189b9ec624..10daeb05664 100644
--- a/engines/tetraedge/game/bonus_menu.cpp
+++ b/engines/tetraedge/game/bonus_menu.cpp
@@ -148,7 +148,7 @@ bool BonusMenu::onLeftButton() {
 }
 
 bool BonusMenu::onMouseMove(const Common::Point &pt) {
-	TeButtonLayout *slideLayout = buttonLayout("slideButton");
+	TeButtonLayout *slideLayout = buttonLayoutChecked("slideButton");
 	if (slideLayout->state() == TeButtonLayout::BUTTON_STATE_DOWN) {
 		TeCurveAnim2<TeLayout, TeVector3f32> *slideAnim = layoutPositionLinearAnimation("slideAnimation");
 		if (!slideAnim->_runTimer.running()) {
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 2e697516797..5667f012051 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -57,7 +57,7 @@ public:
 	};
 
 	struct CharacterSettings {
-		CharacterSettings() : _walkSpeed(0.0f) {}
+		CharacterSettings() : _walkSpeed(0.0f), _invertNormals(false) {}
 
 		Common::String _name;
 		Common::String _modelFileName;
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
index f4d2a9bef77..94b47eccbf8 100644
--- a/engines/tetraedge/game/documents_browser.cpp
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -90,11 +90,11 @@ void DocumentsBrowser::load() {
 	if (docBrowser)
 		addChild(docBrowser);
 
-	TeButtonLayout *button = _gui1.buttonLayout("previousPage");
+	TeButtonLayout *button = _gui1.buttonLayoutChecked("previousPage");
 	button->onMouseClickValidated().add(this, &DocumentsBrowser::onPreviousPage);
-	button = _gui1.buttonLayout("nextPage");
+	button = _gui1.buttonLayoutChecked("nextPage");
 	button->onMouseClickValidated().add(this, &DocumentsBrowser::onNextPage);
-	button = _gui1.buttonLayout("zoomed");
+	button = _gui1.buttonLayoutChecked("zoomed");
 	button->onMouseClickValidated().add(this, &DocumentsBrowser::onZoomedButton);
 	button->setVisible(false);
 
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index e47eba25c5f..f3b92fac736 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -82,7 +82,7 @@ bool Game::addAnimToSet(const Common::String &anim) {
 	const Common::Path animPath(Common::String("scenes/") + anim + "/");
 
 	if (Common::File::exists(animPath)) {
-		Common::StringArray parts = TetraedgeEngine::splitString(anim, '/');
+		const Common::StringArray parts = TetraedgeEngine::splitString(anim, '/');
 		assert(parts.size() >= 2);
 
 		const Common::String layoutName = parts[1];
@@ -602,7 +602,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	video->_tiledSurfacePtr->_frameAnim.onStop().remove(this, &Game::onVideoFinished);
 	video->_tiledSurfacePtr->_frameAnim.onStop().add(this, &Game::onVideoFinished);
 
-	TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
+	TeButtonLayout *invbtn = _inGameGui.buttonLayoutChecked("inventoryButton");
 	invbtn->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
 	invbtn->onMouseClickValidated().add(this, &Game::onInventoryButtonValidated);
 	invbtn->setSizeType(TeILayout::RELATIVE_TO_PARENT);
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 08ec38013eb..76374707740 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -94,7 +94,7 @@ void InGameScene::addAnchorZone(const Common::String &s1, const Common::String &
 	zone->_activated = true;
 
 	if (s1.contains("Int")) {
-		TeButtonLayout *btn = hitObjectGui().buttonLayout(name);
+		TeButtonLayout *btn = hitObjectGui().buttonLayoutChecked(name);
 		TeVector3f32 pos = btn->position();
 		pos.x() += g_engine->getDefaultScreenWidth() / 2.0f;
 		pos.y() += g_engine->getDefaultScreenHeight() / 2.0f;
@@ -1673,7 +1673,7 @@ void InGameScene::updateScroll() {
 		error("No root layout in the background");
 	_scrollOffset = TeVector2f32();
 	TeIntrusivePtr<TeTiledTexture> rootTex = root->_tiledSurfacePtr->tiledTexture();
-	TeVector2s32 texSize = rootTex->totalSize();
+	const TeVector2s32 texSize = rootTex->totalSize();
 	if (texSize._x < 801) {
 		if (texSize._y < 601) {
 			_scrollOffset = TeVector2f32(0.5f, 0.0f);
@@ -1693,10 +1693,10 @@ void InGameScene::updateScroll() {
 				_scrollOffset.setY(elapsed * (y2 - y1) / _verticalScrollTime + y1);
 			} else if (_character && _character->_model) {
 				TeIntrusivePtr<TeCamera> cam = currentCamera();
-				TeMatrix4x4 camProjMatrix = cam->projectionMatrix();
+				const TeMatrix4x4 camProjMatrix = cam->projectionMatrix();
 				TeMatrix4x4 camWorldMatrix = cam->worldTransformationMatrix();
 				camWorldMatrix.inverse();
-				TeMatrix4x4 camProjWorld = camProjMatrix * camWorldMatrix;
+				const TeMatrix4x4 camProjWorld = camProjMatrix * camWorldMatrix;
 				TeVector3f32 charPos = camProjWorld * _character->_model->position();
 				_scrollOffset.setY(1.0f - (charPos.y() + 1.0f));
 			}
@@ -1726,11 +1726,11 @@ void InGameScene::updateScroll() {
 		}
 		TeIntrusivePtr<TeCamera> cam = currentCamera();
 		if (cam) {
-			TeMatrix4x4 camProjMatrix = cam->projectionMatrix();
+			const TeMatrix4x4 camProjMatrix = cam->projectionMatrix();
 			TeMatrix4x4 camWorldMatrix = cam->worldTransformationMatrix();
 			camWorldMatrix.inverse();
-			TeMatrix4x4 camProjWorld = camProjMatrix * camWorldMatrix;
-			TeVector3f32 charPos = camProjWorld * _character->_model->position();
+			const TeMatrix4x4 camProjWorld = camProjMatrix * camWorldMatrix;
+			const TeVector3f32 charPos = camProjWorld * _character->_model->position();
 			_scrollOffset.setX((charPos.x() + 1.0f) / 2.0f);
 			float xmin = x2 / x1;
 			float xmax = 1.0f - (x2 / x1);
diff --git a/engines/tetraedge/game/splash_screens.cpp b/engines/tetraedge/game/splash_screens.cpp
index 22c88aed33d..ed731556fb6 100644
--- a/engines/tetraedge/game/splash_screens.cpp
+++ b/engines/tetraedge/game/splash_screens.cpp
@@ -68,7 +68,7 @@ bool SplashScreens::onAlarm() {
 	} else {
 		load(scriptName);
 
-		TeButtonLayout *btnLayout = buttonLayout("splash");
+		TeButtonLayout *btnLayout = buttonLayoutChecked("splash");
 		btnLayout->onMouseClickValidated().add(this, &SplashScreens::onQuitSplash);
 
 		TeLayout *splash = layout("splash");




More information about the Scummvm-git-logs mailing list