[Scummvm-git-logs] scummvm master -> 8591ae2260ec6c6a202a6d6893a1ebde0164c011

sev- noreply at scummvm.org
Mon Jan 16 16:37:11 UTC 2023


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

Summary:
851c74df30 TETRAEDGE: New WIP engine for Syberia macOS and others
d07187a1a4 TETRAEDGE: More progress on WIP engine.
681b4f8c55 TETRAEDGE: More WIP. First scene now loads, yay.
1d62d47edb TETRAEDGE: More WIP. Can now see Kate in first scene.
2c87391803 TETRAEDGE: WIP, fixed loads of TODOs.
710a1b9f1b TETRAEDGE: More WIP. Idle animations work, character rotation and position now right.
a5811c653a TETRAEDGE: More WIP. Started work on pathfinding.
d28e9a2212 TETRAEDGE: WIP, can almost walk around now.
036ad4bc04 TETRAEDGE: WIP, fix suitcase and faces on the floor.
85ed7742de TETRAEDGE: More WIP.  Can now walk between screens.
194b1a8081 TETRAEDGE: Work in progress, some inventory stuff done.
64c7267283 TETRAEDGE: Many small improvements.
c27dca6b82 TETRAEDGE: More WIP. Lots of progress
501b00ebe7 TETRAEDGE: WIP. Fixed some callbacks, more bits of the game working.
791575b0c2 TETRAEDGE: WIP: Fix more interactions
531b53f66a TETRAEDGE: Switch button priority to z-order
a3eb8b9c1c TETRAEDGE: Fix inventory and other interactions
61e3520067 TETRAEDGE: More WIP. Fixed various object problems.
e3aff09674 TETRAEDGE: Lua bind functions now complete.
4a8f9984d4 TETRAEDGE: Fixed more bugs, started save/load support.
769d8d9b42 TETRAEDGE: Fix a lot of small rendering bugs.
8b3c8844fe TETRAEDGE: Fix a lot of memory cleanup
4479e6e346 TETRAEDGE: Implement FreeMoveZone building
066dd8e155 TETRAEDGE: More improvements
eba2fb9b5f MATH: Add ray triangle intersection and tests
7424452fff TETRAEDGE: More fixes, can now complete first act.
0ba854df9a TETRAEDGE: More fixes, can now complete game.
f84785ff3a TETRAEDGE: Cleanups, started working on credits etc
9dab12cf3c TETRAEDGE: Misc code cleanups. Less public members.
0d981983fc TETRAEDGE: add ability to save/load any time
87bbf9660e TETRAEDGE: Reduce dependency on OpenGL headers
48015aa751 TETRAEDGE: Small fixes.
d0d67e91f8 TETRAEDGE: Reduce movement janks and accidental moves
9cdfb0deee TETRAEDGE: Clean up includes
c4869ff02f TETRAEDGE: Fix whitespace and use uint consistently
b75e9faae3 TETRAEDGE: Remove unused Console class
68790973ba TETRAEDGE: Comment for detection entry
1a793bae5b TETRAEDGE: Remove static objects and improve cleanup
16c607cd94 TETRAEDGE: Split OpenGL-specific code out to allow TinyGL support
fd496bdd55 TETRAEDGE: Remove unused variable
47739e4585 TETRAEDGE: Only compile openGL files when OGL is enabled
e6c8059523 TETRAEDGE: Add TinyGL implementation for TeLight.
8dfe5e4b88 TETRAEDGE: Clean up OGL/TGL classes slightly
91f1e17ed3 TETRAEDGE: Fixes to get to main menu of Syberia 2
b310f5dea3 TETRAEDGE: Fix compiler guards for GL (no shaders)
dbe6da9980 TETRAEDGE: Remove compile warnings
8591ae2260 TETRAEDGE: Fix updating of global light value


Commit: 851c74df306e73b0a36eca93839ce55eac569ed9
    https://github.com/scummvm/scummvm/commit/851c74df306e73b0a36eca93839ce55eac569ed9
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: New WIP engine for Syberia macOS and others

Changed paths:
  A engines/tetraedge/configure.engine
  A engines/tetraedge/console.cpp
  A engines/tetraedge/console.h
  A engines/tetraedge/credits.pl
  A engines/tetraedge/detection.cpp
  A engines/tetraedge/detection.h
  A engines/tetraedge/detection_tables.h
  A engines/tetraedge/game/application.cpp
  A engines/tetraedge/game/application.h
  A engines/tetraedge/game/billboard.cpp
  A engines/tetraedge/game/billboard.h
  A engines/tetraedge/game/bonus_menu.cpp
  A engines/tetraedge/game/bonus_menu.h
  A engines/tetraedge/game/cellphone.cpp
  A engines/tetraedge/game/cellphone.h
  A engines/tetraedge/game/character.cpp
  A engines/tetraedge/game/character.h
  A engines/tetraedge/game/character_settings_xml_parser.cpp
  A engines/tetraedge/game/character_settings_xml_parser.h
  A engines/tetraedge/game/characters_shadow.cpp
  A engines/tetraedge/game/characters_shadow.h
  A engines/tetraedge/game/confirm.cpp
  A engines/tetraedge/game/confirm.h
  A engines/tetraedge/game/credits.cpp
  A engines/tetraedge/game/credits.h
  A engines/tetraedge/game/dialog2.cpp
  A engines/tetraedge/game/dialog2.h
  A engines/tetraedge/game/document.cpp
  A engines/tetraedge/game/document.h
  A engines/tetraedge/game/documents_browser.cpp
  A engines/tetraedge/game/documents_browser.h
  A engines/tetraedge/game/gallery_menu.cpp
  A engines/tetraedge/game/gallery_menu.h
  A engines/tetraedge/game/game.cpp
  A engines/tetraedge/game/game.h
  A engines/tetraedge/game/game_achievements.cpp
  A engines/tetraedge/game/game_achievements.h
  A engines/tetraedge/game/game_sound.cpp
  A engines/tetraedge/game/game_sound.h
  A engines/tetraedge/game/global_bonus_menu.cpp
  A engines/tetraedge/game/global_bonus_menu.h
  A engines/tetraedge/game/help_option_menu.cpp
  A engines/tetraedge/game/help_option_menu.h
  A engines/tetraedge/game/how_to.cpp
  A engines/tetraedge/game/how_to.h
  A engines/tetraedge/game/in_game_scene.cpp
  A engines/tetraedge/game/in_game_scene.h
  A engines/tetraedge/game/inventory.cpp
  A engines/tetraedge/game/inventory.h
  A engines/tetraedge/game/inventory_menu.cpp
  A engines/tetraedge/game/inventory_menu.h
  A engines/tetraedge/game/inventory_object.cpp
  A engines/tetraedge/game/inventory_object.h
  A engines/tetraedge/game/inventory_objects_xml_parser.cpp
  A engines/tetraedge/game/inventory_objects_xml_parser.h
  A engines/tetraedge/game/loading_menu.cpp
  A engines/tetraedge/game/loading_menu.h
  A engines/tetraedge/game/loc_file.cpp
  A engines/tetraedge/game/loc_file.h
  A engines/tetraedge/game/lua_binds.cpp
  A engines/tetraedge/game/lua_binds.h
  A engines/tetraedge/game/main_menu.cpp
  A engines/tetraedge/game/main_menu.h
  A engines/tetraedge/game/notifier.cpp
  A engines/tetraedge/game/notifier.h
  A engines/tetraedge/game/object3d.cpp
  A engines/tetraedge/game/object3d.h
  A engines/tetraedge/game/object_settings_xml_parser.cpp
  A engines/tetraedge/game/object_settings_xml_parser.h
  A engines/tetraedge/game/objectif.cpp
  A engines/tetraedge/game/objectif.h
  A engines/tetraedge/game/options_menu.cpp
  A engines/tetraedge/game/options_menu.h
  A engines/tetraedge/game/owner_error_menu.cpp
  A engines/tetraedge/game/owner_error_menu.h
  A engines/tetraedge/game/question2.cpp
  A engines/tetraedge/game/question2.h
  A engines/tetraedge/game/splash_screens.cpp
  A engines/tetraedge/game/splash_screens.h
  A engines/tetraedge/metaengine.cpp
  A engines/tetraedge/metaengine.h
  A engines/tetraedge/module.mk
  A engines/tetraedge/te/micropather.cpp
  A engines/tetraedge/te/micropather.h
  A engines/tetraedge/te/te_3d_object2.cpp
  A engines/tetraedge/te/te_3d_object2.h
  A engines/tetraedge/te/te_3d_texture.cpp
  A engines/tetraedge/te/te_3d_texture.h
  A engines/tetraedge/te/te_animation.cpp
  A engines/tetraedge/te/te_animation.h
  A engines/tetraedge/te/te_bezier_curve.cpp
  A engines/tetraedge/te/te_bezier_curve.h
  A engines/tetraedge/te/te_button_layout.cpp
  A engines/tetraedge/te/te_button_layout.h
  A engines/tetraedge/te/te_callback.h
  A engines/tetraedge/te/te_camera.cpp
  A engines/tetraedge/te/te_camera.h
  A engines/tetraedge/te/te_checkbox_layout.cpp
  A engines/tetraedge/te/te_checkbox_layout.h
  A engines/tetraedge/te/te_clip_layout.cpp
  A engines/tetraedge/te/te_clip_layout.h
  A engines/tetraedge/te/te_color.cpp
  A engines/tetraedge/te/te_color.h
  A engines/tetraedge/te/te_core.cpp
  A engines/tetraedge/te/te_core.h
  A engines/tetraedge/te/te_curve_anim2.h
  A engines/tetraedge/te/te_extended_text_layout.cpp
  A engines/tetraedge/te/te_extended_text_layout.h
  A engines/tetraedge/te/te_fee_move_zone.cpp
  A engines/tetraedge/te/te_fee_move_zone.h
  A engines/tetraedge/te/te_font3.cpp
  A engines/tetraedge/te/te_font3.h
  A engines/tetraedge/te/te_frame_anim.cpp
  A engines/tetraedge/te/te_frame_anim.h
  A engines/tetraedge/te/te_free_move_zone.cpp
  A engines/tetraedge/te/te_free_move_zone.h
  A engines/tetraedge/te/te_i_3d_object2.cpp
  A engines/tetraedge/te/te_i_3d_object2.h
  A engines/tetraedge/te/te_i_codec.h
  A engines/tetraedge/te/te_i_layout.cpp
  A engines/tetraedge/te/te_i_layout.h
  A engines/tetraedge/te/te_i_loc.cpp
  A engines/tetraedge/te/te_i_loc.h
  A engines/tetraedge/te/te_image.cpp
  A engines/tetraedge/te/te_image.h
  A engines/tetraedge/te/te_input_mgr.cpp
  A engines/tetraedge/te/te_input_mgr.h
  A engines/tetraedge/te/te_interpolation.cpp
  A engines/tetraedge/te/te_interpolation.h
  A engines/tetraedge/te/te_intrusive_ptr.h
  A engines/tetraedge/te/te_jpeg.cpp
  A engines/tetraedge/te/te_jpeg.h
  A engines/tetraedge/te/te_layout.cpp
  A engines/tetraedge/te/te_layout.h
  A engines/tetraedge/te/te_light.cpp
  A engines/tetraedge/te/te_light.h
  A engines/tetraedge/te/te_list_layout.cpp
  A engines/tetraedge/te/te_list_layout.h
  A engines/tetraedge/te/te_lua_context.cpp
  A engines/tetraedge/te/te_lua_context.h
  A engines/tetraedge/te/te_lua_gui.cpp
  A engines/tetraedge/te/te_lua_gui.h
  A engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
  A engines/tetraedge/te/te_lua_gui_lua_callbacks.h
  A engines/tetraedge/te/te_lua_script.cpp
  A engines/tetraedge/te/te_lua_script.h
  A engines/tetraedge/te/te_lua_thread.cpp
  A engines/tetraedge/te/te_lua_thread.h
  A engines/tetraedge/te/te_material.cpp
  A engines/tetraedge/te/te_material.h
  A engines/tetraedge/te/te_matricies_stack.cpp
  A engines/tetraedge/te/te_matricies_stack.h
  A engines/tetraedge/te/te_matrix4x4.cpp
  A engines/tetraedge/te/te_matrix4x4.h
  A engines/tetraedge/te/te_mesh.cpp
  A engines/tetraedge/te/te_mesh.h
  A engines/tetraedge/te/te_model.cpp
  A engines/tetraedge/te/te_model.h
  A engines/tetraedge/te/te_model_animation.cpp
  A engines/tetraedge/te/te_model_animation.h
  A engines/tetraedge/te/te_model_vertex_animation.cpp
  A engines/tetraedge/te/te_model_vertex_animation.h
  A engines/tetraedge/te/te_music.cpp
  A engines/tetraedge/te/te_music.h
  A engines/tetraedge/te/te_name_val_xml_parser.cpp
  A engines/tetraedge/te/te_name_val_xml_parser.h
  A engines/tetraedge/te/te_object.cpp
  A engines/tetraedge/te/te_object.h
  A engines/tetraedge/te/te_obp.cpp
  A engines/tetraedge/te/te_obp.h
  A engines/tetraedge/te/te_palette.cpp
  A engines/tetraedge/te/te_palette.h
  A engines/tetraedge/te/te_pick_mesh2.cpp
  A engines/tetraedge/te/te_pick_mesh2.h
  A engines/tetraedge/te/te_png.cpp
  A engines/tetraedge/te/te_png.h
  A engines/tetraedge/te/te_quaternion.cpp
  A engines/tetraedge/te/te_quaternion.h
  A engines/tetraedge/te/te_real_timer.cpp
  A engines/tetraedge/te/te_real_timer.h
  A engines/tetraedge/te/te_references_counter.h
  A engines/tetraedge/te/te_renderer.cpp
  A engines/tetraedge/te/te_renderer.h
  A engines/tetraedge/te/te_resource.cpp
  A engines/tetraedge/te/te_resource.h
  A engines/tetraedge/te/te_resource_manager.cpp
  A engines/tetraedge/te/te_resource_manager.h
  A engines/tetraedge/te/te_scene.cpp
  A engines/tetraedge/te/te_scene.h
  A engines/tetraedge/te/te_screen.cpp
  A engines/tetraedge/te/te_screen.h
  A engines/tetraedge/te/te_scrolling_layout.cpp
  A engines/tetraedge/te/te_scrolling_layout.h
  A engines/tetraedge/te/te_scummvm_codec.cpp
  A engines/tetraedge/te/te_scummvm_codec.h
  A engines/tetraedge/te/te_sfx.cpp
  A engines/tetraedge/te/te_sfx.h
  A engines/tetraedge/te/te_signal.h
  A engines/tetraedge/te/te_sound_manager.cpp
  A engines/tetraedge/te/te_sound_manager.h
  A engines/tetraedge/te/te_sprite_layout.cpp
  A engines/tetraedge/te/te_sprite_layout.h
  A engines/tetraedge/te/te_text_base2.cpp
  A engines/tetraedge/te/te_text_base2.h
  A engines/tetraedge/te/te_text_layout.cpp
  A engines/tetraedge/te/te_text_layout.h
  A engines/tetraedge/te/te_text_layout_xml_parser.cpp
  A engines/tetraedge/te/te_text_layout_xml_parser.h
  A engines/tetraedge/te/te_tga.cpp
  A engines/tetraedge/te/te_tga.h
  A engines/tetraedge/te/te_theora.cpp
  A engines/tetraedge/te/te_theora.h
  A engines/tetraedge/te/te_tiled_surface.cpp
  A engines/tetraedge/te/te_tiled_surface.h
  A engines/tetraedge/te/te_tiled_texture.cpp
  A engines/tetraedge/te/te_tiled_texture.h
  A engines/tetraedge/te/te_timer.cpp
  A engines/tetraedge/te/te_timer.h
  A engines/tetraedge/te/te_trs.cpp
  A engines/tetraedge/te/te_trs.h
  A engines/tetraedge/te/te_variant.cpp
  A engines/tetraedge/te/te_variant.h
  A engines/tetraedge/te/te_vector2f32.cpp
  A engines/tetraedge/te/te_vector2f32.h
  A engines/tetraedge/te/te_vector2s32.cpp
  A engines/tetraedge/te/te_vector2s32.h
  A engines/tetraedge/te/te_vector3f32.cpp
  A engines/tetraedge/te/te_vector3f32.h
  A engines/tetraedge/te/te_visual_fade.cpp
  A engines/tetraedge/te/te_visual_fade.h
  A engines/tetraedge/te/te_xml_gui.cpp
  A engines/tetraedge/te/te_xml_gui.h
  A engines/tetraedge/tetraedge.cpp
  A engines/tetraedge/tetraedge.h
  A engines/tetraedge/to_lua.cpp
  A engines/tetraedge/to_lua.h


diff --git a/engines/tetraedge/configure.engine b/engines/tetraedge/configure.engine
new file mode 100644
index 00000000000..5712178cc81
--- /dev/null
+++ b/engines/tetraedge/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine tetraedge "Tetraedge" no "" "" "highres freetype2 vorbis png jpeg lua theoradec"
diff --git a/engines/tetraedge/console.cpp b/engines/tetraedge/console.cpp
new file mode 100644
index 00000000000..c455866a69a
--- /dev/null
+++ b/engines/tetraedge/console.cpp
@@ -0,0 +1,38 @@
+/* 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/console.h"
+
+namespace Tetraedge {
+
+Console::Console() : GUI::Debugger() {
+	registerCmd("test",   WRAP_METHOD(Console, Cmd_test));
+}
+
+Console::~Console() {
+}
+
+bool Console::Cmd_test(int argc, const char **argv) {
+	debugPrintf("Test\n");
+	return true;
+}
+
+} // namespace Tetraedge
diff --git a/engines/tetraedge/console.h b/engines/tetraedge/console.h
new file mode 100644
index 00000000000..210f467ab37
--- /dev/null
+++ b/engines/tetraedge/console.h
@@ -0,0 +1,40 @@
+
+/* 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_CONSOLE_H
+#define TETRAEDGE_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Tetraedge {
+
+class Console : public GUI::Debugger {
+private:
+	bool Cmd_test(int argc, const char **argv);
+public:
+	Console();
+	~Console() override;
+};
+
+} // End of namespace Tetraedge
+
+#endif
diff --git a/engines/tetraedge/credits.pl b/engines/tetraedge/credits.pl
new file mode 100644
index 00000000000..a14c198dbb6
--- /dev/null
+++ b/engines/tetraedge/credits.pl
@@ -0,0 +1,3 @@
+begin_section("Tetraedge");
+	add_person("Matthew Duggan", "Handle 1", "");
+end_section();
diff --git a/engines/tetraedge/detection.cpp b/engines/tetraedge/detection.cpp
new file mode 100644
index 00000000000..938be16631e
--- /dev/null
+++ b/engines/tetraedge/detection.cpp
@@ -0,0 +1,47 @@
+/* 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 "base/plugins.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "common/str-array.h"
+#include "common/translation.h"
+#include "common/util.h"
+#include "tetraedge/detection.h"
+#include "tetraedge/detection_tables.h"
+
+const DebugChannelDef TetraedgeMetaEngineDetection::debugFlagList[] = {
+	{ Tetraedge::kDebugGraphics, "Graphics", "Graphics debug level" },
+	{ Tetraedge::kDebugPath, "Path", "Pathfinding debug level" },
+	{ Tetraedge::kDebugFilePath, "FilePath", "File path debug level" },
+	{ Tetraedge::kDebugScan, "Scan", "Scan for unrecognised games" },
+	{ Tetraedge::kDebugScript, "Script", "Enable debug script dump" },
+	DEBUG_CHANNEL_END
+};
+
+TetraedgeMetaEngineDetection::TetraedgeMetaEngineDetection() : AdvancedMetaEngineDetection(Tetraedge::GAME_DESCRIPTIONS,
+	sizeof(ADGameDescription), Tetraedge::GAME_NAMES) {
+	_flags = kADFlagMatchFullPaths;
+	_maxScanDepth = 3;
+}
+
+REGISTER_PLUGIN_STATIC(TETRAEDGE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, TetraedgeMetaEngineDetection);
diff --git a/engines/tetraedge/detection.h b/engines/tetraedge/detection.h
new file mode 100644
index 00000000000..e6efa6712cd
--- /dev/null
+++ b/engines/tetraedge/detection.h
@@ -0,0 +1,67 @@
+/* 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_DETECTION_H
+#define TETRAEDGE_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Tetraedge {
+
+enum TetraedgeDebugChannels {
+	kDebugGraphics = 1 << 0,
+	kDebugPath     = 1 << 1,
+	kDebugScan     = 1 << 2,
+	kDebugFilePath = 1 << 3,
+	kDebugScript   = 1 << 4
+};
+
+extern const PlainGameDescriptor GAME_NAMES[];
+
+extern const ADGameDescription GAME_DESCRIPTIONS[];
+
+} // namespace Tetraedge
+
+class TetraedgeMetaEngineDetection : public AdvancedMetaEngineDetection {
+	static const DebugChannelDef debugFlagList[];
+
+public:
+	TetraedgeMetaEngineDetection();
+	~TetraedgeMetaEngineDetection() override {}
+
+	const char *getEngineName() const override {
+		return "Tetraedge Engine";
+	}
+
+	const char *getName() const override {
+		return "tetraedge";
+	}
+
+	const char *getOriginalCopyright() const override {
+		return "(C) Microids";
+	}
+
+	const DebugChannelDef *getDebugChannels() const override {
+		return debugFlagList;
+	}
+};
+
+#endif
diff --git a/engines/tetraedge/detection_tables.h b/engines/tetraedge/detection_tables.h
new file mode 100644
index 00000000000..a6db2b5a904
--- /dev/null
+++ b/engines/tetraedge/detection_tables.h
@@ -0,0 +1,45 @@
+/* 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/>.
+ *
+ */
+
+namespace Tetraedge {
+
+const PlainGameDescriptor GAME_NAMES[] = {
+	{ "syberia", "Syberia" },
+	{ "amerzone", "Amerzone" },
+	{ "syberia2", "Syberia II" },
+	{ 0, 0 }
+};
+
+const ADGameDescription GAME_DESCRIPTIONS[] = {
+	{
+		"syberia",
+		nullptr,
+		AD_ENTRY1s("MacOS/Syberia", "6951fb8f71fe06f34684564625f73cd8", 10640592),
+		Common::EN_ANY,
+		Common::kPlatformMacintosh,
+		ADGF_UNSTABLE,
+		GUIO1(GUIO_NONE)
+	},
+
+	AD_TABLE_END_MARKER
+};
+
+} // namespace Tetraedge
diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
new file mode 100644
index 00000000000..fbb337d8b1d
--- /dev/null
+++ b/engines/tetraedge/game/application.cpp
@@ -0,0 +1,498 @@
+/* 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 "common/textconsole.h"
+#include "common/file.h"
+#include "common/util.h"
+#include "common/events.h"
+
+#include "graphics/opengl/system_headers.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/te/te_core.h"
+#include "tetraedge/te/te_resource_manager.h"
+#include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_font3.h"
+#include "tetraedge/te/te_input_mgr.h"
+//#include "tetraedge/te/te_textbase2.h"
+
+namespace Tetraedge {
+
+bool Application::_dontUpdateWhenApplicationPaused = false;
+
+Application::Application() : _finishedGame(false), _finishedFremium(false),
+_captureFade(false), _difficulty(1), _created(false), _tutoActivated(false) {
+	TeCore *core = g_engine->getCore();
+	core->_coreNotReady = true;
+	core->fileFlagSystemSetFlag("platform", "MacOSX");
+	core->fileFlagSystemSetFlag("part", "Full");
+	core->fileFlagSystemSetFlag("distributor", "DefaultDistributor");
+
+	TeLuaGUI tempGui;
+	tempGui.load("texts/Part.lua");
+	_applicationTitle = tempGui.value("applicationTitle").toString();
+	_versionString = tempGui.value("versionString").toString();
+	_firstWarpPath = tempGui.value("firstWarpPath").toString();
+	_firstZone = tempGui.value("firstZone").toString();
+	_firstScene = tempGui.value("firstScene").toString();
+
+	// TODO: Configure sound manager here?
+	// TODO: Configure freemium things here?
+	// TODO: Start some timer here?
+
+	loadOptions("options.xml");
+}
+
+void Application::create() {
+	warning("TODO: Move mainWindowCamera to mainWindow");
+
+	const int winWidth = g_engine->getDefaultScreenWidth();
+	const int winHeight = g_engine->getDefaultScreenHeight();
+	// See TeMainWindowBase::initCamera
+	_mainWindowCamera.reset(new TeCamera());
+	_mainWindowCamera->_projectionMatrixType = 4;
+	_mainWindowCamera->viewport(0, 0, winWidth, winHeight);
+	_mainWindowCamera->orthogonalParams(winWidth * -0.5f, winWidth * 0.5f, winHeight * 0.5f, winHeight * -0.5f);
+	_mainWindowCamera->_orthNearVal = -2048.0f;
+	_mainWindowCamera->_orthFarVal = 2048.0f;
+
+	_mainWindow.setSize(TeVector3f32(winWidth, winHeight, 100.0));
+	_mainWindow.setSizeType(TeILayout::ABSOLUTE);
+	_mainWindow.setPositionType(TeILayout::ABSOLUTE);
+
+	TeResourceManager *resmgr = g_engine->getResourceManager();
+	_fontComic = resmgr->getResourceNoSearch<TeFont3>("Common/Fonts/ComicRelief.ttf");
+	_fontComic->load("Common/Fonts/ComicRelief.ttf");
+	_fontArgh = resmgr->getResourceNoSearch<TeFont3>("Common/Fonts/Argh.ttf");
+	_fontArgh->load("Common/Fonts/Argh.ttf");
+	_fontArial = resmgr->getResourceNoSearch<TeFont3>("Common/Fonts/arial.ttf");
+	_fontArial->load("Common/Fonts/arial.ttf");
+	_fontChaucer = resmgr->getResourceNoSearch<TeFont3>("Common/Fonts/CHAUCER.TTF");
+	_fontChaucer->load("Common/Fonts/CHAUCER.ttf");
+	_fontColaborate = resmgr->getResourceNoSearch<TeFont3>("Common/Fonts/Colaborate-Regular.otf");
+	_fontColaborate->load("Common/Fonts/Colaborate-Regular.ttf");
+	_fontProDisplay = resmgr->getResourceNoSearch<TeFont3>("Common/Fonts/ProDisplay.ttf");
+	_fontProDisplay->load("Common/Fonts/ProDisplay.ttf");
+
+	// The app prebuilds some fonts.. cover letters, numbers, a few accented chars, and punctuation.
+	// Skip that here.
+	/*
+	TeTextBase2 textBase;
+	textBase.setText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/,*?;.:/!\xA7&\xE9\"'(-\xE8_\xE7\xE0)=");
+	textBase.setFont(0, _fontComic);
+	textBase.setRect(TeVector2s32(1, 1));
+	textBase.setFontSize(12);
+	textBase.build();
+	textBase.setFontSize(14);
+	textBase.build();
+	textBase.setFontSize(16);
+	textBase.build();
+	textBase.setFontSize(18);
+	textBase.build();
+	textBase.setFontSize(30);
+	textBase.build();
+	textBase.setFont(0, _fontColaborate);
+	textBase.setFontSize(18);
+	textBase.build();
+	textBase.setFont(0, _fontProDisplay);
+	textBase.setFontSize(24);
+	textBase.build();
+	 */
+
+	TeCore *core = g_engine->getCore();
+	static const char allLangs[][3] = {"en", "fr", "de", "es", "it", "ru"};
+	const Common::Path textsPath("texts");
+
+	// Try alternate langs..
+	int i = 0;
+	Common::Path textFilePath;
+	while (i < ARRAYSIZE(allLangs)) {
+		textFilePath = textsPath.join(core->language() + ".xml");
+		if (Common::File::exists(textFilePath))
+			break;
+		core->language(allLangs[i]);
+		i++;
+	}
+	if (i == ARRAYSIZE(allLangs)) {
+		error("Couldn't find texts/[lang].xml for any language.");
+	}
+
+	_loc.load(textFilePath);
+	core->addLoc(&_loc);
+
+	const Common::Path helpMenuPath("menus/help/help_");
+	Common::Path helpMenuFilePath;
+	i = 0;
+	while (i < ARRAYSIZE(allLangs)) {
+		helpMenuFilePath = helpMenuPath.append(core->language() + ".xml");
+		if (Common::File::exists(helpMenuFilePath))
+			break;
+		core->language(allLangs[i]);
+		i++;
+	}
+	if (i == ARRAYSIZE(allLangs)) {
+		error("Couldn't find menus/help/help_[lang].xml for any language.");
+	}
+
+	_helpGui.load(helpMenuFilePath);
+
+	debug("TODO: set TeCore flags here? Do they do anything?");
+
+	// Game calls these here but does nothing with result?
+	//TeGetDeviceDPI();
+	//TeGetDeviceReferenceDPI();
+
+	_backLayout.setName("layoutBack");
+	_backLayout.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT);
+	_backLayout.setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
+	_mainWindow.addChild(&_backLayout);
+
+	_frontOrientationLayout.setName("orientationLayoutFront");
+	_frontOrientationLayout.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT);
+	_frontOrientationLayout.setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
+	_mainWindow.addChild(&_frontOrientationLayout);
+
+	_frontLayout.setName("layoutFront");
+	_frontLayout.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT);
+	_frontLayout.setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
+	_frontOrientationLayout.addChild(&_frontLayout);
+
+	_visFade.init();
+
+	_frontOrientationLayout.addChild(&_visFade._fadeCaptureSprite);
+	_frontOrientationLayout.addChild(&_visFade._blackFadeSprite);
+	_frontOrientationLayout.addChild(&_visFade._buttonLayout);
+
+	_frontLayout.addChild(&_appSpriteLayout);
+	_appSpriteLayout.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT);
+	_appSpriteLayout.setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
+	_appSpriteLayout.setVisible(false);
+
+	// Note: The games do some loading of a "version.ver" file here to add a
+	// watermark to the backLayout, but that file doesn't exist in any of the
+	// GOG games so it was probably only used during development.
+	const Common::Path verFilePath("version.ver");
+	if (Common::File::exists(verFilePath)) {
+		warning("Skipping doing anything with version.ver file");
+	}
+
+	_mouseCursorLayout.setName("mouseCursor");
+
+	// Not needed in scummvm:
+	g_system->showMouse(false);
+	//mainWindow->setNativeCursorVisible(false);
+
+	_mouseCursorLayout.load("pictures/cursor.png");
+	_mouseCursorLayout.setAnchor(TeVector3f32(0.3f, 0.1f, 0.0f));
+	_frontOrientationLayout.addChild(&_mouseCursorLayout);
+
+	_lockCursorButton.setName("lockCursorButton");
+	_lockCursorButton.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT);
+	_lockCursorButton.setSize(TeVector3f32(2.0f, 0.095f, 0.0f));
+	_lockCursorButton.setPositionType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT);
+	_lockCursorButton.setPosition(TeVector3f32(0.95f, 0.95f, 0.0f));
+	_lockCursorButton.setVisible(false);
+	_frontOrientationLayout.addChild(&_lockCursorButton);
+
+	_lockCursorFromActionButton.setName("lockCursorFromActionButton");
+	_lockCursorFromActionButton.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT);
+	_lockCursorFromActionButton.setSize(TeVector3f32(2.0f, 2.0f, 0.0f));
+	_lockCursorFromActionButton.setVisible(false);
+	_frontOrientationLayout.addChild(&_lockCursorFromActionButton);
+
+	_autoSaveIcon1.setName("autosaveIcon");
+	_autoSaveIcon1.setAnchor(TeVector3f32(0.5f, 0.5f, 0.0f));
+	_autoSaveIcon1.setPosition(TeVector3f32(0.2f, 0.9f, 0.0f));
+	_autoSaveIcon1.setSize(TeVector3f32(128.0f, 64.0f, 0.0f));
+	_autoSaveIcon1.load("menus/inGame/autosave_icon.png");
+	_autoSaveIcon1.setVisible(false);
+	_frontOrientationLayout.addChild(&_autoSaveIcon1);
+
+	_autoSaveIconAnim1._runTimer.pausable(false);
+	_autoSaveIconAnim1.pause();
+	_autoSaveIconAnim1._startVal = TeColor(255, 255, 255, 0);
+	_autoSaveIconAnim1._endVal = TeColor(255, 255, 255, 255);
+	Common::Array<float> curve;
+	curve.push_back(0.0f);
+	curve.push_back(1.0f);
+	curve.push_back(1.0f);
+	curve.push_back(0.0f);
+	_autoSaveIconAnim1.setCurve(curve);
+	_autoSaveIconAnim1._duration = 4000.0f;
+	_autoSaveIconAnim1._callbackObj = &_autoSaveIcon1;
+	_autoSaveIconAnim1._callbackMethod = &Te3DObject2::setColor;
+
+	_autoSaveIcon2.setName("autosaveIcon");
+	_autoSaveIcon2.setAnchor(TeVector3f32(0.5f, 0.5f, 0.0f));
+	_autoSaveIcon2.setPosition(TeVector3f32(0.2f, 0.7f, 0.0f));
+	_autoSaveIcon2.setSize(TeVector3f32(68.0f, 86.0f, 0.0f));
+	_autoSaveIcon2.load("menus/inGame/NoCel.png");
+	_autoSaveIcon2.setVisible(false);
+	_frontOrientationLayout.addChild(&_autoSaveIcon2);
+
+	_autoSaveIconAnim2._runTimer.pausable(false);
+	_autoSaveIconAnim2.pause();
+	_autoSaveIconAnim2._startVal = TeColor(255, 255, 255, 0);
+	_autoSaveIconAnim2._endVal = TeColor(255, 255, 255, 255);
+	_autoSaveIconAnim2.setCurve(curve);
+	_autoSaveIconAnim2._duration = 4000.0f;
+	_autoSaveIconAnim2._callbackObj = &_autoSaveIcon2;
+	_autoSaveIconAnim2._callbackMethod = &Te3DObject2::setColor;
+
+	_blackFadeAnimationFinishedSignal.add<Application>(this, &Application::onBlackFadeAnimationFinished);
+
+	g_engine->getInputMgr()->_mouseMoveSignal.add(this, &Application::onMousePositionChanged);
+
+	onMainWindowSizeChanged();
+	_splashScreens.enter();
+	_created = true;
+}
+
+void Application::destroy() {
+	error("TODO: Implement Application::destroy");
+}
+
+void Application::startGame(bool newGame, int difficulty) {
+	_appSpriteLayout.setVisible(false);
+	// TODO: there's another virtual call to appsprite surface here here.. not needed?
+	_appSpriteLayout.unload();
+	if (newGame)
+		_difficulty = difficulty;
+	g_engine->getGame()->enter(newGame);
+}
+
+void Application::resume() {
+	error("TODO: Implement Application::resume");
+}
+
+bool Application::run() {
+	if (_created) {
+		TeTimer::updateAll();
+		if (!_dontUpdateWhenApplicationPaused) {
+			// TODO: finish commented-out bits??
+			// we run the inputmgr separately.. is that ok?
+			//_inputmgr->update();
+			TeAnimation::updateAll();
+			//TeVideo::updateAll();
+		}
+		_captureFade = false;
+
+		TeRenderer *renderer = g_engine->getRenderer();
+		Game *game = g_engine->getGame();
+
+		renderer->reset();
+		game->update();
+		//_soundManager->update(soundmgr);
+		performRender();
+		if (game->_returnToMainMenu) {
+			game->leave(true);
+			if (!game->luaShowOwnerError()) {
+				_mainMenu.enter();
+			} else {
+				_ownerErrorMenu.enter();
+			}
+			game->_returnToMainMenu = false;
+		}
+		if (_finishedGame) {
+			game->leave(false);
+			_mainMenu.enter();
+			if (Common::File::exists("finalURL.lua")) {
+				TeLuaGUI finalGui;
+				finalGui.load("finalURL.lua");
+				/*TeVariant finalVal =*/ finalGui.value("finalURL");
+				debug("TODO: use final URL??");
+				// TODO: Not clear if this variant is ever used in original.
+				finalGui.unload();
+			}
+			_finishedGame = false;
+	  }
+	  InGameScene::updateScroll();
+	  TeObject::deleteNow();
+	}
+	return true;
+}
+
+void Application::suspend() {
+	error("TODO: Implement Application::suspend");
+}
+
+void Application::showNoCellIcon(bool show) {
+	if (!show) {
+		_autoSaveIconAnim2._repeatCount = 1;
+	} else {
+		_autoSaveIcon2.setVisible(true);
+		_autoSaveIcon2.setColor(TeColor(255, 255, 255, 255));
+		_autoSaveIconAnim2._repeatCount = -1;
+		_autoSaveIconAnim2.play();
+	}
+}
+
+void Application::showLoadingIcon(bool show) {
+	if (!show) {
+		_autoSaveIconAnim1._repeatCount = 1;
+	} else {
+		_autoSaveIcon1.setVisible(true);
+		_autoSaveIcon1.setColor(TeColor(255, 255, 255, 255));
+		_autoSaveIconAnim1._repeatCount = -1;
+		_autoSaveIconAnim1.play();
+	}
+}
+
+void Application::saveCorrupted(const Common::String &fname) {
+	error("TODO: Implement Application::showLoadingIcon");
+}
+
+void Application::drawBack() {
+	_mainWindowCamera->apply();
+	_backLayout.draw();
+	TeCamera::restore();
+	g_engine->getRenderer()->loadIdentityMatrix();
+}
+
+void Application::drawFront() {
+	_mainWindowCamera->apply();
+	_frontOrientationLayout.draw();
+	TeCamera::restore();
+	g_engine->getRenderer()->loadIdentityMatrix();
+}
+
+void Application::performRender() {
+	Game *game = g_engine->getGame();
+	TeRenderer *renderer = g_engine->getRenderer();
+
+	if (game->running() && _inGameScene._character && true /*some other ingamescene things*/) {
+		renderer->shadowMode(TeRenderer::ShadowMode1);
+		//_inGameScene._charactersShadow->createTexture(_inGameScene);
+		renderer->shadowMode(TeRenderer::ShadowMode0);
+		error("TODO: Implement characters shadow thing here.");
+	}
+
+	drawBack();
+
+	renderer->renderTransparentMeshes();
+	renderer->clearBuffer(GL_ACCUM);
+
+	if (game->running() && _inGameScene._character && true /*some other ingamescene things*/) {
+		TeIntrusivePtr<TeCamera> currentCamera = _inGameScene.currentCamera();
+		if (currentCamera) {
+			currentCamera->apply();
+			renderer->shadowMode(TeRenderer::ShadowMode2);
+			//_inGameScene._charactersShadow->draw(_inGameScene);
+			renderer->shadowMode(TeRenderer::ShadowMode0);
+			error("TODO: Implement characters shadow thing here.");
+		}
+		game->draw();
+	}
+
+	renderer->renderTransparentMeshes();
+	renderer->clearBuffer(GL_ACCUM);
+	drawFront();
+	renderer->renderTransparentMeshes();
+	// What gets called here??
+	//_inGameScene.removeModel(const Common::String &name)
+	g_system->updateScreen();
+}
+
+//void Application::preloadTextrue(); does nothing..
+
+void Application::fade() {
+	_visFade.animateFade();
+}
+
+void Application::blackFade() {
+	_visFade.animateBlackFade();
+}
+
+void Application::captureFade() {
+	if (_captureFade)
+		return;
+	_captureFade = true;
+	performRender();
+	_visFade.captureFrame();
+}
+
+bool Application::isFading() {
+	warning("Application::isFading: Check field here.");
+	if (true /* field_0xb1e1? */)
+		return _visFade.fading();
+	return false;
+}
+
+bool Application::onBlackFadeAnimationFinished() {
+	error("TODO: Implement Application::onBlackFadeAnimationFinished");
+	return false;
+}
+
+bool Application::onMainWindowSizeChanged() {
+	// This sets HD or SD "definition" in the core depending on device DPI.
+	// For now just default to SD.
+	debug("mainWindowSizeChanged: defaulting to SD.");
+	g_engine->getCore()->fileFlagSystemSetFlag("definition", "SD");
+	return false;
+}
+
+bool Application::onMousePositionChanged(const Common::Point &p) {
+	const TeVector3f32 mainWinSize = _mainWindow.size();
+	const TeVector3f32 newCursorPos(p.x / mainWinSize.x(), p.y / mainWinSize.y(), 0.0);
+	_mouseCursorLayout.setPosition(newCursorPos);
+	return false;
+}
+
+bool Application::isLockCursor() {
+	return _lockCursorButton.visible() || _lockCursorFromActionButton.visible();
+}
+
+bool Application::isLockPad() {
+	error("TODO: Implement Application::isLockPad");
+	return false;
+}
+
+void Application::lockCursor(bool lock) {
+	error("TODO: Implement Application::lockCursor");
+}
+
+void Application::lockCursorFromAction(bool lock) {
+	error("TODO: Implement Application::lockCursorFromAction");
+}
+
+void Application::loadOptions(const Common::String &fname) {
+	// TODO: Maybe load options here - original uses an
+	// xml file but we would want confman.
+	warning("TODO: Implement Application::loadOptions %s", fname.c_str());
+}
+
+void Application::saveOptions(const Common::String &fname) {
+	warning("TODO: Implement Application::saveOptions %s", fname.c_str());
+}
+
+Common::String Application::getHelpText(const Common::String &key) {
+	return _helpGui.value(key);
+}
+
+const char *Application::inAppUnlockFullVersionID() {
+	error("TODO: Implement Application::inAppUnlockFullVersionID");
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
new file mode 100644
index 00000000000..feebf411639
--- /dev/null
+++ b/engines/tetraedge/game/application.h
@@ -0,0 +1,164 @@
+/* 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_APPLICATION_H
+#define TETRAEDGE_GAME_APPLICATION_H
+
+#include "common/str.h"
+#include "common/ptr.h"
+
+#include "tetraedge/game/bonus_menu.h"
+#include "tetraedge/game/credits.h"
+#include "tetraedge/game/global_bonus_menu.h"
+#include "tetraedge/game/main_menu.h"
+#include "tetraedge/game/loc_file.h"
+#include "tetraedge/game/owner_error_menu.h"
+#include "tetraedge/game/splash_screens.h"
+#include "tetraedge/game/in_game_scene.h"
+
+#include "tetraedge/te/te_visual_fade.h"
+#include "tetraedge/te/te_music.h"
+#include "tetraedge/te/te_xml_gui.h"
+#include "tetraedge/te/te_font3.h"
+
+namespace Common {
+struct Event;
+}
+
+namespace Tetraedge {
+
+class Application {
+public:
+	Application();
+
+	void create();
+	void destroy();
+
+	void startGame(bool newGame, int difficulty);
+	void resume();
+	bool run();
+	void suspend();
+	void showNoCellIcon(bool show);
+	void showLoadingIcon(bool show);
+	void saveCorrupted(const Common::String &fname);
+
+	void drawBack();
+	void drawFront();
+	void performRender();
+	//void preloadTextrue(); does nothing..
+
+	void fade();
+	void blackFade();
+	void captureFade();
+	bool isFading();
+	bool onBlackFadeAnimationFinished();
+	bool onMainWindowSizeChanged();
+	bool onMousePositionChanged(const Common::Point &p);
+
+	bool isLockCursor();
+	bool isLockPad();
+	void lockCursor(bool lock);
+	void lockCursorFromAction(bool lock);
+
+	void loadOptions(const Common::String &fname);
+	void saveOptions(const Common::String &fname);
+
+	Common::String getHelpText(const Common::String &key);
+
+	const char *inAppUnlockFullVersionID();
+
+	BonusMenu &bonusMenu() { return _bonusMenu; }
+	GlobalBonusMenu &globalBonusMenu() { return _globalBonusMenu; }
+	MainMenu &mainMenu() { return _mainMenu; }
+	TeMusic &music() { return _music; }
+	Credits &credits() { return _credits; }
+	TeVisualFade &visualFade() { return _visFade; }
+	TeSpriteLayout &appSpriteLayout() { return _appSpriteLayout; }
+	TeSpriteLayout &mouseCursorLayout() { return _mouseCursorLayout; }
+	const Common::String getVersionString() const { return _versionString; }
+	TeLayout &getMainWindow() { return _mainWindow; }
+	void setTutoActivated(bool val) { _tutoActivated = val; }
+	TeCamera *mainWindowCamera() { return _mainWindowCamera.get(); }
+	Common::Array<Common::String> &unrecalAnims() { return _unrecalAnims; }
+	int difficulty() const { return _difficulty; }
+
+	// TODO: Add accessors for these and make them private.
+	bool _finishedGame;
+	bool _finishedFremium;
+	TeLayout _frontLayout;
+	TeLayout _frontOrientationLayout;
+	TeLayout _backLayout;
+	TeButtonLayout _lockCursorButton;
+	LocFile _loc;
+	Common::String _firstWarpPath;
+	Common::String _firstZone;
+	Common::String _firstScene;
+
+private:
+	TeVisualFade _visFade;
+	TeMusic _music;
+	TeSpriteLayout _appSpriteLayout;
+	TeSpriteLayout _mouseCursorLayout;
+	TeButtonLayout _lockCursorFromActionButton;
+	TeSpriteLayout _autoSaveIcon1;
+	TeSpriteLayout _autoSaveIcon2;
+
+	Common::String _applicationTitle;
+	Common::String _versionString;
+
+	Common::Array<Common::String> _unrecalAnims;
+
+	TeCurveAnim2<Te3DObject2, TeColor> _autoSaveIconAnim1;
+	TeCurveAnim2<Te3DObject2, TeColor> _autoSaveIconAnim2;
+
+	TeSignal0Param _blackFadeAnimationFinishedSignal;
+
+	Common::SharedPtr<TeCamera> _mainWindowCamera; // TODO: should be part of TeMainWindow.
+	TeLayout _mainWindow; // TODO: should be a specialised class.
+
+	GlobalBonusMenu _globalBonusMenu;
+	BonusMenu _bonusMenu;
+	MainMenu _mainMenu;
+	Credits _credits;
+	OwnerErrorMenu _ownerErrorMenu;
+	SplashScreens _splashScreens;
+	InGameScene _inGameScene;
+
+	TeIntrusivePtr<TeFont3> _fontComic;
+	TeIntrusivePtr<TeFont3> _fontArgh;
+	TeIntrusivePtr<TeFont3> _fontArial;
+	TeIntrusivePtr<TeFont3> _fontChaucer;
+	TeIntrusivePtr<TeFont3> _fontColaborate;
+	TeIntrusivePtr<TeFont3> _fontProDisplay;
+
+	bool _captureFade;
+	bool _created;
+	bool _tutoActivated;
+
+	int _difficulty;
+
+	TeXmlGui _helpGui;
+	static bool _dontUpdateWhenApplicationPaused;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_APPLICATION_H
diff --git a/engines/tetraedge/game/billboard.cpp b/engines/tetraedge/game/billboard.cpp
new file mode 100644
index 00000000000..e8529237fe5
--- /dev/null
+++ b/engines/tetraedge/game/billboard.cpp
@@ -0,0 +1,55 @@
+/* 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 "common/textconsole.h"
+#include "tetraedge/game/billboard.h"
+
+namespace Tetraedge {
+
+Billboard::Billboard() {
+}
+
+bool Billboard::load(const Common::String &path) {
+	error("TODO: implement Billboard::load");
+	return false;
+}
+
+void Billboard::calcVertex() {
+	error("TODO: implement Billboard::calcVertex");
+}
+
+void Billboard::position(const TeVector3f32 &pos) {
+	_pos = pos;
+	calcVertex();
+}
+
+void Billboard::position2(const TeVector3f32 &pos) {
+	_pos2 = pos;
+	_hasPos2 = true;
+	calcVertex();
+}
+
+void Billboard::size(const TeVector2f32 &size) {
+	_size = size;
+	calcVertex();
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/billboard.h b/engines/tetraedge/game/billboard.h
new file mode 100644
index 00000000000..bd39e309de0
--- /dev/null
+++ b/engines/tetraedge/game/billboard.h
@@ -0,0 +1,53 @@
+/* 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_BILLBOARD_H
+#define TETRAEDGE_GAME_BILLBOARD_H
+
+#include "common/str.h"
+
+#include "tetraedge/te/te_object.h"
+#include "tetraedge/te/te_vector2f32.h"
+#include "tetraedge/te/te_vector3f32.h"
+
+namespace Tetraedge {
+
+class Billboard : public TeObject {
+public:
+	Billboard();
+
+	bool load(const Common::String &path);
+
+	void calcVertex();
+	void position(const TeVector3f32 &pos);
+	void position2(const TeVector3f32 &pos);
+	void size(const TeVector2f32 &size);
+
+private:
+	TeVector3f32 _pos;
+	TeVector3f32 _pos2;
+	TeVector2f32 _size;
+	bool _hasPos2;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_BILLBOARD_H
diff --git a/engines/tetraedge/game/bonus_menu.cpp b/engines/tetraedge/game/bonus_menu.cpp
new file mode 100644
index 00000000000..d91c2c66437
--- /dev/null
+++ b/engines/tetraedge/game/bonus_menu.cpp
@@ -0,0 +1,102 @@
+/* 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/game/bonus_menu.h"
+
+#include "common/textconsole.h"
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+//#include "tetraedge/te/te_input_manager"
+
+namespace Tetraedge {
+
+BonusMenu::BonusMenu() {
+}
+
+void BonusMenu::enter(const Common::String &scriptName) {
+	error("TODO: implement me.");
+}
+
+void BonusMenu::enter() {
+	error("TODO: implement me.");
+}
+
+void BonusMenu::leave() {
+	for (auto *s : _saveButtons) {
+		delete s;
+	}
+	_saveButtons.clear();
+	warning("TODO: remove oMouseMove signal from inputmgr.");
+	TeLuaGUI::unload();
+}
+
+bool BonusMenu::onLeftButton() {
+	error("TODO: implement me.");
+	return false;
+}
+
+bool BonusMenu::onMouseMove() {
+	error("TODO: implement me.");
+	return false;
+}
+
+bool BonusMenu::onPictureButton() {
+	error("TODO: implement me.");
+	return false;
+}
+
+bool BonusMenu::onQuitButton() {
+	Application *app = g_engine->getApplication();
+	assert(app);
+
+	app->captureFade();
+	leave();
+	app->globalBonusMenu().enter();
+	app->fade();
+	return true;
+}
+
+bool BonusMenu::onRightButton() {
+	error("TODO: implement me.");
+	return false;
+}
+
+bool BonusMenu::onSideButtonDown() {
+	/*
+	TeInputManager *inputmgr = g_engine->getInputManager();
+	 TeVector2s32 mousepos = inputmgr->getMousePos();
+	 _slideBtnStartMousePos = mousepos;
+	 buttonLayout("slideButton");
+	 // TODO set some flag in super (TeLuaGUI)
+	*/
+	error("TODO: implement me.");
+	return false;
+}
+
+Common::String BonusMenu::SaveButton::path() const {
+	return Common::String("Backup/") + name() + ".xml";
+}
+
+bool BonusMenu::SaveButton::onLoadSave() {
+	error("TODO: implement me.");
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/bonus_menu.h b/engines/tetraedge/game/bonus_menu.h
new file mode 100644
index 00000000000..0c87516159b
--- /dev/null
+++ b/engines/tetraedge/game/bonus_menu.h
@@ -0,0 +1,68 @@
+/* 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_BONUS_MENU_H
+#define TETRAEDGE_GAME_BONUS_MENU_H
+
+#include "common/array.h"
+#include "common/str.h"
+#include "tetraedge/te/te_layout.h"
+#include "tetraedge/te/te_lua_gui.h"
+#include "tetraedge/te/te_vector2s32.h"
+
+namespace Tetraedge {
+
+class BonusMenu : public TeLuaGUI {
+public:
+	BonusMenu();
+
+	class SaveButton : public TeLayout {
+	public:
+		bool onLoadSave();
+		Common::String path() const;
+	};
+
+	virtual void enter() override;
+	virtual void enter(const Common::String &scriptName);
+	void leave() override;
+
+	void loadGame(Common::String &name) {
+		_gameName = name;
+	}
+
+	bool onLeftButton();
+	bool onMouseMove();
+	bool onPictureButton();
+	bool onQuitButton();
+	bool onRightButton();
+	bool onSideButtonDown();
+
+	// empty? bool onLoadGameConfirmed() { };
+
+private:
+	Common::Array<SaveButton *> _saveButtons;
+	TeVector2s32 _slideBtnStartMousePos;
+	Common::String _gameName;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_BONUS_MENU_H
diff --git a/engines/tetraedge/game/cellphone.cpp b/engines/tetraedge/game/cellphone.cpp
new file mode 100644
index 00000000000..4f836c1c473
--- /dev/null
+++ b/engines/tetraedge/game/cellphone.cpp
@@ -0,0 +1,145 @@
+/* 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/te/te_core.h"
+
+#include "tetraedge/game/cellphone.h"
+#include "tetraedge/te/te_text_layout.h"
+
+namespace Tetraedge {
+
+Cellphone::Cellphone() : _nextNumber(0) {
+}
+
+bool Cellphone::addNumber(const Common::String &num) {
+	for (const Common::String &addedNum : _addedNumbers) {
+		if (addedNum == num)
+			return false;
+	}
+
+	TeTextLayout *layout = new TeTextLayout();
+	static const Common::String namePrefix("numRepertoire");
+	layout->setName(namePrefix + num);
+	layout->setSizeType(RELATIVE_TO_PARENT);
+	layout->setAnchor(TeVector3f32(0.5f, 0.0f, 0.0f));
+	layout->setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
+	layout->setPosition(TeVector3f32(0.5f, 0.08f, 0.0f));
+	layout->setTextSizeType(1);
+	layout->setTextSizeProportionalToWidth(46);
+	Common::String val("Unknown");
+	Common::String *locNum = g_engine->getCore()->loc()->text(num);
+	if (locNum)
+		val = *locNum;
+
+	layout->setText(_gui.value("textAttributs").toString() + val);
+	layout->setVisible(true);
+	_textLayoutArray.push_back(layout);
+	_addedNumbers.push_back(num);
+
+	TeSpriteLayout *sprite = _gui.spriteLayoutChecked("numRepertoire");
+	sprite->addChild(layout);
+	return true;
+}
+
+void Cellphone::currentPage(int offset) {
+	error("TODO: implement Cellphone::currentPage");
+}
+
+void Cellphone::enter() {
+	error("TODO: implement Cellphone::enter");
+}
+
+void Cellphone::leave() {
+	error("TODO: implement Cellphone::leave");
+}
+
+void Cellphone::load() {
+	_nextNumber = 0;
+	TeButtonLayout *btnlayout;
+	_gui.load("menus/cellphone.lua");
+	btnlayout = _gui.buttonLayoutChecked("haut");
+	btnlayout->onMouseClickValidated().add(this, &Cellphone::onPreviousNumber);
+	btnlayout = _gui.buttonLayoutChecked("bas");
+	btnlayout->onMouseClickValidated().add(this, &Cellphone::onNextNumber);
+	btnlayout = _gui.buttonLayoutChecked("appeler");
+	btnlayout->onMouseClickValidated().add(this, &Cellphone::onCallNumberValidated);
+	btnlayout = _gui.buttonLayoutChecked("fermer");
+	btnlayout->onMouseClickValidated().add(this, &Cellphone::onCloseButtonValidated);
+	btnlayout = _gui.buttonLayoutChecked("background");
+	btnlayout->setVisible(false);
+}
+
+void Cellphone::loadFromBackup(const Common::XMLParser::ParserNode *node) {
+	/*
+	 basic algorithm:
+	child = node->lastChild;
+	while (child != nullptr) {
+		if (child->type == ELEMENT) {
+			if (if child->userData == "Number") {
+				addNumber(this, child->getAttribute("num"));
+			}
+		}
+	child = child->prev;
+	}*/
+}
+
+bool Cellphone::onCallNumberValidated() {
+	_onCallNumberSignal.call(_addedNumbers[_nextNumber]);
+	return false;
+}
+
+bool Cellphone::onCloseButtonValidated() {
+	_gui.buttonLayoutChecked("background")->setVisible(false);
+	return false;
+}
+
+bool Cellphone::onNextNumber() {
+	error("TODO: work out how the max num works here.");
+	/*
+	int numoffset = _nextNumber + 1;
+	if (numoffset < _maxnum) {
+		currentPage(numoffset);
+	}*/
+	return false;
+}
+
+bool Cellphone::onPreviousNumber() {
+	int numoffset = _nextNumber - 1;
+	if (numoffset >= 0) {
+	  currentPage(numoffset);
+	}
+	return false;
+}
+
+void Cellphone::saveToBackup(Common::XMLParser::ParserNode *xmlnode) {
+	error("TODO: implement Cellphone::saveToBackup");
+}
+
+void Cellphone::setVisible(bool visible) {
+	_gui.buttonLayoutChecked("background")->setVisible(visible);
+}
+
+void Cellphone::unload() {
+	leave();
+	_gui.unload();
+}
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/cellphone.h b/engines/tetraedge/game/cellphone.h
new file mode 100644
index 00000000000..35c5e68f067
--- /dev/null
+++ b/engines/tetraedge/game/cellphone.h
@@ -0,0 +1,78 @@
+/* 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_CELLPHONE_H
+#define TETRAEDGE_GAME_CELLPHONE_H
+
+#include "common/array.h"
+#include "common/callback.h"
+#include "common/str.h"
+#include "common/xmlparser.h"
+
+#include "tetraedge/te/te_layout.h"
+#include "tetraedge/te/te_text_layout.h"
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class Cellphone : public TeLayout {
+public:
+	Cellphone();
+
+	bool addNumber(const Common::String &num);
+	void currentPage(int offset);
+
+	void enter();
+	void leave();
+
+	void load();
+	void loadFromBackup(const Common::XMLParser::ParserNode *node);
+
+	bool onCallNumberValidated();
+	bool onCloseButtonValidated();
+	bool onNextNumber();
+	bool onPreviousNumber();
+
+	void saveToBackup(Common::XMLParser::ParserNode *xmlnode);
+	void setVisible(bool visible);
+
+	TeSignal1Param<Common::String> &onCallNumber() {
+		return _onCallNumberSignal;
+	}
+
+	void unload();
+
+	TeLuaGUI &gui() { return _gui; }
+
+private:
+
+	int _nextNumber;
+	Common::Array<TeTextLayout*> _textLayoutArray;
+	Common::Array<Common::String> _addedNumbers;
+
+	TeSignal1Param<Common::String> _onCallNumberSignal;
+
+	TeLuaGUI _gui;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_CELLPHONE_H
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
new file mode 100644
index 00000000000..aa05583d4f5
--- /dev/null
+++ b/engines/tetraedge/game/character.cpp
@@ -0,0 +1,431 @@
+/* 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 "common/hashmap.h"
+#include "common/path.h"
+#include "common/file.h"
+#include "common/debug.h"
+
+#include "tetraedge/game/character.h"
+#include "tetraedge/game/character_settings_xml_parser.h"
+#include "tetraedge/te/te_model_animation.h"
+
+namespace Tetraedge {
+
+/*static*/ Common::HashMap<Common::String, Character::CharacterSettings> *Character::_globalCharacterSettings = nullptr;
+
+/*static*/ Common::Array<Character::AnimCacheElement> Character::_animCache;
+uint Character::_animCacheSize = 0;
+
+void Character::CharacterSettings::clear() {
+	_name.clear();
+	_modelFileName.clear();
+	_defaultScale = TeVector3f32();
+	_walkFileName.clear();
+	_walkSettings.clear();
+	_walkSpeed = 0.0f;
+	_cutSceneCurveDemiPosition = TeVector3f32();
+	_defaultEyes.clear();
+	_defaultMouth.clear();
+	_defaultBody.clear();
+}
+
+void Character::WalkSettings::clear() {
+	for (int i = 0; i < 4; i++) {
+		_walkParts[i] = AnimSettings();
+	}
+}
+
+Character::Character() : _curveOffset(0), _lastFrame(-1), _callbacksChanged(false),
+_missingCurrentAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"), _needsSomeUpdate(false),
+_stepSound1("sounds/SFX/PAS_H_BOIS1.ogg"), _stepSound2("sounds/SFX/PAS_H_BOIS2.ogg"),
+_freeMoveZone(nullptr) {
+}
+
+void Character::addCallback(const Common::String &key, const Common::String &s2, float f1, float f2) {
+	/*Callback *c = new Callback();
+	c->x = (int)f1;
+	c->y = (int)f2;
+	c->f = (f2 == -1.0 ? -NAN : 0.0f;*/
+	error("TODO: Implement Character::addCallback");
+}
+
+/*static*/ void Character::animCacheFreeAll() {
+	for (const auto &entry : _animCache)
+		_animCacheSize -= entry._size;
+	_animCache.clear();
+}
+
+/*static*/ void Character::animCacheFreeOldest() {
+	_animCacheSize -= _animCache[_animCache.size() - 1]._size;
+	_animCache.pop_back();
+}
+
+/*static*/ TeIntrusivePtr<TeModelAnimation> Character::animCacheLoad(const Common::Path &path) {
+	static Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>> _cache;
+
+	const Common::String pathStr = path.toString();
+	if (_cache.contains(pathStr))
+		return _cache.getVal(pathStr);
+
+	TeIntrusivePtr<TeModelAnimation> modelAnim = new TeModelAnimation();
+	modelAnim->load(path);
+
+	_cache.setVal(pathStr, modelAnim);
+	return modelAnim;
+}
+
+float Character::animLength(const TeModelAnimation &modelanim, long bone, long lastframe) {
+	int last = modelanim.lastFrame();
+	if (lastframe > last)
+		lastframe = last;
+	int first = modelanim.firstFrame();
+	const TeVector3f32 starttrans = translationVectorFromAnim(modelanim, bone, first);
+	const TeVector3f32 endtrans = translationVectorFromAnim(modelanim, bone, last);
+	const TeVector3f32 secondtrans = translationVectorFromAnim(modelanim, bone, first + 1);
+	return ((endtrans.z() - starttrans.z()) + secondtrans.z()) - starttrans.z();
+}
+
+float Character::animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe) {
+	if (animname.empty()) {
+		*pframeCount = 0;
+		return 0.0f;
+	}
+	TeIntrusivePtr<TeModelAnimation> anim = _model->anim();
+	if (!anim->_loadedPath.toString().contains(animname)) {
+		Common::Path animpath("models/Anims");
+		animpath.joinInPlace(animname);
+		anim = animCacheLoad(animpath);
+		if (!anim)
+			error("Character::animLengthFromFile couldn't load anim %s", animname.c_str());
+	}
+
+	// The "Pere" or "father" bone is the root.
+	float animLen = animLength(*anim, anim->findBone("Pere"), lastframe);
+	int frameCount = anim->lastFrame() + 1 - anim->firstFrame();
+	*pframeCount = frameCount;
+
+	// TODO: Check this maths.. is this really what it does?
+	return animLen * _model->scale().z();
+}
+
+bool Character::blendAnimation(const Common::String &animname, float param_2, bool param_3, bool param_4) {
+	error("TODO: Implement Character::blendAnimation");
+}
+
+TeVector3f32 Character::correctPosition(const TeVector3f32 &pos) {
+	error("TODO: Implement Character::correctPosition");
+}
+
+float Character::curveOffset() {
+	return _curveOffset;
+}
+
+void Character::deleteAllCallback() {
+	_callbacksChanged = true;
+	for (auto &pair : _callbacks) {
+		for (Callback *c : pair._value) {
+			delete c;
+		}
+	}
+	_callbacks.clear();
+}
+
+void Character::deleteAnim() {
+	error("TODO: Implement Character::deleteAnim");
+}
+
+void Character::deleteCallback(const Common::String &str1, const Common::String &str2, float f) {
+	error("TODO: Implement Character::deleteCallback");
+}
+
+//static bool deserialize(TiXmlElement *param_1, Walk *param_2);
+void Character::endMove() {
+	error("TODO: Implement Character::endMove.");
+}
+
+const Character::WalkSettings *Character::getCurrentWalkFiles() {
+	for (const auto & walkSettings : _characterSettings._walkSettings) {
+		if (walkSettings._key == _walkModeStr)
+			return &walkSettings._value;
+	}
+	return nullptr;
+}
+
+bool Character::isFramePassed(uint frameno) {
+	error("TODO: Implement Character::isFramePassed.");
+}
+
+bool Character::isWalkEnd() {
+	error("TODO: Implement Character::isWalkEnd.");
+	return false;
+}
+
+bool Character::leftStepFrame(enum Character::WalkPart walkpart) {
+	error("TODO: Implement Character::leftStepFrame.");
+	return false;
+}
+
+bool Character::rightStepFrame(enum Character::WalkPart walkpart) {
+	error("TODO: Implement Character::rightStepFrame.");
+	return false;
+}
+
+bool Character::loadModel(const Common::String &name, bool param_2) {
+	assert(_globalCharacterSettings);
+	if (_model) {
+		//_model->bonesUpdateSignal().remove(this, &Character::onBonesUpdate);
+	}
+	_model = new TeModel();
+	//_model->bonesUpdateSignal().add(this, &Character::onBonesUpdate);
+
+	if (!_globalCharacterSettings->contains(name))
+		return false;
+
+	_characterSettings = _globalCharacterSettings->getVal(name);
+	_model->_texturePath = Common::Path("models/Textures");
+	_model->_enableLights = true;
+	Common::Path modelPath("models");
+	modelPath.joinInPlace(_characterSettings._modelFileName);
+	if (!_model->load(modelPath))
+		return false;
+
+	_model->setName(name);
+	_model->setScale(_characterSettings._defaultScale);
+
+	for (unsigned int i = 0; i < _model->_meshes.size(); i++)
+		_model->_meshes[i].setVisible(true);
+
+	_model->setVisibleByName("_B_", false);
+	_model->setVisibleByName("_Y_", false);
+
+	_model->setVisibleByName(_characterSettings._defaultEyes, true);
+	_model->setVisibleByName(_characterSettings._defaultMouth, true);
+
+	setAnimation(_characterSettings._walkFileName, true, false, false, -1, 9999);
+
+	_walkPart0AnimLen = animLengthFromFile(walkAnim(WalkPart_Start), &_walkPart0AnimFrameCount, 9999);
+	_walkPart3AnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkPart3AnimFrameCount, 9999);
+	_walkPart1AnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkPart1AnimFrameCount, 9999);
+
+	TeIntrusivePtr<Te3DTexture> shadow = new Te3DTexture();
+	shadow->load("models/Textures/simple_shadow_alpha.tga");
+
+	for (int i = 0; i < 2; i++) {
+		TeModel *pmodel = new TeModel();
+		_shadowModel[i] = pmodel;
+		pmodel->setName("Shadow");
+		Common::Array<TeVector3f32> arr;
+		arr.resize(4);
+		arr[0] = TeVector3f32(-60.0, 0.0, -60.0);
+		arr[1] = TeVector3f32(-60.0, 0.0, 60.0);
+		arr[2] = TeVector3f32(60.0, 0.0, -60.0);
+		arr[3] = TeVector3f32(60.0, 0.0, 60.0);
+		pmodel->setQuad(shadow, arr, TeColor(0xff,0xff,0xff,0x50));
+	}
+	return true;
+}
+
+/*static*/ bool Character::loadSettings(const Common::String &path) {
+	CharacterSettingsXmlParser parser;
+	parser.setAllowText();
+	if (_globalCharacterSettings)
+		delete _globalCharacterSettings;
+	_globalCharacterSettings = new Common::HashMap<Common::String, CharacterSettings>();
+	parser.setCharacterSettings(_globalCharacterSettings);
+
+	// WORKAROUND: This file contains invalid comments
+	// eg, <!--------- and a comment-inside-a-comment.
+	// patch them before parsing.
+	Common::File xmlFile;
+	if (!xmlFile.open(path))
+		error("Character::loadSettings: Can't open %s", path.c_str());
+	const uint32 bufsize = xmlFile.size();
+	char *buf = new char[bufsize+1];
+	buf[bufsize] = '\0';
+	xmlFile.read(buf, bufsize);
+	Common::String fixedbuf(buf);
+	uint32 offset = fixedbuf.find("------------");
+	while (offset != Common::String::npos) {
+		fixedbuf.replace(offset, 12, "--");
+		offset = fixedbuf.find("------------");
+	}
+
+	// Big HACK: Remove the embedded comment in this config.
+	offset = fixedbuf.find("<!--<walk>");
+	if (offset != Common::String::npos) {
+		uint32 endOffset = fixedbuf.find(" -->", offset);
+		if (endOffset != Common::String::npos) {
+			uint32 realEndOffset = fixedbuf.find("walk>-->", endOffset);
+			if (realEndOffset  != Common::String::npos && realEndOffset > endOffset) {
+				fixedbuf.replace(offset, endOffset - offset, "<!-- ");
+			}
+		}
+	}
+
+	if (!parser.loadBuffer((unsigned char *)fixedbuf.c_str(), bufsize))
+		error("Character::loadSettings: Can't open %s", path.c_str());
+
+	if (!parser.parse())
+		error("Character::loadSettings: Can't parse %s", path.c_str());
+
+	return false;
+}
+
+bool Character::onBonesUpdate(const Common::String &param_1, const TeMatrix4x4 *param_2) {
+	error("TODO: Implement Character::onBonesUpdate");
+	return false;
+}
+
+bool Character::onModelAnimationFinished() {
+	error("TODO: Implement Character::onModelAnimationFinished");
+	return false;
+}
+
+void Character::permanentUpdate() {
+	error("TODO: Implement Character::permanentUpdate.");
+}
+
+void Character::placeOnCurve(const TeBezierCurve &curve) {
+	_curve = curve;
+	updatePosition(_curveOffset);
+}
+
+void Character::removeAnim() {
+	if (_curModelAnim) {
+		_curModelAnim->onFinished().remove(this, &Character::onModelAnimationFinished);
+	}
+	_model->removeAnim();
+	if (_curModelAnim) {
+		_curModelAnim.release();
+	}
+}
+
+void Character::removeFromCurve() {
+	error("TODO: Implement Character::removeFromCurve.");
+}
+
+bool Character::setAnimation(const Common::String &name, bool repeat, bool param_3, bool unused, int startFrame, int endFrame)  {
+	if (name.empty())
+		return false;
+
+	Common::Path animPath("models/Anims");
+	animPath.joinInPlace(name);
+	bool validAnim = (name.contains(_characterSettings._walkFileName) ||
+					  name.contains(walkAnim(WalkPart_Start)) ||
+					  name.contains(walkAnim(WalkPart_Loop)) ||
+					  name.contains(walkAnim(WalkPart_EndD)) ||
+					  name.contains(walkAnim(WalkPart_EndG)));
+	_missingCurrentAnim = !validAnim;
+
+	if (_curModelAnim) {
+		_curModelAnim->onFinished().remove(this, &Character::onModelAnimationFinished);
+		_curModelAnim->unbind();
+	}
+
+	_curModelAnim = animCacheLoad(animPath);
+	_curModelAnim->bind(_model);
+	_curModelAnim->setFrameLimits(startFrame, endFrame);
+	_model->setAnim(_curModelAnim, repeat);
+	_lastFrame = -1;
+	_curModelAnim->play();
+	_setAnimName = name;
+	_curAnimName = name;
+	_someRepeatFlag = !(repeat | !param_3);
+
+	return true;
+}
+
+void Character::setAnimationSound(const Common::String &name, uint param_2)  {
+	error("TODO: Implement Character::setAnimationSound.");
+}
+
+void Character::setCurveOffset(float offset) {
+	_curveOffset = offset;
+	updatePosition(offset);
+}
+
+void Character::setFreeMoveZone(const Common::SharedPtr<TeFreeMoveZone> &zone) {
+	_freeMoveZone = zone;
+}
+
+void Character::setStepSound(const Common::String &stepSound1, const Common::String &stepSound2) {
+	_stepSound1 = stepSound1;
+	_stepSound2	= stepSound2;
+}
+
+bool Character::setShadowVisible(bool visible) {
+	_shadowModel[0]->setVisible(visible);
+	_shadowModel[1]->setVisible(visible);
+	return false;
+}
+
+float Character::speedFromAnim(double movepercent) {
+	error("TODO: Implement Character::speedFromAnim");
+	return 0;
+}
+
+float Character::translationFromAnim(const TeModelAnimation &anim, long bone, long param_3) {
+	return translationVectorFromAnim(anim, bone, param_3).z();
+}
+
+TeVector3f32 Character::translationVectorFromAnim(const TeModelAnimation &anim, long bone, long frame)  {
+	const TeTRS trs = trsFromAnim(anim, bone, frame);
+	return trs.getTranslation();
+}
+
+TeTRS Character::trsFromAnim(const TeModelAnimation &anim, long bone, long frame)  {
+	if (bone == -1)
+		return TeTRS();
+
+	return anim.getTRS(bone, frame, false);
+}
+
+void Character::update(double percentval)  {
+	error("TODO: Implement Character::update");
+}
+
+void Character::updateAnimFrame()  {
+	error("TODO: Implement Character::updateAnimFrame");
+}
+
+void Character::updatePosition(float curveOffset) {
+	error("TODO: Implement Character::updatePosition");
+}
+
+Common::String Character::walkAnim(Character::WalkPart part)  {
+	Common::String result;
+	const Character::WalkSettings *settings = getCurrentWalkFiles();
+	if (settings) {
+		return settings->_walkParts[(int)part]._file;
+	}
+	return result;
+}
+
+void Character::walkMode(const Common::String &mode) {
+	error("TODO: Implement Character::walkMode");
+}
+
+void Character::walkTo(float param_1, bool param_2) {
+	error("TODO: Implement Character::walkTo");
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
new file mode 100644
index 00000000000..2556f305845
--- /dev/null
+++ b/engines/tetraedge/game/character.h
@@ -0,0 +1,197 @@
+/* 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_CHARACTER_H
+#define TETRAEDGE_GAME_CHARACTER_H
+
+#include "common/array.h"
+#include "common/str.h"
+#include "common/types.h"
+#include "common/ptr.h"
+#include "tetraedge/te/te_animation.h"
+#include "tetraedge/te/te_model_animation.h"
+#include "tetraedge/te/te_vector3f32.h"
+#include "tetraedge/te/te_matrix4x4.h"
+#include "tetraedge/te/te_model.h"
+#include "tetraedge/te/te_bezier_curve.h"
+#include "tetraedge/te/te_free_move_zone.h"
+#include "tetraedge/te/te_trs.h"
+
+namespace Tetraedge {
+
+class Character : public TeAnimation {
+public:
+	Character();
+	virtual ~Character() {}
+
+	struct AnimSettings {
+		AnimSettings() : _stepLeft(0), _stepRight(0) {};
+		Common::String _file;
+		int _stepRight;
+		int _stepLeft;
+	};
+
+	struct WalkSettings {
+		AnimSettings _walkParts[4];
+
+		void clear();
+	};
+
+	struct CharacterSettings {
+		CharacterSettings() : _walkSpeed(0.0f) {}
+
+		Common::String _name;
+		Common::String _modelFileName;
+		TeVector3f32 _defaultScale;
+		Common::String _walkFileName;
+		Common::HashMap<Common::String, WalkSettings> _walkSettings; // keys are "Walk", "Jog", etc
+		float _walkSpeed;
+
+		TeVector3f32 _cutSceneCurveDemiPosition;
+		Common::String _defaultEyes;	// Note: Engine supports more, but in practice only one ever used.
+		Common::String _defaultMouth; 	// Note: Engine supports more, but in practice only one ever used.
+		Common::String _defaultBody; 	// Note: Engine supports more, but in practice only one ever used.
+
+		void clear();
+	};
+
+	struct AnimCacheElement {
+		TeIntrusivePtr<TeModelAnimation> _modelAnim;
+		int _size;
+	};
+
+	enum WalkPart {
+		WalkPart_Start,
+		WalkPart_Loop,
+		WalkPart_EndD,
+		WalkPart_EndG
+	};
+
+	struct Callback {
+		int x;
+		Common::String s;
+		int y;
+		float f;
+	};
+
+	void addCallback(const Common::String &s1, const Common::String &s2, float f1, float f2);
+
+	static void animCacheFreeAll();
+	static void animCacheFreeOldest();
+	static TeIntrusivePtr<TeModelAnimation> animCacheLoad(const Common::Path &path);
+
+	float animLength(const TeModelAnimation &modelanim, long bone, long lastframe);
+	float animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe);
+	bool blendAnimation(const Common::String &animname, float param_2, bool param_3, bool param_4);
+	TeVector3f32 correctPosition(const TeVector3f32 &pos);
+	float curveOffset();
+	void deleteAllCallback();
+	void deleteAnim();
+	void deleteCallback(const Common::String &str1, const Common::String &str2, float f);
+	//static bool deserialize(TiXmlElement *param_1, Walk *param_2);
+	void endMove();
+
+	const WalkSettings *getCurrentWalkFiles();
+	bool isFramePassed(uint frameno);
+	bool isWalkEnd();
+	bool leftStepFrame(enum WalkPart walkpart);
+	bool rightStepFrame(enum WalkPart walkpart);
+	bool loadModel(const Common::String &name, bool param_2);
+	static bool loadSettings(const Common::String &path);
+
+	bool onBonesUpdate(const Common::String &param_1, const TeMatrix4x4 *param_2);
+	bool onModelAnimationFinished();
+	void permanentUpdate();
+	void placeOnCurve(const TeBezierCurve &curve);
+	//void play() // just called TeAnimation::play();
+	void removeAnim();
+	void removeFromCurve();
+	static Common::String rootBone() { return "Pere"; }
+
+	bool setAnimation(const Common::String &name, bool repeat, bool param_3, bool param_4, int startFrame, int endFrame);
+	void setAnimationSound(const Common::String &name, uint param_2);
+	void setCurveOffset(float offset);
+	void setFreeMoveZone(const Common::SharedPtr<TeFreeMoveZone> &zone);
+	bool setShadowVisible(bool visible);
+	void setStepSound(const Common::String &stepSound1, const Common::String &stepSound2);
+	float speedFromAnim(double movepercent);
+	//void stop(); // just maps to TeAnimation::stop();
+	float translationFromAnim(const TeModelAnimation &anim, long bone, long frame);
+	TeVector3f32 translationVectorFromAnim(const TeModelAnimation &anim, long bone, long frame);
+	TeTRS trsFromAnim(const TeModelAnimation &anim, long bone, long frame);
+	void update(double percentval);
+	void updateAnimFrame();
+	void updatePosition(float curveOffset);
+	Common::String walkAnim(WalkPart part);
+	void walkMode(const Common::String &mode);
+	void walkTo(float param_1, bool param_2);
+
+	TeIntrusivePtr<TeModel> _model;
+	TeIntrusivePtr<TeModel> _shadowModel[2];
+	TeSignal1Param<const Common::String &> _characterAnimPlayerFinishedSignal;
+	TeSignal1Param<const Common::String &> _onCharacterAnimFinishedSignal;
+
+	const CharacterSettings &characterSettings() const { return _characterSettings; }
+	const Common::String walkModeStr() const { return _walkModeStr; }
+	const Common::String curAnimName() const { return _curAnimName; }
+	bool needsSomeUpdate() const { return _needsSomeUpdate; }
+
+private:
+	float _curveOffset;
+	TeBezierCurve _curve;
+	Common::SharedPtr<TeFreeMoveZone> _freeMoveZone;
+	Common::String _stepSound1;
+	Common::String _stepSound2;
+	Common::String _walkModeStr; // Walk or Jog
+
+	TeIntrusivePtr<TeModelAnimation> _curModelAnim;
+
+	CharacterSettings _characterSettings;
+
+	int _walkPart0AnimLen;
+	int _walkPart1AnimLen;
+	int _walkPart3AnimLen;
+
+	uint32 _walkPart0AnimFrameCount;
+	uint32 _walkPart1AnimFrameCount;
+	uint32 _walkPart3AnimFrameCount;
+
+	int _lastFrame;
+	bool _missingCurrentAnim;
+	bool _someRepeatFlag; // TODO: what is this?
+	bool _callbacksChanged;
+	bool _needsSomeUpdate; // TODO: what is this? Field 0x85.
+
+	// TODO: work out how these are different
+	Common::String _setAnimName;
+	Common::String _curAnimName;
+
+	Common::HashMap<Common::String, Common::Array<Callback *>> _callbacks;
+
+	static Common::Array<AnimCacheElement> _animCache;
+	static uint _animCacheSize;
+	static Common::HashMap<Common::String, CharacterSettings> *_globalCharacterSettings;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_CHARACTER_H
diff --git a/engines/tetraedge/game/character_settings_xml_parser.cpp b/engines/tetraedge/game/character_settings_xml_parser.cpp
new file mode 100644
index 00000000000..2c306c32351
--- /dev/null
+++ b/engines/tetraedge/game/character_settings_xml_parser.cpp
@@ -0,0 +1,167 @@
+/* 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/game/character_settings_xml_parser.h"
+
+namespace Tetraedge {
+
+bool CharacterSettingsXmlParser::parserCallback_ModelsSettings(ParserNode *node) {
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_Model(ParserNode *node) {
+	const Character::CharacterSettings emptySettings;
+	const Common::String &name = node->values["name"];
+	_characterSettings->setVal(name, emptySettings);
+	_curCharacter = &_characterSettings->getVal(name);
+	_curCharacter->_name = name;
+	assert(_characterSettings != nullptr);
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_modelFileName(ParserNode *node) {
+	_curTextTag = TagModelFileName;
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_defaultScale(ParserNode *node) {
+	_curTextTag = TagDefaultScale;
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_walk(ParserNode *node) {
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_animationFileName(ParserNode *node) {
+	_curTextTag = TagAnimationFileName;
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_walkType(ParserNode *node) {
+	Common::String walkName = node->values["name"];
+	_curWalkSettings = &(_curCharacter->_walkSettings[walkName]);
+	return true;
+}
+
+Character::AnimSettings CharacterSettingsXmlParser::parseWalkAnimSettings(const ParserNode *node) const {
+	Character::AnimSettings settings;
+	const Common::StringMap &map = node->values;
+	settings._file = map["file"];
+	if (map.contains("stepRight"))
+		settings._stepRight = map["stepRight"].asUint64();
+
+	if (map.contains("stepLeft"))
+		settings._stepLeft = map["stepLeft"].asUint64();
+
+	return settings;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_start(ParserNode *node) {
+	_curWalkSettings->_walkParts[0] = parseWalkAnimSettings(node);
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_loop(ParserNode *node) {
+	_curWalkSettings->_walkParts[1] = parseWalkAnimSettings(node);
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_endD(ParserNode *node) {
+	_curWalkSettings->_walkParts[2] = parseWalkAnimSettings(node);
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_endG(ParserNode *node) {
+	_curWalkSettings->_walkParts[3] = parseWalkAnimSettings(node);
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_speed(ParserNode *node) {
+	_curTextTag = TagSpeed;
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_cutSceneCurveDemi(ParserNode *node) {
+	// Handled in the "position" callback.
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_position(ParserNode *node) {
+	_curTextTag = TagPosition;
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_face(ParserNode *node) {
+	// Handled in "face" and "eyes" callbacks.
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_eyes(ParserNode *node) {
+	_curTextTag = TagEyes;
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_mouth(ParserNode *node) {
+	_curTextTag = TagMouth;
+	return true;
+}
+
+bool CharacterSettingsXmlParser::parserCallback_body(ParserNode *node) {
+	if (node->values["name"] != "default")
+		error("CharacterSettingsXmlParser: Only default body supported.");
+	_curTextTag = TagBody;
+	return true;
+}
+
+bool CharacterSettingsXmlParser::textCallback(const Common::String &val) {
+	switch (_curTextTag) {
+	case TagModelFileName:
+		_curCharacter->_modelFileName = val;
+		break;
+	case TagDefaultScale:
+		_curCharacter->_defaultScale.parse(val);
+		break;
+	case TagAnimationFileName:
+		_curCharacter->_walkFileName = val;
+		break;
+	case TagEyes:
+		_curCharacter->_defaultEyes = val;
+		break;
+	case TagMouth:
+		_curCharacter->_defaultMouth = val;
+		break;
+	case TagSpeed:
+		_curCharacter->_walkSpeed = atof(val.c_str());
+		break;
+	case TagPosition:
+		_curCharacter->_cutSceneCurveDemiPosition.parse(val);
+		break;
+	case TagBody:
+		_curCharacter->_defaultBody = val;
+		break;
+	default:
+		break;
+	}
+	return true;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/character_settings_xml_parser.h b/engines/tetraedge/game/character_settings_xml_parser.h
new file mode 100644
index 00000000000..3d6ae90b288
--- /dev/null
+++ b/engines/tetraedge/game/character_settings_xml_parser.h
@@ -0,0 +1,133 @@
+/* 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_CHARACTER_SETTINGS_XML_PARSER_H
+#define TETRAEDGE_GAME_CHARACTER_SETTINGS_XML_PARSER_H
+
+#include "common/xmlparser.h"
+#include "tetraedge/game/character.h"
+#include "tetraedge/te/te_vector3f32.h"
+
+namespace Tetraedge {
+
+class CharacterSettingsXmlParser : public Common::XMLParser {
+public:
+	void setCharacterSettings(Common::HashMap<Common::String, Character::CharacterSettings> *settings) {
+		_characterSettings = settings;
+	};
+
+	// Parser
+	CUSTOM_XML_PARSER(CharacterSettingsXmlParser) {
+		XML_KEY(ModelsSettings)
+			XML_KEY(Model)
+				XML_PROP(name, true)
+				XML_KEY(modelFileName)
+				KEY_END()
+				XML_KEY(defaultScale)
+				KEY_END()
+				XML_KEY(walk)
+					XML_KEY(animationFileName)
+					KEY_END()
+					XML_KEY(walkType)
+						XML_PROP(name, true)
+						XML_KEY(start)
+							XML_PROP(file, true)
+							XML_PROP(stepRight, false)
+							XML_PROP(stepLeft, false)
+						KEY_END()
+						XML_KEY(loop)
+							XML_PROP(file, true)
+							XML_PROP(stepRight, false)
+							XML_PROP(stepLeft, false)
+						KEY_END()
+						XML_KEY(endD)
+							XML_PROP(file, true)
+						KEY_END()
+						XML_KEY(endG)
+							XML_PROP(file, true)
+						KEY_END()
+					KEY_END()
+					XML_KEY(speed)
+					KEY_END()
+				KEY_END()
+				XML_KEY(cutSceneCurveDemi)
+					XML_KEY(position)
+					KEY_END()
+				KEY_END()
+				XML_KEY(face)
+					XML_PROP(name, true)
+					XML_KEY(eyes)
+					KEY_END()
+					XML_KEY(mouth)
+					KEY_END()
+				KEY_END()
+				XML_KEY(body)
+					XML_PROP(name, true)
+				KEY_END()
+			KEY_END()
+		KEY_END()
+	} PARSER_END()
+
+	// Parser callback methods
+	bool parserCallback_ModelsSettings(ParserNode *node);
+	bool parserCallback_Model(ParserNode *node);
+	bool parserCallback_modelFileName(ParserNode *node);
+	bool parserCallback_defaultScale(ParserNode *node);
+	bool parserCallback_walk(ParserNode *node);
+	bool parserCallback_animationFileName(ParserNode *node);
+	bool parserCallback_walkType(ParserNode *node);
+	bool parserCallback_start(ParserNode *node);  	// walk anim
+	bool parserCallback_loop(ParserNode *node);	  	// walk anim
+	bool parserCallback_endD(ParserNode *node);		// for walk anim
+	bool parserCallback_endG(ParserNode *node);		// for walk anim
+	bool parserCallback_speed(ParserNode *node);	// walk speed
+	bool parserCallback_cutSceneCurveDemi(ParserNode *node);
+	bool parserCallback_position(ParserNode *node);	// position of cutSceneCurveDemi
+	bool parserCallback_face(ParserNode *node);
+	bool parserCallback_eyes(ParserNode *node);
+	bool parserCallback_mouth(ParserNode *node);
+	bool parserCallback_body(ParserNode *node);
+	bool textCallback(const Common::String &val) override;
+
+private:
+	Character::AnimSettings parseWalkAnimSettings(const ParserNode *node) const;
+
+	enum TextTagType {
+		TagModelFileName,
+		TagDefaultScale,
+		TagAnimationFileName,
+		TagEyes,
+		TagMouth,
+		TagSpeed,
+		TagPosition,
+		TagBody
+	};
+
+	TextTagType _curTextTag;
+	// TODO add private members
+	Character::CharacterSettings *_curCharacter;
+	Character::WalkSettings *_curWalkSettings;
+	Common::HashMap<Common::String, Character::CharacterSettings> *_characterSettings;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_CHARACTER_SETTINGS_XML_PARSER_H
diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
new file mode 100644
index 00000000000..46b1abefd64
--- /dev/null
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -0,0 +1,51 @@
+/* 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/characters_shadow.h"
+#include "tetraedge/te/te_renderer.h"
+
+namespace Tetraedge {
+
+CharactersShadow::CharactersShadow() {
+}
+
+void CharactersShadow::create(InGameScene *scene) {
+	error("TODO: Implement me");
+}
+
+void CharactersShadow::createTexture(InGameScene *scene) {
+	error("TODO: Implement me");
+}
+
+void CharactersShadow::destroy() {
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->disableTexture();
+	//glBindTexture(GL_TEXTURE_2D, 0);
+	//glDeleteTextures(1, (uint *)this);
+	error("TODO: Finish implementation here");
+}
+
+void CharactersShadow::draw(InGameScene *scene) {
+	error("TODO: Implement me");
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/characters_shadow.h b/engines/tetraedge/game/characters_shadow.h
new file mode 100644
index 00000000000..a6809f6414a
--- /dev/null
+++ b/engines/tetraedge/game/characters_shadow.h
@@ -0,0 +1,46 @@
+/* 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_CHARACTERS_SHADOW_H
+#define TETRAEDGE_GAME_CHARACTERS_SHADOW_H
+
+#include "tetraedge/game/in_game_scene.h"
+
+namespace Tetraedge {
+
+class CharactersShadow {
+public:
+	CharactersShadow();
+
+	void create(InGameScene *scene);
+	void createTexture(InGameScene *scene);
+	void destroy();
+	void draw(InGameScene *scene);
+	//void drawTexture(); // empty?
+
+private:
+	// TODO add private members
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_CHARACTERS_SHADOW_H
diff --git a/engines/tetraedge/game/confirm.cpp b/engines/tetraedge/game/confirm.cpp
new file mode 100644
index 00000000000..5ba7cbeec5c
--- /dev/null
+++ b/engines/tetraedge/game/confirm.cpp
@@ -0,0 +1,121 @@
+/* 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/game/confirm.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/tetraedge.h"
+
+#include "tetraedge/te/te_text_layout.h"
+
+namespace Tetraedge {
+
+Confirm::Confirm() {
+}
+
+void Confirm::enter(const Common::String &guiPath, const Common::String &y) {
+	_gui.load(guiPath);
+	TeLayout *backgroundLayout = _gui.layoutChecked("background");
+	backgroundLayout->setRatioMode(TeILayout::RATIO_MODE_NONE);
+
+	Application *app = g_engine->getApplication();
+	TeButtonLayout *confirmButtonLayout = _gui.buttonLayout("confirm");
+	app->_frontOrientationLayout.addChild(confirmButtonLayout);
+
+	TeButtonLayout *yesButtonLayout = _gui.buttonLayout("yes");
+	if (yesButtonLayout)
+		yesButtonLayout->onMouseClickValidated().add<Confirm>(this, &Confirm::onButtonYes);
+
+	TeButtonLayout *noButtonLayout = _gui.buttonLayout("no");
+	if (noButtonLayout)
+		noButtonLayout->onMouseClickValidated().add<Confirm>(this, &Confirm::onButtonNo);
+
+	TeLayout *textLayout = _gui.layout("text");
+	if (textLayout) {
+		const Common::String textAttributs = _gui.value("textAttributs").toString();
+		const Common::String textAttributsDown = _gui.value("textAttributsDown").toString();
+		const Common::String *okButtonLoc = app->_loc.value("okButton");
+		const Common::String *cancelButtonLoc = app->_loc.value("cancelButton");
+
+		TeTextLayout *textTextLayout = dynamic_cast<TeTextLayout *>(textLayout->child(0));
+		textTextLayout->setText(textAttributs + *app->_loc.value(textTextLayout->name()));
+
+		if (!okButtonLoc || !cancelButtonLoc) {
+			error("Missing translations for ok and cancel");
+		}
+
+		TeTextLayout *yesUpLayout = _gui.textLayout("yesUpLayout");
+		if (yesUpLayout)
+			yesUpLayout->setText(textAttributs + *okButtonLoc);
+
+		TeTextLayout *yesDownLayout = _gui.textLayout("yesDownLayout");
+		if (yesDownLayout)
+			yesDownLayout->setText(textAttributsDown + *okButtonLoc);
+
+		TeTextLayout *yesRollOverLayout = _gui.textLayout("yesRollOverLayout");
+		if (yesRollOverLayout)
+			yesRollOverLayout->setText(textAttributs + *okButtonLoc);
+
+		TeTextLayout *noUpLayout = _gui.textLayout("noUpLayout");
+		if (noUpLayout)
+			noUpLayout->setText(textAttributs + *cancelButtonLoc);
+
+		TeTextLayout *noDownLayout = _gui.textLayout("noDownLayout");
+		if (noDownLayout)
+			noDownLayout->setText(textAttributsDown + *cancelButtonLoc);
+
+		TeTextLayout *noRollOverLayout = _gui.textLayout("noRollOverLayout");
+		if (noRollOverLayout)
+			noRollOverLayout->setText(textAttributs + *cancelButtonLoc);
+	}
+
+	// Make sure the mouse cursor is back on top.
+	app->_frontOrientationLayout.removeChild(&app->mouseCursorLayout());
+	app->_frontOrientationLayout.addChild(&app->mouseCursorLayout());
+}
+
+void Confirm::leave() {
+	Application *app = g_engine->getApplication();
+	TeButtonLayout *confirmButtonLayout = _gui.buttonLayout("confirm");
+	if (confirmButtonLayout) {
+		app->_frontLayout.removeChild(confirmButtonLayout);
+	}
+	_gui.unload();
+}
+
+bool Confirm::onButtonNo() {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	leave();
+	_onButtonNoSignal.call();
+	app->fade();
+	return true;
+}
+
+bool Confirm::onButtonYes() {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	leave();
+	_onButtonYesSignal.call();
+	app->fade();
+	return true;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/confirm.h b/engines/tetraedge/game/confirm.h
new file mode 100644
index 00000000000..af21e62d15c
--- /dev/null
+++ b/engines/tetraedge/game/confirm.h
@@ -0,0 +1,49 @@
+/* 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_CONFIRM_H
+#define TETRAEDGE_GAME_CONFIRM_H
+
+#include "common/str.h"
+
+#include "tetraedge/te/te_signal.h"
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class Confirm {
+public:
+	Confirm();
+
+	void enter(const Common::String &guiPath, const Common::String &y);
+	void leave();
+
+	bool onButtonNo();
+	bool onButtonYes();
+
+	TeSignal0Param _onButtonNoSignal;
+	TeSignal0Param _onButtonYesSignal;
+	TeLuaGUI _gui;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_CONFIRM_H
diff --git a/engines/tetraedge/game/credits.cpp b/engines/tetraedge/game/credits.cpp
new file mode 100644
index 00000000000..572acc67079
--- /dev/null
+++ b/engines/tetraedge/game/credits.cpp
@@ -0,0 +1,66 @@
+/* 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 "common/textconsole.h"
+#include "tetraedge/game/credits.h"
+
+namespace Tetraedge {
+
+Credits::Credits() {
+}
+
+void Credits::enter(bool flag) {
+	error("TODO: Implement Credits::enter");
+}
+
+void Credits::leave() {
+	error("TODO: Implement Credits::leave");
+}
+
+bool Credits::onAnimFinished() {
+	leave();
+	return false;
+}
+
+bool Credits::onBackgroundAnimFinished() {
+	//TeLayout *buttonsLayout = _gui.layout("buttons");
+	error("TODO: Implement Credits::onBackgroundAnimFinished");
+}
+
+bool Credits::onPadButtonUp(uint button) {
+	// Original calls this function here but it seems unnecessary?
+	//TeLayout *buttonsLayout = _gui.layout("buttons");
+	if (button & 2) // TODO; which button is 2?
+		leave();
+	return false;
+}
+
+bool Credits::onQuitButton() {
+	TeCurveAnim2<TeI3DObject2, TeVector3f32> *anim1 = _gui.layoutPositionLinearAnimation("scrollTextPositionAnim");
+	anim1->stop();
+	TeCurveAnim2<TeI3DObject2, TeVector3f32> *anim2 = _gui.layoutPositionLinearAnimation("scrollTextAnchorAnim");
+	anim2->stop();
+	leave();
+	return true;
+}
+
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/credits.h b/engines/tetraedge/game/credits.h
new file mode 100644
index 00000000000..fada8eefdaa
--- /dev/null
+++ b/engines/tetraedge/game/credits.h
@@ -0,0 +1,55 @@
+/* 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_CREDITS_H
+#define TETRAEDGE_GAME_CREDITS_H
+
+#include "tetraedge/te/te_lua_gui.h"
+#include "tetraedge/te/te_curve_anim2.h"
+#include "tetraedge/te/te_color.h"
+#include "tetraedge/te/te_3d_object2.h"
+
+namespace Tetraedge {
+
+class Credits {
+public:
+	Credits();
+
+	void enter(bool flag);
+	void leave();
+
+	bool onAnimFinished();
+	bool onBackgroundAnimFinished();
+	bool onPadButtonUp(uint button);
+	bool onQuitButton();
+
+private:
+	TeTimer _timer;
+	TeLuaGUI _gui;
+	TeCurveAnim2<Te3DObject2, TeColor> _curveAnim;
+	TeColor _col1;
+	TeColor _col2;
+	Common::Array<double> _doubleArr;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_CREDITS_H
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
new file mode 100644
index 00000000000..6be1ece06e5
--- /dev/null
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -0,0 +1,147 @@
+/* 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/game/dialog2.h"
+#include "tetraedge/te/te_button_layout.h"
+
+namespace Tetraedge {
+
+Dialog2::Dialog2() {
+	_music.onStopSignal().add(this, &Dialog2::onSoundFinished);
+	_minimumTimeTimer.alarmSignal().add(this, &Dialog2::onMinimumTimeTimer);
+}
+
+bool Dialog2::isDialogPlaying() {
+	TeButtonLayout *lockbtn = _gui.buttonLayout("dialogLockButton");
+
+	if (lockbtn)
+		return lockbtn->visible();
+
+	return false;
+}
+
+void Dialog2::launchNextDialog() {
+	error("TODO: Implement Dialog2::launchNextDialog.");
+}
+
+void Dialog2::load() {
+	setName("dialog2");
+	setSizeType(RELATIVE_TO_PARENT);
+	TeVector3f32 usersz = userSize();
+	setSize(TeVector3f32(1.0f, 1.0f, usersz.z()));
+	size(); // refresh size.. seems to do nothing with result?
+	_music.repeat(false);
+	_gui.load("menus/dialog.lua");
+	TeButtonLayout *dialogLockBtn = _gui.buttonLayoutChecked("dialogLockButton");
+
+	dialogLockBtn->setVisible(false);
+	addChild(dialogLockBtn);
+
+	TeButtonLayout *dialogBtn = _gui.buttonLayoutChecked("dialog");
+	dialogBtn->onMouseClickValidated().add(this, &Dialog2::onSkipButton);
+
+	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
+	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+	if (!dialogAnimUp || !dialogAnimDown)
+		error("Dialog2::load: didn't get dialogAnimUp or dialogAnimationDown");
+
+	dialogAnimUp->_callbackObj = dialogBtn;
+	dialogAnimUp->_callbackMethod = &TeLayout::setAnchor;
+	dialogAnimUp->onFinished().add(this, &Dialog2::onAnimationUpFinished);
+
+	dialogAnimDown->_callbackObj = dialogBtn;
+	dialogAnimDown->_callbackMethod = &TeLayout::setAnchor;
+	dialogAnimDown->onFinished().add(this, &Dialog2::onAnimationDownFinished);
+}
+
+void Dialog2::loadFromBackup() {
+	// seems to do nothing useful? just iterates the children..
+}
+
+bool Dialog2::onAnimationDownFinished() {
+	Common::String param(_animDownFinishedResultString);
+	launchNextDialog();
+	_onAnimationDownFinishedSignal.call(param);
+	return false;
+}
+
+bool Dialog2::onAnimationUpFinished() {
+	// Seems like this just prints a debug value??
+	TeButtonLayout *dialogButton = _gui.buttonLayout("dialog");
+	dialogButton->anchor();
+	return false;
+}
+
+bool Dialog2::onMinimumTimeTimer() {
+	_minimumTimeTimer.stop();
+	if (!_music.isPlaying())
+		startDownAnimation();
+	return false;
+}
+
+bool Dialog2::onSkipButton() {
+	const TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
+	if (!dialogAnimUp->_runTimer._stopped) {
+		const TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+		if (dialogAnimDown->_runTimer._stopped) {
+			startDownAnimation();
+			_music.stop();
+		}
+	}
+	return false;
+}
+
+bool Dialog2::onSoundFinished() {
+	if (_minimumTimeTimer._stopped)
+		startDownAnimation();
+	return false;
+}
+
+void Dialog2::pushDialog(const Common::String &param_1, const Common::String &param_2, const Common::String &param_3, int param_4) {
+	error("TODO: Implement Dialog2::pushDialog");
+}
+
+void Dialog2::pushDialog(const Common::String &param_1, const Common::String &param_2, const Common::String &param_3,
+						 const Common::String &param_4, const Common::String &param_5, float param_6) {
+	error("TODO: Implement Dialog2::pushDialog");
+}
+
+//void saveToBackup(TiXmlNode *node)
+
+void Dialog2::startDownAnimation() {
+	_minimumTimeTimer.stop();
+	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+	dialogAnimDown->play();
+}
+
+void Dialog2::unload() {
+	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
+	dialogAnimUp->stop();
+	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+	dialogAnimDown->stop();
+	_music.close();
+	_gui.unload();
+	error("TODO: Finish Dialog2::unload");
+	//_dialogDataList.clear();
+	//_minimumTimeTimer.stop();
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/dialog2.h b/engines/tetraedge/game/dialog2.h
new file mode 100644
index 00000000000..9e2a8f025ad
--- /dev/null
+++ b/engines/tetraedge/game/dialog2.h
@@ -0,0 +1,74 @@
+/* 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_DIALOG2_H
+#define TETRAEDGE_GAME_DIALOG2_H
+
+#include "tetraedge/te/te_timer.h"
+#include "tetraedge/te/te_music.h"
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class Dialog2 : public TeLayout {
+public:
+	Dialog2();
+
+	class DialogData {
+		bool operator=(const DialogData &other);
+	};
+
+	bool isDialogPlaying();
+	void launchNextDialog();
+	void load();
+	void loadFromBackup(); // seems to do nothing useful? just iterates the children..
+	bool onAnimationDownFinished();
+	bool onAnimationUpFinished();
+	bool onMinimumTimeTimer();
+	bool onSkipButton();
+	bool onSoundFinished();
+
+	void pushDialog(const Common::String &param_1, const Common::String &param_2, const Common::String &param_3, int param_4);
+	void pushDialog(const Common::String &param_1, const Common::String &param_2, const Common::String &param_3,
+					const Common::String &param_4, const Common::String &param_5, float param_6);
+	//void saveToBackup(TiXmlNode *node)
+	void startDownAnimation();
+	void unload();
+
+	TeLuaGUI &gui() { return _gui; }
+
+	Common::String prevSceneName() { return _prevSceneName; };
+
+private:
+	TeTimer _minimumTimeTimer;
+
+	Common::String _prevSceneName;
+	Common::String _animDownFinishedResultString;
+
+	TeLuaGUI _gui;
+	TeMusic _music;
+
+	TeSignal1Param<const Common::String &> _onAnimationDownFinishedSignal;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_DIALOG2_H
diff --git a/engines/tetraedge/game/document.cpp b/engines/tetraedge/game/document.cpp
new file mode 100644
index 00000000000..be6ccd15604
--- /dev/null
+++ b/engines/tetraedge/game/document.cpp
@@ -0,0 +1,31 @@
+/* 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/game/document.h"
+
+namespace Tetraedge {
+
+Document::Document() {
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/document.h b/engines/tetraedge/game/document.h
new file mode 100644
index 00000000000..9c6bbeb8a65
--- /dev/null
+++ b/engines/tetraedge/game/document.h
@@ -0,0 +1,53 @@
+/* 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_DOCUMENT_H
+#define TETRAEDGE_GAME_DOCUMENT_H
+
+#include "common/str.h"
+
+namespace Tetraedge {
+
+class Document {
+public:
+	Document();
+	~Document() {
+		unload();
+		// TODO: check for other destructor things.
+	}
+
+	void load(const Common::String &name);
+	//void loadFromBackup(TiXmlElement &node) {
+	// load(node->Attribute("id");
+	//}
+	bool onButtonDown();
+	//void saveToBackup(TiXmlElement &node);
+	Common::String spritePath() const;
+	void unload();
+
+private:
+	// TODO add private members
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_DOCUMENT_H
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
new file mode 100644
index 00000000000..5762275900f
--- /dev/null
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -0,0 +1,112 @@
+/* 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/game/documents_browser.h"
+
+namespace Tetraedge {
+
+DocumentsBrowser::DocumentsBrowser() {
+	_timer.alarmSignal().add(this, &DocumentsBrowser::onQuitDocumentDoubleClickTimer);
+}
+
+void DocumentsBrowser::enter() {
+	setVisible(true);
+	currentPage(_curPage);
+}
+
+void DocumentsBrowser::leave() {
+	_timer.stop();
+	setVisible(false);
+}
+
+void DocumentsBrowser::load() {
+	setVisible(false);
+	setName("documentsBrowser");
+
+	setSizeType(RELATIVE_TO_PARENT);
+	const TeVector3f32 userSz = TeLayout::userSize();
+	setSize(TeVector3f32(1.0f, 1.0f, userSz.z()));
+
+	TeLuaGUI::load("DocumentsBrowser/DocumentsBrowser.lua");
+
+	TeLayout *docBrowser = TeLuaGUI::layout("documentBrowser");
+	if (docBrowser)
+		addChild(docBrowser);
+
+	TeButtonLayout *button = buttonLayout("previousPage");
+	button->onMouseClickValidated().add(this, &DocumentsBrowser::onPreviousPage);
+	button = buttonLayout("nextPage");
+	button->onMouseClickValidated().add(this, &DocumentsBrowser::onNextPage);
+	button = TeLuaGUI::buttonLayout("zoomed");
+	button->onMouseClickValidated().add(this, &DocumentsBrowser::onZoomedButton);
+	button = TeLuaGUI::buttonLayout("zoomed");
+	button->setVisible(false);
+
+	// Game tries to load a file that doesn't exist..
+	debug("TODO?? DocumentsBrowser::load: Game opens Documents.xml here.");
+	_timer.start();
+}
+
+void DocumentsBrowser::loadZoomed() {
+	_zoomedLayout.setSizeType(RELATIVE_TO_PARENT);
+	TeVector3f32 usersz = userSize();
+	_zoomedLayout.setSize(TeVector3f32(1.0f, 1.0f, usersz.z()));
+	TeLayout *zoomedChild = layout("zoomed");
+	_zoomedLayout.addChild(zoomedChild);
+}
+
+void DocumentsBrowser::currentPage(long page) {
+	const Common::String pageName = Common::String::format("page%ld", page);
+	TeLayout *pageLayout = layout(pageName);
+	if (!pageLayout)
+		return;
+
+	_curPage = page;
+
+	error("TODO: Implement DocumentsBrowser::currentPage");
+}
+
+bool DocumentsBrowser::onQuitDocumentDoubleClickTimer() {
+	error("TODO: Implement DocumentsBrowser::onQuitDocumentDoubleClickTimer");
+}
+
+bool DocumentsBrowser::onNextPage() {
+	currentPage(_curPage + 1);
+	return false;
+}
+
+bool DocumentsBrowser::onPreviousPage() {
+	currentPage(_curPage - 1);
+	return false;
+}
+
+bool DocumentsBrowser::onZoomedButton() {
+	error("TODO: Implement DocumentsBrowser::onZoomedButton");
+}
+
+void DocumentsBrowser::showDocument(const Common::String &str, long n) {
+	error("TODO: Implement DocumentsBrowser::showDocument");
+}
+
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/documents_browser.h b/engines/tetraedge/game/documents_browser.h
new file mode 100644
index 00000000000..0331cb2cb28
--- /dev/null
+++ b/engines/tetraedge/game/documents_browser.h
@@ -0,0 +1,97 @@
+/* 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_DOCUMENTS_BROWSER_H
+#define TETRAEDGE_GAME_DOCUMENTS_BROWSER_H
+
+#include "common/str.h"
+#include "tetraedge/game/document.h"
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class DocumentsBrowser : public TeLuaGUI, public TeLayout {
+public:
+	DocumentsBrowser();
+
+	void addDocument(Document *document);
+	void addDocument(const Common::String &str);
+
+	void currentPage(long page);
+	int documentCount(const Common::String &str) { // never used?
+		return 1;
+	}
+
+	Common::String documentDescription(const Common::String &name);
+	Common::String documentName(const Common::String &name);
+
+	void enter();
+	void hideDocument();
+	void leave();
+	void load();
+	// void loadFromBackup(TiXmlNode *node);
+	void loadZoomed();
+	// void saveToBackup(TiXmlNode *node);
+
+	void onDocumentSelected(Document *doc);
+	bool onNextPage();
+	bool onPreviousPage();
+	bool onQuitDocumentDoubleClickTimer();
+	bool onZoomedButton();
+
+	// Sorry, this is how the original does it...
+	bool onShowedDocumentButton0();
+	bool onShowedDocumentButton1();
+	bool onShowedDocumentButton2();
+	bool onShowedDocumentButton3();
+	bool onShowedDocumentButton4();
+	bool onShowedDocumentButton5();
+	bool onShowedDocumentButton6();
+	bool onShowedDocumentButton7();
+	bool onShowedDocumentButton8();
+	bool onShowedDocumentButton9();
+	bool onShowedDocumentButton10();
+	bool onShowedDocumentButton11();
+	bool onShowedDocumentButton12();
+	bool onShowedDocumentButton13();
+	bool onShowedDocumentButton14();
+	bool onShowedDocumentButton15();
+	bool onShowedDocumentButton16();
+	bool onShowedDocumentButton17();
+	bool onShowedDocumentButton18();
+	bool onShowedDocumentButton19();
+
+	void showDocument(const Common::String &str, long n);
+	void unload();
+
+	TeLayout &zoomedLayout() { return _zoomedLayout; }
+
+private:
+	TeTimer _timer;
+	TeLayout _zoomedLayout;
+	unsigned long _curPage;
+	// TODO add private members
+	// TiXmlDocument _xmldoc;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_DOCUMENTS_BROWSER_H
diff --git a/engines/tetraedge/game/gallery_menu.cpp b/engines/tetraedge/game/gallery_menu.cpp
new file mode 100644
index 00000000000..935443f1c8b
--- /dev/null
+++ b/engines/tetraedge/game/gallery_menu.cpp
@@ -0,0 +1,69 @@
+/* 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/application.h"
+#include "tetraedge/game/gallery_menu.h"
+
+namespace Tetraedge {
+
+GalleryMenu::GalleryMenu() {
+}
+
+bool GalleryMenu::onLockVideoButtonValidated() {
+	onSkipVideoButtonValidated();
+	return false;
+}
+
+bool GalleryMenu::onSkipVideoButtonValidated() {
+	error("TODO: Implement onSkipVideoButtonValidated");
+	return false;
+}
+
+bool GalleryMenu::onQuitButton() {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	leave();
+	app->mainMenu().enter();
+	app->fade();
+	return true;
+}
+
+bool GalleryMenu::onVideoFinished() {
+	if (_loaded) {
+		Application *app = g_engine->getApplication();
+		app->captureFade();
+		onSkipVideoButtonValidated();
+		app->music().play();
+		app->fade();
+	}
+	return false;
+}
+
+void GalleryMenu::enter() {
+	error("TODO: implement GalleryMenu::enter");
+}
+
+void GalleryMenu::leave() {
+	error("TODO: implement GalleryMenu::leave");
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/gallery_menu.h b/engines/tetraedge/game/gallery_menu.h
new file mode 100644
index 00000000000..50dd39ee656
--- /dev/null
+++ b/engines/tetraedge/game/gallery_menu.h
@@ -0,0 +1,54 @@
+/* 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_GALLERY_MENU_H
+#define TETRAEDGE_GAME_GALLERY_MENU_H
+
+#include "common/array.h"
+#include "tetraedge/te/te_music.h"
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class GalleryMenu : public TeLuaGUI {
+public:
+	GalleryMenu();
+
+	class GalleryBtnObject {
+		bool OnValidated();
+	};
+
+	void enter();
+	void leave();
+
+	bool onQuitButton();
+	bool onLockVideoButtonValidated();
+	bool onSkipVideoButtonValidated();
+	bool onVideoFinished();
+
+private:
+	TeMusic _music;
+	Common::Array<GalleryBtnObject *> _btnObjects;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_GALLERY_MENU_H
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
new file mode 100644
index 00000000000..9d4ee36803f
--- /dev/null
+++ b/engines/tetraedge/game/game.cpp
@@ -0,0 +1,1034 @@
+/* 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 "common/file.h"
+#include "common/path.h"
+#include "common/str-array.h"
+#include "common/system.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/game/character.h"
+#include "tetraedge/game/in_game_scene.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/game_achievements.h"
+#include "tetraedge/game/lua_binds.h"
+#include "tetraedge/game/object3d.h"
+#include "tetraedge/te/te_core.h"
+#include "tetraedge/te/te_input_mgr.h"
+#include "tetraedge/te/te_variant.h"
+
+namespace Tetraedge {
+
+Game::Game() : _objectsTakenVal(0), _score(0), _entered(false), _gameLoadState(0),
+_noScaleLayout(nullptr), _noScaleLayout2(nullptr), _warped(false), _saveRequested(false),
+_firstInventory(true) {
+	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
+		_objectsTakenBits[i] = false;
+	}
+}
+
+/*static*/ const char *Game::OBJECTS_TAKEN_IDS[5] = {
+	"BCylindreBarr",
+	"VCylindreMusique",
+	"VCylindreVal",
+	"CCylindreCite",
+	"VPoupeeMammouth"
+};
+
+bool Game::addAnimToSet(const Common::String &anim) {
+	// Get path to lua script, eg scenes/ValVoralberg/14040/Set14040.lua
+	const Common::Path animPath(Common::String("scenes/") + anim + "/");
+
+	bool retval = false;
+	if (Common::File::exists(animPath)) {
+		Common::StringArray parts = TetraedgeEngine::splitString(anim, '/');
+		assert(parts.size() >= 2);
+
+		Common::String layoutName = parts[1];
+		Common::String path = Common::String("scenes/") + parts[0] + "/" + parts[1] + "/Set" + parts[1];
+
+		_gui2.load(path + ".lua");
+		/*
+		TeILayout *layout = _gui2.layout("root");
+		TeSpriteLayout *spriteLayout2 = findSpriteLayoutByName(layout, layoutName);
+
+		TeLayout *layout2 = TeLuaGUI::layout(&(this->scene).field_0x170,"root");
+		long lVar5 = 0;
+		if (spriteLayout2) {
+			lVar5 = (long)plVar3 + *(long *)(*plVar3 + -0x198);
+		}
+		(**(code **)(*(long *)((long)&pTVar2->vptr + (long)pTVar2->vptr[-0x33]) + 0x30))
+					((long)&pTVar2->vptr + (long)pTVar2->vptr[-0x33],lVar5);
+		 */
+	  retval = true;
+	}
+
+	return retval;
+}
+
+void Game::addArtworkUnlocked(const Common::String &name, bool notify) {
+	if (_unlockedArtwork.contains(name))
+		return;
+	_unlockedArtwork[name] = true;
+	if (notify) {
+		_notifier.push("BONUS!", "Inventory/Objects/VPapierCrayon.png");
+	}
+}
+
+void Game::addNoScale2Child(TeLayout *layout) {
+	if (_noScaleLayout2 && layout) {
+		_noScaleLayout2->addChild(layout);
+	}
+}
+
+void Game::addNoScale2Children() {
+	if (!_noScaleLayout2)
+		return;
+
+	TeLayout *vidbtn = _gui4.layout("videoButtonLayout");
+	if (vidbtn)
+		_noScaleLayout2->addChild(vidbtn);
+
+	TeLayout *bg = _inventory.cellphone()->gui().layout("background");
+	if (bg)
+		_noScaleLayout2->addChild(bg);
+
+	TeButtonLayout *bgbtn = _objectif.gui1().buttonLayout("background");
+	if (bgbtn)
+		_noScaleLayout2->addChild(bgbtn);
+}
+
+void Game::addNoScaleChildren() {
+	if (!_noScaleLayout)
+		return;
+	TeLayout *inGame = _gui4.layout("inGame");
+	if (inGame)
+		_noScaleLayout->addChild(inGame);
+
+	_noScaleLayout->addChild(&_question2);
+
+	Application *app = g_engine->getApplication();
+	app->_frontLayout.addChild(&_dialog2);
+
+	_noScaleLayout->addChild(&_inventory);
+	_noScaleLayout->addChild(&_inventoryMenu);
+	_noScaleLayout->addChild(&_documentsBrowser);
+	_noScaleLayout->addChild(&_documentsBrowser.zoomedLayout());
+}
+
+void Game::addRandomSound(const Common::String &s1, const Common::String &s2, float f1, float f2) {
+	warning("TODO: Implemet Game::addRandomSound %s %s %f %f", s1.c_str(), s1.c_str(), f1, f2);
+}
+
+void Game::addToBag(const Common::String &objname) {
+	if (_inventory.objectCount(objname) != 0)
+		return;
+	_inventory.addObject(objname);
+	Common::String imgpath("Inventory/Objects/");
+	imgpath += _inventory.objectName(objname);
+	imgpath += ".png";
+	_notifier.push(objname, imgpath);
+	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
+		if (objname == OBJECTS_TAKEN_IDS[i] && !_objectsTakenBits[i]) {
+			_objectsTakenBits[i] = true;
+			_objectsTakenVal++;
+		}
+	}
+	// Seeems strange as we're already in Game, but that's what original does?
+	Game *game = g_engine->getGame();
+	game->_score += 10;
+	debug("Updated score: %d", game->_score);
+}
+
+void Game::addToHand(const Common::String &objname) {
+	_inventory.addObject(objname);
+	_inventory.selectedObject(objname);
+}
+
+void Game::addToScore(int score) {
+	_score += score;
+}
+
+bool Game::changeWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
+	Application *app = g_engine->getApplication();
+	if (fadeFlag) {
+		app->blackFade();
+	} else {
+		app->captureFade();
+	}
+	_warpZone = zone;
+	_warpScene = scene;
+	_warpFadeFlag = fadeFlag;
+	_warped = true;
+	return true;
+}
+
+bool Game::changeWarp2(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
+	error("TODO: Implemet me");
+}
+
+void Game::deleteNoScale() {
+	error("TODO: Implemet me");
+}
+
+void Game::draw() {
+	if (_running) {
+	  _frameCounter++;
+	  _scene.draw();
+	}
+}
+
+void Game::enter(bool newgame) {
+	warning("TODO: Game::enter set field_0x42f0 true here");
+	_entered = true;
+	_luaShowOwnerError = false;
+	_score = 0;
+	Application *app = g_engine->getApplication();
+	app->visualFade().init();
+	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, -1000.0f));
+	g_engine->getInputMgr()->_mouseLUpSignal.insert(callbackptr);
+	warning("TODO: Game::enter set some other fields here");
+	_sceneCharacterVisibleFromLoad = false;
+	Character::loadSettings("models/ModelsSettings.xml");
+	Object3D::loadSettings("objects/ObjectsSettings.xml");
+	if (_scene._character) {
+		_scene._character->onFinished().remove(this, &Game::onDisplacementFinished);
+		_scene.unloadCharacter(_scene._character->_model->name());
+	}
+	bool loaded = loadPlayerCharacter("Kate");
+	if (!loaded)
+		error("[Game::enter] Can't load player character");
+
+	_scene._character->_model->setVisible(true);
+	_running = true;
+	_luaContext.create();
+	GameAchievements::registerAchievements(_luaContext);
+
+	_luaContext.setGlobal("BUTTON_VALID", 1);
+	_luaContext.setGlobal("BUTTON_CANCEL", 2);
+	_luaContext.setGlobal("BUTTON_EXTRA1", 4);
+	_luaContext.setGlobal("BUTTON_EXTRA2", 8);
+	_luaContext.setGlobal("BUTTON_L1", 0x10);
+	_luaContext.setGlobal("BUTTON_R1", 0x20);
+	_luaContext.setGlobal("BUTTON_START", 0x40);
+	_luaContext.setGlobal("BUTTON_UP", 0x80);
+	_luaContext.setGlobal("BUTTON_DOWN", 0x100);
+	_luaContext.setGlobal("BUTTON_LEFT", 0x200);
+	_luaContext.setGlobal("BUTTON_RIGHT", 0x400);
+	_luaContext.setGlobal("BUTTON_LS_CLIC", 0x800);
+	_luaContext.setGlobal("BUTTON_RS_CLIC", 0x1000);
+	_luaContext.setGlobal("BUTTON_BACK", 0x2000);
+	_luaContext.setGlobal("BUTTON_SELECT", 0x4000);
+	_luaContext.setGlobal("BUTTON_L2", 0x8000);
+	_luaContext.setGlobal("BUTTON_R2", 0x10000);
+	_luaContext.setGlobal("BUTTON_LS_UP", 0x20000);
+	_luaContext.setGlobal("BUTTON_LS_DOWN", 0x40000);
+	_luaContext.setGlobal("BUTTON_LS_LEFT", 0x80000);
+	_luaContext.setGlobal("BUTTON_LS_RIGHT", 0x100000);
+	_luaContext.setGlobal("BUTTON_RS_UP", 0x200000);
+	_luaContext.setGlobal("BUTTON_RS_DOWN", 0x400000);
+	_luaContext.setGlobal("BUTTON_RS_LEFT", 0x800000);
+	_luaContext.setGlobal("BUTTON_RS_RIGHT", 0x1000000);
+
+	_luaScript.attachToContext(&_luaContext);
+
+	if (!_objectif.gui1().loaded()) {
+		_objectif.load();
+	}
+	_question2.load();
+	_dialog2.load();
+	_documentsBrowser.load();
+	_documentsBrowser.loadZoomed();
+	_inventory.load();
+
+	_inventory.cellphone()->onCallNumber().add(this, &Game::onCallNumber);
+
+	if (!newgame) {
+		loadBackup(_loadName);
+	} else {
+		_gameLoadState = 1;
+		onFinishedLoadingBackup("");
+	}
+	_sceneCharacterVisibleFromLoad = true;
+	_scene._character->onFinished().remove(this, &Game::onDisplacementFinished);
+	_scene._character->onFinished().add(this, &Game::onDisplacementFinished);
+	_dialog2.prevSceneName().clear();
+	_notifier.load();
+}
+
+/*static*/ TeI3DObject2 *Game::findLayoutByName(TeLayout *parent, const Common::String &name) {
+	error("TODO: Implement me - although maybe this is never used?");
+}
+
+/*static*/ TeSpriteLayout *Game::findSpriteLayoutByName(TeLayout *parent, const Common::String &name) {
+	if (!parent)
+		return nullptr;
+
+	if (parent->name() == name)
+		return dynamic_cast<TeSpriteLayout *>(parent);
+
+	for (auto &child : parent->childList()) {
+		TeSpriteLayout *val = findSpriteLayoutByName(dynamic_cast<TeLayout *>(child), name);
+		if (val)
+			return val;
+	}
+
+	return nullptr;
+}
+
+void Game::finishFreemium() {
+	Application *app = g_engine->getApplication();
+	app->_finishedGame = true;
+	app->_finishedFremium = true;
+}
+
+void Game::finishGame() {
+	Application *app = g_engine->getApplication();
+	app->_finishedGame = true;
+	_playedTimer.stop();
+	/* Game does this but does nothing with result?
+	if (app->difficulty() == 2) {
+	  _playedTimer.getTimeFromStart();
+	} */
+	app->credits().enter(false);
+}
+
+void Game::initLoadedBackupData() {
+	if (!_loadName.empty()) {
+		error("TODO: Implemet Game::initLoadedBackupData loading part");
+	}
+	Application *app = g_engine->getApplication();
+	const Common::String firstWarpPath = app->_firstWarpPath;
+	_currentScene = app->_firstScene;
+	_currentZone = app->_firstZone;
+	_playedTimer.start();
+	_objectsTakenVal = 0;
+	for (int i = 0; i < ARRAYSIZE(_objectsTakenBits); i++) {
+		_objectsTakenBits[i] = 0;
+	}
+	_dialogsTold = 0;
+	if (_loadName == "NO_OWNER")
+		_luaShowOwnerError = true;
+	_gameLoadState = 0;
+	app->showLoadingIcon(false);
+	_loadName.clear();
+	initScene(true, firstWarpPath);
+}
+
+void Game::initNoScale() {
+	if (!_noScaleLayout) {
+		_noScaleLayout = new TeLayout();
+		_noScaleLayout->setName("noScaleLayout");
+		_noScaleLayout->setSizeType(TeILayout::RELATIVE_TO_PARENT);
+		_noScaleLayout->setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
+	}
+
+	if (!_noScaleLayout2) {
+		_noScaleLayout2 = new TeLayout();
+		_noScaleLayout2->setName("noScaleLayout2");
+		_noScaleLayout2->setSizeType(TeILayout::RELATIVE_TO_PARENT);
+		_noScaleLayout2->setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
+	}
+}
+
+void Game::initScene(bool fade, const Common::String &scenePath) {
+	_luaContext.setGlobal("SHOW_OWNER_ERROR", _luaShowOwnerError);
+	initWarp(_currentZone, _currentScene, fade);
+	loadScene(scenePath);
+	if (_scene._character->_model.get() && !_scene.findKate()) {
+		_scene.models().push_back(_scene._character->_model);
+	}
+	_scene._character->_model->setVisible(true);
+}
+
+void Game::initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
+	_inventoryMenu.unload();
+	_gui4.unload();
+	warning("Game::initWarp: set field_0x4248 false here");
+	_sceneCharacterVisibleFromLoad = true;
+
+	if (_scene._character) {
+		_scene._character->_model->setVisible(false);
+		_scene._character->deleteAllCallback();
+		_scene._character->stop();
+		_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true, false, false, -1, 9999);
+		if (!_scene.findKate()) {
+			_scene.models().push_back(_scene._character->_model);
+			_scene.models().push_back(_scene._character->_shadowModel[0]);
+			_scene.models().push_back(_scene._character->_shadowModel[1]);
+		}
+	}
+
+	_currentZone = zone;
+	_currentScene = scene;
+
+	_scene.loadBlockers();
+	Common::Path scenePath("scenes");
+	scenePath.joinInPlace(zone);
+	scenePath.joinInPlace(scene);
+	_sceneZonePath = scenePath;
+
+	const Common::Path intLuaPath = scenePath.join(Common::String::format("Int%s.lua", scene.c_str()));
+	const Common::Path logicLuaPath = scenePath.join(Common::String::format("Logic%s.lua", scene.c_str()));
+	const Common::Path setLuaPath = scenePath.join(Common::String::format("Set%s.lua", scene.c_str()));
+	const Common::Path forLuaPath = scenePath.join(Common::String::format("For%s.lua", scene.c_str()));
+	const Common::Path markerLuaPath = scenePath.join(Common::String::format("Marker%s.lua", scene.c_str()));
+
+	bool intLuaExists = Common::File::exists(intLuaPath);
+	bool logicLuaExists = Common::File::exists(logicLuaPath);
+	bool setLuaExists = Common::File::exists(setLuaPath);
+	bool forLuaExists = Common::File::exists(forLuaPath);
+	bool markerLuaExists = Common::File::exists(markerLuaPath);
+
+	if (!intLuaExists && !logicLuaExists && !setLuaExists && !forLuaExists && !markerLuaExists) {
+		debug("No lua scripts for scene %s zone %s", scene.c_str(), zone.c_str());
+		return;
+	}
+
+	warning("TODO: Game::initWarp: stop game sounds");
+
+	if (logicLuaExists) {
+		_luaContext.addBindings(LuaBinds::LuaOpenBinds);
+		_luaScript.attachToContext(&_luaContext);
+		_luaScript.load("menus/help/help.lua");
+		_luaScript.load(logicLuaPath);
+	}
+
+	if (_gui3.loaded())
+		_gui3.unload();
+
+	_scene.reset();
+	_scene.bgGui().unload();
+	_gui2.unload();
+	Common::Path geomPath(Common::String::format("scenes/%s/Geometry/%s.bin",
+												 zone.c_str(), zone.c_str()));
+	_scene.load(geomPath);
+	_scene.loadBackground(setLuaPath);
+
+	Application *app = g_engine->getApplication();
+	if (forLuaExists) {
+		_gui3.load(forLuaPath);
+		TeLayout *bg = _gui3.layout("background");
+		bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
+		app->_frontLayout.addChild(bg);
+		TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayout("background");
+		app->_frontLayout.removeChild(cellbg);
+		app->_frontLayout.addChild(cellbg);
+		_objectif.reattachLayout(&app->_frontLayout);
+	}
+
+	if (intLuaExists) {
+		_scene.loadInteractions(intLuaPath);
+		warning("TODO: Game::initWarp: Finish interactions.");
+	}
+
+	_inventoryMenu.load();
+	_gui4.load("InGame.lua");
+
+	TeButtonLayout *skipbtn = _gui4.buttonLayout("skipVideoButton");
+	skipbtn->setVisible(false);
+	skipbtn->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
+	skipbtn->onMouseClickValidated().add(this, &Game::onSkipVideoButtonValidated);
+
+	TeButtonLayout *vidbgbtn = _gui4.buttonLayout("videoBackgroundButton");
+	vidbgbtn->setVisible(false);
+	vidbgbtn->onMouseClickValidated().remove(this, &Game::onLockVideoButtonValidated);
+	vidbgbtn->onMouseClickValidated().add(this, &Game::onLockVideoButtonValidated);
+
+	TeSpriteLayout *video = _gui4.spriteLayout("video");
+	video->setVisible(false);
+	video->_tiledSurfacePtr->_frameAnim.onFinished().remove(this, &Game::onVideoFinished);
+	video->_tiledSurfacePtr->_frameAnim.onFinished().add(this, &Game::onVideoFinished);
+
+	TeButtonLayout *invbtn = _gui4.buttonLayout("inventoryButton");
+	invbtn->setSizeType(TeILayout::RELATIVE_TO_PARENT); // TODO: Double-check if this is the right virt fn.
+	invbtn->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
+	invbtn->onMouseClickValidated().add(this, &Game::onInventoryButtonValidated);
+
+	const TeVector3f32 winSize = app->getMainWindow().size();
+	if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
+		invbtn->setSize(TeVector3f32(0.12, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.12, 0.0));
+	} else {
+		invbtn->setSize(TeVector3f32(0.08, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.08, 0.0));
+	}
+
+	TeCheckboxLayout *markersCheckbox = _gui4.checkboxLayout("markersVisibleButton");
+	markersCheckbox->setVisible(!_markersVisible);
+	markersCheckbox->onStateChangedSignal().add(this, &Game::onMarkersVisible);
+
+	initNoScale();
+	removeNoScale2Children();
+	app->_frontLayout.removeChild(_noScaleLayout2);
+
+	TeLayout *vidLayout = _gui4.layout("videoLayout");
+	app->_frontLayout.removeChild(vidLayout);
+	removeNoScaleChildren();
+	app->_frontLayout.removeChild(_noScaleLayout);
+
+	app->_frontLayout.addChild(_noScaleLayout);
+	addNoScaleChildren();
+	app->_frontLayout.addChild(vidLayout);
+	app->_frontLayout.addChild(_noScaleLayout2);
+	addNoScale2Children();
+	if (!fadeFlag) {
+		if (_inventory.selectedObject().size()) {
+			_inventory.selectedObject(*_inventory.selectedInventoryObject());
+		}
+		_inventory.setVisible(true);
+		_objectif.setVisibleObjectif(false);
+		_objectif.setVisibleButtonHelp(true);
+		_running = true;
+		loadScene("save.xml");
+	}
+
+	app->_backLayout.addChild(_scene.background());
+
+	if (markerLuaExists) {
+		TeLayout *bg = _gui2.layout("background");
+		app->_frontLayout.addChild(bg);
+	}
+
+	Common::String camname = Common::String("Camera") + scene;
+	_scene.setCurrentCamera(camname);
+
+	// Special hacks for certain scenes (don't blame me, original does this..)
+	if (scene == "14050") {
+		TeIntrusivePtr<TeCamera> curcamera = _scene.currentCamera();
+		const TeVector3f32 coords(1200.6f,-1937.5f,1544.1f);
+		curcamera->setPosition(coords);
+	} else if (scene == "34610") {
+		TeIntrusivePtr<TeCamera> curcamera = _scene.currentCamera();
+		const TeVector3f32 coords(-328.243f,340.303f,-1342.84f);
+		curcamera->setPosition(coords);
+		const TeQuaternion rot(0.003194, 0.910923, -0.009496, -0.412389);
+		curcamera->setRotation(rot);
+	}
+
+	if (logicLuaExists) {
+		_exitZone.clear();
+		_luaScript.execute();
+		_luaScript.execute("OnEnter", _prevSceneName);
+		_luaScript.execute("OnSelectedObject", _inventory.selectedObject());
+	}
+
+	//for (auto & sound : _gameSounds) {
+	warning("TODO: Game::initWarp: Do game sound stuff here");
+
+	_scene.initScroll();
+}
+
+bool Game::isDocumentOpened() {
+	TeLayout *layout = _documentsBrowser.layout("zoomed");
+	return layout->visible();
+}
+
+bool Game::isMoviePlaying() {
+	TeButtonLayout *vidButton = _gui4.buttonLayout("videoBackgroundButton");
+	if (vidButton)
+		return vidButton->visible();
+	return false;
+}
+
+bool Game::launchDialog(const Common::String &param_1, uint param_2, const Common::String &param_3,
+				  const Common::String &param_4, float param_5)  {
+	error("TODO: Implemet Game::launchDialog");
+}
+
+void Game::leave(bool flag) {
+	error("TODO: Implemet Game::leave");
+}
+
+bool Game::loadBackup(const Common::String &path) {
+	error("TODO: Implemet Game::loadBackup");
+}
+
+bool Game::loadCharacter(const Common::String &name) {
+	bool result = true;
+	Character *character = _scene.character(name);
+	if (!character) {
+		result = false;
+		bool loaded = _scene.loadCharacter(name);
+		if (loaded) {
+			character = _scene.character(name);
+			character->_onCharacterAnimFinishedSignal.remove<Game>(this, &Game::onCharacterAnimationFinished);
+			character->_onCharacterAnimFinishedSignal.add<Game>(this, &Game::onCharacterAnimationFinished);
+			character->onFinished().add<Game>(this, &Game::onDisplacementFinished);
+		}
+	}
+	return result;
+}
+
+bool Game::loadPlayerCharacter(const Common::String &name) {
+	bool result = _scene.loadPlayerCharacter(name);
+	if (result) {
+		_scene._character->_characterAnimPlayerFinishedSignal.remove<Game>(this, &Game::onCharacterAnimationPlayerFinished);
+		_scene._character->_characterAnimPlayerFinishedSignal.add<Game>(this, &Game::onCharacterAnimationPlayerFinished);
+		_scene._character->onFinished().remove<Game>(this, &Game::onDisplacementFinished);
+		_scene._character->onFinished().add<Game>(this, &Game::onDisplacementFinished);
+	}
+	return result;
+}
+
+bool Game::loadScene(const Common::String &name) {
+	_luaScript.load("scenes/OnGameEnter.lua");
+	_luaScript.execute();
+	Character *character = _scene._character;
+	if (character && character->_model->visible()) {
+		_sceneCharacterVisibleFromLoad = true;
+	}
+	return false;
+}
+
+bool Game::onAnswered(const Common::String &val) {
+	_luaScript.execute("OnAnswered", TeVariant(val));
+	return false;
+}
+
+bool Game::onCallNumber(Common::String val) {
+	_luaScript.execute("OnCallNumber", TeVariant(val));
+	return false;
+}
+
+bool Game::onCharacterAnimationFinished(const Common::String &val) {
+	error("TODO: Implemet me");
+}
+
+bool Game::onCharacterAnimationPlayerFinished(const Common::String &val) {
+	error("TODO: Implemet me");
+}
+
+bool Game::onDialogFinished(const Common::String &val) {
+	error("TODO: Implemet me");
+}
+
+bool Game::onDisplacementFinished() {
+	error("TODO: Implemet me");
+}
+
+bool Game::onFinishedCheckBackup(bool result) {
+	if (_gameLoadState == 1) {
+		_gameLoadState = 0;
+		return true;
+	}
+	return false;
+}
+
+bool Game::onFinishedLoadingBackup(const Common::String &val) {
+	if (_gameLoadState == 1) {
+		_loadName = val;
+		_gameLoadState = 2;
+		return true;
+	}
+	return false;
+}
+
+bool Game::onFinishedSavingBackup(int something) {
+	if (something) {
+		g_engine->getGame()->_returnToMainMenu = true;
+	}
+	g_engine->getApplication()->showLoadingIcon(false);
+	return true;
+}
+
+bool Game::onInventoryButtonValidated() {
+	_inventoryMenu.enter();
+	return false;
+}
+
+bool Game::onLockVideoButtonValidated() {
+	TeButtonLayout *btn = _gui4.buttonLayoutChecked("skipVideoButton");
+	btn->setVisible(!btn->visible());
+	return true;
+}
+
+bool Game::onMarkersVisible(TeCheckboxLayout::State state) {
+	_markersVisible = (state == 0);
+	showMarkers(state == 0);
+	return false;
+}
+
+bool Game::onMouseClick(const Common::Point &pt)  {
+	Application *app = g_engine->getApplication();
+
+	if (app->isFading())
+		return true;
+
+	_posPlayer = TeVector3f32(-1.0f, -1.0f, -1.0f);
+	if (_previousMousePos == TeVector2s32(-1, -1)) {
+		_previousMousePos = pt;
+	} else {
+		TeVector3f32 winSize = app->getMainWindow().size();
+		TeVector2s32 lastMousePos = _previousMousePos;
+		_previousMousePos = pt;
+		float xdist = pt.x / winSize.x() - lastMousePos._x / winSize.x();
+		float ydist = pt.y / winSize.y() - lastMousePos._y / winSize.y();
+		float sqrdist = xdist * xdist + ydist * ydist;
+		if (sqrdist > 0.0001 && (_walkTimer._stopped || _walkTimer.timeElapsed() > 300000.0
+						 || (_scene._character && _scene._character->walkModeStr() != "Walk"))) {
+			return false;
+			// Double-click, but already jogging
+		}
+	}
+
+	if (!app->_frontLayout.isMouseIn(pt))
+		return false;
+
+	TeIntrusivePtr<TeCamera> curCamera = _scene.currentCamera();
+	//TePickMesh2 *nearestMesh = findNearestMesh(curCamera, _scene._pickMeshes, nullptr, false);
+
+	warning("TODO: Finish Game::onMouseClick");
+	return false;
+}
+
+// Note: None of these cursor files seem to be actually shipped with the game
+// but the logic is reproduced here just in case there's some different
+// version that uses them.
+static const char cursorsTable[][2][80] = {
+	{"H000", "pictures/F000.png"},
+	{"H045", "pictures/F045.png"},
+	{"H090", "pictures/F090.png"},
+	{"H135", "pictures/F135.png"},
+	{"H180", "pictures/F180.png"},
+	{"H225", "pictures/F225.png"},
+	{"H270", "pictures/F270.png"},
+	{"H315", "pictures/F315.png"},
+	{"Main", "pictures/Main.png"},
+	{"Action", "pictures/Action.png"},
+	{"Parler", "pictures/Cursor_Large/Anim_Cursor_Talking.anim"},
+	{"Type01", "pictures/Type01.png"},
+	{"Type02", "pictures/Type02.png"},
+	{"Type03", "pictures/Type03.png"},
+	{"Type04", "pictures/Type04.png"},
+	{"Type05", "pictures/Type05.png"}
+};
+
+bool Game::onMouseMove() {
+	if (!_entered)
+		return false;
+
+	static const Common::Path DEFAULT_CURSOR("pictures/cursor.png");
+
+	Application *app = g_engine->getApplication();
+
+	if (app->isLockCursor()) {
+		app->mouseCursorLayout().load(DEFAULT_CURSOR);
+		return false;
+	}
+
+	TeVector2s32 mouseLoc = g_engine->getInputMgr()->lastMousePos();
+	bool skipFullSearch = false;
+
+	TeLayout *cellphone = _inventory.cellphone()->gui().layoutChecked("cellphone");
+	TeLayout *cellbg = _inventory.cellphone()->gui().layoutChecked("background");
+	if (cellphone->isMouseIn(mouseLoc)) {
+		skipFullSearch = true;
+		if (!cellbg->visible() && _objectif.isMouseIn(mouseLoc)) {
+			app->mouseCursorLayout().load(DEFAULT_CURSOR);
+			return false;
+		}
+	}
+	if (_dialog2.gui().layout("imgDialog")) {
+		warning("TODO: Finish Game::onMouseMove");
+	}
+
+	bool checkedCursor = false;
+	for (unsigned int i = 0; i < cellbg->childCount(); i++) {
+		TeLayout *childlayout = dynamic_cast<TeLayout *>(cellbg->child(i));
+		if (childlayout && childlayout->isMouseIn(mouseLoc) && childlayout->visible()) {
+			for (int i = 0; i < ARRAYSIZE(cursorsTable); i++) {
+				if (childlayout->name().contains(cursorsTable[i][0])) {
+					app->mouseCursorLayout().load(cursorsTable[i][1]);
+					if (Common::String(cursorsTable[i][0]).contains(".anim")) {
+						app->mouseCursorLayout()._tiledSurfacePtr->_frameAnim._loopCount = -1;
+						app->mouseCursorLayout()._tiledSurfacePtr->_frameAnim.play();
+					}
+				}
+			}
+			checkedCursor = true;
+		}
+	}
+
+	if (!checkedCursor)
+		app->mouseCursorLayout().load(DEFAULT_CURSOR);
+	return false;
+}
+
+bool Game::onSkipVideoButtonValidated() {
+	TeSpriteLayout *sprite = _gui4.spriteLayoutChecked("video");
+	TeButtonLayout *btn = _gui4.buttonLayoutChecked("videoBackgroundButton");
+	sprite->stop();
+	btn->setVisible(false);
+	return false;
+}
+
+bool Game::onVideoFinished() {
+	if (!_gui4.loaded())
+		return false;
+
+	Application *app = g_engine->getApplication();
+
+	app->captureFade();
+
+	TeSpriteLayout *video = _gui4.spriteLayoutChecked("video");
+	TeButtonLayout *btn = _gui4.buttonLayoutChecked("videoBackgroundButton");
+	btn->setVisible(false);
+	btn = _gui4.buttonLayoutChecked("skipVideoButton");
+	btn->setVisible(false);
+	video->setVisible(false);
+	_music.stop();
+	_running = true;
+	warning("TODO: Game::onVideoFinished: update yieldedCallbacks");
+	_luaScript.execute("OnMovieFinished", video->_tiledSurfacePtr->path().toString());
+	app->fade();
+	return false;
+}
+
+void Game::pauseMovie() {
+	_music.pause();
+	TeSpriteLayout *sprite = _gui4.spriteLayoutChecked("video");
+	sprite->pause();
+}
+
+void Game::playMovie(const Common::String &vidPath, const Common::String &musicPath) {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	TeButtonLayout *videoBackgroundButton = _gui4.buttonLayoutChecked("videoBackgroundButton");
+	videoBackgroundButton->setVisible(true);
+
+	TeButtonLayout *skipVideoButton = _gui4.buttonLayoutChecked("skipVideoButton");
+	skipVideoButton->setVisible(false);
+
+	TeMusic &music = app->music();
+	music.stop();
+	music.setChannelName("video");
+	music.repeat(false);
+	music.volume(1.0f);
+	music.load(musicPath);
+
+	_running = false;
+
+	TeSpriteLayout *videoSpriteLayout = _gui4.spriteLayoutChecked("video");
+	videoSpriteLayout->load(vidPath);
+	videoSpriteLayout->setVisible(true);
+	music.play();
+	videoSpriteLayout->play();
+
+	app->fade();
+}
+
+void Game::playRandomSound(const Common::String &name) {
+	error("TODO: Implemet Game::playRandomSound");
+}
+
+void Game::playSound(const Common::String &name, int param_2, float param_3) {
+	error("TODO: Implemet Game::playSound");
+}
+
+void Game::removeNoScale2Child(TeLayout *layout) {
+	if (!_noScaleLayout2 || !layout)
+		return;
+	_noScaleLayout2->removeChild(layout);
+}
+
+void Game::removeNoScale2Children() {
+	if (!_noScaleLayout2)
+		return;
+
+	TeLayout *vidbtn = _gui4.layout("videoButtonLayout");
+	if (vidbtn)
+		_noScaleLayout2->removeChild(vidbtn);
+
+	TeLayout *bg = _inventory.cellphone()->gui().layout("background");
+	if (bg)
+		_noScaleLayout2->removeChild(bg);
+
+	TeButtonLayout *bgbtn = _objectif.gui1().buttonLayout("background");
+	if (bgbtn)
+		_noScaleLayout2->removeChild(bgbtn);
+
+	TeLayout *notifier = _notifier.gui().layout("notifier");
+	if (notifier)
+		_noScaleLayout2->removeChild(notifier);
+}
+
+void Game::removeNoScaleChildren() {
+	if (!_noScaleLayout)
+		return;
+	_noScaleLayout->removeChild(&_question2);
+	Application *app = g_engine->getApplication();
+	app->_frontLayout.removeChild(&_dialog2);
+	_noScaleLayout->removeChild(&_inventory);
+	_noScaleLayout->removeChild(&_inventoryMenu);
+	_noScaleLayout->removeChild(&_documentsBrowser);
+	_noScaleLayout->removeChild(&_documentsBrowser.zoomedLayout());
+}
+
+void Game::resetPreviousMousePos() {
+	_previousMousePos = TeVector2s32(-1, -1);
+}
+
+void Game::resumeMovie() {
+	_music.play();
+	_gui4.spriteLayout("video")->play();
+}
+
+void Game::saveBackup(const Common::String &saveName) {
+	error("TODO: Implemet me");
+}
+
+void Game::setBackground(const Common::String &name) {
+	_scene.changeBackground(name);
+}
+
+void Game::setCurrentObjectSprite(const Common::String &spritePath) {
+	TeSpriteLayout *currentSprite = _gui4.spriteLayout("currentObjectSprite");
+	if (currentSprite) {
+		if (!spritePath.empty()) {
+			currentSprite->unload();
+		} else {
+			currentSprite->load(spritePath);
+	  }
+	}
+}
+
+bool Game::showMarkers(bool val) {
+	error("TODO: Implemet me");
+}
+
+bool Game::startAnimation(const Common::String &animName, int param_2, bool param_3) {
+	error("TODO: Implemet me");
+}
+
+void Game::stopSound(const Common::String &name) {
+	error("TODO: Implemet me");
+}
+
+bool Game::unloadCharacter(const Common::String &charname) {
+	Character *c = _scene.character(charname);
+	if (!c)
+		return false;
+
+	for (unsigned int i = 0; i < _scene.models().size(); i++) {
+		if (_scene.models()[i] == c->_model) {
+			_scene.models().remove_at(i);
+			break;
+		}
+	}
+	c->_onCharacterAnimFinishedSignal.remove(this, &Game::onCharacterAnimationFinished);
+	c->removeAnim();
+	c->onFinished().remove(this, &Game::onDisplacementFinished);
+	_scene.unloadCharacter(charname);
+	return true;
+}
+
+bool Game::unloadCharacters() {
+	// Loop will update the array, take a copy first.
+	Common::Array<Character *> chars = _scene._characters;
+	for (Character *c : chars) {
+		unloadCharacter(c->_model->name());
+	}
+	return true;
+}
+
+bool Game::unloadPlayerCharacter(const Common::String &character) {
+	_scene.unloadPlayerCharacter(character);
+	return true;
+}
+
+void Game::update() {
+	if (!_entered)
+		return;
+
+	TeTextLayout *debugTimeTextLayout = _gui4.textLayout("debugTimeText1");
+	if (debugTimeTextLayout) {
+		warning("TODO: Game::update: Fill out debugTimeTextLayout");
+	}
+
+	if (!_warped) {
+		if (_gameLoadState == 2) {
+			initLoadedBackupData();
+			return;
+		} else if (_gameLoadState != 0) {
+			return;
+		}
+
+		Application *app = g_engine->getApplication();
+
+		if (_scene._character) {
+			if (!_scene._character->_model->visible())
+				app->_lockCursorButton.setVisible(false);
+		}
+
+		TeButtonLayout *invbtn = _gui4.buttonLayout("inventoryButton");
+		if (invbtn)
+			invbtn->setVisible(!app->isLockCursor() && !_dialog2.isDialogPlaying());
+		else
+			warning("Game::update: InventoryButton is null.");
+
+		if (_scene._character) {
+			TeIntrusivePtr<TeModel> model = _scene._character->_model;
+			bool modelVisible = model->visible();
+			if (!model->anim().get())
+				_scene._character->permanentUpdate();
+			if (modelVisible) {
+				if (_scene._character->needsSomeUpdate()) {
+					Game *game = g_engine->getGame();
+					game->_sceneCharacterVisibleFromLoad = false;
+					error("TODO: Game::update: Finish bit after permanentUpdate");
+				}
+
+				const Common::String &charAnim = _scene._character->curAnimName();
+				bool lockCursor = (charAnim == _scene._character->walkAnim(Character::WalkPart_Start) ||
+						charAnim == _scene._character->walkAnim(Character::WalkPart_Loop) ||
+						charAnim == _scene._character->walkAnim(Character::WalkPart_EndD) ||
+						charAnim == _scene._character->walkAnim(Character::WalkPart_EndG) ||
+						charAnim == _scene._character->characterSettings()._walkFileName);
+				app->lockCursor(lockCursor);
+			}
+		}
+
+		Common::Array<Character *> characters = _scene._characters;
+		for (Character *c : characters) {
+			if (!c->_model->anim().get())
+				c->permanentUpdate();
+		}
+
+		Common::Point mousePos = g_engine->getInputMgr()->lastMousePos();
+		if (_lastUpdateMousePos != mousePos) {
+			onMouseMove();
+			_lastUpdateMousePos = mousePos;
+		}
+		if (_saveRequested) {
+			_saveRequested = false;
+			error("TODO: Game::update: Save game");
+		}
+
+		_luaScript.execute("Update");
+		_objectif.update();
+		_scene.update();
+	} else {
+		warning("TODO: Game::update: Stop sounds before warping");
+		changeWarp2(_warpZone, _warpScene, _warpFadeFlag);
+	}
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
new file mode 100644
index 00000000000..62906a4ee61
--- /dev/null
+++ b/engines/tetraedge/game/game.h
@@ -0,0 +1,233 @@
+/* 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_GAME_H
+#define TETRAEDGE_GAME_GAME_H
+
+#include "common/types.h"
+#include "common/str.h"
+#include "tetraedge/game/documents_browser.h"
+#include "tetraedge/game/inventory.h"
+#include "tetraedge/game/inventory_menu.h"
+#include "tetraedge/game/in_game_scene.h"
+#include "tetraedge/game/notifier.h"
+#include "tetraedge/game/cellphone.h"
+#include "tetraedge/game/objectif.h"
+#include "tetraedge/game/question2.h"
+#include "tetraedge/game/dialog2.h"
+#include "tetraedge/te/te_lua_gui.h"
+#include "tetraedge/te/te_music.h"
+#include "tetraedge/te/te_checkbox_layout.h"
+#include "tetraedge/te/te_vector2s32.h"
+
+namespace Tetraedge {
+
+class Game {
+public:
+	Game();
+
+	class HitObject {
+		byte OnChangeWarp();
+		byte OnDown();
+		byte OnUp();
+		byte OnValidated();
+		//byte OnVisible(); empty never used?
+	};
+
+	class RandomSound {
+	public:
+		Common::Path _path;
+		Common::String _str;
+		TeMusic _music;
+		float f1;
+		float f2;
+		byte onSoundFinished();
+	};
+
+	//enum EGameScoreID {}; // Not needed?
+
+	bool addAnimToSet(const Common::String &path);
+	void addArtworkUnlocked(const Common::String &name, bool notify);
+	void addNoScale2Child(TeLayout *layout);
+	void addNoScale2Children();
+	void addNoScaleChildren();
+	void addRandomSound(const Common::String &s1, const Common::String &s2, float f1, float f2);
+	void addToBag(const Common::String &objname);
+	void addToHand(const Common::String &objname);
+	void addToScore(int score);
+	void attachButtonsLayoutGoto() {}; // does nothing?
+	void createButtonsLayoutGoto() {}; // does nothing?
+	void deleteButtonsLayoutGoto() {}; // does nothing?
+
+	bool changeWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag);
+	bool changeWarp2(const Common::String &zone, const Common::String &scene, bool fadeFlag);
+
+	void deleteNoScale();
+	void draw();
+	void enter(bool newgame);
+	// Note: game uses ILayouts here..
+	static TeI3DObject2 *findLayoutByName(TeLayout *layout, const Common::String &name);
+	static TeSpriteLayout *findSpriteLayoutByName(TeLayout *layout, const Common::String &name);
+
+	void finishFreemium();
+	void finishGame();
+	void initLoadedBackupData();
+	void initNoScale();
+	void initScene(bool param_1, const Common::String &scenePath);
+	void initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag);
+	bool isDocumentOpened();
+	bool isMouse() { return false; }
+	bool isMoviePlaying();
+	bool launchDialog(const Common::String &param_1, uint param_2, const Common::String &param_3,
+					  const Common::String &param_4, float param_5);
+	void leave(bool flag);
+	bool loadBackup(const Common::String &path);
+	bool loadCharacter(const Common::String &name);
+	bool loadPlayerCharacter(const Common::String &name);
+	bool loadScene(const Common::String &name);
+
+	bool onAnswered(const Common::String &val);
+	bool onCallNumber(Common::String val);
+	bool onCharacterAnimationFinished(const Common::String &val);
+	bool onCharacterAnimationPlayerFinished(const Common::String &val);
+	bool onDialogFinished(const Common::String &val);
+	bool onDisplacementFinished();
+	bool onFinishedCheckBackup(bool result);
+	bool onFinishedLoadingBackup(const Common::String &val);
+	bool onFinishedSavingBackup(int something);
+	bool onInventoryButtonValidated();
+	bool onLockVideoButtonValidated();
+	bool onMarkersVisible(TeCheckboxLayout::State state);
+	bool onMouseClick(const Common::Point &pt);
+	bool onMouseMove();
+	bool onSkipVideoButtonValidated();
+	bool onVideoFinished();
+
+	void pauseMovie();
+	void pauseSounds() {}; // does nothing?
+	void playMovie(const Common::String &vidPath, const Common::String &musicPath);
+	void playRandomSound(const Common::String &name);
+	void playSound(const Common::String &name, int param_2, float param_3);
+	void removeNoScale2Child(TeLayout *layout);
+	void removeNoScale2Children();
+	void removeNoScaleChildren();
+	void resetPreviousMousePos();
+	void resumeMovie();
+	void resumeSounds() {}; // does nothing?
+	void saveBackup(const Common::String &saveName);
+	void setBackground(const Common::String &name);
+	void setCurrentObjectSprite(const Common::String &spritePath);
+	bool showMarkers(bool val);
+	bool startAnimation(const Common::String &animName, int param_2, bool param_3);
+	void startAnimationPart(const Common::String &param_1, int param_2, int param_3, int param_4, bool param_5) {};
+	void stopSound(const Common::String &name);
+	bool unloadCharacter(const Common::String &character);
+	bool unloadCharacters();
+	bool unloadPlayerCharacter(const Common::String &character);
+	void update();
+
+	InventoryMenu &inventoryMenu() { return _inventoryMenu; }
+	Inventory &inventory() { return _inventory; }
+	DocumentsBrowser &documentsBrowser() { return _documentsBrowser; }
+	bool entered() const { return _entered; }
+	bool running() const { return _running; }
+	bool luaShowOwnerError() const { return _luaShowOwnerError; }
+
+	bool _returnToMainMenu;
+	bool _firstInventory;
+
+	const Common::String &currentZone() { return _currentZone; }
+	const Common::String &currentScene() { return _currentScene; }
+	TeLuaScript &luaScript() { return _luaScript; }
+	InGameScene &scene() { return _scene; }
+
+private:
+	bool _luaShowOwnerError;
+	bool _running;
+	bool _entered;
+
+	TeLuaGUI _gui1;
+	TeLuaGUI _gui2;
+	TeLuaGUI _gui3;
+	TeLuaGUI _gui4;
+
+	Inventory _inventory;
+	InventoryMenu _inventoryMenu;
+	int _score;
+
+	int _frameCounter;
+
+	InGameScene _scene;
+
+	static char **_objectsTakenIDs;
+
+	TeVector2s32 _previousMousePos;
+
+	Common::String _warpZone;
+	Common::String _warpScene;
+	bool _warpFadeFlag;
+	bool _warped;
+
+	Common::String _currentZone;
+	Common::String _currentScene;
+	Common::String _exitZone;
+	Common::String _prevSceneName;
+	Common::Path _sceneZonePath;
+
+	Common::String _loadName;
+
+	Common::HashMap<Common::String, bool> _unlockedArtwork;
+
+	int _gameLoadState;
+
+	TeTimer _playedTimer;
+	TeTimer _walkTimer;
+	TeLuaScript _luaScript;
+	TeLuaContext _luaContext;
+	TeMusic _music;
+	Notifier _notifier;
+	DocumentsBrowser _documentsBrowser;
+
+	Question2 _question2;
+	Dialog2 _dialog2;
+	Objectif _objectif;
+
+	static const int NUM_OBJECTS_TAKEN_IDS = 5;
+	static const char *OBJECTS_TAKEN_IDS[NUM_OBJECTS_TAKEN_IDS];
+	bool _objectsTakenBits[NUM_OBJECTS_TAKEN_IDS];
+	int _objectsTakenVal;
+	int _dialogsTold;
+
+	bool _sceneCharacterVisibleFromLoad;
+	bool _markersVisible;
+	bool _saveRequested;
+
+	TeLayout *_noScaleLayout;
+	TeLayout *_noScaleLayout2;
+
+	TeVector3f32 _posPlayer;
+
+	Common::Point _lastUpdateMousePos;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_GAME_H
diff --git a/engines/tetraedge/game/game_achievements.cpp b/engines/tetraedge/game/game_achievements.cpp
new file mode 100644
index 00000000000..5a28713f797
--- /dev/null
+++ b/engines/tetraedge/game/game_achievements.cpp
@@ -0,0 +1,59 @@
+/* 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/game/game_achievements.h"
+#include "tetraedge/te/te_lua_context.h"
+
+namespace Tetraedge {
+
+GameAchievements::GameAchievements() {
+}
+
+/*static*/ void GameAchievements::registerAchievements(TeLuaContext &context) {
+	context.setGlobal("PS3_Welcome", 0);
+	context.setGlobal("PS3_Legacy", -1);
+	context.setGlobal("PS3_Graduate", -2);
+	context.setGlobal("PS3_Easten", -3);
+	context.setGlobal("PS3_Odyssey", -4);
+	context.setGlobal("PS3_Code", -5);
+	context.setGlobal("PS3_Escape", -6);
+	context.setGlobal("PS3_Amerzone", -7);
+	context.setGlobal("PS3_Music", -8);
+	context.setGlobal("PS3_OnWay", -9);
+	context.setGlobal("PS3_Gossip", -10);
+	context.setGlobal("PS3_Snoop", -0xb);
+	context.setGlobal("PS3_School", -0xc);
+	context.setGlobal("XBOX_Welcome", 1);
+	context.setGlobal("XBOX_Legacy", 2);
+	context.setGlobal("XBOX_Graduate", 3);
+	context.setGlobal("XBOX_Easten", 4);
+	context.setGlobal("XBOX_Odyssey", 5);
+	context.setGlobal("XBOX_Code", 6);
+	context.setGlobal("XBOX_Escape", 7);
+	context.setGlobal("XBOX_Amerzone", 8);
+	context.setGlobal("XBOX_Music", 9);
+	context.setGlobal("XBOX_OnWay", 10);
+	context.setGlobal("XBOX_Gossip", 0xb);
+	context.setGlobal("XBOX_Snoop", 0xc);
+	context.setGlobal("XBOX_School", 0xd);
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/game_achievements.h b/engines/tetraedge/game/game_achievements.h
new file mode 100644
index 00000000000..c9cf6b9e75d
--- /dev/null
+++ b/engines/tetraedge/game/game_achievements.h
@@ -0,0 +1,44 @@
+/* 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_GAME_ACHIEVEMENTS_H
+#define TETRAEDGE_GAME_GAME_ACHIEVEMENTS_H
+
+
+namespace Tetraedge {
+
+class TeLuaContext;
+
+class GameAchievements {
+public:
+	GameAchievements();
+
+	static void registerAchievements(TeLuaContext &context);
+	// TODO add public members
+
+private:
+	// TODO add private members
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_GAME_ACHIEVEMENTS_H
diff --git a/engines/tetraedge/game/game_sound.cpp b/engines/tetraedge/game/game_sound.cpp
new file mode 100644
index 00000000000..257f2b89d33
--- /dev/null
+++ b/engines/tetraedge/game/game_sound.cpp
@@ -0,0 +1,31 @@
+/* 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/game/game_sound.h"
+
+namespace Tetraedge {
+
+GameSound::GameSound() {
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/game_sound.h b/engines/tetraedge/game/game_sound.h
new file mode 100644
index 00000000000..2ea73e8edfd
--- /dev/null
+++ b/engines/tetraedge/game/game_sound.h
@@ -0,0 +1,43 @@
+/* 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_GAME_SOUND_H
+#define TETRAEDGE_GAME_GAME_SOUND_H
+
+#include "common/str.h"
+#include "tetraedge/te/te_music.h"
+
+namespace Tetraedge {
+
+class GameSound : public TeMusic {
+public:
+	GameSound();
+
+	void onSoundStopped();
+
+private:
+	Common::String _name;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_GAME_SOUND_H
diff --git a/engines/tetraedge/game/global_bonus_menu.cpp b/engines/tetraedge/game/global_bonus_menu.cpp
new file mode 100644
index 00000000000..610e79884a3
--- /dev/null
+++ b/engines/tetraedge/game/global_bonus_menu.cpp
@@ -0,0 +1,96 @@
+/* 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/application.h"
+#include "tetraedge/game/global_bonus_menu.h"
+
+namespace Tetraedge {
+
+GlobalBonusMenu::GlobalBonusMenu() : _entered(false) {
+}
+
+void GlobalBonusMenu::enter() {
+	error("TODO: Finish implementing GlobalBonusMenu::enter");
+	//Application *app = g_engine->getApplication();
+	//todo: call some virtual function on a field in app
+	//app->captureFade();
+	//_entered = true;
+	//load("menus/bonusmenu/GlobalBonusMenu.lua");
+	//TeLayout *menu = layout("menu");
+	//if (menu) {
+	//   ...
+	//}
+}
+
+void GlobalBonusMenu::leave() {
+	if (_entered != 0) {
+		Application *app = g_engine->getApplication();
+		app->captureFade();
+		TeLuaGUI::unload();
+		app->fade();
+		_entered = false;
+	}
+}
+
+bool GlobalBonusMenu::onSomeButtonValidated(const char *script) {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	leave();
+	app->bonusMenu().enter(script);
+	app->fade();
+	return false;
+}
+
+bool GlobalBonusMenu::onAraButtonValidated() {
+	return onSomeButtonValidated("menus/bonusmenu/Ara.lua");
+}
+
+bool GlobalBonusMenu::onBarButtonValidated() {
+	return onSomeButtonValidated("menus/bonusmenu/Bar.lua");
+}
+
+bool GlobalBonusMenu::onCitButtonValidated() {
+	return onSomeButtonValidated("menus/bonusmenu/Cit.lua");
+}
+
+bool GlobalBonusMenu::onSyb2ButtonValidated() {
+	return onSomeButtonValidated("menus/bonusmenu/Syb2.lua");
+}
+
+bool GlobalBonusMenu::onSyb3ButtonValidated() {
+	return onSomeButtonValidated("menus/bonusmenu/Syb3.lua");
+}
+
+bool GlobalBonusMenu::onValButtonValidated() {
+	return onSomeButtonValidated("menus/bonusmenu/Val.lua");
+}
+
+bool GlobalBonusMenu::onQuitButton() {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	leave();
+	app->mainMenu().enter();
+	app->fade();
+	return true;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/global_bonus_menu.h b/engines/tetraedge/game/global_bonus_menu.h
new file mode 100644
index 00000000000..d1b10d7344d
--- /dev/null
+++ b/engines/tetraedge/game/global_bonus_menu.h
@@ -0,0 +1,54 @@
+/* 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_GLOBAL_BONUS_MENU_H
+#define TETRAEDGE_GAME_GLOBAL_BONUS_MENU_H
+
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class GlobalBonusMenu : public TeLuaGUI {
+public:
+	GlobalBonusMenu();
+
+	void enter() override;
+	void leave() override;
+
+	bool onAraButtonValidated();
+	bool onBarButtonValidated();
+	bool onCitButtonValidated();
+	bool onSyb2ButtonValidated();
+	bool onSyb3ButtonValidated();
+	bool onValButtonValidated();
+
+	bool onQuitButton();
+
+private:
+	bool onSomeButtonValidated(const char *script);
+
+	bool _entered;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_GLOBAL_BONUS_MENU_H
diff --git a/engines/tetraedge/game/help_option_menu.cpp b/engines/tetraedge/game/help_option_menu.cpp
new file mode 100644
index 00000000000..0e17ec9c287
--- /dev/null
+++ b/engines/tetraedge/game/help_option_menu.cpp
@@ -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/>.
+ *
+ */
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/game/help_option_menu.h"
+
+namespace Tetraedge {
+
+HelpOptionMenu::HelpOptionMenu() : _entered(false) {
+}
+
+void HelpOptionMenu::enter() {
+	if (!_entered) {
+		Application *app = g_engine->getApplication();
+		app->captureFade();
+		load("menus/helpoptionMenu/optionsMenu.lua");
+		error("TODO: finish implementation of HelpOptionMenu::enter");
+	}
+}
+
+void HelpOptionMenu::leave() {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	unload();
+	app->fade();
+	_entered = false;
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/help_option_menu.h b/engines/tetraedge/game/help_option_menu.h
new file mode 100644
index 00000000000..056c0cccc34
--- /dev/null
+++ b/engines/tetraedge/game/help_option_menu.h
@@ -0,0 +1,43 @@
+/* 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_HELP_OPTION_MENU_H
+#define TETRAEDGE_GAME_HELP_OPTION_MENU_H
+
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class HelpOptionMenu : public TeLuaGUI {
+public:
+	HelpOptionMenu();
+
+	void enter() override;
+	void leave() override;
+
+private:
+	bool _entered;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_HELP_OPTION_MENU_H
diff --git a/engines/tetraedge/game/how_to.cpp b/engines/tetraedge/game/how_to.cpp
new file mode 100644
index 00000000000..a863d4e8729
--- /dev/null
+++ b/engines/tetraedge/game/how_to.cpp
@@ -0,0 +1,38 @@
+/* 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/game/how_to.h"
+
+namespace Tetraedge {
+
+HowTo::HowTo() : _entered(false) {
+}
+
+void HowTo::leave()	{
+
+}
+
+void HowTo::enter()	{
+
+}
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/how_to.h b/engines/tetraedge/game/how_to.h
new file mode 100644
index 00000000000..d4ce9211887
--- /dev/null
+++ b/engines/tetraedge/game/how_to.h
@@ -0,0 +1,47 @@
+/* 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_HOW_TO_H
+#define TETRAEDGE_GAME_HOW_TO_H
+
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class HowTo : public TeLuaGUI {
+public:
+	HowTo();
+
+	void enter() override;
+	void leave() override;
+
+	bool onDefaultPadButtonUp(uint32 flags);
+
+private:
+	bool updateDisplay(uint oldval, uint newval);
+
+	bool _entered;
+	uint _val;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_HOW_TO_H
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
new file mode 100644
index 00000000000..92f4eb2f52c
--- /dev/null
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -0,0 +1,239 @@
+/* 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 "common/file.h"
+#include "common/path.h"
+#include "common/textconsole.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/game/billboard.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/in_game_scene.h"
+#include "tetraedge/game/character.h"
+#include "tetraedge/game/object3d.h"
+
+namespace Tetraedge {
+
+InGameScene::InGameScene() : _character(nullptr) {
+}
+
+void InGameScene::draw() {
+	error("TODO: implement InGameScene::draw");
+}
+
+bool InGameScene::changeBackground(const Common::String &name) {
+	if (Common::File::exists(name)) {
+		TeSpriteLayout *spriteLayout = _bgGui.spriteLayout("root");
+		assert(spriteLayout);
+		spriteLayout->load(name);
+		return true;
+	}
+	return false;
+}
+
+
+/*static*/ float InGameScene::angularDistance(float a1, float a2) {
+	float result;
+
+	result = a2 - a1;
+	if (result >= -3.141593 && result > 3.141593) {
+		result = result + -6.283185;
+	} else {
+		result = result + 6.283185;
+	}
+	return result;
+}
+
+TeLayout *InGameScene::background() {
+	return _bgGui.layout("background");
+}
+
+void InGameScene::close() {
+	reset();
+	error("TODO: Implement InGameScene::close");
+}
+
+void InGameScene::reset() {
+	if (_character)
+		_character->setFreeMoveZone(Common::SharedPtr<TeFreeMoveZone>());
+	freeSceneObjects();
+	_bgGui.unload();
+	unloadSpriteLayouts();
+	_gui2.unload();
+	_gui3.unload();
+}
+
+void InGameScene::deleteAllCallback() {
+	warning("TODO: implement InGameScene::deleteAllCallback");
+}
+
+
+void InGameScene::freeSceneObjects() {
+	if (_character) {
+		warning("TODO: InGameScene::freeSceneObjects: Set field on character here");
+		_character->deleteAllCallback();
+	}
+	if (_characters.size() == 1) {
+		_characters[0]->deleteAllCallback();
+	}
+
+	Game *game = g_engine->getGame();
+	game->unloadCharacters();
+
+	_characters.clear();
+
+	for (Object3D *obj : _object3Ds) {
+		obj->deleteLater();
+	}
+	_object3Ds.clear();
+
+	for (Billboard *bb : _billboards) {
+		bb->deleteLater();
+	}
+	_billboards.clear();
+
+	for (TeSpriteLayout *sprite : _sprites) {
+		sprite->deleteLater();
+	}
+	_sprites.clear();
+
+	deleteAllCallback();
+	_markers.clear();
+
+	for (InGameScene::AnchorZone *zone : _anchorZones) {
+		delete zone;
+	}
+	_anchorZones.clear();
+}
+
+void InGameScene::unloadSpriteLayouts() {
+	for (auto *animobj : _animObjects) {
+		delete animobj;
+	}
+	_animObjects.clear();
+}
+
+Character *InGameScene::character(const Common::String &name) {
+	error("TODO: Implement InGameScene::character");
+}
+
+bool InGameScene::loadCharacter(const Common::String &name) {
+	error("TODO: Implement InGameScene::loadCharacter");
+}
+
+bool InGameScene::loadPlayerCharacter(const Common::String &name) {
+	if (_character == nullptr) {
+		_character = new Character();
+		if (!_character->loadModel(name, true)) {
+			_playerCharacterModel.release();
+			return false;
+		}
+
+		_playerCharacterModel = _character->_model;
+
+		if (!findKate()) {
+			Common::Array<TeIntrusivePtr<TeModel>> &ms = models();
+			ms.push_back(_character->_model);
+			ms.push_back(_character->_shadowModel[0]);
+			ms.push_back(_character->_shadowModel[1]);
+		}
+	}
+
+	_character->_model->setVisible(true);
+	return true;
+}
+
+void InGameScene::unloadPlayerCharacter(const Common::String &name) {
+	error("TODO: Implement InGameScene::unloadPlayerCharacter %s", name.c_str());
+}
+
+void InGameScene::unloadCharacter(const Common::String &name) {
+	warning("TODO: Implement InGameScene::unloadCharacter %s", name.c_str());
+}
+
+bool InGameScene::findKate() {
+	for (auto &m : models()) {
+		if (m->name() == "Kate")
+			return true;
+	}
+	return false;
+}
+
+Common::Path InGameScene::getBlockersFileName() {
+	Game *game = g_engine->getGame();
+	Common::Path retval("scenes");
+	retval.joinInPlace(game->currentZone());
+	retval.joinInPlace(game->currentScene());
+	retval.joinInPlace("blockers.bin");
+	return retval;
+}
+
+void InGameScene::loadBlockers() {
+	_blockers.clear();
+	_rectBlockers.clear();
+	const Common::Path blockersPath = getBlockersFileName();
+	if (Common::File::exists(blockersPath)) {
+		error("TODO: Implement InGameScene::loadBlockers");
+	}
+}
+
+void InGameScene::loadBackground(const Common::Path &path) {
+	_bgGui.load(path);
+	TeLayout *bg = _bgGui.layout("background");
+	TeLayout *root = _bgGui.layout("root");
+	bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
+	root->setRatioMode(TeILayout::RATIO_MODE_NONE);
+	TeCamera *wincam = g_engine->getApplication()->mainWindowCamera();
+	bg->disableAutoZ();
+	bg->setZPosition(wincam->_orthNearVal);
+
+	for (auto layoutEntry : _bgGui.spriteLayouts()) {
+		AnimObject *animobj = new AnimObject();
+		animobj->_name = layoutEntry._key;
+		animobj->_layout = layoutEntry._value;
+		animobj->_layout->_tiledSurfacePtr->_frameAnim.onFinished().add<AnimObject>(animobj, &AnimObject::onFinished);
+		if (animobj->_name != "root")
+			animobj->_layout->setVisible(false);
+		_animObjects.push_back(animobj);
+	}
+}
+
+void InGameScene::loadInteractions(const Common::Path &path) {
+	error("TODO: Implement InGameScene::loadInteractions");
+}
+
+void InGameScene::setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2) {
+	SoundStep ss;
+	ss._stepSound1 = step1;
+	ss._stepSound2 = step2;
+	_soundSteps[scene] = ss;
+}
+
+void InGameScene::initScroll() {
+	_someScrollVector = TeVector2f32(0.5f, 0.0f);
+}
+
+bool InGameScene::AnimObject::onFinished() {
+	error("TODO: Implement InGameScene::AnimObject::onFinished");
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
new file mode 100644
index 00000000000..d0e73c94eeb
--- /dev/null
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -0,0 +1,131 @@
+/* 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_IN_GAME_SCENE_H
+#define TETRAEDGE_GAME_IN_GAME_SCENE_H
+
+#include "common/array.h"
+#include "common/str.h"
+#include "common/hashmap.h"
+
+#include "tetraedge/game/object3d.h"
+#include "tetraedge/game/billboard.h"
+#include "tetraedge/te/te_scene.h"
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class Character;
+class TeLayout;
+
+class InGameScene : public TeScene {
+public:
+	InGameScene();
+
+	struct AnimObject {
+		bool onFinished();
+
+		Common::String _name;
+		TeSpriteLayout *_layout;
+	};
+
+	struct SoundStep {
+		Common::String _stepSound1;
+		Common::String _stepSound2;
+	};
+
+	class AnchorZone {
+	};
+
+	class TeMarker {
+	};
+
+	void activateAnchorZone(const Common::String &name, bool param_2);
+	void addAnchorZone(const Common::String &param_1, const Common::String &param_2, float param_3);
+	void addBlockingObject(const Common::String &obj) {
+		_blockingObjects.push_back(obj);
+	}
+	void addCallbackAnimation2D(const Common::String &param_1, const Common::String &param_2, float param_3);
+	void addMarker(const Common::String &name, const Common::String &param_2, float param_3, float param_4, const Common::String &param_5, const Common::String &param_6);
+	static float angularDistance(float a1, float a2);
+	bool aroundAnchorZone(const AnchorZone *zone);
+	TeLayout *background();
+	void loadBackground(const Common::Path &path);
+	void loadInteractions(const Common::Path &path);
+	void initScroll();
+
+	void draw();
+	Character *character(const Common::String &name);
+	bool loadCharacter(const Common::String &name);
+	bool loadPlayerCharacter(const Common::String &name);
+	bool changeBackground(const Common::String &name);
+	void unloadPlayerCharacter(const Common::String &character);
+	void unloadCharacter(const Common::String &name);
+
+	void close();
+	void reset();
+	void freeSceneObjects();
+	void unloadSpriteLayouts();
+	void deleteAllCallback();
+
+	void setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2);
+
+	void loadBlockers();
+	Common::Path getBlockersFileName();
+
+	// Does nothing, but to keep calls from original..
+	static void updateScroll() {};
+
+	bool findKate();
+
+	Character *_character;
+	Common::Array<Character *> _characters;
+
+	TeLuaGUI &bgGui() { return _bgGui; }
+
+private:
+	struct TeBlocker {};
+	struct TeRectBlocker {};
+
+	Common::Array<TeBlocker> _blockers;
+	Common::Array<TeRectBlocker> _rectBlockers;
+	Common::Array<TeMarker *> _markers;
+	Common::Array<AnchorZone *> _anchorZones;
+	Common::Array<AnimObject *> _animObjects;
+	Common::Array<Object3D *> _object3Ds;
+	Common::Array<Billboard *> _billboards;
+	Common::Array<TeSpriteLayout *> _sprites;
+
+	Common::HashMap<Common::String, SoundStep> _soundSteps;
+
+	TeIntrusivePtr<TeModel> _playerCharacterModel;
+	Common::Array<Common::String> _blockingObjects;
+	TeLuaGUI _bgGui;
+	TeLuaGUI _gui2; // TODO: find a better name.
+	TeLuaGUI _gui3; // TODO: find a better name.
+	// TODO add private members
+
+	TeVector2f32 _someScrollVector;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_IN_GAME_SCENE_H
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
new file mode 100644
index 00000000000..ee1be431277
--- /dev/null
+++ b/engines/tetraedge/game/inventory.cpp
@@ -0,0 +1,309 @@
+/* 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 "common/textconsole.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/game/cellphone.h"
+#include "tetraedge/game/character.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/inventory.h"
+#include "tetraedge/game/inventory_object.h"
+#include "tetraedge/game/inventory_objects_xml_parser.h"
+
+#include "tetraedge/te/te_core.h"
+
+namespace Tetraedge {
+
+Inventory::Inventory() : _cellphone(nullptr), _selectedObject(nullptr) {
+}
+
+void Inventory::enter() {
+	setVisible(true);
+	Game *game = g_engine->getGame();
+	Character *character = game->scene()._character;
+	character->stop();
+	character->setAnimation(character->characterSettings()._walkFileName, true, false, false, -1, 9999);
+	_gui.layoutChecked("textObject")->setVisible(false);
+
+	if (!game->_firstInventory) {
+		_gui.buttonLayoutChecked("Aide")->setVisible(false);
+	} else {
+		game->_firstInventory = false;
+	}
+
+	if (_selectedObject)
+		selectedObject(*_selectedObject);
+}
+
+void Inventory::leave() {
+	setVisible(false);
+	if (_selectedObject) {
+		Game *game = g_engine->getGame();
+		if (game->entered())
+			game->luaScript().execute("OnSelectedObject", _selectedObject->name());
+	}
+}
+
+void Inventory::load() {
+	setName("inventory");
+	setSizeType(RELATIVE_TO_PARENT);
+	setSize(TeVector3f32(1.0f, 1.0f, userSize().z()));
+	_gui.load("Inventory/Inventory.lua");
+	TeLayout *invlayout = _gui.layout("inventory");
+	if (!invlayout)
+		error("Inventory::load: Couldn't find inventory layout after loading");
+	addChild(invlayout);
+
+	TeButtonLayout *btn;
+	btn = _gui.buttonLayoutChecked("cellphone");
+	btn->onMouseClickValidated().add(this, &Inventory::onVisibleCellphone);
+
+	btn = _gui.buttonLayoutChecked("prendre");
+	btn->setVisible(false);
+	btn->onMouseClickValidated().add(this, &Inventory::onTakeObjectSelected);
+
+	btn = _gui.buttonLayoutChecked("lire");
+	btn->setVisible(false);
+	btn->setEnable(false);
+	btn->onMouseClickValidated().add(this, &Inventory::onZoomed);
+
+	btn = _gui.buttonLayoutChecked("quitButton");
+	btn->setVisible(true);
+	btn->onMouseClickValidated().add(this, &Inventory::onQuitButton);
+
+	btn = _gui.buttonLayoutChecked("quitBackground");
+	btn->setVisible(true);
+	btn->onMouseClickValidated().add(this, &Inventory::onQuitButton);
+
+	btn = _gui.buttonLayoutChecked("mainMenuButton");
+	btn->setVisible(true);
+	btn->onMouseClickValidated().add(this, &Inventory::onMainMenuButton);
+
+	_selectedObject = nullptr;
+	loadCellphone();
+
+	const Common::Path objectsPathPrefix("Inventory/Objects/Objects_");
+	const Common::String &lang = g_engine->getCore()->language();
+	Common::Path langXmlPath = objectsPathPrefix.append(lang).appendInPlace(".xml");
+	if (!Common::File::exists(langXmlPath)) {
+		langXmlPath = objectsPathPrefix.append(".xml");
+		if (!Common::File::exists(langXmlPath)) {
+			langXmlPath = objectsPathPrefix.append("en.xml");
+			if (!Common::File::exists(langXmlPath)) {
+				error("Inventory::load Couldn't find inventory objects xml.");
+			}
+		}
+	}
+
+	loadXMLFile(langXmlPath);
+
+	TeLayout *layout = _gui.layout("selectionSprite");
+	layout->setVisible(false);
+
+	setVisible(false);
+}
+
+void Inventory::loadXMLFile(const Common::Path &path) {
+	Common::File xmlfile;
+	xmlfile.open(path);
+	int64 fileLen = xmlfile.size();
+	char *buf = new char[fileLen + 1];
+	buf[fileLen] = '\0';
+	xmlfile.read(buf, fileLen);
+	const Common::String xmlContents = Common::String::format("<?xml version=\"1.0\" encoding=\"UTF-8\"?><document>%s</document>", buf);
+	delete [] buf;
+	xmlfile.close();
+
+	InventoryObjectsXmlParser parser;
+	if (!parser.loadBuffer((byte *)xmlContents.c_str(), xmlContents.size()))
+		error("Couldn't load inventory xml.");
+	if (!parser.parse())
+		error("Couldn't parse inventory xml.");
+	_objectData = parser._objects;
+}
+
+void Inventory::unload() {
+	int pageNo = 0;
+	while (true) {
+		const Common::String pageStr = Common::String::format("page%d", pageNo);
+		if (_gui.layout(pageStr)) {
+			int slotNo = 0;
+			while (true) {
+				const Common::String slotStr = Common::String::format("page%dSlot%d", pageNo, slotNo);
+				TeLayout *slotLayout = _gui.layout(slotStr);
+				if (slotLayout) {
+					// Take a copy of the list as we may be deleting some
+					// and that removes them from the parent.
+					Common::Array<Te3DObject2 *> children = slotLayout->childList();
+					for (Te3DObject2 *child : children) {
+						InventoryObject *invObj = dynamic_cast<InventoryObject *>(child);
+						if (invObj)
+							delete invObj;
+					}
+					slotNo++;
+				} else {
+					break;
+				}
+			}
+			pageNo++;
+		} else {
+			break;
+		}
+	}
+}
+
+void Inventory::loadCellphone() {
+	_cellphone = new Cellphone();
+	_cellphone->load();
+}
+
+//void loadFromBackup(TiXmlNode *node);
+//void saveToBackup(TiXmlNode *node);
+
+void Inventory::addObject(const Common::String &objId) {
+	InventoryObject *newobj = new InventoryObject();
+	newobj->load(objId);
+	if (!addObject(*newobj))
+		delete newobj;
+}
+
+bool Inventory::addObject(InventoryObject &obj) {
+	error("TODO: implement Inventory::addObject.");
+}
+
+bool Inventory::isDocument(const Common::String &objId) {
+	if (!_objectData.contains(objId))
+		return false;
+	return _objectData.getVal(objId)._isDocument;
+}
+
+int Inventory::objectCount(const Common::String &objId) {
+	for (const InventoryObject *obj : _invObjects) {
+		if (obj->name() == objId)
+			return 1;
+	}
+	return 0;
+}
+
+Common::String Inventory::objectDescription(const Common::String &objId) {
+	// Note: The game XML files don't actually include descriptions.
+	return "";
+}
+
+Common::String Inventory::objectName(const Common::String &objId) {
+	if (!_objectData.contains(objId))
+		return "";
+	return _objectData.getVal(objId)._name;
+}
+
+bool Inventory::onMainMenuButton() {
+	leave();
+	Game *game = g_engine->getGame();
+	game->leave(false);
+	Application *app = g_engine->getApplication();
+	app->mainMenu().enter();
+	return true;
+}
+
+bool Inventory::onObjectSelected(InventoryObject &obj) {
+	selectedObject(obj);
+	if (!_selectedTimer._stopped) {
+		if (_selectedTimer.timeElapsed() < 300000)
+			g_engine->getGame()->inventoryMenu().leave();
+	} else {
+		_selectedTimer.start();
+	}
+	return false;
+}
+
+bool Inventory::onQuitButton() {
+	Game *game = g_engine->getGame();
+	game->inventoryMenu().leave();
+	return true;
+}
+
+bool Inventory::onTakeObjectSelected() {
+	Game *game = g_engine->getGame();
+	game->inventoryMenu().leave();
+	return false;
+}
+
+bool Inventory::onVisibleCellphone() {
+	_cellphone->enter();
+	Game *game = g_engine->getGame();
+	game->inventoryMenu().leave();
+	leave();
+	return false;
+}
+
+bool Inventory::onZoomed() {
+	const Common::String &selected = selectedObject();
+	if (!selected.empty()) {
+		Game *game = g_engine->getGame();
+		game->documentsBrowser().showDocument(selected, 0);
+	}
+	return false;
+}
+
+void Inventory::pauseAnims() {
+	error("TODO: implement Inventory::pauseAnims");
+}
+
+void Inventory::unPauseAnims() {
+	error("TODO: implement Inventory::unPauseAnims");
+}
+
+void Inventory::removeObject(const Common::String &objname) {
+	error("TODO: implement Inventory::removeObject");
+}
+
+void Inventory::removeSelectedObject() {
+	error("TODO: implement Inventory::removeSelectedObject");
+}
+
+InventoryObject *Inventory::selectedInventoryObject() {
+	return _selectedObject;
+}
+
+void Inventory::selectedObject(const Common::String &objname) {
+	error("TODO: implement Inventory::selectedObject");
+}
+
+void Inventory::selectedObject(InventoryObject &obj) {
+	error("TODO: implement Inventory::selectedObject");
+}
+
+const Common::String &Inventory::selectedObject() {
+	static const Common::String blank;
+	if (_selectedObject == nullptr)
+		return blank;
+	else
+		return _selectedObject->name();
+}
+
+bool Inventory::updateLayout() {
+	error("TODO: implement Inventory::updateLayout");
+}
+
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/inventory.h b/engines/tetraedge/game/inventory.h
new file mode 100644
index 00000000000..aa81722db0e
--- /dev/null
+++ b/engines/tetraedge/game/inventory.h
@@ -0,0 +1,97 @@
+/* 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_INVENTORY_H
+#define TETRAEDGE_GAME_INVENTORY_H
+
+#include "common/str.h"
+#include "tetraedge/game/inventory_object.h"
+#include "tetraedge/te/te_layout.h"
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class Cellphone;
+
+class Inventory : public TeLayout {
+public:
+	struct InventoryObjectData {
+		Common::String _id;
+		Common::String _name;
+		bool _isDocument;
+	};
+
+	Inventory();
+
+	void enter();
+	void leave();
+	void load();
+	void unload();
+	void loadCellphone();
+
+	//void loadFromBackup(TiXmlNode *node);
+	//void saveToBackup(TiXmlNode *node);
+
+	void addObject(const Common::String &objname);
+	bool addObject(InventoryObject &obj);
+	bool isDocument(const Common::String &objname);
+
+	int objectCount(const Common::String &objname);
+	Common::String objectDescription(const Common::String &objname);
+	Common::String objectName(const Common::String &objname);
+
+	bool onMainMenuButton();
+	bool onObjectSelected(InventoryObject &obj);
+	bool onQuitButton();
+	bool onTakeObjectSelected();
+	bool onVisibleCellphone();
+	bool onZoomed();
+
+	void pauseAnims();
+	void unPauseAnims();
+
+	void removeObject(const Common::String &objname);
+	void removeSelectedObject();
+
+	InventoryObject *selectedInventoryObject();
+	void selectedObject(const Common::String &objname);
+	void selectedObject(InventoryObject &obj);
+	const Common::String &selectedObject();
+
+	bool updateLayout();
+
+	Cellphone *cellphone() { return _cellphone; }
+
+private:
+	void loadXMLFile(const Common::Path &path);
+
+	TeLuaGUI _gui;
+	Common::Array<InventoryObject *> _invObjects;
+	Cellphone *_cellphone;
+	InventoryObject *_selectedObject;
+	Common::HashMap<Common::String, InventoryObjectData> _objectData;
+
+	TeTimer _selectedTimer;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_INVENTORY_H
diff --git a/engines/tetraedge/game/inventory_menu.cpp b/engines/tetraedge/game/inventory_menu.cpp
new file mode 100644
index 00000000000..31f0294eb29
--- /dev/null
+++ b/engines/tetraedge/game/inventory_menu.cpp
@@ -0,0 +1,123 @@
+/* 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/application.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/inventory_menu.h"
+
+namespace Tetraedge {
+
+InventoryMenu::InventoryMenu() {
+}
+
+void InventoryMenu::enter() {
+	Application *app = g_engine->getApplication();
+	app->mouseCursorLayout().load("pictures/cursor.png");
+
+	_gui.layoutChecked("inventoryMenu")->setVisible(true);
+	onInventoryButton();
+}
+
+void InventoryMenu::leave() {
+	Game *game = g_engine->getGame();
+	game->inventory().leave();
+	game->documentsBrowser().leave();
+	TeLayout *invMenu = _gui.layout("inventoryMenu");
+	if (invMenu) {
+		invMenu->setVisible(false);
+	}
+}
+
+void InventoryMenu::load() {
+	setName("inventoryMenu");
+	setSizeType(RELATIVE_TO_PARENT);
+	TeVector3f32 usersz = userSize();
+	setSize(TeVector3f32(1.0f, 1.0f, usersz.z()));
+	_gui.load("InventoryMenu/InventoryMenu.lua");
+	TeLayout *menu = _gui.layoutChecked("inventoryMenu");
+	addChild(menu);
+	TeButtonLayout *btn;
+	btn = _gui.buttonLayoutChecked("quitButton");
+	btn->onMouseClickValidated().add(this, &InventoryMenu::onQuitButton);
+	btn = _gui.buttonLayoutChecked("quitBackground");
+	btn->onMouseClickValidated().add(this, &InventoryMenu::onQuitButton);
+	btn = _gui.buttonLayoutChecked("mainMenuButton");
+	btn->onMouseClickValidated().add(this, &InventoryMenu::onMainMenuButton);
+	btn = _gui.buttonLayoutChecked("documentsButton");
+	btn->onMouseClickValidated().add(this, &InventoryMenu::onDocumentsButton);
+	btn = _gui.buttonLayoutChecked("inventoryButton");
+	btn->onMouseClickValidated().add(this, &InventoryMenu::onInventoryButton);
+
+	TeLayout *invmenu = _gui.layoutChecked("inventoryMenu");
+	invmenu->setVisible(false);
+	setVisible(false);
+}
+
+void InventoryMenu::unload() {
+	leave();
+	_gui.unload();
+}
+
+bool InventoryMenu::isVisible() {
+	TeLayout *menuLayout = _gui.layout("inventoryMenu");
+	return menuLayout->visible();
+}
+
+bool InventoryMenu::onDocumentsButton() {
+	_gui.buttonLayoutChecked("mainMenuButton")->setEnable(true);
+	_gui.buttonLayoutChecked("documentsButton")->setEnable(false);
+	_gui.buttonLayoutChecked("inventoryButton")->setEnable(true);
+	Game *game = g_engine->getGame();
+	game->inventory().leave();
+	game->documentsBrowser().enter();
+	return false;
+}
+
+bool InventoryMenu::onInventoryButton() {
+	_gui.buttonLayoutChecked("mainMenuButton")->setEnable(true);
+	_gui.buttonLayoutChecked("documentsButton")->setEnable(true);
+	_gui.buttonLayoutChecked("inventoryButton")->setEnable(false);
+	Game *game = g_engine->getGame();
+	game->inventory().enter();
+	game->documentsBrowser().leave();
+	return false;
+}
+
+bool InventoryMenu::onMainMenuButton() {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	Game *game = g_engine->getGame();
+	game->_returnToMainMenu = true;
+	app->fade();
+	return false;
+}
+
+bool InventoryMenu::onQuitButton() {
+	leave();
+	return false;
+}
+
+bool InventoryMenu::onSaveButton(){
+   return false;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/inventory_menu.h b/engines/tetraedge/game/inventory_menu.h
new file mode 100644
index 00000000000..349a2a6105c
--- /dev/null
+++ b/engines/tetraedge/game/inventory_menu.h
@@ -0,0 +1,53 @@
+/* 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_INVENTORY_MENU_H
+#define TETRAEDGE_GAME_INVENTORY_MENU_H
+
+#include "tetraedge/te/te_layout.h"
+
+namespace Tetraedge {
+
+class InventoryMenu : public TeLayout {
+public:
+	InventoryMenu();
+
+	void enter();
+	void leave();
+	void load();
+	void unload();
+
+	bool isVisible();
+
+	bool onDocumentsButton();
+	bool onInventoryButton();
+	bool onMainMenuButton();
+	bool onQuitButton();
+	bool onSaveButton();
+
+private:
+	TeLuaGUI _gui;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_INVENTORY_MENU_H
diff --git a/engines/tetraedge/game/inventory_object.cpp b/engines/tetraedge/game/inventory_object.cpp
new file mode 100644
index 00000000000..af214d5969c
--- /dev/null
+++ b/engines/tetraedge/game/inventory_object.cpp
@@ -0,0 +1,35 @@
+/* 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/game/inventory_object.h"
+
+namespace Tetraedge {
+
+InventoryObject::InventoryObject() {
+}
+
+void InventoryObject::load(const Common::String &name) {
+	error("TODO: Implement InventoryObject::load");
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/inventory_object.h b/engines/tetraedge/game/inventory_object.h
new file mode 100644
index 00000000000..8a9ae452f7b
--- /dev/null
+++ b/engines/tetraedge/game/inventory_object.h
@@ -0,0 +1,44 @@
+/* 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_INVENTORY_OBJECT_H
+#define TETRAEDGE_GAME_INVENTORY_OBJECT_H
+
+#include "common/str.h"
+
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class InventoryObject : TeLuaGUI {
+public:
+	InventoryObject();
+
+	void load(const Common::String &name);
+	const Common::String &name() const { return _name; }
+private:
+	Common::String _name;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_INVENTORY_OBJECT_H
diff --git a/engines/tetraedge/game/inventory_objects_xml_parser.cpp b/engines/tetraedge/game/inventory_objects_xml_parser.cpp
new file mode 100644
index 00000000000..7467dc6e490
--- /dev/null
+++ b/engines/tetraedge/game/inventory_objects_xml_parser.cpp
@@ -0,0 +1,35 @@
+/* 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/game/inventory_objects_xml_parser.h"
+
+namespace Tetraedge {
+
+bool InventoryObjectsXmlParser::parserCallback_Object(ParserNode *node) {
+	Inventory::InventoryObjectData data;
+	data._id = node->values["id"];
+	data._name = node->values["name"];
+	data._isDocument = node->values.contains("isDocument");
+	_objects.setVal(data._id, data);
+	return true;
+};
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/inventory_objects_xml_parser.h b/engines/tetraedge/game/inventory_objects_xml_parser.h
new file mode 100644
index 00000000000..6f86575527d
--- /dev/null
+++ b/engines/tetraedge/game/inventory_objects_xml_parser.h
@@ -0,0 +1,55 @@
+/* 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 "common/hashmap.h"
+#include "common/str.h"
+#include "common/xmlparser.h"
+#include "tetraedge/game/inventory.h"
+
+#ifndef TETRAEDGE_GAME_INVENTORY_OBJECTS_XML_PARSER_H
+#define TETRAEDGE_GAME_INVENTORY_OBJECTS_XML_PARSER_H
+
+namespace Tetraedge {
+
+class InventoryObjectsXmlParser : public Common::XMLParser {
+public:
+	// Parser
+	CUSTOM_XML_PARSER(InventoryObjectsXmlParser) {
+		XML_KEY(document)
+			XML_KEY(Object)
+				XML_PROP(id, true)
+				XML_PROP(name, true)
+				XML_PROP(isDocument, false)
+			KEY_END()
+		KEY_END()
+	} PARSER_END()
+
+	bool parserCallback_document(ParserNode *node) { return true; };
+	bool parserCallback_Object(ParserNode *node);
+
+public:
+	Common::HashMap<Common::String, Inventory::InventoryObjectData> _objects;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_INVENTORY_OBJECTS_XML_PARSER_H
diff --git a/engines/tetraedge/game/loading_menu.cpp b/engines/tetraedge/game/loading_menu.cpp
new file mode 100644
index 00000000000..d8bc4a53afb
--- /dev/null
+++ b/engines/tetraedge/game/loading_menu.cpp
@@ -0,0 +1,31 @@
+/* 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/game/loading_menu.h"
+
+namespace Tetraedge {
+
+LoadingMenu::LoadingMenu() {
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/loading_menu.h b/engines/tetraedge/game/loading_menu.h
new file mode 100644
index 00000000000..34021f76d43
--- /dev/null
+++ b/engines/tetraedge/game/loading_menu.h
@@ -0,0 +1,40 @@
+/* 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_LOADING_MENU_H
+#define TETRAEDGE_GAME_LOADING_MENU_H
+
+namespace Tetraedge {
+
+class LoadingMenu {
+public:
+	LoadingMenu();
+
+	// TODO add public members
+
+private:
+	// TODO add private members
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_LOADING_MENU_H
diff --git a/engines/tetraedge/game/loc_file.cpp b/engines/tetraedge/game/loc_file.cpp
new file mode 100644
index 00000000000..3e511527187
--- /dev/null
+++ b/engines/tetraedge/game/loc_file.cpp
@@ -0,0 +1,61 @@
+/* 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 "common/file.h"
+#include "common/textconsole.h"
+#include "common/xmlparser.h"
+
+#include "tetraedge/game/loc_file.h"
+#include "tetraedge/te/te_name_val_xml_parser.h"
+
+namespace Tetraedge {
+
+LocFile::LocFile() {
+}
+
+void LocFile::load(const Common::Path &path) {
+	TeNameValXmlParser parser;
+	static const Common::String xmlHeader("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+	Common::File locFile;
+	if (!locFile.open(path))
+		error("LocFile::load: failed to open %s.", path.toString().c_str());
+
+	int64 fileLen = locFile.size();
+	char *buf = new char[fileLen + 1];
+	buf[fileLen] = '\0';
+	locFile.read(buf, fileLen);
+	const Common::String xmlContents = xmlHeader + buf;
+	delete [] buf;
+	locFile.close();
+	if (!parser.loadBuffer((const byte *)xmlContents.c_str(), xmlContents.size()))
+		error("LocFile::load: failed to load %s.", path.toString().c_str());
+
+	if (!parser.parse())
+		error("LocFile::load: failed to parse %s.", path.toString().c_str());
+
+	_map = parser.getMap();
+}
+
+const Common::String *LocFile::value(const Common::String &key) {
+	return text(key);
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/loc_file.h b/engines/tetraedge/game/loc_file.h
new file mode 100644
index 00000000000..31c49104be6
--- /dev/null
+++ b/engines/tetraedge/game/loc_file.h
@@ -0,0 +1,47 @@
+/* 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_LOC_FILE_H
+#define TETRAEDGE_GAME_LOC_FILE_H
+
+#include "common/str.h"
+#include "common/path.h"
+
+#include "tetraedge/te/te_i_loc.h"
+
+namespace Tetraedge {
+
+class LocFile : public TeILoc {
+public:
+	LocFile();
+
+	//const Common::String *avatar(const Common::String &key);
+	void load(const Common::Path &path);
+	const Common::String *value(const Common::String &key);
+
+private:
+	// TODO add private members
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_LOC_FILE_H
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
new file mode 100644
index 00000000000..a3f95d08d18
--- /dev/null
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -0,0 +1,312 @@
+/* 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/application.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/lua_binds.h"
+#include "tetraedge/to_lua.h"
+
+namespace Tetraedge {
+
+namespace LuaBinds {
+
+using namespace ToLua;
+
+static void PlayMovie(const Common::String &vidpath, const Common::String &musicpath) {
+	Application *app = g_engine->getApplication();
+	app->mouseCursorLayout().load("pictures/cursor.png");
+	Game *game = g_engine->getGame();
+	game->playMovie(vidpath, musicpath);
+}
+
+static int tolua_ExportedFunctions_PlayMovie00(lua_State *L) {
+tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		PlayMovie(s1, s2);
+		return 0;
+	}
+	error("#ferror in function 'PlayMovie': %d %d %s", err.index, err.array, err.type);
+}
+
+static void AddRandomSound(const Common::String &s1, const Common::String &s2, float f1, float f2){
+	Game *game = g_engine->getGame();
+  	game->addRandomSound(s1, s2, f1, f2);
+}
+
+static int tolua_ExportedFunctions_AddRandomSound00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		float f1 = tolua_tonumber(L, 3, 0.0);
+		float f2 = tolua_tonumber(L, 4, 1.0);
+		AddRandomSound(s1, s2, f1, f2);
+		return 0;
+	}
+	error("#ferror in function 'AddRandomSound': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetSoundStep(const Common::String &scene, const Common::String &step1, const Common::String &step2) {
+	Game *game = g_engine->getGame();
+	game->scene().setStep(scene, step1, step2);
+}
+
+static int tolua_ExportedFunctions_SetSoundStep00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isstring(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));
+		Common::String s3(tolua_tostring(L, 3, nullptr));
+		SetSoundStep(s1, s2, s3);
+		return 0;
+	}
+	error("#ferror in function 'SetSoundStep': %d %d %s", err.index, err.array, err.type);
+}
+
+static void AddNumber(const Common::String &number) {
+  Game *game = g_engine->getGame();
+  if (!game->inventory().cellphone()->addNumber(number))
+	warning("[AddNumber] Number \"%s\" already exist.", number.c_str());
+}
+
+static int tolua_ExportedFunctions_AddNumber00(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));
+		AddNumber(s1);
+		return 0;
+	}
+	error("#ferror in function 'AddNumber': %d %d %s", err.index, err.array, err.type);
+}
+
+static void AddUnrecalAnim(const Common::String &newanim) {
+	Application *app = g_engine->getApplication();
+	Common::Array<Common::String> &anims = app->unrecalAnims();
+	for (const Common::String &anim : anims) {
+		if (anim == newanim)
+			return;
+	}
+	anims.push_back(newanim);
+}
+
+static int tolua_ExportedFunctions_AddUnrecalAnim00(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));
+		AddUnrecalAnim(s1);
+		return 0;
+	}
+	error("#ferror in function 'AddUnrecalAnim': %d %d %s", err.index, err.array, err.type);
+}
+
+static void UnlockArtwork(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	game->addArtworkUnlocked(name, true);
+	Application *app = g_engine->getApplication();
+	app->saveOptions("options.xml");
+}
+
+static int tolua_ExportedFunctions_UnlockArtwork00(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));
+		UnlockArtwork(s1);
+		return 0;
+	}
+	error("#ferror in function 'UnlockArtwork': %d %d %s", err.index, err.array, err.type);
+}
+
+static void ChangeWarp(const Common::String &scene, const Common::String &zone) {
+}
+
+static int tolua_ExportedFunctions_ChangeWarp00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		ChangeWarp(s1, s2);
+		return 0;
+	}
+	error("#ferror in function 'ChangeWarp': %d %d %s", err.index, err.array, err.type);
+}
+
+
+void LuaOpenBinds(lua_State *L) {
+  tolua_open(L);
+  tolua_module(L, 0, 0);
+  tolua_beginmodule(L, 0);
+  /*
+  tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials00);
+  tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);
+  tolua_function(L, "HideObject", tolua_ExportedFunctions_HideObject00);
+  tolua_function(L, "ShowObject", tolua_ExportedFunctions_ShowObject00);
+  tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00);
+  tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);
+  tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00);
+  tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00);*/
+  tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
+  tolua_function(L, "PlayMovie", tolua_ExportedFunctions_PlayMovie00);
+  /*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);
+  tolua_function(L, "StartAnimationPart", tolua_ExportedFunctions_StartAnimationPart00);
+  tolua_function(L, "StartAnimation", tolua_ExportedFunctions_StartAnimation00);
+  tolua_function(L, "StartAnimationAndWaitForEnd",
+                 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00);
+  tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00);
+  tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
+  tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);
+  tolua_function(L, "AddMarker", tolua_ExportedFunctions_AddMarker00);
+  tolua_function(L, "SetVisibleMarker", tolua_ExportedFunctions_SetVisibleMarker00);
+  tolua_function(L, "DeleteMarker", tolua_ExportedFunctions_DeleteMarker00);
+  tolua_function(L, "SetVisibleCellphone", tolua_ExportedFunctions_SetVisibleCellphone00);
+  tolua_function(L, "DisabledZone", tolua_ExportedFunctions_DisabledZone00);
+  tolua_function(L, "DisabledInt", tolua_ExportedFunctions_DisabledInt00);
+  tolua_function(L, "LockCursor", tolua_ExportedFunctions_LockCursor00);
+  tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00);
+  tolua_function(L, "UnsetCondition", tolua_ExportedFunctions_UnsetCondition00);
+  tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00);
+  tolua_function(L, "LaunchDialog", tolua_ExportedFunctions_LaunchDialog00);
+  tolua_function(L, "LaunchDialogAndWaitForEnd", tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00);
+  tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
+  tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);
+  tolua_function(L, "PushTask", tolua_ExportedFunctions_PushTask00);
+  tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
+  tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);
+  tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00);
+  tolua_function(L, "PlaySound", tolua_ExportedFunctions_PlaySound00);
+  tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);
+  tolua_function(L, "StopSound", tolua_ExportedFunctions_StopSound00);*/
+  tolua_function(L, "AddRandomSound", tolua_ExportedFunctions_AddRandomSound00);
+  /*tolua_function(L, "PlayRandomSound", tolua_ExportedFunctions_PlayRandomSound00);
+  tolua_function(L, "PlayMusic", tolua_ExportedFunctions_PlayMusic00);*/
+  tolua_function(L, "SetSoundStep", tolua_ExportedFunctions_SetSoundStep00);
+  /*tolua_function(L, "Selected", tolua_ExportedFunctions_Selected00);
+  tolua_function(L, "TakeObject", tolua_ExportedFunctions_TakeObject00);
+  tolua_function(L, "TakeObjectInHand", tolua_ExportedFunctions_TakeObjectInHand00);
+  tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
+  tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject01);*/
+  tolua_function(L, "AddNumber", tolua_ExportedFunctions_AddNumber00);
+  /*tolua_function(L, "ShowDocument", tolua_ExportedFunctions_ShowDocument00);
+  tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00);
+  tolua_function(L, "HideDocument", tolua_ExportedFunctions_HideDocument00);
+  tolua_function(L, "AddDocument", tolua_ExportedFunctions_AddDocument00);
+  tolua_function(L, "LoadCharacter", tolua_ExportedFunctions_LoadCharacter00);
+  tolua_function(L, "UnloadCharacter", tolua_ExportedFunctions_UnloadCharacter00);
+  tolua_function(L, "GetRotationCharacter", tolua_ExportedFunctions_GetRotationCharacter00);
+  tolua_function(L, "GetXPositionCharacter", tolua_ExportedFunctions_GetXPositionCharacter00);
+  tolua_function(L, "GetYPositionCharacter", tolua_ExportedFunctions_GetYPositionCharacter00);
+  tolua_function(L, "GetZPositionCharacter", tolua_ExportedFunctions_GetZPositionCharacter00);
+  tolua_function(L, "MoveCharacterTo", tolua_ExportedFunctions_MoveCharacterTo00);
+  tolua_function(L, "MoveCharacterToAndWaitForEnd",
+                 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);
+  tolua_function(L, "MoveCharacterPlayerTo", tolua_ExportedFunctions_MoveCharacterPlayerTo00);
+  tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
+                 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00);
+  tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00);
+  tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
+  tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);
+  tolua_function(L, "SetCharacterRotation", tolua_ExportedFunctions_SetCharacterRotation00);
+  tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
+  tolua_function(L, "SetCharacterAnimation", tolua_ExportedFunctions_SetCharacterAnimation00);
+  tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
+                 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);
+  tolua_function(L, "BlendCharacterAnimation", tolua_ExportedFunctions_BlendCharacterAnimation00);
+  tolua_function(L, "BlendCharacterAnimationAndWaitForEnd",
+                 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);
+  tolua_function(L, "CurrentCharacterAnimation", tolua_ExportedFunctions_CurrentCharacterAnimation00);
+  tolua_function(L, "SetCharacterPlayerVisible", tolua_ExportedFunctions_SetCharacterPlayerVisible00);
+  tolua_function(L, "MoveCharacterPlayerDisabled",
+                 tolua_ExportedFunctions_MoveCharacterPlayerDisabled00);
+  tolua_function(L, "SetRunMode", tolua_ExportedFunctions_SetRunMode00);
+  tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200);
+  tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00);
+  tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00);
+  tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);
+  tolua_function(L, "AddCallback", tolua_ExportedFunctions_AddCallback00);
+  tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00);
+  tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00);
+  tolua_function(L, "DeleteCallback", tolua_ExportedFunctions_DeleteCallback00);
+  tolua_function(L, "DeleteCallbackPlayer", tolua_ExportedFunctions_DeleteCallbackPlayer00);
+  tolua_function(L, "DeleteCallbackAnimation2D", tolua_ExportedFunctions_DeleteCallbackAnimation2D00);
+  tolua_function(L, "SetObjectOnCharacter", tolua_ExportedFunctions_SetObjectOnCharacter00);
+  tolua_function(L, "SetObjectRotation", tolua_ExportedFunctions_SetObjectRotation00);
+  tolua_function(L, "SetObjectTranslation", tolua_ExportedFunctions_SetObjectTranslation00);
+  tolua_function(L, "SetObjectScale", tolua_ExportedFunctions_SetObjectScale00);
+  tolua_function(L, "SetObjectFrames", tolua_ExportedFunctions_SetObjectFrames00);
+  tolua_function(L, "LoadObject", tolua_ExportedFunctions_LoadObject00);
+  tolua_function(L, "UnloadObject", tolua_ExportedFunctions_UnloadObject00);
+  tolua_function(L, "SetGroundObjectPosition", tolua_ExportedFunctions_SetGroundObjectPosition00);
+  tolua_function(L, "SetGroundObjectRotation", tolua_ExportedFunctions_SetGroundObjectRotation00);
+  tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00);
+  tolua_function(L, "RotateGroundObject", tolua_ExportedFunctions_RotateGroundObject00);
+  tolua_function(L, "SetLightPlayerCharacter", tolua_ExportedFunctions_SetLightPlayerCharacter00);
+  tolua_function(L, "SetLightPos", tolua_ExportedFunctions_SetLightPos00);
+  tolua_function(L, "EnableLight", tolua_ExportedFunctions_EnableLight00);
+  tolua_function(L, "SetLightDiffuse", tolua_ExportedFunctions_SetLightDiffuse00);
+  tolua_function(L, "SetLightAmbient", tolua_ExportedFunctions_SetLightAmbient00);
+  tolua_function(L, "SetLightSpecular", tolua_ExportedFunctions_SetLightSpecular00);
+  tolua_function(L, "LoadBillBoard", tolua_ExportedFunctions_LoadBillBoard00);
+  tolua_function(L, "SetBillboardPosition", tolua_ExportedFunctions_SetBillboardPosition00);
+  tolua_function(L, "SetBillboardPosition2", tolua_ExportedFunctions_SetBillboardPosition200);
+  tolua_function(L, "SetBillboardSize", tolua_ExportedFunctions_SetBillboardSize00);
+  tolua_function(L, "ShowBillboard", tolua_ExportedFunctions_ShowBillboard00);
+  tolua_function(L, "HideBillboard", tolua_ExportedFunctions_HideBillboard00);
+  tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
+  tolua_function(L, "Save", tolua_ExportedFunctions_Save00);
+  tolua_function(L, "Wait", tolua_ExportedFunctions_Wait00);
+  tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00);
+  tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00);
+  tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
+  tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);
+  tolua_function(L, "BFGRateImmediately", tolua_ExportedFunctions_BFGRateImmediately00);
+  tolua_function(L, "BFGReportEvent", tolua_ExportedFunctions_BFGReportEvent00);
+  tolua_function(L, "BFGReportEventWithValue", tolua_ExportedFunctions_BFGReportEventWithValue00);
+  tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00);
+  tolua_function(L, "TestFileFlagSystemFlag", tolua_ExportedFunctions_TestFileFlagSystemFlag00);
+  tolua_function(L, "PrintDebugMessage", tolua_ExportedFunctions_PrintDebugMessage00);
+  tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);
+  tolua_function(L, "EnableRectBlocker", tolua_ExportedFunctions_EnableRectBlocker00);
+  tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);
+  tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
+  tolua_function(L, "ActivateAnchorZone", tolua_ExportedFunctions_ActivateAnchorZone00);
+  tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00);
+  tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
+  tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
+  tolua_function(L, "SetCharacterMeshVisible", tolua_ExportedFunctions_SetCharacterMeshVisible00);
+  tolua_function(L, "SetRecallageY", tolua_ExportedFunctions_SetRecallageY00);
+  tolua_function(L, "IsFreemiumUnlocked", tolua_ExportedFunctions_IsFreemiumUnlocked00);
+  tolua_function(L, "ReachedFreemiumLimit", tolua_ExportedFunctions_ReachedFreemiumLimit00);*/
+  tolua_function(L, "AddUnrecalAnim", tolua_ExportedFunctions_AddUnrecalAnim00);
+  tolua_function(L, "UnlockArtwork", tolua_ExportedFunctions_UnlockArtwork00);
+
+  tolua_endmodule(L);
+}
+
+
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/lua_binds.h b/engines/tetraedge/game/lua_binds.h
new file mode 100644
index 00000000000..c22f39eeeeb
--- /dev/null
+++ b/engines/tetraedge/game/lua_binds.h
@@ -0,0 +1,37 @@
+/* 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_LUA_BINDS_H
+#define TETRAEDGE_GAME_LUA_BINDS_H
+
+struct lua_State;
+
+namespace Tetraedge {
+
+namespace LuaBinds {
+
+void LuaOpenBinds(lua_State *L);
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_LUA_BINDS_H
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
new file mode 100644
index 00000000000..7fb93fbd6c1
--- /dev/null
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -0,0 +1,309 @@
+/* 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 "common/config-manager.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "common/savefile.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/confirm.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/main_menu.h"
+#include "tetraedge/game/application.h"
+
+#include "tetraedge/te/te_button_layout.h"
+#include "tetraedge/te/te_sprite_layout.h"
+#include "tetraedge/te/te_text_layout.h"
+#include "tetraedge/te/te_music.h"
+
+
+namespace Tetraedge {
+
+static const char *LAST_SAVE_CONF = "lastSaveSlot";
+
+MainMenu::MainMenu() : _entered(false), _confirmingTuto(false) {
+	_newGameConfirm._onButtonYesSignal.add(this, &MainMenu::onNewGameConfirmed);
+	_tutoConfirm._onButtonYesSignal.add(this, &MainMenu::onActivedTuto);
+	_tutoConfirm._onButtonNoSignal.add(this, &MainMenu::onDisabledTuto);
+	_quitConfirm._onButtonYesSignal.add(this, &MainMenu::onQuit);
+	onFacebookLoggedSignal.add(this, &MainMenu::onFacebookLogged);
+}
+
+void MainMenu::enter() {
+	Application *app = g_engine->getApplication();
+	TeSpriteLayout &appSpriteLayout = app->appSpriteLayout();
+	appSpriteLayout.setVisible(true);
+	if (appSpriteLayout._tiledSurfacePtr->_frameAnim._runTimer._stopped) {
+		appSpriteLayout.load("menus/menu.ogv");
+		appSpriteLayout._tiledSurfacePtr->_frameAnim._loopCount = -1;
+		appSpriteLayout._tiledSurfacePtr->play();
+	}
+	app->captureFade();
+
+	_entered = true;
+	load("menus/mainMenu/mainMenu.lua");
+
+	TeLayout *menuLayout = layout("menu");
+	appSpriteLayout.addChild(menuLayout);
+
+	app->mouseCursorLayout().setVisible(true);
+	app->mouseCursorLayout().load("pictures/cursor.png");
+
+	TeMusic &music = app->music();
+	if (music.isPlaying()) {
+		// TODO: something here??
+	}
+	music.load(value("musicPath").toString());
+	music.play();
+	music.volume(1.0f);
+
+	TeButtonLayout *newGameButton = buttonLayout("newGameButton");
+	if (newGameButton)
+		newGameButton->onMouseClickValidated().add(this, &MainMenu::onNewGameButtonValidated);
+
+	TeButtonLayout *continueGameButton = buttonLayout("continueGameButton");
+	if (continueGameButton) {
+		continueGameButton->onMouseClickValidated().add(this, &MainMenu::onContinueGameButtonValidated);
+		continueGameButton->setEnable(ConfMan.hasKey(LAST_SAVE_CONF));
+	}
+
+	TeButtonLayout *loadGameButton = buttonLayout("loadGameButton");
+	if (loadGameButton)
+		loadGameButton->onMouseClickValidated().add(this, &MainMenu::onLoadGameButtonValidated);
+
+	TeButtonLayout *optionsButton = buttonLayout("optionsButton");
+	if (optionsButton)
+		optionsButton->onMouseClickValidated().add(this, &MainMenu::onOptionsButtonValidated);
+
+	TeButtonLayout *galleryButton = buttonLayout("galleryButton");
+	if (galleryButton)
+		galleryButton->onMouseClickValidated().add(this, &MainMenu::onGalleryButtonValidated);
+
+	TeButtonLayout *quitButton = buttonLayout("quitButton");
+	if (quitButton)
+		quitButton->onMouseClickValidated().add(this, &MainMenu::onQuitButtonValidated);
+
+	// TODO: confirmation (menus/confirm/confirmNotSound.lua)
+	// if TeSoundManager is not valid.
+
+	_confirmingTuto = false;
+	TeLayout *panel = layout("panel");
+
+	if (panel) {
+		const Common::String panelTypoVal = value("panelTypo").toString();
+		for (auto *child : panel->childList()) {
+			TeTextLayout *childText = dynamic_cast<TeTextLayout *>(child);
+			if (!childText)
+				continue;
+			childText->setName(panelTypoVal + childText->name());
+		}
+	}
+	setCenterButtonsVisibility(true);
+	TeTextLayout *versionNum = textLayout("versionNumber");
+	if (versionNum) {
+		const Common::String versionSectionStr("<section style=\"left\" /><color r=\"255\" g=\"255\" b=\"255\"/><font file=\"Common/Fonts/arial.ttf\" size=\"12\" />");
+		versionNum->setText(versionSectionStr + app->getVersionString());
+	}
+}
+
+void MainMenu::leave() {
+	if (!_entered)
+		return;
+
+	Application	*app = g_engine->getApplication();
+	app->captureFade();
+	warning("TODO: MainMenu::leave Stop some game sounds here.");
+	//Game *game = g_engine->getGame();
+	//game->stopSound("sounds/Ambiances/b_automatebike.ogg");
+	//game->stopSound("sounds/Ambiances/b_engrenagebg.ogg");
+	TeLuaGUI::unload();
+	app->fade();
+	_entered= false;
+}
+
+bool MainMenu::deleteFile(const Common::String &name) {
+	error("TODO: Implement MainMenu::deleteFile");
+}
+
+bool MainMenu::onActivedTuto() {
+	Application *app = g_engine->getApplication();
+	app->setTutoActivated(true);
+	// TODO: Set game val false too?
+	app->captureFade();
+	leave();
+	app->startGame(true, 1);
+	app->fade();
+	return false;
+}
+
+bool MainMenu::onBFGRateIt2ButtonValidated() {
+	error("TODO: Implement MainMenu::onBFGRateIt2ButtonValidated");
+}
+
+bool MainMenu::onBFGRateItButtonValidated() {
+	error("TODO: Implement MainMenu::onBFGRateItButtonValidated");
+}
+
+bool MainMenu::onBFGRateItQuitButtonValidated() {
+	error("TODO: Implement MainMenu function");
+}
+
+bool MainMenu::onBFGUnlockGameButtonValidated() {
+	error("TODO: Implement MainMenu function");
+}
+
+void MainMenu::tryDisableButton(const Common::String &btnName) {
+	TeButtonLayout *button = buttonLayout(btnName);
+	if (button)
+		button->setEnable(false);
+}
+
+bool MainMenu::onContinueGameButtonValidated() {
+	Application *app = g_engine->getApplication();
+	const Common::String lastSave = ConfMan.get(LAST_SAVE_CONF);
+	if (!lastSave.empty()) {
+		int saveSlot = lastSave.asUint64();
+		g_engine->loadGameState(saveSlot);
+		return false;
+	}
+
+	tryDisableButton("newGameButton");
+	tryDisableButton("continueGameButton");
+	tryDisableButton("loadGameButton");
+	tryDisableButton("optionsButton");
+	tryDisableButton("galleryButton");
+	tryDisableButton("quitButton");
+
+	if (_confirmingTuto)
+	  return false;
+
+	app->captureFade();
+	leave();
+	app->startGame(false, 1);
+	app->fade();
+	return false;
+}
+
+bool MainMenu::onDisabledTuto() {
+	Application *app = g_engine->getApplication();
+	app->setTutoActivated(false);
+	g_engine->getGame()->_firstInventory = false;
+	app->captureFade();
+	leave();
+	app->startGame(true, 1);
+	app->fade();
+	return false;
+}
+
+bool MainMenu::onEnterGameRotateAnimFinished() {
+	error("TODO: Implement MainMenu function");
+}
+
+bool MainMenu::onGalleryButtonValidated() {
+	error("TODO: Implement MainMenu function");
+}
+
+bool MainMenu::onHowToButtonValidated() {
+	onContinueGameButtonValidated();
+	_confirmingTuto = false;
+	return false;
+}
+
+bool MainMenu::onLoadGameButtonValidated() {
+	g_engine->loadGameDialog();
+	return false;
+}
+
+bool MainMenu::onNewGameButtonValidated() {
+	// Note: Original confirms whether to start new game here
+	// with "menus/confirm/confirmNewGame.lua"
+	// because only one save is allowed.  We just clear last
+	// save slot number and go ahead and start.
+	ConfMan.set(LAST_SAVE_CONF, "");
+	onNewGameConfirmed();
+	return false;
+}
+
+bool MainMenu::onNewGameConfirmed() {
+	// Note: Original game deletes saves here.  Don't do that..
+	_confirmingTuto = true;
+	_tutoConfirm.enter("menus/confirm/confirmTuto.lua", "");
+	onContinueGameButtonValidated();
+	return false;
+}
+
+bool MainMenu::onOptionsButtonValidated() {
+	g_engine->openConfigDialog();
+	return true;
+}
+
+bool MainMenu::onQuit() {
+	g_engine->quitGame();
+	leave();
+	return false;
+}
+
+bool MainMenu::onQuitButtonValidated() {
+	//Confirm::enter("menus/confirm/confirmQuit.lua", "");
+	error("TODO: Implement MainMenu::onQuitButtonValidated");
+}
+
+bool MainMenu::onUnlockGameButtonValidated() {
+	error("TODO: Implement MainMenu::onUnlockGameButtonValidated");
+}
+
+void MainMenu::refresh() {
+	// TODO: get a real value
+	bool haveSave = false;
+	TeButtonLayout *continueGameButton = buttonLayout("continueGameButton");
+	if (continueGameButton) {
+		continueGameButton->setEnable(haveSave);
+	}
+}
+
+void MainMenu::setCenterButtonsVisibility(bool visible) {
+	bool haveSave = false;
+
+	TeButtonLayout *continuegameunlockButton = buttonLayout("continuegameunlockButton");
+	if (continuegameunlockButton) {
+		continuegameunlockButton->setVisible(haveSave & visible);
+	}
+
+	TeButtonLayout *newGameUnlockButton = buttonLayout("newgameunlockButton");
+	if (newGameUnlockButton) {
+		newGameUnlockButton->setVisible(visible & !haveSave);
+	}
+
+	TeButtonLayout *unlockgameButton = buttonLayout("unlockgameButton");
+	if (unlockgameButton) {
+		unlockgameButton->setVisible(false);
+	}
+
+	TeLayout *rateItButton = layout("rateItButton");
+	if (rateItButton) {
+		rateItButton->setVisible(false);
+	}
+}
+
+
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/main_menu.h b/engines/tetraedge/game/main_menu.h
new file mode 100644
index 00000000000..4d256465c89
--- /dev/null
+++ b/engines/tetraedge/game/main_menu.h
@@ -0,0 +1,85 @@
+/* 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_MAIN_MENU_H
+#define TETRAEDGE_GAME_MAIN_MENU_H
+
+#include "common/str.h"
+#include "tetraedge/game/confirm.h"
+#include "tetraedge/te/te_signal.h"
+
+namespace Tetraedge {
+
+class MainMenu : public TeLuaGUI {
+public:
+	MainMenu();
+
+	void enter() override;
+	void leave() override;
+
+	bool deleteFile(const Common::String &name);
+	bool onActivedTuto();
+	bool onBFGFreeGamesButtonValidated() { return false; }
+	bool onBFGRateIt2ButtonValidated();
+	bool onBFGRateItButtonValidated();
+	bool onBFGRateItQuitButtonValidated();
+	bool onBFGSplashButtonUpdated() { return false; }
+	bool onBFGSplashButtonValidated()  { return false; }
+	bool onBFGTellAFriendButtonValidated()  { return false; }
+	bool onBFGUnlockGameButtonValidated();
+	bool onContinueGameButtonValidated();
+	bool onDisabledTuto();
+	bool onEnterGameRotateAnimFinished();
+	bool onFacebookButtonValidated()  { return false; }
+	bool onFacebookLogged()  { return false; }
+	bool onGalleryButtonValidated();
+	bool onHowToButtonValidated();
+	bool onLoadGameButtonValidated();
+	bool onNewGameButtonValidated();
+	bool onNewGameConfirmed();
+	bool onOptionsButtonValidated();
+	bool onQuit();
+	bool onQuitButtonValidated();
+	bool onUnlockGameButtonValidated();
+	bool onWalkThroughButtonValidated() { return false; };
+
+	void refresh();
+	void setCenterButtonsVisibility(bool visible);
+
+private:
+
+	void tryDisableButton(const Common::String &btnName);
+
+	Confirm _newGameConfirm;
+	Confirm _tutoConfirm;
+	Confirm _quitConfirm;
+
+	// TODO add private members
+	TeSignal0Param onFacebookLoggedSignal;
+
+	bool _entered;
+	bool _confirmingTuto;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_MAIN_MENU_H
diff --git a/engines/tetraedge/game/notifier.cpp b/engines/tetraedge/game/notifier.cpp
new file mode 100644
index 00000000000..9e1533b5b17
--- /dev/null
+++ b/engines/tetraedge/game/notifier.cpp
@@ -0,0 +1,86 @@
+/* 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/game.h"
+#include "tetraedge/game/notifier.h"
+#include "tetraedge/te/te_layout.h"
+
+namespace Tetraedge {
+
+Notifier::Notifier() {
+}
+
+void Notifier::launchNextnotifier() {
+	TeCurveAnim2<Te3DObject2, TeColor> *fadeInAnim = _gui.colorLinearAnimation("fadeIn");
+	if (fadeInAnim->_runTimer._stopped) {
+		warning("TODO: Implement Notifier::launchNextnotifier");
+	}
+}
+
+void Notifier::load() {
+	_gui.load("menus/Notifier.lua");
+	TeLayout *notifierLayout = _gui.layout("notifier");
+	Game *game = g_engine->getGame();
+	game->addNoScale2Child(notifierLayout);
+	notifierLayout->setVisible(false);
+
+	TeCurveAnim2<Te3DObject2, TeColor> *fadeIn = _gui.colorLinearAnimation("fadeIn");
+	fadeIn->onFinished().add(this, &Notifier::onFadeInFinished);
+
+	TeCurveAnim2<Te3DObject2, TeColor> *visible = _gui.colorLinearAnimation("visible");
+	visible->onFinished().add(this, &Notifier::onVisibleFinished);
+
+	TeCurveAnim2<Te3DObject2, TeColor> *fadeOut = _gui.colorLinearAnimation("fadeOut");
+	fadeOut->onFinished().add(this, &Notifier::onFadeOutFinished);
+}
+
+bool Notifier::onFadeInFinished() {
+	//TeCurveAnim2<Te3DObject2, TeColor> *visible = _gui.colorLinearAnimation("visible");
+	error("TODO: Implement me.");
+}
+
+bool Notifier::onFadeOutFinished() {
+	TeLayout *notifierLayout = _gui.layout("notifier");
+	notifierLayout->setVisible(false);
+	launchNextnotifier();
+	return false;
+}
+
+bool Notifier::onVisibleFinished() {
+	error("TODO: Implement me.");
+}
+
+void Notifier::push(const Common::String &name, const Common::String &imgpath) {
+	notifierData n = {name, imgpath};
+	_notifierDataArray.push_back(n);
+	launchNextnotifier();
+}
+
+void Notifier::unload() {
+	TeLayout *layout = _gui.layout("notifier");
+	g_engine->getGame()->removeNoScale2Child(layout);
+	_gui.unload();
+}
+
+
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/notifier.h b/engines/tetraedge/game/notifier.h
new file mode 100644
index 00000000000..b277f907ead
--- /dev/null
+++ b/engines/tetraedge/game/notifier.h
@@ -0,0 +1,57 @@
+/* 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_NOTIFIER_H
+#define TETRAEDGE_GAME_NOTIFIER_H
+
+#include "common/str.h"
+
+namespace Tetraedge {
+
+class Notifier {
+public:
+	Notifier();
+
+	void launchNextnotifier();
+	void load();
+	bool onFadeInFinished();
+	bool onFadeOutFinished();
+	bool onVisibleFinished();
+
+	void push(const Common::String &name, const Common::String &imgpath);
+	void unload();
+
+	TeLuaGUI &gui() { return _gui; }
+
+private:
+	struct notifierData {
+		Common::String name;
+		Common::String imgpath;
+	};
+	Common::Array<notifierData> _notifierDataArray;
+	TeLuaGUI _gui;
+	// TODO add private members
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_NOTIFIER_H
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
new file mode 100644
index 00000000000..eb95daa4d34
--- /dev/null
+++ b/engines/tetraedge/game/object3d.cpp
@@ -0,0 +1,58 @@
+/* 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 "common/textconsole.h"
+#include "tetraedge/game/object3d.h"
+#include "tetraedge/game/object_settings_xml_parser.h"
+
+namespace Tetraedge {
+
+/*static*/ Common::HashMap<Common::String, Object3D::ObjectSettings> *Object3D::_objectSettings = nullptr;
+
+
+Object3D::Object3D() {
+}
+
+/*static*/ bool Object3D::loadSettings(const Common::String &path) {
+	ObjectSettingsXmlParser parser;
+	parser.setAllowText();
+
+	if (_objectSettings)
+		delete _objectSettings;
+	_objectSettings = new Common::HashMap<Common::String, ObjectSettings>();
+	parser.setObjectSettings(_objectSettings);
+
+	if (!parser.loadFile(path))
+		error("Object3D::loadSettings: Can't load %s", path.c_str());
+	if (!parser.parse())
+		error("Object3D::loadSettings: Can't parse %s", path.c_str());
+
+	return false;
+}
+
+void Object3D::ObjectSettings::clear() {
+	_name.clear();
+	_modelFileName.clear();
+	_defaultScale = TeVector3f32();
+}
+
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/object3d.h b/engines/tetraedge/game/object3d.h
new file mode 100644
index 00000000000..ae248fd581a
--- /dev/null
+++ b/engines/tetraedge/game/object3d.h
@@ -0,0 +1,60 @@
+/* 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_OBJECT3D_H
+#define TETRAEDGE_GAME_OBJECT3D_H
+
+#include "common/str.h"
+#include "common/hashmap.h"
+
+#include "tetraedge/te/te_object.h"
+#include "tetraedge/te/te_model.h"
+#include "tetraedge/te/te_vector3f32.h"
+
+namespace Tetraedge {
+
+class Object3D : public TeObject {
+public:
+	struct ObjectSettings {
+		Common::String _name;
+		Common::String _modelFileName;
+		TeVector3f32 _defaultScale;
+
+		void clear();
+	};
+
+	Object3D();
+
+	bool loadModel(const Common::String &name);
+
+	static bool loadSettings(const Common::String &path);
+
+private:
+	static Common::HashMap<Common::String, ObjectSettings> *_objectSettings;
+
+	TeIntrusivePtr<TeModel> _modelPtr;
+	Common::String _modelFileName;
+	TeVector3f32 _defaultScale;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_OBJECT3D_H
diff --git a/engines/tetraedge/game/object_settings_xml_parser.cpp b/engines/tetraedge/game/object_settings_xml_parser.cpp
new file mode 100644
index 00000000000..906978068a1
--- /dev/null
+++ b/engines/tetraedge/game/object_settings_xml_parser.cpp
@@ -0,0 +1,63 @@
+/* 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/game/object_settings_xml_parser.h"
+
+namespace Tetraedge {
+
+bool ObjectSettingsXmlParser::parserCallback_ObjectsSettings(ParserNode *node) {
+	// Nothing to do, data handled in the child keys.
+	return true;
+}
+
+bool ObjectSettingsXmlParser::parserCallback_Object(ParserNode *node) {
+	const Common::String &objname = node->values["name"];
+	_curObject._name = objname;
+	_objectSettings->setVal(objname, _curObject);
+	_curObject.clear();
+	return true;
+}
+
+bool ObjectSettingsXmlParser::parserCallback_modelFileName(ParserNode *node) {
+	_textTagType = TagModelFileName;
+	return true;
+}
+
+bool ObjectSettingsXmlParser::parserCallback_defaultScale(ParserNode *node) {
+	_textTagType = TagDefaultScale;
+	return true;
+}
+
+bool ObjectSettingsXmlParser::textCallback(const Common::String &val) {
+	switch (_textTagType) {
+		case TagModelFileName:
+			_curObject._modelFileName = val;
+			break;
+		case TagDefaultScale:
+			_curObject._defaultScale.parse(val);
+			break;
+		default:
+			error("should only see text for model file name or scale");
+	}
+	return true;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/object_settings_xml_parser.h b/engines/tetraedge/game/object_settings_xml_parser.h
new file mode 100644
index 00000000000..c759156018b
--- /dev/null
+++ b/engines/tetraedge/game/object_settings_xml_parser.h
@@ -0,0 +1,70 @@
+/* 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_OBJECT_SETTINGS_XML_PARSER_H
+#define TETRAEDGE_GAME_OBJECT_SETTINGS_XML_PARSER_H
+
+#include "common/xmlparser.h"
+#include "tetraedge/game/object3d.h"
+#include "tetraedge/te/te_vector3f32.h"
+
+namespace Tetraedge {
+
+class ObjectSettingsXmlParser : public Common::XMLParser {
+public:
+	void setObjectSettings(Common::HashMap<Common::String, Object3D::ObjectSettings> *settings) {
+		_objectSettings = settings;
+	}
+
+	// Parser
+	CUSTOM_XML_PARSER(ObjectSettingsXmlParser) {
+		XML_KEY(ObjectsSettings)
+			XML_KEY(Object)
+				XML_PROP(name, true)
+				XML_KEY(modelFileName)
+				KEY_END()
+				XML_KEY(defaultScale)
+				KEY_END()
+			KEY_END()
+		KEY_END()
+	} PARSER_END()
+
+	// Parser callback methods
+	bool parserCallback_ObjectsSettings(ParserNode *node);
+	bool parserCallback_Object(ParserNode *node);
+	bool parserCallback_modelFileName(ParserNode *node);
+	bool parserCallback_defaultScale(ParserNode *node);
+	bool textCallback(const Common::String &val) override;
+
+private:
+	enum TextTagType {
+		TagModelFileName,
+		TagDefaultScale
+	};
+
+	TextTagType _textTagType;
+	Object3D::ObjectSettings _curObject;
+	Common::HashMap<Common::String, Object3D::ObjectSettings> *_objectSettings;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_OBJECT_SETTINGS_XML_PARSER_H
diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
new file mode 100644
index 00000000000..825042955f1
--- /dev/null
+++ b/engines/tetraedge/game/objectif.cpp
@@ -0,0 +1,213 @@
+/* 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 "common/textconsole.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/objectif.h"
+#include "tetraedge/te/te_vector2f32.h"
+
+namespace Tetraedge {
+
+/*static*/
+bool Objectif::_layoutsDirty = false;
+
+Objectif::Objectif() : _helpButtonVisible(false) {
+}
+
+void Objectif::enter() {
+	_gui1.buttonLayoutChecked("helpButton")->setVisible(true);
+	_helpButtonVisible = true;
+}
+
+bool Objectif::isMouseIn(const TeVector2s32 &mousept) {
+	TeLayout *bg = _gui1.layoutChecked("background");
+	if (bg->visible()) {
+		TeLayout *calepin = _gui1.layoutChecked("Calepin");
+		if (calepin->isMouseIn(mousept))
+			return true;
+		// otherwise check the helpButton
+	}
+	TeButtonLayout *btn = _gui2.buttonLayoutChecked("helpButton");
+	if (btn->visible())
+		return btn->isMouseIn(mousept);
+	return false;
+}
+
+void Objectif::load() {
+	Application *app = g_engine->getApplication();
+	_gui1.load("menus/objectif.lua");
+	_gui2.load("menus/helpButton.lua");
+
+	TeButtonLayout *btn = _gui2.buttonLayoutChecked("helpButton");
+	app->_frontLayout.addChild(btn);
+	btn->setVisible(true);
+	_helpButtonVisible = true;
+	btn->onMouseClickValidated().add(this, &Objectif::onHelpButtonValidated);
+
+	btn = _gui1.buttonLayoutChecked("helpQuit");
+	btn->onMouseClickValidated().add(this, &Objectif::onHelpButtonValidated);
+
+	_gui1.buttonLayoutChecked("background")->setVisible(false);
+
+	_gui2.spriteLayoutChecked("newUp")->setVisible(false);
+	_gui2.spriteLayoutChecked("newDown")->setVisible(false);
+	_gui2.spriteLayoutChecked("notNewUp")->setVisible(true);
+	_gui2.spriteLayoutChecked("notNewDown")->setVisible(true);
+
+	_layoutsDirty = true;
+}
+
+void Objectif::leave() {
+	TeLayout *layout;
+	layout = _gui1.layout("background");
+	if (layout)
+		layout->setVisible(false);
+	layout = _gui2.layout("helpButton");
+	if (layout) {
+		layout->setVisible(false);
+		_helpButtonVisible = false;
+	}
+}
+
+bool Objectif::onHelpButtonValidated() {
+	if (!_helpButtonVisible) {
+		_gui1.buttonLayoutChecked("background")->setVisible(false);
+		_gui2.buttonLayoutChecked("helpButton")->setVisible(true);
+		_helpButtonVisible = true;
+	} else {
+		_gui1.buttonLayoutChecked("background")->setVisible(true);
+		_gui2.spriteLayoutChecked("newUp")->setVisible(false);
+		_gui2.spriteLayoutChecked("newDown")->setVisible(false);
+		_gui2.spriteLayoutChecked("notNewUp")->setVisible(true);
+		_gui2.spriteLayoutChecked("notNewUp")->setVisible(true);
+		_gui2.spriteLayoutChecked("helpButton")->setVisible(false);
+		_helpButtonVisible = false;
+	}
+	return false;
+}
+
+void Objectif::reattachLayout(TeLayout *layout) {
+	TeButtonLayout *btn;
+
+	btn = _gui1.buttonLayout("background");
+	if (btn) {
+		layout->removeChild(btn);
+		layout->addChild(btn);
+	}
+
+	btn = _gui2.buttonLayout("helpButton");
+	if (btn) {
+		layout->removeChild(btn);
+		layout->addChild(btn);
+	}
+}
+
+void Objectif::removeChildren() {
+	TeLayout *tasks = _gui1.layoutChecked("tasks");
+	while (tasks->childCount()) {
+		Te3DObject2 *child = tasks->child(0);
+		TeTextLayout *text = dynamic_cast<TeTextLayout*>(child);
+		tasks->removeChild(child);
+		if (text)
+			delete text;
+	}
+	_layoutsDirty = true;
+}
+
+void Objectif::update() {
+	Game *game = g_engine->getGame();
+	game->luaScript().execute("UpdateHelp");
+	if (_layoutsDirty) {
+		TeLayout *tasks = _gui1.layoutChecked("tasks");
+		removeChildren();
+
+		int last_i = -1;
+		for (unsigned int i = 0; i < _tasks.size(); i++) {
+			if (!_tasks[i]._taskFlag)
+				continue;
+			if (last_i != -1 && _tasks[i]._headTask == _tasks[last_i]._headTask)
+				continue;
+			last_i = i;
+			createChildLayout(tasks, _tasks[i]._headTask, false);
+			// Creating the subtasks for this head
+			for (unsigned int j = 0; j < _tasks.size(); j++) {
+				if (_tasks[j]._taskFlag && _tasks[j]._headTask == _tasks[i]._headTask && _tasks[j]._subTask != "")
+					createChildLayout(tasks, _tasks[j]._subTask, true);
+			}
+		}
+
+		warning("TODO: Finish main part of Objectif::update");
+	}
+	_layoutsDirty = false;
+}
+
+void Objectif::createChildLayout(TeLayout *layout, Common::String const &taskId, bool isSubTask) {
+	TeTextLayout *text = new TeTextLayout();
+	text->setName(taskId);
+	text->setAnchor(TeVector3f32(0.0f, 0.0f, 0.0f));
+	text->setPositionType(TeILayout::RELATIVE_TO_PARENT);
+	text->setSizeType(TeILayout::RELATIVE_TO_PARENT);
+	Application *app = g_engine->getApplication();
+	// No help at difficulty 2.
+	if (app->difficulty() != 2) {
+		Common::String textVal;
+		if (!isSubTask) {
+			text->setSize(TeVector3f32(0.8f, 1.0f, 0.1f));
+			text->setPosition(TeVector3f32(0.1f, 0.0f, 0.1f));
+			textVal = "<section style=\"left\" /><color r=\"39\" g=\"85\" b=\"97\"/><font file=\"Common/Fonts/ComicRelief.ttf\" size=\"12\"/>";
+		} else {
+			text->setSize(TeVector3f32(0.75f, 1.0f, 0.1f));
+			text->setPosition(TeVector3f32(0.15f, 0.0f, 0.1f));
+			if (app->difficulty() == 0) {
+				textVal = "<section style=\"left\" /><color r=\"0\" g=\"0\" b=\"0\"/><font file=\"Common/Fonts/ComicRelief.ttf\" size=\"12\"/>\t";
+			} else {
+				textVal = "<section style=\"left\" /><color r=\"0\" g=\"0\" b=\"0\"/><font file=\"Common/Fonts/arial.ttf\" size=\"16\"/>";
+			}
+		}
+		textVal += app->getHelpText(taskId);
+		text->setText(textVal);
+	}
+
+	layout->addChild(text);
+}
+
+
+void Objectif::unload() {
+	removeChildren();
+	leave();
+	_gui1.unload();
+	_gui2.unload();
+	_tasks.clear();
+}
+
+void Objectif::setVisibleButtonHelp(bool visible) {
+	_gui2.buttonLayoutChecked("helpButton")->setVisible(visible);
+	_helpButtonVisible = visible;
+}
+
+void Objectif::setVisibleObjectif(bool visible) {
+	_gui1.buttonLayoutChecked("background")->setVisible(visible);
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/objectif.h b/engines/tetraedge/game/objectif.h
new file mode 100644
index 00000000000..2584462d2fe
--- /dev/null
+++ b/engines/tetraedge/game/objectif.h
@@ -0,0 +1,70 @@
+/* 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_OBJECTIF_H
+#define TETRAEDGE_GAME_OBJECTIF_H
+
+#include "tetraedge/te/te_vector2f32.h"
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class Objectif {
+public:
+	struct Task {
+		Common::String _headTask;
+		Common::String _subTask;
+		bool _taskFlag;
+	};
+
+	Objectif();
+
+	void createChildLayout(TeLayout *layout, Common::String const &taskId, bool isSubTask);
+	void enter();
+	bool hideBouton();
+	bool isMouseIn(const TeVector2s32 &mousept);
+	bool isVisibleObjectif();
+	void leave();
+	void load();
+	bool onHelpButtonValidated();
+	void reattachLayout(TeLayout *layout);
+	void removeChildren();
+	// void save()
+	void setVisibleButtonHelp(bool visible);
+	void setVisibleObjectif(bool visible);
+	// TODO add public members
+	void unload();
+	void update();
+
+	TeLuaGUI &gui1() { return _gui1; }
+
+private:
+	TeLuaGUI _gui1;
+	TeLuaGUI _gui2;
+	Common::Array<Task> _tasks;
+	bool _helpButtonVisible;
+
+	static bool _layoutsDirty;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_OBJECTIF_H
diff --git a/engines/tetraedge/game/options_menu.cpp b/engines/tetraedge/game/options_menu.cpp
new file mode 100644
index 00000000000..904445178e9
--- /dev/null
+++ b/engines/tetraedge/game/options_menu.cpp
@@ -0,0 +1,39 @@
+/* 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/game/options_menu.h"
+
+namespace Tetraedge {
+
+OptionsMenu::OptionsMenu() {
+}
+
+void OptionsMenu::enter() {
+
+}
+
+void OptionsMenu::leave() {
+
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/options_menu.h b/engines/tetraedge/game/options_menu.h
new file mode 100644
index 00000000000..075e24bf7c4
--- /dev/null
+++ b/engines/tetraedge/game/options_menu.h
@@ -0,0 +1,69 @@
+/* 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_OPTIONS_MENU_H
+#define TETRAEDGE_GAME_OPTIONS_MENU_H
+
+#include "tetraedge/te/te_lua_gui.h"
+#include "tetraedge/te/te_music.h"
+
+namespace Tetraedge {
+
+class OptionsMenu : public TeLuaGUI {
+public:
+	OptionsMenu();
+
+	void enter() override;
+	void leave() override;
+
+	bool onCloseTuto();
+	bool onCreditsButton();
+	bool onDialogVolumeMinusButton();
+	bool onDialogVolumePlusButton();
+	bool onMusicVolumeMinusButton();
+	bool onMusicVolumePlusButton();
+	bool onPrivacyPolicyButton();
+	bool onQuitButton();
+	bool onSFXVolumeMinusButton();
+	bool onSFXVolumePlusButton();
+	bool onSupportButton();
+	bool onTermsOfServiceButton();
+	bool onVideoVolumeMinusButton();
+	bool onVideoVolumePlusButton();
+	bool onVisibleTuto();
+	bool onVisibleTutoNextPage();
+
+	void updateDialogVolumeJauge();
+	void updateMusicVolumeJauge();
+	void updateSFXVolumeJauge();
+	void updateVideoVolumeJauge();
+
+private:
+
+	//  TODO: work out virtual thing here TeLuaGUI _gui2;
+
+	TeMusic _music1;
+	TeMusic _music2;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_OPTIONS_MENU_H
diff --git a/engines/tetraedge/game/owner_error_menu.cpp b/engines/tetraedge/game/owner_error_menu.cpp
new file mode 100644
index 00000000000..52aef50351d
--- /dev/null
+++ b/engines/tetraedge/game/owner_error_menu.cpp
@@ -0,0 +1,53 @@
+/* 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 "common/path.h"
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/game/owner_error_menu.h"
+
+namespace Tetraedge {
+
+OwnerErrorMenu::OwnerErrorMenu() : _entered(false) {
+}
+
+void OwnerErrorMenu::enter() {
+	_entered = true;
+	static const Common::Path luaPath("menus/ownerError/ownerError.lua");
+	load(luaPath.toString());
+	error("TODO: Finish implementation of OwnerErrorMenu::enter");
+	/*
+	Application *app = g_engine->getApplication();
+	TeLayout *menuLayout = TeLuaGUI::layout("menu");
+	 ...
+	 */
+}
+
+void OwnerErrorMenu::leave() {
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	TeLuaGUI::unload();
+	_entered = false;
+	app->mainMenu().enter();
+	app->fade();
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/owner_error_menu.h b/engines/tetraedge/game/owner_error_menu.h
new file mode 100644
index 00000000000..f935bb013da
--- /dev/null
+++ b/engines/tetraedge/game/owner_error_menu.h
@@ -0,0 +1,42 @@
+/* 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_OWNER_ERROR_MENU_H
+#define TETRAEDGE_GAME_OWNER_ERROR_MENU_H
+
+#include "tetraedge/te/te_lua_gui.h"
+
+namespace Tetraedge {
+
+class OwnerErrorMenu : public TeLuaGUI {
+public:
+	OwnerErrorMenu();
+
+	void enter() override;
+	void leave() override;
+
+private:
+	bool _entered;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_OWNER_ERROR_MENU_H
diff --git a/engines/tetraedge/game/question2.cpp b/engines/tetraedge/game/question2.cpp
new file mode 100644
index 00000000000..fbc55be9189
--- /dev/null
+++ b/engines/tetraedge/game/question2.cpp
@@ -0,0 +1,156 @@
+/* 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/game/application.h"
+#include "tetraedge/game/question2.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/tetraedge.h"
+
+namespace Tetraedge {
+
+Question2::Question2() {
+}
+
+Question2::~Question2() {
+	// Should have been cleared in leave() but just in case..
+	for (Answer *answer : _answers) {
+		delete answer;
+	}
+}
+
+void Question2::enter() {
+	TeButtonLayout *backgroundButton = _gui.buttonLayout("background");
+	if (backgroundButton)
+		backgroundButton->setVisible(true);
+	g_engine->getGame()->showMarkers(true);
+}
+
+void Question2::leave() {
+	TeLayout *background = _gui.layout("background");
+	if (!background)
+		return;
+
+	background->setVisible(false);
+
+	TeSpriteLayout *calepinLayout = _gui.spriteLayout("Calepin");
+	if (!calepinLayout)
+		error("Question2::leave: can't find Calepin.");
+
+	for (Answer *answer : _answers) {
+		TeLayout *alayout = answer->layout();
+		if (alayout == nullptr)
+			continue;
+		calepinLayout->removeChild(alayout);
+		answer->unload();
+		// TODO: original uses TeObject::deleteLater here.. should we
+		// do the same? why defer it?
+		delete answer;
+	}
+	_answers.clear();
+}
+
+void Question2::load() {
+	setName("dialog2");
+	setSizeType(RELATIVE_TO_PARENT);
+	const TeVector3f32 usersz = userSize();
+	setSize(TeVector3f32(1.0, 1.0, usersz.z()));
+	_gui.load("menus/answer.lua");
+
+	TeButtonLayout *backgroundButton = _gui.buttonLayout("background");
+	if (backgroundButton) {
+		addChild(backgroundButton);
+		backgroundButton->setVisible(false);
+	}
+}
+
+bool Question2::onAnswerValidated(Answer &answer) {
+	_onAnswerSignal.call(answer._str);
+	g_engine->getGame()->showMarkers(false);
+	leave();
+	return false;
+}
+
+void Question2::pushAnswer(const Common::String &name, const Common::String &unk, const Common::String &path) {
+	Answer *answer = new Answer();
+	answer->load(name, unk, path);
+	answer->_onButtonValidatedSignal.add(this, &Question2::onAnswerValidated);
+	TeLayout *alayout = answer->layout();
+	if (!alayout)
+		error("No Answer layout after loading %s!", path.c_str());
+	TeButtonLayout *blayout = dynamic_cast<TeButtonLayout *>(alayout);
+	if (!blayout)
+		error("No Answer button layout after loading %s!", path.c_str());
+
+	blayout->setState(TeButtonLayout::BUTTON_STATE_UP);
+	_answers.push_back(answer);
+
+	float xpos;
+	blayout->setPositionType(RELATIVE_TO_PARENT);
+	if (!path.contains("Cal_FIN.lua")) {
+		setSize(TeVector3f32(0.45f, 0.065f, 1.0f));
+		xpos = 0.3f;
+	} else {
+		setSize(TeVector3f32(0.15f, 0.065f, 1.0f));
+		xpos = 0.15f;
+	}
+	setPosition(TeVector3f32(xpos, _answers.size() * 0.08f + 0.06f, 1.0f));
+
+	blayout->_upLayout->setSizeType(RELATIVE_TO_PARENT);
+	blayout->_upLayout->setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
+	blayout->_downLayout->setSizeType(RELATIVE_TO_PARENT);
+	blayout->_downLayout->setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
+
+	TeSpriteLayout *calepinLayout = _gui.spriteLayout("Calepin");
+	if (calepinLayout)
+		calepinLayout->setParent(alayout);
+
+	enter();
+}
+
+void Question2::unload() {
+	leave();
+	_gui.unload();
+}
+
+TeLayout *Question2::Answer::layout() {
+	return _gui.layout("answer");
+}
+
+void Question2::Answer::load(const Common::String &name, const Common::String &unk, const Common::String &path) {
+	_str = name;
+	_gui.load(path);
+	TeButtonLayout *answerButton = _gui.buttonLayout("answer");
+	if (answerButton) {
+		answerButton->onMouseClickValidated().add(this, &Question2::Answer::onButtonValidated);
+		answerButton->_someClickFlag = false;
+	}
+}
+
+void Question2::Answer::unload() {
+	_gui.unload();
+}
+
+bool Question2::Answer::onButtonValidated() {
+	_onButtonValidatedSignal.call(*this);
+	return false;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/question2.h b/engines/tetraedge/game/question2.h
new file mode 100644
index 00000000000..6bf9f408560
--- /dev/null
+++ b/engines/tetraedge/game/question2.h
@@ -0,0 +1,64 @@
+/* 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_QUESTION2_H
+#define TETRAEDGE_GAME_QUESTION2_H
+
+#include "tetraedge/te/te_lua_gui.h"
+#include "tetraedge/te/te_layout.h"
+
+
+namespace Tetraedge {
+
+class Question2 : public TeLayout {
+public:
+	Question2();
+	~Question2();
+
+	class Answer {
+	public:
+		TeLayout *layout();
+		void load(const Common::String &name, const Common::String &unk, const Common::String &path);
+		void unload();
+		bool onButtonValidated();
+
+		TeLuaGUI _gui;
+		Common::String _str;
+		TeSignal1Param<Question2::Answer &> _onButtonValidatedSignal;
+	};
+
+	void enter();
+	void leave();
+	void load();
+	bool onAnswerValidated(Answer &answer);
+	void pushAnswer(const Common::String &name, const Common::String &unk, const Common::String &path);
+	void unload();
+
+private:
+	TeLuaGUI _gui;
+	Common::Array<Answer *> _answers;
+	TeSignal1Param<const Common::String &> _onAnswerSignal;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_QUESTION2_H
diff --git a/engines/tetraedge/game/splash_screens.cpp b/engines/tetraedge/game/splash_screens.cpp
new file mode 100644
index 00000000000..a14519a9b96
--- /dev/null
+++ b/engines/tetraedge/game/splash_screens.cpp
@@ -0,0 +1,95 @@
+/* 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 "common/config-manager.h"
+#include "common/file.h"
+#include "common/path.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/game/splash_screens.h"
+
+namespace Tetraedge {
+
+SplashScreens::SplashScreens() : _splashNo(0), _entered(false) {
+	_timer.alarmSignal().add(this, &SplashScreens::onAlarm);
+}
+
+void SplashScreens::enter()	{
+	if (!_entered) {
+		_entered = true;
+		_splashNo = 0;
+		static const Common::Path scriptPath("menus/splashes/splash0.lua");
+		if (Common::File::exists(scriptPath)) {
+			TeLuaGUI::load(scriptPath.toString());
+			Application *app = g_engine->getApplication();
+			TeLayout *splash = layout("splash");
+			app->_frontLayout.addChild(splash);
+			app->performRender();
+		}
+		onAlarm();
+	}
+}
+
+bool SplashScreens::onAlarm() {
+	Application *app = g_engine->getApplication();
+	app->visualFade().init();
+	app->captureFade();
+	TeLuaGUI::unload();
+	const Common::String scriptName = Common::String::format("menus/splashes/splash%d.lua", _splashNo);
+	_splashNo++;
+
+	if (ConfMan.get("skipsplash") == "true") {
+		onQuitSplash();
+		return true;
+	}
+
+	if (!Common::File::exists(scriptName)) {
+		onQuitSplash();
+	} else {
+		load(scriptName);
+
+		TeButtonLayout *btnLayout = buttonLayout("splash");
+		btnLayout->onMouseClickValidated().add<SplashScreens>(this, &SplashScreens::onQuitSplash);
+
+		TeLayout *splash = layout("splash");
+		app->_frontLayout.addChild(splash);
+
+		_timer.start();
+		_timer.setAlarmIn(1500000);
+	}
+
+	app->fade();
+	return true;
+}
+
+bool SplashScreens::onQuitSplash() {
+	_timer.stop();
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	TeLuaGUI::unload();
+	_entered = false;
+	app->mainMenu().enter();
+	app->fade();
+	return false;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/splash_screens.h b/engines/tetraedge/game/splash_screens.h
new file mode 100644
index 00000000000..b393dca3991
--- /dev/null
+++ b/engines/tetraedge/game/splash_screens.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_SPLASH_SCREENS_H
+#define TETRAEDGE_GAME_SPLASH_SCREENS_H
+
+#include "tetraedge/te/te_lua_gui.h"
+#include "tetraedge/te/te_timer.h"
+
+namespace Tetraedge {
+
+class SplashScreens : public TeLuaGUI {
+public:
+	SplashScreens();
+
+	void enter() override;
+	void leave() override {
+		_entered = false;
+	}
+
+	bool onAlarm();
+	bool onQuitSplash();
+
+private:
+	bool _entered;
+	TeTimer _timer;
+	int _splashNo;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_SPLASH_SCREENS_H
diff --git a/engines/tetraedge/metaengine.cpp b/engines/tetraedge/metaengine.cpp
new file mode 100644
index 00000000000..ea4c2035e6c
--- /dev/null
+++ b/engines/tetraedge/metaengine.cpp
@@ -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/>.
+ *
+ */
+
+#include "tetraedge/metaengine.h"
+#include "tetraedge/detection.h"
+#include "tetraedge/tetraedge.h"
+
+const char *TetraedgeMetaEngine::getName() const {
+	return "tetraedge";
+}
+
+Common::Error TetraedgeMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+	*engine = new Tetraedge::TetraedgeEngine(syst, desc);
+	return Common::kNoError;
+}
+
+bool TetraedgeMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return
+		(f == kSavesUseExtendedFormat) ||
+		(f == kSimpleSavesNames) ||
+	    (f == kSupportsListSaves) ||
+	    (f == kSupportsDeleteSave) ||
+	    (f == kSavesSupportMetaInfo) ||
+	    (f == kSavesSupportThumbnail) ||
+	    (f == kSupportsLoadingDuringStartup);
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(TETRAEDGE)
+REGISTER_PLUGIN_DYNAMIC(TETRAEDGE, PLUGIN_TYPE_ENGINE, TetraedgeMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(TETRAEDGE, PLUGIN_TYPE_ENGINE, TetraedgeMetaEngine);
+#endif
diff --git a/engines/tetraedge/metaengine.h b/engines/tetraedge/metaengine.h
new file mode 100644
index 00000000000..13a43c89711
--- /dev/null
+++ b/engines/tetraedge/metaengine.h
@@ -0,0 +1,42 @@
+/* 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_METAENGINE_H
+#define TETRAEDGE_METAENGINE_H
+
+#include "common/achievements.h"
+#include "engines/advancedDetector.h"
+
+class TetraedgeMetaEngine : public AdvancedMetaEngine {
+public:
+	const char *getName() const override;
+
+	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
+
+	/**
+	 * Determine whether the engine supports the specified MetaEngine feature.
+	 *
+	 * Used by e.g. the launcher to determine whether to enable the Load button.
+	 */
+	bool hasFeature(MetaEngineFeature f) const override;
+};
+
+#endif
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
new file mode 100644
index 00000000000..9873d9ceabf
--- /dev/null
+++ b/engines/tetraedge/module.mk
@@ -0,0 +1,126 @@
+MODULE := engines/tetraedge
+
+MODULE_OBJS := \
+	tetraedge.o \
+	to_lua.o \
+	game/application.o \
+	game/billboard.o \
+	game/bonus_menu.o \
+	game/cellphone.o \
+	game/character.o \
+	game/character_settings_xml_parser.o \
+	game/characters_shadow.o \
+	game/confirm.o \
+	game/credits.o \
+	game/dialog2.o \
+	game/document.o \
+	game/documents_browser.o \
+	game/gallery_menu.o \
+	game/game.o \
+	game/game_achievements.o \
+	game/game_sound.o \
+	game/global_bonus_menu.o \
+	game/help_option_menu.o \
+	game/how_to.o \
+	game/in_game_scene.o \
+	game/inventory.o \
+	game/inventory_menu.o \
+	game/inventory_object.o \
+	game/inventory_objects_xml_parser.o \
+	game/loading_menu.o \
+	game/loc_file.o \
+	game/lua_binds.o \
+	game/main_menu.o \
+	game/notifier.o \
+	game/object3d.o \
+	game/object_settings_xml_parser.o \
+	game/objectif.o \
+	game/options_menu.o \
+	game/owner_error_menu.o \
+	game/question2.o \
+	game/splash_screens.o \
+	te/micropather.o \
+	te/te_3d_object2.o \
+	te/te_3d_texture.o \
+	te/te_animation.o \
+	te/te_bezier_curve.o \
+	te/te_button_layout.o \
+	te/te_camera.o \
+	te/te_checkbox_layout.o \
+	te/te_clip_layout.o \
+	te/te_color.o \
+	te/te_core.o \
+	te/te_extended_text_layout.o \
+	te/te_fee_move_zone.o \
+	te/te_font3.o \
+	te/te_frame_anim.o \
+	te/te_free_move_zone.o \
+	te/te_i_3d_object2.o \
+	te/te_i_layout.o \
+	te/te_i_loc.o \
+	te/te_image.o \
+	te/te_input_mgr.o \
+	te/te_interpolation.o \
+	te/te_jpeg.o \
+	te/te_layout.o \
+	te/te_light.o \
+	te/te_list_layout.o \
+	te/te_lua_context.o \
+	te/te_lua_gui.o \
+	te/te_lua_gui_lua_callbacks.o \
+	te/te_lua_script.o \
+	te/te_lua_thread.o \
+	te/te_material.o \
+	te/te_matricies_stack.o \
+	te/te_matrix4x4.o \
+	te/te_mesh.o \
+	te/te_model.o \
+	te/te_model_animation.o \
+	te/te_model_vertex_animation.o \
+	te/te_music.o \
+	te/te_name_val_xml_parser.o \
+	te/te_object.o \
+	te/te_obp.o \
+	te/te_palette.o \
+	te/te_pick_mesh2.o \
+	te/te_png.o \
+	te/te_quaternion.o \
+	te/te_real_timer.o \
+	te/te_renderer.o \
+	te/te_resource.o \
+	te/te_resource_manager.o \
+	te/te_scene.o \
+	te/te_screen.o \
+	te/te_scrolling_layout.o \
+	te/te_scummvm_codec.o \
+	te/te_sfx.o \
+	te/te_sound_manager.o \
+	te/te_sprite_layout.o \
+	te/te_text_base2.o \
+	te/te_text_layout.o \
+	te/te_text_layout_xml_parser.o \
+	te/te_tga.o \
+	te/te_theora.o \
+	te/te_tiled_surface.o \
+	te/te_tiled_texture.o \
+	te/te_timer.o \
+	te/te_trs.o \
+	te/te_variant.o \
+	te/te_vector2f32.o \
+	te/te_vector2s32.o \
+	te/te_vector3f32.o \
+	te/te_visual_fade.o \
+	te/te_xml_gui.o \
+	console.o \
+	metaengine.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_TETRAEDGE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o
diff --git a/engines/tetraedge/te/micropather.cpp b/engines/tetraedge/te/micropather.cpp
new file mode 100644
index 00000000000..2e469d6791f
--- /dev/null
+++ b/engines/tetraedge/te/micropather.cpp
@@ -0,0 +1,1101 @@
+/* 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/>.
+ *
+ */
+
+/*
+
+This is a lightly modified version of MicroPather, from
+github.com/leethomason/MicroPather.  Modifications were made to fit with
+ScummVM coding style and APIs.
+
+The original copyright message is:
+
+-------
+Copyright (c) 2000-2009 Lee Thomason (www.grinninglizard.com)
+
+Grinning Lizard Utilities.
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#define MPASSERT assert
+
+//#define DEBUG_PATH
+//#define DEBUG_PATH_DEEP
+//#define TRACK_COLLISION
+//#define DEBUG_CACHING
+
+//#ifdef DEBUG_CACHING
+//#include "../grinliz/gldebug.h"
+//#endif
+
+#include "micropather.h"
+
+using namespace Tetraedge::micropather;
+
+class OpenQueue
+{
+  public:
+	OpenQueue( Graph* _graph )
+	{
+		graph = _graph;
+		sentinel = (PathNode*) sentinelMem;
+		sentinel->InitSentinel();
+		#ifdef DEBUG
+			sentinel->CheckList();
+		#endif
+	}
+	~OpenQueue()	{}
+
+	void Push( PathNode* pNode );
+	PathNode* Pop();
+	void Update( PathNode* pNode );
+
+	bool Empty()	{ return sentinel->next == sentinel; }
+
+  private:
+	OpenQueue( const OpenQueue& );	// undefined and unsupported
+	void operator=( const OpenQueue& );
+
+	PathNode* sentinel;
+	int sentinelMem[ ( sizeof( PathNode ) + sizeof( int ) ) / sizeof( int ) ];
+	Graph* graph;	// for debugging
+};
+
+
+void OpenQueue::Push( PathNode* pNode )
+{
+
+	MPASSERT( pNode->inOpen == 0 );
+	MPASSERT( pNode->inClosed == 0 );
+
+#ifdef DEBUG_PATH_DEEP
+	debug( "Open Push: " );
+	graph->PrintStateInfo( pNode->state );
+	debug( " total=%.1f\n", pNode->totalCost );
+#endif
+
+	// Add sorted. Lowest to highest cost path. Note that the sentinel has
+	// a value of FLT_MAX, so it should always be sorted in.
+	MPASSERT( pNode->totalCost < FLT_MAX );
+	PathNode* iter = sentinel->next;
+	while ( true )
+	{
+		if ( pNode->totalCost < iter->totalCost ) {
+			iter->AddBefore( pNode );
+			pNode->inOpen = 1;
+			break;
+		}
+		iter = iter->next;
+	}
+	MPASSERT( pNode->inOpen );	// make sure this was actually added.
+#ifdef DEBUG
+	sentinel->CheckList();
+#endif
+}
+
+PathNode* OpenQueue::Pop()
+{
+	MPASSERT( sentinel->next != sentinel );
+	PathNode* pNode = sentinel->next;
+	pNode->Unlink();
+#ifdef DEBUG
+	sentinel->CheckList();
+#endif
+
+	MPASSERT( pNode->inClosed == 0 );
+	MPASSERT( pNode->inOpen == 1 );
+	pNode->inOpen = 0;
+
+#ifdef DEBUG_PATH_DEEP
+	debug( "Open Pop: " );
+	graph->PrintStateInfo( pNode->state );
+	debug( " total=%.1f\n", pNode->totalCost );
+#endif
+
+	return pNode;
+}
+
+void OpenQueue::Update( PathNode* pNode )
+{
+#ifdef DEBUG_PATH_DEEP
+	debug( "Open Update: " );
+	graph->PrintStateInfo( pNode->state );
+	debug( " total=%.1f\n", pNode->totalCost );
+#endif
+
+	MPASSERT( pNode->inOpen );
+
+	// If the node now cost less than the one before it,
+	// move it to the front of the list.
+	if ( pNode->prev != sentinel && pNode->totalCost < pNode->prev->totalCost ) {
+		pNode->Unlink();
+		sentinel->next->AddBefore( pNode );
+	}
+
+	// If the node is too high, move to the right.
+	if ( pNode->totalCost > pNode->next->totalCost ) {
+		PathNode* it = pNode->next;
+		pNode->Unlink();
+
+		while ( pNode->totalCost > it->totalCost )
+			it = it->next;
+
+		it->AddBefore( pNode );
+#ifdef DEBUG
+		sentinel->CheckList();
+#endif
+	}
+}
+
+
+class ClosedSet
+{
+  public:
+	ClosedSet( Graph* _graph )		{ this->graph = _graph; }
+	~ClosedSet()	{}
+
+	void Add( PathNode* pNode )
+	{
+		#ifdef DEBUG_PATH_DEEP
+			debug( "Closed add: " );
+			graph->PrintStateInfo( pNode->state );
+			debug( " total=%.1f\n", pNode->totalCost );
+		#endif
+		#ifdef DEBUG
+		MPASSERT( pNode->inClosed == 0 );
+		MPASSERT( pNode->inOpen == 0 );
+		#endif
+		pNode->inClosed = 1;
+	}
+
+	void Remove( PathNode* pNode )
+	{
+		#ifdef DEBUG_PATH_DEEP
+			debug( "Closed remove: " );
+			graph->PrintStateInfo( pNode->state );
+			debug( " total=%.1f\n", pNode->totalCost );
+		#endif
+		MPASSERT( pNode->inClosed == 1 );
+		MPASSERT( pNode->inOpen == 0 );
+
+		pNode->inClosed = 0;
+	}
+
+  private:
+	ClosedSet( const ClosedSet& );
+	void operator=( const ClosedSet& );
+	Graph* graph;
+};
+
+
+PathNodePool::PathNodePool( unsigned _allocate, unsigned _typicalAdjacent )
+	: firstBlock( 0 ),
+	  blocks( 0 ),
+#if defined( MICROPATHER_STRESS )
+	  allocate( 32 ),
+#else
+	  allocate( _allocate ),
+#endif
+	  nAllocated( 0 ),
+	  nAvailable( 0 )
+{
+	freeMemSentinel.InitSentinel();
+
+	cacheCap = allocate * _typicalAdjacent;
+	cacheSize = 0;
+	cache = (NodeCost*)malloc(cacheCap * sizeof(NodeCost));
+
+	// Want the behavior that if the actual number of states is specified, the cache
+	// will be at least that big.
+	hashShift = 3;	// 8 (only useful for stress testing)
+#if !defined( MICROPATHER_STRESS )
+	while( HashSize() < allocate )
+		++hashShift;
+#endif
+	hashTable = (PathNode**)calloc( HashSize(), sizeof(PathNode*) );
+
+	blocks = firstBlock = NewBlock();
+//	debug( "HashSize=%d allocate=%d\n", HashSize(), allocate );
+	totalCollide = 0;
+}
+
+
+PathNodePool::~PathNodePool()
+{
+	Clear();
+	free( firstBlock );
+	free( cache );
+	free( hashTable );
+#ifdef TRACK_COLLISION
+	debug( "Total collide=%d HashSize=%d HashShift=%d\n", totalCollide, HashSize(), hashShift );
+#endif
+}
+
+
+bool PathNodePool::PushCache( const NodeCost* nodes, int nNodes, int* start ) {
+	*start = -1;
+	if ( nNodes+cacheSize <= cacheCap ) {
+		for( int i=0; i<nNodes; ++i ) {
+			cache[i+cacheSize] = nodes[i];
+		}
+		*start = cacheSize;
+		cacheSize += nNodes;
+		return true;
+	}
+	return false;
+}
+
+
+void PathNodePool::GetCache( int start, int nNodes, NodeCost* nodes ) {
+	MPASSERT( start >= 0 && start < cacheCap );
+	MPASSERT( nNodes > 0 );
+	MPASSERT( start + nNodes <= cacheCap );
+	memcpy( nodes, &cache[start], sizeof(NodeCost)*nNodes );
+}
+
+
+void PathNodePool::Clear()
+{
+#ifdef TRACK_COLLISION
+	// Collision tracking code.
+	int collide=0;
+	for( unsigned i=0; i<HashSize(); ++i ) {
+		if ( hashTable[i] && (hashTable[i]->child[0] || hashTable[i]->child[1]) )
+			++collide;
+	}
+	//debug( "PathNodePool %d/%d collision=%d %.1f%%\n", nAllocated, HashSize(), collide, 100.0f*(float)collide/(float)HashSize() );
+	totalCollide += collide;
+#endif
+
+	Block* b = blocks;
+	while( b ) {
+		Block* temp = b->nextBlock;
+		if ( b != firstBlock ) {
+			free( b );
+		}
+		b = temp;
+	}
+	blocks = firstBlock;	// Don't delete the first block (we always need at least that much memory.)
+
+	// Set up for new allocations (but don't do work we don't need to. Reset/Clear can be called frequently.)
+	if ( nAllocated > 0 ) {
+		freeMemSentinel.next = &freeMemSentinel;
+		freeMemSentinel.prev = &freeMemSentinel;
+
+		memset( hashTable, 0, sizeof(PathNode*)*HashSize() );
+		for( unsigned i=0; i<allocate; ++i ) {
+			freeMemSentinel.AddBefore( &firstBlock->pathNode[i] );
+		}
+	}
+	nAvailable = allocate;
+	nAllocated = 0;
+	cacheSize = 0;
+}
+
+
+PathNodePool::Block* PathNodePool::NewBlock()
+{
+	Block* block = (Block*) calloc( 1, sizeof(Block) + sizeof(PathNode)*(allocate-1) );
+	block->nextBlock = 0;
+
+	nAvailable += allocate;
+	for( unsigned i=0; i<allocate; ++i ) {
+		freeMemSentinel.AddBefore( &block->pathNode[i] );
+	}
+	return block;
+}
+
+
+unsigned PathNodePool::Hash( void* voidval )
+{
+	/*
+		Spent quite some time on this, and the result isn't quite satifactory. The
+		input set is the size of a void*, and is generally (x,y) pairs or memory pointers.
+
+		FNV resulting in about 45k collisions in a (large) test and some other approaches
+		about the same.
+
+		Simple folding reduces collisions to about 38k - big improvement. However, that may
+		be an artifact of the (x,y) pairs being well distributed. And for either the x,y case
+		or the pointer case, there are probably very poor hash table sizes that cause "overlaps"
+		and grouping. (An x,y encoding with a hashShift of 8 is begging for trouble.)
+
+		The best tested results are simple folding, but that seems to beg for a pathelogical case.
+		FNV-1a was the next best choice, without obvious pathelogical holes.
+
+		Finally settled on h%HashMask(). Simple, but doesn't have the obvious collision cases of folding.
+	*/
+	/*
+	// Time: 567
+	// FNV-1a
+	// http://isthe.com/chongo/tech/comp/fnv/
+	// public domain.
+	MP_UPTR val = (MP_UPTR)(voidval);
+	const unsigned char *p = (unsigned char *)(&val);
+	unsigned int h = 2166136261;
+
+	for( size_t i=0; i<sizeof(MP_UPTR); ++i, ++p ) {
+		h ^= *p;
+		h *= 16777619;
+	}
+	// Fold the high bits to the low bits. Doesn't (generally) use all
+	// the bits since the shift is usually < 16, but better than not
+	// using the high bits at all.
+	return ( h ^ (h>>hashShift) ^ (h>>(hashShift*2)) ^ (h>>(hashShift*3)) ) & HashMask();
+	*/
+	/*
+	// Time: 526
+	MP_UPTR h = (MP_UPTR)(voidval);
+	return ( h ^ (h>>hashShift) ^ (h>>(hashShift*2)) ^ (h>>(hashShift*3)) ) & HashMask();
+	*/
+
+	// Time: 512
+	// The HashMask() is used as the divisor. h%1024 has lots of common
+	// repetitions, but h%1023 will move things out more.
+	MP_UPTR h = (MP_UPTR)(voidval);
+	return h % HashMask();
+}
+
+
+
+PathNode* PathNodePool::Alloc()
+{
+	if ( freeMemSentinel.next == &freeMemSentinel ) {
+		MPASSERT( nAvailable == 0 );
+
+		Block* b = NewBlock();
+		b->nextBlock = blocks;
+		blocks = b;
+		MPASSERT( freeMemSentinel.next != &freeMemSentinel );
+	}
+	PathNode* pathNode = freeMemSentinel.next;
+	pathNode->Unlink();
+
+	++nAllocated;
+	MPASSERT( nAvailable > 0 );
+	--nAvailable;
+	return pathNode;
+}
+
+
+void PathNodePool::AddPathNode( unsigned key, PathNode* root )
+{
+	if ( hashTable[key] ) {
+		PathNode* p = hashTable[key];
+		while( true ) {
+			int dir = (root->state < p->state) ? 0 : 1;
+			if ( p->child[dir] ) {
+				p = p->child[dir];
+			}
+			else {
+				p->child[dir] = root;
+				break;
+			}
+		}
+	}
+	else {
+		hashTable[key] = root;
+	}
+}
+
+
+PathNode* PathNodePool::FetchPathNode( void* state )
+{
+	unsigned key = Hash( state );
+
+	PathNode* root = hashTable[key];
+	while( root ) {
+		if ( root->state == state ) {
+			break;
+		}
+		root = ( state < root->state ) ? root->child[0] : root->child[1];
+	}
+	MPASSERT( root );
+	return root;
+}
+
+
+PathNode* PathNodePool::GetPathNode( unsigned frame, void* _state, float _costFromStart, float _estToGoal, PathNode* _parent )
+{
+	unsigned key = Hash( _state );
+
+	PathNode* root = hashTable[key];
+	while( root ) {
+		if ( root->state == _state ) {
+			if ( root->frame == frame )		// This is the correct state and correct frame.
+				break;
+			// Correct state, wrong frame.
+			root->Init( frame, _state, _costFromStart, _estToGoal, _parent );
+			break;
+		}
+		root = ( _state < root->state ) ? root->child[0] : root->child[1];
+	}
+	if ( !root ) {
+		// allocate new one
+		root = Alloc();
+		root->Clear();
+		root->Init( frame, _state, _costFromStart, _estToGoal, _parent );
+		AddPathNode( key, root );
+	}
+	return root;
+}
+
+
+void PathNode::Init(	unsigned _frame,
+						void* _state,
+						float _costFromStart,
+						float _estToGoal,
+						PathNode* _parent )
+{
+	state = _state;
+	costFromStart = _costFromStart;
+	estToGoal = _estToGoal;
+	CalcTotalCost();
+	parent = _parent;
+	frame = _frame;
+	inOpen = 0;
+	inClosed = 0;
+}
+
+
+void PathNode::Clear()
+{
+	memset( this, 0, sizeof( PathNode ) );
+	numAdjacent = -1;
+	cacheIndex  = -1;
+}
+
+MicroPather::MicroPather( Graph* _graph, unsigned allocate, unsigned typicalAdjacent, bool cache )
+	:	pathNodePool( allocate, typicalAdjacent ),
+		graph( _graph ),
+		frame( 0 )
+{
+	MPASSERT( allocate );
+	MPASSERT( typicalAdjacent );
+	pathCache = 0;
+	if ( cache ) {
+		pathCache = new PathCache( allocate*4 );	// untuned arbitrary constant
+	}
+}
+
+
+MicroPather::~MicroPather()
+{
+	delete pathCache;
+}
+
+
+void MicroPather::Reset()
+{
+	pathNodePool.Clear();
+	if ( pathCache ) {
+		pathCache->Reset();
+	}
+	frame = 0;
+}
+
+
+void MicroPather::GoalReached( PathNode* node, void* start, void* end, Common::Array< void* > *_path )
+{
+	Common::Array< void* >& path = *_path;
+	path.clear();
+
+	// We have reached the goal.
+	// How long is the path? Used to allocate the vector which is returned.
+	int count = 1;
+	PathNode* it = node;
+	while( it->parent )
+	{
+		++count;
+		it = it->parent;
+	}
+
+	// Now that the path has a known length, allocate
+	// and fill the vector that will be returned.
+	if ( count < 3 )
+	{
+		// Handle the short, special case.
+		path.resize(2);
+		path[0] = start;
+		path[1] = end;
+	}
+	else
+	{
+		path.resize(count);
+
+		path[0] = start;
+		path[count-1] = end;
+		count-=2;
+		it = node->parent;
+
+		while ( it->parent )
+		{
+			path[count] = it->state;
+			it = it->parent;
+			--count;
+		}
+	}
+
+	if ( pathCache ) {
+		costVec.clear();
+
+		PathNode* pn0 = pathNodePool.FetchPathNode( path[0] );
+		PathNode* pn1 = 0;
+		for( unsigned i=0; i<path.size()-1; ++i ) {
+			pn1 = pathNodePool.FetchPathNode( path[i+1] );
+			nodeCostVec.clear();
+			GetNodeNeighbors( pn0, &nodeCostVec );
+			for( unsigned j=0; j<nodeCostVec.size(); ++j ) {
+				if ( nodeCostVec[j].node == pn1 ) {
+					costVec.push_back( nodeCostVec[j].cost );
+					break;
+				}
+			}
+			MPASSERT( costVec.size() == i+1 );
+			pn0 = pn1;
+		}
+		pathCache->Add( path, costVec );
+	}
+
+	#ifdef DEBUG_PATH
+	debug( "Path: " );
+	int counter=0;
+	#endif
+	for ( unsigned k=0; k<path.size(); ++k )
+	{
+		#ifdef DEBUG_PATH
+		graph->PrintStateInfo( path[k] );
+		debug( " " );
+		++counter;
+		if ( counter == 8 )
+		{
+			debug( "\n" );
+			counter = 0;
+		}
+		#endif
+	}
+	#ifdef DEBUG_PATH
+	debug( "Cost=%.1f Checksum %d\n", node->costFromStart, checksum );
+	#endif
+}
+
+
+void MicroPather::GetNodeNeighbors( PathNode* node, Common::Array< NodeCost >* pNodeCost )
+{
+	if ( node->numAdjacent == 0 ) {
+		// it has no neighbors.
+		pNodeCost->resize( 0 );
+	}
+	else if ( node->cacheIndex < 0 )
+	{
+		// Not in the cache. Either the first time or just didn't fit. We don't know
+		// the number of neighbors and need to call back to the client.
+		stateCostVec.resize( 0 );
+		graph->AdjacentCost( node->state, &stateCostVec );
+
+		#ifdef DEBUG
+		{
+			// If this assert fires, you have passed a state
+			// as its own neighbor state. This is impossible --
+			// bad things will happen.
+			for ( unsigned i=0; i<stateCostVec.size(); ++i )
+				MPASSERT( stateCostVec[i].state != node->state );
+		}
+		#endif
+
+		pNodeCost->resize( stateCostVec.size() );
+		node->numAdjacent = stateCostVec.size();
+
+		if ( node->numAdjacent > 0 ) {
+			// Now convert to pathNodes.
+			// Note that the microsoft std library is actually pretty slow.
+			// Move things to temp vars to help.
+			const unsigned stateCostVecSize = stateCostVec.size();
+			const StateCost* stateCostVecPtr = &stateCostVec[0];
+			NodeCost* pNodeCostPtr = &(*pNodeCost)[0];
+
+			for( unsigned i=0; i<stateCostVecSize; ++i ) {
+				void* state = stateCostVecPtr[i].state;
+				pNodeCostPtr[i].cost = stateCostVecPtr[i].cost;
+				pNodeCostPtr[i].node = pathNodePool.GetPathNode( frame, state, FLT_MAX, FLT_MAX, 0 );
+			}
+
+			// Can this be cached?
+			int start = 0;
+			if ( pNodeCost->size() > 0 && pathNodePool.PushCache( pNodeCostPtr, pNodeCost->size(), &start ) ) {
+				node->cacheIndex = start;
+			}
+		}
+	}
+	else {
+		// In the cache!
+		pNodeCost->resize( node->numAdjacent );
+		NodeCost* pNodeCostPtr = &(*pNodeCost)[0];
+		pathNodePool.GetCache( node->cacheIndex, node->numAdjacent, pNodeCostPtr );
+
+		// A node is uninitialized (even if memory is allocated) if it is from a previous frame.
+		// Check for that, and Init() as necessary.
+		for( int i=0; i<node->numAdjacent; ++i ) {
+			PathNode* pNode = pNodeCostPtr[i].node;
+			if ( pNode->frame != frame ) {
+				pNode->Init( frame, pNode->state, FLT_MAX, FLT_MAX, 0 );
+			}
+		}
+	}
+}
+
+
+#ifdef DEBUG
+/*
+void MicroPather::DumpStats()
+{
+	int hashTableEntries = 0;
+	for( int i=0; i<HASH_SIZE; ++i )
+		if ( hashTable[i] )
+			++hashTableEntries;
+
+	int pathNodeBlocks = 0;
+	for( PathNode* node = pathNodeMem; node; node = node[ALLOCATE-1].left )
+		++pathNodeBlocks;
+	debug( "HashTableEntries=%d/%d PathNodeBlocks=%d [%dk] PathNodes=%d SolverCalled=%d\n",
+			  hashTableEntries, HASH_SIZE, pathNodeBlocks,
+			  pathNodeBlocks*ALLOCATE*sizeof(PathNode)/1024,
+			  pathNodeCount,
+			  frame );
+}
+*/
+#endif
+
+
+void MicroPather::StatesInPool( Common::Array< void* >* stateVec )
+{
+ 	stateVec->clear();
+	pathNodePool.AllStates( frame, stateVec );
+}
+
+
+void PathNodePool::AllStates( unsigned frame, Common::Array< void* >* stateVec )
+{
+    for ( Block* b=blocks; b; b=b->nextBlock )
+    {
+    	for( unsigned i=0; i<allocate; ++i )
+    	{
+    	    if ( b->pathNode[i].frame == frame )
+	    	    stateVec->push_back( b->pathNode[i].state );
+    	}
+	}
+}
+
+
+PathCache::PathCache( int _allocated )
+{
+	mem = new Item[_allocated];
+	memset( mem, 0, sizeof(*mem)*_allocated );
+	allocated = _allocated;
+	nItems = 0;
+	hit = 0;
+	miss = 0;
+}
+
+
+PathCache::~PathCache()
+{
+	delete [] mem;
+}
+
+
+void PathCache::Reset()
+{
+	if ( nItems ) {
+		memset( mem, 0, sizeof(*mem)*allocated );
+		nItems = 0;
+		hit = 0;
+		miss = 0;
+	}
+}
+
+
+void PathCache::Add( const Common::Array< void* >& path, const Common::Array< float >& cost )
+{
+	if ( nItems + (int)path.size() > allocated*3/4 ) {
+		return;
+	}
+
+	for( unsigned i=0; i<path.size()-1; ++i ) {
+		// example: a->b->c->d
+		// Huge memory saving to only store 3 paths to 'd'
+		// Can put more in cache with also adding path to b, c, & d
+		// But uses much more memory. Experiment with this commented
+		// in and out and how to set.
+
+		void* end   = path[path.size()-1];
+		Item item = { path[i], end, path[i+1], cost[i] };
+		AddItem( item );
+	}
+}
+
+
+void PathCache::AddNoSolution( void* end, void* states[], int count )
+{
+	if ( count + nItems > allocated*3/4 ) {
+		return;
+	}
+
+	for( int i=0; i<count; ++i ) {
+		Item item = { states[i], end, 0, FLT_MAX };
+		AddItem( item );
+	}
+}
+
+
+int PathCache::Solve( void* start, void* end, Common::Array< void* >* path, float* totalCost )
+{
+	const Item* item = Find( start, end );
+	if ( item ) {
+		if ( item->cost == FLT_MAX ) {
+			++hit;
+			return MicroPather::NO_SOLUTION;
+		}
+
+		path->clear();
+		path->push_back( start );
+		*totalCost = 0;
+
+		for ( ;start != end; start=item->next, item=Find(start, end) ) {
+			MPASSERT( item );
+			*totalCost += item->cost;
+			path->push_back( item->next );
+		}
+		++hit;
+		return MicroPather::SOLVED;
+	}
+	++miss;
+	return MicroPather::NOT_CACHED;
+}
+
+
+void PathCache::AddItem( const Item& item )
+{
+	MPASSERT( allocated );
+	int index = item.Hash() % allocated;
+	while( true ) {
+		if ( mem[index].Empty() ) {
+			mem[index] = item;
+			++nItems;
+#ifdef DEBUG_CACHING
+			GLOUTPUT(( "Add: start=%x next=%x end=%x\n", item.start, item.next, item.end ));
+#endif
+			break;
+		}
+		else if ( mem[index].KeyEqual( item ) ) {
+			MPASSERT( (mem[index].next && item.next) || (mem[index].next==0 && item.next == 0) );
+			// do nothing; in cache
+			break;
+		}
+		++index;
+		if ( index == allocated )
+			index = 0;
+	}
+}
+
+
+const PathCache::Item* PathCache::Find( void* start, void* end )
+{
+	MPASSERT( allocated );
+	Item fake = { start, end, 0, 0 };
+	int index = fake.Hash() % allocated;
+	while( true ) {
+		if ( mem[index].Empty() ) {
+			return 0;
+		}
+		if ( mem[index].KeyEqual( fake )) {
+			return mem + index;
+		}
+		++index;
+		if ( index == allocated )
+			index = 0;
+	}
+}
+
+
+void MicroPather::GetCacheData( CacheData* data )
+{
+	memset( data, 0, sizeof(*data) );
+
+	if ( pathCache ) {
+		data->nBytesAllocated = pathCache->AllocatedBytes();
+		data->nBytesUsed = pathCache->UsedBytes();
+		data->memoryFraction = (float)( (double)data->nBytesUsed / (double)data->nBytesAllocated );
+
+		data->hit = pathCache->hit;


Commit: d07187a1a445806042937be1cc4145ae5c9c3c1b
    https://github.com/scummvm/scummvm/commit/d07187a1a445806042937be1cc4145ae5c9c3c1b
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More progress on WIP engine.

Changed paths:
  A engines/tetraedge/game/scene_lights_xml_parser.cpp
  A engines/tetraedge/game/scene_lights_xml_parser.h
  A engines/tetraedge/te/te_act_zone.cpp
  A engines/tetraedge/te/te_act_zone.h
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/application.h
    engines/tetraedge/game/billboard.cpp
    engines/tetraedge/game/billboard.h
    engines/tetraedge/game/cellphone.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/characters_shadow.cpp
    engines/tetraedge/game/characters_shadow.h
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/gallery_menu.cpp
    engines/tetraedge/game/gallery_menu.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/global_bonus_menu.cpp
    engines/tetraedge/game/help_option_menu.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory_menu.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/main_menu.h
    engines/tetraedge/game/notifier.cpp
    engines/tetraedge/game/notifier.h
    engines/tetraedge/game/object_settings_xml_parser.h
    engines/tetraedge/game/objectif.cpp
    engines/tetraedge/game/question2.h
    engines/tetraedge/module.mk
    engines/tetraedge/te/micropather.h
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_3d_object2.h
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture.h
    engines/tetraedge/te/te_animation.cpp
    engines/tetraedge/te/te_animation.h
    engines/tetraedge/te/te_bezier_curve.cpp
    engines/tetraedge/te/te_bezier_curve.h
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_callback.h
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_camera.h
    engines/tetraedge/te/te_checkbox_layout.cpp
    engines/tetraedge/te/te_checkbox_layout.h
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_i_3d_object2.h
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_image.h
    engines/tetraedge/te/te_intrusive_ptr.h
    engines/tetraedge/te/te_layout.cpp
    engines/tetraedge/te/te_light.cpp
    engines/tetraedge/te/te_light.h
    engines/tetraedge/te/te_lua_gui.cpp
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_matrix4x4.cpp
    engines/tetraedge/te/te_matrix4x4.h
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_mesh.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_model_animation.h
    engines/tetraedge/te/te_model_vertex_animation.cpp
    engines/tetraedge/te/te_model_vertex_animation.h
    engines/tetraedge/te/te_obp.cpp
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_pick_mesh2.h
    engines/tetraedge/te/te_scene.cpp
    engines/tetraedge/te/te_scene.h
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_vector3f32.cpp
    engines/tetraedge/te/te_vector3f32.h
    engines/tetraedge/te/te_visual_fade.cpp
    engines/tetraedge/to_lua.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index fbb337d8b1d..9345f2d11af 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -29,6 +29,8 @@
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/application.h"
+#include "tetraedge/game/characters_shadow.h"
+#include "tetraedge/game/in_game_scene.h"
 #include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_resource_manager.h"
 #include "tetraedge/te/te_renderer.h"
@@ -380,11 +382,12 @@ void Application::performRender() {
 	Game *game = g_engine->getGame();
 	TeRenderer *renderer = g_engine->getRenderer();
 
-	if (game->running() && _inGameScene._character && true /*some other ingamescene things*/) {
+	if (game->running() && game->scene()._character
+			&& game->scene()._shadowLightNo != -1
+			&& game->scene()._charactersShadow != nullptr) {
 		renderer->shadowMode(TeRenderer::ShadowMode1);
-		//_inGameScene._charactersShadow->createTexture(_inGameScene);
+		game->scene()._charactersShadow->createTexture(&game->scene());
 		renderer->shadowMode(TeRenderer::ShadowMode0);
-		error("TODO: Implement characters shadow thing here.");
 	}
 
 	drawBack();
@@ -392,14 +395,17 @@ void Application::performRender() {
 	renderer->renderTransparentMeshes();
 	renderer->clearBuffer(GL_ACCUM);
 
-	if (game->running() && _inGameScene._character && true /*some other ingamescene things*/) {
-		TeIntrusivePtr<TeCamera> currentCamera = _inGameScene.currentCamera();
-		if (currentCamera) {
-			currentCamera->apply();
-			renderer->shadowMode(TeRenderer::ShadowMode2);
-			//_inGameScene._charactersShadow->draw(_inGameScene);
-			renderer->shadowMode(TeRenderer::ShadowMode0);
-			error("TODO: Implement characters shadow thing here.");
+	if (game->running()) {
+		if (game->scene()._character
+			&& game->scene()._shadowLightNo != -1
+			&& game->scene()._charactersShadow != nullptr) {
+			TeIntrusivePtr<TeCamera> currentCamera = game->scene().currentCamera();
+			if (currentCamera) {
+				currentCamera->apply();
+				renderer->shadowMode(TeRenderer::ShadowMode2);
+				game->scene()._charactersShadow->createTexture(&game->scene());
+				renderer->shadowMode(TeRenderer::ShadowMode0);
+			}
 		}
 		game->draw();
 	}
@@ -409,7 +415,7 @@ void Application::performRender() {
 	drawFront();
 	renderer->renderTransparentMeshes();
 	// What gets called here??
-	//_inGameScene.removeModel(const Common::String &name)
+	game->scene().drawPath();
 	g_system->updateScreen();
 }
 
@@ -463,16 +469,20 @@ bool Application::isLockCursor() {
 }
 
 bool Application::isLockPad() {
-	error("TODO: Implement Application::isLockPad");
-	return false;
+	Game *game = g_engine->getGame();
+	bool result = isLockCursor() || game->dialog2().isDialogPlaying() || game->isMoviePlaying()
+		|| game->question2().gui().layoutChecked("background")->visible()
+		|| game->isDocumentOpened();
+	return result;
 }
 
 void Application::lockCursor(bool lock) {
-	error("TODO: Implement Application::lockCursor");
+	_lockCursorButton.setVisible(lock);
 }
 
 void Application::lockCursorFromAction(bool lock) {
-	error("TODO: Implement Application::lockCursorFromAction");
+	_lockCursorFromActionButton.setVisible(lock);
+	g_engine->getGame()->showMarkers(lock);
 }
 
 void Application::loadOptions(const Common::String &fname) {
diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
index feebf411639..7ffb34901eb 100644
--- a/engines/tetraedge/game/application.h
+++ b/engines/tetraedge/game/application.h
@@ -140,7 +140,6 @@ private:
 	Credits _credits;
 	OwnerErrorMenu _ownerErrorMenu;
 	SplashScreens _splashScreens;
-	InGameScene _inGameScene;
 
 	TeIntrusivePtr<TeFont3> _fontComic;
 	TeIntrusivePtr<TeFont3> _fontArgh;
diff --git a/engines/tetraedge/game/billboard.cpp b/engines/tetraedge/game/billboard.cpp
index e8529237fe5..eef7974e0b1 100644
--- a/engines/tetraedge/game/billboard.cpp
+++ b/engines/tetraedge/game/billboard.cpp
@@ -20,7 +20,10 @@
  */
 
 #include "common/textconsole.h"
+
+#include "tetraedge/tetraedge.h"
 #include "tetraedge/game/billboard.h"
+#include "tetraedge/game/game.h"
 
 namespace Tetraedge {
 
@@ -28,11 +31,21 @@ Billboard::Billboard() {
 }
 
 bool Billboard::load(const Common::String &path) {
-	error("TODO: implement Billboard::load");
+	_model = new TeModel();
+	TeIntrusivePtr<Te3DTexture> texture = new Te3DTexture();
+	Game *game = g_engine->getGame();
+	Common::Path texpath = game->sceneZonePath().join(path);
+	texture->load(texpath);
+	_model->setName(texpath.toString());
+	Common::Array<TeVector3f32> quad;
+	quad.resize(4);
+	_model->setQuad(texture, quad, TeColor(0xff, 0xff, 0xff, 0xff));
+	game->scene().models().push_back(_model);
 	return false;
 }
 
 void Billboard::calcVertex() {
+	//Game *game = g_engine->getGame();
 	error("TODO: implement Billboard::calcVertex");
 }
 
diff --git a/engines/tetraedge/game/billboard.h b/engines/tetraedge/game/billboard.h
index bd39e309de0..4fc376fac99 100644
--- a/engines/tetraedge/game/billboard.h
+++ b/engines/tetraedge/game/billboard.h
@@ -25,6 +25,8 @@
 #include "common/str.h"
 
 #include "tetraedge/te/te_object.h"
+#include "tetraedge/te/te_intrusive_ptr.h"
+#include "tetraedge/te/te_model.h"
 #include "tetraedge/te/te_vector2f32.h"
 #include "tetraedge/te/te_vector3f32.h"
 
@@ -42,6 +44,7 @@ public:
 	void size(const TeVector2f32 &size);
 
 private:
+	TeIntrusivePtr<TeModel> _model;
 	TeVector3f32 _pos;
 	TeVector3f32 _pos2;
 	TeVector2f32 _size;
diff --git a/engines/tetraedge/game/cellphone.cpp b/engines/tetraedge/game/cellphone.cpp
index 4f836c1c473..0228fd3a17d 100644
--- a/engines/tetraedge/game/cellphone.cpp
+++ b/engines/tetraedge/game/cellphone.cpp
@@ -61,15 +61,28 @@ bool Cellphone::addNumber(const Common::String &num) {
 }
 
 void Cellphone::currentPage(int offset) {
-	error("TODO: implement Cellphone::currentPage");
+	if (_textLayoutArray.empty())
+		return;
+
+	_nextNumber = offset;
+	TeLayout *repertoire = _gui.layoutChecked("numRepertoire");
+	for (int i = 0; i < repertoire->childCount(); i++) {
+		repertoire->child(i)->setVisible(i == offset);
+	}
 }
 
 void Cellphone::enter() {
-	error("TODO: implement Cellphone::enter");
+	_gui.buttonLayoutChecked("background")->setVisible(true);
+	currentPage(_nextNumber);
 }
 
 void Cellphone::leave() {
-	error("TODO: implement Cellphone::leave");
+	_gui.buttonLayoutChecked("background")->setVisible(false);
+	for (TeTextLayout *text : _textLayoutArray) {
+		text->deleteLater();
+	}
+	_textLayoutArray.clear();
+	_addedNumbers.clear();
 }
 
 void Cellphone::load() {
@@ -89,6 +102,7 @@ void Cellphone::load() {
 }
 
 void Cellphone::loadFromBackup(const Common::XMLParser::ParserNode *node) {
+	error("TODO: implement Cellphone::loadFromBackup");
 	/*
 	 basic algorithm:
 	child = node->lastChild;
@@ -113,12 +127,10 @@ bool Cellphone::onCloseButtonValidated() {
 }
 
 bool Cellphone::onNextNumber() {
-	error("TODO: work out how the max num works here.");
-	/*
-	int numoffset = _nextNumber + 1;
-	if (numoffset < _maxnum) {
+	unsigned int numoffset = _nextNumber + 1;
+	if (numoffset < _textLayoutArray.size()) {
 		currentPage(numoffset);
-	}*/
+	}
 	return false;
 }
 
@@ -142,4 +154,5 @@ void Cellphone::unload() {
 	leave();
 	_gui.unload();
 }
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index aa05583d4f5..9aabbf0220c 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -57,7 +57,8 @@ void Character::WalkSettings::clear() {
 Character::Character() : _curveOffset(0), _lastFrame(-1), _callbacksChanged(false),
 _missingCurrentAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"), _needsSomeUpdate(false),
 _stepSound1("sounds/SFX/PAS_H_BOIS1.ogg"), _stepSound2("sounds/SFX/PAS_H_BOIS2.ogg"),
-_freeMoveZone(nullptr) {
+_freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(nullptr) {
+	_curModelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
 }
 
 void Character::addCallback(const Common::String &key, const Common::String &s2, float f1, float f2) {
@@ -127,12 +128,37 @@ float Character::animLengthFromFile(const Common::String &animname, uint *pframe
 	return animLen * _model->scale().z();
 }
 
-bool Character::blendAnimation(const Common::String &animname, float param_2, bool param_3, bool param_4) {
-	error("TODO: Implement Character::blendAnimation");
+bool Character::blendAnimation(const Common::String &animname, float amount, bool repeat, bool param_4) {
+	Common::Path animpath("models/Anims");
+	animpath.joinInPlace(animname);
+
+	_missingCurrentAnim = !(animname.contains(_characterSettings._walkFileName)
+			|| animname.contains(walkAnim(WalkPart_Start))
+			|| animname.contains(walkAnim(WalkPart_Loop))
+			|| animname.contains(walkAnim(WalkPart_EndG))
+			|| animname.contains(walkAnim(WalkPart_EndD)));
+
+	if (_curModelAnim)
+		_curModelAnim->onFinished().remove(this, &Character::onModelAnimationFinished);
+
+	_curModelAnim = animCacheLoad(animpath);
+	_curModelAnim->onFinished().add(this, &Character::onModelAnimationFinished);
+
+	_curModelAnim->bind(_model);
+	_model->blendAnim(_curModelAnim, amount, repeat);
+	_lastFrame = -1;
+	_curModelAnim->play();
+	_curAnimName = animname;
+	warning("TODO: Set field 0x2d1 in Character::blendAnimation");
+	return true;
 }
 
 TeVector3f32 Character::correctPosition(const TeVector3f32 &pos) {
-	error("TODO: Implement Character::correctPosition");
+	bool flag;
+	TeVector3f32 result = _freeMoveZone->correctCharacterPosition(pos, &flag, true);
+	if (!flag)
+		result = _model->position();
+	return result;
 }
 
 float Character::curveOffset() {
@@ -150,7 +176,12 @@ void Character::deleteAllCallback() {
 }
 
 void Character::deleteAnim() {
-	error("TODO: Implement Character::deleteAnim");
+	if (_curModelAnim) {
+		_curModelAnim->onFinished().remove(this, &Character::onModelAnimationFinished);
+		_curModelAnim->unbind();
+	}
+	_model->removeAnim();
+	_curModelAnim.release();
 }
 
 void Character::deleteCallback(const Common::String &str1, const Common::String &str2, float f) {
@@ -159,7 +190,11 @@ void Character::deleteCallback(const Common::String &str1, const Common::String
 
 //static bool deserialize(TiXmlElement *param_1, Walk *param_2);
 void Character::endMove() {
-	error("TODO: Implement Character::endMove.");
+	if (_model->name() == "Kate")
+		walkMode("Walk");
+
+	_onFinishedSignal.call();
+	stop();
 }
 
 const Character::WalkSettings *Character::getCurrentWalkFiles() {
@@ -170,23 +205,34 @@ const Character::WalkSettings *Character::getCurrentWalkFiles() {
 	return nullptr;
 }
 
-bool Character::isFramePassed(uint frameno) {
-	error("TODO: Implement Character::isFramePassed.");
+bool Character::isFramePassed(int frameno) {
+	return (frameno > _lastAnimFrame && _model->anim()->curFrame2() >= frameno);
 }
 
 bool Character::isWalkEnd() {
-	error("TODO: Implement Character::isWalkEnd.");
+	Common::String animFile = _model->anim()->_loadedPath.getLastComponent().toString();
+	for (const auto & walkSettings : _characterSettings._walkSettings) {
+		if (walkSettings._value._walkParts[WalkPart_EndD]._file.contains(animFile)
+				|| walkSettings._value._walkParts[WalkPart_EndG]._file.contains(animFile))
+			return true;
+	}
 	return false;
 }
 
-bool Character::leftStepFrame(enum Character::WalkPart walkpart) {
-	error("TODO: Implement Character::leftStepFrame.");
-	return false;
+int Character::leftStepFrame(enum Character::WalkPart walkpart) {
+	const Character::WalkSettings *settings = getCurrentWalkFiles();
+	if (settings) {
+		return settings->_walkParts[(int)walkpart]._stepLeft;
+	}
+	return -1;
 }
 
-bool Character::rightStepFrame(enum Character::WalkPart walkpart) {
-	error("TODO: Implement Character::rightStepFrame.");
-	return false;
+int Character::rightStepFrame(enum Character::WalkPart walkpart) {
+	const Character::WalkSettings *settings = getCurrentWalkFiles();
+	if (settings) {
+		return settings->_walkParts[(int)walkpart]._stepRight;
+	}
+	return -1;
 }
 
 bool Character::loadModel(const Common::String &name, bool param_2) {
@@ -290,7 +336,7 @@ bool Character::loadModel(const Common::String &name, bool param_2) {
 	return false;
 }
 
-bool Character::onBonesUpdate(const Common::String &param_1, const TeMatrix4x4 *param_2) {
+bool Character::onBonesUpdate(const Common::String &boneName, const TeMatrix4x4 &param_2) {
 	error("TODO: Implement Character::onBonesUpdate");
 	return false;
 }
@@ -304,7 +350,7 @@ void Character::permanentUpdate() {
 	error("TODO: Implement Character::permanentUpdate.");
 }
 
-void Character::placeOnCurve(const TeBezierCurve &curve) {
+void Character::placeOnCurve(TeIntrusivePtr<TeBezierCurve> &curve) {
 	_curve = curve;
 	updatePosition(_curveOffset);
 }
@@ -320,10 +366,10 @@ void Character::removeAnim() {
 }
 
 void Character::removeFromCurve() {
-	error("TODO: Implement Character::removeFromCurve.");
+	_curve.release();
 }
 
-bool Character::setAnimation(const Common::String &name, bool repeat, bool param_3, bool unused, int startFrame, int endFrame)  {
+bool Character::setAnimation(const Common::String &name, bool repeat, bool param_3, bool unused, int startFrame, int endFrame) {
 	if (name.empty())
 		return false;
 
@@ -354,8 +400,10 @@ bool Character::setAnimation(const Common::String &name, bool repeat, bool param
 	return true;
 }
 
-void Character::setAnimationSound(const Common::String &name, uint param_2)  {
-	error("TODO: Implement Character::setAnimationSound.");
+void Character::setAnimationSound(const Common::String &name, uint offset) {
+	warning("TODO: Set field 0x2f8 to 0 in Character::setAnimationSound.");
+	_animSound = name;
+	_animSoundOffset = offset;
 }
 
 void Character::setCurveOffset(float offset) {
@@ -363,7 +411,7 @@ void Character::setCurveOffset(float offset) {
 	updatePosition(offset);
 }
 
-void Character::setFreeMoveZone(const Common::SharedPtr<TeFreeMoveZone> &zone) {
+void Character::setFreeMoveZone(TeFreeMoveZone *zone) {
 	_freeMoveZone = zone;
 }
 
@@ -387,23 +435,26 @@ float Character::translationFromAnim(const TeModelAnimation &anim, long bone, lo
 	return translationVectorFromAnim(anim, bone, param_3).z();
 }
 
-TeVector3f32 Character::translationVectorFromAnim(const TeModelAnimation &anim, long bone, long frame)  {
+TeVector3f32 Character::translationVectorFromAnim(const TeModelAnimation &anim, long bone, long frame) {
 	const TeTRS trs = trsFromAnim(anim, bone, frame);
 	return trs.getTranslation();
 }
 
-TeTRS Character::trsFromAnim(const TeModelAnimation &anim, long bone, long frame)  {
+TeTRS Character::trsFromAnim(const TeModelAnimation &anim, long bone, long frame) {
 	if (bone == -1)
 		return TeTRS();
 
 	return anim.getTRS(bone, frame, false);
 }
 
-void Character::update(double percentval)  {
+void Character::update(double percentval) {
 	error("TODO: Implement Character::update");
 }
 
-void Character::updateAnimFrame()  {
+void Character::updateAnimFrame() {
+	if (_model->anim()) {
+		_lastAnimFrame = _model->anim()->curFrame2();
+	}
 	error("TODO: Implement Character::updateAnimFrame");
 }
 
@@ -411,7 +462,7 @@ void Character::updatePosition(float curveOffset) {
 	error("TODO: Implement Character::updatePosition");
 }
 
-Common::String Character::walkAnim(Character::WalkPart part)  {
+Common::String Character::walkAnim(Character::WalkPart part) {
 	Common::String result;
 	const Character::WalkSettings *settings = getCurrentWalkFiles();
 	if (settings) {
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 2556f305845..dd0d9b13329 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -45,8 +45,8 @@ public:
 	struct AnimSettings {
 		AnimSettings() : _stepLeft(0), _stepRight(0) {};
 		Common::String _file;
-		int _stepRight;
 		int _stepLeft;
+		int _stepRight;
 	};
 
 	struct WalkSettings {
@@ -100,7 +100,7 @@ public:
 
 	float animLength(const TeModelAnimation &modelanim, long bone, long lastframe);
 	float animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe);
-	bool blendAnimation(const Common::String &animname, float param_2, bool param_3, bool param_4);
+	bool blendAnimation(const Common::String &animname, float amount, bool repeat, bool param_4);
 	TeVector3f32 correctPosition(const TeVector3f32 &pos);
 	float curveOffset();
 	void deleteAllCallback();
@@ -110,26 +110,26 @@ public:
 	void endMove();
 
 	const WalkSettings *getCurrentWalkFiles();
-	bool isFramePassed(uint frameno);
+	bool isFramePassed(int frameno);
 	bool isWalkEnd();
-	bool leftStepFrame(enum WalkPart walkpart);
-	bool rightStepFrame(enum WalkPart walkpart);
+	int leftStepFrame(enum WalkPart walkpart);
+	int rightStepFrame(enum WalkPart walkpart);
 	bool loadModel(const Common::String &name, bool param_2);
 	static bool loadSettings(const Common::String &path);
 
-	bool onBonesUpdate(const Common::String &param_1, const TeMatrix4x4 *param_2);
+	bool onBonesUpdate(const Common::String &boneName, const TeMatrix4x4 &param_2);
 	bool onModelAnimationFinished();
 	void permanentUpdate();
-	void placeOnCurve(const TeBezierCurve &curve);
+	void placeOnCurve(TeIntrusivePtr<TeBezierCurve> &curve);
 	//void play() // just called TeAnimation::play();
 	void removeAnim();
 	void removeFromCurve();
 	static Common::String rootBone() { return "Pere"; }
 
 	bool setAnimation(const Common::String &name, bool repeat, bool param_3, bool param_4, int startFrame, int endFrame);
-	void setAnimationSound(const Common::String &name, uint param_2);
+	void setAnimationSound(const Common::String &name, uint offset);
 	void setCurveOffset(float offset);
-	void setFreeMoveZone(const Common::SharedPtr<TeFreeMoveZone> &zone);
+	void setFreeMoveZone(TeFreeMoveZone *zone);
 	bool setShadowVisible(bool visible);
 	void setStepSound(const Common::String &stepSound1, const Common::String &stepSound2);
 	float speedFromAnim(double movepercent);
@@ -153,14 +153,20 @@ public:
 	const Common::String walkModeStr() const { return _walkModeStr; }
 	const Common::String curAnimName() const { return _curAnimName; }
 	bool needsSomeUpdate() const { return _needsSomeUpdate; }
+	void setCharLookingAt(Character *other) { _charLookingAt = other; }
 
 private:
 	float _curveOffset;
-	TeBezierCurve _curve;
-	Common::SharedPtr<TeFreeMoveZone> _freeMoveZone;
+	TeIntrusivePtr<TeBezierCurve> _curve;
+	TeFreeMoveZone *_freeMoveZone;
 	Common::String _stepSound1;
 	Common::String _stepSound2;
 	Common::String _walkModeStr; // Walk or Jog
+	Common::String _animSound;
+
+	Character *_charLookingAt;
+
+	uint _animSoundOffset;
 
 	TeIntrusivePtr<TeModelAnimation> _curModelAnim;
 
@@ -175,6 +181,7 @@ private:
 	uint32 _walkPart3AnimFrameCount;
 
 	int _lastFrame;
+	int _lastAnimFrame;
 	bool _missingCurrentAnim;
 	bool _someRepeatFlag; // TODO: what is this?
 	bool _callbacksChanged;
diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
index 46b1abefd64..2ed2fba9c5b 100644
--- a/engines/tetraedge/game/characters_shadow.cpp
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -19,33 +19,64 @@
  *
  */
 
+#include "graphics/opengl/system_headers.h"
+
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/characters_shadow.h"
+#include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_3d_texture.h"
 
 namespace Tetraedge {
 
+/*static*/
+Te3DObject2 *CharactersShadow::_camTarget = nullptr;
+
 CharactersShadow::CharactersShadow() {
 }
 
 void CharactersShadow::create(InGameScene *scene) {
-	error("TODO: Implement me");
+	_texSize = 720;
+	_camTarget = new Te3DObject2();
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->enableTexture();
+	_camera = new TeCamera();
+	_camera->_projectionMatrixType = 2;
+	// TODO: set camera field 0x130 to 1.0?
+	_camera->viewport(0, 0, _texSize, _texSize);
+	Te3DTexture::unbind();
+	glGenTextures(1, &_glTex);
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, _texSize, _texSize, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, nullptr);
+	renderer->disableTexture();
 }
 
 void CharactersShadow::createTexture(InGameScene *scene) {
-	error("TODO: Implement me");
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->enableTexture();
+	//TeLight *light = scene->shadowLight();
+	error("TODO: Implement CharactersShadow::createTexture");
 }
 
 void CharactersShadow::destroy() {
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->disableTexture();
-	//glBindTexture(GL_TEXTURE_2D, 0);
-	//glDeleteTextures(1, (uint *)this);
-	error("TODO: Finish implementation here");
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glDeleteTextures(1, &_glTex);
+	if (_camera)
+		_camera = nullptr;
+	if (_camTarget) {
+		delete _camTarget;
+		_camTarget = nullptr;
+	}
 }
 
 void CharactersShadow::draw(InGameScene *scene) {
-	error("TODO: Implement me");
+	error("TODO: Implement CharactersShadow::draw");
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/characters_shadow.h b/engines/tetraedge/game/characters_shadow.h
index a6809f6414a..e0b8c51efe7 100644
--- a/engines/tetraedge/game/characters_shadow.h
+++ b/engines/tetraedge/game/characters_shadow.h
@@ -26,6 +26,8 @@
 
 namespace Tetraedge {
 
+class InGameScene;
+
 class CharactersShadow {
 public:
 	CharactersShadow();
@@ -37,8 +39,10 @@ public:
 	//void drawTexture(); // empty?
 
 private:
-	// TODO add private members
-
+	uint _glTex;
+	int _texSize;
+	TeIntrusivePtr<TeCamera> _camera;
+	static Te3DObject2 *_camTarget;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 6be1ece06e5..1d46d18f5b0 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -140,6 +140,7 @@ void Dialog2::unload() {
 	_music.close();
 	_gui.unload();
 	error("TODO: Finish Dialog2::unload");
+
 	//_dialogDataList.clear();
 	//_minimumTimeTimer.stop();
 }
diff --git a/engines/tetraedge/game/gallery_menu.cpp b/engines/tetraedge/game/gallery_menu.cpp
index 935443f1c8b..7403f62ec18 100644
--- a/engines/tetraedge/game/gallery_menu.cpp
+++ b/engines/tetraedge/game/gallery_menu.cpp
@@ -21,10 +21,14 @@
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/application.h"
+#include "tetraedge/game/game.h"
 #include "tetraedge/game/gallery_menu.h"
 
 namespace Tetraedge {
 
+static const char *AMBIENT_SND_BIKE = "sounds/Ambiances/b_automatebike.ogg";
+static const char *AMBIENT_SND_ENGR = "sounds/Ambiances/b_engrenagebg.ogg";
+
 GalleryMenu::GalleryMenu() {
 }
 
@@ -34,7 +38,22 @@ bool GalleryMenu::onLockVideoButtonValidated() {
 }
 
 bool GalleryMenu::onSkipVideoButtonValidated() {
-	error("TODO: Implement onSkipVideoButtonValidated");
+	Application *app = g_engine->getApplication();
+	app->music().play();
+	Game *game = g_engine->getGame();
+
+	game->stopSound(AMBIENT_SND_BIKE);
+	game->playSound(AMBIENT_SND_BIKE, -1, 0.1);
+
+	game->stopSound(AMBIENT_SND_ENGR);
+	game->playSound(AMBIENT_SND_ENGR, -1, 0.09);
+
+	TeSpriteLayout *video = spriteLayoutChecked("video");
+	video->stop();
+	video->setVisible(false);
+	buttonLayoutChecked("videoBackgroundButton")->setVisible(false);
+	buttonLayoutChecked("skipVideoButton")->setVisible(false);
+	_music.stop();
 	return false;
 }
 
@@ -59,11 +78,44 @@ bool GalleryMenu::onVideoFinished() {
 }
 
 void GalleryMenu::enter() {
-	error("TODO: implement GalleryMenu::enter");
+	Application *app = g_engine->getApplication();
+	Game *game = g_engine->getGame();
+
+	load("menus/galleryMenu/galleryMenu.lua");
+	TeLayout *menu = layoutChecked("galleryMenu");
+	app->_frontLayout.addChild(menu);
+
+	game->stopSound(AMBIENT_SND_BIKE);
+	game->playSound(AMBIENT_SND_BIKE, -1, 0.1);
+
+	game->stopSound(AMBIENT_SND_ENGR);
+	game->playSound(AMBIENT_SND_ENGR, -1, 0.09);
+
+	TeButtonLayout *btn = buttonLayoutChecked("quitButton");
+	btn->onMouseClickValidated().add(this, &GalleryMenu::onQuitButton);
+
+	//TeLayout *list = layoutChecked("galleryList");
+
+	error("TODO: Finish GalleryMenu::enter");
 }
 
 void GalleryMenu::leave() {
-	error("TODO: implement GalleryMenu::leave");
+	if (!_loaded)
+		return;
+
+	Game *game = g_engine->getGame();
+	game->stopSound(AMBIENT_SND_BIKE);
+	game->stopSound(AMBIENT_SND_ENGR);
+	unload();
+	for (GalleryBtnObject *btn : _btnObjects) {
+		delete btn;
+	}
+	_btnObjects.clear();
+}
+
+bool GalleryMenu::GalleryBtnObject::onValidated() {
+	error("TODO: Implement GalleryMenu::GalleryBtnObject::onValidated");
 }
 
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/gallery_menu.h b/engines/tetraedge/game/gallery_menu.h
index 50dd39ee656..cb974f424b6 100644
--- a/engines/tetraedge/game/gallery_menu.h
+++ b/engines/tetraedge/game/gallery_menu.h
@@ -32,8 +32,12 @@ class GalleryMenu : public TeLuaGUI {
 public:
 	GalleryMenu();
 
-	class GalleryBtnObject {
-		bool OnValidated();
+	struct GalleryBtnObject {
+		bool onValidated();
+
+		Common::String _audioPath;
+		Common::String _moviePath;
+		GalleryMenu *_owner;
 	};
 
 	void enter();
@@ -43,6 +47,7 @@ public:
 	bool onLockVideoButtonValidated();
 	bool onSkipVideoButtonValidated();
 	bool onVideoFinished();
+	TeMusic &music();
 
 private:
 	TeMusic _music;
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 9d4ee36803f..427a56abbf4 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -40,7 +40,7 @@ namespace Tetraedge {
 
 Game::Game() : _objectsTakenVal(0), _score(0), _entered(false), _gameLoadState(0),
 _noScaleLayout(nullptr), _noScaleLayout2(nullptr), _warped(false), _saveRequested(false),
-_firstInventory(true) {
+_firstInventory(true), _movePlayerCharacterDisabled(false) {
 	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
 		_objectsTakenBits[i] = false;
 	}
@@ -183,11 +183,56 @@ bool Game::changeWarp(const Common::String &zone, const Common::String &scene, b
 }
 
 bool Game::changeWarp2(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
-	error("TODO: Implemet me");
+	_warped = false;
+	_movePlayerCharacterDisabled = false;
+	_sceneCharacterVisibleFromLoad = false;
+	// TODO: set 3 other fields here (0x3f40 = -1, 0x4249 = 1, 0x424b = 0)
+	Common::Path luapath("scenes");
+	luapath.joinInPlace(zone);
+	luapath.joinInPlace(scene);
+	luapath.joinInPlace("Logic");
+	luapath.appendInPlace(zone);
+	luapath.appendInPlace(".lua");
+
+	if (Common::File::exists(luapath)) {
+		_luaScript.execute("OnLeave");
+		_luaContext.removeGlobal("On");
+		_luaContext.removeGlobal("OnEnter");
+		_luaContext.removeGlobal("OnWarpObjectHit");
+		_luaContext.removeGlobal("OnButtonDown");
+		_luaContext.removeGlobal("OnButtonUp");
+		_luaContext.removeGlobal("OnFinishedAnim");
+		_luaContext.removeGlobal("OnCharacterAnimationFinished");
+		_luaContext.removeGlobal("OnCharacterAnimationPlayerFinished");
+		_luaContext.removeGlobal("OnDisplacementFinished");
+		_luaContext.removeGlobal("OnFreeSoundFinished");
+		_luaContext.removeGlobal("OnDocumentClosed");
+		_luaContext.removeGlobal("OnSelectedObject");
+		_luaContext.removeGlobal("OnDialogFinished");
+		_luaContext.removeGlobal("OnAnswered");
+		_luaContext.removeGlobal("OnLeave");
+		_luaScript.unload();
+	}
+
+	_gui3.unload();
+	_prevSceneName = _currentScene;
+	if (fadeFlag)
+		g_engine->getApplication()->fade();
+
+	return initWarp(zone, scene, false);
 }
 
 void Game::deleteNoScale() {
-	error("TODO: Implemet me");
+	if (_noScaleLayout) {
+		removeNoScaleChildren();
+		delete _noScaleLayout;
+		_noScaleLayout = nullptr;
+	}
+	if (_noScaleLayout2) {
+		removeNoScale2Children();
+		delete _noScaleLayout2;
+		_noScaleLayout2 = nullptr;
+	}
 }
 
 void Game::draw() {
@@ -206,6 +251,7 @@ void Game::enter(bool newgame) {
 	app->visualFade().init();
 	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, -1000.0f));
 	g_engine->getInputMgr()->_mouseLUpSignal.insert(callbackptr);
+	_movePlayerCharacterDisabled = false;
 	warning("TODO: Game::enter set some other fields here");
 	_sceneCharacterVisibleFromLoad = false;
 	Character::loadSettings("models/ModelsSettings.xml");
@@ -360,10 +406,10 @@ void Game::initScene(bool fade, const Common::String &scenePath) {
 	_scene._character->_model->setVisible(true);
 }
 
-void Game::initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
+bool Game::initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
 	_inventoryMenu.unload();
 	_gui4.unload();
-	warning("Game::initWarp: set field_0x4248 false here");
+	_movePlayerCharacterDisabled = false;
 	_sceneCharacterVisibleFromLoad = true;
 
 	if (_scene._character) {
@@ -401,10 +447,11 @@ void Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	if (!intLuaExists && !logicLuaExists && !setLuaExists && !forLuaExists && !markerLuaExists) {
 		debug("No lua scripts for scene %s zone %s", scene.c_str(), zone.c_str());
-		return;
+		return false;
 	}
 
-	warning("TODO: Game::initWarp: stop game sounds");
+	if (!_gameSounds.empty())
+		warning("TODO: Game::initWarp: stop game sounds");
 
 	if (logicLuaExists) {
 		_luaContext.addBindings(LuaBinds::LuaOpenBinds);
@@ -451,8 +498,9 @@ void Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	TeButtonLayout *vidbgbtn = _gui4.buttonLayout("videoBackgroundButton");
 	vidbgbtn->setVisible(false);
-	vidbgbtn->onMouseClickValidated().remove(this, &Game::onLockVideoButtonValidated);
-	vidbgbtn->onMouseClickValidated().add(this, &Game::onLockVideoButtonValidated);
+	/* TODO: Restore the original behavior here (onLockVideoButtonValidated) */
+	vidbgbtn->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
+	vidbgbtn->onMouseClickValidated().add(this, &Game::onSkipVideoButtonValidated);
 
 	TeSpriteLayout *video = _gui4.spriteLayout("video");
 	video->setVisible(false);
@@ -530,10 +578,14 @@ void Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_luaScript.execute("OnSelectedObject", _inventory.selectedObject());
 	}
 
-	//for (auto & sound : _gameSounds) {
-	warning("TODO: Game::initWarp: Do game sound stuff here");
+	if (!_gameSounds.empty()) {
+		//for (auto & sound : _gameSounds) {
+		warning("TODO: Game::initWarp: Do game sound stuff here");
+	}
+	// TODO: Also do random sound stuff here.
 
 	_scene.initScroll();
+	return true;
 }
 
 bool Game::isDocumentOpened() {
@@ -549,7 +601,7 @@ bool Game::isMoviePlaying() {
 }
 
 bool Game::launchDialog(const Common::String &param_1, uint param_2, const Common::String &param_3,
-				  const Common::String &param_4, float param_5)  {
+				  const Common::String &param_4, float param_5) {
 	error("TODO: Implemet Game::launchDialog");
 }
 
@@ -666,7 +718,7 @@ bool Game::onMarkersVisible(TeCheckboxLayout::State state) {
 	return false;
 }
 
-bool Game::onMouseClick(const Common::Point &pt)  {
+bool Game::onMouseClick(const Common::Point &pt) {
 	Application *app = g_engine->getApplication();
 
 	if (app->isFading())
@@ -832,6 +884,10 @@ void Game::playMovie(const Common::String &vidPath, const Common::String &musicP
 	music.play();
 	videoSpriteLayout->play();
 
+	// FIXME TODO!! Stop the movie and soundearly for testing.
+	videoSpriteLayout->_tiledSurfacePtr->_frameAnim._nbFrames = 10;
+	music.stop();
+
 	app->fade();
 }
 
@@ -950,7 +1006,7 @@ bool Game::unloadCharacters() {
 }
 
 bool Game::unloadPlayerCharacter(const Common::String &character) {
-	_scene.unloadPlayerCharacter(character);
+	_scene.unloadCharacter(character);
 	return true;
 }
 
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index 62906a4ee61..d65a9fc7d36 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -30,6 +30,7 @@
 #include "tetraedge/game/in_game_scene.h"
 #include "tetraedge/game/notifier.h"
 #include "tetraedge/game/cellphone.h"
+#include "tetraedge/game/game_sound.h"
 #include "tetraedge/game/objectif.h"
 #include "tetraedge/game/question2.h"
 #include "tetraedge/game/dialog2.h"
@@ -44,12 +45,16 @@ class Game {
 public:
 	Game();
 
-	class HitObject {
+	struct HitObject {
 		byte OnChangeWarp();
 		byte OnDown();
 		byte OnUp();
 		byte OnValidated();
 		//byte OnVisible(); empty never used?
+		
+		Common::String _name;
+		Game *_game;
+		TeButtonLayout *_button;
 	};
 
 	class RandomSound {
@@ -92,7 +97,7 @@ public:
 	void initLoadedBackupData();
 	void initNoScale();
 	void initScene(bool param_1, const Common::String &scenePath);
-	void initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag);
+	bool initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag);
 	bool isDocumentOpened();
 	bool isMouse() { return false; }
 	bool isMoviePlaying();
@@ -156,8 +161,11 @@ public:
 
 	const Common::String &currentZone() { return _currentZone; }
 	const Common::String &currentScene() { return _currentScene; }
+	const Common::Path &sceneZonePath() { return _sceneZonePath; }
 	TeLuaScript &luaScript() { return _luaScript; }
 	InGameScene &scene() { return _scene; }
+	Dialog2 &dialog2() { return _dialog2; }
+	Question2 &question2() { return _question2; }
 
 private:
 	bool _luaShowOwnerError;
@@ -194,6 +202,8 @@ private:
 
 	Common::String _loadName;
 
+	Common::Array<GameSound *> _gameSounds;
+
 	Common::HashMap<Common::String, bool> _unlockedArtwork;
 
 	int _gameLoadState;
@@ -219,6 +229,7 @@ private:
 	bool _sceneCharacterVisibleFromLoad;
 	bool _markersVisible;
 	bool _saveRequested;
+	bool _movePlayerCharacterDisabled;
 
 	TeLayout *_noScaleLayout;
 	TeLayout *_noScaleLayout2;
diff --git a/engines/tetraedge/game/global_bonus_menu.cpp b/engines/tetraedge/game/global_bonus_menu.cpp
index 610e79884a3..dfabd6a27dc 100644
--- a/engines/tetraedge/game/global_bonus_menu.cpp
+++ b/engines/tetraedge/game/global_bonus_menu.cpp
@@ -29,26 +29,48 @@ GlobalBonusMenu::GlobalBonusMenu() : _entered(false) {
 }
 
 void GlobalBonusMenu::enter() {
-	error("TODO: Finish implementing GlobalBonusMenu::enter");
-	//Application *app = g_engine->getApplication();
-	//todo: call some virtual function on a field in app
-	//app->captureFade();
-	//_entered = true;
-	//load("menus/bonusmenu/GlobalBonusMenu.lua");
-	//TeLayout *menu = layout("menu");
-	//if (menu) {
-	//   ...
-	//}
+	Application *app = g_engine->getApplication();
+	app->appSpriteLayout().setVisible(true);
+	app->captureFade();
+	_entered = true;
+	load("menus/bonusmenu/GlobalBonusMenu.lua");
+	TeLayout *menu = layoutChecked("menu");
+	app->_frontLayout.addChild(menu);
+
+	// Original checks each layout's existence
+	TeButtonLayout *btn;
+	btn = buttonLayoutChecked("Val");
+	if (btn)
+		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onValButtonValidated);
+	btn = buttonLayoutChecked("Bar");
+	if (btn)
+		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onBarButtonValidated);
+	btn = buttonLayoutChecked("Cit");
+	if (btn)
+		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onCitButtonValidated);
+	btn = buttonLayoutChecked("Ara");
+	if (btn)
+		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onAraButtonValidated);
+	btn = buttonLayoutChecked("Syb2");
+	if (btn)
+		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onSyb2ButtonValidated);
+	btn = buttonLayoutChecked("Syb3");
+	if (btn)
+		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onSyb3ButtonValidated);
+	btn = buttonLayoutChecked("Back");
+	if (btn)
+		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onQuitButton);
 }
 
 void GlobalBonusMenu::leave() {
-	if (_entered != 0) {
-		Application *app = g_engine->getApplication();
-		app->captureFade();
-		TeLuaGUI::unload();
-		app->fade();
-		_entered = false;
-	}
+	if (!_entered)
+		return;
+
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	TeLuaGUI::unload();
+	app->fade();
+	_entered = false;
 }
 
 bool GlobalBonusMenu::onSomeButtonValidated(const char *script) {
diff --git a/engines/tetraedge/game/help_option_menu.cpp b/engines/tetraedge/game/help_option_menu.cpp
index 0e17ec9c287..1c3cd972e1e 100644
--- a/engines/tetraedge/game/help_option_menu.cpp
+++ b/engines/tetraedge/game/help_option_menu.cpp
@@ -33,7 +33,10 @@ void HelpOptionMenu::enter() {
 		Application *app = g_engine->getApplication();
 		app->captureFade();
 		load("menus/helpoptionMenu/optionsMenu.lua");
-		error("TODO: finish implementation of HelpOptionMenu::enter");
+
+		TeLayout *menu = layoutChecked("menu");
+		app->appSpriteLayout().addChild(menu);
+		app->fade();
 	}
 }
 
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 92f4eb2f52c..9c88e98f9dc 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -29,29 +29,67 @@
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/in_game_scene.h"
 #include "tetraedge/game/character.h"
+#include "tetraedge/game/characters_shadow.h"
 #include "tetraedge/game/object3d.h"
+#include "tetraedge/game/scene_lights_xml_parser.h"
+
+#include "tetraedge/te/te_bezier_curve.h"
+#include "tetraedge/te/te_camera.h"
+#include "tetraedge/te/te_free_move_zone.h"
+#include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_renderer.h"
 
 namespace Tetraedge {
 
-InGameScene::InGameScene() : _character(nullptr) {
+InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr), _shadowLightNo(-1) {
+}
+
+void InGameScene::activateAnchorZone(const Common::String &name, bool val) {
+	for (AnchorZone *zone : _anchorZones) {
+		if (zone->_name == name)
+			zone->_activated = val;
+	}
 }
 
 void InGameScene::draw() {
-	error("TODO: implement InGameScene::draw");
+	TeScene::draw();
+
+	if (currentCameraIndex() >= (int)cameras().size())
+		return;
+
+	currentCamera()->apply();
+	TeLight::updateGlobal();
+	for (unsigned int i = 0; i < _lights.size(); i++)
+		_lights[i].update(i);
+
+	currentCamera()->restore();
+}
+
+void InGameScene::drawPath() {
+	if (currentCameraIndex() >= (int)cameras().size())
+		return;
+
+	currentCamera()->apply();
+	g_engine->getRenderer()->disableZBuffer();
+
+	warning("TODO: Do free move zones in InGameScene::drawPath");
+	//for (unsigned int i = 0; i < _freeMoveZones.size(); i++)
+	//	_freeMoveZones[i]->
+
+	g_engine->getRenderer()->enableZBuffer();
 }
 
+
 bool InGameScene::changeBackground(const Common::String &name) {
 	if (Common::File::exists(name)) {
-		TeSpriteLayout *spriteLayout = _bgGui.spriteLayout("root");
-		assert(spriteLayout);
-		spriteLayout->load(name);
+		_bgGui.spriteLayoutChecked("root")->load(name);
 		return true;
 	}
 	return false;
 }
 
-
-/*static*/ float InGameScene::angularDistance(float a1, float a2) {
+/*static*/
+float InGameScene::angularDistance(float a1, float a2) {
 	float result;
 
 	result = a2 - a1;
@@ -74,7 +112,7 @@ void InGameScene::close() {
 
 void InGameScene::reset() {
 	if (_character)
-		_character->setFreeMoveZone(Common::SharedPtr<TeFreeMoveZone>());
+		_character->setFreeMoveZone(nullptr);
 	freeSceneObjects();
 	_bgGui.unload();
 	unloadSpriteLayouts();
@@ -86,10 +124,9 @@ void InGameScene::deleteAllCallback() {
 	warning("TODO: implement InGameScene::deleteAllCallback");
 }
 
-
 void InGameScene::freeSceneObjects() {
 	if (_character) {
-		warning("TODO: InGameScene::freeSceneObjects: Set field on character here");
+		_character->setCharLookingAt(nullptr);
 		_character->deleteAllCallback();
 	}
 	if (_characters.size() == 1) {
@@ -136,10 +173,212 @@ Character *InGameScene::character(const Common::String &name) {
 	error("TODO: Implement InGameScene::character");
 }
 
+bool InGameScene::load(const Common::Path &path) {
+	_actZones.clear();
+	Common::File actzonefile;
+	if (actzonefile.open(getActZoneFileName())) {
+		if (Te3DObject2::loadAndCheckFourCC(actzonefile, "0TCA")) {
+			uint32 count = actzonefile.readUint32LE();
+			if (count > 1000000)
+				error("Improbable number of actzones %d", count);
+			_actZones.resize(count);
+			for (unsigned int i = 0; i < _actZones.size(); i++) {
+				_actZones[i].s1 = Te3DObject2::deserializeString(actzonefile);
+				_actZones[i].s2 = Te3DObject2::deserializeString(actzonefile);
+				for (int j = 0; j < 4; j++)
+					TeVector2f32::deserialize(actzonefile, _actZones[i].points[j]);
+				_actZones[i].flag1 = (actzonefile.readByte() != 0);
+				_actZones[i].flag2 = true;
+			}
+		}
+	}
+	if (!_lights.empty()) {
+		TeLight::disableAll();
+		for (unsigned int i = 0; i < _lights.size(); i++) {
+			_lights[i].disable(i);
+		}
+		_lights.clear();
+	}
+	_shadowLightNo = -1;
+
+	const Common::Path lightspath = getLightsFileName();
+	if (Common::File::exists(lightspath))
+		loadLights(lightspath);
+
+	if (!Common::File::exists(path))
+		return false;
+
+	TeScene::close();
+	_loadedPath = path;
+	Common::File scenefile;
+	if (!scenefile.open(path))
+		return false;
+
+	uint32 ncameras = scenefile.readUint32LE();
+	for (unsigned int i = 0; i < ncameras; i++) {
+		TeIntrusivePtr<TeCamera> cam = new TeCamera();
+		deserializeCam(scenefile, cam);
+		cameras().push_back(cam);
+	}
+
+	uint32 nobjects = scenefile.readUint32LE();
+	for (unsigned int i = 0; i < nobjects; i++) {
+		TeIntrusivePtr<TeModel> model = new TeModel();
+		const Common::String modelname = Te3DObject2::deserializeString(scenefile);
+		model->setName(modelname);
+		const Common::String objname = Te3DObject2::deserializeString(scenefile);
+		TePickMesh2 *pickmesh = new TePickMesh2();
+		deserializeModel(scenefile, model, pickmesh);
+		if (modelname.contains("Clic")) {
+			_hitObjects.push_back(model);
+			// TODO: double-check this, probably right?
+			model->setVisible(false);
+			model->setColor(TeColor(0, 0xff, 0, 0xff));
+			models().push_back(model);
+			pickmesh->setName(modelname);
+		} else {
+			delete pickmesh;
+			if (modelname.substr(0, 2) != "ZB") {
+				if (objname.empty()) {
+					warning("[InGameScene::load] Unknown type of object named : %s", modelname.c_str());
+				} else {
+					InGameScene::Object obj;
+					obj._name = objname;
+					obj._model = model;
+					_objects.push_back(obj);
+					model->setVisible(false);
+					models().push_back(model);
+				}
+			}
+		}
+	}
+
+	uint32 nfreemovezones = scenefile.readUint32LE();
+	for (unsigned int i = 0; i < nfreemovezones; i++) {
+		TeFreeMoveZone *zone = new TeFreeMoveZone();
+		TeFreeMoveZone::deserialize(scenefile, *zone, &_blockers, &_rectBlockers, &_actZones);
+
+	}
+
+	uint32 ncurves = scenefile.readUint32LE();
+	for (unsigned int i = 0; i < ncurves; i++) {
+		TeIntrusivePtr<TeBezierCurve> curve = new TeBezierCurve();
+		TeBezierCurve::deserialize(scenefile, *curve);
+		curve->setVisible(true);
+		_bezierCurves.push_back(curve);
+	}
+
+	uint32 ndummies = scenefile.readUint32LE();
+	for (unsigned int i = 0; i < ndummies; i++) {
+		InGameScene::Dummy dummy;
+		TeVector3f32 vec;
+		TeQuaternion rot;
+		dummy._name = Te3DObject2::deserializeString(scenefile);
+		TeVector3f32::deserialize(scenefile, vec);
+		dummy._position = vec;
+		TeQuaternion::deserialize(scenefile, rot);
+		dummy._rotation = rot;
+		TeVector3f32::deserialize(scenefile, vec);
+		dummy._scale = vec;
+		_dummies.push_back(dummy);
+	}
+
+	for (TeFreeMoveZone *zone : _freeMoveZones) {
+		convertPathToMesh(zone);
+	}
+	_charactersShadow = new CharactersShadow();
+	_charactersShadow->create(this);
+	onMainWindowSizeChanged();
+	return true;
+}
+
+void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
+	error("TODO: Implement InGameScene::convertPathToMesh");
+}
+
+void InGameScene::onMainWindowSizeChanged() {
+	error("TODO: Implement InGameScene::onMainWindowSizeChanged");
+}
+
+bool InGameScene::loadLights(const Common::Path &path) {
+	SceneLightsXmlParser parser;
+
+	parser.setLightArray(&_lights);
+
+	if (!parser.loadFile(path.toString()))
+		error("InGameScene::loadLights: Can't load %s", path.toString().c_str());
+	if (!parser.parse())
+		error("InGameScene::loadLights: Can't parse %s", path.toString().c_str());
+
+	_shadowColor = parser.getShadowColor();
+	_shadowLightNo = parser.getShadowLightNo();
+	_shadowFarPlane = parser.getShadowFarPlane();
+	_shadowNearPlane = parser.getShadowNearPlane();
+	_shadowFov = parser.getShadowFov();
+
+	return true;
+}
+
 bool InGameScene::loadCharacter(const Common::String &name) {
 	error("TODO: Implement InGameScene::loadCharacter");
 }
 
+void InGameScene::deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam) {
+	cam->_projectionMatrixType = 2;
+	cam->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
+	Te3DObject2::deserialize(stream, *cam);
+	cam->_focalLenMaybe = stream.readFloatLE();
+	cam->_somePerspectiveVal = stream.readFloatLE();
+	cam->_orthNearVal = stream.readFloatLE();
+	cam->_orthFarVal = 3000.0;
+}
+
+void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<TeModel> &model, TePickMesh2 *pickmesh) {
+	TeVector3f32 vec;
+	TeVector2f32 vec2;
+	TeQuaternion rot;
+	TeColor col;
+	TeMesh mesh;
+
+	TeVector3f32::deserialize(stream, vec);
+	model->setPosition(vec);
+	pickmesh->setPosition(vec);
+	TeQuaternion::deserialize(stream, rot);
+	model->setRotation(rot);
+	pickmesh->setRotation(rot);
+	TeVector3f32::deserialize(stream, vec);
+	model->setScale(vec);
+	pickmesh->setScale(vec);
+	uint32 indexcount = stream.readUint32LE();
+	uint32 vertexcount = stream.readUint32LE();
+
+	mesh.setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
+	for (unsigned int i = 0; i < indexcount; i++)
+		mesh.setIndex(i, stream.readUint32LE());
+	for (unsigned int i = 0; i < vertexcount; i++) {
+		TeVector3f32::deserialize(stream, vec);
+		mesh.setVertex(i, vec);
+	}
+	for (unsigned int i = 0; i < vertexcount; i++) {
+		TeVector3f32::deserialize(stream, vec);
+		mesh.setNormal(i, vec);
+	}
+	for (unsigned int i = 0; i < vertexcount; i++) {
+		TeVector2f32::deserialize(stream, vec2);
+		mesh.setTextureUV(i, vec2);
+	}
+	for (unsigned int i = 0; i < vertexcount; i++) {
+		col.deserialize(stream);
+		mesh.setColor(i, col);
+	}
+	pickmesh->setNbTriangles(indexcount / 3);
+	for (unsigned int i = 0; i < indexcount; i++) {
+		vec = mesh.vertex(mesh.index(i));
+		pickmesh->verticies().push_back(vec);
+	}
+	model->addMesh(mesh);
+}
+
 bool InGameScene::loadPlayerCharacter(const Common::String &name) {
 	if (_character == nullptr) {
 		_character = new Character();
@@ -162,10 +401,6 @@ bool InGameScene::loadPlayerCharacter(const Common::String &name) {
 	return true;
 }
 
-void InGameScene::unloadPlayerCharacter(const Common::String &name) {
-	error("TODO: Implement InGameScene::unloadPlayerCharacter %s", name.c_str());
-}
-
 void InGameScene::unloadCharacter(const Common::String &name) {
 	warning("TODO: Implement InGameScene::unloadCharacter %s", name.c_str());
 }
@@ -178,21 +413,62 @@ bool InGameScene::findKate() {
 	return false;
 }
 
-Common::Path InGameScene::getBlockersFileName() {
+static Common::Path _sceneFileNameBase() {
 	Game *game = g_engine->getGame();
 	Common::Path retval("scenes");
 	retval.joinInPlace(game->currentZone());
 	retval.joinInPlace(game->currentScene());
-	retval.joinInPlace("blockers.bin");
 	return retval;
 }
 
+Common::Path InGameScene::getLightsFileName() const {
+	return _sceneFileNameBase().joinInPlace("lights.xml");
+}
+
+Common::Path InGameScene::getActZoneFileName() const {
+	return _sceneFileNameBase().joinInPlace("actions.bin");
+}
+
+Common::Path InGameScene::getBlockersFileName() const {
+	return _sceneFileNameBase().joinInPlace("blockers.bin");
+}
+
 void InGameScene::loadBlockers() {
 	_blockers.clear();
 	_rectBlockers.clear();
 	const Common::Path blockersPath = getBlockersFileName();
-	if (Common::File::exists(blockersPath)) {
-		error("TODO: Implement InGameScene::loadBlockers");
+	if (!Common::File::exists(blockersPath))
+		return;
+
+	Common::File blockersfile;
+	if (!blockersfile.open(blockersPath)) {
+		warning("Couldn't open blockers file %s.", blockersPath.toString().c_str());
+		return;
+	}
+
+	bool hasHeader = Te3DObject2::loadAndCheckFourCC(blockersfile, "BLK0");
+	if (!hasHeader)
+		blockersfile.seek(0);
+
+	uint32 nblockers = blockersfile.readUint32LE();
+	_blockers.resize(nblockers);
+	for (unsigned int i = 0; i < nblockers; i++) {
+		_blockers[i]._s = Te3DObject2::deserializeString(blockersfile);
+		TeVector2f32::deserialize(blockersfile, _blockers[i]._pts[0]);
+		TeVector2f32::deserialize(blockersfile, _blockers[i]._pts[1]);
+		_blockers[i]._x = 1;
+	}
+
+	if (hasHeader) {
+		uint32 nrectblockers = blockersfile.readUint32LE();
+		_rectBlockers.resize(nrectblockers);
+		for (unsigned int i = 0; i < nrectblockers; i++) {
+			_rectBlockers[i]._s = Te3DObject2::deserializeString(blockersfile);
+			for (unsigned int j = 0; j < 4l; j++) {
+				TeVector2f32::deserialize(blockersfile, _rectBlockers[i]._pts[j]);
+			}
+			_rectBlockers[i]._x = 1;
+		}
 	}
 }
 
@@ -218,7 +494,16 @@ void InGameScene::loadBackground(const Common::Path &path) {
 }
 
 void InGameScene::loadInteractions(const Common::Path &path) {
-	error("TODO: Implement InGameScene::loadInteractions");
+	_gui3.load(path);
+	TeLayout *bgbackground = _bgGui.layoutChecked("background");
+	Game *game = g_engine->getGame();
+	TeSpriteLayout *root = game->findSpriteLayoutByName(bgbackground, "root");
+	TeLayout *background = _gui3.layoutChecked("background");
+	// TODO: For all TeButtonLayout childen of background, call
+	// setDoubleValidationProtectionEnabled(false)
+	// For now our button doesn't implement that.
+	background->setRatioMode(TeILayout::RATIO_MODE_NONE);
+	root->addChild(background);
 }
 
 void InGameScene::setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2) {
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index d0e73c94eeb..ec557257632 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -28,12 +28,19 @@
 
 #include "tetraedge/game/object3d.h"
 #include "tetraedge/game/billboard.h"
+
+#include "tetraedge/te/te_act_zone.h"
+#include "tetraedge/te/te_bezier_curve.h"
+#include "tetraedge/te/te_free_move_zone.h"
 #include "tetraedge/te/te_scene.h"
+#include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_lua_gui.h"
+#include "tetraedge/te/te_pick_mesh2.h"
 
 namespace Tetraedge {
 
 class Character;
+class CharactersShadow;
 class TeLayout;
 
 class InGameScene : public TeScene {
@@ -52,13 +59,27 @@ public:
 		Common::String _stepSound2;
 	};
 
-	class AnchorZone {
+	struct AnchorZone {
+		Common::String _name;
+		bool _activated;
+	};
+
+	struct Object {
+		TeIntrusivePtr<TeModel> _model;
+		Common::String _name;
 	};
 
 	class TeMarker {
 	};
 
-	void activateAnchorZone(const Common::String &name, bool param_2);
+	struct Dummy {
+		Common::String _name;
+		TeVector3f32 _position;
+		TeQuaternion _rotation;
+		TeVector3f32 _scale;
+	};
+
+	void activateAnchorZone(const Common::String &name, bool val);
 	void addAnchorZone(const Common::String &param_1, const Common::String &param_2, float param_3);
 	void addBlockingObject(const Common::String &obj) {
 		_blockingObjects.push_back(obj);
@@ -68,18 +89,30 @@ public:
 	static float angularDistance(float a1, float a2);
 	bool aroundAnchorZone(const AnchorZone *zone);
 	TeLayout *background();
+	virtual bool load(const Common::Path &path) override;
 	void loadBackground(const Common::Path &path);
 	void loadInteractions(const Common::Path &path);
 	void initScroll();
 
 	void draw();
+	void drawPath();
 	Character *character(const Common::String &name);
 	bool loadCharacter(const Common::String &name);
+	void loadBlockers();
 	bool loadPlayerCharacter(const Common::String &name);
+	bool loadLights(const Common::Path &path);
 	bool changeBackground(const Common::String &name);
-	void unloadPlayerCharacter(const Common::String &character);
 	void unloadCharacter(const Common::String &name);
 
+	// Original has a typo, "converPathToMesh", corrected.
+	void convertPathToMesh(TeFreeMoveZone *zone);
+	void onMainWindowSizeChanged();
+
+	// Original just calls these "deserialize" but that's a fairly vague name
+	// so renamed to be more meaningful.
+	void deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam);
+	void deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<TeModel> &model, TePickMesh2 *pickmesh);
+
 	void close();
 	void reset();
 	void freeSceneObjects();
@@ -88,8 +121,9 @@ public:
 
 	void setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2);
 
-	void loadBlockers();
-	Common::Path getBlockersFileName();
+	Common::Path getActZoneFileName() const;
+	Common::Path getBlockersFileName() const;
+	Common::Path getLightsFileName() const;
 
 	// Does nothing, but to keep calls from original..
 	static void updateScroll() {};
@@ -101,12 +135,19 @@ public:
 
 	TeLuaGUI &bgGui() { return _bgGui; }
 
+	int _shadowLightNo;
+	CharactersShadow *_charactersShadow;
+
 private:
-	struct TeBlocker {};
-	struct TeRectBlocker {};
+	TeColor _shadowColor;
+	float _shadowFarPlane;
+	float _shadowNearPlane;
+	float _shadowFov;
 
 	Common::Array<TeBlocker> _blockers;
 	Common::Array<TeRectBlocker> _rectBlockers;
+	Common::Array<TeActZone> _actZones;
+	Common::Array<TeFreeMoveZone*> _freeMoveZones;
 	Common::Array<TeMarker *> _markers;
 	Common::Array<AnchorZone *> _anchorZones;
 	Common::Array<AnimObject *> _animObjects;
@@ -116,14 +157,23 @@ private:
 
 	Common::HashMap<Common::String, SoundStep> _soundSteps;
 
+	Common::Array<TeIntrusivePtr<TeModel>> _hitObjects;
+	Common::Array<Object> _objects;
+	Common::Array<TeIntrusivePtr<TeBezierCurve>> _bezierCurves;
+	Common::Array<Dummy> _dummies;
+
 	TeIntrusivePtr<TeModel> _playerCharacterModel;
 	Common::Array<Common::String> _blockingObjects;
 	TeLuaGUI _bgGui;
 	TeLuaGUI _gui2; // TODO: find a better name.
 	TeLuaGUI _gui3; // TODO: find a better name.
-	// TODO add private members
+
+	Common::Array<TeLight> _lights;
 
 	TeVector2f32 _someScrollVector;
+	TeVector2f32 _viewportSize;
+
+	Common::Path _loadedPath;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/inventory_menu.cpp b/engines/tetraedge/game/inventory_menu.cpp
index 31f0294eb29..a98c855c2da 100644
--- a/engines/tetraedge/game/inventory_menu.cpp
+++ b/engines/tetraedge/game/inventory_menu.cpp
@@ -117,7 +117,7 @@ bool InventoryMenu::onQuitButton() {
 }
 
 bool InventoryMenu::onSaveButton(){
-   return false;
+	return false;
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index a3f95d08d18..4a38a714a58 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -52,7 +52,7 @@ tolua_Error err;
 
 static void AddRandomSound(const Common::String &s1, const Common::String &s2, float f1, float f2){
 	Game *game = g_engine->getGame();
-  	game->addRandomSound(s1, s2, f1, f2);
+	game->addRandomSound(s1, s2, f1, f2);
 }
 
 static int tolua_ExportedFunctions_AddRandomSound00(lua_State *L) {
@@ -89,9 +89,9 @@ static int tolua_ExportedFunctions_SetSoundStep00(lua_State *L) {
 }
 
 static void AddNumber(const Common::String &number) {
-  Game *game = g_engine->getGame();
-  if (!game->inventory().cellphone()->addNumber(number))
-	warning("[AddNumber] Number \"%s\" already exist.", number.c_str());
+	Game *game = g_engine->getGame();
+	if (!game->inventory().cellphone()->addNumber(number))
+		warning("[AddNumber] Number \"%s\" already exist.", number.c_str());
 }
 
 static int tolua_ExportedFunctions_AddNumber00(lua_State *L) {
@@ -141,15 +141,21 @@ static int tolua_ExportedFunctions_UnlockArtwork00(lua_State *L) {
 	error("#ferror in function 'UnlockArtwork': %d %d %s", err.index, err.array, err.type);
 }
 
-static void ChangeWarp(const Common::String &scene, const Common::String &zone) {
+static void ChangeWarp(const Common::String &zone, const Common::String &scene, bool flag) {
+	Game *game = g_engine->getGame();
+	if (game->changeWarp(zone, scene, flag))
+		return;
+	warning("[ChangeWarp] Zone \"%s\" with number Scene \"%s\" don't exist. Please reload and change with correct name.",
+				zone.c_str(), scene.c_str());
 }
 
 static int tolua_ExportedFunctions_ChangeWarp00(lua_State *L) {
 	tolua_Error err;
-	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isboolean(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		Common::String s2(tolua_tostring(L, 2, nullptr));
-		ChangeWarp(s1, s2);
+		bool flag = tolua_toboolean(L, 3, 0);
+		ChangeWarp(s1, s2, flag);
 		return 0;
 	}
 	error("#ferror in function 'ChangeWarp': %d %d %s", err.index, err.array, err.type);
@@ -157,153 +163,153 @@ static int tolua_ExportedFunctions_ChangeWarp00(lua_State *L) {
 
 
 void LuaOpenBinds(lua_State *L) {
-  tolua_open(L);
-  tolua_module(L, 0, 0);
-  tolua_beginmodule(L, 0);
-  /*
-  tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials00);
-  tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);
-  tolua_function(L, "HideObject", tolua_ExportedFunctions_HideObject00);
-  tolua_function(L, "ShowObject", tolua_ExportedFunctions_ShowObject00);
-  tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00);
-  tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);
-  tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00);
-  tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00);*/
-  tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
-  tolua_function(L, "PlayMovie", tolua_ExportedFunctions_PlayMovie00);
-  /*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);
-  tolua_function(L, "StartAnimationPart", tolua_ExportedFunctions_StartAnimationPart00);
-  tolua_function(L, "StartAnimation", tolua_ExportedFunctions_StartAnimation00);
-  tolua_function(L, "StartAnimationAndWaitForEnd",
-                 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00);
-  tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00);
-  tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
-  tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);
-  tolua_function(L, "AddMarker", tolua_ExportedFunctions_AddMarker00);
-  tolua_function(L, "SetVisibleMarker", tolua_ExportedFunctions_SetVisibleMarker00);
-  tolua_function(L, "DeleteMarker", tolua_ExportedFunctions_DeleteMarker00);
-  tolua_function(L, "SetVisibleCellphone", tolua_ExportedFunctions_SetVisibleCellphone00);
-  tolua_function(L, "DisabledZone", tolua_ExportedFunctions_DisabledZone00);
-  tolua_function(L, "DisabledInt", tolua_ExportedFunctions_DisabledInt00);
-  tolua_function(L, "LockCursor", tolua_ExportedFunctions_LockCursor00);
-  tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00);
-  tolua_function(L, "UnsetCondition", tolua_ExportedFunctions_UnsetCondition00);
-  tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00);
-  tolua_function(L, "LaunchDialog", tolua_ExportedFunctions_LaunchDialog00);
-  tolua_function(L, "LaunchDialogAndWaitForEnd", tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00);
-  tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
-  tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);
-  tolua_function(L, "PushTask", tolua_ExportedFunctions_PushTask00);
-  tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
-  tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);
-  tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00);
-  tolua_function(L, "PlaySound", tolua_ExportedFunctions_PlaySound00);
-  tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);
-  tolua_function(L, "StopSound", tolua_ExportedFunctions_StopSound00);*/
-  tolua_function(L, "AddRandomSound", tolua_ExportedFunctions_AddRandomSound00);
-  /*tolua_function(L, "PlayRandomSound", tolua_ExportedFunctions_PlayRandomSound00);
-  tolua_function(L, "PlayMusic", tolua_ExportedFunctions_PlayMusic00);*/
-  tolua_function(L, "SetSoundStep", tolua_ExportedFunctions_SetSoundStep00);
-  /*tolua_function(L, "Selected", tolua_ExportedFunctions_Selected00);
-  tolua_function(L, "TakeObject", tolua_ExportedFunctions_TakeObject00);
-  tolua_function(L, "TakeObjectInHand", tolua_ExportedFunctions_TakeObjectInHand00);
-  tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
-  tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject01);*/
-  tolua_function(L, "AddNumber", tolua_ExportedFunctions_AddNumber00);
-  /*tolua_function(L, "ShowDocument", tolua_ExportedFunctions_ShowDocument00);
-  tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00);
-  tolua_function(L, "HideDocument", tolua_ExportedFunctions_HideDocument00);
-  tolua_function(L, "AddDocument", tolua_ExportedFunctions_AddDocument00);
-  tolua_function(L, "LoadCharacter", tolua_ExportedFunctions_LoadCharacter00);
-  tolua_function(L, "UnloadCharacter", tolua_ExportedFunctions_UnloadCharacter00);
-  tolua_function(L, "GetRotationCharacter", tolua_ExportedFunctions_GetRotationCharacter00);
-  tolua_function(L, "GetXPositionCharacter", tolua_ExportedFunctions_GetXPositionCharacter00);
-  tolua_function(L, "GetYPositionCharacter", tolua_ExportedFunctions_GetYPositionCharacter00);
-  tolua_function(L, "GetZPositionCharacter", tolua_ExportedFunctions_GetZPositionCharacter00);
-  tolua_function(L, "MoveCharacterTo", tolua_ExportedFunctions_MoveCharacterTo00);
-  tolua_function(L, "MoveCharacterToAndWaitForEnd",
-                 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);
-  tolua_function(L, "MoveCharacterPlayerTo", tolua_ExportedFunctions_MoveCharacterPlayerTo00);
-  tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
-                 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00);
-  tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00);
-  tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
-  tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);
-  tolua_function(L, "SetCharacterRotation", tolua_ExportedFunctions_SetCharacterRotation00);
-  tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
-  tolua_function(L, "SetCharacterAnimation", tolua_ExportedFunctions_SetCharacterAnimation00);
-  tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
-                 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);
-  tolua_function(L, "BlendCharacterAnimation", tolua_ExportedFunctions_BlendCharacterAnimation00);
-  tolua_function(L, "BlendCharacterAnimationAndWaitForEnd",
-                 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);
-  tolua_function(L, "CurrentCharacterAnimation", tolua_ExportedFunctions_CurrentCharacterAnimation00);
-  tolua_function(L, "SetCharacterPlayerVisible", tolua_ExportedFunctions_SetCharacterPlayerVisible00);
-  tolua_function(L, "MoveCharacterPlayerDisabled",
-                 tolua_ExportedFunctions_MoveCharacterPlayerDisabled00);
-  tolua_function(L, "SetRunMode", tolua_ExportedFunctions_SetRunMode00);
-  tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200);
-  tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00);
-  tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00);
-  tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);
-  tolua_function(L, "AddCallback", tolua_ExportedFunctions_AddCallback00);
-  tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00);
-  tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00);
-  tolua_function(L, "DeleteCallback", tolua_ExportedFunctions_DeleteCallback00);
-  tolua_function(L, "DeleteCallbackPlayer", tolua_ExportedFunctions_DeleteCallbackPlayer00);
-  tolua_function(L, "DeleteCallbackAnimation2D", tolua_ExportedFunctions_DeleteCallbackAnimation2D00);
-  tolua_function(L, "SetObjectOnCharacter", tolua_ExportedFunctions_SetObjectOnCharacter00);
-  tolua_function(L, "SetObjectRotation", tolua_ExportedFunctions_SetObjectRotation00);
-  tolua_function(L, "SetObjectTranslation", tolua_ExportedFunctions_SetObjectTranslation00);
-  tolua_function(L, "SetObjectScale", tolua_ExportedFunctions_SetObjectScale00);
-  tolua_function(L, "SetObjectFrames", tolua_ExportedFunctions_SetObjectFrames00);
-  tolua_function(L, "LoadObject", tolua_ExportedFunctions_LoadObject00);
-  tolua_function(L, "UnloadObject", tolua_ExportedFunctions_UnloadObject00);
-  tolua_function(L, "SetGroundObjectPosition", tolua_ExportedFunctions_SetGroundObjectPosition00);
-  tolua_function(L, "SetGroundObjectRotation", tolua_ExportedFunctions_SetGroundObjectRotation00);
-  tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00);
-  tolua_function(L, "RotateGroundObject", tolua_ExportedFunctions_RotateGroundObject00);
-  tolua_function(L, "SetLightPlayerCharacter", tolua_ExportedFunctions_SetLightPlayerCharacter00);
-  tolua_function(L, "SetLightPos", tolua_ExportedFunctions_SetLightPos00);
-  tolua_function(L, "EnableLight", tolua_ExportedFunctions_EnableLight00);
-  tolua_function(L, "SetLightDiffuse", tolua_ExportedFunctions_SetLightDiffuse00);
-  tolua_function(L, "SetLightAmbient", tolua_ExportedFunctions_SetLightAmbient00);
-  tolua_function(L, "SetLightSpecular", tolua_ExportedFunctions_SetLightSpecular00);
-  tolua_function(L, "LoadBillBoard", tolua_ExportedFunctions_LoadBillBoard00);
-  tolua_function(L, "SetBillboardPosition", tolua_ExportedFunctions_SetBillboardPosition00);
-  tolua_function(L, "SetBillboardPosition2", tolua_ExportedFunctions_SetBillboardPosition200);
-  tolua_function(L, "SetBillboardSize", tolua_ExportedFunctions_SetBillboardSize00);
-  tolua_function(L, "ShowBillboard", tolua_ExportedFunctions_ShowBillboard00);
-  tolua_function(L, "HideBillboard", tolua_ExportedFunctions_HideBillboard00);
-  tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
-  tolua_function(L, "Save", tolua_ExportedFunctions_Save00);
-  tolua_function(L, "Wait", tolua_ExportedFunctions_Wait00);
-  tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00);
-  tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00);
-  tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
-  tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);
-  tolua_function(L, "BFGRateImmediately", tolua_ExportedFunctions_BFGRateImmediately00);
-  tolua_function(L, "BFGReportEvent", tolua_ExportedFunctions_BFGReportEvent00);
-  tolua_function(L, "BFGReportEventWithValue", tolua_ExportedFunctions_BFGReportEventWithValue00);
-  tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00);
-  tolua_function(L, "TestFileFlagSystemFlag", tolua_ExportedFunctions_TestFileFlagSystemFlag00);
-  tolua_function(L, "PrintDebugMessage", tolua_ExportedFunctions_PrintDebugMessage00);
-  tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);
-  tolua_function(L, "EnableRectBlocker", tolua_ExportedFunctions_EnableRectBlocker00);
-  tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);
-  tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
-  tolua_function(L, "ActivateAnchorZone", tolua_ExportedFunctions_ActivateAnchorZone00);
-  tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00);
-  tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
-  tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
-  tolua_function(L, "SetCharacterMeshVisible", tolua_ExportedFunctions_SetCharacterMeshVisible00);
-  tolua_function(L, "SetRecallageY", tolua_ExportedFunctions_SetRecallageY00);
-  tolua_function(L, "IsFreemiumUnlocked", tolua_ExportedFunctions_IsFreemiumUnlocked00);
-  tolua_function(L, "ReachedFreemiumLimit", tolua_ExportedFunctions_ReachedFreemiumLimit00);*/
-  tolua_function(L, "AddUnrecalAnim", tolua_ExportedFunctions_AddUnrecalAnim00);
-  tolua_function(L, "UnlockArtwork", tolua_ExportedFunctions_UnlockArtwork00);
+	tolua_open(L);
+	tolua_module(L, 0, 0);
+	tolua_beginmodule(L, 0);
+	/*
+	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials00);
+	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);
+	tolua_function(L, "HideObject", tolua_ExportedFunctions_HideObject00);
+	tolua_function(L, "ShowObject", tolua_ExportedFunctions_ShowObject00);
+	tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00);
+	tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);
+	tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00);
+	tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00);*/
+	tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
+	tolua_function(L, "PlayMovie", tolua_ExportedFunctions_PlayMovie00);
+	/*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);
+	tolua_function(L, "StartAnimationPart", tolua_ExportedFunctions_StartAnimationPart00);
+	tolua_function(L, "StartAnimation", tolua_ExportedFunctions_StartAnimation00);
+	tolua_function(L, "StartAnimationAndWaitForEnd",
+				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00);
+	tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00);
+	tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
+	tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);
+	tolua_function(L, "AddMarker", tolua_ExportedFunctions_AddMarker00);
+	tolua_function(L, "SetVisibleMarker", tolua_ExportedFunctions_SetVisibleMarker00);
+	tolua_function(L, "DeleteMarker", tolua_ExportedFunctions_DeleteMarker00);
+	tolua_function(L, "SetVisibleCellphone", tolua_ExportedFunctions_SetVisibleCellphone00);
+	tolua_function(L, "DisabledZone", tolua_ExportedFunctions_DisabledZone00);
+	tolua_function(L, "DisabledInt", tolua_ExportedFunctions_DisabledInt00);
+	tolua_function(L, "LockCursor", tolua_ExportedFunctions_LockCursor00);
+	tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00);
+	tolua_function(L, "UnsetCondition", tolua_ExportedFunctions_UnsetCondition00);
+	tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00);
+	tolua_function(L, "LaunchDialog", tolua_ExportedFunctions_LaunchDialog00);
+	tolua_function(L, "LaunchDialogAndWaitForEnd", tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00);
+	tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
+	tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);
+	tolua_function(L, "PushTask", tolua_ExportedFunctions_PushTask00);
+	tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
+	tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);
+	tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00);
+	tolua_function(L, "PlaySound", tolua_ExportedFunctions_PlaySound00);
+	tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);
+	tolua_function(L, "StopSound", tolua_ExportedFunctions_StopSound00);*/
+	tolua_function(L, "AddRandomSound", tolua_ExportedFunctions_AddRandomSound00);
+	/*tolua_function(L, "PlayRandomSound", tolua_ExportedFunctions_PlayRandomSound00);
+	tolua_function(L, "PlayMusic", tolua_ExportedFunctions_PlayMusic00);*/
+	tolua_function(L, "SetSoundStep", tolua_ExportedFunctions_SetSoundStep00);
+	/*tolua_function(L, "Selected", tolua_ExportedFunctions_Selected00);
+	tolua_function(L, "TakeObject", tolua_ExportedFunctions_TakeObject00);
+	tolua_function(L, "TakeObjectInHand", tolua_ExportedFunctions_TakeObjectInHand00);
+	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
+	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject01);*/
+	tolua_function(L, "AddNumber", tolua_ExportedFunctions_AddNumber00);
+	/*tolua_function(L, "ShowDocument", tolua_ExportedFunctions_ShowDocument00);
+	tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00);
+	tolua_function(L, "HideDocument", tolua_ExportedFunctions_HideDocument00);
+	tolua_function(L, "AddDocument", tolua_ExportedFunctions_AddDocument00);
+	tolua_function(L, "LoadCharacter", tolua_ExportedFunctions_LoadCharacter00);
+	tolua_function(L, "UnloadCharacter", tolua_ExportedFunctions_UnloadCharacter00);
+	tolua_function(L, "GetRotationCharacter", tolua_ExportedFunctions_GetRotationCharacter00);
+	tolua_function(L, "GetXPositionCharacter", tolua_ExportedFunctions_GetXPositionCharacter00);
+	tolua_function(L, "GetYPositionCharacter", tolua_ExportedFunctions_GetYPositionCharacter00);
+	tolua_function(L, "GetZPositionCharacter", tolua_ExportedFunctions_GetZPositionCharacter00);
+	tolua_function(L, "MoveCharacterTo", tolua_ExportedFunctions_MoveCharacterTo00);
+	tolua_function(L, "MoveCharacterToAndWaitForEnd",
+				 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);
+	tolua_function(L, "MoveCharacterPlayerTo", tolua_ExportedFunctions_MoveCharacterPlayerTo00);
+	tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
+				 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00);
+	tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00);
+	tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
+	tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);
+	tolua_function(L, "SetCharacterRotation", tolua_ExportedFunctions_SetCharacterRotation00);
+	tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
+	tolua_function(L, "SetCharacterAnimation", tolua_ExportedFunctions_SetCharacterAnimation00);
+	tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
+				 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);
+	tolua_function(L, "BlendCharacterAnimation", tolua_ExportedFunctions_BlendCharacterAnimation00);
+	tolua_function(L, "BlendCharacterAnimationAndWaitForEnd",
+				 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);
+	tolua_function(L, "CurrentCharacterAnimation", tolua_ExportedFunctions_CurrentCharacterAnimation00);
+	tolua_function(L, "SetCharacterPlayerVisible", tolua_ExportedFunctions_SetCharacterPlayerVisible00);
+	tolua_function(L, "MoveCharacterPlayerDisabled",
+				 tolua_ExportedFunctions_MoveCharacterPlayerDisabled00);
+	tolua_function(L, "SetRunMode", tolua_ExportedFunctions_SetRunMode00);
+	tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200);
+	tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00);
+	tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00);
+	tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);
+	tolua_function(L, "AddCallback", tolua_ExportedFunctions_AddCallback00);
+	tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00);
+	tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00);
+	tolua_function(L, "DeleteCallback", tolua_ExportedFunctions_DeleteCallback00);
+	tolua_function(L, "DeleteCallbackPlayer", tolua_ExportedFunctions_DeleteCallbackPlayer00);
+	tolua_function(L, "DeleteCallbackAnimation2D", tolua_ExportedFunctions_DeleteCallbackAnimation2D00);
+	tolua_function(L, "SetObjectOnCharacter", tolua_ExportedFunctions_SetObjectOnCharacter00);
+	tolua_function(L, "SetObjectRotation", tolua_ExportedFunctions_SetObjectRotation00);
+	tolua_function(L, "SetObjectTranslation", tolua_ExportedFunctions_SetObjectTranslation00);
+	tolua_function(L, "SetObjectScale", tolua_ExportedFunctions_SetObjectScale00);
+	tolua_function(L, "SetObjectFrames", tolua_ExportedFunctions_SetObjectFrames00);
+	tolua_function(L, "LoadObject", tolua_ExportedFunctions_LoadObject00);
+	tolua_function(L, "UnloadObject", tolua_ExportedFunctions_UnloadObject00);
+	tolua_function(L, "SetGroundObjectPosition", tolua_ExportedFunctions_SetGroundObjectPosition00);
+	tolua_function(L, "SetGroundObjectRotation", tolua_ExportedFunctions_SetGroundObjectRotation00);
+	tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00);
+	tolua_function(L, "RotateGroundObject", tolua_ExportedFunctions_RotateGroundObject00);
+	tolua_function(L, "SetLightPlayerCharacter", tolua_ExportedFunctions_SetLightPlayerCharacter00);
+	tolua_function(L, "SetLightPos", tolua_ExportedFunctions_SetLightPos00);
+	tolua_function(L, "EnableLight", tolua_ExportedFunctions_EnableLight00);
+	tolua_function(L, "SetLightDiffuse", tolua_ExportedFunctions_SetLightDiffuse00);
+	tolua_function(L, "SetLightAmbient", tolua_ExportedFunctions_SetLightAmbient00);
+	tolua_function(L, "SetLightSpecular", tolua_ExportedFunctions_SetLightSpecular00);
+	tolua_function(L, "LoadBillBoard", tolua_ExportedFunctions_LoadBillBoard00);
+	tolua_function(L, "SetBillboardPosition", tolua_ExportedFunctions_SetBillboardPosition00);
+	tolua_function(L, "SetBillboardPosition2", tolua_ExportedFunctions_SetBillboardPosition200);
+	tolua_function(L, "SetBillboardSize", tolua_ExportedFunctions_SetBillboardSize00);
+	tolua_function(L, "ShowBillboard", tolua_ExportedFunctions_ShowBillboard00);
+	tolua_function(L, "HideBillboard", tolua_ExportedFunctions_HideBillboard00);
+	tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
+	tolua_function(L, "Save", tolua_ExportedFunctions_Save00);
+	tolua_function(L, "Wait", tolua_ExportedFunctions_Wait00);
+	tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00);
+	tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00);
+	tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
+	tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);
+	tolua_function(L, "BFGRateImmediately", tolua_ExportedFunctions_BFGRateImmediately00);
+	tolua_function(L, "BFGReportEvent", tolua_ExportedFunctions_BFGReportEvent00);
+	tolua_function(L, "BFGReportEventWithValue", tolua_ExportedFunctions_BFGReportEventWithValue00);
+	tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00);
+	tolua_function(L, "TestFileFlagSystemFlag", tolua_ExportedFunctions_TestFileFlagSystemFlag00);
+	tolua_function(L, "PrintDebugMessage", tolua_ExportedFunctions_PrintDebugMessage00);
+	tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);
+	tolua_function(L, "EnableRectBlocker", tolua_ExportedFunctions_EnableRectBlocker00);
+	tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);
+	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
+	tolua_function(L, "ActivateAnchorZone", tolua_ExportedFunctions_ActivateAnchorZone00);
+	tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00);
+	tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
+	tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
+	tolua_function(L, "SetCharacterMeshVisible", tolua_ExportedFunctions_SetCharacterMeshVisible00);
+	tolua_function(L, "SetRecallageY", tolua_ExportedFunctions_SetRecallageY00);
+	tolua_function(L, "IsFreemiumUnlocked", tolua_ExportedFunctions_IsFreemiumUnlocked00);
+	tolua_function(L, "ReachedFreemiumLimit", tolua_ExportedFunctions_ReachedFreemiumLimit00);*/
+	tolua_function(L, "AddUnrecalAnim", tolua_ExportedFunctions_AddUnrecalAnim00);
+	tolua_function(L, "UnlockArtwork", tolua_ExportedFunctions_UnlockArtwork00);
 
-  tolua_endmodule(L);
+	tolua_endmodule(L);
 }
 
 
diff --git a/engines/tetraedge/game/main_menu.h b/engines/tetraedge/game/main_menu.h
index 4d256465c89..aefc03d2b83 100644
--- a/engines/tetraedge/game/main_menu.h
+++ b/engines/tetraedge/game/main_menu.h
@@ -42,14 +42,14 @@ public:
 	bool onBFGRateItButtonValidated();
 	bool onBFGRateItQuitButtonValidated();
 	bool onBFGSplashButtonUpdated() { return false; }
-	bool onBFGSplashButtonValidated()  { return false; }
-	bool onBFGTellAFriendButtonValidated()  { return false; }
+	bool onBFGSplashButtonValidated() { return false; }
+	bool onBFGTellAFriendButtonValidated() { return false; }
 	bool onBFGUnlockGameButtonValidated();
 	bool onContinueGameButtonValidated();
 	bool onDisabledTuto();
 	bool onEnterGameRotateAnimFinished();
-	bool onFacebookButtonValidated()  { return false; }
-	bool onFacebookLogged()  { return false; }
+	bool onFacebookButtonValidated() { return false; }
+	bool onFacebookLogged() { return false; }
 	bool onGalleryButtonValidated();
 	bool onHowToButtonValidated();
 	bool onLoadGameButtonValidated();
diff --git a/engines/tetraedge/game/notifier.cpp b/engines/tetraedge/game/notifier.cpp
index 9e1533b5b17..e24ee2a2c09 100644
--- a/engines/tetraedge/game/notifier.cpp
+++ b/engines/tetraedge/game/notifier.cpp
@@ -30,10 +30,49 @@ Notifier::Notifier() {
 }
 
 void Notifier::launchNextnotifier() {
-	TeCurveAnim2<Te3DObject2, TeColor> *fadeInAnim = _gui.colorLinearAnimation("fadeIn");
-	if (fadeInAnim->_runTimer._stopped) {
-		warning("TODO: Implement Notifier::launchNextnotifier");
+	TeCurveAnim2<Te3DObject2, TeColor> *colorAnim = _gui.colorLinearAnimation("fadeIn");
+	assert(colorAnim);
+	if (!colorAnim->_runTimer._stopped)
+		return;
+
+	colorAnim = _gui.colorLinearAnimation("fadeOut");
+	if (!colorAnim->_runTimer._stopped) {
+		colorAnim = _gui.colorLinearAnimation("visible");
+		bool abort = true;
+		if (!colorAnim->_runTimer._stopped) {
+			abort = _notifierDataArray.empty();
+		}
+		if (abort)
+			return;
 	}
+
+	unload();
+	load();
+
+	if (_notifierDataArray.empty())
+		return;
+
+	TeVariant textformat = _gui.value("textFormat");
+	Common::String formattedName = Common::String::format(textformat.toString().c_str(), _notifierDataArray[0]._name.c_str());
+
+	TeTextLayout *text = _gui.textLayout("text");
+	text->setText(formattedName);
+
+	if (!_notifierDataArray[0]._imgpath.empty()) {
+		_gui.spriteLayoutChecked("image")->load(_notifierDataArray[0]._imgpath);
+	}
+
+	_gui.layoutChecked("notifier")->setVisible(true);
+
+	colorAnim = _gui.colorLinearAnimation("fadeIn");
+	colorAnim->_callbackObj = _gui.layoutChecked("sprite");
+	colorAnim->play();
+
+	colorAnim = _gui.colorLinearAnimation("fadeInImage");
+	colorAnim->_callbackObj = _gui.layoutChecked("image");
+	colorAnim->play();
+
+	_notifierDataArray.remove_at(0);
 }
 
 void Notifier::load() {
@@ -54,8 +93,15 @@ void Notifier::load() {
 }
 
 bool Notifier::onFadeInFinished() {
-	//TeCurveAnim2<Te3DObject2, TeColor> *visible = _gui.colorLinearAnimation("visible");
-	error("TODO: Implement me.");
+	TeCurveAnim2<Te3DObject2, TeColor> *colorAnim = _gui.colorLinearAnimation("visible");
+	colorAnim->_callbackObj = _gui.layout("sprite");
+	colorAnim->play();
+
+	colorAnim = _gui.colorLinearAnimation("visibleImage");
+	colorAnim->_callbackObj = _gui.layout("image");
+	colorAnim->play();
+
+	return false;
 }
 
 bool Notifier::onFadeOutFinished() {
@@ -66,7 +112,14 @@ bool Notifier::onFadeOutFinished() {
 }
 
 bool Notifier::onVisibleFinished() {
-	error("TODO: Implement me.");
+	TeCurveAnim2<Te3DObject2, TeColor> *colorAnim = _gui.colorLinearAnimation("fadeOut");
+	colorAnim->_callbackObj = _gui.layout("sprite");
+	colorAnim->play();
+
+	colorAnim = _gui.colorLinearAnimation("fadeOutImage");
+	colorAnim->_callbackObj = _gui.layout("image");
+	colorAnim->play();
+	return false;
 }
 
 void Notifier::push(const Common::String &name, const Common::String &imgpath) {
diff --git a/engines/tetraedge/game/notifier.h b/engines/tetraedge/game/notifier.h
index b277f907ead..dcd1d70bc8f 100644
--- a/engines/tetraedge/game/notifier.h
+++ b/engines/tetraedge/game/notifier.h
@@ -43,8 +43,8 @@ public:
 
 private:
 	struct notifierData {
-		Common::String name;
-		Common::String imgpath;
+		Common::String _name;
+		Common::String _imgpath;
 	};
 	Common::Array<notifierData> _notifierDataArray;
 	TeLuaGUI _gui;
diff --git a/engines/tetraedge/game/object_settings_xml_parser.h b/engines/tetraedge/game/object_settings_xml_parser.h
index c759156018b..6328f814041 100644
--- a/engines/tetraedge/game/object_settings_xml_parser.h
+++ b/engines/tetraedge/game/object_settings_xml_parser.h
@@ -47,6 +47,7 @@ public:
 		KEY_END()
 	} PARSER_END()
 
+private:
 	// Parser callback methods
 	bool parserCallback_ObjectsSettings(ParserNode *node);
 	bool parserCallback_Object(ParserNode *node);
@@ -54,7 +55,6 @@ public:
 	bool parserCallback_defaultScale(ParserNode *node);
 	bool textCallback(const Common::String &val) override;
 
-private:
 	enum TextTagType {
 		TagModelFileName,
 		TagDefaultScale
diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
index 825042955f1..44c99aba5dc 100644
--- a/engines/tetraedge/game/objectif.cpp
+++ b/engines/tetraedge/game/objectif.cpp
@@ -26,6 +26,7 @@
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/objectif.h"
 #include "tetraedge/te/te_vector2f32.h"
+#include "tetraedge/te/te_text_layout.h"
 
 namespace Tetraedge {
 
@@ -157,7 +158,16 @@ void Objectif::update() {
 			}
 		}
 
-		warning("TODO: Finish main part of Objectif::update");
+		float z = 0.1;
+		for (Te3DObject2 *child : tasks->childList()) {
+			TeTextLayout *text = dynamic_cast<TeTextLayout *>(child);
+			/*TeVector3f32 size =*/
+			text->size();
+			TeVector3f32 userPos = text->userPosition();
+			userPos.z() = z;
+			text->setPosition(userPos);
+			z += text->userSize().y();
+		}
 	}
 	_layoutsDirty = false;
 }
diff --git a/engines/tetraedge/game/question2.h b/engines/tetraedge/game/question2.h
index 6bf9f408560..ca5ddd7ee6f 100644
--- a/engines/tetraedge/game/question2.h
+++ b/engines/tetraedge/game/question2.h
@@ -51,6 +51,7 @@ public:
 	bool onAnswerValidated(Answer &answer);
 	void pushAnswer(const Common::String &name, const Common::String &unk, const Common::String &path);
 	void unload();
+	TeLuaGUI &gui() { return _gui; }
 
 private:
 	TeLuaGUI _gui;
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.cpp b/engines/tetraedge/game/scene_lights_xml_parser.cpp
new file mode 100644
index 00000000000..2d2da20620b
--- /dev/null
+++ b/engines/tetraedge/game/scene_lights_xml_parser.cpp
@@ -0,0 +1,169 @@
+/* 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/game/scene_lights_xml_parser.h"
+#include "tetraedge/te/te_light.h"
+
+namespace Tetraedge {
+
+bool SceneLightsXmlParser::parserCallback_Global(ParserNode *node) {
+	_parent = Parent_Global;
+	return true;
+}
+
+bool SceneLightsXmlParser::parseCol(ParserNode *node, TeColor &colout) {
+	uint r = node->values["r"].asUint64();
+	uint g = node->values["g"].asUint64();
+	uint b = node->values["b"].asUint64();
+	uint a;
+	if (node->values.contains("a"))
+		a = node->values["a"].asUint64();
+	else
+		a = 0xff;
+
+	if (r > 255 || g > 255 || b > 255 | a > 255) {
+		parserError("Invalid color values");
+		return false;
+	}
+	colout = TeColor(r, g, b, a);
+	return true;
+}
+
+
+bool SceneLightsXmlParser::parserCallback_Ambient(ParserNode *node) {
+	// can appear under either global or light
+	TeColor col;
+	if (!parseCol(node, col))
+		return false;
+
+	if (_parent == Parent_Global) {
+		TeLight::setGlobalAmbient(col);
+	} else {
+		_lights->back().setAmbient(col);
+	}
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Lights(ParserNode *node) {
+	// Nothing to do, data handled in the child keys.
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Light(ParserNode *node) {
+	_parent = Parent_Light;
+	_lights->push_back(TeLight());
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Position(ParserNode *node) {
+	float x = atof(node->values["x"].c_str());
+	float y = atof(node->values["y"].c_str());
+	float z = atof(node->values["z"].c_str());
+	_lights->back().setPosition3d(TeVector3f32(x, y, z));
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Direction(ParserNode *node) {
+	float h = (atof(node->values["h"].c_str()) * 3.141593) / 180.0;
+	float v = (atof(node->values["v"].c_str()) * 3.141593) / 180.0;
+	_lights->back().setPositionRadial(TeVector2f32(h, v));
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Diffuse(ParserNode *node) {
+	TeColor col;
+	if (!parseCol(node, col))
+		return false;
+
+	_lights->back().setDiffuse(col);
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Specular(ParserNode *node) {
+	TeColor col;
+	if (!parseCol(node, col))
+		return false;
+
+	_lights->back().setSpecular(col);
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Attenuation(ParserNode *node) {
+	_lights->back().setConstAtten(atof(node->values["constant"].c_str()));
+	_lights->back().setLinearAtten(atof(node->values["linear"].c_str()));
+	_lights->back().setQuadraticAtten(atof(node->values["quadratic"].c_str()));
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Cutoff(ParserNode *node) {
+	float f = atof(node->values["value"].c_str());
+	_lights->back().setCutoff((f * 3.141593) / 180.0);
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Exponent(ParserNode *node) {
+	_lights->back().setExponent(atof(node->values["value"].c_str()));
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_DisplaySize(ParserNode *node) {
+	_lights->back().setDisplaySize(atof(node->values["value"].c_str()));
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Shadow(ParserNode *node) {
+	_parent = Parent_Shadow;
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_SourceLight(ParserNode *node) {
+	_shadowLightNo = atof(node->values["number"].c_str());
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Fov(ParserNode *node) {
+	_shadowFov = atof(node->values["value"].c_str());
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_NearPlane(ParserNode *node) {
+	_shadowNearPlane = atof(node->values["value"].c_str());
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_FarPlane(ParserNode *node) {
+	_shadowFarPlane = atof(node->values["value"].c_str());
+	return true;
+}
+
+bool SceneLightsXmlParser::parserCallback_Color(ParserNode *node) {
+	TeColor col;
+	if (parseCol(node, col)) {
+		_shadowColor = col;
+		return true;
+	}
+	return false;
+}
+
+
+
+}
+ // end namespace Tetraedge
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.h b/engines/tetraedge/game/scene_lights_xml_parser.h
new file mode 100644
index 00000000000..0b85362ec5f
--- /dev/null
+++ b/engines/tetraedge/game/scene_lights_xml_parser.h
@@ -0,0 +1,162 @@
+/* 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_SCENE_LIGHTS_XML_PARSER_H
+#define TETRAEDGE_GAME_SCENE_LIGHTS_XML_PARSER_H
+
+#include "common/xmlparser.h"
+#include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_vector3f32.h"
+
+
+namespace Tetraedge {
+
+class SceneLightsXmlParser : public Common::XMLParser {
+public:
+	void setLightArray(Common::Array<TeLight> *lights) {
+		_lights = lights;
+	}
+	TeColor getShadowColor() { return _shadowColor; }
+	int getShadowLightNo() { return _shadowLightNo; }
+	float getShadowFarPlane() { return _shadowFarPlane; }
+	float getShadowNearPlane() { return _shadowNearPlane; }
+	float getShadowFov() { return _shadowFov; }
+
+	// Parser
+	CUSTOM_XML_PARSER(SceneLightsXmlParser) {
+		XML_KEY(Global)
+			XML_KEY(Ambient)
+				XML_PROP(r, true)
+				XML_PROP(g, true)
+				XML_PROP(b, true)
+			KEY_END()
+		KEY_END()
+		XML_KEY(Lights)
+			XML_KEY(Light)
+				XML_PROP(Type, true)
+				XML_KEY(Position)
+					XML_PROP(x, true)
+					XML_PROP(y, true)
+					XML_PROP(z, true)
+				KEY_END()
+				XML_KEY(Direction)
+					XML_PROP(h, true)
+					XML_PROP(v, true)
+				KEY_END()
+				XML_KEY(Ambient)
+					XML_PROP(r, true)
+					XML_PROP(g, true)
+					XML_PROP(b, true)
+				KEY_END()
+				XML_KEY(Diffuse)
+					XML_PROP(r, true)
+					XML_PROP(g, true)
+					XML_PROP(b, true)
+				KEY_END()
+				XML_KEY(Specular)
+					XML_PROP(r, true)
+					XML_PROP(g, true)
+					XML_PROP(b, true)
+				KEY_END()
+				XML_KEY(Attenuation)
+					XML_PROP(constant, true)
+					XML_PROP(linear, true)
+					XML_PROP(quadratic, true)
+				KEY_END()
+				XML_KEY(Cutoff)
+					XML_PROP(value, true)
+				KEY_END()
+				XML_KEY(Exponent)
+					XML_PROP(value, true)
+				KEY_END()
+				XML_KEY(DisplaySize)
+					XML_PROP(value, true)
+				KEY_END()
+			KEY_END()
+		KEY_END()
+		XML_KEY(Shadow)
+			XML_KEY(SourceLight)
+				XML_PROP(number, true)
+			KEY_END()
+			XML_KEY(Fov)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(NearPlane)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(FarPlane)
+				XML_PROP(value, true)
+			KEY_END()
+			XML_KEY(Color)
+				XML_PROP(r, true)
+				XML_PROP(g, true)
+				XML_PROP(b, true)
+				XML_PROP(a, true)
+			KEY_END()
+		KEY_END()
+	} PARSER_END()
+
+private:
+	Common::Array<TeLight> *_lights;
+
+	enum ParentNodeType {
+		Parent_Global,
+		Parent_Light,
+		Parent_Shadow
+	};
+
+	TeColor _shadowColor;
+	int _shadowLightNo;
+	float _shadowFarPlane;
+	float _shadowNearPlane;
+	float _shadowFov;
+	ParentNodeType _parent;
+
+	// Parser callback methods
+	bool parserCallback_Global(ParserNode *node);
+	bool parserCallback_Ambient(ParserNode *node);
+
+	bool parserCallback_Lights(ParserNode *node);
+	bool parserCallback_Light(ParserNode *node);
+	bool parserCallback_Position(ParserNode *node);
+	bool parserCallback_Direction(ParserNode *node);
+	//bool parserCallback_Ambient(ParserNode *node);
+	bool parserCallback_Diffuse(ParserNode *node);
+	bool parserCallback_Specular(ParserNode *node);
+	bool parserCallback_Attenuation(ParserNode *node);
+	bool parserCallback_Cutoff(ParserNode *node);
+	bool parserCallback_Exponent(ParserNode *node);
+	bool parserCallback_DisplaySize(ParserNode *node);
+
+	bool parserCallback_Shadow(ParserNode *node);
+	bool parserCallback_SourceLight(ParserNode *node);
+	bool parserCallback_Fov(ParserNode *node);
+	bool parserCallback_NearPlane(ParserNode *node);
+	bool parserCallback_FarPlane(ParserNode *node);
+	bool parserCallback_Color(ParserNode *node);
+
+	bool parseCol(ParserNode *node, TeColor &colout);
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_GAME_SCENE_LIGHTS_XML_PARSER_H
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index 9873d9ceabf..2908af876e3 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -38,10 +38,12 @@ MODULE_OBJS := \
 	game/options_menu.o \
 	game/owner_error_menu.o \
 	game/question2.o \
+	game/scene_lights_xml_parser.o \
 	game/splash_screens.o \
 	te/micropather.o \
 	te/te_3d_object2.o \
 	te/te_3d_texture.o \
+	te/te_act_zone.o \
 	te/te_animation.o \
 	te/te_bezier_curve.o \
 	te/te_button_layout.o \
diff --git a/engines/tetraedge/te/micropather.h b/engines/tetraedge/te/micropather.h
index 5e0b7461f00..493c035b583 100644
--- a/engines/tetraedge/te/micropather.h
+++ b/engines/tetraedge/te/micropather.h
@@ -58,6 +58,7 @@ distribution.
 
 #include "common/array.h"
 #include "common/util.h"
+#include "common/math.h"
 #include "common/types.h"
 
 
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index 73fb4e2e3bc..f1830449f1a 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -42,13 +42,25 @@ Te3DObject2::~Te3DObject2() {
 	setParent(nullptr);
 }
 
-void Te3DObject2::addChild(Te3DObject2 *child) {
-	_children.push_back(child);
-	child->setParent(this);
+void Te3DObject2::addChild(Te3DObject2 *newChild) {
+	assert(newChild != this && newChild != _parent);
+	for (auto *c : _children) {
+		if (c == newChild)
+			error("Trying to re-add child %s to object %s", newChild->name().c_str(), _name.c_str());
+	}
+
+	_children.push_back(newChild);
+	newChild->setParent(this);
 	_childListChangedSignal.call();
 }
 
 void Te3DObject2::addChildBefore(Te3DObject2 *newChild, const Te3DObject2 *ref) {
+	assert(newChild != this && newChild != _parent);
+	for (auto *c : _children) {
+		if (c == newChild)
+			error("Trying to re-add child %s to object %s", newChild->name().c_str(), _name.c_str());
+	}
+
 	Common::Array<Te3DObject2 *>::iterator iter;
 	for (iter = _children.begin(); iter != _children.end(); iter++) {
 		if (*iter == ref) {
@@ -67,15 +79,16 @@ Te3DObject2 *Te3DObject2::child(long offset) {
 	return _children[offset];
 }
 
-long Te3DObject2::childIndex(Te3DObject2 *child) {
+long Te3DObject2::childIndex(Te3DObject2 *c) const {
 	for (uint i = 0; i < _children.size(); i++) {
-		if (_children[i] == child)
+		if (_children[i] == c)
 			return i;
 	}
 	return -1;
 }
 
-/*static*/ Common::String Te3DObject2::deserializeString(Common::ReadStream &stream) {
+/*static*/
+Common::String Te3DObject2::deserializeString(Common::ReadStream &stream) {
 	uint slen = stream.readUint32LE();
 	if (slen > 1024 * 1024)
 		error("Improbable string size %d", slen);
@@ -91,7 +104,8 @@ long Te3DObject2::childIndex(Te3DObject2 *child) {
 	return Common::String();
 }
 
-/*static*/ void Te3DObject2::deserialize(Common::ReadStream &stream, Te3DObject2 &dest) {
+/*static*/
+void Te3DObject2::deserialize(Common::ReadStream &stream, Te3DObject2 &dest) {
 	Common::String str = deserializeString(stream);
 	dest.setName(str);
 
@@ -107,7 +121,8 @@ long Te3DObject2::childIndex(Te3DObject2 *child) {
 	dest.setScale(vect);
 }
 
-/*static*/ void Te3DObject2::serialize(Common::WriteStream &stream, Te3DObject2 &src) {
+/*static*/
+void Te3DObject2::serialize(Common::WriteStream &stream, Te3DObject2 &src) {
 	const Common::String &name = src.name();
 	stream.writeUint32LE(name.size());
 	stream.write(name.c_str(), name.size());
@@ -171,6 +186,7 @@ void Te3DObject2::setColor(const TeColor &col) {
 }
 
 void Te3DObject2::setParent(Te3DObject2 *newparent) {
+	assert(newparent != this);
 	if (_parent) {
 		if (_onWorldVisibleChangedParentCallback)
 			_parent->onWorldVisibleChanged().remove(_onWorldVisibleChangedParentCallback);
@@ -305,4 +321,12 @@ bool Te3DObject2::worldVisible() {
 	}
 }
 
+/*static*/
+bool Te3DObject2::loadAndCheckFourCC(Common::ReadStream &stream, const char *str) {
+	char buf[5];
+	buf[4] = '\0';
+	stream.read(buf, 4);
+	return !strncmp(buf, str, 4);
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_3d_object2.h b/engines/tetraedge/te/te_3d_object2.h
index c39a69fe48b..54fd905d8eb 100644
--- a/engines/tetraedge/te/te_3d_object2.h
+++ b/engines/tetraedge/te/te_3d_object2.h
@@ -46,9 +46,9 @@ public:
 		return _children.size();
 	}
 
-	long childIndex(Te3DObject2 *childToFind);
+	long childIndex(Te3DObject2 *childToFind) const;
 
-	Common::Array<Te3DObject2 *> &childList() {
+	const Common::Array<Te3DObject2 *> &childList() const {
 		return _children;
 	}
 
@@ -68,7 +68,7 @@ public:
 	static void serialize(Common::WriteStream &stream, Te3DObject2 &src);
 	static Common::String deserializeString(Common::ReadStream &stream);
 
-	virtual void draw() = 0;
+	virtual void draw() {}
 	const Common::String &name() const {
 		return _name;
 	}
@@ -142,6 +142,8 @@ public:
 	virtual float ySize() { return _size.y(); };
 	virtual float zSize() { return _size.z(); };
 
+	static bool loadAndCheckFourCC(Common::ReadStream &stream, const char *str);
+
 protected:
 	TeVector3f32 _size;
 	TeVector3f32 _position;
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index 408d87d57a9..6680bf37559 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -95,8 +95,18 @@ void Te3DTexture::destroy() {
 	_glTexture = NO_TEXTURE;
 }
 
-void Te3DTexture::ForceTexData(uint gltextures, uint xsize, uint ysize) {
-	error("TODO: Implement me");
+void Te3DTexture::forceTexData(uint gltextures, uint xsize, uint ysize) {
+	if (_glTexture != 0xffffffff) {
+		if (_createdTexture)
+			glDeleteTextures(1, &_glTexture);
+		_createdTexture = false;
+		_loaded = false;
+	}
+	_glTexture = gltextures;
+	_width = xsize;
+	_height = ysize;
+	_texWidth = xsize;
+	_texHeight = ysize;
 }
 
 bool Te3DTexture::hasAlpha() const {
@@ -217,6 +227,7 @@ TeVector2s32 Te3DTexture::optimisedSize(const TeVector2s32 &size) {
 	return TeVector2s32(v1, v2);
 }
 
+/*static*/
 void Te3DTexture::unbind() {
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->setMatrixMode(TeRenderer::MM_GL_TEXTURE);
diff --git a/engines/tetraedge/te/te_3d_texture.h b/engines/tetraedge/te/te_3d_texture.h
index 039dc42efa6..cbf228836fe 100644
--- a/engines/tetraedge/te/te_3d_texture.h
+++ b/engines/tetraedge/te/te_3d_texture.h
@@ -44,7 +44,7 @@ public:
 	void create();
 	void destroy();
 
-	void ForceTexData(uint gltextures, uint xsize, uint ysize);
+	void forceTexData(uint gltextures, uint xsize, uint ysize);
 
 	TeImage::Format getFormat() const { return _format; }
 	bool hasAlpha() const;
@@ -55,7 +55,7 @@ public:
 
 	static TeVector2s32 optimisedSize(const TeVector2s32 &size);
 
-	void unbind();
+	static void unbind();
 	bool unload();
 	void update(const TeImage &img, uint xoff, uint yoff);
 
diff --git a/engines/tetraedge/te/te_act_zone.cpp b/engines/tetraedge/te/te_act_zone.cpp
new file mode 100644
index 00000000000..e6ec2ca3b09
--- /dev/null
+++ b/engines/tetraedge/te/te_act_zone.cpp
@@ -0,0 +1,31 @@
+/* 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/te/te_act_zone.h"
+
+namespace Tetraedge {
+
+TeActZone::TeActZone() {
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_act_zone.h b/engines/tetraedge/te/te_act_zone.h
new file mode 100644
index 00000000000..ef4ecc34911
--- /dev/null
+++ b/engines/tetraedge/te/te_act_zone.h
@@ -0,0 +1,47 @@
+/* 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_TE_TE_ACT_ZONE_H
+#define TETRAEDGE_TE_TE_ACT_ZONE_H
+
+#include "common/str.h"
+#include "tetraedge/te/te_vector2f32.h"
+
+namespace Tetraedge {
+
+class TeActZone {
+public:
+	TeActZone();
+
+	Common::String s1;
+	Common::String s2;
+	TeVector2f32 points[4];
+	bool flag1;
+	bool flag2;
+
+private:
+	// TODO add private members
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_TE_TE_ACT_ZONE_H
diff --git a/engines/tetraedge/te/te_animation.cpp b/engines/tetraedge/te/te_animation.cpp
index f2c7cd71b34..7c5a8c2defe 100644
--- a/engines/tetraedge/te/te_animation.cpp
+++ b/engines/tetraedge/te/te_animation.cpp
@@ -35,6 +35,9 @@ Common::Array<TeAnimation *> *TeAnimation::_animations = nullptr;
 TeAnimation::TeAnimation() : _repeatCount(1), _dontRepeat(false) {
 }
 
+TeAnimation::~TeAnimation() {
+	stop();
+}
 
 void TeAnimation::cont() {
 	if (_runTimer._stopped) {
@@ -98,10 +101,13 @@ void TeAnimation::seekToStart() {
 }
 
 /*static*/ void TeAnimation::updateAll() {
-	for (auto &anim : *animations()) {
-		if (!anim->_runTimer._stopped) {
-			float msFromStart = anim->_runTimer.getTimeFromStart() / 1000.0;
-			anim->update(msFromStart);
+	Common::Array<TeAnimation *> &anims = *animations();
+	// Note: update can cause events which cascade into animtaions
+	// getting deleted, so be careful about the numbers.
+	for (unsigned int i = 0; i < anims.size(); i++) {
+		if (!anims[i]->_runTimer._stopped) {
+			float msFromStart = anims[i]->_runTimer.getTimeFromStart() / 1000.0;
+			anims[i]->update(msFromStart);
 		}
 	}
 
diff --git a/engines/tetraedge/te/te_animation.h b/engines/tetraedge/te/te_animation.h
index 3f71c68b692..ed1da6436cb 100644
--- a/engines/tetraedge/te/te_animation.h
+++ b/engines/tetraedge/te/te_animation.h
@@ -31,7 +31,7 @@ namespace Tetraedge {
 class TeAnimation {
 public:
 	TeAnimation();
-	virtual ~TeAnimation() {};
+	virtual ~TeAnimation();
 
 	virtual void cont();
 	virtual void pause();
diff --git a/engines/tetraedge/te/te_bezier_curve.cpp b/engines/tetraedge/te/te_bezier_curve.cpp
index 2d9ff8b79d1..d30beb5191c 100644
--- a/engines/tetraedge/te/te_bezier_curve.cpp
+++ b/engines/tetraedge/te/te_bezier_curve.cpp
@@ -42,7 +42,19 @@ void TeBezierCurve::draw() {
 
 float TeBezierCurve::length() {
 	if (_lengthNeedsUpdate) {
-		error("TODO: Implement TeBezierCurve::length");
+		_length = 0.0;
+		_lengthNeedsUpdate = false;
+		_lengths.clear();
+
+		TeVector3f32 lastpt = _controlPoints[0];
+		for (unsigned int i = 0; i < _numiterations; i++) {
+			float amount = (float)i / _numiterations;
+			const TeVector3f32 pt = retrievePoint(amount);
+			float len = (lastpt - pt).length();
+			_length += len;
+			_lengths.push_back(_length);
+			lastpt = pt;
+		}
 	}
 	return _length;
 }
@@ -81,5 +93,24 @@ void TeBezierCurve::setNbIterations(unsigned long iterations) {
 	_numiterations = iterations;
 }
 
+/*static*/
+void TeBezierCurve::serialize(Common::WriteStream &stream, const TeBezierCurve &curve) {
+	error("TODO: Implement TeBezierCurve::serialize");
+}
+
+/*static*/
+void TeBezierCurve::deserialize(Common::ReadStream &stream, TeBezierCurve &curve) {
+	Te3DObject2::deserialize(stream, curve);
+
+	curve._lengthNeedsUpdate = false;
+	curve._length = stream.readFloatLE();
+	uint32 npoints = stream.readUint32LE();
+
+	for (unsigned int i = 0; i < npoints; i++) {
+		TeVector3f32 vec;
+		TeVector3f32::deserialize(stream, vec);
+		curve._controlPoints.push_back(vec);
+	}
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_bezier_curve.h b/engines/tetraedge/te/te_bezier_curve.h
index 858616e498a..ace2c2ceea5 100644
--- a/engines/tetraedge/te/te_bezier_curve.h
+++ b/engines/tetraedge/te/te_bezier_curve.h
@@ -44,19 +44,18 @@ public:
 	void setControlPoints(const Common::Array<TeVector3f32> &points);
 	void setNbIterations(unsigned long iterations);
 
-	static void serialize(Common::WriteStream &stream, const TeBezierCurve &mesh);
-	static void deserialize(Common::ReadStream &stream, TeBezierCurve &mesh);
+	static void serialize(Common::WriteStream &stream, const TeBezierCurve &curve);
+	static void deserialize(Common::ReadStream &stream, TeBezierCurve &curve);
 
 private:
-	int _numiterations;
+	unsigned int _numiterations;
 	float _length;
 	float _rawLength;
 	bool _lengthNeedsUpdate;
 	bool _rawLengthNeedsUpdate;
 	Common::Array<TeVector3f32> _controlPoints;
 	Common::Array<float> _rawLengths;
-	// TODO add private members
-
+	Common::Array<float> _lengths;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index 97aff6289ee..31bb77bb74e 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -209,6 +209,7 @@ void TeButtonLayout::resetTimeFromLastValidation() {
 }
 
 long TeButtonLayout::timeFromLastValidation() {
+	// probably not needed because we reimplemented how this works.
 	error("TODO: Implement TeButtonLayout::timeFromLastValidation.");
 }
 
@@ -305,9 +306,11 @@ void TeButtonLayout::setEnable(bool enable) {
 
 void TeButtonLayout::setPosition(const TeVector3f32 &pos) {
 	TeLayout::setPosition(pos);
+
 	if (_currentState != BUTTON_STATE_DISABLED) {
 		int somethingCount = 0;
 		if (!_intArr.empty()) {
+			// probably not needed as we reimplememted how this works.
 			error("TODO: Implement setPosition logic for up/down state");
 		}
 		if (_someClickFlag) {
diff --git a/engines/tetraedge/te/te_callback.h b/engines/tetraedge/te/te_callback.h
index 3530a46b276..2db003d15dc 100644
--- a/engines/tetraedge/te/te_callback.h
+++ b/engines/tetraedge/te/te_callback.h
@@ -66,7 +66,7 @@ public:
 	virtual ~TeICallback1Param() {}
 	virtual bool operator()(T data) = 0;
 	virtual bool call(T data) = 0;
-	virtual float priority() const = 0;
+	virtual float &priority() = 0;
 	virtual bool equals(const TeICallback1Param *other) const = 0;
 };
 
@@ -84,7 +84,7 @@ public:
 	bool operator()(S data) override { return (_object->*_method)(data); }
 	bool call(S data) override { return (_object->*_method)(data); }
 
-	virtual float priority() const override { return _priority; }
+	virtual float &priority() override { return _priority; }
 
 	bool equals(const TeICallback1Param<S> *other) const override {
 		const TeCallback1Param<T, S> *o = dynamic_cast<const TeCallback1Param<T, S> *>(other);
@@ -99,7 +99,7 @@ public:
 	virtual ~TeICallback2Param() {}
 	virtual bool operator()(S data1, T data2) = 0;
 	virtual bool call(S data1, T data2) = 0;
-	virtual float priority() const = 0;
+	virtual float &priority() = 0;
 	virtual bool equals(const TeICallback2Param *other) const = 0;
 };
 
@@ -117,7 +117,7 @@ public:
 	bool operator()(S data1, T data2) override { return (_object->*_method)(data1, data2); }
 	bool call(S data1, T data2) override { return (_object->*_method)(data1, data2); }
 
-	virtual float priority() const override { return _priority; }
+	virtual float &priority() override { return _priority; }
 
 	bool equals(const TeICallback2Param<S, T> *other) const override {
 		const TeCallback2Param<C, S, T> *o = dynamic_cast<const TeCallback2Param<C, S, T> *>(other);
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index e11b857287c..07ae8dd4501 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -30,7 +30,8 @@ namespace Tetraedge {
 
 TeCamera::TeCamera() : _projectionMatrixType(0), _orthogonalParamL(1.0f),
 	_orthogonalParamR(0.0f), _orthogonalParamT(1.0f), _orthogonalParamB(0.0f),
-	_orthNearVal(10.0), _orthFarVal(4000.0), _transformA(0), _transformB(0)
+	_orthNearVal(10.0f), _orthFarVal(4000.0f), _transformA(0), _transformB(0),
+	_focalLenMaybe(40.0f), _somePerspectiveVal(1.0f)
 {
 }
 
diff --git a/engines/tetraedge/te/te_camera.h b/engines/tetraedge/te/te_camera.h
index a6de84f02bc..1f02ccf0154 100644
--- a/engines/tetraedge/te/te_camera.h
+++ b/engines/tetraedge/te/te_camera.h
@@ -73,6 +73,8 @@ public:
 	int _projectionMatrixType;
 	float _orthNearVal;
 	float _orthFarVal;
+	float _focalLenMaybe;
+	float _somePerspectiveVal;
 
 private:
 	int _viewportX;
diff --git a/engines/tetraedge/te/te_checkbox_layout.cpp b/engines/tetraedge/te/te_checkbox_layout.cpp
index 075fcf7acb8..05ad75eec21 100644
--- a/engines/tetraedge/te/te_checkbox_layout.cpp
+++ b/engines/tetraedge/te/te_checkbox_layout.cpp
@@ -45,6 +45,14 @@ _clickPassThrough(false), _state(CheckboxState6)
 	inputmgr->_mouseLUpSignal.insert(_onMouseLeftUpMaxPriorityCallback);
 }
 
+TeCheckboxLayout::~TeCheckboxLayout() {
+	TeInputMgr *inputmgr = g_engine->getInputMgr();
+	inputmgr->_mouseMoveSignal.remove(_onMousePositionChangedCallback);
+	inputmgr->_mouseLDownSignal.remove(_onMouseLeftDownCallback);
+	inputmgr->_mouseLUpSignal.remove(_onMouseLeftUpCallback);
+	inputmgr->_mouseLUpSignal.remove(_onMouseLeftUpMaxPriorityCallback);
+}
+
 void TeCheckboxLayout::setActiveLayout(TeLayout *layout) {
 	if (_activeLayout)
 		removeChild(_activeLayout);
diff --git a/engines/tetraedge/te/te_checkbox_layout.h b/engines/tetraedge/te/te_checkbox_layout.h
index 915b68836a3..11cb9def573 100644
--- a/engines/tetraedge/te/te_checkbox_layout.h
+++ b/engines/tetraedge/te/te_checkbox_layout.h
@@ -30,6 +30,7 @@ namespace Tetraedge {
 class TeCheckboxLayout : public TeLayout {
 public:
 	TeCheckboxLayout();
+	virtual ~TeCheckboxLayout();
 
 	enum State {
 		CheckboxStateActive,
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 15c516515fd..6f072e56676 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -23,9 +23,41 @@
 
 namespace Tetraedge {
 
-TeFreeMoveZone::TeFreeMoveZone() {
+TeFreeMoveZone::TeFreeMoveZone() : _actzones(nullptr), _blockers(nullptr), _rectBlockers(nullptr),
+_transformedVerticiesDirty(true), _bordersDirty(true), _pickMeshDirty(true), _projectedPointsDirty(true)
+{
 }
 
-// TODO: Add more functions here.
+void TeFreeMoveZone::buildAStar() {
+	error("TODO: Implement TeFreeMoveZone::buildAStar");
+}
+
+void TeFreeMoveZone::calcGridMatrix() {
+	error("TODO: Implement TeFreeMoveZone::calcGridMatrix");
+}
+
+void TeFreeMoveZone::clear() {
+	error("TODO: Implement TeFreeMoveZone::clear");
+}
+
+Common::Array<TeVector3f32> TeFreeMoveZone::collisions(const TeVector3f32 &v1, const TeVector3f32 &v2) {
+	error("TODO: Implement TeFreeMoveZone::collisions");
+}
+
+TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool f) {
+	error("TODO: Implement TeFreeMoveZone::correctCharacterPosition");
+}
+
+/*static*/
+void TeFreeMoveZone::deserialize(Common::ReadStream &stream, TeFreeMoveZone &dest, Common::Array<TeBlocker> *blockers,
+               Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones) {
+	dest.clear();
+	TePickMesh2::deserialize(stream, dest);
+
+	error("TODO: Implement TeFreeMoveZone::deserialize");
+	dest._blockers = blockers;
+	dest._rectBlockers = rectblockers;
+	dest._actzones = actzones;
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index 48c61612198..d1ddb9a3f97 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -26,6 +26,7 @@
 
 #include "tetraedge/te/te_pick_mesh2.h"
 #include "tetraedge/te/te_vector3f32.h"
+#include "tetraedge/te/te_act_zone.h"
 #include "tetraedge/te/te_timer.h"
 
 namespace Tetraedge {
@@ -34,6 +35,20 @@ namespace micropather {
 	class MicroPather;
 }
 
+// TODO: should these structs be moved to their own headers?
+struct TeBlocker {
+	Common::String _s;
+	TeVector2f32 _pts[2];
+	long _x;
+};
+
+struct TeRectBlocker {
+	Common::String _s;
+	TeVector2f32 _pts[4];
+	long _x;
+};
+
+
 class TeFreeMoveZone : public TePickMesh2 {
 public:
 	struct CollidePoint {
@@ -55,12 +70,17 @@ public:
 	void buildAStar();
 	void calcGridMatrix();
 	void clear();
-	Common::Array<TeVector3f32> collisions(TeVector3f32 const&, TeVector2f32 const&);
-	TeVector3f32 correctCharacterPosition(TeVector3f32 const&, bool*, bool);
+	Common::Array<TeVector3f32> collisions(const TeVector3f32 &v1, const TeVector3f32 &v2);
+	TeVector3f32 correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool f);
 
-	// TODO add public members
+	static void deserialize(Common::ReadStream &stream, TeFreeMoveZone &dest, Common::Array<TeBlocker> *blockers,
+               Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones);
 
 private:
+	Common::Array<TeActZone> *_actzones;
+	Common::Array<TeBlocker> *_blockers;
+	Common::Array<TeRectBlocker> *_rectBlockers;
+
 	TeFreeMoveZoneGraph *_graph;
 	bool _transformedVerticiesDirty;
 	bool _bordersDirty;
diff --git a/engines/tetraedge/te/te_i_3d_object2.h b/engines/tetraedge/te/te_i_3d_object2.h
index 9313c2535a9..de922feb320 100644
--- a/engines/tetraedge/te/te_i_3d_object2.h
+++ b/engines/tetraedge/te/te_i_3d_object2.h
@@ -28,6 +28,8 @@ class TeI3DObject2 {
 public:
 	TeI3DObject2();
 
+	virtual ~TeI3DObject2() {}
+
 	// TODO add public members
 
 private:
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index 8092ab1ef79..b59155364c8 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -58,7 +58,7 @@ void TeImage::create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
 	Graphics::Surface::fillRect(Common::Rect(0, 0, xsize, ysize), 0xff883311);
 }
 
-void TeImage::deSerialize(Common::ReadStream &stream) {
+void TeImage::deserialize(Common::ReadStream &stream) {
 	error("TODO: TeImage: Implement me.");
 }
 
@@ -71,21 +71,21 @@ void TeImage::drawPlot(void *outbuf, int x, int y, const TeVector2s32 &bufsize,
 	error("TODO: TeImage: Implement me.");
 }
 
-void TeImage::fill(byte val)  {
+void TeImage::fill(byte val) {
 	error("TODO: TeImage: Implement me.");
 }
 
-void TeImage::fill(byte r, byte g, byte b, byte a)  {
+void TeImage::fill(byte r, byte g, byte b, byte a) {
 	Common::Rect wholeSurf(0, 0, w, h);
 	uint32 col = (r << 24) | (g << 16) | (b << 8) | a;
 	Graphics::Surface::fillRect(wholeSurf, col);
 }
 
-void TeImage::getBuff(uint x, uint y, byte *pout, uint w_, uint h_)  {
+void TeImage::getBuff(uint x, uint y, byte *pout, uint w_, uint h_) {
 	error("TODO: TeImage: Implement me.");
 }
 
-bool TeImage::isExtensionSupported(const Common::Path &path)  {
+bool TeImage::isExtensionSupported(const Common::Path &path) {
 	error("TODO: TeImage: Implement me.");
 }
 
@@ -105,15 +105,15 @@ bool TeImage::load(const Common::Path &path) {
 	return true;
 }
 
-bool TeImage::load(Common::ReadStream &stream, const Common::Path &path)  {
+bool TeImage::load(Common::ReadStream &stream, const Common::Path &path) {
 	error("TODO: TeImage::load Implement me.");
 }
 
-bool TeImage::save(const Common::Path &path, enum Type type)  {
+bool TeImage::save(const Common::Path &path, enum Type type) {
 	error("TODO: TeImage::save Implement me.");
 }
 
-int TeImage::serialize(Common::WriteStream &stream)  {
+int TeImage::serialize(Common::WriteStream &stream) {
 	error("TODO: TeImage::serialize Implement me.");
 }
 
diff --git a/engines/tetraedge/te/te_image.h b/engines/tetraedge/te/te_image.h
index 4dced597c29..995e8be2f91 100644
--- a/engines/tetraedge/te/te_image.h
+++ b/engines/tetraedge/te/te_image.h
@@ -61,7 +61,7 @@ public:
 	}
 	void create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
 				Format format, uint bufxsize, uint bufysize);
-	void deSerialize(Common::ReadStream &stream);
+	void deserialize(Common::ReadStream &stream);
 	void destroy();
 	void drawPlot(int x, int y, const TeColor &col) {
 		drawPlot(getPixels(), x, y, bufSize(), col);
diff --git a/engines/tetraedge/te/te_intrusive_ptr.h b/engines/tetraedge/te/te_intrusive_ptr.h
index a3eb1b28759..dd9224ae3ba 100644
--- a/engines/tetraedge/te/te_intrusive_ptr.h
+++ b/engines/tetraedge/te/te_intrusive_ptr.h
@@ -30,20 +30,20 @@ namespace Tetraedge {
  */
 template<class T> class TeIntrusivePtr {
 public:
-	TeIntrusivePtr() : _p(nullptr) {}
+	typedef void(T::*Tdestructor)();
 
-	TeIntrusivePtr(const TeIntrusivePtr<T> &other) {
+	TeIntrusivePtr() : _p(nullptr), _deleteFn(nullptr) {}
+
+	TeIntrusivePtr(const TeIntrusivePtr<T> &other) : _deleteFn(nullptr) {
 		_p = other._p;
 		if (_p)
 			_p->incrementCounter();
-
 	}
 
-	TeIntrusivePtr(T *obj) {
+	TeIntrusivePtr(T *obj) : _deleteFn(nullptr) {
 		_p = obj;
 		if (_p)
 			_p->incrementCounter();
-
 	}
 
 	virtual ~TeIntrusivePtr() {
@@ -64,6 +64,7 @@ public:
 		if (this != &other) {
 			release();
 			_p = other._p;
+			_deleteFn = other._deleteFn;
 			if (_p)
 				_p->incrementCounter();
 		}
@@ -72,8 +73,12 @@ public:
 
 	void release() {
 		if (_p) {
-			if (_p->decrementCounter())
-				delete _p;
+			if (_p->decrementCounter()) {
+				if (_deleteFn)
+					(_p->*_deleteFn)();
+				else
+					delete _p;
+			}
 		}
 		_p = nullptr;
 	}
@@ -110,8 +115,13 @@ public:
 		return _p;
 	}
 
+	void setDeleteFn(Tdestructor destructor) {
+		_deleteFn = destructor;
+	}
+
 private:
 	T *_p;
+	Tdestructor _deleteFn;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_layout.cpp b/engines/tetraedge/te/te_layout.cpp
index e3aa96f95c2..02072ef0511 100644
--- a/engines/tetraedge/te/te_layout.cpp
+++ b/engines/tetraedge/te/te_layout.cpp
@@ -36,7 +36,7 @@ TeLayout::TeLayout() : Te3DObject2(), _updatingZ(false), _updatingZSize(false),
 	_safeAreaRatio(1.3333334f), _ratioMode(RATIO_MODE_NONE),
 	_positionType(CoordinatesType::RELATIVE_TO_PARENT)
 {
-	_userPosition = _position = TeVector3f32(0.5f, 0.5f, 0.5f);
+	_userPosition = _position = TeVector3f32(0.5f, 0.5f, 0.0f);
 	_size = TeVector3f32(1.0f, 1.0f, 1.0f);
 	_onChildSizeChangedCallback.reset(
 		new TeCallback0Param<TeLayout>(this, &TeLayout::onChildSizeChanged));
@@ -50,8 +50,10 @@ TeLayout::TeLayout() : Te3DObject2(), _updatingZ(false), _updatingZSize(false),
 }
 
 TeLayout::~TeLayout() {
-	if (parent() && _onParentSizeChangedCallback)
+	if (parent() && _onParentSizeChangedCallback) {
 		parent()->onSizeChanged().remove(_onParentSizeChangedCallback);
+		parent()->onWorldTransformationMatrixChanged().remove(_onParentWorldTransformationMatrixChangedCallback);
+	}
 
 	if (_onChildSizeChangedCallback) {
 		for (auto &child : childList()) {
@@ -111,7 +113,6 @@ bool TeLayout::isAutoZEnabled() {
 
 void TeLayout::draw() {
 	if (visible() && worldVisible()) {
-		//debug("Draw TeLayout %p (%s)", this, name().empty() ? "no name" : name().c_str());
 		// Ensure world transform is up-to-date.
 		worldTransformationMatrix();
 		for (auto &child : childList()) {
@@ -195,6 +196,7 @@ void TeLayout::setMode(DrawMode mode) {
 }
 
 void TeLayout::setParent(Te3DObject2 *parent) {
+	assert(parent != this);
 	Te3DObject2 *oldParent = Te3DObject2::parent();
 	if (oldParent) {
 		if (_onParentSizeChangedCallback)
@@ -253,7 +255,7 @@ void TeLayout::setRatioMode(RatioMode mode) {
 		_ratioMode = mode;
 		_sizeChanged = true;
 		_worldMatrixChanged = true;
-   }
+	}
 }
 
 void TeLayout::setRotation(const TeQuaternion &rot) {
@@ -342,7 +344,8 @@ void TeLayout::updatePosition() {
 		const TeVector3f32 parentSize(parentObj->xSize(), parentObj->ySize(), 0.0);
 		const TeVector3f32 offsetAnchor =  midPoint - _anchor;
 		const TeVector3f32 thisSize(xSize(), ySize(), 0.0);
-		_position = (offsetUserPos * parentSize) + (offsetAnchor * thisSize);
+		const TeVector3f32 newpos = (offsetUserPos * parentSize) + (offsetAnchor * thisSize);
+		_position = newpos;
 	} else if (_positionType == RELATIVE_TO_PARENT && !parentObj) {
 		// Not in original, but no parent -> set midpoint.
 		const TeVector3f32 offsetAnchor =  midPoint - _anchor;
@@ -351,6 +354,7 @@ void TeLayout::updatePosition() {
 	} else if (_positionType == ABSOLUTE) {
 		_position = _userPosition;
 	}
+	_position.z() = _userPosition.z();
 	_worldMatrixChanged = true;
 	_updatingPosition = false;
 
diff --git a/engines/tetraedge/te/te_light.cpp b/engines/tetraedge/te/te_light.cpp
index e5faa68d5cd..f02c6990e8b 100644
--- a/engines/tetraedge/te/te_light.cpp
+++ b/engines/tetraedge/te/te_light.cpp
@@ -19,24 +19,159 @@
  *
  */
 
+#include "common/math.h"
+
 #include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_color.h"
+#include "tetraedge/te/te_quaternion.h"
+#include "tetraedge/te/te_vector3f32.h"
 
 #include "graphics/opengl/system_headers.h"
 
 namespace Tetraedge {
 
-TeLight::TeLight() {
+static inline uint _toGlLight(uint lightno) {
+	return GL_LIGHT0 + lightno;
 }
 
-/*static*/ void TeLight::enableAll() {
-	glEnable(GL_LIGHTING);
+/*static*/
+TeColor TeLight::_globalAmbientColor;
+
+TeLight::TeLight() : _colAmbient(0, 0, 0, 0xff), _colDiffuse(0, 0, 0, 0xff), _colSpecular(0xff, 0xff, 0xff, 0xff),
+_constAtten(0.0f), _linearAtten(1.0f), _quadraticAtten(0.0f), _cutoff(0.0f), _exponent(0.0f), _type(LightTypePoint),
+_displaySize(3.0)
+{
+}
+
+TeVector3f32 TeLight::directionVector() const {
+	float cosx = cosf(_positionRadial.getX());
+	float cosy = cosf(_positionRadial.getY());
+	float sinx = sinf(_positionRadial.getX());
+	float siny = sinf(_positionRadial.getY());
+	return TeVector3f32(cosx * cosy, siny, sinx * cosy);
+}
+
+void TeLight::disable(uint lightno) {
+	glDisable(_toGlLight(lightno));
 }
 
-/*static*/ void TeLight::disableAll() {
+void TeLight::enable(uint lightno) {
+	if (_colDiffuse.r() == 0 && _colDiffuse.g() == 0 && _colDiffuse.b() == 0)
+		glDisable(_toGlLight(lightno));
+	else
+		glEnable(_toGlLight(lightno));
+}
+
+/*static*/
+void TeLight::disableAll() {
 	glDisable(GL_LIGHTING);
 }
 
+/*static*/
+void TeLight::enableAll() {
+	glEnable(GL_LIGHTING);
+}
+
+void TeLight::draw(TeCamera &camera) {
+	error("TODO: Finish TeLight::draw");
+}
+
+void TeLight::transformDirPoint(const TeVector3f32 &pt1,TeVector3f32 &pt2) {
+	const TeQuaternion q1 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 1, 0), _positionRadial.getX() + M_PI);
+	const TeQuaternion q2 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 0, -1), -_positionRadial.getY());
+	pt2.rotate(q2);
+	pt2.rotate(q1);
+	pt2 += pt1;
+}
+
+void TeLight::transformSpotPoint(TeVector3f32 &pt) {
+	const TeQuaternion q1 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 1, 0), _positionRadial.getX());
+	const TeQuaternion q2 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 0, -1), _positionRadial.getY());
+	pt.rotate(q2);
+	pt.rotate(q1);
+	pt += _position3d;
+}
+
+void TeLight::update(uint lightno) {
+	float col[4];
+	const uint glLight = _toGlLight(lightno);
+	col[0] = _colAmbient.r() / 255.0f;
+	col[1] = _colAmbient.g() / 255.0f;
+	col[2] = _colAmbient.b() / 255.0f;
+	col[3] = 1.0;
+	glLightfv(glLight, GL_AMBIENT, col);
+
+	col[0] = _colDiffuse.r() / 255.0f;
+	col[1] = _colDiffuse.g() / 255.0f;
+	col[2] = _colDiffuse.b() / 255.0f;
+	col[3] = 1.0;
+	glLightfv(glLight, GL_DIFFUSE, col);
+	if (col[0] < 0.01f && col[1] < 0.01f && col[2] < 0.01f)
+		glDisable(glLight);
+
+	col[0] = _colSpecular.r() / 255.0f;
+	col[1] = _colSpecular.g() / 255.0f;
+	col[2] = _colSpecular.b() / 255.0f;
+	col[3] = 1.0;
+	glLightfv(glLight, GL_SPECULAR, col);
+
+	if (_type == LightTypeSpot || _type == LightTypePoint) {
+		float pos[4];
+		pos[0] = _position3d.x();
+		pos[1] = _position3d.y();
+		pos[2] = _position3d.z();
+		pos[3] = 1.0f;
+		glLightfv(glLight, GL_POSITION, pos);
+		glLightfv(glLight, GL_CONSTANT_ATTENUATION, &_constAtten);
+		glLightfv(glLight, GL_LINEAR_ATTENUATION, &_linearAtten);
+		glLightfv(glLight, GL_QUADRATIC_ATTENUATION, &_quadraticAtten);
+	}
+
+	if (_type == LightTypeDirectional) {
+		float pos[4];
+		float cosx = cosf(_positionRadial.getX());
+		float cosy = cosf(_positionRadial.getY());
+		float sinx = sinf(_positionRadial.getX());
+		float siny = sinf(_positionRadial.getY());
+		pos[0] = cosx * cosy;
+		pos[1] = siny;
+		pos[2] = sinx * cosy;
+		pos[3] = 0.0;
+		glLightfv(glLight, GL_POSITION, pos);
+	}
+
+	float atten;
+	uint atype;
+	if (_type == LightTypeSpot) {
+		float pos[4];
+		float cosx = cosf(_positionRadial.getX());
+		float cosy = cosf(_positionRadial.getY());
+		float sinx = sinf(_positionRadial.getX());
+		float siny = sinf(_positionRadial.getY());
+		pos[0] = cosx * cosy;
+		pos[1] = siny;
+		pos[2] = sinx * cosy;
+		pos[3] = 0.0;
+		glLightfv(glLight, GL_SPOT_DIRECTION, pos);
+		glLightf(glLight, GL_SPOT_CUTOFF, (_cutoff * 180.0) / M_PI);
+		atten = _exponent;
+		atype = GL_SPOT_EXPONENT;
+	} else {
+		atten = 180.0;
+		atype = GL_SPOT_CUTOFF;
+	}
+	glLightf(glLight, atype, atten);
+}
+
+/*static*/
+void TeLight::updateGlobal() {
+	float col[4];
+	col[0] = _globalAmbientColor.r() / 255.0f;
+	col[1] = _globalAmbientColor.g() / 255.0f;
+	col[2] = _globalAmbientColor.b() / 255.0f;
+	col[3] = 1.0;
+	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
+}
 
-// TODO: Add more functions here.
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_light.h b/engines/tetraedge/te/te_light.h
index 547b2896631..633a5c125c2 100644
--- a/engines/tetraedge/te/te_light.h
+++ b/engines/tetraedge/te/te_light.h
@@ -29,9 +29,9 @@
 namespace Tetraedge {
 
 enum TeLightType {
-	Type0 = 0,
-	Type1 = 1,
-	Type2 = 2  // uses quadratic attenuation?
+	LightTypePoint = 0,
+	LightTypeDirectional = 1,
+	LightTypeSpot = 2
 };
 
 class TeCamera;
@@ -43,29 +43,49 @@ public:
 	TeVector3f32 directionVector() const;
 	void disable(uint lightno);
 	void enable(uint lightno);
-	static void enableAll();
 	static void disableAll();
+	static void enableAll();
 
 	void draw(TeCamera &camera);
 
-	void transformDirPoint(TeVector3f32 &pt1,TeVector3f32 &pt2);
+	void transformDirPoint(const TeVector3f32 &pt1, TeVector3f32 &pt2);
 	void transformSpotPoint(TeVector3f32 &pt1);
 
 	void update(uint lightno);
-	void updateGlobal();
+	static void updateGlobal();
+	static void setGlobalAmbient(const TeColor &col) { _globalAmbientColor = col; }
+
+	void setSpecular(const TeColor &col) { _colSpecular = col; }
+	void setDiffuse(const TeColor &col) { _colDiffuse = col; }
+	void setAmbient(const TeColor &col) { _colAmbient = col; }
+
+	void setConstAtten(float val) { _constAtten = val; }
+	void setLinearAtten(float val) { _linearAtten = val; }
+	void setQuadraticAtten(float val) { _quadraticAtten = val; }
+	void setCutoff(float val) { _cutoff = val; }
+	void setExponent(float val) { _exponent = val; }
+	void setDisplaySize(float val) { _displaySize = val; }
+	void setPosition3d(const TeVector3f32 &pos) { _position3d = pos; }
+	void setPositionRadial(const TeVector2f32 &pos) { _positionRadial = pos; }
 
 private:
 	TeVector3f32 _position3d;
 	TeVector2f32 _positionRadial;
 
-	TeColor col1;
-	TeColor col2;
-	TeColor col3;
-	// TODO add private members
+	TeColor _colAmbient;
+	TeColor _colDiffuse;
+	TeColor _colSpecular;
 
 	enum TeLightType _type;
 
 	static TeColor _globalAmbientColor;
+
+	float _constAtten;
+	float _linearAtten;
+	float _quadraticAtten;
+	float _cutoff;
+	float _exponent;
+	float _displaySize;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_lua_gui.cpp b/engines/tetraedge/te/te_lua_gui.cpp
index a70c2268f09..e6d55d14ed5 100644
--- a/engines/tetraedge/te/te_lua_gui.cpp
+++ b/engines/tetraedge/te/te_lua_gui.cpp
@@ -269,6 +269,24 @@ void TeLuaGUI::unload() {
 		iter._value->deleteLater();
 	}
 	_extendedTextLayouts.clear();
+
+	for (auto &iter : _layoutAnchorLinearAnimations) {
+		iter._value->stop();
+		delete iter._value;
+	}
+	_layoutAnchorLinearAnimations.clear();
+
+	for (auto &iter : _layoutPositionLinearAnimations) {
+		iter._value->stop();
+		delete iter._value;
+	}
+	_layoutPositionLinearAnimations.clear();
+
+	for (auto &iter : _colorLinearAnimations) {
+		iter._value->stop();
+		delete iter._value;
+	}
+	_colorLinearAnimations.clear();
 }
 
 TeVariant TeLuaGUI::value(const Common::String &globalName) {
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index 85f125033bd..b8a04c324c1 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -75,12 +75,12 @@ static float TeLuaToF32(lua_State *L, int index) {
 }
 
 static bool TeLuaToBool(lua_State *L,int index) {
-  if (lua_type(L, index) != LUA_TBOOLEAN) {
-	  warning("TeLuaToBool:: not a bool");
-	  return false;
-  } else {
-	  return lua_toboolean(L, index);
-  }
+	if (lua_type(L, index) != LUA_TBOOLEAN) {
+		warning("TeLuaToBool:: not a bool");
+		return false;
+	} else {
+		return lua_toboolean(L, index);
+	}
 }
 
 
@@ -245,7 +245,7 @@ int layoutBindings(lua_State *L) {
 		layout->setName(Common::String::format("%p", (void *)layout));
 	}
 	lua_pushstring(L, "__TeLuaGUIThis");
-    lua_gettable(L, LUA_REGISTRYINDEX);
+	lua_gettable(L, LUA_REGISTRYINDEX);
 	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
 	TeLuaGUI::StringMap<TeLayout *> &layouts = gui->layouts();
 	if (!layouts.contains(layout->name())) {
@@ -384,7 +384,7 @@ int spriteLayoutBindings(lua_State *L) {
 		}
 		lua_settop(L, -2);
 	}
-	if (!imgFullPath.empty())
+	if (!imgFullPath.empty()) {}
 		layout->load(imgFullPath);
 
 	lua_pushnil(L);
diff --git a/engines/tetraedge/te/te_matrix4x4.cpp b/engines/tetraedge/te/te_matrix4x4.cpp
index 2c53fdfb996..1f54fbec634 100644
--- a/engines/tetraedge/te/te_matrix4x4.cpp
+++ b/engines/tetraedge/te/te_matrix4x4.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "tetraedge/te/te_matrix4x4.h"
+#include "tetraedge/te/te_trs.h"
 
 namespace Tetraedge {
 
@@ -65,10 +66,12 @@ TeMatrix4x4 operator*(const TeMatrix4x4 &left, const TeMatrix4x4 &right) {
 	return retval;
 }
 
+/*
 TeMatrix4x4 &TeMatrix4x4::operator*=(const TeMatrix4x4 &mul) {
-	error("TODO: Opeartor *=");
+	TeMatrix4x4 result = operator*(*this, mul);
+	*this = result;
 	return *this;
-}
+}*/
 
 bool TeMatrix4x4::operator==(const TeMatrix4x4 &other) const {
 	for (int i = 0; i < 16; i++) {
@@ -316,5 +319,30 @@ void TeMatrix4x4::serialize(Common::WriteStream &stream) const {
 	}
 }
 
+/*static*/
+TeMatrix4x4 TeMatrix4x4::fromTRS(const TeTRS &trs) {
+	TeMatrix4x4 result;
+	const TeVector3f32 trans = trs.getTranslation();
+	TeMatrix4x4 transm;
+	float *tm = transm.getData();
+	tm[12] = trans.x();
+	tm[13] = trans.y();
+	tm[14] = trans.z();
+	result = result * transm;
+
+	const TeMatrix4x4 rotm = trs.getRotation().toMatrix();
+	result = result * rotm;
+
+	const TeVector3f32 scle = trs.getScale();
+	TeMatrix4x4 scalem;
+	float *sm = scalem.getData();
+	sm[0] = scle.x();
+	sm[5] = scle.y();
+	sm[10] = scle.z();
+	result = result * scalem;
+
+	return result;
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_matrix4x4.h b/engines/tetraedge/te/te_matrix4x4.h
index 73c93c3eae2..36aac4935cd 100644
--- a/engines/tetraedge/te/te_matrix4x4.h
+++ b/engines/tetraedge/te/te_matrix4x4.h
@@ -28,6 +28,8 @@
 
 namespace Tetraedge {
 
+class TeTRS;
+
 /* A 4x4 matrix, but stored in *column-major* order to match
  * OpenGL (and the original engine)
  */
@@ -51,7 +53,7 @@ public:
 		return !operator==(other);
 	}
 
-	TeMatrix4x4 &operator*=(const TeMatrix4x4 &mul);
+	//TeMatrix4x4 &operator*=(const TeMatrix4x4 &mul);
 
 	TeVector3f32 operator*(const TeVector3f32 &mul) const;
 
@@ -73,6 +75,8 @@ public:
 
 	bool inverse();
 
+	static TeMatrix4x4 fromTRS(const TeTRS &trs);
+
 	const float *getData() const { return _data; }
 	float *getData() { return _data; }
 
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index de83f7f15dd..bdf7075fda5 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -361,7 +361,17 @@ void TeMesh::update(const Common::Array<TeMatrix4x4> *matricies1, const Common::
 }
 
 void TeMesh::update(TeIntrusivePtr<TeModelVertexAnimation> vertexanim) {
+	_updatedVerticies.resize(_verticies.size());
+	_updatedNormals.resize(_normals.size());
 
+	const Common::Array<TeVector3f32> &animverts = vertexanim->getVertices();
+	assert(animverts.size() >= _verticies.size());
+	for (unsigned int i = 0; i < _verticies.size(); i++) {
+		_updatedVerticies[i] = animverts[i];
+	}
+	for (unsigned int i = 0; i < _normals.size(); i++) {
+		_updatedNormals[i] = _normals[i];
+	}
 }
 
 void TeMesh::updateTo(const Common::Array<TeMatrix4x4> *matricies1, const Common::Array<TeMatrix4x4> *matricies2,
diff --git a/engines/tetraedge/te/te_mesh.h b/engines/tetraedge/te/te_mesh.h
index 9012f087cb4..9e3fa5612b9 100644
--- a/engines/tetraedge/te/te_mesh.h
+++ b/engines/tetraedge/te/te_mesh.h
@@ -37,6 +37,8 @@
 
 namespace Tetraedge {
 
+class TeModel;
+class TeModelVertexAnimation;
 
 class TeMesh : public Te3DObject2 {
 public:
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index fa3446b8cdb..b1ca1f1f3d5 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -33,7 +33,39 @@
 
 namespace Tetraedge {
 
-TeModel::TeModel() : _enableLights(false), _skipBoneMatricies(false) {
+TeModel::TeModel() : _enableLights(false), _skipBoneMatricies(false), _matrixForced(false) {
+	// TODO: set 0x17c to 1.0
+	// TODO: set 0x178, 0x170 to 0
+	_modelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
+	_modelVertexAnim.setDeleteFn(&TeModelVertexAnimation::deleteLater);
+	create();
+}
+
+TeModel::~TeModel() {
+	destroy();
+}
+
+void TeModel::create() {
+	// TODO: set field_0x158 to 0
+	_modelAnim.release();
+	_modelVertexAnim.release();
+	_matrixForced = false;
+	_skipBoneMatricies = false;
+}
+
+void TeModel::destroy() {
+	_weightElements.clear();
+	// TODO: clear matrix array 0x148
+	_meshes.clear();
+	_bones.clear();
+	// TODO: clear matrix array 0x190
+	_boneMatrices.clear();
+	for (MeshBlender *blender : _meshBlenders)
+		delete blender;
+	_meshBlenders.clear();
+	for (BonesBlender *blender : _boneBlenders)
+		delete blender;
+	_boneBlenders.clear();
 }
 
 int TeModel::checkFileType(Common::SeekableReadStream &instream) {
@@ -49,11 +81,11 @@ int TeModel::checkFileType(Common::SeekableReadStream &instream) {
 	return 0;
 }
 
-void TeModel::blendAnim(TeIntrusivePtr<TeModelAnimation>& anim, float amount, bool repeat) {
+void TeModel::blendAnim(TeIntrusivePtr<TeModelAnimation>& anim, float seconds, bool repeat) {
 	if (!_modelAnim) {
 		setAnim(anim, repeat);
 	} else {
-		BonesBlender *blender = new BonesBlender(anim, amount);
+		BonesBlender *blender = new BonesBlender(anim, seconds);
 		anim->_repeatCount = (repeat ? -1 : 1);
 		anim->play();
 		_boneBlenders.push_back(blender);
@@ -73,7 +105,7 @@ void TeModel::draw() {
 		renderer->pushMatrix();
 		renderer->multiplyMatrix(transform);
 		for (TeMesh &mesh : _meshes) {
-			// TODO: Set some value in the mesh here to this->field_0x158??
+			// TODO: Set some flag in the mesh here to this->field_0x158??
 			mesh.draw();
 		}
 		renderer->popMatrix();
@@ -81,6 +113,20 @@ void TeModel::draw() {
 	}
 }
 
+void TeModel::forceMatrix(const TeMatrix4x4 &matrix) {
+	_matrixForced = true;
+	_forcedMatrix = matrix;
+}
+
+TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned long num) {
+	if (anim) {
+		int bone = anim->findBone(_bones[num]._name);
+		if (bone != -1)
+			return anim->getTRS(bone, anim->curFrame2(), false);
+	}
+	return _bones[num]._trs;
+}
+
 void TeModel::setColor(const TeColor &col) {
 	Te3DObject2::setColor(col);
 	for (TeMesh &mesh : _meshes) {
@@ -99,9 +145,17 @@ void TeModel::removeAnim() {
 void TeModel::update() {
 	Common::Array<TeMatrix4x4> matricies;
 	matricies.resize(_bones.size());
-	for (const bone &b : _bones) {
-		//TeMatrix4x4 matrix = TeMatrix4x4::fomTRS(b._trs);
-		warning("TODO: Finish TeModel::update.");
+	for (unsigned int i = 0; i < _bones.size(); i++) {
+		const bone &b = _bones[i];
+		TeMatrix4x4 matrix = TeMatrix4x4::fromTRS(b._trs);
+		if (b._x == -1 || _bones.size() < 2) {
+			matricies[0] = matrix;
+		} else {
+			matricies[i] = matricies[b._x] * matrix;
+		}
+	}
+	if (_bones.size()) {
+		//warning("TODO: Finish TeModel::update. (disasm 190 ~ 697)");
 	}
 	for (TeMesh &mesh : _meshes) {
 		if (!_modelVertexAnim) {
@@ -128,14 +182,16 @@ _name(name), _amount(amount) {
 	_timer.start();
 }
 
-/*static*/ void TeModel::loadAlign(Common::SeekableReadStream &stream) {
+/*static*/
+void TeModel::loadAlign(Common::SeekableReadStream &stream) {
 	int64 pos = stream.pos();
 	if (pos % 4) {
 		stream.seek(4 - (pos % 4), SEEK_CUR);
 	}
 }
 
-/*static*/ void TeModel::saveAlign(Common::SeekableWriteStream &stream) {
+/*static*/
+void TeModel::saveAlign(Common::SeekableWriteStream &stream) {
 	int64 pos = stream.pos();
 	if (pos % 4) {
 		stream.seek(4 - (pos % 4), SEEK_CUR);
@@ -143,7 +199,10 @@ _name(name), _amount(amount) {
 }
 
 bool TeModel::load(Common::SeekableReadStream &stream) {
-	if (!loadAndCheckString(stream, "TEMD")) {
+	destroy();
+	create();
+
+	if (!loadAndCheckFourCC(stream, "TEMD")) {
 		error("[TeModel::load] Unknown format.");
 	}
 
@@ -162,7 +221,7 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 		_skipBoneMatricies = stream.readUint32LE();
 	}
 
-	if (!loadAndCheckString(stream, "SKEL")) {
+	if (!loadAndCheckFourCC(stream, "SKEL")) {
 		error("[TeModel::load] Unable to find skeleton.");
 	}
 
@@ -182,7 +241,7 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 		}
 	}
 
-	if (!loadAndCheckString(stream, "WEIG")) {
+	if (!loadAndCheckFourCC(stream, "WEIG")) {
 		error("[TeModel::load] Unable to load weight.");
 	}
 	for (unsigned int i = 0; i < _weightElements.size(); i++) {
@@ -204,7 +263,7 @@ bool TeModel::load(const Common::Path &path) {
 	}
 
 	bool retval;
-	if (loadAndCheckString(modelFile, "TEZ0")) {
+	if (loadAndCheckFourCC(modelFile, "TEZ0")) {
 		Common::SeekableReadStream *zlibStream = tryLoadZlibStream(modelFile);
 		if (!zlibStream)
 			return false;
@@ -233,7 +292,7 @@ Common::SeekableReadStream *TeModel::tryLoadZlibStream(Common::SeekableReadStrea
 	return Common::wrapCompressedReadStream(&substream, uncompressedSize);
 }
 
-bool TeModel::loadWeights(Common::ReadStream &stream, Common::Array<weightElement> weights) {
+bool TeModel::loadWeights(Common::ReadStream &stream, Common::Array<weightElement> &weights) {
 	uint32 nweights = stream.readUint32LE();
 	if (nweights > 100000)
 		error("Improbable number of weights %d", (int)nweights);
@@ -247,7 +306,7 @@ bool TeModel::loadWeights(Common::ReadStream &stream, Common::Array<weightElemen
 }
 
 bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
-	if (!loadAndCheckString(stream, "MESH"))
+	if (!loadAndCheckFourCC(stream, "MESH"))
 		return false;
 
 	uint32 vertcount = stream.readUint32LE();
@@ -265,7 +324,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	mesh.setName(Te3DObject2::deserializeString(stream));
 	loadAlign(stream);
 
-	if (!loadAndCheckString(stream, "MTRL"))
+	if (!loadAndCheckFourCC(stream, "MTRL"))
 		return false;
 
 	for (unsigned int i = 0; i < mesh.materials().size(); i++) {
@@ -276,7 +335,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 		mesh.attachMaterial(i, mat);
 	}
 
-	if (!loadAndCheckString(stream, "VERT"))
+	if (!loadAndCheckFourCC(stream, "VERT"))
 		return false;
 
 	for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
@@ -285,7 +344,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 		mesh.setVertex(i, v);
 	}
 	if (mesh.hasUvs()) {
-		if (!loadAndCheckString(stream, "TUVS"))
+		if (!loadAndCheckFourCC(stream, "TUVS"))
 			return false;
 		for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
 			TeVector2f32 v;
@@ -294,7 +353,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 		}
 	}
 
-	if (!loadAndCheckString(stream, "NORM"))
+	if (!loadAndCheckFourCC(stream, "NORM"))
 		return false;
 
 	for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
@@ -304,7 +363,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	}
 
 	if (mesh.hasColor()) {
-		if (!loadAndCheckString(stream, "COLS"))
+		if (!loadAndCheckFourCC(stream, "COLS"))
 			return false;
 
 		for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
@@ -314,7 +373,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 		}
 	}
 
-	if (!loadAndCheckString(stream, "FCPM"))
+	if (!loadAndCheckFourCC(stream, "FCPM"))
 		return false;
 
 	for (unsigned int i = 0; i < mesh.materials().size(); i++) {
@@ -322,7 +381,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	}
 
 	loadAlign(stream);
-	if (!loadAndCheckString(stream, "MTXI"))
+	if (!loadAndCheckFourCC(stream, "MTXI"))
 		return false;
 
 	for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
@@ -330,7 +389,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	}
 
 	loadAlign(stream);
-	if (!loadAndCheckString(stream, "IDXS"))
+	if (!loadAndCheckFourCC(stream, "IDXS"))
 		return false;
 
 	for (unsigned int i = 0; i < mesh.numIndexes(); i++) {
@@ -377,6 +436,11 @@ void TeModel::setAnim(TeIntrusivePtr<TeModelAnimation> &anim, bool repeat) {
 	_modelAnim = anim;
 }
 
+void TeModel::setVertexAnim(TeIntrusivePtr<TeModelVertexAnimation> &anim, bool repeat) {
+	anim->_repeatCount = (repeat ? -1 : 1);
+	_modelVertexAnim = anim;
+}
+
 void TeModel::setVisibleByName(const Common::String &name, bool vis) {
 	for (TeMesh &mesh : _meshes) {
 		if (mesh.name().contains(name)) {
@@ -385,14 +449,14 @@ void TeModel::setVisibleByName(const Common::String &name, bool vis) {
 	}
 }
 
-bool TeModel::loadAndCheckString(Common::ReadStream &stream, const char *str) {
-	char buf[5];
-	buf[4] = '\0';
-	stream.read(buf, 4);
-	return !strncmp(buf, str, 4);
+TeMatrix4x4 TeModel::skinOffset(unsigned long boneno) const {
+	if (boneno >= _boneMatrices.size())
+		return TeMatrix4x4();
+	return _boneMatrices[boneno];
 }
 
 TeModel::BonesBlender::BonesBlender(TeIntrusivePtr<TeModelAnimation> anim, float seconds) : _anim(anim), _seconds(seconds) {
+	_anim.setDeleteFn(&TeModelAnimation::deleteLater);
 	_timer.stop();
 	_timer.start();
 }
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index 74f17a3be64..532916e9c04 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -29,17 +29,18 @@
 #include "tetraedge/te/te_trs.h"
 #include "tetraedge/te/te_mesh.h"
 #include "tetraedge/te/te_model_animation.h"
+#include "tetraedge/te/te_model_vertex_animation.h"
 #include "tetraedge/te/te_tiled_texture.h"
 #include "tetraedge/te/te_intrusive_ptr.h"
 
 namespace Tetraedge {
 
+class TeModelVertexAnimation;
 class TeModelAnimation;
+class TeMesh;
 
 class TeModel : public Te3DObject2, public TeResource {
 public:
-	TeModel();
-
 	class BonesBlender {
 	public:
 		// Note: original takes a TeModel & but ignores it.
@@ -63,14 +64,18 @@ public:
 
 	struct bone {
 		Common::String _name;
-		unsigned short _x;
+		short _x;
 		TeTRS _trs;
 	};
+
 	struct weightElement {
 		float _w;
 		unsigned short _x;
 	};
 
+	TeModel();
+	virtual ~TeModel();
+
 	void addMesh(const TeMesh &mesh) {
 		_meshes.push_back(mesh);
 	}
@@ -83,8 +88,16 @@ public:
 
 	int checkFileType(Common::SeekableReadStream &instream);
 
+	void create();
+	void destroy();
+
 	void draw() override;
-	virtual void setColor(const TeColor &col) override;
+
+	int findModelBone(const Common::String &name);
+	int findOrAddWeights(const Common::Array<weightElement> &weights);
+	void forceMatrix(const TeMatrix4x4 &matrix);
+	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned long num);
+	TeMatrix4x4 lerpElementsMatrix(unsigned long num, Common::Array<TeMatrix4x4> &matricies);
 
 	/* Align the stream to the nearest 4 byte boudary*/
 	static void loadAlign(Common::SeekableReadStream &stream);
@@ -93,17 +106,26 @@ public:
 	bool load(const Common::Path &path);
 	bool load(Common::SeekableReadStream &stream);
 
-	bool loadWeights(Common::ReadStream &stream, Common::Array<weightElement> weights);
+	bool loadWeights(Common::ReadStream &stream, Common::Array<weightElement> &weights);
 	bool loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh);
 
+	void optimize();
 	void update();
 	void removeAnim();
-	void setVisibleByName(const Common::String &name, bool vis);
-	void setQuad(const TeIntrusivePtr<Te3DTexture> &tex, const Common::Array<TeVector3f32> &verts, const TeColor &col);
+
+	void saveBone(Common::SeekableWriteStream &stream, unsigned long boneno);
+	void saveMesh(Common::SeekableWriteStream &stream, const TeMesh &mesh);
+	void saveModel(Common::SeekableWriteStream &stream, unsigned int num);
+	void saveWeights(Common::SeekableWriteStream &stream, const Common::Array<weightElement> weights);
 
 	void setAnim(TeIntrusivePtr<TeModelAnimation> &anim, bool repeat);
+	virtual void setColor(const TeColor &col) override;
+	void setQuad(const TeIntrusivePtr<Te3DTexture> &tex, const Common::Array<TeVector3f32> &verts, const TeColor &col);
+	void setVertexAnim(TeIntrusivePtr<TeModelVertexAnimation> &anim, bool repeat);
+	void setVisibleByName(const Common::String &name, bool vis);
+
+	TeMatrix4x4 skinOffset(unsigned long boneno) const;
 
-	static bool loadAndCheckString(Common::ReadStream &stream, const char *str);
 	static Common::SeekableReadStream *tryLoadZlibStream(Common::SeekableReadStream &instr);
 	TeIntrusivePtr<TeTiledTexture> _tiledTexture;
 
@@ -114,6 +136,8 @@ public:
 	Common::Array<TeMesh> _meshes;
 
 protected:
+	bool _matrixForced;
+	TeMatrix4x4 _forcedMatrix;
 	Common::Array<MeshBlender *> _meshBlenders;
 	Common::Array<bone> _bones;
 	Common::Array<TeMatrix4x4> _boneMatrices;
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index f0565646660..8fad3f12fda 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -34,12 +34,12 @@ _curFrame(0), _curFrameValFresh(false), _repeatNum(0), _finishedSignalPending(fa
 _curFrame2(0), _useNMOArrays(0), _speed(0.0f) {
 }
 
-int TeModelAnimation::calcCurrentFrame(double proportion) {
+int TeModelAnimation::calcCurrentFrame(double millis) {
 	if (!_curFrameValFresh) {
 		int lastf = lastFrame();
 		int firstf = _firstFrame < 0 ? 0 : _firstFrame;
 
-		int curf = (proportion / 1000.0) * _speed;
+		int curf = (millis / 1000.0) * _speed;
 
 		curf = curf % ((lastf + 1) - firstf) + firstf;
 		if (!_dontRepeat && curf < _curFrame) {
@@ -175,7 +175,7 @@ bool TeModelAnimation::load(const Common::Path &path) {
 		return false;
 	}
 	bool retval;
-	if (TeModel::loadAndCheckString(modelFile, "TEZ0")) {
+	if (Te3DObject2::loadAndCheckFourCC(modelFile, "TEZ0")) {
 		Common::SeekableReadStream *zlibStream = TeModel::tryLoadZlibStream(modelFile);
 		if (!zlibStream)
 			return false;
@@ -190,7 +190,7 @@ bool TeModelAnimation::load(const Common::Path &path) {
 }
 
 bool TeModelAnimation::load(Common::SeekableReadStream &stream) {
-	if (!TeModel::loadAndCheckString(stream, "TEAN")) {
+	if (!Te3DObject2::loadAndCheckFourCC(stream, "TEAN")) {
 		warning("[TeModelAnimation::load] Unknown format.");
 		return false;
 	}
@@ -214,11 +214,11 @@ bool TeModelAnimation::load(Common::SeekableReadStream &stream) {
 
 	_speed = stream.readFloatLE();
 	for (unsigned int i = 0; i < numBones; i++) {
-		if (!TeModel::loadAndCheckString(stream, "BONE"))
+		if (!Te3DObject2::loadAndCheckFourCC(stream, "BONE"))
 			return false;
 		const Common::String boneName = Te3DObject2::deserializeString(stream);
 		setBoneName(i, boneName);
-		if (!TeModel::loadAndCheckString(stream, "BTRA"))
+		if (!Te3DObject2::loadAndCheckFourCC(stream, "BTRA"))
 			return false;
 		uint32 numTrans = stream.readUint32LE();
 		for (unsigned int j = 0; j < numTrans; j++) {
@@ -227,7 +227,7 @@ bool TeModelAnimation::load(Common::SeekableReadStream &stream) {
 			TeVector3f32::deserialize(stream, trans);
 			setTranslation(j, f, trans);
 		}
-		if (!TeModel::loadAndCheckString(stream, "BROT"))
+		if (!Te3DObject2::loadAndCheckFourCC(stream, "BROT"))
 			return false;
 		uint32 numRots = stream.readUint32LE();
 		for (unsigned int j = 0; j < numRots; j++) {
diff --git a/engines/tetraedge/te/te_model_animation.h b/engines/tetraedge/te/te_model_animation.h
index dff1540e2c4..b06a5c9b150 100644
--- a/engines/tetraedge/te/te_model_animation.h
+++ b/engines/tetraedge/te/te_model_animation.h
@@ -57,7 +57,7 @@ public:
 	void bind(const TeIntrusivePtr<TeModel> &ptr) {
 		_model = ptr;
 	};
-	int calcCurrentFrame(double proportion);
+	int calcCurrentFrame(double millis);
 	void cont() override;
 	void destroy();
 	int findBone(const Common::String &name);
@@ -87,6 +87,8 @@ public:
 	void unbind();
 	void update(double proportion) override;
 
+	int curFrame2() const { return _curFrame2; }
+
 	TeIntrusivePtr<TeModel> _model;
 	int _firstFrame;
 	int _lastFrame;
diff --git a/engines/tetraedge/te/te_model_vertex_animation.cpp b/engines/tetraedge/te/te_model_vertex_animation.cpp
index 305765ed6bd..cef7f635c47 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.cpp
+++ b/engines/tetraedge/te/te_model_vertex_animation.cpp
@@ -23,9 +23,84 @@
 
 namespace Tetraedge {
 
-TeModelVertexAnimation::TeModelVertexAnimation() {
+TeModelVertexAnimation::TeModelVertexAnimation() : _rot(1.0f, 0.0f, 0.0f, 0.0f),
+_lastMillis(0.0f), _modelAnim(nullptr) {
+	// TODO: set some other things up here.
+	_rot.fromAxisAndAngle(TeVector3f32(0.0f, 1.0f, 0.0f), -1.570796);
 }
 
-// TODO: Add more functions here.
+void TeModelVertexAnimation::bind(TeIntrusivePtr<TeModel> &model) {
+	_model = model;
+	_lastMillis = 0.0f;
+}
+
+void TeModelVertexAnimation::destroy() {
+	_keydata.clear();
+}
+
+TeVector3f32 TeModelVertexAnimation::getKeyVertex(unsigned long keyno, unsigned int vertexno) {
+	assert(keyno < _keydata.size());
+	const KeyData &data = _keydata[keyno];
+	assert(vertexno < data._vectors.size());
+	TeVector3f32 retval = data._vectors[vertexno];
+	if (!data._matricies.empty()) {
+		retval = data._matricies[vertexno] * retval;
+		retval.rotate(_rot);
+	}
+	return retval;
+}
+
+const Common::Array<TeVector3f32> &TeModelVertexAnimation::getVertices() {
+	static Common::Array<TeVector3f32> lerpVtx;
+
+	lerpVtx.clear();
+	if (_keydata.size() < 2)
+		return lerpVtx;
+
+	float frame = fmod((_lastMillis / 1000.0) * 30, _keydata[_keydata.size() - 1]._frame);
+	unsigned int keyno = 0;
+	while (keyno < _keydata.size() - 1 && _keydata[keyno]._frame < frame)
+		keyno++;
+
+	lerpVtx.resize(_keydata[0]._vectors.size());
+	float prevFrame = _keydata[keyno]._frame;
+	float nextFrame = _keydata[keyno + 1]._frame;
+	float interp = (frame - nextFrame) / (nextFrame - prevFrame);
+
+	for (unsigned int i = 0; i < _keydata[0]._vectors.size(); i++) {
+		const TeVector3f32 prevVector = getKeyVertex(keyno, i);
+		const TeVector3f32 nextVector = getKeyVertex(keyno + 1, i);
+		lerpVtx.push_back(prevVector * (1.0 - interp) + nextVector * interp);
+	}
+
+	return lerpVtx;
+}
+
+bool TeModelVertexAnimation::load(Common::ReadStream &stream) {
+	error("TODO: implement TeModelVertexAnimation::load");
+}
+
+void TeModelVertexAnimation::save(Common::WriteStream &stream) const {
+	error("TODO: implement TeModelVertexAnimation::save");
+}
+
+void TeModelVertexAnimation::update(double millis) {
+	if (_keydata.empty())
+		return;
+
+	double lastMillis = _lastMillis;
+	double lastFrame = fmod((lastMillis / 1000.0) * 30.0, _keydata.back()._frame);
+
+	if (_modelAnim) {
+		int frame = _modelAnim->calcCurrentFrame(millis);
+		millis = (frame * 1000.0) / 30.0;
+	}
+	_lastMillis = millis;
+	double thisFrame = fmod((millis / 1000.0) * 30.0, _keydata.back()._frame);
+	if (lastFrame > thisFrame)
+		_onFinishedSignal.call();
+}
+
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_model_vertex_animation.h b/engines/tetraedge/te/te_model_vertex_animation.h
index 6f0a5a809ec..9e3e7ce9e76 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.h
+++ b/engines/tetraedge/te/te_model_vertex_animation.h
@@ -19,37 +19,56 @@
  *
  */
 
+#ifndef TETRAEDGE_TE_TE_MODEL_VERTEX_ANIMATION_H
+#define TETRAEDGE_TE_TE_MODEL_VERTEX_ANIMATION_H
+
 #include "common/str.h"
 #include "common/array.h"
+#include "common/stream.h"
 
 #include "tetraedge/te/te_animation.h"
 #include "tetraedge/te/te_matrix4x4.h"
+#include "tetraedge/te/te_model.h"
+#include "tetraedge/te/te_intrusive_ptr.h"
 #include "tetraedge/te/te_resource.h"
 #include "tetraedge/te/te_vector3f32.h"
 
-#ifndef TETRAEDGE_TE_TE_MODEL_VERTEX_ANIMATION_H
-#define TETRAEDGE_TE_TE_MODEL_VERTEX_ANIMATION_H
-
 namespace Tetraedge {
 
+class TeModel;
+class TeModelAnimation;
+
 class TeModelVertexAnimation : public TeAnimation, public TeResource {
 public:
 	struct KeyData {
-		float _f;
+		float _frame;
 		Common::Array<TeVector3f32> _vectors;
 		Common::Array<TeMatrix4x4> _matricies;
 	};
 
 	TeModelVertexAnimation();
 
+	void bind(TeIntrusivePtr<TeModel> &model);
+	// void deleteLater() // original overrides this, but just calls the super.
+	void destroy();
+
 	const Common::String &head() const { return _head; }
+	TeVector3f32 getKeyVertex(unsigned long keyno, unsigned int vertexno);
+	const Common::Array<TeVector3f32> &getVertices();
+
+	bool load(Common::ReadStream &stream);
+	void save(Common::WriteStream &stream) const;
+
+	void update(double amount) override;
 
 private:
+	float _lastMillis;
+	TeIntrusivePtr<TeModel> _model;
+	TeModelAnimation *_modelAnim;
+	TeQuaternion _rot;
 	Common::String _head;
 	Common::Array<KeyData> _keydata;
 
-	// TODO add private members
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_obp.cpp b/engines/tetraedge/te/te_obp.cpp
index e86d4cdc667..613ec30323e 100644
--- a/engines/tetraedge/te/te_obp.cpp
+++ b/engines/tetraedge/te/te_obp.cpp
@@ -81,7 +81,7 @@ void TeOBP::setScale(const TeVector3f32 &scale) {
 	_boundsNeedUpdate = true;
 }
 
-void TeOBP::translate(const TeVector3f32 &offset)  {
+void TeOBP::translate(const TeVector3f32 &offset) {
 	Te3DObject2::translate(offset);
 	_boundsNeedUpdate = true;
 }
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 1621a035992..4b279900895 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -97,5 +97,24 @@ void TePickMesh2::setTriangle(unsigned long num, const TeVector3f32 &v1, const T
 	_verticies[num * 3 + 2] = v3;
 }
 
+/*static*/
+void TePickMesh2::serialize(Common::WriteStream &stream, const TePickMesh2 &mesh) {
+	error("TODO: Implement TePickMesh2::serialize");
+}
+
+/*static*/
+void TePickMesh2::deserialize(Common::ReadStream &stream, TePickMesh2 &mesh) {
+	Te3DObject2::deserialize(stream, mesh);
+	uint32 ntriangles = stream.readUint32LE();
+	mesh._verticies.resize(ntriangles * 3);
+	mesh._lastTriangleHit = 0;
+
+	for (unsigned int i = 0; i < ntriangles * 3; i++) {
+		TeVector3f32 vec;
+		TeVector3f32::deserialize(stream, vec);
+		mesh._verticies.push_back(vec);
+	}
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_pick_mesh2.h b/engines/tetraedge/te/te_pick_mesh2.h
index 5d04d10aac2..1e7c07e8bb5 100644
--- a/engines/tetraedge/te/te_pick_mesh2.h
+++ b/engines/tetraedge/te/te_pick_mesh2.h
@@ -50,6 +50,7 @@ public:
 	static void serialize(Common::WriteStream &stream, const TePickMesh2 &mesh);
 	static void deserialize(Common::ReadStream &stream, TePickMesh2 &mesh);
 
+	Common::Array<TeVector3f32> &verticies() { return _verticies; }
 private:
 	Common::Array<TeVector3f32> _verticies;
 	unsigned long _lastTriangleHit;
diff --git a/engines/tetraedge/te/te_scene.cpp b/engines/tetraedge/te/te_scene.cpp
index 3610bb6ce10..bfe9396c04d 100644
--- a/engines/tetraedge/te/te_scene.cpp
+++ b/engines/tetraedge/te/te_scene.cpp
@@ -23,7 +23,7 @@
 
 namespace Tetraedge {
 
-TeScene::TeScene() {
+TeScene::TeScene() : _currentCameraIndex(0) {
 }
 
 void TeScene::close() {
@@ -90,7 +90,7 @@ void TeScene::setCurrentCamera(const Common::String &name) {
 		}
 	}
 	if (i == n) {
-		warning("setCurrentCamera: Couldn't find camera %s", name.c_str());
+		//warning("TeScene::setCurrentCamera: Couldn't find camera %s", name.c_str());
 		return;
 	}
 	_currentCameraIndex = i;
diff --git a/engines/tetraedge/te/te_scene.h b/engines/tetraedge/te/te_scene.h
index 1a66565e33e..f01e5bace6e 100644
--- a/engines/tetraedge/te/te_scene.h
+++ b/engines/tetraedge/te/te_scene.h
@@ -47,7 +47,7 @@ public:
 	Common::String currentCameraName() const;
 
 	void draw();
-	virtual void load(Common::Path &path) {};
+	virtual bool load(const Common::Path &path) { return false; };
 
 	void removeModel(const Common::String &name);
 	void setCurrentCamera(const Common::String &name);
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index 816454c83e1..8de44498a49 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -139,6 +139,7 @@ void TeTextBase2::clearText() {
 }
 
 void TeTextBase2::computeNbSpaces(Line &line, unsigned int startOffset, unsigned int endOffset) {
+	// only needed if we implement Justify
 	error("TODO: Implement TeTextBase2::computeNbSpaces");
 }
 
@@ -292,8 +293,9 @@ void TeTextBase2::strikethrough(bool val) {
 		_strikethrough = val;
 		_valueWasSet = true;
 	}
-	if (val)
+	if (val) {
 		warning("TODO: Implement TeTextBase2::draw strikethrough support");
+	}
 }
 
 
diff --git a/engines/tetraedge/te/te_vector3f32.cpp b/engines/tetraedge/te/te_vector3f32.cpp
index 9acbf3366d4..ee7b2f62bf7 100644
--- a/engines/tetraedge/te/te_vector3f32.cpp
+++ b/engines/tetraedge/te/te_vector3f32.cpp
@@ -21,7 +21,9 @@
 
 #include "tetraedge/tetraedge.h"
 #include "common/str-array.h"
+#include "math/matrix4.h"
 #include "tetraedge/te/te_vector3f32.h"
+#include "tetraedge/te/te_quaternion.h"
 
 namespace Tetraedge {
 
@@ -35,5 +37,10 @@ bool TeVector3f32::parse(const Common::String &val) {
 	return true;
 }
 
+void TeVector3f32::rotate(const TeQuaternion &rot) {
+	Math::Matrix4 matrix = rot.toMatrix();
+	matrix.transform(this, false);
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_vector3f32.h b/engines/tetraedge/te/te_vector3f32.h
index fa5fbce8aae..e6070b91310 100644
--- a/engines/tetraedge/te/te_vector3f32.h
+++ b/engines/tetraedge/te/te_vector3f32.h
@@ -28,6 +28,8 @@
 
 namespace Tetraedge {
 
+class TeQuaternion;
+
 class TeVector3f32 : public Math::Vector3d {
 
 public:
@@ -42,6 +44,10 @@ public:
 		return *this;
 	}
 
+	//TeVector3f32 operator*(const TeVector3f32 &other) const {
+	//	return TeVector3f32(x() * other.x(), y() * other.y(), z() * other.z());
+	//}
+
 	explicit TeVector3f32(const TeVector2s32 &vec2d) {
 		set(vec2d._x, vec2d._y, 0.0);
 	}
@@ -68,16 +74,8 @@ public:
 	Common::String dump() const {
 		return Common::String::format("TeVector3f32(%.02f %.02f %.02f)", x(), y(), z());
 	}
-	/*
-	 TODO: do we need anything not already provided by Vector3d?
-public:
-	TeVector3f32();
 
-public:
-	float x;
-	float y;
-	float z;
-	 */
+	void rotate(const TeQuaternion &rot);
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_visual_fade.cpp b/engines/tetraedge/te/te_visual_fade.cpp
index dcfcd49adf8..3c085d84001 100644
--- a/engines/tetraedge/te/te_visual_fade.cpp
+++ b/engines/tetraedge/te/te_visual_fade.cpp
@@ -30,7 +30,7 @@ TeVisualFade::TeVisualFade() {
 }
 
 void TeVisualFade::animateBlackFade() {
-	error("TODO: Implement me.");
+	error("TODO: Implement TeVisualFade::animateBlackFade.");
 }
 
 void TeVisualFade::animateFade() {
diff --git a/engines/tetraedge/to_lua.cpp b/engines/tetraedge/to_lua.cpp
index 39eba13663a..a9ddd9c486c 100644
--- a/engines/tetraedge/to_lua.cpp
+++ b/engines/tetraedge/to_lua.cpp
@@ -163,13 +163,13 @@ static int tolua_bnd_type(lua_State *L) {
 }
 
 static void *tolua_clone(lua_State *L, void *dest, lua_CFunction fn) {
-  lua_pushstring(L, "tolua_gc");
-  lua_rawget(L, LUA_REGISTRYINDEX);
-  lua_pushlightuserdata(L, dest);
-  lua_pushcclosure(L, fn, 0);
-  lua_rawset(L,-3);
-  lua_settop(L,-2);
-  return dest;
+	lua_pushstring(L, "tolua_gc");
+	lua_rawget(L, LUA_REGISTRYINDEX);
+	lua_pushlightuserdata(L, dest);
+	lua_pushcclosure(L, fn, 0);
+	lua_rawset(L,-3);
+	lua_settop(L,-2);
+	return dest;
 }
 
 
@@ -182,13 +182,13 @@ static int tolua_bnd_takeownership(lua_State *L) {
 			if (lua_iscfunction(L,-1)) {
 				fn = lua_tocfunction(L, -1);
 			}
-	  lua_settop(L,-3);
-	  void *data = lua_touserdata(L, 1);
-	  tolua_clone(L, data, fn);
+			lua_settop(L,-3);
+			void *data = lua_touserdata(L, 1);
+			tolua_clone(L, data, fn);
+		}
 	}
-  }
-  lua_pushboolean(L, (uint)(fn != nullptr));
-  return 1;
+	lua_pushboolean(L, (uint)(fn != nullptr));
+	return 1;
 }
 
 static int tolua_bnd_releaseownership(lua_State *L) {
@@ -309,12 +309,12 @@ static int module_newindex_event(lua_State *L) {
 }
 
 static void tolua_moduleevents(lua_State *L) {
-  lua_pushstring(L, "__index");
-  lua_pushcclosure(L, module_index_event, 0);
-  lua_rawset(L, -3);
-  lua_pushstring(L, "__newindex");
-  lua_pushcclosure(L, module_newindex_event, 0);
-  lua_rawset(L, -3);
+	lua_pushstring(L, "__index");
+	lua_pushcclosure(L, module_index_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__newindex");
+	lua_pushcclosure(L, module_newindex_event, 0);
+	lua_rawset(L, -3);
 }
 
 static bool tolua_ismodulemetatable(lua_State *L) {
@@ -388,11 +388,11 @@ int tolua_isboolean(lua_State *L, int lo, int def, tolua_Error *err) {
 
 int tolua_isnoobj(lua_State *L, int lo, tolua_Error *err) {
 	if (lua_gettop(L) < abs(lo))
-          return 1;
+		return 1;
 	err->index = lo;
 	err->array = false;
 	err->type = "[no object]";
-   return 0;
+	return 0;
 }
 
 int tolua_isnumber(lua_State *L, int lo, int def, tolua_Error *err) {
@@ -423,7 +423,7 @@ double tolua_tonumber(lua_State *L, int narg, double def) {
 }
 
 const char *tolua_tostring(lua_State *L, int narg, const char *def) {
-   return lua_gettop(L) < abs(narg) ? def : lua_tostring(L, narg);
+	return lua_gettop(L) < abs(narg) ? def : lua_tostring(L, narg);
 }
 
 void *tolua_tousertype(lua_State *L, int narg, void* def) {


Commit: 681b4f8c555fd9ceae00beb7efb46aaad82bb4a3
    https://github.com/scummvm/scummvm/commit/681b4f8c555fd9ceae00beb7efb46aaad82bb4a3
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More WIP. First scene now loads, yay.

Changed paths:
  A engines/tetraedge/te/te_ray_intersection.cpp
  A engines/tetraedge/te/te_ray_intersection.h
  R engines/tetraedge/te/te_fee_move_zone.cpp
  R engines/tetraedge/te/te_fee_move_zone.h
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/application.h
    engines/tetraedge/game/character.h
    engines/tetraedge/game/character_settings_xml_parser.h
    engines/tetraedge/game/characters_shadow.cpp
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/documents_browser.cpp
    engines/tetraedge/game/documents_browser.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/help_option_menu.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/main_menu.cpp
    engines/tetraedge/game/main_menu.h
    engines/tetraedge/game/notifier.cpp
    engines/tetraedge/game/notifier.h
    engines/tetraedge/game/objectif.h
    engines/tetraedge/module.mk
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_3d_object2.h
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_animation.cpp
    engines/tetraedge/te/te_bezier_curve.h
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_button_layout.h
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_camera.h
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_light.h
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_lua_thread.cpp
    engines/tetraedge/te/te_lua_thread.h
    engines/tetraedge/te/te_matrix4x4.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_model_animation.h
    engines/tetraedge/te/te_obp.cpp
    engines/tetraedge/te/te_pick_mesh2.h
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_scene.cpp
    engines/tetraedge/te/te_scene.h
    engines/tetraedge/te/te_scrolling_layout.cpp
    engines/tetraedge/te/te_sprite_layout.h
    engines/tetraedge/te/te_text_layout.h
    engines/tetraedge/te/te_tiled_surface.h
    engines/tetraedge/te/te_timer.h
    engines/tetraedge/te/te_vector2s32.cpp
    engines/tetraedge/te/te_vector2s32.h
    engines/tetraedge/te/te_vector3f32.cpp
    engines/tetraedge/te/te_vector3f32.h
    engines/tetraedge/te/te_visual_fade.cpp
    engines/tetraedge/te/te_visual_fade.h
    engines/tetraedge/te/te_xml_gui.h


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 9345f2d11af..b985b63e921 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -60,7 +60,8 @@ _captureFade(false), _difficulty(1), _created(false), _tutoActivated(false) {
 
 	// TODO: Configure sound manager here?
 	// TODO: Configure freemium things here?
-	// TODO: Start some timer here?
+
+	// Note: original has an app run timer, but it's never used?
 
 	loadOptions("options.xml");
 }
@@ -72,6 +73,7 @@ void Application::create() {
 	const int winHeight = g_engine->getDefaultScreenHeight();
 	// See TeMainWindowBase::initCamera
 	_mainWindowCamera.reset(new TeCamera());
+	_mainWindowCamera->setName("_mainWinCam");
 	_mainWindowCamera->_projectionMatrixType = 4;
 	_mainWindowCamera->viewport(0, 0, winWidth, winHeight);
 	_mainWindowCamera->orthogonalParams(winWidth * -0.5f, winWidth * 0.5f, winHeight * 0.5f, winHeight * -0.5f);
@@ -234,6 +236,7 @@ void Application::create() {
 	_autoSaveIconAnim1.pause();
 	_autoSaveIconAnim1._startVal = TeColor(255, 255, 255, 0);
 	_autoSaveIconAnim1._endVal = TeColor(255, 255, 255, 255);
+	_autoSaveIconAnim1._repeatCount = -1;
 	Common::Array<float> curve;
 	curve.push_back(0.0f);
 	curve.push_back(1.0f);
@@ -256,12 +259,13 @@ void Application::create() {
 	_autoSaveIconAnim2.pause();
 	_autoSaveIconAnim2._startVal = TeColor(255, 255, 255, 0);
 	_autoSaveIconAnim2._endVal = TeColor(255, 255, 255, 255);
+	_autoSaveIconAnim2._repeatCount = 1;
 	_autoSaveIconAnim2.setCurve(curve);
 	_autoSaveIconAnim2._duration = 4000.0f;
 	_autoSaveIconAnim2._callbackObj = &_autoSaveIcon2;
 	_autoSaveIconAnim2._callbackMethod = &Te3DObject2::setColor;
 
-	_blackFadeAnimationFinishedSignal.add<Application>(this, &Application::onBlackFadeAnimationFinished);
+	_visFade.blackFadeCurveAnim().onFinished().add(this, &Application::onBlackFadeAnimationFinished);
 
 	g_engine->getInputMgr()->_mouseMoveSignal.add(this, &Application::onMousePositionChanged);
 
@@ -394,7 +398,6 @@ void Application::performRender() {
 
 	renderer->renderTransparentMeshes();
 	renderer->clearBuffer(GL_ACCUM);
-
 	if (game->running()) {
 		if (game->scene()._character
 			&& game->scene()._shadowLightNo != -1
@@ -403,7 +406,7 @@ void Application::performRender() {
 			if (currentCamera) {
 				currentCamera->apply();
 				renderer->shadowMode(TeRenderer::ShadowMode2);
-				game->scene()._charactersShadow->createTexture(&game->scene());
+				game->scene()._charactersShadow->draw(&game->scene());
 				renderer->shadowMode(TeRenderer::ShadowMode0);
 			}
 		}
@@ -438,10 +441,7 @@ void Application::captureFade() {
 }
 
 bool Application::isFading() {
-	warning("Application::isFading: Check field here.");
-	if (true /* field_0xb1e1? */)
-		return _visFade.fading();
-	return false;
+	return _visFade.blackFading() || _visFade.fading();
 }
 
 bool Application::onBlackFadeAnimationFinished() {
diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
index 7ffb34901eb..707642cba43 100644
--- a/engines/tetraedge/game/application.h
+++ b/engines/tetraedge/game/application.h
@@ -107,6 +107,7 @@ public:
 	TeLayout _frontOrientationLayout;
 	TeLayout _backLayout;
 	TeButtonLayout _lockCursorButton;
+	TeButtonLayout _lockCursorFromActionButton;
 	LocFile _loc;
 	Common::String _firstWarpPath;
 	Common::String _firstZone;
@@ -117,7 +118,6 @@ private:
 	TeMusic _music;
 	TeSpriteLayout _appSpriteLayout;
 	TeSpriteLayout _mouseCursorLayout;
-	TeButtonLayout _lockCursorFromActionButton;
 	TeSpriteLayout _autoSaveIcon1;
 	TeSpriteLayout _autoSaveIcon2;
 
@@ -129,8 +129,6 @@ private:
 	TeCurveAnim2<Te3DObject2, TeColor> _autoSaveIconAnim1;
 	TeCurveAnim2<Te3DObject2, TeColor> _autoSaveIconAnim2;
 
-	TeSignal0Param _blackFadeAnimationFinishedSignal;
-
 	Common::SharedPtr<TeCamera> _mainWindowCamera; // TODO: should be part of TeMainWindow.
 	TeLayout _mainWindow; // TODO: should be a specialised class.
 
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index dd0d9b13329..d71aa86c60a 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -67,8 +67,8 @@ public:
 
 		TeVector3f32 _cutSceneCurveDemiPosition;
 		Common::String _defaultEyes;	// Note: Engine supports more, but in practice only one ever used.
-		Common::String _defaultMouth; 	// Note: Engine supports more, but in practice only one ever used.
-		Common::String _defaultBody; 	// Note: Engine supports more, but in practice only one ever used.
+		Common::String _defaultMouth;	// Note: Engine supports more, but in practice only one ever used.
+		Common::String _defaultBody;	// Note: Engine supports more, but in practice only one ever used.
 
 		void clear();
 	};
@@ -150,15 +150,19 @@ public:
 	TeSignal1Param<const Common::String &> _onCharacterAnimFinishedSignal;
 
 	const CharacterSettings &characterSettings() const { return _characterSettings; }
-	const Common::String walkModeStr() const { return _walkModeStr; }
-	const Common::String curAnimName() const { return _curAnimName; }
+	const Common::String &walkModeStr() const { return _walkModeStr; }
+	const Common::String &curAnimName() const { return _curAnimName; }
+	TeFreeMoveZone *freeMoveZone() { return _freeMoveZone; }
+	const Common::String &freeMoveZoneName() const { return _freeMoveZoneName; }
 	bool needsSomeUpdate() const { return _needsSomeUpdate; }
+	void setNeedsSomeUpdate(bool val) { _needsSomeUpdate = val; }
 	void setCharLookingAt(Character *other) { _charLookingAt = other; }
 
 private:
 	float _curveOffset;
 	TeIntrusivePtr<TeBezierCurve> _curve;
 	TeFreeMoveZone *_freeMoveZone;
+	Common::String _freeMoveZoneName;
 	Common::String _stepSound1;
 	Common::String _stepSound2;
 	Common::String _walkModeStr; // Walk or Jog
diff --git a/engines/tetraedge/game/character_settings_xml_parser.h b/engines/tetraedge/game/character_settings_xml_parser.h
index 3d6ae90b288..1c15e95e0f9 100644
--- a/engines/tetraedge/game/character_settings_xml_parser.h
+++ b/engines/tetraedge/game/character_settings_xml_parser.h
@@ -94,8 +94,8 @@ public:
 	bool parserCallback_walk(ParserNode *node);
 	bool parserCallback_animationFileName(ParserNode *node);
 	bool parserCallback_walkType(ParserNode *node);
-	bool parserCallback_start(ParserNode *node);  	// walk anim
-	bool parserCallback_loop(ParserNode *node);	  	// walk anim
+	bool parserCallback_start(ParserNode *node);	// walk anim
+	bool parserCallback_loop(ParserNode *node);		// walk anim
 	bool parserCallback_endD(ParserNode *node);		// for walk anim
 	bool parserCallback_endG(ParserNode *node);		// for walk anim
 	bool parserCallback_speed(ParserNode *node);	// walk speed
@@ -122,7 +122,6 @@ private:
 	};
 
 	TextTagType _curTextTag;
-	// TODO add private members
 	Character::CharacterSettings *_curCharacter;
 	Character::WalkSettings *_curWalkSettings;
 	Common::HashMap<Common::String, Character::CharacterSettings> *_characterSettings;
diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
index 2ed2fba9c5b..92951817947 100644
--- a/engines/tetraedge/game/characters_shadow.cpp
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -22,6 +22,7 @@
 #include "graphics/opengl/system_headers.h"
 
 #include "tetraedge/tetraedge.h"
+#include "tetraedge/game/character.h"
 #include "tetraedge/game/characters_shadow.h"
 #include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_renderer.h"
@@ -42,7 +43,8 @@ void CharactersShadow::create(InGameScene *scene) {
 	renderer->enableTexture();
 	_camera = new TeCamera();
 	_camera->_projectionMatrixType = 2;
-	// TODO: set camera field 0x130 to 1.0?
+	_camera->_somePerspectiveVal = 1.0;
+	_camera->setName("_shadowCam");
 	_camera->viewport(0, 0, _texSize, _texSize);
 	Te3DTexture::unbind();
 	glGenTextures(1, &_glTex);
@@ -58,8 +60,33 @@ void CharactersShadow::create(InGameScene *scene) {
 void CharactersShadow::createTexture(InGameScene *scene) {
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->enableTexture();
-	//TeLight *light = scene->shadowLight();
-	error("TODO: Implement CharactersShadow::createTexture");
+	TeLight *light = scene->shadowLight();
+	if (light) {
+		TeQuaternion q1 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 1, 0), light->positionRadial().getX() - M_PI_2);
+		TeQuaternion q2 = TeQuaternion::fromAxisAndAngle(TeVector3f32(1, 0, 0), light->positionRadial().getY());
+		_camera->rotate(q2 * q1);
+		_camera->setPosition(light->position3d());
+	}
+	_camera->_fov = scene->shadowFov() * M_PI / 180.0;
+	_camera->_orthNearVal = scene->shadowNearPlane();
+	_camera->_orthFarVal = scene->shadowFarPlane();
+	_camera->apply();
+
+	glClearColor(0.0, 0.0, 0.0, 0.0);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+
+	for (Character *character : scene->_characters) {
+		character->_model->draw();
+	}
+	scene->_character->_model->draw();
+	Te3DTexture::unbind();
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _texSize, _texSize);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+	TeCamera::restore();
+	TeCamera::restore();
 }
 
 void CharactersShadow::destroy() {
@@ -76,7 +103,69 @@ void CharactersShadow::destroy() {
 }
 
 void CharactersShadow::draw(InGameScene *scene) {
-	error("TODO: Implement CharactersShadow::draw");
+	TeRenderer *renderer = g_engine->getRenderer();
+	glDepthMask(false);
+	renderer->disableZBuffer();
+	renderer->enableTexture();
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	Te3DTexture::unbind();
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	glEnable(GL_BLEND);
+	renderer->setCurrentColor(scene->shadowColor());
+
+	TeMatrix4x4 matrix;
+	matrix.translate(TeVector3f32(0.5f, 0.5f, 0.5f));
+	matrix.scale(TeVector3f32(0.5f, 0.5f, 0.5f));
+	matrix = matrix * _camera->projectionMatrix();
+
+	TeMatrix4x4 cammatrix = _camera->worldTransformationMatrix();
+	cammatrix.inverse();
+
+	matrix = matrix * cammatrix;
+
+	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+	float f[4];
+	for (unsigned int i = 0; i < 4; i++)
+		f[i] = matrix(i, 0);
+
+	glTexGenfv(GL_S, GL_EYE_PLANE, f);
+	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+	for (unsigned int i = 0; i < 4; i++)
+		f[i] = matrix(i, 1);
+
+	glTexGenfv(GL_T, GL_EYE_PLANE, f);
+	glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+	for (unsigned int i = 0; i < 4; i++)
+		f[i] = matrix(i, 2);
+
+	glTexGenfv(GL_R, GL_EYE_PLANE, f);
+	glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+	for (unsigned int i = 0; i < 4; i++)
+		f[i] = matrix(i, 3);
+
+	glTexGenfv(GL_Q, GL_EYE_PLANE, f);
+
+	Te3DTexture::unbind();
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	glEnable(GL_BLEND);
+	renderer->setCurrentColor(scene->shadowColor());
+
+	for (TeIntrusivePtr<TeModel> model : scene->models()) {
+		if (model->_meshes.size() > 0 && model->_meshes[0].materials().empty()) {
+			model->_meshes[0].defaultMaterial(TeIntrusivePtr<Te3DTexture>());
+			model->_meshes[0].materials()[0]._enableSomethingDefault0 = true;
+			model->_meshes[0].materials()[0]._diffuseColor = scene->shadowColor();
+		}
+		model->draw();
+	}
+
+	renderer->disableTexture();
+	glDepthMask(true);
+	renderer->enableZBuffer();
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 1d46d18f5b0..07847b0845d 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -99,9 +99,9 @@ bool Dialog2::onMinimumTimeTimer() {
 
 bool Dialog2::onSkipButton() {
 	const TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
-	if (!dialogAnimUp->_runTimer._stopped) {
+	if (dialogAnimUp->_runTimer.running()) {
 		const TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
-		if (dialogAnimDown->_runTimer._stopped) {
+		if (!dialogAnimDown->_runTimer.running()) {
 			startDownAnimation();
 			_music.stop();
 		}
@@ -110,7 +110,7 @@ bool Dialog2::onSkipButton() {
 }
 
 bool Dialog2::onSoundFinished() {
-	if (_minimumTimeTimer._stopped)
+	if (!_minimumTimeTimer.running())
 		startDownAnimation();
 	return false;
 }
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
index 5762275900f..e9f29b577a2 100644
--- a/engines/tetraedge/game/documents_browser.cpp
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -32,6 +32,10 @@ void DocumentsBrowser::enter() {
 	currentPage(_curPage);
 }
 
+void DocumentsBrowser::hideDocument() {
+	error("TODO: Implement DocumentsBrowser::hideDocument");
+}
+
 void DocumentsBrowser::leave() {
 	_timer.stop();
 	setVisible(false);
@@ -106,7 +110,9 @@ void DocumentsBrowser::showDocument(const Common::String &str, long n) {
 	error("TODO: Implement DocumentsBrowser::showDocument");
 }
 
-
-// TODO: Add more functions here.
+void DocumentsBrowser::unload() {
+	hideDocument();
+	error("TODO: Implement DocumentsBrowser::unload");
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/documents_browser.h b/engines/tetraedge/game/documents_browser.h
index 0331cb2cb28..f1d4818cb2d 100644
--- a/engines/tetraedge/game/documents_browser.h
+++ b/engines/tetraedge/game/documents_browser.h
@@ -88,7 +88,6 @@ private:
 	TeTimer _timer;
 	TeLayout _zoomedLayout;
 	unsigned long _curPage;
-	// TODO add private members
 	// TiXmlDocument _xmldoc;
 };
 
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 427a56abbf4..69a40880ee9 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -32,15 +32,19 @@
 #include "tetraedge/game/game_achievements.h"
 #include "tetraedge/game/lua_binds.h"
 #include "tetraedge/game/object3d.h"
+
+#include "tetraedge/te/te_camera.h"
 #include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_input_mgr.h"
+#include "tetraedge/te/te_ray_intersection.h"
 #include "tetraedge/te/te_variant.h"
 
 namespace Tetraedge {
 
 Game::Game() : _objectsTakenVal(0), _score(0), _entered(false), _gameLoadState(0),
 _noScaleLayout(nullptr), _noScaleLayout2(nullptr), _warped(false), _saveRequested(false),
-_firstInventory(true), _movePlayerCharacterDisabled(false) {
+_firstInventory(true), _movePlayerCharacterDisabled(false), _enteredFlag2(false),
+_luaShowOwnerError(false), _markersVisible(true), _running(false), _loadName("save.xml") {
 	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
 		_objectsTakenBits[i] = false;
 	}
@@ -135,8 +139,16 @@ void Game::addNoScaleChildren() {
 	_noScaleLayout->addChild(&_documentsBrowser.zoomedLayout());
 }
 
-void Game::addRandomSound(const Common::String &s1, const Common::String &s2, float f1, float f2) {
-	warning("TODO: Implemet Game::addRandomSound %s %s %f %f", s1.c_str(), s1.c_str(), f1, f2);
+void Game::addRandomSound(const Common::String &name, const Common::String &path, float f1, float f2) {
+	if (!_randomSounds.contains(name)) {
+		_randomSounds[name] = Common::Array<RandomSound*>();
+	}
+	RandomSound *randsound = new RandomSound();
+	randsound->_path = path;
+	randsound->_f1 = f1;
+	randsound->_f2 = f2;
+	randsound->_name = name;
+	_randomSounds[name].push_back(randsound);
 }
 
 void Game::addToBag(const Common::String &objname) {
@@ -169,6 +181,7 @@ void Game::addToScore(int score) {
 }
 
 bool Game::changeWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
+	debug("Game::changeWarp(%s, %s, %s)", zone.c_str(), scene.c_str(), fadeFlag ? "true" : "false");
 	Application *app = g_engine->getApplication();
 	if (fadeFlag) {
 		app->blackFade();
@@ -183,6 +196,7 @@ bool Game::changeWarp(const Common::String &zone, const Common::String &scene, b
 }
 
 bool Game::changeWarp2(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
+	debug("Game::changeWarp2(%s, %s, %s)", zone.c_str(), scene.c_str(), fadeFlag ? "true" : "false");
 	_warped = false;
 	_movePlayerCharacterDisabled = false;
 	_sceneCharacterVisibleFromLoad = false;
@@ -191,7 +205,7 @@ bool Game::changeWarp2(const Common::String &zone, const Common::String &scene,
 	luapath.joinInPlace(zone);
 	luapath.joinInPlace(scene);
 	luapath.joinInPlace("Logic");
-	luapath.appendInPlace(zone);
+	luapath.appendInPlace(scene);
 	luapath.appendInPlace(".lua");
 
 	if (Common::File::exists(luapath)) {
@@ -243,7 +257,7 @@ void Game::draw() {
 }
 
 void Game::enter(bool newgame) {
-	warning("TODO: Game::enter set field_0x42f0 true here");
+	_enteredFlag2 = true;
 	_entered = true;
 	_luaShowOwnerError = false;
 	_score = 0;
@@ -322,7 +336,7 @@ void Game::enter(bool newgame) {
 }
 
 /*static*/ TeI3DObject2 *Game::findLayoutByName(TeLayout *parent, const Common::String &name) {
-	error("TODO: Implement me - although maybe this is never used?");
+	error("TODO: Implement Game::findLayoutByName - although maybe never used?");
 }
 
 /*static*/ TeSpriteLayout *Game::findSpriteLayoutByName(TeLayout *parent, const Common::String &name) {
@@ -407,6 +421,7 @@ void Game::initScene(bool fade, const Common::String &scenePath) {
 }
 
 bool Game::initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
+	debug("Game::initWarp(%s, %s, %s)", zone.c_str(), scene.c_str(), fadeFlag ? "true" : "false");
 	_inventoryMenu.unload();
 	_gui4.unload();
 	_movePlayerCharacterDisabled = false;
@@ -465,8 +480,9 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	_scene.reset();
 	_scene.bgGui().unload();
-	_gui2.unload();
-	Common::Path geomPath(Common::String::format("scenes/%s/Geometry/%s.bin",
+	_scene.markerGui().unload();
+	_scene.hitObjectGui().unload();
+	Common::Path geomPath(Common::String::format("scenes/%s/Geometry%s.bin",
 												 zone.c_str(), zone.c_str()));
 	_scene.load(geomPath);
 	_scene.loadBackground(setLuaPath);
@@ -485,7 +501,18 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	if (intLuaExists) {
 		_scene.loadInteractions(intLuaPath);
-		warning("TODO: Game::initWarp: Finish interactions.");
+		TeLuaGUI::StringMap<TeButtonLayout *> &blayouts = _scene.hitObjectGui().buttonLayouts();
+		for (auto &entry : blayouts) {
+			HitObject *hobj = new HitObject();
+			TeButtonLayout *btn = entry._value;
+			hobj->_game = this;
+			hobj->_button = btn;
+			hobj->_name = btn->name();
+			btn->onMouseClickValidated().add(hobj, &HitObject::onValidated);
+			btn->onButtonChangedToStateDownSignal().add(hobj, &HitObject::onDown);
+			btn->onButtonChangedToStateUpSignal().add(hobj, &HitObject::onUp);
+			_gameHitObjects.push_back(hobj);
+		}
 	}
 
 	_inventoryMenu.load();
@@ -541,7 +568,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		if (_inventory.selectedObject().size()) {
 			_inventory.selectedObject(*_inventory.selectedInventoryObject());
 		}
-		_inventory.setVisible(true);
+		_inventory.setVisible(false);
 		_objectif.setVisibleObjectif(false);
 		_objectif.setVisibleButtonHelp(true);
 		_running = true;
@@ -561,13 +588,13 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	// Special hacks for certain scenes (don't blame me, original does this..)
 	if (scene == "14050") {
 		TeIntrusivePtr<TeCamera> curcamera = _scene.currentCamera();
-		const TeVector3f32 coords(1200.6f,-1937.5f,1544.1f);
+		const TeVector3f32 coords(1200.6f, -1937.5f, 1544.1f);
 		curcamera->setPosition(coords);
 	} else if (scene == "34610") {
 		TeIntrusivePtr<TeCamera> curcamera = _scene.currentCamera();
-		const TeVector3f32 coords(-328.243f,340.303f,-1342.84f);
+		const TeVector3f32 coords(-328.243f, 340.303f, -1342.84f);
 		curcamera->setPosition(coords);
-		const TeQuaternion rot(0.003194, 0.910923, -0.009496, -0.412389);
+		const TeQuaternion rot(0.003194f, 0.910923f, -0.009496f, -0.412389f);
 		curcamera->setRotation(rot);
 	}
 
@@ -582,15 +609,21 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		//for (auto & sound : _gameSounds) {
 		warning("TODO: Game::initWarp: Do game sound stuff here");
 	}
-	// TODO: Also do random sound stuff here.
+
+	for (auto &randsoundlist : _randomSounds) {
+		for (auto *randsound : randsoundlist._value) {
+			delete randsound;
+		}
+		randsoundlist._value.clear();
+	}
+	_randomSounds.clear();
 
 	_scene.initScroll();
 	return true;
 }
 
 bool Game::isDocumentOpened() {
-	TeLayout *layout = _documentsBrowser.layout("zoomed");
-	return layout->visible();
+	return _documentsBrowser.layoutChecked("zoomed")->visible();
 }
 
 bool Game::isMoviePlaying() {
@@ -600,17 +633,62 @@ bool Game::isMoviePlaying() {
 	return false;
 }
 
-bool Game::launchDialog(const Common::String &param_1, uint param_2, const Common::String &param_3,
-				  const Common::String &param_4, float param_5) {
-	error("TODO: Implemet Game::launchDialog");
+bool Game::launchDialog(const Common::String &param_1, uint param_2, const Common::String &charname,
+				  const Common::String &animfile, float param_5) {
+	error("TODO: Implemet Game::launchDialog %s %d %s %s %f", param_1.c_str(),
+			param_2, charname.c_str(), animfile.c_str(), param_5);
 }
 
 void Game::leave(bool flag) {
-	error("TODO: Implemet Game::leave");
+	if (!_enteredFlag2)
+		return;
+
+	deleteNoScale();
+	_entered = false;
+	_running = false;
+	_notifier.unload();
+	g_engine->getInputMgr()->_mouseLUpSignal.remove(this, &Game::onMouseClick);
+	_question2.unload();
+	_inventory.cellphone()->unload();
+	_dialog2.unload();
+	_inventory.unload();
+	_documentsBrowser.unload();
+	_inventoryMenu.unload();
+	_gui1.unload();
+	_scene.close();
+	_gui3.unload();
+	if (_scene._character) {
+		_scene._character->deleteAllCallback();
+		_scene._character->stop();
+		_scene.unloadCharacter(_scene._character->_model->name());
+	}
+	warning("TODO: Game::leave: clear game sounds");
+
+	for (auto *hitobj : _gameHitObjects) {
+		delete hitobj;
+	}
+	_gameHitObjects.clear();
+
+	// TODO: clear Game::HitObject tree here?
+
+	_luaContext.destroy();
+	_running = false;
+	_gui4.buttonLayoutChecked("skipVideoButton")->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
+	_gui4.buttonLayoutChecked("videoBackgroundButton")->onMouseClickValidated().remove(this, &Game::onLockVideoButtonValidated);
+	_gui4.spriteLayoutChecked("video")->_tiledSurfacePtr->_frameAnim.onFinished().remove(this, &Game::onSkipVideoButtonValidated);
+	_gui4.buttonLayoutChecked("inventoryButton")->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
+	_gui4.unload();
+	_playedTimer.stop();
+
+	Application *app = g_engine->getApplication();
+	app->_lockCursorButton.setVisible(false);
+	app->_lockCursorFromActionButton.setVisible(false);
+	// TODO: Set some inputmgr flag here?
+	Character::animCacheFreeAll();
 }
 
 bool Game::loadBackup(const Common::String &path) {
-	error("TODO: Implemet Game::loadBackup");
+	error("TODO: Implemet Game::loadBackup %s", path.c_str());
 }
 
 bool Game::loadCharacter(const Common::String &name) {
@@ -661,19 +739,19 @@ bool Game::onCallNumber(Common::String val) {
 }
 
 bool Game::onCharacterAnimationFinished(const Common::String &val) {
-	error("TODO: Implemet me");
+	error("TODO: Implemet Game::onCharacterAnimationFinished %s", val.c_str());
 }
 
 bool Game::onCharacterAnimationPlayerFinished(const Common::String &val) {
-	error("TODO: Implemet me");
+	error("TODO: Implemet Game::onCharacterAnimationPlayerFinished %s", val.c_str());
 }
 
 bool Game::onDialogFinished(const Common::String &val) {
-	error("TODO: Implemet me");
+	error("TODO: Implemet Game::onDialogFinished %s", val.c_str());
 }
 
 bool Game::onDisplacementFinished() {
-	error("TODO: Implemet me");
+	error("TODO: Implemet Game::onDisplacementFinished");
 }
 
 bool Game::onFinishedCheckBackup(bool result) {
@@ -718,6 +796,57 @@ bool Game::onMarkersVisible(TeCheckboxLayout::State state) {
 	return false;
 }
 
+static
+TePickMesh2 *findNearestMesh(TeIntrusivePtr<TeCamera> &camera, const TeVector2s32 &frompt,
+			Common::Array<TePickMesh2*> &pickMeshes, TeVector3f32 *outloc, bool lastHitFirst) {
+	TeVector3f32 locresult;
+	TePickMesh2 *nearest = nullptr;
+	float furthest = camera->_orthFarVal;
+	if (!pickMeshes.empty()) {
+		TeVector3f32 v1;
+		TeVector3f32 v2;
+		for (unsigned int i = 0; i < pickMeshes.size(); i++) {
+			TePickMesh2 *mesh = pickMeshes[i];
+			const TeMatrix4x4 transform = mesh->worldTransformationMatrix();
+			if (lastHitFirst) {
+				unsigned int tricount = mesh->verticies().size() / 3;
+				unsigned int vert = mesh->lastTriangleHit() * 3;
+				if (mesh->lastTriangleHit() >= tricount)
+					vert = 0;
+				const TeVector3f32 v3 = transform * mesh->verticies()[vert];
+				const TeVector3f32 v4 = transform * mesh->verticies()[vert + 1];
+				const TeVector3f32 v5 = transform * mesh->verticies()[vert + 2];
+				TeVector3f32 result;
+				float fresult;
+				int intresult = TeRayIntersection::intersect(v1, v2, v3, v4, v5, result, fresult);
+				if (intresult == 1 && fresult < furthest && fresult >= camera->_orthNearVal)
+					return mesh;
+			}
+			for (unsigned int tri = 0; tri < mesh->verticies().size() / 3; tri++) {
+				const TeVector3f32 v3 = transform * mesh->verticies()[tri * 3];
+				const TeVector3f32 v4 = transform * mesh->verticies()[tri * 3 + 1];
+				const TeVector3f32 v5 = transform * mesh->verticies()[tri * 3 + 1];
+				camera->getRay(frompt, v1, v2);
+				TeVector3f32 result;
+				float fresult;
+				int intresult = TeRayIntersection::intersect(v1, v2, v3, v4, v5, result, fresult);
+				if (intresult == 1 && fresult < furthest && fresult >= camera->_orthNearVal) {
+					mesh->setLastTriangleHit(tri);
+					locresult = result;
+					furthest = fresult;
+					nearest = mesh;
+					if (lastHitFirst)
+						break;
+				}
+			}
+		}
+	}
+	if (outloc) {
+		*outloc = locresult;
+	}
+	return nearest;
+}
+
 bool Game::onMouseClick(const Common::Point &pt) {
 	Application *app = g_engine->getApplication();
 
@@ -734,7 +863,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 		float xdist = pt.x / winSize.x() - lastMousePos._x / winSize.x();
 		float ydist = pt.y / winSize.y() - lastMousePos._y / winSize.y();
 		float sqrdist = xdist * xdist + ydist * ydist;
-		if (sqrdist > 0.0001 && (_walkTimer._stopped || _walkTimer.timeElapsed() > 300000.0
+		if (sqrdist > 0.0001 && (!_walkTimer.running() || _walkTimer.timeElapsed() > 300000.0
 						 || (_scene._character && _scene._character->walkModeStr() != "Walk"))) {
 			return false;
 			// Double-click, but already jogging
@@ -744,10 +873,79 @@ bool Game::onMouseClick(const Common::Point &pt) {
 	if (!app->_frontLayout.isMouseIn(pt))
 		return false;
 
+	Common::String nearestMeshName = "None";
 	TeIntrusivePtr<TeCamera> curCamera = _scene.currentCamera();
-	//TePickMesh2 *nearestMesh = findNearestMesh(curCamera, _scene._pickMeshes, nullptr, false);
+	Common::Array<TePickMesh2*> pickMeshes = _scene.pickMeshes();
+	TePickMesh2 *nearestMesh = findNearestMesh(curCamera, pt, pickMeshes, nullptr, false);
+	if (nearestMesh) {
+		nearestMeshName = nearestMesh->name();
+		_lastCharMoveMousePos = TeVector2s32(0, 0);
+	}
+
+	if (app->isLockCursor() || _movePlayerCharacterDisabled)
+		return false;
+	
+	Character *character = _scene._character;
+	const Common::String &charAnim = character->curAnimName();
+	
+	if (charAnim == character->characterSettings()._walkFileName
+		|| charAnim == character->walkAnim(Character::WalkPart_Start)
+		|| charAnim == character->walkAnim(Character::WalkPart_Loop)
+		|| charAnim == character->walkAnim(Character::WalkPart_EndD)
+		|| charAnim == character->walkAnim(Character::WalkPart_EndG)) {
+		_luaScript.execute("On");
+		if (!_scene.isObjectBlocking(nearestMeshName)) {
+			if (character->freeMoveZone()) {
+				TeVector3f32 charPos = character->_model->position();
+				TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(charPos, TeVector2s32(pt), 8.0, true);
+				if (curve) {
+					_scene.setCurve(curve);
+					// TODO: set character field_0x214 to TeVector3f32(0,0,0)
+					if (curve->controlPoints().size() == 1) {
+						character->endMove();
+					} else {
+						if (!_walkTimer.running() || _walkTimer.timeElapsed() > 300000) {
+							_walkTimer.stop();
+							_walkTimer.start();
+							character->walkMode("Walk");
+						} else {
+							// Note: original checks the timer elapsed again here.. why?
+							_walkTimer.stop();
+							character->walkMode("Jog");
+						}
+						character->placeOnCurve(curve);
+						character->setCurveOffset(0.0);
+						if (charAnim != character->walkAnim(Character::WalkPart_Start)) {
+							character->setAnimation(character->walkAnim(Character::WalkPart_Start), false, false, false, -1, 9999);
+						}
+						character->walkTo(1.0, false);
+						_sceneCharacterVisibleFromLoad = false;
+						_lastCharMoveMousePos = pt;
+					}
+				} else {
+					return false;
+				}
+			}
+			// TOOD: Finis this (line ~180 onward)
+			warning("TODO: Finish Game::onMouseClick");
+		}
+		TeVector3f32 lastPoint = _scene.curve()->controlPoints().back();
+		character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true, false, false, -1, 9999);
+		character->walkTo(1.0, false);
+		// TODO: Set app field field_0x910b
+		_posPlayer = lastPoint;
+	}
+	
+	if (!_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._walkFileName)) {
+		_lastCharMoveMousePos = TeVector2s32(0, 0);
+		_movePlayerCharacterDisabled = true;
+		// TODO: Set field 	0x4249 to false
+		if (nearestMesh) {
+			character->stop();
+			_luaScript.execute("OnWarpObjectHit", nearestMeshName);
+		}
+	}
 
-	warning("TODO: Finish Game::onMouseClick");
 	return false;
 }
 
@@ -786,6 +984,16 @@ bool Game::onMouseMove() {
 		return false;
 	}
 
+	// TODO: All the logic below is a bit suspicious and unfinished.
+	//
+	// The original game goes through a series of checks of mouseIn and
+	// visible before deciding whether to do a full search for a mouse
+	// cursor to apply.  But after all that, in practice, none of the
+	// mouse cursors above actually exist in the game data.
+	//
+	// So maybe all this is useless?
+
+#if ENABLE_CUSTOM_CURSOR_CHECKS
 	TeVector2s32 mouseLoc = g_engine->getInputMgr()->lastMousePos();
 	bool skipFullSearch = false;
 
@@ -798,29 +1006,37 @@ bool Game::onMouseMove() {
 			return false;
 		}
 	}
-	if (_dialog2.gui().layout("imgDialog")) {
-		warning("TODO: Finish Game::onMouseMove");
+	TeLayout *imgDialog = _dialog2.gui().layoutChecked("imgDialog");
+	bool mouseInImgDialog = imgDialog->isMouseIn(mouseLoc);
+	if (mouseInImgDialog || !imgDialog->visible()) {
+		if (!mouseInImgDialog)
+			skipFullSearch = true;
+		//warning("TODO: Finish Game::onMouseMove");
 	}
 
 	bool checkedCursor = false;
-	for (unsigned int i = 0; i < cellbg->childCount(); i++) {
-		TeLayout *childlayout = dynamic_cast<TeLayout *>(cellbg->child(i));
-		if (childlayout && childlayout->isMouseIn(mouseLoc) && childlayout->visible()) {
-			for (int i = 0; i < ARRAYSIZE(cursorsTable); i++) {
-				if (childlayout->name().contains(cursorsTable[i][0])) {
-					app->mouseCursorLayout().load(cursorsTable[i][1]);
-					if (Common::String(cursorsTable[i][0]).contains(".anim")) {
-						app->mouseCursorLayout()._tiledSurfacePtr->_frameAnim._loopCount = -1;
-						app->mouseCursorLayout()._tiledSurfacePtr->_frameAnim.play();
+	if (!skipFullSearch && _scene.gui2().loaded()) {
+		TeLayout *bglayout = _scene.gui2().layoutChecked("background");
+		for (unsigned int i = 0; i < bglayout->childCount(); i++) {
+			TeLayout *childlayout = dynamic_cast<TeLayout *>(bglayout->child(i));
+			if (childlayout && childlayout->isMouseIn(mouseLoc) && childlayout->visible()) {
+				for (int i = 0; i < ARRAYSIZE(cursorsTable); i++) {
+					if (childlayout->name().contains(cursorsTable[i][0])) {
+						app->mouseCursorLayout().load(cursorsTable[i][1]);
+						if (Common::String(cursorsTable[i][0]).contains(".anim")) {
+							app->mouseCursorLayout()._tiledSurfacePtr->_frameAnim._loopCount = -1;
+							app->mouseCursorLayout()._tiledSurfacePtr->_frameAnim.play();
+						}
 					}
 				}
+				checkedCursor = true;
 			}
-			checkedCursor = true;
 		}
 	}
 
 	if (!checkedCursor)
 		app->mouseCursorLayout().load(DEFAULT_CURSOR);
+#endif // ENABLE_CUSTOM_CURSOR_CHECKS
 	return false;
 }
 
@@ -848,7 +1064,7 @@ bool Game::onVideoFinished() {
 	video->setVisible(false);
 	_music.stop();
 	_running = true;
-	warning("TODO: Game::onVideoFinished: update yieldedCallbacks");
+	warning("TODO: Game::onVideoFinished: update yieldedCallbacks %s", video->_tiledSurfacePtr->path().toString().c_str());
 	_luaScript.execute("OnMovieFinished", video->_tiledSurfacePtr->path().toString());
 	app->fade();
 	return false;
@@ -892,7 +1108,11 @@ void Game::playMovie(const Common::String &vidPath, const Common::String &musicP
 }
 
 void Game::playRandomSound(const Common::String &name) {
-	error("TODO: Implemet Game::playRandomSound");
+	if (!_randomSounds.contains(name)) {
+		warning("Game::playRandomSound: can't find sound list %s", name.c_str());
+		return;
+    }
+    error("TODO: Implemet Game::playRandomSound");
 }
 
 void Game::playSound(const Common::String &name, int param_2, float param_3) {
@@ -1049,7 +1269,17 @@ void Game::update() {
 				if (_scene._character->needsSomeUpdate()) {
 					Game *game = g_engine->getGame();
 					game->_sceneCharacterVisibleFromLoad = false;
-					error("TODO: Game::update: Finish bit after permanentUpdate");
+					TeVector3f32 charPos = _scene._character->_model->position();
+					const Common::String charName = _scene._character->_model->name();
+					TeFreeMoveZone *zone = _scene.pathZone(_scene._character->freeMoveZoneName());
+					if (zone) {
+						TeIntrusivePtr<TeCamera> cam = _scene.currentCamera();
+						zone->setCamera(cam, false);
+						_scene._character->setFreeMoveZone(zone);
+						_scene.setPositionCharacter(charName, _scene._character->freeMoveZoneName(), charPos);
+						error("TODO: Game::update: Finish bit after permanentUpdate");
+					}
+					_scene._character->setNeedsSomeUpdate(false);
 				}
 
 				const Common::String &charAnim = _scene._character->curAnimName();
@@ -1087,4 +1317,31 @@ void Game::update() {
 	}
 }
 
+
+bool Game::HitObject::onChangeWarp() {
+	// Seems like this function is never used?
+	error("TODO: Implement Game::HitObject::onChangeWarp");
+	return false;
+}
+
+bool Game::HitObject::onDown() {
+	_game->luaScript().execute("OnButtonDown", _name);
+	// TODO: set this field _game->field_0x4249 = 1;
+	return false;
+}
+
+bool Game::HitObject::onUp() {
+	_game->luaScript().execute("OnButtonUp", _name);
+	// TODO: set this field _game->field_0x4249 = 1;
+	return false;
+}
+
+bool Game::HitObject::onValidated() {
+	if (!g_engine->getApplication()->isLockCursor()) {
+		_game->luaScript().execute("OnWarpObjectHit", _name);
+		// TODO: set this field _game->field_0x4249 = 1;
+	}
+	return false;
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index d65a9fc7d36..c7bf5dca547 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -46,12 +46,12 @@ public:
 	Game();
 
 	struct HitObject {
-		byte OnChangeWarp();
-		byte OnDown();
-		byte OnUp();
-		byte OnValidated();
+		bool onChangeWarp();
+		bool onDown();
+		bool onUp();
+		bool onValidated();
 		//byte OnVisible(); empty never used?
-		
+
 		Common::String _name;
 		Game *_game;
 		TeButtonLayout *_button;
@@ -60,10 +60,10 @@ public:
 	class RandomSound {
 	public:
 		Common::Path _path;
-		Common::String _str;
+		Common::String _name;
 		TeMusic _music;
-		float f1;
-		float f2;
+		float _f1;
+		float _f2;
 		byte onSoundFinished();
 	};
 
@@ -158,6 +158,7 @@ public:
 
 	bool _returnToMainMenu;
 	bool _firstInventory;
+	bool _movePlayerCharacterDisabled;
 
 	const Common::String &currentZone() { return _currentZone; }
 	const Common::String &currentScene() { return _currentScene; }
@@ -171,6 +172,7 @@ private:
 	bool _luaShowOwnerError;
 	bool _running;
 	bool _entered;
+	bool _enteredFlag2;
 
 	TeLuaGUI _gui1;
 	TeLuaGUI _gui2;
@@ -188,6 +190,7 @@ private:
 	static char **_objectsTakenIDs;
 
 	TeVector2s32 _previousMousePos;
+	TeVector2s32 _lastCharMoveMousePos;
 
 	Common::String _warpZone;
 	Common::String _warpScene;
@@ -203,8 +206,10 @@ private:
 	Common::String _loadName;
 
 	Common::Array<GameSound *> _gameSounds;
+	Common::Array<HitObject *> _gameHitObjects;
 
 	Common::HashMap<Common::String, bool> _unlockedArtwork;
+	Common::HashMap<Common::String, Common::Array<RandomSound*>> _randomSounds;
 
 	int _gameLoadState;
 
@@ -229,7 +234,6 @@ private:
 	bool _sceneCharacterVisibleFromLoad;
 	bool _markersVisible;
 	bool _saveRequested;
-	bool _movePlayerCharacterDisabled;
 
 	TeLayout *_noScaleLayout;
 	TeLayout *_noScaleLayout2;
diff --git a/engines/tetraedge/game/help_option_menu.cpp b/engines/tetraedge/game/help_option_menu.cpp
index 1c3cd972e1e..b1f890774c1 100644
--- a/engines/tetraedge/game/help_option_menu.cpp
+++ b/engines/tetraedge/game/help_option_menu.cpp
@@ -48,6 +48,4 @@ void HelpOptionMenu::leave() {
 	_entered = false;
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 9c88e98f9dc..2a89abd7afa 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -62,7 +62,7 @@ void InGameScene::draw() {
 	for (unsigned int i = 0; i < _lights.size(); i++)
 		_lights[i].update(i);
 
-	currentCamera()->restore();
+	TeCamera::restore();
 }
 
 void InGameScene::drawPath() {
@@ -72,9 +72,8 @@ void InGameScene::drawPath() {
 	currentCamera()->apply();
 	g_engine->getRenderer()->disableZBuffer();
 
-	warning("TODO: Do free move zones in InGameScene::drawPath");
-	//for (unsigned int i = 0; i < _freeMoveZones.size(); i++)
-	//	_freeMoveZones[i]->
+	for (unsigned int i = 0; i < _freeMoveZones.size(); i++)
+		_freeMoveZones[i]->draw();
 
 	g_engine->getRenderer()->enableZBuffer();
 }
@@ -116,8 +115,8 @@ void InGameScene::reset() {
 	freeSceneObjects();
 	_bgGui.unload();
 	unloadSpriteLayouts();
-	_gui2.unload();
-	_gui3.unload();
+	_markerGui.unload();
+	_hitObjectGui.unload();
 }
 
 void InGameScene::deleteAllCallback() {
@@ -215,6 +214,8 @@ bool InGameScene::load(const Common::Path &path) {
 		return false;
 
 	uint32 ncameras = scenefile.readUint32LE();
+	if (ncameras > 1024)
+		error("Improbable number of cameras %d", ncameras);
 	for (unsigned int i = 0; i < ncameras; i++) {
 		TeIntrusivePtr<TeCamera> cam = new TeCamera();
 		deserializeCam(scenefile, cam);
@@ -222,6 +223,8 @@ bool InGameScene::load(const Common::Path &path) {
 	}
 
 	uint32 nobjects = scenefile.readUint32LE();
+	if (nobjects > 1024)
+		error("Improbable number of objects %d", nobjects);
 	for (unsigned int i = 0; i < nobjects; i++) {
 		TeIntrusivePtr<TeModel> model = new TeModel();
 		const Common::String modelname = Te3DObject2::deserializeString(scenefile);
@@ -254,6 +257,8 @@ bool InGameScene::load(const Common::Path &path) {
 	}
 
 	uint32 nfreemovezones = scenefile.readUint32LE();
+	if (nfreemovezones > 1024)
+		error("Improbable number of free move zones %d", nfreemovezones);
 	for (unsigned int i = 0; i < nfreemovezones; i++) {
 		TeFreeMoveZone *zone = new TeFreeMoveZone();
 		TeFreeMoveZone::deserialize(scenefile, *zone, &_blockers, &_rectBlockers, &_actZones);
@@ -261,6 +266,8 @@ bool InGameScene::load(const Common::Path &path) {
 	}
 
 	uint32 ncurves = scenefile.readUint32LE();
+	if (ncurves > 1024)
+		error("Improbable number of curves %d", ncurves);
 	for (unsigned int i = 0; i < ncurves; i++) {
 		TeIntrusivePtr<TeBezierCurve> curve = new TeBezierCurve();
 		TeBezierCurve::deserialize(scenefile, *curve);
@@ -269,6 +276,8 @@ bool InGameScene::load(const Common::Path &path) {
 	}
 
 	uint32 ndummies = scenefile.readUint32LE();
+	if (ndummies > 1024)
+		error("Improbable number of dummies %d", ndummies);
 	for (unsigned int i = 0; i < ndummies; i++) {
 		InGameScene::Dummy dummy;
 		TeVector3f32 vec;
@@ -297,7 +306,12 @@ void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
 }
 
 void InGameScene::onMainWindowSizeChanged() {
-	error("TODO: Implement InGameScene::onMainWindowSizeChanged");
+	TeCamera *mainWinCam = g_engine->getApplication()->mainWindowCamera();
+	_viewportSize = mainWinCam->viewportSize();
+	Common::Array<TeIntrusivePtr<TeCamera>> &cams = cameras();
+	for (unsigned int i = 0; i < cams.size(); i++) {
+		cams[i]->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
+	}
 }
 
 bool InGameScene::loadLights(const Common::Path &path) {
@@ -326,10 +340,13 @@ bool InGameScene::loadCharacter(const Common::String &name) {
 void InGameScene::deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam) {
 	cam->_projectionMatrixType = 2;
 	cam->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
+	// load name/position/rotation/scale
 	Te3DObject2::deserialize(stream, *cam);
-	cam->_focalLenMaybe = stream.readFloatLE();
+	cam->_fov = stream.readFloatLE();
 	cam->_somePerspectiveVal = stream.readFloatLE();
 	cam->_orthNearVal = stream.readFloatLE();
+	// Original loads the val then ignores it and sets 3000.
+	stream.readFloatLE();
 	cam->_orthFarVal = 3000.0;
 }
 
@@ -413,6 +430,13 @@ bool InGameScene::findKate() {
 	return false;
 }
 
+TeLight *InGameScene::shadowLight() {
+	if (_shadowLightNo == -1) {
+		return nullptr;
+	}
+	return &_lights[_shadowLightNo];
+}
+
 static Common::Path _sceneFileNameBase() {
 	Game *game = g_engine->getGame();
 	Common::Path retval("scenes");
@@ -494,11 +518,11 @@ void InGameScene::loadBackground(const Common::Path &path) {
 }
 
 void InGameScene::loadInteractions(const Common::Path &path) {
-	_gui3.load(path);
+	_hitObjectGui.load(path);
 	TeLayout *bgbackground = _bgGui.layoutChecked("background");
 	Game *game = g_engine->getGame();
 	TeSpriteLayout *root = game->findSpriteLayoutByName(bgbackground, "root");
-	TeLayout *background = _gui3.layoutChecked("background");
+	TeLayout *background = _hitObjectGui.layoutChecked("background");
 	// TODO: For all TeButtonLayout childen of background, call
 	// setDoubleValidationProtectionEnabled(false)
 	// For now our button doesn't implement that.
@@ -517,6 +541,84 @@ void InGameScene::initScroll() {
 	_someScrollVector = TeVector2f32(0.5f, 0.0f);
 }
 
+bool InGameScene::isObjectBlocking(const Common::String &name) {
+	for (const Common::String &b: _blockingObjects) {
+		if (name == b)
+			return true;
+	}
+	return false;
+}
+
+void InGameScene::addMarker(const Common::String &name, const Common::String &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal) {
+	error("TODO: Implement InGameScene::addMarker");
+}
+
+void InGameScene::setVisibleMarker(const Common::String &markerName, bool val) {
+	if (!isMarker(markerName))
+		return;
+
+	error("TODO: Implement InGameScene::setVisibleMarker");
+}
+
+
+void InGameScene::deleteMarker(const Common::String &markerName) {
+	if (!isMarker(markerName))
+		return;
+
+	error("TODO: Implement InGameScene::deleteMarker");
+}
+
+
+bool InGameScene::isMarker(const Common::String &name) {
+	for (const TeMarker *marker : _markers) {
+		if (marker->_name == name)
+			return true;
+	}
+	return false;
+}
+
+TeFreeMoveZone *InGameScene::pathZone(const Common::String &name) {
+	for (TeFreeMoveZone *zone: _freeMoveZones) {
+		if (zone->name() == name)
+			return zone;
+	}
+	return nullptr;
+}
+
+void InGameScene::moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd) {
+	Character *c = character(charName);
+	if (c != nullptr && c != _character) {
+		Game *game = g_engine->getGame();
+		if (!game->_movePlayerCharacterDisabled) {
+			// TODO: c->field_0x214 = c->characterSettings()._cutSceneCurveDemiPosition;
+			TeIntrusivePtr<TeBezierCurve> crve = curve(curveName);
+			c->placeOnCurve(crve);
+			c->setCurveOffset(curveOffset);
+			const Common::String walkStartAnim = c->walkAnim(Character::WalkPart_Start);
+			if (walkStartAnim.empty()) {
+				c->setAnimation(c->walkAnim(Character::WalkPart_Loop), true, false, false, -1, 9999);
+			} else {
+				c->setAnimation(c->walkAnim(Character::WalkPart_Start), false, false, false, -1, 9999);
+			}
+			c->walkTo(curveEnd, false);
+			error("TODO: Finish InGameScene::moveCharacterTo");
+		}
+	}
+}
+
+TeIntrusivePtr<TeBezierCurve> InGameScene::curve(const Common::String &curveName) {
+	for (TeIntrusivePtr<TeBezierCurve> &c : _bezierCurves) {
+		if (c->name() == curveName)
+			return c;
+	}
+	return TeIntrusivePtr<TeBezierCurve>();
+}
+
+void InGameScene::setPositionCharacter(const Common::String &charName, const Common::String &freeMoveZoneName, const TeVector3f32 &position) {
+	error("TODO: Implement InGameScene::setPositionCharacter");
+}
+
+
 bool InGameScene::AnimObject::onFinished() {
 	error("TODO: Implement InGameScene::AnimObject::onFinished");
 }
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index ec557257632..e18559207c8 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -69,7 +69,9 @@ public:
 		Common::String _name;
 	};
 
-	class TeMarker {
+	struct TeMarker {
+		Common::String _name;
+		Common::String _val;
 	};
 
 	struct Dummy {
@@ -85,7 +87,7 @@ public:
 		_blockingObjects.push_back(obj);
 	}
 	void addCallbackAnimation2D(const Common::String &param_1, const Common::String &param_2, float param_3);
-	void addMarker(const Common::String &name, const Common::String &param_2, float param_3, float param_4, const Common::String &param_5, const Common::String &param_6);
+	void addMarker(const Common::String &name, const Common::String &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal);
 	static float angularDistance(float a1, float a2);
 	bool aroundAnchorZone(const AnchorZone *zone);
 	TeLayout *background();
@@ -93,6 +95,14 @@ public:
 	void loadBackground(const Common::Path &path);
 	void loadInteractions(const Common::Path &path);
 	void initScroll();
+	bool isObjectBlocking(const Common::String &name);
+	bool isMarker(const Common::String &name);
+	TeFreeMoveZone *pathZone(const Common::String &name);
+	void moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd);
+	TeIntrusivePtr<TeBezierCurve> curve(const Common::String &curveName);
+	void setPositionCharacter(const Common::String &charName, const Common::String &freeMoveZoneName, const TeVector3f32 &position);
+	void setVisibleMarker(const Common::String &markerName, bool val);
+	void deleteMarker(const Common::String &markerName);
 
 	void draw();
 	void drawPath();
@@ -120,6 +130,7 @@ public:
 	void deleteAllCallback();
 
 	void setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2);
+	TeLight *shadowLight();
 
 	Common::Path getActZoneFileName() const;
 	Common::Path getBlockersFileName() const;
@@ -134,9 +145,20 @@ public:
 	Common::Array<Character *> _characters;
 
 	TeLuaGUI &bgGui() { return _bgGui; }
+	TeLuaGUI &hitObjectGui() { return _hitObjectGui; }
+	TeLuaGUI &markerGui() { return _markerGui; }
+
+	Common::Array<TePickMesh2 *> &pickMeshes() { return _pickMeshes; }
+
+	float shadowFarPlane() const { return _shadowFarPlane; }
+	float shadowNearPlane() const { return _shadowNearPlane; }
+	float shadowFov() const { return _shadowFov; }
+	const TeColor &shadowColor() const { return _shadowColor; }
 
 	int _shadowLightNo;
 	CharactersShadow *_charactersShadow;
+	TeIntrusivePtr<TeBezierCurve> curve() { return _curve; }
+	void setCurve(TeIntrusivePtr<TeBezierCurve> &c) { c = _curve; }
 
 private:
 	TeColor _shadowColor;
@@ -154,6 +176,7 @@ private:
 	Common::Array<Object3D *> _object3Ds;
 	Common::Array<Billboard *> _billboards;
 	Common::Array<TeSpriteLayout *> _sprites;
+	Common::Array<TePickMesh2 *> _pickMeshes;
 
 	Common::HashMap<Common::String, SoundStep> _soundSteps;
 
@@ -163,10 +186,11 @@ private:
 	Common::Array<Dummy> _dummies;
 
 	TeIntrusivePtr<TeModel> _playerCharacterModel;
+	TeIntrusivePtr<TeBezierCurve> _curve;
 	Common::Array<Common::String> _blockingObjects;
 	TeLuaGUI _bgGui;
-	TeLuaGUI _gui2; // TODO: find a better name.
-	TeLuaGUI _gui3; // TODO: find a better name.
+	TeLuaGUI _markerGui;
+	TeLuaGUI _hitObjectGui;
 
 	Common::Array<TeLight> _lights;
 
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index ee1be431277..79fcacffb71 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -227,7 +227,7 @@ bool Inventory::onMainMenuButton() {
 
 bool Inventory::onObjectSelected(InventoryObject &obj) {
 	selectedObject(obj);
-	if (!_selectedTimer._stopped) {
+	if (_selectedTimer.running()) {
 		if (_selectedTimer.timeElapsed() < 300000)
 			g_engine->getGame()->inventoryMenu().leave();
 	} else {
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 4a38a714a58..462411909e5 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -22,6 +22,7 @@
 #include "tetraedge/tetraedge.h"
 
 #include "tetraedge/game/application.h"
+#include "tetraedge/game/character.h"
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/lua_binds.h"
 #include "tetraedge/to_lua.h"
@@ -161,6 +162,105 @@ static int tolua_ExportedFunctions_ChangeWarp00(lua_State *L) {
 	error("#ferror in function 'ChangeWarp': %d %d %s", err.index, err.array, err.type);
 }
 
+static void SetCharacterPlayerVisible(bool val) {
+	Game *game = g_engine->getGame();
+	game->scene()._character->_model->setVisible(val);
+}
+
+static int tolua_ExportedFunctions_SetCharacterPlayerVisible00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isboolean(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		SetCharacterPlayerVisible(tolua_toboolean(L, 1, 0));
+		return 0;
+	}
+	error("#ferror in function 'SetCharacterPlayerVisible': %d %d %s", err.index, err.array, err.type);
+}
+
+static void MoveCharacterPlayerDisabled(bool val) {
+	Game *game = g_engine->getGame();
+	game->_movePlayerCharacterDisabled = val;
+}
+
+static int tolua_ExportedFunctions_MoveCharacterPlayerDisabled00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isboolean(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		MoveCharacterPlayerDisabled(tolua_toboolean(L, 1, 0));
+		return 0;
+	}
+	error("#ferror in function 'MoveCharacterPlayerDisabled': %d %d %s", err.index, err.array, err.type);
+}
+
+static void AddMarker(const Common::String &markerName, const Common::String &imgPath, float x, float y,
+				const Common::String &loctype, const Common::String &markerVal) {
+	Game *game = g_engine->getGame();
+	game->scene().addMarker(markerName, imgPath, x, y, loctype, markerVal);
+}
+
+static int tolua_ExportedFunctions_AddMarker00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+			&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+			&& tolua_isstring(L, 5, 1, &err) && tolua_isstring(L, 6, 1, &err)
+			&& tolua_isnoobj(L, 7, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		double n1 = tolua_tonumber(L, 3, 0.0);
+		double n2 = tolua_tonumber(L, 4, 0.0);
+		Common::String s3(tolua_tostring(L, 5, ""));
+		Common::String s4(tolua_tostring(L, 6, ""));
+		AddMarker(s1, s2, n1, n2, s3, s4);
+		return 0;
+	}
+	error("#ferror in function 'AddMarker': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetVisibleMarker(const Common::String &markerName, bool val) {
+	Game *game = g_engine->getGame();
+	game->scene().setVisibleMarker(markerName, val);
+}
+
+static int tolua_ExportedFunctions_SetVisibleMarker00(lua_State *L) {
+tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s(tolua_tostring(L, 1, nullptr));
+		bool b = tolua_toboolean(L, 1, 0);
+		SetVisibleMarker(s, b);
+		return 0;
+	}
+	error("#ferror in function 'SetVisibleMarker': %d %d %s", err.index, err.array, err.type);
+}
+
+static void DeleteMarker(const Common::String &markerName) {
+	Game *game = g_engine->getGame();
+	game->scene().deleteMarker(markerName);
+}
+
+static int tolua_ExportedFunctions_DeleteMarker00(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));
+		DeleteMarker(s1);
+		return 0;
+	}
+	error("#ferror in function 'DeleteMarker': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetVisibleCellphone(bool visible) {
+	Game *game = g_engine->getGame();
+	game->inventory().cellphone()->setVisible(visible);
+}
+
+static int tolua_ExportedFunctions_SetVisibleCellphone00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isboolean(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		SetVisibleCellphone(tolua_toboolean(L, 1, 0));
+		return 0;
+	}
+	error("#ferror in function 'SetVisibleCellphone': %d %d %s", err.index, err.array, err.type);
+}
+
+
+// ////////////////////////////////////////////////////////////////////////
 
 void LuaOpenBinds(lua_State *L) {
 	tolua_open(L);
@@ -184,12 +284,12 @@ void LuaOpenBinds(lua_State *L) {
 				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00);
 	tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00);
 	tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
-	tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);
+	tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);*/
 	tolua_function(L, "AddMarker", tolua_ExportedFunctions_AddMarker00);
 	tolua_function(L, "SetVisibleMarker", tolua_ExportedFunctions_SetVisibleMarker00);
 	tolua_function(L, "DeleteMarker", tolua_ExportedFunctions_DeleteMarker00);
 	tolua_function(L, "SetVisibleCellphone", tolua_ExportedFunctions_SetVisibleCellphone00);
-	tolua_function(L, "DisabledZone", tolua_ExportedFunctions_DisabledZone00);
+	/*tolua_function(L, "DisabledZone", tolua_ExportedFunctions_DisabledZone00);
 	tolua_function(L, "DisabledInt", tolua_ExportedFunctions_DisabledInt00);
 	tolua_function(L, "LockCursor", tolua_ExportedFunctions_LockCursor00);
 	tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00);
@@ -243,11 +343,11 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "BlendCharacterAnimation", tolua_ExportedFunctions_BlendCharacterAnimation00);
 	tolua_function(L, "BlendCharacterAnimationAndWaitForEnd",
 				 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);
-	tolua_function(L, "CurrentCharacterAnimation", tolua_ExportedFunctions_CurrentCharacterAnimation00);
+	tolua_function(L, "CurrentCharacterAnimation", tolua_ExportedFunctions_CurrentCharacterAnimation00);*/
 	tolua_function(L, "SetCharacterPlayerVisible", tolua_ExportedFunctions_SetCharacterPlayerVisible00);
 	tolua_function(L, "MoveCharacterPlayerDisabled",
 				 tolua_ExportedFunctions_MoveCharacterPlayerDisabled00);
-	tolua_function(L, "SetRunMode", tolua_ExportedFunctions_SetRunMode00);
+	/*tolua_function(L, "SetRunMode", tolua_ExportedFunctions_SetRunMode00);
 	tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200);
 	tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00);
 	tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00);
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
index 7fb93fbd6c1..e2adbfb54f8 100644
--- a/engines/tetraedge/game/main_menu.cpp
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -52,7 +52,7 @@ void MainMenu::enter() {
 	Application *app = g_engine->getApplication();
 	TeSpriteLayout &appSpriteLayout = app->appSpriteLayout();
 	appSpriteLayout.setVisible(true);
-	if (appSpriteLayout._tiledSurfacePtr->_frameAnim._runTimer._stopped) {
+	if (!appSpriteLayout._tiledSurfacePtr->_frameAnim._runTimer.running()) {
 		appSpriteLayout.load("menus/menu.ogv");
 		appSpriteLayout._tiledSurfacePtr->_frameAnim._loopCount = -1;
 		appSpriteLayout._tiledSurfacePtr->play();
diff --git a/engines/tetraedge/game/main_menu.h b/engines/tetraedge/game/main_menu.h
index aefc03d2b83..81708ecfe89 100644
--- a/engines/tetraedge/game/main_menu.h
+++ b/engines/tetraedge/game/main_menu.h
@@ -72,7 +72,6 @@ private:
 	Confirm _tutoConfirm;
 	Confirm _quitConfirm;
 
-	// TODO add private members
 	TeSignal0Param onFacebookLoggedSignal;
 
 	bool _entered;
diff --git a/engines/tetraedge/game/notifier.cpp b/engines/tetraedge/game/notifier.cpp
index e24ee2a2c09..bd5085f2123 100644
--- a/engines/tetraedge/game/notifier.cpp
+++ b/engines/tetraedge/game/notifier.cpp
@@ -32,14 +32,14 @@ Notifier::Notifier() {
 void Notifier::launchNextnotifier() {
 	TeCurveAnim2<Te3DObject2, TeColor> *colorAnim = _gui.colorLinearAnimation("fadeIn");
 	assert(colorAnim);
-	if (!colorAnim->_runTimer._stopped)
+	if (colorAnim->_runTimer.running())
 		return;
 
 	colorAnim = _gui.colorLinearAnimation("fadeOut");
-	if (!colorAnim->_runTimer._stopped) {
+	if (!colorAnim->_runTimer.running()) {
 		colorAnim = _gui.colorLinearAnimation("visible");
 		bool abort = true;
-		if (!colorAnim->_runTimer._stopped) {
+		if (!colorAnim->_runTimer.running()) {
 			abort = _notifierDataArray.empty();
 		}
 		if (abort)
diff --git a/engines/tetraedge/game/notifier.h b/engines/tetraedge/game/notifier.h
index dcd1d70bc8f..afcea1aa2f2 100644
--- a/engines/tetraedge/game/notifier.h
+++ b/engines/tetraedge/game/notifier.h
@@ -48,7 +48,6 @@ private:
 	};
 	Common::Array<notifierData> _notifierDataArray;
 	TeLuaGUI _gui;
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/game/objectif.h b/engines/tetraedge/game/objectif.h
index 2584462d2fe..9e71169ef02 100644
--- a/engines/tetraedge/game/objectif.h
+++ b/engines/tetraedge/game/objectif.h
@@ -50,7 +50,6 @@ public:
 	// void save()
 	void setVisibleButtonHelp(bool visible);
 	void setVisibleObjectif(bool visible);
-	// TODO add public members
 	void unload();
 	void update();
 
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index 2908af876e3..c276c075ceb 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -53,7 +53,6 @@ MODULE_OBJS := \
 	te/te_color.o \
 	te/te_core.o \
 	te/te_extended_text_layout.o \
-	te/te_fee_move_zone.o \
 	te/te_font3.o \
 	te/te_frame_anim.o \
 	te/te_free_move_zone.o \
@@ -87,6 +86,7 @@ MODULE_OBJS := \
 	te/te_pick_mesh2.o \
 	te/te_png.o \
 	te/te_quaternion.o \
+	te/te_ray_intersection.o \
 	te/te_real_timer.o \
 	te/te_renderer.o \
 	te/te_resource.o \
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index f1830449f1a..8c1ecb8cad0 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -87,23 +87,6 @@ long Te3DObject2::childIndex(Te3DObject2 *c) const {
 	return -1;
 }
 
-/*static*/
-Common::String Te3DObject2::deserializeString(Common::ReadStream &stream) {
-	uint slen = stream.readUint32LE();
-	if (slen > 1024 * 1024)
-		error("Improbable string size %d", slen);
-
-	if (slen) {
-		char *buf = new char[slen + 1];
-		buf[slen] = '\0';
-		stream.read(buf, slen);
-		Common::String str(buf);
-		delete[] buf;
-		return str;
-	}
-	return Common::String();
-}
-
 /*static*/
 void Te3DObject2::deserialize(Common::ReadStream &stream, Te3DObject2 &dest) {
 	Common::String str = deserializeString(stream);
@@ -329,4 +312,42 @@ bool Te3DObject2::loadAndCheckFourCC(Common::ReadStream &stream, const char *str
 	return !strncmp(buf, str, 4);
 }
 
+/*static*/
+Common::String Te3DObject2::deserializeString(Common::ReadStream &stream) {
+	uint slen = stream.readUint32LE();
+	if (slen > 1024 * 1024)
+		error("Improbable string size %d", slen);
+
+	if (slen) {
+		char *buf = new char[slen + 1];
+		buf[slen] = '\0';
+		stream.read(buf, slen);
+		Common::String str(buf);
+		delete[] buf;
+		return str;
+	}
+	return Common::String();
+}
+
+/*static*/
+void Te3DObject2::deserializeVectorArray(Common::ReadStream &stream, Common::Array<TeVector3f32> &dest) {
+	uint32 nentries = stream.readUint32LE();
+	if (nentries > 1000000)
+		error("TeFreeMoveZone improbable number of vectors %d", nentries);
+	dest.resize(nentries);
+	for (unsigned int i = 0; i < nentries; i++)
+		TeVector3f32::deserialize(stream, dest[i]);
+}
+
+/*static*/
+void Te3DObject2::deserializeUintArray(Common::ReadStream &stream, Common::Array<unsigned int> &dest) {
+	uint32 nentries = stream.readUint32LE();
+	if (nentries > 1000000)
+		error("TeFreeMoveZone improbable number of ints %d", nentries);
+	dest.resize(nentries);
+	for (unsigned int i = 0; i < nentries; i++)
+		dest[i] = stream.readUint32LE();
+}
+
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_3d_object2.h b/engines/tetraedge/te/te_3d_object2.h
index 54fd905d8eb..3c5e090888a 100644
--- a/engines/tetraedge/te/te_3d_object2.h
+++ b/engines/tetraedge/te/te_3d_object2.h
@@ -66,7 +66,6 @@ public:
 
 	static void deserialize(Common::ReadStream &stream, Te3DObject2 &dest);
 	static void serialize(Common::WriteStream &stream, Te3DObject2 &src);
-	static Common::String deserializeString(Common::ReadStream &stream);
 
 	virtual void draw() {}
 	const Common::String &name() const {
@@ -143,6 +142,9 @@ public:
 	virtual float zSize() { return _size.z(); };
 
 	static bool loadAndCheckFourCC(Common::ReadStream &stream, const char *str);
+	static Common::String deserializeString(Common::ReadStream &stream);
+	static void deserializeVectorArray(Common::ReadStream &stream, Common::Array<TeVector3f32> &dest);
+	static void deserializeUintArray(Common::ReadStream &stream, Common::Array<unsigned int> &dest);
 
 protected:
 	TeVector3f32 _size;
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index 6680bf37559..ca8f6ae00f3 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -268,6 +268,4 @@ void Te3DTexture::update(const TeImage &img, uint xoff, uint yoff) {
 	return;
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_animation.cpp b/engines/tetraedge/te/te_animation.cpp
index 7c5a8c2defe..08b9cc9bf3d 100644
--- a/engines/tetraedge/te/te_animation.cpp
+++ b/engines/tetraedge/te/te_animation.cpp
@@ -40,7 +40,7 @@ TeAnimation::~TeAnimation() {
 }
 
 void TeAnimation::cont() {
-	if (_runTimer._stopped) {
+	if (!_runTimer.running()) {
 		_runTimer.start();
 		animations()->push_back(this);
 		update(_runTimer.getTimeFromStart() / 1000.0);
@@ -60,14 +60,14 @@ void TeAnimation::removeThisFromAnimations() {
 }
 
 void TeAnimation::pause() {
-	if (!_runTimer._stopped) {
+	if (_runTimer.running()) {
 		removeThisFromAnimations();
 		_runTimer.pause();
 	}
 }
 
 void TeAnimation::stop() {
-	if (!_runTimer._stopped) {
+	if (_runTimer.running()) {
 		removeThisFromAnimations();
 		_runTimer.stop();
 		_onStopSignal.call();
@@ -75,7 +75,7 @@ void TeAnimation::stop() {
 }
 
 void TeAnimation::reset() {
-	if (!_runTimer._stopped) {
+	if (_runTimer.running()) {
 		removeThisFromAnimations();
 		stop();
 	}
@@ -87,30 +87,32 @@ void TeAnimation::seekToStart() {
 	update(_runTimer.getTimeFromStart() / 1000.0);
 }
 
-/*static*/ void TeAnimation::pauseAll() {
+/*static*/
+void TeAnimation::pauseAll() {
 	for (auto &anim : *animations()) {
-		if (!anim->_runTimer._stopped)
+		if (anim->_runTimer.running())
 			anim->pause();
 	}
 }
 
-/*static*/ void TeAnimation::resumeAll() {
+/*static*/
+void TeAnimation::resumeAll() {
 	for (auto &anim : *animations()) {
 		anim->cont();
 	}
 }
 
-/*static*/ void TeAnimation::updateAll() {
+/*static*/
+void TeAnimation::updateAll() {
 	Common::Array<TeAnimation *> &anims = *animations();
 	// Note: update can cause events which cascade into animtaions
 	// getting deleted, so be careful about the numbers.
 	for (unsigned int i = 0; i < anims.size(); i++) {
-		if (!anims[i]->_runTimer._stopped) {
+		if (anims[i]->_runTimer.running()) {
 			float msFromStart = anims[i]->_runTimer.getTimeFromStart() / 1000.0;
 			anims[i]->update(msFromStart);
 		}
 	}
-
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_bezier_curve.h b/engines/tetraedge/te/te_bezier_curve.h
index ace2c2ceea5..9b201c13f50 100644
--- a/engines/tetraedge/te/te_bezier_curve.h
+++ b/engines/tetraedge/te/te_bezier_curve.h
@@ -47,6 +47,8 @@ public:
 	static void serialize(Common::WriteStream &stream, const TeBezierCurve &curve);
 	static void deserialize(Common::ReadStream &stream, TeBezierCurve &curve);
 
+	const Common::Array<TeVector3f32> &controlPoints() { return _controlPoints; }
+
 private:
 	unsigned int _numiterations;
 	float _length;
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index 31bb77bb74e..f0235e3492c 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -60,7 +60,7 @@ _hitZoneLayout(nullptr)
 	inputmgr->_mouseLUpSignal.insert(_onMouseLeftUpMaxPriorityCallback);
 
 	setEditionColor(TeColor(128, 128, 128, 255));
-	if (getDoubleValidationProtectionTimer()->_stopped)
+	if (!getDoubleValidationProtectionTimer()->running())
 		getDoubleValidationProtectionTimer()->start();
 }
 
@@ -202,7 +202,7 @@ void TeButtonLayout::reset() {
 
 void TeButtonLayout::resetTimeFromLastValidation() {
 	TeTimer *timer = getDoubleValidationProtectionTimer();
-	if (timer->_stopped) {
+	if (!timer->running()) {
 		timer->start();
 	}
 	timer->timeElapsed();
diff --git a/engines/tetraedge/te/te_button_layout.h b/engines/tetraedge/te/te_button_layout.h
index a8a7b7068b3..ebeaab28ae0 100644
--- a/engines/tetraedge/te/te_button_layout.h
+++ b/engines/tetraedge/te/te_button_layout.h
@@ -88,6 +88,9 @@ public:
 	void setState(State newState);
 
 	TeSignal0Param &onMouseClickValidated() { return _onMouseClickValidatedSignal; };
+	TeSignal0Param &onButtonChangedToStateUpSignal() { return _onButtonChangedToStateUpSignal; };
+	TeSignal0Param &onButtonChangedToStateDownSignal() { return _onButtonChangedToStateDownSignal; };
+	TeSignal0Param &onButtonChangedToStateRolloverSignal() { return _onButtonChangedToStateRolloverSignal; };
 
 	bool _someClickFlag;
 	TeLayout *_upLayout;
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index 07ae8dd4501..f41baa6d4c4 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -31,10 +31,18 @@ namespace Tetraedge {
 TeCamera::TeCamera() : _projectionMatrixType(0), _orthogonalParamL(1.0f),
 	_orthogonalParamR(0.0f), _orthogonalParamT(1.0f), _orthogonalParamB(0.0f),
 	_orthNearVal(10.0f), _orthFarVal(4000.0f), _transformA(0), _transformB(0),
-	_focalLenMaybe(40.0f), _somePerspectiveVal(1.0f)
+	_fov(40.0f), _somePerspectiveVal(1.0f)
 {
 }
 
+void TeCamera::apply() {
+	/*debug("TeCamera::apply %13s mtype %d fov %.2f persp %.2f orth(%.2f %.2f) pos %s scale %s", name().c_str(),
+			_projectionMatrixType, _fov, _somePerspectiveVal, _orthNearVal, _orthFarVal,
+			position().dump().c_str(), scale().dump().c_str());*/
+	applyProjection();
+	applyTransformations();
+}
+
 void TeCamera::applyProjection() {
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->setCurrentCamera(this);
@@ -50,7 +58,7 @@ void TeCamera::applyProjection() {
 void TeCamera::applyTransformations() {
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->setMatrixMode(TeRenderer::MatrixMode::MM_GL_MODELVIEW);
-	TeMatrix4x4 matrix = transformationMatrix();
+	TeMatrix4x4 matrix = worldTransformationMatrix();
 	matrix.inverse();
 	renderer->loadMatrix(matrix);
 	renderer->loadCurrentMatrixToGL();
@@ -92,23 +100,60 @@ void TeCamera::buildOrthoMatrix() {
 }
 
 void TeCamera::buildPerspectiveMatrix() {
-	error("TODO: Implement TeCamera::buildPerspectiveMatrix");
+	_projectionMatrix = TeMatrix4x4();
+	float f = tanf(_fov * 0.5);
+	_projectionMatrix.setValue(0, 0, (1.0 / f) / ((float)_viewportW / _viewportH));
+	_projectionMatrix.setValue(1, 1, 1.0 / f);
+	_projectionMatrix.setValue(2, 2, (_orthNearVal + _orthFarVal) / (_orthNearVal - _orthFarVal));
+	_projectionMatrix.setValue(2, 2, (_orthNearVal * _orthFarVal) / (_orthNearVal - _orthFarVal));
+	_projectionMatrix.setValue(2, 3, -1);
+	_projectionMatrix.setValue(3, 3, 0.0);
 }
 
 void TeCamera::buildPerspectiveMatrix2() {
-	error("TODO: Implement TeCamera::buildPerspectiveMatrix2");
+	_projectionMatrix = TeMatrix4x4();
+	float f = tanf(_fov * 0.5);
+	_projectionMatrix.setValue(0, 0, 1.0 / f);
+	_projectionMatrix.setValue(1, 1, _somePerspectiveVal / f);
+	_projectionMatrix.setValue(2, 2, -(_orthNearVal + _orthFarVal) / (_orthNearVal - _orthFarVal));
+	_projectionMatrix.setValue(3, 2, 1.0);
+	_projectionMatrix.setValue(2, 3, (_orthFarVal * 2) * _orthNearVal / (_orthNearVal - _orthFarVal));
+	_projectionMatrix.setValue(3, 3, 0.0);
 }
 
 void TeCamera::buildPerspectiveMatrix3() {
-	error("TODO: Implement TeCamera::buildPerspectiveMatrix3");
+	_projectionMatrix = TeMatrix4x4();
+	float f = tanf(_fov * 0.5);
+	_projectionMatrix.setValue(0, 0, (1.0 / f) / _somePerspectiveVal);
+	_projectionMatrix.setValue(1, 1, 1.0 / f);
+	_projectionMatrix.setValue(2, 2, -(_orthNearVal + _orthFarVal) / (_orthNearVal - _orthFarVal));
+	_projectionMatrix.setValue(3, 2, 1.0);
+	_projectionMatrix.setValue(2, 3, (_orthFarVal * 2) * _orthNearVal / (_orthNearVal - _orthFarVal));
+	_projectionMatrix.setValue(3, 3, 0.0);
 }
 
 void TeCamera::draw() {
 	error("TODO: Implement TeCamera::draw");
 }
 
-void TeCamera::getRay(const TeVector2s32 &param_1, TeVector3f32 &out1, TeVector3f32 &out2) {
-	error("TODO: Implement TeCamera::getRay");
+void TeCamera::getRay(const TeVector2s32 &pxloc, TeVector3f32 &out1, TeVector3f32 &out2) {
+	float xval = (pxloc._x - _viewportX) / fabs(_viewportW);
+	out2.x() = xval * 2 - 1;
+	float yval = (pxloc._y - _viewportY) / fabs(_viewportH);
+	out2.y() = yval * 2 - 1;
+	out2.z() = 1.0;
+
+	TeMatrix4x4 inverse = projectionMatrix();
+	inverse.inverse();
+	out2 = inverse * out2;
+	out2.normalize();
+
+	TeVector3f32 pos = position();
+	TeQuaternion rot = rotation();
+	out1 = pos;
+	rot.normalize();
+	TeMatrix4x4 rotmatrix = rot.toMatrix();
+	out2 = rotmatrix * out2;
 }
 
 void TeCamera::loadBin(const Common::String &path) {
diff --git a/engines/tetraedge/te/te_camera.h b/engines/tetraedge/te/te_camera.h
index 1f02ccf0154..f6344d53a24 100644
--- a/engines/tetraedge/te/te_camera.h
+++ b/engines/tetraedge/te/te_camera.h
@@ -36,11 +36,7 @@ class TeCamera : public Te3DObject2, public TeReferencesCounter {
 public:
 	TeCamera();
 
-	void apply() {
-		applyProjection();
-		applyTransformations();
-	}
-
+	void apply();
 	void applyProjection();
 	void applyTransformations();
 	void buildOrthoMatrix();
@@ -69,11 +65,12 @@ public:
 	void updateProjectionMatrix();
 
 	void viewport(int x, int y, uint width, uint height);
+	TeVector2f32 viewportSize() const { return TeVector2f32(_viewportW, _viewportH); }
 
 	int _projectionMatrixType;
 	float _orthNearVal;
 	float _orthFarVal;
-	float _focalLenMaybe;
+	float _fov;
 	float _somePerspectiveVal;
 
 private:
diff --git a/engines/tetraedge/te/te_fee_move_zone.cpp b/engines/tetraedge/te/te_fee_move_zone.cpp
deleted file mode 100644
index 414c9a0841b..00000000000
--- a/engines/tetraedge/te/te_fee_move_zone.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/* 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/te/te_fee_move_zone.h"
-
-namespace Tetraedge {
-
-TeFeeMoveZone::TeFeeMoveZone() {
-}
-
-// TODO: Add more functions here.
-
-} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 6f072e56676..89999b5717e 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -19,13 +19,50 @@
  *
  */
 
+#include "tetraedge/tetraedge.h"
+
 #include "tetraedge/te/te_free_move_zone.h"
+#include "tetraedge/te/micropather.h"
+#include "tetraedge/te/te_renderer.h"
 
 namespace Tetraedge {
 
+class TeFreeMoveZoneGraph : micropather::Graph {
+	friend class TeFreeMoveZone;
+	TeVector2s32 _size;
+	Common::Array<char> _flags;
+	float _bordersDistance;
+	TeFreeMoveZone *_owner;
+
+	// These don't match ScummVM naming convention but are needed to match MicroPather API.
+	virtual float LeastCostEstimate(void * stateStart, void *stateEnd);
+	virtual void AdjacentCost(void *state, Common::Array<micropather::StateCost> *adjacent);
+	virtual void PrintStateInfo(void *state);
+
+	int flag(const TeVector2s32 &loc);
+	void setSize(const TeVector2s32 &size);
+
+	void deserialize(Common::ReadStream &stream);
+	void serialize(Common::WriteStream &stream) const;
+};
+
+
 TeFreeMoveZone::TeFreeMoveZone() : _actzones(nullptr), _blockers(nullptr), _rectBlockers(nullptr),
 _transformedVerticiesDirty(true), _bordersDirty(true), _pickMeshDirty(true), _projectedPointsDirty(true)
 {
+	_graph = new TeFreeMoveZoneGraph();
+	_graph->_bordersDistance = 2048.0f;
+	_graph->_owner = this;
+	_micropather = new micropather::MicroPather(_graph);
+}
+
+TeFreeMoveZone::~TeFreeMoveZone() {
+	// TODO: remove signal.
+	delete _micropather;
+}
+
+float TeFreeMoveZone::bordersDistance() const {
+	return _graph->_bordersDistance;
 }
 
 void TeFreeMoveZone::buildAStar() {
@@ -37,10 +74,19 @@ void TeFreeMoveZone::calcGridMatrix() {
 }
 
 void TeFreeMoveZone::clear() {
-	error("TODO: Implement TeFreeMoveZone::clear");
+	setNbTriangles(0);
+	_pickMeshDirty = true;
+	_projectedPointsDirty = true;
+	// TODO: Clear 3 other point vectors here.
+	// TODO: _gridDirty = true;
+	_graph->_flags.clear();
+	_graph->_size = TeVector2s32(0, 0);
+	_micropather->Reset();
 }
 
 Common::Array<TeVector3f32> TeFreeMoveZone::collisions(const TeVector3f32 &v1, const TeVector3f32 &v2) {
+	updatePickMesh();
+	updateProjectedPoints();
 	error("TODO: Implement TeFreeMoveZone::collisions");
 }
 
@@ -48,16 +94,215 @@ TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, b
 	error("TODO: Implement TeFreeMoveZone::correctCharacterPosition");
 }
 
+TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4, float param_5, bool findMeshFlag) {
+	error("TODO: Implement TeFreeMoveZone::curve");
+}
+
+TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4) {
+	error("TODO: Implement TeFreeMoveZone::curve");
+}
+
 /*static*/
 void TeFreeMoveZone::deserialize(Common::ReadStream &stream, TeFreeMoveZone &dest, Common::Array<TeBlocker> *blockers,
                Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones) {
 	dest.clear();
 	TePickMesh2::deserialize(stream, dest);
+	TeVector2f32::deserialize(stream, dest._gridOffsetSomething);
+	dest._transformedVerticiesDirty = (stream.readByte() != 0);
+	dest._bordersDirty = (stream.readByte() != 0);
+	dest._pickMeshDirty = (stream.readByte() != 0);
+	dest._projectedPointsDirty = (stream.readByte() != 0);
+	dest._gridDirty = (stream.readByte() != 0);
+
+	Te3DObject2::deserializeVectorArray(stream, dest._freeMoveZoneVerticies);
+	Te3DObject2::deserializeUintArray(stream, dest._uintArray1);
+	Te3DObject2::deserializeVectorArray(stream, dest._vectorArray);
+	Te3DObject2::deserializeUintArray(stream, dest._uintArray2);
+
+	TeOBP::deserialize(stream, dest._obp);
 
-	error("TODO: Implement TeFreeMoveZone::deserialize");
+	TeVector2f32::deserialize(stream, dest._someGridVec1);
+	TeVector2f32::deserialize(stream, dest._someGridVec2);
+	dest._someGridFloat = stream.readFloatLE();
+
+	dest._graph->deserialize(stream);
+	if (dest.name().contains("19000")) {
+		dest._gridOffsetSomething = TeVector2f32(2.0, 2.0);
+		dest._gridDirty = true;
+	}
 	dest._blockers = blockers;
 	dest._rectBlockers = rectblockers;
 	dest._actzones = actzones;
 }
 
+void TeFreeMoveZone::draw() {
+	if (!worldVisible())
+		return;
+
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->enableWireFrame();
+	TePickMesh2::draw();
+	TeMesh mesh;
+	mesh.setConf(_uintArray2.size(), _uintArray2.size(), TeMesh::MeshMode_Lines, 0, 0);
+
+	error("TODO: Finish TeFreeMoveZone::draw");
+}
+
+TeVector3f32 TeFreeMoveZone::findNearestPointOnBorder(const TeVector2f32 &pt) {
+	error("TODO: Implement TeFreeMoveZone::findNearestPointOnBorder");
+}
+
+bool TeFreeMoveZone::hasBlockerIntersection(const TeVector2s32 &pt) {
+	error("TODO: Implement TeFreeMoveZone::hasBlockerIntersection");
+}
+
+bool TeFreeMoveZone::hasCellBorderIntersection(const TeVector2s32 &pt) {
+	error("TODO: Implement TeFreeMoveZone::hasCellBorderIntersection");
+}
+
+TeActZone *TeFreeMoveZone::isInZone(const TeVector3f32 &pt) {
+	error("TODO: Implement TeFreeMoveZone::isInZone");
+}
+
+bool TeFreeMoveZone::onViewportChanged() {
+	_projectedPointsDirty = true;
+	return false;
+}
+
+void TeFreeMoveZone::preUpdateGrid() {
+	error("TODO: Implement TeFreeMoveZone::preUpdateGrid");
+}
+
+TeVector3f32 TeFreeMoveZone::projectOnAStarGrid(const TeVector3f32 &pt) {
+	error("TODO: Implement TeFreeMoveZone::projectOnAStarGrid");
+}
+
+Common::Array<TeVector3f32> &TeFreeMoveZone::removeInsignificantPoints(const Common::Array<TeVector3f32> &points) {
+	error("TODO: Implement TeFreeMoveZone::removeInsignificantPoints");
+}
+
+void TeFreeMoveZone::setBordersDistance(float dist) {
+	_graph->_bordersDistance = dist;
+}
+
+void TeFreeMoveZone::setCamera(TeIntrusivePtr<TeCamera> &cam, bool recalcProjPoints) {
+	error("TODO: Implement TeFreeMoveZone::setCamera");
+}
+
+void TeFreeMoveZone::setNbTriangles(unsigned long len) {
+	_freeMoveZoneVerticies.resize(len * 3);
+
+	_gridDirty = true;
+	_transformedVerticiesDirty = true;
+	_bordersDirty = true;
+	_pickMeshDirty = true;
+	_projectedPointsDirty = true;
+}
+
+void TeFreeMoveZone::setPathFindingOccluder(const TeOBP &occluder) {
+	error("TODO: Implement TeFreeMoveZone::setPathFindingOccluder");
+}
+
+void TeFreeMoveZone::setVertex(unsigned long offset, const TeVector3f32 &vertex) {
+	_freeMoveZoneVerticies[offset] = vertex;
+
+	_gridDirty = true;
+	_transformedVerticiesDirty = true;
+	_bordersDirty = true;
+	_pickMeshDirty = true;
+	_projectedPointsDirty = true;
+}
+
+TeVector2s32 TeFreeMoveZone::transformAStarGridInWorldSpace(const TeVector2s32 &gridpt) {
+	error("TODO: Implement TeFreeMoveZone::transformAStarGridInWorldSpace");
+}
+
+float TeFreeMoveZone::transformHeightMin(float minval) {
+	error("TODO: Implement TeFreeMoveZone::transformHeightMin");
+}
+
+TeVector3f32 TeFreeMoveZone::transformVectorInWorldSpace(float param_3,float param_4) {
+	error("TODO: Implement TeFreeMoveZone::transformVectorInWorldSpace");
+}
+
+void TeFreeMoveZone::updateBorders() {
+	error("TODO: Implement TeFreeMoveZone::updateBorders");
+}
+
+void TeFreeMoveZone::updateGrid(bool force) {
+	if (!force && !_gridDirty)
+		return;
+	_gridDirty = true;
+	_updateTimer.stop();
+	_updateTimer.start();
+	buildAStar();
+	_micropather->Reset();
+	debug("[TeFreeMoveZone::updateGrid()] %s time : %.2f", name().c_str(), _updateTimer.getTimeFromStart() / 1000000.0);
+	_gridDirty = false;
+}
+
+void TeFreeMoveZone::updatePickMesh() {
+	if (!_pickMeshDirty)
+		return;
+
+	error("TODO: Implement TeFreeMoveZone::updatePickMesh");
+}
+
+void TeFreeMoveZone::updateProjectedPoints() {
+	if (!_projectedPointsDirty)
+		return;
+
+	error("TODO: Implement TeFreeMoveZone::updateProjectedPoints");
+}
+
+void TeFreeMoveZone::updateTransformedVertices() {
+	if (!_transformedVerticiesDirty)
+		return;
+
+	error("TODO: Implement TeFreeMoveZone::updateTransformedVertices");
+}
+
+/*========*/
+
+float TeFreeMoveZoneGraph::LeastCostEstimate(void * stateStart, void *stateEnd) {
+	error("TODO: Implement TeFreeMoveZone::TeFreeMoveZoneGraph::LeastCostEstimate");
+}
+
+void TeFreeMoveZoneGraph::AdjacentCost(void *state, Common::Array<micropather::StateCost> *adjacent) {
+	error("TODO: Implement TeFreeMoveZone::TeFreeMoveZoneGraph::AdjacentCost");
+}
+
+void TeFreeMoveZoneGraph::PrintStateInfo(void *state) {
+	error("TODO: Implement TeFreeMoveZone::TeFreeMoveZoneGraph::PrintStateInfo");
+}
+
+int TeFreeMoveZoneGraph::flag(const TeVector2s32 &loc) {
+	if (loc._x < 0 || loc._x >= _size._x || loc._y < 0 || loc._y >= _size._y)
+		return 1;
+	return _flags[loc._y * _size._x + loc._x];
+}
+
+void TeFreeMoveZoneGraph::setSize(const TeVector2s32 &size) {
+	_flags.clear();
+	_size = size;
+	_flags.resize(size._x * _size._y);
+}
+
+void TeFreeMoveZoneGraph::deserialize(Common::ReadStream &stream) {
+	TeVector2s32::deserialize(stream, _size);
+	uint32 flaglen = stream.readUint32LE();
+	if (flaglen > 1000000 || (int)flaglen != _size._x * _size._y)
+		error("Flags unexpected size, expect %d got %d", _size._x * _size._y, flaglen);
+	_flags.resize(flaglen);
+	for (unsigned int i = 0; i < flaglen; i++) {
+		_flags[i] = stream.readByte();
+	}
+	_bordersDistance = stream.readFloatLE();
+}
+
+void TeFreeMoveZoneGraph::serialize(Common::WriteStream &stream) const {
+	error("TODO: Implement TeFreeMoveZoneGraph::serialize");
+}
+
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index d1ddb9a3f97..13f0b9856ee 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -24,6 +24,10 @@
 
 #include "common/array.h"
 
+#include "tetraedge/te/te_bezier_curve.h"
+#include "tetraedge/te/te_camera.h"
+#include "tetraedge/te/te_intrusive_ptr.h"
+#include "tetraedge/te/te_obp.h"
 #include "tetraedge/te/te_pick_mesh2.h"
 #include "tetraedge/te/te_vector3f32.h"
 #include "tetraedge/te/te_act_zone.h"
@@ -48,6 +52,7 @@ struct TeRectBlocker {
 	long _x;
 };
 
+class TeFreeMoveZoneGraph;
 
 class TeFreeMoveZone : public TePickMesh2 {
 public:
@@ -56,31 +61,76 @@ public:
 		float _distance;
 	};
 
-	class TeFreeMoveZoneGraph {
-		friend class TeFreeMoveZone;
-		TeVector2s32 _size;
-		Common::Array<char> _flags;
-		float _f;
-		TeFreeMoveZone *_owner;
-	};
-
 	TeFreeMoveZone();
+	~TeFreeMoveZone();
 
-	float bordersDistance() const { return _graph->_f; }
+	float bordersDistance() const;
 	void buildAStar();
 	void calcGridMatrix();
 	void clear();
 	Common::Array<TeVector3f32> collisions(const TeVector3f32 &v1, const TeVector3f32 &v2);
 	TeVector3f32 correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool f);
 
+	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4, float param_5, bool findMeshFlag);
+	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4);
+
+	void draw() override;
+	TeVector3f32 findNearestPointOnBorder(const TeVector2f32 &pt);
+	bool hasBlockerIntersection(const TeVector2s32 &pt);
+	bool hasCellBorderIntersection(const TeVector2s32 &pt);
+
+	TeActZone *isInZone(const TeVector3f32 &pt);
+
+	// loadBin() 2 versions, seem unused
+
+	// name(), onPositionChanged(), position(), rotate(), rotation(), scale(),
+	// setName(), setPosition(), setRotation(), setScale(), setVisible(),
+	// translate(), and visible() are all implemented in original, but all
+	// just do the same as super.
+
+	bool onViewportChanged();
+	void preUpdateGrid();
+	TeVector3f32 projectOnAStarGrid(const TeVector3f32 &pt);
+	Common::Array<TeVector3f32> &removeInsignificantPoints(const Common::Array<TeVector3f32> &points);
+	void setBordersDistance(float dist);
+	void setCamera(TeIntrusivePtr<TeCamera> &cam, bool recalcProjPoints);
+	void setNbTriangles(unsigned long len);
+	void setPathFindingOccluder(const TeOBP &occluder);
+	void setVertex(unsigned long offset, const TeVector3f32 &vertex);
+	TeVector2s32 transformAStarGridInWorldSpace(const TeVector2s32 &gridpt);
+	float transformHeightMin(float minval);
+	TeVector3f32 transformVectorInWorldSpace(float param_3,float param_4);
+	void updateBorders();
+	void updateGrid(bool force);
+	void updatePickMesh();
+	void updateProjectedPoints();
+	void updateTransformedVertices();
+
+	static float normalizeAngle(float angle);
 	static void deserialize(Common::ReadStream &stream, TeFreeMoveZone &dest, Common::Array<TeBlocker> *blockers,
                Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones);
+	static void serialize(Common::WriteStream &stream, const TeFreeMoveZone &src, bool updateFirst);
 
 private:
 	Common::Array<TeActZone> *_actzones;
 	Common::Array<TeBlocker> *_blockers;
 	Common::Array<TeRectBlocker> *_rectBlockers;
 
+	Common::Array<TeVector3f32> _freeMoveZoneVerticies;
+	// TODO: Find better names..
+	Common::Array<unsigned int> _uintArray1;
+	Common::Array<TeVector3f32> _vectorArray;
+	Common::Array<unsigned int> _uintArray2;
+
+	TeVector2f32 _gridOffsetSomething;
+	TeVector2f32 _someGridVec1;
+	TeVector2f32 _someGridVec2;
+
+	float _someGridFloat;
+
+	TeOBP _obp;
+
+	bool _gridDirty;
 	TeFreeMoveZoneGraph *_graph;
 	bool _transformedVerticiesDirty;
 	bool _bordersDirty;
diff --git a/engines/tetraedge/te/te_light.h b/engines/tetraedge/te/te_light.h
index 633a5c125c2..72f2a05cd38 100644
--- a/engines/tetraedge/te/te_light.h
+++ b/engines/tetraedge/te/te_light.h
@@ -68,6 +68,9 @@ public:
 	void setPosition3d(const TeVector3f32 &pos) { _position3d = pos; }
 	void setPositionRadial(const TeVector2f32 &pos) { _positionRadial = pos; }
 
+	const TeVector2f32 &positionRadial() const { return _positionRadial; }
+	const TeVector3f32 &position3d() const { return _position3d; }
+
 private:
 	TeVector3f32 _position3d;
 	TeVector2f32 _positionRadial;
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index b8a04c324c1..78850e02155 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -332,7 +332,7 @@ int spriteLayoutBindings(lua_State *L) {
 	TeSpriteLayout *layout = new TeSpriteLayout();
 	lua_pushnil(L);
 
-	bool playNow = !layout->_tiledSurfacePtr->_frameAnim._runTimer._stopped;
+	bool playNow = layout->_tiledSurfacePtr->_frameAnim._runTimer.running();
 	int startingFrame = 0;
 	int endingFrame = -1;
 	Common::Path imgFullPath;
diff --git a/engines/tetraedge/te/te_lua_thread.cpp b/engines/tetraedge/te/te_lua_thread.cpp
index ad4653c05e7..baa7bbadb36 100644
--- a/engines/tetraedge/te/te_lua_thread.cpp
+++ b/engines/tetraedge/te/te_lua_thread.cpp
@@ -76,7 +76,7 @@ void TeLuaThread::execute(const Common::String &fname) {
 		_resume(0);
 	} else {
 		if (!fname.contains("Update"))
-			warning("[TeLuaThread::Execute] La fonction : \"%s\" n'existe pas.", fname.c_str());
+			warning("[TeLuaThread::Execute0] La fonction : \"%s\" n'existe pas.", fname.c_str());
 		lua_settop(_luaThread, -2);
 	}
 }
@@ -91,7 +91,7 @@ void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1) {
 		_resume(1);
 	} else {
 		if (!fname.contains("Update"))
-			warning("[TeLuaThread::Execute] La fonction : \"%s\" n'existe pas.", fname.c_str());
+			warning("[TeLuaThread::Execute1] La fonction : \"%s\" n'existe pas.", fname.c_str());
 		lua_settop(_luaThread, -2);
 	}
 }
@@ -107,7 +107,7 @@ void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1, cons
 		_resume(2);
 	} else {
 		if (!fname.contains("Update"))
-			warning("[TeLuaThread::Execute] La fonction : \"%s\" n'existe pas.", fname.c_str());
+			warning("[TeLuaThread::Execute2] La fonction : \"%s\" n'existe pas.", fname.c_str());
 		lua_settop(_luaThread, -2);
 	}
 }
@@ -124,7 +124,7 @@ void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1, cons
 		_resume(3);
 	} else {
 		if (!fname.contains("Update"))
-			warning("[TeLuaThread::Execute] La fonction : \"%s\" n'existe pas.", fname.c_str());
+			warning("[TeLuaThread::Execute3] La fonction : \"%s\" n'existe pas.", fname.c_str());
 		lua_settop(_luaThread, -2);
 	}
 }
diff --git a/engines/tetraedge/te/te_lua_thread.h b/engines/tetraedge/te/te_lua_thread.h
index 5f8b0e77f4f..ac2be3cd209 100644
--- a/engines/tetraedge/te/te_lua_thread.h
+++ b/engines/tetraedge/te/te_lua_thread.h
@@ -69,7 +69,6 @@ private:
 	bool _released;
 
 	static Common::Array<TeLuaThread *> _threadList;
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/te/te_matrix4x4.h b/engines/tetraedge/te/te_matrix4x4.h
index 36aac4935cd..68159bac5bd 100644
--- a/engines/tetraedge/te/te_matrix4x4.h
+++ b/engines/tetraedge/te/te_matrix4x4.h
@@ -61,7 +61,6 @@ public:
 	void translate(const TeVector3f32 &vec);
 	TeVector3f32 mult3x3(const TeVector3f32 &vec) const;
 	TeVector3f32 mult4x3(const TeVector3f32 &vec) const;
-	// TODO add public members
 
 	Common::String toString() const;
 
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index b1ca1f1f3d5..5b22b028a4d 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -214,6 +214,8 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 	_meshes.resize(stream.readUint32LE());
 	_weightElements.resize(stream.readUint32LE());
 	uint32 bonecount = stream.readUint32LE();
+	if (bonecount > 100000)
+		error("TeModel::load: Unexpected number of bones %d", bonecount);
 	_bones.resize(bonecount);
 	_boneMatrices.resize(bonecount);
 
@@ -248,10 +250,8 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 		loadWeights(stream, _weightElements[i]);
 	}
 
-	//if (*(long *)(*(long *)&(_bones).field_0x8 + 0x68) != 0) {
-	//	return true;
-	//}
-	_bones.resize(1);
+	if (_bones.empty())
+		_bones.resize(1);
 	return true;
 }
 
@@ -313,6 +313,10 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	uint32 matcount = stream.readUint32LE();
 	uint32 matidxcount = stream.readUint32LE();
 	uint32 idxcount = stream.readUint32LE();
+
+	if (vertcount > 100000 || matcount > 100000 || matidxcount > 100000 || idxcount > 100000)
+		error("Improbable mesh sizes %d %d %d %d", vertcount, matcount, matidxcount, idxcount);
+
 	mesh.setConf(vertcount, idxcount, TeMesh::MeshMode_TriangleFan, matcount, matidxcount);
 
 	uint32 flags = stream.readUint32LE();
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index 532916e9c04..10166e37a2c 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -146,7 +146,6 @@ protected:
 
 	TeIntrusivePtr<TeModelAnimation> _modelAnim;
 	TeIntrusivePtr<TeModelVertexAnimation> _modelVertexAnim;
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index 8fad3f12fda..98a0062be70 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -323,7 +323,7 @@ void TeModelAnimation::update(double proportion) {
 	}
 
 	if (frames) {
-	  	_curFrameValFresh = false;
+		_curFrameValFresh = false;
 		_curFrame2 = calcCurrentFrame(proportion);
 		if (_finishedSignalPending) {
 			_finishedSignalPending = false;
diff --git a/engines/tetraedge/te/te_model_animation.h b/engines/tetraedge/te/te_model_animation.h
index b06a5c9b150..8765b7ab840 100644
--- a/engines/tetraedge/te/te_model_animation.h
+++ b/engines/tetraedge/te/te_model_animation.h
@@ -108,7 +108,6 @@ private:
 	int _useNMOArrays; // TODO: probably a bad name?
 	int _numNMOFrames;
 	float _speed;
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/te/te_obp.cpp b/engines/tetraedge/te/te_obp.cpp
index 613ec30323e..bb501152ad7 100644
--- a/engines/tetraedge/te/te_obp.cpp
+++ b/engines/tetraedge/te/te_obp.cpp
@@ -105,4 +105,14 @@ void TeOBP::updateTransformed() {
 	_boundsNeedUpdate = false;
 }
 
+/*static*/
+void TeOBP::deserialize(Common::ReadStream &stream, TeOBP &dest) {
+	dest._boundsNeedUpdate = true;
+	Te3DObject2::deserialize(stream, dest);
+	TeVector3f32::deserialize(stream, dest._corner1);
+	TeVector3f32::deserialize(stream, dest._corner2);
+	TeVector3f32::deserialize(stream, dest._corner3);
+	TeVector3f32::deserialize(stream, dest._corner4);
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_pick_mesh2.h b/engines/tetraedge/te/te_pick_mesh2.h
index 1e7c07e8bb5..c43ee54f5ac 100644
--- a/engines/tetraedge/te/te_pick_mesh2.h
+++ b/engines/tetraedge/te/te_pick_mesh2.h
@@ -51,6 +51,7 @@ public:
 	static void deserialize(Common::ReadStream &stream, TePickMesh2 &mesh);
 
 	Common::Array<TeVector3f32> &verticies() { return _verticies; }
+	const Common::Array<TeVector3f32> &verticies() const { return _verticies; }
 private:
 	Common::Array<TeVector3f32> _verticies;
 	unsigned long _lastTriangleHit;
diff --git a/engines/tetraedge/te/te_ray_intersection.cpp b/engines/tetraedge/te/te_ray_intersection.cpp
new file mode 100644
index 00000000000..fa1d6080a29
--- /dev/null
+++ b/engines/tetraedge/te/te_ray_intersection.cpp
@@ -0,0 +1,76 @@
+/* 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/te/te_ray_intersection.h"
+
+namespace Tetraedge {
+
+namespace TeRayIntersection {
+
+TePickMesh *getMesh(const TeVector3f32 &param_1, const TeVector3f32 &param_2, const Common::Array<TePickMesh *> &pickMeshes,
+			float param_4, float param_5, TeVector3f32 *param_6) {
+	error("TODO: implement TeRayIntersection::getMesh");
+}
+
+int intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3,
+              const TeVector3f32 &v4, const TeVector3f32 &v5, TeVector3f32 &vout, float &fout) {
+	const TeVector3f32 v4_v3 = (v4 - v3);
+	const TeVector3f32 v5_v3 = (v5 - v3);
+	TeVector3f32 v = v4_v3 ^ v5_v3;
+
+	if (v == TeVector3f32(0.0f, 0.0f, 0.0f))
+		return -1;
+
+	int result = -1;
+	float f1 = v.dotProduct(v1 - v3);
+	float f2 = v.dotProduct(v2);
+	if (fabs(f2) > 1e-9) {
+		f2 = -f1 / f2;
+		fout = f2;
+		if (f2 >= 0.0) {
+			vout = v1 + (v2 * f2);
+			float dot1 = v4_v3.dotProduct(v4_v3);
+			float dot2 = v4_v3.dotProduct(v5_v3);
+			float dot3 = v5_v3.dotProduct(v5_v3);
+			const TeVector3f32 vout_v3 = vout - v3;
+			float dots1 = (dot2 * dot2) - (dot1 * dot3);
+			float dot4 = vout_v3.dotProduct(v4_v3);
+			float dot5 = vout_v3.dotProduct(v5_v3);
+			float dots2 = ((dot2 * dot5) - (dot3 * dot4)) / dots1;
+			if (dots2 >= 0.0) {
+				result = 0;
+				if (dots2 <= 1.0) {
+					float dots3 = (dot2 * dot4 - dot1 * dot5) / dots1;
+					if (dots3 >= 0 && dots2 + dots3 <= 1.0)
+						result = 1;
+				}
+			}
+		}
+	}
+	return result;
+}
+
+
+}
+
+
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_fee_move_zone.h b/engines/tetraedge/te/te_ray_intersection.h
similarity index 59%
rename from engines/tetraedge/te/te_fee_move_zone.h
rename to engines/tetraedge/te/te_ray_intersection.h
index 30d0e642958..d4f4f19c329 100644
--- a/engines/tetraedge/te/te_fee_move_zone.h
+++ b/engines/tetraedge/te/te_ray_intersection.h
@@ -19,22 +19,26 @@
  *
  */
 
-#ifndef TETRAEDGE_TE_TE_FEE_MOVE_ZONE_H
-#define TETRAEDGE_TE_TE_FEE_MOVE_ZONE_H
+#ifndef TETRAEDGE_TE_TE_RAY_INTERSECTION_H
+#define TETRAEDGE_TE_TE_RAY_INTERSECTION_H
+
+#include "common/array.h"
+#include "tetraedge/te/te_vector3f32.h"
 
 namespace Tetraedge {
 
-class TeFeeMoveZone {
-public:
-	TeFeeMoveZone();
+class TePickMesh;
+
+namespace TeRayIntersection {
 
-	// TODO add public members
+TePickMesh *getMesh(const TeVector3f32 &param_1, const TeVector3f32 &param_2, const Common::Array<TePickMesh *> &pickMeshes,
+			float param_4, float param_5, TeVector3f32 *param_6);
 
-private:
-	// TODO add private members
+int intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3,
+              const TeVector3f32 &v4, const TeVector3f32 &v5, TeVector3f32 &vout, float &fout);
 
-};
+} // end namespace TeRayIntersection
 
 } // end namespace Tetraedge
 
-#endif // TETRAEDGE_TE_TE_FEE_MOVE_ZONE_H
+#endif // TETRAEDGE_TE_TE_RAY_INTERSECTION_H
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index e49b9c6aebb..6250e74b634 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -455,10 +455,10 @@ void TeRenderer::renderTransparentMeshes() {
 		}
 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 		if (meshProperties._scissorEnabled) {
-		  	glDisable(GL_SCISSOR_TEST);
+			glDisable(GL_SCISSOR_TEST);
 		}
 		if (texture) {
-		  	glDisable(GL_TEXTURE_2D);
+			glDisable(GL_TEXTURE_2D);
 			_textureEnabled = false;
 		}
 		glPopMatrix();
diff --git a/engines/tetraedge/te/te_scene.cpp b/engines/tetraedge/te/te_scene.cpp
index bfe9396c04d..3df69b9b559 100644
--- a/engines/tetraedge/te/te_scene.cpp
+++ b/engines/tetraedge/te/te_scene.cpp
@@ -90,10 +90,13 @@ void TeScene::setCurrentCamera(const Common::String &name) {
 		}
 	}
 	if (i == n) {
-		//warning("TeScene::setCurrentCamera: Couldn't find camera %s", name.c_str());
+		debug("TeScene::setCurrentCamera: Couldn't find camera %s", name.c_str());
 		return;
 	}
 	_currentCameraIndex = i;
+	TeCamera *c = _cameras[i].get();
+	assert(c);
+	debug("TeScene::setCurrentCamera: Set %s", c->name().c_str());
 }
 
 void TeScene::update() {
diff --git a/engines/tetraedge/te/te_scene.h b/engines/tetraedge/te/te_scene.h
index f01e5bace6e..4302a16c81f 100644
--- a/engines/tetraedge/te/te_scene.h
+++ b/engines/tetraedge/te/te_scene.h
@@ -57,9 +57,11 @@ public:
 
 	void update();
 
-	Common::Array<TeIntrusivePtr<TeCamera>> &cameras() { return _cameras; }
 	Common::Array<TeIntrusivePtr<TeModel>> &models() { return _models; }
 
+protected:
+	Common::Array<TeIntrusivePtr<TeCamera>> &cameras() { return _cameras; }
+
 private:
 	uint _currentCameraIndex;
 	Common::Array<TeIntrusivePtr<TeCamera>> _cameras;
diff --git a/engines/tetraedge/te/te_scrolling_layout.cpp b/engines/tetraedge/te/te_scrolling_layout.cpp
index 0e9317c4300..d4e3b334a22 100644
--- a/engines/tetraedge/te/te_scrolling_layout.cpp
+++ b/engines/tetraedge/te/te_scrolling_layout.cpp
@@ -33,7 +33,7 @@ void TeScrollingLayout::setContentLayout(TeLayout *layout) {
 	_contentLayout = layout;
 	if (layout) {
 		_contentLayoutUserPos = layout->userPosition();
-		// TODO: original seems to call addChildBefore(layout this) which doesn't make sense?
+		// TODO: original seems to call addChildBefore(layout, this) which doesn't make sense?
 		addChild(_contentLayout);
 	}
 }
diff --git a/engines/tetraedge/te/te_sprite_layout.h b/engines/tetraedge/te/te_sprite_layout.h
index ffac3e577b6..4784cabb8e2 100644
--- a/engines/tetraedge/te/te_sprite_layout.h
+++ b/engines/tetraedge/te/te_sprite_layout.h
@@ -66,8 +66,6 @@ private:
 	bool _sizeSet;
 	bool _allowFloatTranslate;
 
-	// TODO add private members
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_text_layout.h b/engines/tetraedge/te/te_text_layout.h
index 7aa5ddd2c55..d889f0d8d63 100644
--- a/engines/tetraedge/te/te_text_layout.h
+++ b/engines/tetraedge/te/te_text_layout.h
@@ -58,7 +58,6 @@ private:
 	int _baseFontSize;
 
 	TeTextBase2 _base;
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/te/te_tiled_surface.h b/engines/tetraedge/te/te_tiled_surface.h
index e7e9b8e4455..e081f5c1df9 100644
--- a/engines/tetraedge/te/te_tiled_surface.h
+++ b/engines/tetraedge/te/te_tiled_surface.h
@@ -103,8 +103,6 @@ private:
 
 	Common::Path _path;
 
-	// TODO add private members
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_timer.h b/engines/tetraedge/te/te_timer.h
index 6d1d972e999..fa7962516ee 100644
--- a/engines/tetraedge/te/te_timer.h
+++ b/engines/tetraedge/te/te_timer.h
@@ -50,7 +50,7 @@ public:
 	static void resumeAll();
 	static void updateAll();
 
-	bool _stopped;
+	bool running() const { return !_stopped; }
 
 private:
 	static TeRealTimer *realTimer();
@@ -62,6 +62,7 @@ private:
 	bool _pausable;
 	bool _alarmSet;
 	bool _updated;
+	bool _stopped;
 
 	TeSignal0Param _alarmSignal;
 
diff --git a/engines/tetraedge/te/te_vector2s32.cpp b/engines/tetraedge/te/te_vector2s32.cpp
index db80dae6bf6..2541e265f41 100644
--- a/engines/tetraedge/te/te_vector2s32.cpp
+++ b/engines/tetraedge/te/te_vector2s32.cpp
@@ -19,6 +19,8 @@
  *
  */
 
+#include "common/stream.h"
+
 #include "tetraedge/te/te_vector2s32.h"
 
 namespace Tetraedge {
@@ -26,6 +28,10 @@ namespace Tetraedge {
 TeVector2s32::TeVector2s32() : _x(0), _y(0) {
 }
 
-// TODO: Add more functions here.
+/*static*/
+void TeVector2s32::deserialize(Common::ReadStream &stream, TeVector2s32 &dest) {
+	dest._x = stream.readSint32LE();
+	dest._y = stream.readSint32LE();
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_vector2s32.h b/engines/tetraedge/te/te_vector2s32.h
index db6fd56dc6f..ecaa6e05e08 100644
--- a/engines/tetraedge/te/te_vector2s32.h
+++ b/engines/tetraedge/te/te_vector2s32.h
@@ -23,6 +23,7 @@
 #define TETRAEDGE_TE_TE_VECTOR2S32_H
 
 #include "common/rect.h"
+#include "common/stream.h"
 
 namespace Tetraedge {
 
@@ -45,6 +46,8 @@ public:
 		return *this;
 	}
 
+	static void deserialize(Common::ReadStream &stream, TeVector2s32 &dest);
+
 public:
 	int _x;
 	int _y;
diff --git a/engines/tetraedge/te/te_vector3f32.cpp b/engines/tetraedge/te/te_vector3f32.cpp
index ee7b2f62bf7..07baa317294 100644
--- a/engines/tetraedge/te/te_vector3f32.cpp
+++ b/engines/tetraedge/te/te_vector3f32.cpp
@@ -42,5 +42,20 @@ void TeVector3f32::rotate(const TeQuaternion &rot) {
 	matrix.transform(this, false);
 }
 
+TeVector3f32 operator^(const TeVector3f32 &left, const TeVector3f32 &right) {
+	TeVector3f32 retval;
+	float rx = right.x();
+	float ry = right.y();
+	float rz = right.z();
+	float lx = left.x();
+	float ly = left.y();
+	float lz = left.z();
+	retval.x() = ly * rz - lz * ry;
+	retval.y() = lz * rx - rz * lx;
+	retval.z() = ry * lx - ly * rx;
+	return retval;
+}
+
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_vector3f32.h b/engines/tetraedge/te/te_vector3f32.h
index e6070b91310..668328aa7aa 100644
--- a/engines/tetraedge/te/te_vector3f32.h
+++ b/engines/tetraedge/te/te_vector3f32.h
@@ -72,12 +72,14 @@ public:
 	bool parse(const Common::String &val);
 
 	Common::String dump() const {
-		return Common::String::format("TeVector3f32(%.02f %.02f %.02f)", x(), y(), z());
+		return Common::String::format("TeVec3f32(%.02f %.02f %.02f)", x(), y(), z());
 	}
 
 	void rotate(const TeQuaternion &rot);
 };
 
+TeVector3f32 operator^(const TeVector3f32 &left, const TeVector3f32 &right);
+
 } // end namespace Tetraedge
 
 #endif // TETRAEDGE_TE_TE_VECTOR3F32_H
diff --git a/engines/tetraedge/te/te_visual_fade.cpp b/engines/tetraedge/te/te_visual_fade.cpp
index 3c085d84001..4773118de7f 100644
--- a/engines/tetraedge/te/te_visual_fade.cpp
+++ b/engines/tetraedge/te/te_visual_fade.cpp
@@ -30,24 +30,47 @@ TeVisualFade::TeVisualFade() {
 }
 
 void TeVisualFade::animateBlackFade() {
-	error("TODO: Implement TeVisualFade::animateBlackFade.");
+	_fadeCaptureSprite.setVisible(false);
+	_fadeCurveAnim.stop();
+	_fadeCurveAnim._startVal = TeColor(255, 255, 255, 0);
+	_fadeCurveAnim._endVal = TeColor(255, 255, 255, 255);
+	Common::Array<float> curve;
+	curve.push_back(0.0);
+	curve.push_back(0.0);
+	curve.push_back(1.0);
+	curve.push_back(1.0);
+	curve.push_back(1.0);
+	_fadeCurveAnim.setCurve(curve);
+	_fadeCurveAnim._duration = 2000.0;
+	_fadeCurveAnim._callbackObj = &_fadeCaptureSprite;
+	_fadeCurveAnim._callbackMethod = &Te3DObject2::setColor;
+	_fadeCurveAnim.play();
+
+	_blackFadeSprite.setVisible(true);
+	_blackFadeCurveAnim.stop();
+	_blackFadeCurveAnim._startVal = TeColor(255, 255, 255, 255);
+	_blackFadeCurveAnim._endVal = TeColor(255, 255, 255, 0);
+	_blackFadeCurveAnim.setCurve(curve);
+	_blackFadeCurveAnim._duration = 2000.0;
+	_blackFadeCurveAnim._callbackObj = &_blackFadeSprite;
+	_blackFadeCurveAnim._callbackMethod = &Te3DObject2::setColor;
+	_blackFadeCurveAnim.play();
 }
 
 void TeVisualFade::animateFade() {
-	//debug("visual fade %p animate", this);
-	_animateFadeCurveAnim.stop();
-	_animateFadeCurveAnim._runTimer.pausable(false);
+	_fadeCurveAnim.stop();
+	_fadeCurveAnim._runTimer.pausable(false);
 	_fadeCaptureSprite.setVisible(true);
-	_animateFadeCurveAnim._startVal = TeColor(255, 255, 255, 255);
-	_animateFadeCurveAnim._endVal = TeColor(255, 255, 255, 0);
+	_fadeCurveAnim._startVal = TeColor(255, 255, 255, 255);
+	_fadeCurveAnim._endVal = TeColor(255, 255, 255, 0);
 	Common::Array<float> curve;
 	curve.push_back(0.0);
 	curve.push_back(1.0);
-	_animateFadeCurveAnim.setCurve(curve);
-	_animateFadeCurveAnim._duration = 400.0;
-	_animateFadeCurveAnim._callbackObj = &_fadeCaptureSprite;
-	_animateFadeCurveAnim._callbackMethod = &Te3DObject2::setColor;
-	_animateFadeCurveAnim.play();
+	_fadeCurveAnim.setCurve(curve);
+	_fadeCurveAnim._duration = 400.0;
+	_fadeCurveAnim._callbackObj = &_fadeCaptureSprite;
+	_fadeCurveAnim._callbackMethod = &Te3DObject2::setColor;
+	_fadeCurveAnim.play();
 }
 
 void TeVisualFade::captureFrame() {
diff --git a/engines/tetraedge/te/te_visual_fade.h b/engines/tetraedge/te/te_visual_fade.h
index 389cb7de304..307c2d95983 100644
--- a/engines/tetraedge/te/te_visual_fade.h
+++ b/engines/tetraedge/te/te_visual_fade.h
@@ -45,14 +45,17 @@ public:
 	TeSpriteLayout _blackFadeSprite;
 	TeButtonLayout _buttonLayout;
 
-	bool fading() const { return !_animateFadeCurveAnim._runTimer._stopped; }
+	bool fading() const { return _fadeCurveAnim._runTimer.running(); }
+	bool blackFading() const { return _blackFadeCurveAnim._runTimer.running(); }
+
+	TeCurveAnim2<Te3DObject2, TeColor> blackFadeCurveAnim() { return _blackFadeCurveAnim; }
 
 private:
 
 	TeIntrusivePtr<Te3DTexture> _texturePtr;
-	TeCurveAnim2<Te3DObject2, TeColor> _animateFadeCurveAnim;
+	TeCurveAnim2<Te3DObject2, TeColor> _fadeCurveAnim;
+	TeCurveAnim2<Te3DObject2, TeColor> _blackFadeCurveAnim;
 	TeImage _image;
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/te/te_xml_gui.h b/engines/tetraedge/te/te_xml_gui.h
index 7e119f5bbd3..e187bab5fdc 100644
--- a/engines/tetraedge/te/te_xml_gui.h
+++ b/engines/tetraedge/te/te_xml_gui.h
@@ -37,11 +37,8 @@ public:
 
 	void load(const Common::Path &path);
 
-	// TODO add public members
-
 private:
 	Common::StringMap _map;
-	// TODO add private members
 
 };
 


Commit: 1d62d47edba2495673f3c79769635b1230fe35e6
    https://github.com/scummvm/scummvm/commit/1d62d47edba2495673f3c79769635b1230fe35e6
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More WIP. Can now see Kate in first scene.

Changed paths:
  A engines/tetraedge/te/te_images_sequence.cpp
  A engines/tetraedge/te/te_images_sequence.h
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/application.h
    engines/tetraedge/game/billboard.h
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/characters_shadow.cpp
    engines/tetraedge/game/confirm.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/inventory_object.cpp
    engines/tetraedge/game/inventory_object.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/main_menu.cpp
    engines/tetraedge/game/object3d.cpp
    engines/tetraedge/game/object3d.h
    engines/tetraedge/game/objectif.cpp
    engines/tetraedge/game/objectif.h
    engines/tetraedge/game/owner_error_menu.cpp
    engines/tetraedge/game/splash_screens.cpp
    engines/tetraedge/module.mk
    engines/tetraedge/te/te_camera.h
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_frame_anim.cpp
    engines/tetraedge/te/te_frame_anim.h
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_i_codec.h
    engines/tetraedge/te/te_lua_script.cpp
    engines/tetraedge/te/te_lua_thread.cpp
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_pick_mesh2.h
    engines/tetraedge/te/te_quaternion.cpp
    engines/tetraedge/te/te_quaternion.h
    engines/tetraedge/te/te_scene.cpp
    engines/tetraedge/te/te_scene.h
    engines/tetraedge/te/te_scummvm_codec.h
    engines/tetraedge/te/te_sound_manager.cpp
    engines/tetraedge/te/te_sound_manager.h
    engines/tetraedge/te/te_theora.cpp
    engines/tetraedge/te/te_theora.h
    engines/tetraedge/te/te_tiled_surface.h
    engines/tetraedge/te/te_timer.cpp
    engines/tetraedge/te/te_timer.h
    engines/tetraedge/te/te_xml_gui.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index b985b63e921..174870ef10f 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/textconsole.h"
 #include "common/file.h"
 #include "common/util.h"
@@ -43,7 +44,8 @@ namespace Tetraedge {
 bool Application::_dontUpdateWhenApplicationPaused = false;
 
 Application::Application() : _finishedGame(false), _finishedFremium(false),
-_captureFade(false), _difficulty(1), _created(false), _tutoActivated(false) {
+_captureFade(false), _difficulty(1), _created(false), _tutoActivated(false),
+_drawShadows(true) {
 	TeCore *core = g_engine->getCore();
 	core->_coreNotReady = true;
 	core->fileFlagSystemSetFlag("platform", "MacOSX");
@@ -271,6 +273,8 @@ void Application::create() {
 
 	onMainWindowSizeChanged();
 	_splashScreens.enter();
+
+	_drawShadows = (ConfMan.get("disable_shadows") != "true");
 	_created = true;
 }
 
@@ -386,7 +390,7 @@ void Application::performRender() {
 	Game *game = g_engine->getGame();
 	TeRenderer *renderer = g_engine->getRenderer();
 
-	if (game->running() && game->scene()._character
+	if (_drawShadows && game->running() && game->scene()._character
 			&& game->scene()._shadowLightNo != -1
 			&& game->scene()._charactersShadow != nullptr) {
 		renderer->shadowMode(TeRenderer::ShadowMode1);
@@ -399,7 +403,7 @@ void Application::performRender() {
 	renderer->renderTransparentMeshes();
 	renderer->clearBuffer(GL_ACCUM);
 	if (game->running()) {
-		if (game->scene()._character
+		if (_drawShadows && game->scene()._character
 			&& game->scene()._shadowLightNo != -1
 			&& game->scene()._charactersShadow != nullptr) {
 			TeIntrusivePtr<TeCamera> currentCamera = game->scene().currentCamera();
@@ -417,7 +421,6 @@ void Application::performRender() {
 	renderer->clearBuffer(GL_ACCUM);
 	drawFront();
 	renderer->renderTransparentMeshes();
-	// What gets called here??
 	game->scene().drawPath();
 	g_system->updateScreen();
 }
diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
index 707642cba43..d5a43e8c21e 100644
--- a/engines/tetraedge/game/application.h
+++ b/engines/tetraedge/game/application.h
@@ -149,6 +149,7 @@ private:
 	bool _captureFade;
 	bool _created;
 	bool _tutoActivated;
+	bool _drawShadows;
 
 	int _difficulty;
 
diff --git a/engines/tetraedge/game/billboard.h b/engines/tetraedge/game/billboard.h
index 4fc376fac99..3860294c713 100644
--- a/engines/tetraedge/game/billboard.h
+++ b/engines/tetraedge/game/billboard.h
@@ -43,6 +43,8 @@ public:
 	void position2(const TeVector3f32 &pos);
 	void size(const TeVector2f32 &size);
 
+	TeIntrusivePtr<TeModel> &model() { return _model; }
+
 private:
 	TeIntrusivePtr<TeModel> _model;
 	TeVector3f32 _pos;
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 9aabbf0220c..e231c0c32f9 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -55,7 +55,8 @@ void Character::WalkSettings::clear() {
 }
 
 Character::Character() : _curveOffset(0), _lastFrame(-1), _callbacksChanged(false),
-_missingCurrentAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"), _needsSomeUpdate(false),
+_missingCurrentAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"),
+_needsSomeUpdate(false), _positionFlag(false),
 _stepSound1("sounds/SFX/PAS_H_BOIS1.ogg"), _stepSound2("sounds/SFX/PAS_H_BOIS2.ogg"),
 _freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(nullptr) {
 	_curModelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
@@ -235,9 +236,10 @@ int Character::rightStepFrame(enum Character::WalkPart walkpart) {
 	return -1;
 }
 
-bool Character::loadModel(const Common::String &name, bool param_2) {
+bool Character::loadModel(const Common::String &name, bool unused) {
 	assert(_globalCharacterSettings);
 	if (_model) {
+		//TODO
 		//_model->bonesUpdateSignal().remove(this, &Character::onBonesUpdate);
 	}
 	_model = new TeModel();
@@ -257,14 +259,16 @@ bool Character::loadModel(const Common::String &name, bool param_2) {
 	_model->setName(name);
 	_model->setScale(_characterSettings._defaultScale);
 
-	for (unsigned int i = 0; i < _model->_meshes.size(); i++)
-		_model->_meshes[i].setVisible(true);
+	for (auto &mesh : _model->_meshes)
+		mesh.setVisible(true);
 
 	_model->setVisibleByName("_B_", false);
 	_model->setVisibleByName("_Y_", false);
 
+	// Note: game loops through "faces" here, but it only ever uses the default ones.
 	_model->setVisibleByName(_characterSettings._defaultEyes, true);
 	_model->setVisibleByName(_characterSettings._defaultMouth, true);
+	_model->setVisibleByName(_characterSettings._defaultBody, true);
 
 	setAnimation(_characterSettings._walkFileName, true, false, false, -1, 9999);
 
@@ -304,12 +308,12 @@ bool Character::loadModel(const Common::String &name, bool param_2) {
 	Common::File xmlFile;
 	if (!xmlFile.open(path))
 		error("Character::loadSettings: Can't open %s", path.c_str());
-	const uint32 bufsize = xmlFile.size();
+	const int64 bufsize = xmlFile.size();
 	char *buf = new char[bufsize+1];
 	buf[bufsize] = '\0';
 	xmlFile.read(buf, bufsize);
 	Common::String fixedbuf(buf);
-	uint32 offset = fixedbuf.find("------------");
+	size_t offset = fixedbuf.find("------------");
 	while (offset != Common::String::npos) {
 		fixedbuf.replace(offset, 12, "--");
 		offset = fixedbuf.find("------------");
@@ -318,9 +322,9 @@ bool Character::loadModel(const Common::String &name, bool param_2) {
 	// Big HACK: Remove the embedded comment in this config.
 	offset = fixedbuf.find("<!--<walk>");
 	if (offset != Common::String::npos) {
-		uint32 endOffset = fixedbuf.find(" -->", offset);
+		size_t endOffset = fixedbuf.find(" -->", offset);
 		if (endOffset != Common::String::npos) {
-			uint32 realEndOffset = fixedbuf.find("walk>-->", endOffset);
+			size_t realEndOffset = fixedbuf.find("walk>-->", endOffset);
 			if (realEndOffset  != Common::String::npos && realEndOffset > endOffset) {
 				fixedbuf.replace(offset, endOffset - offset, "<!-- ");
 			}
@@ -455,11 +459,14 @@ void Character::updateAnimFrame() {
 	if (_model->anim()) {
 		_lastAnimFrame = _model->anim()->curFrame2();
 	}
-	error("TODO: Implement Character::updateAnimFrame");
 }
 
 void Character::updatePosition(float curveOffset) {
-	error("TODO: Implement Character::updatePosition");
+	if (!_curve->controlPoints().empty()) {
+		//TeVector3f32 pt = _curve->retrievePoint(curveOffset);
+		// add field 0x214
+		error("TODO: Implement Character::updatePosition");
+	}
 }
 
 Common::String Character::walkAnim(Character::WalkPart part) {
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index d71aa86c60a..9e13e8b89c7 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -154,9 +154,13 @@ public:
 	const Common::String &curAnimName() const { return _curAnimName; }
 	TeFreeMoveZone *freeMoveZone() { return _freeMoveZone; }
 	const Common::String &freeMoveZoneName() const { return _freeMoveZoneName; }
+	void setFreeMoveZoneName(const Common::String &val) { _freeMoveZoneName = val; }
 	bool needsSomeUpdate() const { return _needsSomeUpdate; }
 	void setNeedsSomeUpdate(bool val) { _needsSomeUpdate = val; }
 	void setCharLookingAt(Character *other) { _charLookingAt = other; }
+	void setPositionCharacter(const TeVector3f32 &val) { _positionCharacter = val; }
+	bool positionFlag() const { return _positionFlag; }
+	void setPositionFlag(bool val) { _positionFlag = val; }
 
 private:
 	float _curveOffset;
@@ -190,6 +194,9 @@ private:
 	bool _someRepeatFlag; // TODO: what is this?
 	bool _callbacksChanged;
 	bool _needsSomeUpdate; // TODO: what is this? Field 0x85.
+	bool _positionFlag;
+
+	TeVector3f32 _positionCharacter;
 
 	// TODO: work out how these are different
 	Common::String _setAnimName;
diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
index 92951817947..19025678183 100644
--- a/engines/tetraedge/game/characters_shadow.cpp
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -75,7 +75,6 @@ void CharactersShadow::createTexture(InGameScene *scene) {
 	glClearColor(0.0, 0.0, 0.0, 0.0);
 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
-
 	for (Character *character : scene->_characters) {
 		character->_model->draw();
 	}
@@ -154,7 +153,7 @@ void CharactersShadow::draw(InGameScene *scene) {
 	glEnable(GL_BLEND);
 	renderer->setCurrentColor(scene->shadowColor());
 
-	for (TeIntrusivePtr<TeModel> model : scene->models()) {
+	for (TeIntrusivePtr<TeModel> model : scene->zoneModels()) {
 		if (model->_meshes.size() > 0 && model->_meshes[0].materials().empty()) {
 			model->_meshes[0].defaultMaterial(TeIntrusivePtr<Te3DTexture>());
 			model->_meshes[0].materials()[0]._enableSomethingDefault0 = true;
diff --git a/engines/tetraedge/game/confirm.cpp b/engines/tetraedge/game/confirm.cpp
index 5ba7cbeec5c..b27d994b0c3 100644
--- a/engines/tetraedge/game/confirm.cpp
+++ b/engines/tetraedge/game/confirm.cpp
@@ -19,6 +19,8 @@
  *
  */
 
+#include "common/config-manager.h"
+
 #include "tetraedge/game/confirm.h"
 #include "tetraedge/game/application.h"
 #include "tetraedge/tetraedge.h"
@@ -89,6 +91,10 @@ void Confirm::enter(const Common::String &guiPath, const Common::String &y) {
 	// Make sure the mouse cursor is back on top.
 	app->_frontOrientationLayout.removeChild(&app->mouseCursorLayout());
 	app->_frontOrientationLayout.addChild(&app->mouseCursorLayout());
+
+	if (ConfMan.get("skip_confirm") == "true") {
+		onButtonYes();
+	}
 }
 
 void Confirm::leave() {
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 69a40880ee9..6bfc12cc8c4 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -23,6 +23,7 @@
 #include "common/path.h"
 #include "common/str-array.h"
 #include "common/system.h"
+#include "common/config-manager.h"
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/application.h"
@@ -37,7 +38,9 @@
 #include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_input_mgr.h"
 #include "tetraedge/te/te_ray_intersection.h"
+#include "tetraedge/te/te_sound_manager.h"
 #include "tetraedge/te/te_variant.h"
+#include "tetraedge/te/te_lua_thread.h"
 
 namespace Tetraedge {
 
@@ -62,31 +65,26 @@ bool Game::addAnimToSet(const Common::String &anim) {
 	// Get path to lua script, eg scenes/ValVoralberg/14040/Set14040.lua
 	const Common::Path animPath(Common::String("scenes/") + anim + "/");
 
-	bool retval = false;
 	if (Common::File::exists(animPath)) {
 		Common::StringArray parts = TetraedgeEngine::splitString(anim, '/');
 		assert(parts.size() >= 2);
 
-		Common::String layoutName = parts[1];
-		Common::String path = Common::String("scenes/") + parts[0] + "/" + parts[1] + "/Set" + parts[1];
+		const Common::String layoutName = parts[1];
+		const Common::String path = Common::String("scenes/") + parts[0] + "/" + parts[1] + "/Set" + parts[1];
 
 		_gui2.load(path + ".lua");
-		/*
-		TeILayout *layout = _gui2.layout("root");
-		TeSpriteLayout *spriteLayout2 = findSpriteLayoutByName(layout, layoutName);
-
-		TeLayout *layout2 = TeLuaGUI::layout(&(this->scene).field_0x170,"root");
-		long lVar5 = 0;
-		if (spriteLayout2) {
-			lVar5 = (long)plVar3 + *(long *)(*plVar3 + -0x198);
-		}
-		(**(code **)(*(long *)((long)&pTVar2->vptr + (long)pTVar2->vptr[-0x33]) + 0x30))
-					((long)&pTVar2->vptr + (long)pTVar2->vptr[-0x33],lVar5);
-		 */
-	  retval = true;
+
+		// Note: game makes this here, but never uses it..
+		// it seems like a random memory leak??
+		// TeSpriteLayout *spritelayout = new TeSpriteLayout();
+
+		TeSpriteLayout *spritelayout = findSpriteLayoutByName(_gui2.layoutChecked("root"), layoutName);
+
+		_scene.bgGui().layoutChecked("root")->addChild(spritelayout);
+		return true;
 	}
 
-	return retval;
+	return false;
 }
 
 void Game::addArtworkUnlocked(const Common::String &name, bool notify) {
@@ -108,7 +106,7 @@ void Game::addNoScale2Children() {
 	if (!_noScaleLayout2)
 		return;
 
-	TeLayout *vidbtn = _gui4.layout("videoButtonLayout");
+	TeLayout *vidbtn = _inGameGui.layout("videoButtonLayout");
 	if (vidbtn)
 		_noScaleLayout2->addChild(vidbtn);
 
@@ -124,7 +122,7 @@ void Game::addNoScale2Children() {
 void Game::addNoScaleChildren() {
 	if (!_noScaleLayout)
 		return;
-	TeLayout *inGame = _gui4.layout("inGame");
+	TeLayout *inGame = _inGameGui.layout("inGame");
 	if (inGame)
 		_noScaleLayout->addChild(inGame);
 
@@ -309,7 +307,7 @@ void Game::enter(bool newgame) {
 	_luaContext.setGlobal("BUTTON_RS_LEFT", 0x800000);
 	_luaContext.setGlobal("BUTTON_RS_RIGHT", 0x1000000);
 
-	_luaScript.attachToContext(&_luaContext);
+	_gameEnterScript.attachToContext(&_luaContext);
 
 	if (!_objectif.gui1().loaded()) {
 		_objectif.load();
@@ -373,7 +371,9 @@ void Game::finishGame() {
 }
 
 void Game::initLoadedBackupData() {
+	bool warpFlag = true;
 	if (!_loadName.empty()) {
+		warpFlag = false;
 		error("TODO: Implemet Game::initLoadedBackupData loading part");
 	}
 	Application *app = g_engine->getApplication();
@@ -391,7 +391,7 @@ void Game::initLoadedBackupData() {
 	_gameLoadState = 0;
 	app->showLoadingIcon(false);
 	_loadName.clear();
-	initScene(true, firstWarpPath);
+	initScene(warpFlag, firstWarpPath);
 }
 
 void Game::initNoScale() {
@@ -423,12 +423,12 @@ void Game::initScene(bool fade, const Common::String &scenePath) {
 bool Game::initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
 	debug("Game::initWarp(%s, %s, %s)", zone.c_str(), scene.c_str(), fadeFlag ? "true" : "false");
 	_inventoryMenu.unload();
-	_gui4.unload();
+	_inGameGui.unload();
 	_movePlayerCharacterDisabled = false;
 	_sceneCharacterVisibleFromLoad = true;
 
 	if (_scene._character) {
-		_scene._character->_model->setVisible(false);
+		_scene._character->_model->setVisible(true);
 		_scene._character->deleteAllCallback();
 		_scene._character->stop();
 		_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true, false, false, -1, 9999);
@@ -472,6 +472,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_luaContext.addBindings(LuaBinds::LuaOpenBinds);
 		_luaScript.attachToContext(&_luaContext);
 		_luaScript.load("menus/help/help.lua");
+		_luaScript.execute();
 		_luaScript.load(logicLuaPath);
 	}
 
@@ -516,25 +517,25 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	}
 
 	_inventoryMenu.load();
-	_gui4.load("InGame.lua");
+	_inGameGui.load("InGame.lua");
 
-	TeButtonLayout *skipbtn = _gui4.buttonLayout("skipVideoButton");
+	TeButtonLayout *skipbtn = _inGameGui.buttonLayout("skipVideoButton");
 	skipbtn->setVisible(false);
 	skipbtn->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
 	skipbtn->onMouseClickValidated().add(this, &Game::onSkipVideoButtonValidated);
 
-	TeButtonLayout *vidbgbtn = _gui4.buttonLayout("videoBackgroundButton");
+	TeButtonLayout *vidbgbtn = _inGameGui.buttonLayout("videoBackgroundButton");
 	vidbgbtn->setVisible(false);
 	/* TODO: Restore the original behavior here (onLockVideoButtonValidated) */
 	vidbgbtn->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
 	vidbgbtn->onMouseClickValidated().add(this, &Game::onSkipVideoButtonValidated);
 
-	TeSpriteLayout *video = _gui4.spriteLayout("video");
+	TeSpriteLayout *video = _inGameGui.spriteLayout("video");
 	video->setVisible(false);
 	video->_tiledSurfacePtr->_frameAnim.onFinished().remove(this, &Game::onVideoFinished);
 	video->_tiledSurfacePtr->_frameAnim.onFinished().add(this, &Game::onVideoFinished);
 
-	TeButtonLayout *invbtn = _gui4.buttonLayout("inventoryButton");
+	TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
 	invbtn->setSizeType(TeILayout::RELATIVE_TO_PARENT); // TODO: Double-check if this is the right virt fn.
 	invbtn->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
 	invbtn->onMouseClickValidated().add(this, &Game::onInventoryButtonValidated);
@@ -546,7 +547,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		invbtn->setSize(TeVector3f32(0.08, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.08, 0.0));
 	}
 
-	TeCheckboxLayout *markersCheckbox = _gui4.checkboxLayout("markersVisibleButton");
+	TeCheckboxLayout *markersCheckbox = _inGameGui.checkboxLayout("markersVisibleButton");
 	markersCheckbox->setVisible(!_markersVisible);
 	markersCheckbox->onStateChangedSignal().add(this, &Game::onMarkersVisible);
 
@@ -554,7 +555,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	removeNoScale2Children();
 	app->_frontLayout.removeChild(_noScaleLayout2);
 
-	TeLayout *vidLayout = _gui4.layout("videoLayout");
+	TeLayout *vidLayout = _inGameGui.layout("videoLayout");
 	app->_frontLayout.removeChild(vidLayout);
 	removeNoScaleChildren();
 	app->_frontLayout.removeChild(_noScaleLayout);
@@ -627,15 +628,15 @@ bool Game::isDocumentOpened() {
 }
 
 bool Game::isMoviePlaying() {
-	TeButtonLayout *vidButton = _gui4.buttonLayout("videoBackgroundButton");
+	TeButtonLayout *vidButton = _inGameGui.buttonLayout("videoBackgroundButton");
 	if (vidButton)
 		return vidButton->visible();
 	return false;
 }
 
-bool Game::launchDialog(const Common::String &param_1, uint param_2, const Common::String &charname,
+bool Game::launchDialog(const Common::String &dname, uint param_2, const Common::String &charname,
 				  const Common::String &animfile, float param_5) {
-	error("TODO: Implemet Game::launchDialog %s %d %s %s %f", param_1.c_str(),
+	error("TODO: Implemet Game::launchDialog %s %d %s %s %f", dname.c_str(),
 			param_2, charname.c_str(), animfile.c_str(), param_5);
 }
 
@@ -673,11 +674,11 @@ void Game::leave(bool flag) {
 
 	_luaContext.destroy();
 	_running = false;
-	_gui4.buttonLayoutChecked("skipVideoButton")->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
-	_gui4.buttonLayoutChecked("videoBackgroundButton")->onMouseClickValidated().remove(this, &Game::onLockVideoButtonValidated);
-	_gui4.spriteLayoutChecked("video")->_tiledSurfacePtr->_frameAnim.onFinished().remove(this, &Game::onSkipVideoButtonValidated);
-	_gui4.buttonLayoutChecked("inventoryButton")->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
-	_gui4.unload();
+	_inGameGui.buttonLayoutChecked("skipVideoButton")->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
+	_inGameGui.buttonLayoutChecked("videoBackgroundButton")->onMouseClickValidated().remove(this, &Game::onLockVideoButtonValidated);
+	_inGameGui.spriteLayoutChecked("video")->_tiledSurfacePtr->_frameAnim.onFinished().remove(this, &Game::onSkipVideoButtonValidated);
+	_inGameGui.buttonLayoutChecked("inventoryButton")->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
+	_inGameGui.unload();
 	_playedTimer.stop();
 
 	Application *app = g_engine->getApplication();
@@ -695,9 +696,8 @@ bool Game::loadCharacter(const Common::String &name) {
 	bool result = true;
 	Character *character = _scene.character(name);
 	if (!character) {
-		result = false;
-		bool loaded = _scene.loadCharacter(name);
-		if (loaded) {
+		result = _scene.loadCharacter(name);
+		if (result) {
 			character = _scene.character(name);
 			character->_onCharacterAnimFinishedSignal.remove<Game>(this, &Game::onCharacterAnimationFinished);
 			character->_onCharacterAnimFinishedSignal.add<Game>(this, &Game::onCharacterAnimationFinished);
@@ -719,8 +719,8 @@ bool Game::loadPlayerCharacter(const Common::String &name) {
 }
 
 bool Game::loadScene(const Common::String &name) {
-	_luaScript.load("scenes/OnGameEnter.lua");
-	_luaScript.execute();
+	_gameEnterScript.load("scenes/OnGameEnter.lua");
+	_gameEnterScript.execute();
 	Character *character = _scene._character;
 	if (character && character->_model->visible()) {
 		_sceneCharacterVisibleFromLoad = true;
@@ -739,6 +739,9 @@ bool Game::onCallNumber(Common::String val) {
 }
 
 bool Game::onCharacterAnimationFinished(const Common::String &val) {
+	if (!_scene._character)
+		return false;
+
 	error("TODO: Implemet Game::onCharacterAnimationFinished %s", val.c_str());
 }
 
@@ -747,11 +750,54 @@ bool Game::onCharacterAnimationPlayerFinished(const Common::String &val) {
 }
 
 bool Game::onDialogFinished(const Common::String &val) {
-	error("TODO: Implemet Game::onDialogFinished %s", val.c_str());
+	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+		YieldedCallback &cb = _yieldedCallbacks[i];
+		if (cb._luaFnName == "OnDialogFinished" && cb._luaParam == val) {
+			TeLuaThread *lua = cb._luaThread;
+			_yieldedCallbacks.remove_at(i);
+			if (lua) {
+				lua->resume();
+				return false;
+			}
+			break;
+		}
+	}
+
+	_luaScript.execute("OnDialogFinished", val);
+	_luaScript.execute("OnCellDialogFinished", val);
+	return false;
 }
 
 bool Game::onDisplacementFinished() {
-	error("TODO: Implemet Game::onDisplacementFinished");
+	_sceneCharacterVisibleFromLoad = true;
+	_scene._character->stop();
+	_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true, false, false, -1, 9999);
+
+	// TODO: Twiddle flags 0x424b and 0x4249
+	/*
+	if (!_field_0x424b) {
+		_field_0x4249 = false;
+	} else {
+		_field_0x424b = false;
+		_field_0x4249 = true;
+	}*/
+
+	TeLuaThread *thread = nullptr;
+
+	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+		YieldedCallback &cb = _yieldedCallbacks[i];
+		if (cb._luaFnName == "OnDisplacementFinished") {
+			thread = cb._luaThread;
+			_yieldedCallbacks.remove_at(i);
+			break;
+		}
+	}
+	if (thread) {
+		thread->resume();
+	} else {
+		_luaScript.execute("OnDisplacementFinished");
+	}
+	return false;
 }
 
 bool Game::onFinishedCheckBackup(bool result) {
@@ -785,7 +831,7 @@ bool Game::onInventoryButtonValidated() {
 }
 
 bool Game::onLockVideoButtonValidated() {
-	TeButtonLayout *btn = _gui4.buttonLayoutChecked("skipVideoButton");
+	TeButtonLayout *btn = _inGameGui.buttonLayoutChecked("skipVideoButton");
 	btn->setVisible(!btn->visible());
 	return true;
 }
@@ -884,10 +930,10 @@ bool Game::onMouseClick(const Common::Point &pt) {
 
 	if (app->isLockCursor() || _movePlayerCharacterDisabled)
 		return false;
-	
+
 	Character *character = _scene._character;
 	const Common::String &charAnim = character->curAnimName();
-	
+
 	if (charAnim == character->characterSettings()._walkFileName
 		|| charAnim == character->walkAnim(Character::WalkPart_Start)
 		|| charAnim == character->walkAnim(Character::WalkPart_Loop)
@@ -935,7 +981,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 		// TODO: Set app field field_0x910b
 		_posPlayer = lastPoint;
 	}
-	
+
 	if (!_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._walkFileName)) {
 		_lastCharMoveMousePos = TeVector2s32(0, 0);
 		_movePlayerCharacterDisabled = true;
@@ -1041,48 +1087,61 @@ bool Game::onMouseMove() {
 }
 
 bool Game::onSkipVideoButtonValidated() {
-	TeSpriteLayout *sprite = _gui4.spriteLayoutChecked("video");
-	TeButtonLayout *btn = _gui4.buttonLayoutChecked("videoBackgroundButton");
+	TeSpriteLayout *sprite = _inGameGui.spriteLayoutChecked("video");
+	TeButtonLayout *btn = _inGameGui.buttonLayoutChecked("videoBackgroundButton");
 	sprite->stop();
 	btn->setVisible(false);
 	return false;
 }
 
 bool Game::onVideoFinished() {
-	if (!_gui4.loaded())
+	if (!_inGameGui.loaded())
 		return false;
 
 	Application *app = g_engine->getApplication();
 
 	app->captureFade();
 
-	TeSpriteLayout *video = _gui4.spriteLayoutChecked("video");
-	TeButtonLayout *btn = _gui4.buttonLayoutChecked("videoBackgroundButton");
+	TeSpriteLayout *video = _inGameGui.spriteLayoutChecked("video");
+	Common::String vidPath = video->_tiledSurfacePtr->path().toString();
+	TeButtonLayout *btn = _inGameGui.buttonLayoutChecked("videoBackgroundButton");
 	btn->setVisible(false);
-	btn = _gui4.buttonLayoutChecked("skipVideoButton");
+	btn = _inGameGui.buttonLayoutChecked("skipVideoButton");
 	btn->setVisible(false);
 	video->setVisible(false);
 	_music.stop();
 	_running = true;
-	warning("TODO: Game::onVideoFinished: update yieldedCallbacks %s", video->_tiledSurfacePtr->path().toString().c_str());
-	_luaScript.execute("OnMovieFinished", video->_tiledSurfacePtr->path().toString());
+	bool resumed = false;
+	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+		YieldedCallback &cb = _yieldedCallbacks[i];
+		if (cb._luaFnName == "OnMovieFinished" && cb._luaParam == vidPath) {
+			TeLuaThread *lua = cb._luaThread;
+			_yieldedCallbacks.remove_at(i);
+			resumed = true;
+			if (lua)
+				lua->resume();
+			break;
+		}
+	}
+	if (!resumed)
+		_luaScript.execute("OnMovieFinished", vidPath);
 	app->fade();
 	return false;
 }
 
 void Game::pauseMovie() {
 	_music.pause();
-	TeSpriteLayout *sprite = _gui4.spriteLayoutChecked("video");
+	TeSpriteLayout *sprite = _inGameGui.spriteLayoutChecked("video");
 	sprite->pause();
 }
 
 void Game::playMovie(const Common::String &vidPath, const Common::String &musicPath) {
 	Application *app = g_engine->getApplication();
 	app->captureFade();
-	TeButtonLayout *videoBackgroundButton = _gui4.buttonLayoutChecked("videoBackgroundButton");
+	TeButtonLayout *videoBackgroundButton = _inGameGui.buttonLayoutChecked("videoBackgroundButton");
 	videoBackgroundButton->setVisible(true);
 
-	TeButtonLayout *skipVideoButton = _gui4.buttonLayoutChecked("skipVideoButton");
+	TeButtonLayout *skipVideoButton = _inGameGui.buttonLayoutChecked("skipVideoButton");
 	skipVideoButton->setVisible(false);
 
 	TeMusic &music = app->music();
@@ -1094,15 +1153,17 @@ void Game::playMovie(const Common::String &vidPath, const Common::String &musicP
 
 	_running = false;
 
-	TeSpriteLayout *videoSpriteLayout = _gui4.spriteLayoutChecked("video");
+	TeSpriteLayout *videoSpriteLayout = _inGameGui.spriteLayoutChecked("video");
 	videoSpriteLayout->load(vidPath);
 	videoSpriteLayout->setVisible(true);
 	music.play();
 	videoSpriteLayout->play();
 
 	// FIXME TODO!! Stop the movie and soundearly for testing.
-	videoSpriteLayout->_tiledSurfacePtr->_frameAnim._nbFrames = 10;
-	music.stop();
+	if (ConfMan.get("skip_videos") == "true") {
+		videoSpriteLayout->_tiledSurfacePtr->_frameAnim._nbFrames = 10;
+		music.stop();
+	}
 
 	app->fade();
 }
@@ -1112,11 +1173,11 @@ void Game::playRandomSound(const Common::String &name) {
 		warning("Game::playRandomSound: can't find sound list %s", name.c_str());
 		return;
     }
-    error("TODO: Implemet Game::playRandomSound");
+    warning("TODO: Implemet Game::playRandomSound");
 }
 
 void Game::playSound(const Common::String &name, int param_2, float param_3) {
-	error("TODO: Implemet Game::playSound");
+	warning("TODO: Implemet Game::playSound");
 }
 
 void Game::removeNoScale2Child(TeLayout *layout) {
@@ -1129,7 +1190,7 @@ void Game::removeNoScale2Children() {
 	if (!_noScaleLayout2)
 		return;
 
-	TeLayout *vidbtn = _gui4.layout("videoButtonLayout");
+	TeLayout *vidbtn = _inGameGui.layout("videoButtonLayout");
 	if (vidbtn)
 		_noScaleLayout2->removeChild(vidbtn);
 
@@ -1164,19 +1225,19 @@ void Game::resetPreviousMousePos() {
 
 void Game::resumeMovie() {
 	_music.play();
-	_gui4.spriteLayout("video")->play();
+	_inGameGui.spriteLayout("video")->play();
 }
 
 void Game::saveBackup(const Common::String &saveName) {
-	error("TODO: Implemet me");
+	warning("TODO: Implemet Game::saveBackup %s", saveName.c_str());
 }
 
-void Game::setBackground(const Common::String &name) {
-	_scene.changeBackground(name);
+bool Game::setBackground(const Common::String &name) {
+	return _scene.changeBackground(name);
 }
 
 void Game::setCurrentObjectSprite(const Common::String &spritePath) {
-	TeSpriteLayout *currentSprite = _gui4.spriteLayout("currentObjectSprite");
+	TeSpriteLayout *currentSprite = _inGameGui.spriteLayout("currentObjectSprite");
 	if (currentSprite) {
 		if (!spritePath.empty()) {
 			currentSprite->unload();
@@ -1187,15 +1248,35 @@ void Game::setCurrentObjectSprite(const Common::String &spritePath) {
 }
 
 bool Game::showMarkers(bool val) {
-	error("TODO: Implemet me");
+	TeLayout *bg = _gui3.layoutChecked("background");
+	for (unsigned int i = 0; i < bg->childCount(); i++) {
+		const InGameScene::TeMarker *marker = _scene.findMarker(bg->child(i)->name());
+		if (marker)
+			bg->child(i)->setVisible(!val);
+	}
+	return false;
 }
 
-bool Game::startAnimation(const Common::String &animName, int param_2, bool param_3) {
-	error("TODO: Implemet me");
+bool Game::startAnimation(const Common::String &animName, int loopcount, bool reversed) {
+	TeSpriteLayout *layout = _scene.bgGui().spriteLayout(animName);
+	if (layout) {
+		layout->_tiledSurfacePtr->_frameAnim._loopCount = loopcount;
+		layout->_tiledSurfacePtr->_frameAnim._reversed = reversed;
+		layout->_tiledSurfacePtr->play();
+	}
+	return layout != nullptr;
 }
 
 void Game::stopSound(const Common::String &name) {
-	error("TODO: Implemet me");
+	for (unsigned int i = 0; i < _gameSounds.size(); i++) {
+		GameSound *sound = _gameSounds[i];
+		if (sound->getAccessName() == name) {
+			sound->stop();
+			sound->deleteLater();
+		}
+		_gameSounds.remove_at(i);
+	}
+	g_engine->getSoundManager()->stopFreeSound(name);
 }
 
 bool Game::unloadCharacter(const Common::String &charname) {
@@ -1234,7 +1315,7 @@ void Game::update() {
 	if (!_entered)
 		return;
 
-	TeTextLayout *debugTimeTextLayout = _gui4.textLayout("debugTimeText1");
+	TeTextLayout *debugTimeTextLayout = _inGameGui.textLayout("debugTimeText1");
 	if (debugTimeTextLayout) {
 		warning("TODO: Game::update: Fill out debugTimeTextLayout");
 	}
@@ -1254,7 +1335,7 @@ void Game::update() {
 				app->_lockCursorButton.setVisible(false);
 		}
 
-		TeButtonLayout *invbtn = _gui4.buttonLayout("inventoryButton");
+		TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
 		if (invbtn)
 			invbtn->setVisible(!app->isLockCursor() && !_dialog2.isDialogPlaying());
 		else
@@ -1305,7 +1386,7 @@ void Game::update() {
 		}
 		if (_saveRequested) {
 			_saveRequested = false;
-			error("TODO: Game::update: Save game");
+			saveBackup("save.xml");
 		}
 
 		_luaScript.execute("Update");
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index c7bf5dca547..f383f899f3a 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -41,6 +41,8 @@
 
 namespace Tetraedge {
 
+class TeLuaThread;
+
 class Game {
 public:
 	Game();
@@ -67,6 +69,13 @@ public:
 		byte onSoundFinished();
 	};
 
+	struct YieldedCallback {
+		TeLuaThread *_luaThread;
+		Common::String _luaParam;
+		Common::String _luaFnName;
+		// Note: original game has more String, long, and int fields.. seem unused.
+	};
+
 	//enum EGameScoreID {}; // Not needed?
 
 	bool addAnimToSet(const Common::String &path);
@@ -89,8 +98,8 @@ public:
 	void draw();
 	void enter(bool newgame);
 	// Note: game uses ILayouts here..
-	static TeI3DObject2 *findLayoutByName(TeLayout *layout, const Common::String &name);
-	static TeSpriteLayout *findSpriteLayoutByName(TeLayout *layout, const Common::String &name);
+	static TeI3DObject2 *findLayoutByName(TeLayout *parent, const Common::String &name);
+	static TeSpriteLayout *findSpriteLayoutByName(TeLayout *parent, const Common::String &name);
 
 	void finishFreemium();
 	void finishGame();
@@ -138,10 +147,10 @@ public:
 	void resumeMovie();
 	void resumeSounds() {}; // does nothing?
 	void saveBackup(const Common::String &saveName);
-	void setBackground(const Common::String &name);
+	bool setBackground(const Common::String &name);
 	void setCurrentObjectSprite(const Common::String &spritePath);
 	bool showMarkers(bool val);
-	bool startAnimation(const Common::String &animName, int param_2, bool param_3);
+	bool startAnimation(const Common::String &animName, int loopcount, bool reversed);
 	void startAnimationPart(const Common::String &param_1, int param_2, int param_3, int param_4, bool param_5) {};
 	void stopSound(const Common::String &name);
 	bool unloadCharacter(const Common::String &character);
@@ -167,6 +176,11 @@ public:
 	InGameScene &scene() { return _scene; }
 	Dialog2 &dialog2() { return _dialog2; }
 	Question2 &question2() { return _question2; }
+	TeLuaGUI &gui3() { return _gui3; }
+	Objectif &objectif() { return _objectif; }
+	Common::Array<YieldedCallback> yieldedCallbacks() { return _yieldedCallbacks; }
+	void setSaveRequested() { _saveRequested = true; }
+	bool markersVisible() const { return _markersVisible; }
 
 private:
 	bool _luaShowOwnerError;
@@ -174,10 +188,10 @@ private:
 	bool _entered;
 	bool _enteredFlag2;
 
-	TeLuaGUI _gui1;
+	TeLuaGUI _gui1; // TODO: get better names for these.
 	TeLuaGUI _gui2;
 	TeLuaGUI _gui3;
-	TeLuaGUI _gui4;
+	TeLuaGUI _inGameGui;
 
 	Inventory _inventory;
 	InventoryMenu _inventoryMenu;
@@ -207,6 +221,8 @@ private:
 
 	Common::Array<GameSound *> _gameSounds;
 	Common::Array<HitObject *> _gameHitObjects;
+	// These are static in original, but cleaner to keep here.
+	Common::Array<YieldedCallback> _yieldedCallbacks;
 
 	Common::HashMap<Common::String, bool> _unlockedArtwork;
 	Common::HashMap<Common::String, Common::Array<RandomSound*>> _randomSounds;
@@ -217,6 +233,7 @@ private:
 	TeTimer _walkTimer;
 	TeLuaScript _luaScript;
 	TeLuaContext _luaContext;
+	TeLuaScript _gameEnterScript;
 	TeMusic _music;
 	Notifier _notifier;
 	DocumentsBrowser _documentsBrowser;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 2a89abd7afa..157ef41ff73 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -35,13 +35,19 @@
 
 #include "tetraedge/te/te_bezier_curve.h"
 #include "tetraedge/te/te_camera.h"
+#include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_free_move_zone.h"
 #include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_lua_script.h"
+#include "tetraedge/te/te_lua_thread.h"
 
 namespace Tetraedge {
 
-InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr), _shadowLightNo(-1) {
+InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr),
+_shadowLightNo(-1), _waitTime(-1.0f), _shadowColor(0, 0, 0, 0x80), _shadowFov(20.0f),
+_shadowFarPlane(1000), _shadowNearPlane(1)
+ {
 }
 
 void InGameScene::activateAnchorZone(const Common::String &name, bool val) {
@@ -51,6 +57,253 @@ void InGameScene::activateAnchorZone(const Common::String &name, bool val) {
 	}
 }
 
+void InGameScene::addAnchorZone(const Common::String &s1, const Common::String &name, float radius) {
+	for (AnchorZone *zone : _anchorZones) {
+		if (zone->_name == name) {
+			zone->_radius = radius;
+			return;
+		}
+	}
+	warning("TODO: Finish InGameScene::addAnchorZone");
+}
+
+bool InGameScene::addMarker(const Common::String &markerName, const Common::String &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal) {
+	const TeMarker *marker = findMarker(markerName);
+	if (!marker) {
+		Game *game = g_engine->getGame();
+		TeSpriteLayout *markerSprite = new TeSpriteLayout();
+		// Note: game checks paths here but seems to just use the original?
+		markerSprite->setName(markerName);
+		markerSprite->setAnchor(TeVector3f32(0.0f, 0.0f, 0.0f));
+		markerSprite->load(imgPath);
+		markerSprite->setSizeType(TeILayout::RELATIVE_TO_PARENT);
+			markerSprite->setPositionType(TeILayout::RELATIVE_TO_PARENT);
+		TeVector3f32 newSize;
+		if (locType == "PERCENT") {
+			Application *app = g_engine->getApplication();
+			TeVector3f32 frontLayoutSize = app->_frontLayout.userSize();
+			newSize.x() = frontLayoutSize.x() * (x / 100.0);
+			newSize.y() = frontLayoutSize.y() * (y / 100.0);
+		} else {
+			newSize.x() = x / 800.0;
+			newSize.y() = y / 600.0;
+		}
+		markerSprite->setSize(newSize);
+
+		float screenWidth = (float)g_engine->getDefaultScreenWidth();
+		float screenHeight = (float)g_engine->getDefaultScreenHeight();
+		if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
+			markerSprite->setPosition(TeVector3f32(0.07, (4.0 / ((screenWidth / screenHeight) * 4.0)) * 0.04, 0.0));
+		} else {
+			markerSprite->setPosition(TeVector3f32(0.04, (4.0 / ((screenWidth / screenHeight) * 4.0)) * 0.04, 0.0));
+		}
+		markerSprite->setVisible(game->markersVisible());
+		markerSprite->_tiledSurfacePtr->_frameAnim._loopCount = -1;
+		markerSprite->play();
+
+		TeMarker newMarker;
+		newMarker._name = markerName;
+		newMarker._val = markerVal;
+		_markers.push_back(newMarker);
+		TeLayout *bg = game->gui3().layout("background");
+		if (bg)
+			bg->addChild(markerSprite);
+		_sprites.push_back(markerSprite);
+	} else  {
+		setImagePathMarker(markerName, imgPath);
+	}
+	return true;
+}
+
+/*static*/
+float InGameScene::angularDistance(float a1, float a2) {
+	float result;
+
+	result = a2 - a1;
+	if (result >= -3.141593 && result > 3.141593) {
+		result = result + -6.283185;
+	} else {
+		result = result + 6.283185;
+	}
+	return result;
+}
+
+bool InGameScene::aroundAnchorZone(const AnchorZone *zone) {
+	if (!zone->_activated)
+		return false;
+	const TeVector3f32 charpos = _character->_model->position();
+
+	float xoff = charpos.x() - zone->_loc.x();
+	float zoff = charpos.z() - zone->_loc.z();
+    return sqrt(xoff * xoff + zoff * zoff) <= zone->_radius;
+}
+
+TeLayout *InGameScene::background() {
+	return _bgGui.layout("background");
+}
+
+Billboard *InGameScene::billboard(const Common::String &name) {
+	for (Billboard *billboard : _billboards) {
+		if (billboard->model()->name() == name)
+			return billboard;
+	}
+	return nullptr;
+}
+
+bool InGameScene::changeBackground(const Common::String &name) {
+	if (Common::File::exists(name)) {
+		_bgGui.spriteLayoutChecked("root")->load(name);
+		return true;
+	}
+	return false;
+}
+
+Character *InGameScene::character(const Common::String &name) {
+	if (_character->_model->name() == name)
+		return _character;
+
+	for (Character *c : _characters) {
+		if (c->_model->name() == name)
+			return c;
+	}
+
+	return nullptr;
+}
+
+void InGameScene::close() {
+	reset();
+	_loadedPath = "";
+	TeScene::close();
+	freeGeometry();
+	if (_character && _character->_model && !findKate()) {
+		models().push_back(_character->_model);
+		models().push_back(_character->_shadowModel[0]);
+		models().push_back(_character->_shadowModel[1]);
+	}
+	_objects.clear();
+	for (TeFreeMoveZone *zone : _freeMoveZones)
+		delete zone;
+	_freeMoveZones.clear();
+	_hitObjects.clear();
+	for (TePickMesh2 *mesh : _pickMeshes)
+		delete mesh;
+	_pickMeshes.clear();
+	_bezierCurves.clear();
+	_dummies.clear();
+	freeSceneObjects();
+}
+
+void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
+	TeIntrusivePtr<TeModel> model = new TeModel();
+	model->_meshes.resize(1);
+	model->setName("shadowReceiving");
+	model->setPosition(zone->position());
+	model->setRotation(zone->rotation());
+	model->setScale(zone->scale());
+	unsigned long nverticies = zone->verticies().size();
+	model->_meshes[0].setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
+	for (unsigned int i = 0; i < nverticies; i++) {
+		model->_meshes[0].setIndex(i, i);
+		model->_meshes[0].setVertex(i, zone->verticies()[i]);
+		model->_meshes[0].setNormal(i, TeVector3f32(0, 0, 1));
+	}
+	_zoneModels.push_back(model);
+}
+
+TeIntrusivePtr<TeBezierCurve> InGameScene::curve(const Common::String &curveName) {
+	for (TeIntrusivePtr<TeBezierCurve> &c : _bezierCurves) {
+		if (c->name() == curveName)
+			return c;
+	}
+	return TeIntrusivePtr<TeBezierCurve>();
+}
+
+void InGameScene::deleteAllCallback() {
+	warning("TODO: implement InGameScene::deleteAllCallback");
+}
+
+void InGameScene::deleteMarker(const Common::String &markerName) {
+	if (!isMarker(markerName))
+		return;
+
+	for (unsigned int i = 0; i < _markers.size(); i++) {
+		if (_markers[i]._name == markerName) {
+			_markers.remove_at(i);
+			break;
+		}
+	}
+
+	Game *game = g_engine->getGame();
+	TeLayout *bg = game->gui3().layout("background");
+	if (!bg)
+		return;
+	for (Te3DObject2 *child : bg->childList()) {
+		if (child->name() == markerName) {
+			bg->removeChild(child);
+			break;
+		}
+	}
+}
+
+void InGameScene::deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam) {
+	cam->_projectionMatrixType = 2;
+	cam->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
+	// load name/position/rotation/scale
+	Te3DObject2::deserialize(stream, *cam);
+	cam->_fov = stream.readFloatLE();
+	cam->_somePerspectiveVal = stream.readFloatLE();
+	cam->_orthNearVal = stream.readFloatLE();
+	// Original loads the val then ignores it and sets 3000.
+	stream.readFloatLE();
+	cam->_orthFarVal = 3000.0;
+}
+
+void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<TeModel> &model, TePickMesh2 *pickmesh) {
+	TeVector3f32 vec;
+	TeVector2f32 vec2;
+	TeQuaternion rot;
+	TeColor col;
+	TeMesh mesh;
+
+	TeVector3f32::deserialize(stream, vec);
+	model->setPosition(vec);
+	pickmesh->setPosition(vec);
+	TeQuaternion::deserialize(stream, rot);
+	model->setRotation(rot);
+	pickmesh->setRotation(rot);
+	TeVector3f32::deserialize(stream, vec);
+	model->setScale(vec);
+	pickmesh->setScale(vec);
+	uint32 indexcount = stream.readUint32LE();
+	uint32 vertexcount = stream.readUint32LE();
+
+	mesh.setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
+	for (unsigned int i = 0; i < indexcount; i++)
+		mesh.setIndex(i, stream.readUint32LE());
+	for (unsigned int i = 0; i < vertexcount; i++) {
+		TeVector3f32::deserialize(stream, vec);
+		mesh.setVertex(i, vec);
+	}
+	for (unsigned int i = 0; i < vertexcount; i++) {
+		TeVector3f32::deserialize(stream, vec);
+		mesh.setNormal(i, vec);
+	}
+	for (unsigned int i = 0; i < vertexcount; i++) {
+		TeVector2f32::deserialize(stream, vec2);
+		mesh.setTextureUV(i, vec2);
+	}
+	for (unsigned int i = 0; i < vertexcount; i++) {
+		col.deserialize(stream);
+		mesh.setColor(i, col);
+	}
+	pickmesh->setNbTriangles(indexcount / 3);
+	for (unsigned int i = 0; i < indexcount; i++) {
+		vec = mesh.vertex(mesh.index(i));
+		pickmesh->verticies().push_back(vec);
+	}
+	model->addMesh(mesh);
+}
+
 void InGameScene::draw() {
 	TeScene::draw();
 
@@ -78,49 +331,59 @@ void InGameScene::drawPath() {
 	g_engine->getRenderer()->enableZBuffer();
 }
 
-
-bool InGameScene::changeBackground(const Common::String &name) {
-	if (Common::File::exists(name)) {
-		_bgGui.spriteLayoutChecked("root")->load(name);
-		return true;
+InGameScene::Dummy InGameScene::dummy(const Common::String &name) {
+	for (const Dummy &dummy : _dummies) {
+		if (dummy._name == name)
+			return dummy;
 	}
-	return false;
+	return Dummy();
 }
 
-/*static*/
-float InGameScene::angularDistance(float a1, float a2) {
-	float result;
-
-	result = a2 - a1;
-	if (result >= -3.141593 && result > 3.141593) {
-		result = result + -6.283185;
-	} else {
-		result = result + 6.283185;
+bool InGameScene::findKate() {
+	for (auto &m : models()) {
+		if (m->name() == "Kate")
+			return true;
 	}
-	return result;
+	return false;
 }
 
-TeLayout *InGameScene::background() {
-	return _bgGui.layout("background");
+const InGameScene::TeMarker *InGameScene::findMarker(const Common::String &name) {
+	for (const TeMarker &marker : _markers) {
+		if (marker._name == name)
+			return ▮
+	}
+	return nullptr;
 }
 
-void InGameScene::close() {
-	reset();
-	error("TODO: Implement InGameScene::close");
+const InGameScene::TeMarker *InGameScene::findMarkerByInt(const Common::String &val) {
+	for (const TeMarker &marker : _markers) {
+		if (marker._val == val)
+			return ▮
+	}
+	return nullptr;
 }
 
-void InGameScene::reset() {
-	if (_character)
-		_character->setFreeMoveZone(nullptr);
-	freeSceneObjects();
-	_bgGui.unload();
-	unloadSpriteLayouts();
-	_markerGui.unload();
-	_hitObjectGui.unload();
+InGameScene::SoundStep InGameScene::findSoundStep(const Common::String &name) {
+	for (const auto &step : _soundSteps) {
+		if (step._key == name)
+			return step._value;
+	}
+	return SoundStep();
 }
 
-void InGameScene::deleteAllCallback() {
-	warning("TODO: implement InGameScene::deleteAllCallback");
+void InGameScene::freeGeometry() {
+	_loadedPath.set("");
+	for (TeFreeMoveZone *zone : _freeMoveZones)
+		delete zone;
+	_freeMoveZones.clear();
+	_bezierCurves.clear();
+	_dummies.clear();
+	cameras().clear();
+	_zoneModels.clear();
+	if (_charactersShadow) {
+		delete _charactersShadow;
+		_charactersShadow = nullptr;
+	}
 }
 
 void InGameScene::freeSceneObjects() {
@@ -161,15 +424,48 @@ void InGameScene::freeSceneObjects() {
 	_anchorZones.clear();
 }
 
-void InGameScene::unloadSpriteLayouts() {
-	for (auto *animobj : _animObjects) {
-		delete animobj;
+float InGameScene::getHeadHorizontalRotation(Character *cter, const TeVector3f32 &vec) {
+	//TeVector3f32 pos = cter->_model->position() - vec;
+	error("TODO: Implement InGameScene::getHeadHorizontalRotation");
+}
+
+float InGameScene::getHeadVerticalRotation(Character *cter, const TeVector3f32 &vec) {
+	//TeVector3f32 pos = cter->_model->position() - vec;
+	error("TODO: Implement InGameScene::getHeadVerticalRotation");
+}
+
+Common::String InGameScene::imagePathMarker(const Common::String &name) {
+	if (!isMarker(name))
+		return Common::String();
+	Game *game = g_engine->getGame();
+	TeLayout *bg = game->gui3().layoutChecked("background");
+	for (Te3DObject2 *child : bg->childList()) {
+		TeSpriteLayout *spritelayout = dynamic_cast<TeSpriteLayout *>(child);
+		if (spritelayout && spritelayout->name() == name) {
+			return spritelayout->_tiledSurfacePtr->path().toString();
+		}
 	}
-	_animObjects.clear();
+	return Common::String();
 }
 
-Character *InGameScene::character(const Common::String &name) {
-	error("TODO: Implement InGameScene::character");
+void InGameScene::initScroll() {
+	_someScrollVector = TeVector2f32(0.5f, 0.0f);
+}
+
+bool InGameScene::isMarker(const Common::String &name) {
+	for (const TeMarker &marker : _markers) {
+		if (marker._name == name)
+			return true;
+	}
+	return false;
+}
+
+bool InGameScene::isObjectBlocking(const Common::String &name) {
+	for (const Common::String &b: _blockingObjects) {
+		if (name == b)
+			return true;
+	}
+	return false;
 }
 
 bool InGameScene::load(const Common::Path &path) {
@@ -262,7 +558,8 @@ bool InGameScene::load(const Common::Path &path) {
 	for (unsigned int i = 0; i < nfreemovezones; i++) {
 		TeFreeMoveZone *zone = new TeFreeMoveZone();
 		TeFreeMoveZone::deserialize(scenefile, *zone, &_blockers, &_rectBlockers, &_actZones);
-
+		_freeMoveZones.push_back(zone);
+		zone->setVisible(false);
 	}
 
 	uint32 ncurves = scenefile.readUint32LE();
@@ -298,20 +595,31 @@ bool InGameScene::load(const Common::Path &path) {
 	_charactersShadow = new CharactersShadow();
 	_charactersShadow->create(this);
 	onMainWindowSizeChanged();
-	return true;
-}
 
-void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
-	error("TODO: Implement InGameScene::convertPathToMesh");
+	// FIXME: For some reason the game never re-adds kate's model to the list here..
+	// how does it ever get drawn???
+	if (!findKate()) {
+		models().push_back(_character->_model);
+	}
+
+	return true;
 }
 
-void InGameScene::onMainWindowSizeChanged() {
-	TeCamera *mainWinCam = g_engine->getApplication()->mainWindowCamera();
-	_viewportSize = mainWinCam->viewportSize();
-	Common::Array<TeIntrusivePtr<TeCamera>> &cams = cameras();
-	for (unsigned int i = 0; i < cams.size(); i++) {
-		cams[i]->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
+bool InGameScene::loadCharacter(const Common::String &name) {
+	Character *c = character(name);
+	if (!c) {
+		c = new Character();
+		if (!c->loadModel(name, false)) {
+			delete c;
+			return false;
+		}
+		models().push_back(c->_model);
+		models().push_back(c->_shadowModel[0]);
+		models().push_back(c->_shadowModel[1]);
+		_characters.push_back(c);
 	}
+	c->_model->setVisible(true);
+	return true;
 }
 
 bool InGameScene::loadLights(const Common::Path &path) {
@@ -333,67 +641,35 @@ bool InGameScene::loadLights(const Common::Path &path) {
 	return true;
 }
 
-bool InGameScene::loadCharacter(const Common::String &name) {
-	error("TODO: Implement InGameScene::loadCharacter");
+void InGameScene::loadMarkers(const Common::Path &path) {
+	_markerGui.load(path);
+	TeLayout *bg = _bgGui.layoutChecked("background");
+	TeSpriteLayout *root = Game::findSpriteLayoutByName(bg, "root");
+	bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
+	root->addChild(bg);
 }
 
-void InGameScene::deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam) {
-	cam->_projectionMatrixType = 2;
-	cam->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
-	// load name/position/rotation/scale
-	Te3DObject2::deserialize(stream, *cam);
-	cam->_fov = stream.readFloatLE();
-	cam->_somePerspectiveVal = stream.readFloatLE();
-	cam->_orthNearVal = stream.readFloatLE();
-	// Original loads the val then ignores it and sets 3000.
-	stream.readFloatLE();
-	cam->_orthFarVal = 3000.0;
+bool InGameScene::loadObject(const Common::String &name) {
+	Object3D *obj = object3D(name);
+	if (!obj) {
+		obj = new Object3D();
+		if (!obj->loadModel(name)) {
+			delete obj;
+			return false;
+		}
+		models().push_back(obj->model());
+		_object3Ds.push_back(obj);
+	}
+	obj->model()->setVisible(true);
+	return true;
 }
 
-void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<TeModel> &model, TePickMesh2 *pickmesh) {
-	TeVector3f32 vec;
-	TeVector2f32 vec2;
-	TeQuaternion rot;
-	TeColor col;
-	TeMesh mesh;
-
-	TeVector3f32::deserialize(stream, vec);
-	model->setPosition(vec);
-	pickmesh->setPosition(vec);
-	TeQuaternion::deserialize(stream, rot);
-	model->setRotation(rot);
-	pickmesh->setRotation(rot);
-	TeVector3f32::deserialize(stream, vec);
-	model->setScale(vec);
-	pickmesh->setScale(vec);
-	uint32 indexcount = stream.readUint32LE();
-	uint32 vertexcount = stream.readUint32LE();
+void InGameScene::loadObjectMaterials(const Common::String &name) {
+	error("TODO: InGameScene::loadObjectMaterials");
+}
 
-	mesh.setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
-	for (unsigned int i = 0; i < indexcount; i++)
-		mesh.setIndex(i, stream.readUint32LE());
-	for (unsigned int i = 0; i < vertexcount; i++) {
-		TeVector3f32::deserialize(stream, vec);
-		mesh.setVertex(i, vec);
-	}
-	for (unsigned int i = 0; i < vertexcount; i++) {
-		TeVector3f32::deserialize(stream, vec);
-		mesh.setNormal(i, vec);
-	}
-	for (unsigned int i = 0; i < vertexcount; i++) {
-		TeVector2f32::deserialize(stream, vec2);
-		mesh.setTextureUV(i, vec2);
-	}
-	for (unsigned int i = 0; i < vertexcount; i++) {
-		col.deserialize(stream);
-		mesh.setColor(i, col);
-	}
-	pickmesh->setNbTriangles(indexcount / 3);
-	for (unsigned int i = 0; i < indexcount; i++) {
-		vec = mesh.vertex(mesh.index(i));
-		pickmesh->verticies().push_back(vec);
-	}
-	model->addMesh(mesh);
+void InGameScene::loadObjectMaterials(const Common::String &path, const Common::String &name) {
+	error("TODO: InGameScene::loadObjectMaterials");
 }
 
 bool InGameScene::loadPlayerCharacter(const Common::String &name) {
@@ -407,10 +683,9 @@ bool InGameScene::loadPlayerCharacter(const Common::String &name) {
 		_playerCharacterModel = _character->_model;
 
 		if (!findKate()) {
-			Common::Array<TeIntrusivePtr<TeModel>> &ms = models();
-			ms.push_back(_character->_model);
-			ms.push_back(_character->_shadowModel[0]);
-			ms.push_back(_character->_shadowModel[1]);
+			models().push_back(_character->_model);
+			models().push_back(_character->_shadowModel[0]);
+			models().push_back(_character->_shadowModel[1]);
 		}
 	}
 
@@ -418,25 +693,6 @@ bool InGameScene::loadPlayerCharacter(const Common::String &name) {
 	return true;
 }
 
-void InGameScene::unloadCharacter(const Common::String &name) {
-	warning("TODO: Implement InGameScene::unloadCharacter %s", name.c_str());
-}
-
-bool InGameScene::findKate() {
-	for (auto &m : models()) {
-		if (m->name() == "Kate")
-			return true;
-	}
-	return false;
-}
-
-TeLight *InGameScene::shadowLight() {
-	if (_shadowLightNo == -1) {
-		return nullptr;
-	}
-	return &_lights[_shadowLightNo];
-}
-
 static Common::Path _sceneFileNameBase() {
 	Game *game = g_engine->getGame();
 	Common::Path retval("scenes");
@@ -517,6 +773,20 @@ void InGameScene::loadBackground(const Common::Path &path) {
 	}
 }
 
+bool InGameScene::loadBillboard(const Common::String &name) {
+	Billboard *b = billboard(name);
+	if (b)
+		return true;
+	b = new Billboard();
+	if (b->load(name)) {
+		_billboards.push_back(b);
+		return true;
+	} else {
+		delete b;
+		return false;
+	}
+}
+
 void InGameScene::loadInteractions(const Common::Path &path) {
 	_hitObjectGui.load(path);
 	TeLayout *bgbackground = _bgGui.layoutChecked("background");
@@ -530,92 +800,199 @@ void InGameScene::loadInteractions(const Common::Path &path) {
 	root->addChild(background);
 }
 
-void InGameScene::setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2) {
-	SoundStep ss;
-	ss._stepSound1 = step1;
-	ss._stepSound2 = step2;
-	_soundSteps[scene] = ss;
+void InGameScene::moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd) {
+	Character *c = character(charName);
+	if (c != nullptr && c != _character) {
+		Game *game = g_engine->getGame();
+		if (!game->_movePlayerCharacterDisabled) {
+			// TODO: c->field_0x214 = c->characterSettings()._cutSceneCurveDemiPosition;
+			TeIntrusivePtr<TeBezierCurve> crve = curve(curveName);
+			c->placeOnCurve(crve);
+			c->setCurveOffset(curveOffset);
+			const Common::String walkStartAnim = c->walkAnim(Character::WalkPart_Start);
+			if (walkStartAnim.empty()) {
+				c->setAnimation(c->walkAnim(Character::WalkPart_Loop), true, false, false, -1, 9999);
+			} else {
+				c->setAnimation(c->walkAnim(Character::WalkPart_Start), false, false, false, -1, 9999);
+			}
+			c->walkTo(curveEnd, false);
+			error("TODO: Finish InGameScene::moveCharacterTo");
+		}
+	}
 }
 
-void InGameScene::initScroll() {
-	_someScrollVector = TeVector2f32(0.5f, 0.0f);
+void InGameScene::onMainWindowSizeChanged() {
+	TeCamera *mainWinCam = g_engine->getApplication()->mainWindowCamera();
+	_viewportSize = mainWinCam->viewportSize();
+	Common::Array<TeIntrusivePtr<TeCamera>> &cams = cameras();
+	for (unsigned int i = 0; i < cams.size(); i++) {
+		cams[i]->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
+	}
 }
 
-bool InGameScene::isObjectBlocking(const Common::String &name) {
-	for (const Common::String &b: _blockingObjects) {
-		if (name == b)
-			return true;
+Object3D *InGameScene::object3D(const Common::String &name) {
+	for (Object3D *obj : _object3Ds) {
+		if (obj->model()->name() == name)
+			return obj;
 	}
-	return false;
+	return nullptr;
 }
 
-void InGameScene::addMarker(const Common::String &name, const Common::String &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal) {
-	error("TODO: Implement InGameScene::addMarker");
+TeFreeMoveZone *InGameScene::pathZone(const Common::String &name) {
+	for (TeFreeMoveZone *zone: _freeMoveZones) {
+		if (zone->name() == name)
+			return zone;
+	}
+	return nullptr;
 }
 
-void InGameScene::setVisibleMarker(const Common::String &markerName, bool val) {
+void InGameScene::reset() {
+	if (_character)
+		_character->setFreeMoveZone(nullptr);
+	freeSceneObjects();
+	_bgGui.unload();
+	unloadSpriteLayouts();
+	_markerGui.unload();
+	_hitObjectGui.unload();
+}
+
+TeLight *InGameScene::shadowLight() {
+	if (_shadowLightNo == -1) {
+		return nullptr;
+	}
+	return &_lights[_shadowLightNo];
+}
+
+void InGameScene::setImagePathMarker(const Common::String &markerName, const Common::String &path) {
 	if (!isMarker(markerName))
 		return;
 
-	error("TODO: Implement InGameScene::setVisibleMarker");
+	Game *game = g_engine->getGame();
+	TeLayout *bg = game->gui3().layoutChecked("background");
+
+	for (Te3DObject2 *child : bg->childList()) {
+		if (child->name() == markerName) {
+			TeSpriteLayout *sprite = dynamic_cast<TeSpriteLayout *>(child);
+			if (sprite) {
+				sprite->load(path);
+				sprite->_tiledSurfacePtr->_frameAnim._loopCount = -1;
+				sprite->play();
+			}
+		}
+	}
 }
 
+void InGameScene::setPositionCharacter(const Common::String &charName, const Common::String &freeMoveZoneName, const TeVector3f32 &position) {
+	Character *c = character(charName);
+	if (!c) {
+		warning("[SetCharacterPosition] Character not found %s", charName.c_str());
+	} else if (c == _character && c->positionFlag()) {
+		c->setFreeMoveZoneName(freeMoveZoneName);
+		c->setPositionCharacter(position);
+		c->setPositionFlag(false);
+		c->setNeedsSomeUpdate(true);
+	} else {
+		c->stop();
+		TeFreeMoveZone *zone = pathZone(freeMoveZoneName);
+		if (!zone) {
+			warning("[SetCharacterPosition] PathZone not found %s", freeMoveZoneName.c_str());
+			for (TeFreeMoveZone *zone : _freeMoveZones)
+				warning("zone: %s", zone->name().c_str());
+			return;
+		}
+		TeIntrusivePtr<TeCamera> cam = currentCamera();
+		zone->setCamera(cam, false);
+		c->setFreeMoveZone(zone);
+		SoundStep step = findSoundStep(freeMoveZoneName);
+		c->setStepSound(step._stepSound1, step._stepSound2);
+		bool correctFlag = true;
+		const TeVector3f32 corrected = zone->correctCharacterPosition(position, &correctFlag, true);
+		c->_model->setPosition(corrected);
+		if (!correctFlag)
+			error("[SetCharacterPosition] Warning : The character is not above the ground %s", charName.c_str());
+	}
+}
 
-void InGameScene::deleteMarker(const Common::String &markerName) {
+void InGameScene::setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2) {
+	SoundStep ss;
+	ss._stepSound1 = step1;
+	ss._stepSound2 = step2;
+	_soundSteps[scene] = ss;
+}
+
+void InGameScene::setVisibleMarker(const Common::String &markerName, bool val) {
 	if (!isMarker(markerName))
 		return;
 
-	error("TODO: Implement InGameScene::deleteMarker");
+	error("TODO: Implement InGameScene::setVisibleMarker");
 }
 
-
-bool InGameScene::isMarker(const Common::String &name) {
-	for (const TeMarker *marker : _markers) {
-		if (marker->_name == name)
-			return true;
+void InGameScene::unloadCharacter(const Common::String &name) {
+	if (_character && _character->_model->name() == name) {
+		_character->removeAnim();
+		_character->deleteAnim();
+		_character->deleteAllCallback();
+		// TODO: deleteLater() something here..
+		_character = nullptr;
+	}
+	for (unsigned int i = 0; i < _characters.size(); i++) {
+		Character *c = _characters[i];
+		if (c && c->_model->name() == name) {
+			c->removeAnim();
+			c->deleteAnim();
+			c->deleteAllCallback();
+			// TODO: deleteLater() something here..
+			_characters.remove_at(i);
+			break;
+		}
 	}
-	return false;
 }
 
-TeFreeMoveZone *InGameScene::pathZone(const Common::String &name) {
-	for (TeFreeMoveZone *zone: _freeMoveZones) {
-		if (zone->name() == name)
-			return zone;
+void InGameScene::unloadObject(const Common::String &name) {
+	error("TODO: InGameScene::unloadObject");
+}
+
+void InGameScene::unloadSpriteLayouts() {
+	for (auto *animobj : _animObjects) {
+		delete animobj;
 	}
-	return nullptr;
+	_animObjects.clear();
 }
 
-void InGameScene::moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd) {
-	Character *c = character(charName);
-	if (c != nullptr && c != _character) {
-		Game *game = g_engine->getGame();
-		if (!game->_movePlayerCharacterDisabled) {
-			// TODO: c->field_0x214 = c->characterSettings()._cutSceneCurveDemiPosition;
-			TeIntrusivePtr<TeBezierCurve> crve = curve(curveName);
-			c->placeOnCurve(crve);
-			c->setCurveOffset(curveOffset);
-			const Common::String walkStartAnim = c->walkAnim(Character::WalkPart_Start);
-			if (walkStartAnim.empty()) {
-				c->setAnimation(c->walkAnim(Character::WalkPart_Loop), true, false, false, -1, 9999);
-			} else {
-				c->setAnimation(c->walkAnim(Character::WalkPart_Start), false, false, false, -1, 9999);
+void InGameScene::update() {
+	Game *game = g_engine->getGame();
+	if (_bgGui.loaded()) {
+		_bgGui.layoutChecked("background")->setZPosition(0.0f);
+	}
+	if (_character) {
+		// TODO: Do some stuff for character here.
+	}
+	for (Character *c : _characters) {
+		// TODO: Something with other characters.
+	}
+	// TODO: some other stuff with callbacks and spritelayouts here
+
+	TeScene::update();
+
+	float waitTime = _waitTimeTimer.timeFromLastTimeElapsed();
+	if (_waitTime != -1.0 && waitTime > _waitTime) {
+		bool resumed = false;
+		for (unsigned int i = 0; i < game->yieldedCallbacks().size(); i++) {
+			Game::YieldedCallback &yc = game->yieldedCallbacks()[i];
+			if (yc._luaFnName == "OnWaitFinished") {
+				TeLuaThread *thread = yc._luaThread;
+				game->yieldedCallbacks().remove_at(i);
+				thread->resume();
+				resumed = true;
 			}
-			c->walkTo(curveEnd, false);
-			error("TODO: Finish InGameScene::moveCharacterTo");
 		}
+		if (!resumed)
+			game->luaScript().execute("OnWaitFinished");
 	}
-}
 
-TeIntrusivePtr<TeBezierCurve> InGameScene::curve(const Common::String &curveName) {
-	for (TeIntrusivePtr<TeBezierCurve> &c : _bezierCurves) {
-		if (c->name() == curveName)
-			return c;
+	for (Object3D *obj : _object3Ds) {
+		// TODO: something with object3ds
 	}
-	return TeIntrusivePtr<TeBezierCurve>();
-}
-
-void InGameScene::setPositionCharacter(const Common::String &charName, const Common::String &freeMoveZoneName, const TeVector3f32 &position) {
-	error("TODO: Implement InGameScene::setPositionCharacter");
 }
 
 
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index e18559207c8..cc8dcf76f61 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -62,6 +62,8 @@ public:
 	struct AnchorZone {
 		Common::String _name;
 		bool _activated;
+		TeVector3f32 _loc;
+		float _radius;
 	};
 
 	struct Object {
@@ -82,64 +84,89 @@ public:
 	};
 
 	void activateAnchorZone(const Common::String &name, bool val);
-	void addAnchorZone(const Common::String &param_1, const Common::String &param_2, float param_3);
+	void addAnchorZone(const Common::String &s1, const Common::String &name, float radius);
 	void addBlockingObject(const Common::String &obj) {
 		_blockingObjects.push_back(obj);
 	}
 	void addCallbackAnimation2D(const Common::String &param_1, const Common::String &param_2, float param_3);
-	void addMarker(const Common::String &name, const Common::String &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal);
+	bool addMarker(const Common::String &name, const Common::String &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal);
 	static float angularDistance(float a1, float a2);
 	bool aroundAnchorZone(const AnchorZone *zone);
 	TeLayout *background();
-	virtual bool load(const Common::Path &path) override;
-	void loadBackground(const Common::Path &path);
-	void loadInteractions(const Common::Path &path);
-	void initScroll();
-	bool isObjectBlocking(const Common::String &name);
-	bool isMarker(const Common::String &name);
-	TeFreeMoveZone *pathZone(const Common::String &name);
-	void moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd);
-	TeIntrusivePtr<TeBezierCurve> curve(const Common::String &curveName);
-	void setPositionCharacter(const Common::String &charName, const Common::String &freeMoveZoneName, const TeVector3f32 &position);
-	void setVisibleMarker(const Common::String &markerName, bool val);
-	void deleteMarker(const Common::String &markerName);
-
-	void draw();
-	void drawPath();
-	Character *character(const Common::String &name);
-	bool loadCharacter(const Common::String &name);
-	void loadBlockers();
-	bool loadPlayerCharacter(const Common::String &name);
-	bool loadLights(const Common::Path &path);
+	Billboard *billboard(const Common::String &name);
+	TeVector2f32 boundLayerSize();
 	bool changeBackground(const Common::String &name);
-	void unloadCharacter(const Common::String &name);
-
+	Character *character(const Common::String &name);
+	virtual void close() override;
 	// Original has a typo, "converPathToMesh", corrected.
 	void convertPathToMesh(TeFreeMoveZone *zone);
-	void onMainWindowSizeChanged();
-
+	TeIntrusivePtr<TeBezierCurve> curve(const Common::String &curveName);
+	void deleteAllCallback();
+	void deleteCallback(const Common::String &key, const Common::String &name, float f);
+	void deleteMarker(const Common::String &markerName);
 	// Original just calls these "deserialize" but that's a fairly vague name
 	// so renamed to be more meaningful.
 	void deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam);
 	void deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<TeModel> &model, TePickMesh2 *pickmesh);
-
-	void close();
-	void reset();
+	virtual void draw() override;
+	void drawPath();
+	Dummy dummy(const Common::String &name);
+	bool findKate();
+	const TeMarker *findMarker(const Common::String &name);
+	const TeMarker *findMarkerByInt(const Common::String &name);
+	SoundStep findSoundStep(const Common::String &name);
+	void freeGeometry();
 	void freeSceneObjects();
-	void unloadSpriteLayouts();
-	void deleteAllCallback();
-
-	void setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2);
-	TeLight *shadowLight();
-
 	Common::Path getActZoneFileName() const;
 	Common::Path getBlockersFileName() const;
 	Common::Path getLightsFileName() const;
+	float getHeadHorizontalRotation(Character *cter, const TeVector3f32 &vec);
+	float getHeadVerticalRotation(Character *cter, const TeVector3f32 &vec);
+	Common::String imagePathMarker(const Common::String &name);
+	void initScroll();
+	bool isMarker(const Common::String &name);
+	bool isObjectBlocking(const Common::String &name);
+	bool isVisibleMarker(const Common::String &name);
+	TeVector2f32 layerSize();
+
+	virtual bool load(const Common::Path &path) override;
+	void loadBackground(const Common::Path &path);
+	bool loadBillboard(const Common::String &name);
+	void loadBlockers();
+	bool loadCharacter(const Common::String &name);
+	void loadInteractions(const Common::Path &path);
+	bool loadLights(const Common::Path &path);
+	void loadMarkers(const Common::Path &path);
+	bool loadObject(const Common::String &name);
+	void loadObjectMaterials(const Common::String &name);
+	void loadObjectMaterials(const Common::String &path, const Common::String &name);
+	bool loadPlayerCharacter(const Common::String &name);
+
+	void moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd);
+	int object(const Common::String &name);
+	Object3D *object3D(const Common::String &name);
+	void onMainWindowSizeChanged();
+	TeFreeMoveZone *pathZone(const Common::String &name);
+	TeVector3f32 positionMarker(const Common::String &name);
+	void removeBlockingObject(const Common::String &name);
+
+	void reset();
+	void setImagePathMarker(const Common::String &markerName, const Common::String &path);
+	void setPositionCharacter(const Common::String &charName, const Common::String &freeMoveZoneName, const TeVector3f32 &position);
+	void setPositionMarker(const Common::String &name, const TeVector3f32 &vec);
+	void setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2);
+	void setVisibleMarker(const Common::String &markerName, bool val);
+	TeLight *shadowLight();
+	bool showAllObjects(const Common::String &name);
+	void unloadAllObjects();
+	void unloadCharacter(const Common::String &name);
+	void unloadObject(const Common::String &name);
+	void unloadSpriteLayouts();
+	void update() override;
 
 	// Does nothing, but to keep calls from original..
 	static void updateScroll() {};
-
-	bool findKate();
+	static void updateViewport() {};
 
 	Character *_character;
 	Common::Array<Character *> _characters;
@@ -159,6 +186,8 @@ public:
 	CharactersShadow *_charactersShadow;
 	TeIntrusivePtr<TeBezierCurve> curve() { return _curve; }
 	void setCurve(TeIntrusivePtr<TeBezierCurve> &c) { c = _curve; }
+	Common::Array<TeIntrusivePtr<TeModel>> &zoneModels() { return _zoneModels; }
+	Common::Array<TeRectBlocker> &rectBlockers() { return _rectBlockers; }
 
 private:
 	TeColor _shadowColor;
@@ -166,11 +195,14 @@ private:
 	float _shadowNearPlane;
 	float _shadowFov;
 
+	float _waitTime;
+	TeTimer _waitTimeTimer;
+
 	Common::Array<TeBlocker> _blockers;
 	Common::Array<TeRectBlocker> _rectBlockers;
 	Common::Array<TeActZone> _actZones;
 	Common::Array<TeFreeMoveZone*> _freeMoveZones;
-	Common::Array<TeMarker *> _markers;
+	Common::Array<TeMarker> _markers;
 	Common::Array<AnchorZone *> _anchorZones;
 	Common::Array<AnimObject *> _animObjects;
 	Common::Array<Object3D *> _object3Ds;
@@ -184,6 +216,7 @@ private:
 	Common::Array<Object> _objects;
 	Common::Array<TeIntrusivePtr<TeBezierCurve>> _bezierCurves;
 	Common::Array<Dummy> _dummies;
+	Common::Array<TeIntrusivePtr<TeModel>> _zoneModels;
 
 	TeIntrusivePtr<TeModel> _playerCharacterModel;
 	TeIntrusivePtr<TeBezierCurve> _curve;
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 79fcacffb71..2ad6b17d88b 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -188,6 +188,8 @@ void Inventory::addObject(const Common::String &objId) {
 }
 
 bool Inventory::addObject(InventoryObject &obj) {
+	_invObjects.push_back(&obj);
+	obj.selectedSignal().add(this, &Inventory::onObjectSelected);
 	error("TODO: implement Inventory::addObject.");
 }
 
@@ -266,10 +268,18 @@ bool Inventory::onZoomed() {
 }
 
 void Inventory::pauseAnims() {
+	Game *game = g_engine->getGame();
+	if (game->scene()._character) {
+		
+	}
 	error("TODO: implement Inventory::pauseAnims");
 }
 
 void Inventory::unPauseAnims() {
+	Game *game = g_engine->getGame();
+	if (game->scene()._character) {
+		
+	}
 	error("TODO: implement Inventory::unPauseAnims");
 }
 
diff --git a/engines/tetraedge/game/inventory_object.cpp b/engines/tetraedge/game/inventory_object.cpp
index af214d5969c..f12e169b6fb 100644
--- a/engines/tetraedge/game/inventory_object.cpp
+++ b/engines/tetraedge/game/inventory_object.cpp
@@ -27,9 +27,25 @@ InventoryObject::InventoryObject() {
 }
 
 void InventoryObject::load(const Common::String &name) {
-	error("TODO: Implement InventoryObject::load");
+	setSizeType(RELATIVE_TO_PARENT);
+	setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
+	_gui.load("Inventory/InventoryObject.lua");
+	addChild(_gui.layoutChecked("object"));
+	setName(name);
+	_gui.spriteLayoutChecked("upLayout")->load(spritePath());
+	TeButtonLayout *btn = _gui.buttonLayoutChecked("object");
+	btn->onMouseClickValidated().add(this, &InventoryObject::onButtonDown);
+	// TODO: btn->setDoubleValidationProtectionEnabled(false)
+}
+
+Common::Path InventoryObject::spritePath() {
+	return Common::Path("Inventory/Objects").join(name()).append(".png");
+}
+
+bool InventoryObject::onButtonDown() {
+	_selectedSignal.call(*this);
+	return false;
 }
 
-// TODO: Add more functions here.
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/inventory_object.h b/engines/tetraedge/game/inventory_object.h
index 8a9ae452f7b..5464f813811 100644
--- a/engines/tetraedge/game/inventory_object.h
+++ b/engines/tetraedge/game/inventory_object.h
@@ -28,15 +28,20 @@
 
 namespace Tetraedge {
 
-class InventoryObject : TeLuaGUI {
+class InventoryObject : TeLayout {
 public:
 	InventoryObject();
 
 	void load(const Common::String &name);
+	Common::Path spritePath();
+	bool onButtonDown();
+	TeSignal1Param<InventoryObject&> &selectedSignal() { return _selectedSignal; };
+
 	const Common::String &name() const { return _name; }
 private:
 	Common::String _name;
-
+	TeLuaGUI _gui;
+	TeSignal1Param<InventoryObject&> _selectedSignal;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 462411909e5..88745e3c0e3 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -59,7 +59,7 @@ static void AddRandomSound(const Common::String &s1, const Common::String &s2, f
 static int tolua_ExportedFunctions_AddRandomSound00(lua_State *L) {
 	tolua_Error err;
 	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
-		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 1, &err)
 		&& tolua_isnoobj(L, 5, &err)) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		Common::String s2(tolua_tostring(L, 2, nullptr));
@@ -223,7 +223,7 @@ static int tolua_ExportedFunctions_SetVisibleMarker00(lua_State *L) {
 tolua_Error err;
 	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
 		Common::String s(tolua_tostring(L, 1, nullptr));
-		bool b = tolua_toboolean(L, 1, 0);
+		bool b = tolua_toboolean(L, 2, 0);
 		SetVisibleMarker(s, b);
 		return 0;
 	}
@@ -259,20 +259,380 @@ static int tolua_ExportedFunctions_SetVisibleCellphone00(lua_State *L) {
 	error("#ferror in function 'SetVisibleCellphone': %d %d %s", err.index, err.array, err.type);
 }
 
+static void RequestAutoSave() {
+	Game *game = g_engine->getGame();
+	game->setSaveRequested();
+}
+
+static int tolua_ExportedFunctions_RequestAutoSave00(lua_State *L) {
+	RequestAutoSave();
+	return 0;
+}
+
+static void HideObject(const Common::String &objName) {
+	warning("TODO: HideObject");
+}
+
+static int tolua_ExportedFunctions_HideObject00(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));
+		HideObject(s1);
+		return 0;
+	}
+	error("#ferror in function 'HideObject': %d %d %s", err.index, err.array, err.type);
+}
+
+static void ShowObject(const Common::String &objName) {
+	warning("TODO: ShowObject");
+}
+
+static int tolua_ExportedFunctions_ShowObject00(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));
+		ShowObject(s1);
+		return 0;
+	}
+	error("#ferror in function 'ShowObject': %d %d %s", err.index, err.array, err.type);
+}
+
+static void LoadObject(const Common::String &objName) {
+	Game *game = g_engine->getGame();
+	game->scene().loadObject(objName);
+}
+
+static int tolua_ExportedFunctions_LoadObject00(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));
+		LoadObject(s1);
+		return 0;
+	}
+	error("#ferror in function 'LoadObject': %d %d %s", err.index, err.array, err.type);
+}
+
+static void UnloadObject(const Common::String &objName) {
+	Game *game = g_engine->getGame();
+	game->scene().unloadObject(objName);
+}
+
+static int tolua_ExportedFunctions_UnloadObject00(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));
+		UnloadObject(s1);
+		return 0;
+	}
+	error("#ferror in function 'UnloadObject': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetCharacterRotation(const Common::String &charname, float f1, float f2, float f3) {
+	// TODO: check if this is good.
+	TeQuaternion quat = TeQuaternion::fromEuler(TeVector3f32(f1 * M_PI / 180.0, f2 * M_PI / 180.0, f3 * M_PI / 180.0));
+	Game *game = g_engine->getGame();
+	Character *c = game->scene().character(charname);
+	if (c) {
+		c->_model->setRotation(quat);
+	} else {
+		warning("[SetCharacterRotation] Character not found %s", charname.c_str());
+	}
+}
+
+static int tolua_ExportedFunctions_SetCharacterRotation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 1.0);
+		SetCharacterRotation(s1, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetCharacterRotation': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetCharacterPosition(const Common::String &charname, const Common::String &zonename, float f1, float f2, float f3) {
+	Game *game = g_engine->getGame();
+	game->scene().setPositionCharacter(charname, zonename, TeVector3f32(f1, f2, f3));
+}
+
+static int tolua_ExportedFunctions_SetCharacterPosition00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnumber(L, 5, 0, &err) && tolua_isnoobj(L, 6, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		float f1 = tolua_tonumber(L, 3, 0.0);
+		float f2 = tolua_tonumber(L, 4, 1.0);
+		float f3 = tolua_tonumber(L, 5, 0.0);
+		SetCharacterPosition(s1, s2, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetCharacterPosition': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetGroundObjectPosition(const Common::String &objname, float f1, float f2, float f3) {
+	warning("TODO: Implement SetGroundObjectPosition");
+}
+
+static int tolua_ExportedFunctions_SetGroundObjectPosition00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 1.0);
+		SetGroundObjectPosition(s1, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetGroundObjectPosition': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetGroundObjectRotation(const Common::String &objname, float f1, float f2, float f3) {
+	warning("TODO: Implement SetGroundObjectRotation");
+}
+
+static int tolua_ExportedFunctions_SetGroundObjectRotation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 1.0);
+		SetGroundObjectRotation(s1, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetGroundObjectRotation': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetBackground(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	if (!game->setBackground(name))
+		warning("[SetBackground] Background \"%s\" doesn't exist.", name.c_str());
+}
+
+static int tolua_ExportedFunctions_SetBackground00(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));
+		SetBackground(s1);
+		return 0;
+	}
+	error("#ferror in function 'SetBackground': %d %d %s", err.index, err.array, err.type);
+}
+
+static void PushTask(const Common::String &s1, const Common::String &s2) {
+	Game *game = g_engine->getGame();
+	game->objectif().pushObjectif(s1, s2);
+}
+
+static int tolua_ExportedFunctions_PushTask00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		PushTask(s1, s2);
+		return 0;
+	}
+	error("#ferror in function 'PushTask': %d %d %s", err.index, err.array, err.type);
+}
+
+static void EnableRectBlocker(uint offset, bool enabled) {
+	Game *game = g_engine->getGame();
+	if (game->scene().rectBlockers().size() < offset)
+		error("invalid rectblocker offset %d", offset);
+
+	game->scene().rectBlockers()[offset]._x = enabled;
+}
+
+static int tolua_ExportedFunctions_EnableRectBlocker00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isboolean(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		double d1 = tolua_tonumber(L, 1, 0.0f);
+		bool b1 = tolua_toboolean(L, 2, 0);
+		EnableRectBlocker((uint)d1, b1);
+		return 0;
+	}
+	error("#ferror in function 'EnableRectBlocker': %d %d %s", err.index, err.array, err.type);
+}
+
+static void AddAnchorZone(const Common::String &s1, const Common::String &s2, float f1) {
+	if (s1.empty())
+		return;
+
+	Game *game = g_engine->getGame();
+
+	if (s1.contains("Dummy")) {
+		game->scene().addAnchorZone(s1, s2, f1);
+	} else if (s1.contains("Int")) {
+		if (game->scene().hitObjectGui().loaded()) {
+			TeButtonLayout *layout = game->scene().hitObjectGui().buttonLayout(s2);
+			if (!layout) {
+				warning("[AddAnchorZone] Zone \"%s\" doesn't exist.", s2.c_str());
+			} else {
+				game->scene().addAnchorZone(s1, s2, f1);
+			}
+		}
+	}
+}
+
+static int tolua_ExportedFunctions_AddAnchorZone00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnumber(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		double d1 = tolua_tonumber(L, 3, 1.0);
+		AddAnchorZone(s1, s2, d1);
+		return 0;
+	}
+	error("#ferror in function 'AddAnchorZone': %d %d %s", err.index, err.array, err.type);
+}
+
+static void DisabledZone(const Common::String &s1, bool b) {
+	Game *game = g_engine->getGame();
+	if (!game->scene().markerGui().loaded())
+		return;
+
+	warning("TODO: Implement DisabledZone");
+}
+
+static int tolua_ExportedFunctions_DisabledZone00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 1, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s(tolua_tostring(L, 1, nullptr));
+		bool b = tolua_toboolean(L, 2, 1);
+		DisabledZone(s, b);
+		return 0;
+	}
+	error("#ferror in function 'DisabledZone': %d %d %s", err.index, err.array, err.type);
+}
+
+static void DisabledInt(const Common::String &name, bool b) {
+	Game *game = g_engine->getGame();
+	if (!game->scene().hitObjectGui().loaded())
+		return;
+
+	TeButtonLayout *layout = game->scene().hitObjectGui().buttonLayout(name);
+	if (layout) {
+		layout->setVisible(!b);
+	}
+}
+
+static int tolua_ExportedFunctions_DisabledInt00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 1, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s(tolua_tostring(L, 1, nullptr));
+		bool b = tolua_toboolean(L, 2, 1);
+		DisabledInt(s, b);
+		return 0;
+	}
+	error("#ferror in function 'DisabledInt': %d %d %s", err.index, err.array, err.type);
+}
+
+static void LockCursor(bool b) {
+	Application *app = g_engine->getApplication();
+	app->lockCursorFromAction(b);
+}
+
+static int tolua_ExportedFunctions_LockCursor00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isboolean(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		bool b = tolua_toboolean(L, 1, 0);
+		LockCursor(b);
+		return 0;
+	}
+	error("#ferror in function 'LockCursor': %d %d %s", err.index, err.array, err.type);
+}
+
+static void PlaySound(const Common::String &name, int i1, float f2) {
+	Game *game = g_engine->getGame();
+	game->playSound(name, i1, f2);
+}
+
+static int tolua_ExportedFunctions_PlaySound00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 1, &err) && tolua_isnumber(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		double d1 = tolua_tonumber(L, 2, -1.0);
+		double d2 = tolua_tonumber(L, 3, 1.0);
+		PlaySound(s1, d1, d2);
+		return 0;
+	}
+	error("#ferror in function 'PlaySound': %d %d %s", err.index, err.array, err.type);
+}
+
+static void StopSound(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	game->stopSound(name);
+}
+
+static int tolua_ExportedFunctions_StopSound00(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));
+		StopSound(s1);
+		return 0;
+	}
+	error("#ferror in function 'StopSound': %d %d %s", err.index, err.array, err.type);
+}
+
+static void PlayRandomSound(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	game->playRandomSound(name);
+}
+
+static int tolua_ExportedFunctions_PlayRandomSound00(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));
+		PlayRandomSound(s1);
+		return 0;
+	}
+	error("#ferror in function 'PlayRandomSound': %d %d %s", err.index, err.array, err.type);
+}
+
+static void PlayMusic(const Common::String &path) {
+	TeMusic &music = g_engine->getApplication()->music();
+	music.load(path);
+	music.play();
+	music.repeat(false);
+	music.volume(1.0);
+}
+
+static int tolua_ExportedFunctions_PlayMusic00(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));
+		PlayMusic(s1);
+		return 0;
+	}
+	error("#ferror in function 'PlayMusic': %d %d %s", err.index, err.array, err.type);
+}
+
 
 // ////////////////////////////////////////////////////////////////////////
 
+
+
 void LuaOpenBinds(lua_State *L) {
 	tolua_open(L);
 	tolua_module(L, 0, 0);
 	tolua_beginmodule(L, 0);
 	/*
 	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials00);
-	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);
+	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);*/
 	tolua_function(L, "HideObject", tolua_ExportedFunctions_HideObject00);
 	tolua_function(L, "ShowObject", tolua_ExportedFunctions_ShowObject00);
-	tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00);
-	tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);
+	/*tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00);*/
+	tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);/*
 	tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00);
 	tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00);*/
 	tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
@@ -282,33 +642,33 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "StartAnimation", tolua_ExportedFunctions_StartAnimation00);
 	tolua_function(L, "StartAnimationAndWaitForEnd",
 				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00);
-	tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00);
+	tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00); */
 	tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
-	tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);*/
+	/*tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);*/
 	tolua_function(L, "AddMarker", tolua_ExportedFunctions_AddMarker00);
 	tolua_function(L, "SetVisibleMarker", tolua_ExportedFunctions_SetVisibleMarker00);
 	tolua_function(L, "DeleteMarker", tolua_ExportedFunctions_DeleteMarker00);
 	tolua_function(L, "SetVisibleCellphone", tolua_ExportedFunctions_SetVisibleCellphone00);
-	/*tolua_function(L, "DisabledZone", tolua_ExportedFunctions_DisabledZone00);
+	tolua_function(L, "DisabledZone", tolua_ExportedFunctions_DisabledZone00);
 	tolua_function(L, "DisabledInt", tolua_ExportedFunctions_DisabledInt00);
 	tolua_function(L, "LockCursor", tolua_ExportedFunctions_LockCursor00);
-	tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00);
+	/*tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00);
 	tolua_function(L, "UnsetCondition", tolua_ExportedFunctions_UnsetCondition00);
 	tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00);
 	tolua_function(L, "LaunchDialog", tolua_ExportedFunctions_LaunchDialog00);
 	tolua_function(L, "LaunchDialogAndWaitForEnd", tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00);
 	tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
-	tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);
+	tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);*/
 	tolua_function(L, "PushTask", tolua_ExportedFunctions_PushTask00);
-	tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
+	/*tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
 	tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);
-	tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00);
+	tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00);*/
 	tolua_function(L, "PlaySound", tolua_ExportedFunctions_PlaySound00);
-	tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);
-	tolua_function(L, "StopSound", tolua_ExportedFunctions_StopSound00);*/
+	/*tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);*/
+	tolua_function(L, "StopSound", tolua_ExportedFunctions_StopSound00);
 	tolua_function(L, "AddRandomSound", tolua_ExportedFunctions_AddRandomSound00);
-	/*tolua_function(L, "PlayRandomSound", tolua_ExportedFunctions_PlayRandomSound00);
-	tolua_function(L, "PlayMusic", tolua_ExportedFunctions_PlayMusic00);*/
+	tolua_function(L, "PlayRandomSound", tolua_ExportedFunctions_PlayRandomSound00);
+	tolua_function(L, "PlayMusic", tolua_ExportedFunctions_PlayMusic00);
 	tolua_function(L, "SetSoundStep", tolua_ExportedFunctions_SetSoundStep00);
 	/*tolua_function(L, "Selected", tolua_ExportedFunctions_Selected00);
 	tolua_function(L, "TakeObject", tolua_ExportedFunctions_TakeObject00);
@@ -332,11 +692,11 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "MoveCharacterPlayerTo", tolua_ExportedFunctions_MoveCharacterPlayerTo00);
 	tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
 				 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00);
-	tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00);
+	tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00);*/
 	tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
-	tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);
+	/*tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);*/
 	tolua_function(L, "SetCharacterRotation", tolua_ExportedFunctions_SetCharacterRotation00);
-	tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
+	/*tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
 	tolua_function(L, "SetCharacterAnimation", tolua_ExportedFunctions_SetCharacterAnimation00);
 	tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
 				 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);
@@ -362,12 +722,12 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetObjectRotation", tolua_ExportedFunctions_SetObjectRotation00);
 	tolua_function(L, "SetObjectTranslation", tolua_ExportedFunctions_SetObjectTranslation00);
 	tolua_function(L, "SetObjectScale", tolua_ExportedFunctions_SetObjectScale00);
-	tolua_function(L, "SetObjectFrames", tolua_ExportedFunctions_SetObjectFrames00);
+	tolua_function(L, "SetObjectFrames", tolua_ExportedFunctions_SetObjectFrames00);*/
 	tolua_function(L, "LoadObject", tolua_ExportedFunctions_LoadObject00);
 	tolua_function(L, "UnloadObject", tolua_ExportedFunctions_UnloadObject00);
 	tolua_function(L, "SetGroundObjectPosition", tolua_ExportedFunctions_SetGroundObjectPosition00);
 	tolua_function(L, "SetGroundObjectRotation", tolua_ExportedFunctions_SetGroundObjectRotation00);
-	tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00);
+	/*tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00);
 	tolua_function(L, "RotateGroundObject", tolua_ExportedFunctions_RotateGroundObject00);
 	tolua_function(L, "SetLightPlayerCharacter", tolua_ExportedFunctions_SetLightPlayerCharacter00);
 	tolua_function(L, "SetLightPos", tolua_ExportedFunctions_SetLightPos00);
@@ -394,10 +754,10 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00);
 	tolua_function(L, "TestFileFlagSystemFlag", tolua_ExportedFunctions_TestFileFlagSystemFlag00);
 	tolua_function(L, "PrintDebugMessage", tolua_ExportedFunctions_PrintDebugMessage00);
-	tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);
+	tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);*/
 	tolua_function(L, "EnableRectBlocker", tolua_ExportedFunctions_EnableRectBlocker00);
-	tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);
-	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
+	/*tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);*/
+	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);/*
 	tolua_function(L, "ActivateAnchorZone", tolua_ExportedFunctions_ActivateAnchorZone00);
 	tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00);
 	tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
index e2adbfb54f8..7c56dccb83d 100644
--- a/engines/tetraedge/game/main_menu.cpp
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -123,6 +123,10 @@ void MainMenu::enter() {
 		const Common::String versionSectionStr("<section style=\"left\" /><color r=\"255\" g=\"255\" b=\"255\"/><font file=\"Common/Fonts/arial.ttf\" size=\"12\" />");
 		versionNum->setText(versionSectionStr + app->getVersionString());
 	}
+
+	if (ConfMan.get("skip_mainmenu") == "true") {
+		onNewGameConfirmed();
+	}
 }
 
 void MainMenu::leave() {
@@ -131,10 +135,9 @@ void MainMenu::leave() {
 
 	Application	*app = g_engine->getApplication();
 	app->captureFade();
-	warning("TODO: MainMenu::leave Stop some game sounds here.");
-	//Game *game = g_engine->getGame();
-	//game->stopSound("sounds/Ambiances/b_automatebike.ogg");
-	//game->stopSound("sounds/Ambiances/b_engrenagebg.ogg");
+	Game *game = g_engine->getGame();
+	game->stopSound("sounds/Ambiances/b_automatebike.ogg");
+	game->stopSound("sounds/Ambiances/b_engrenagebg.ogg");
 	TeLuaGUI::unload();
 	app->fade();
 	_entered= false;
@@ -164,11 +167,11 @@ bool MainMenu::onBFGRateItButtonValidated() {
 }
 
 bool MainMenu::onBFGRateItQuitButtonValidated() {
-	error("TODO: Implement MainMenu function");
+	error("TODO: Implement MainMenu::onBFGRateItQuitButtonValidated");
 }
 
 bool MainMenu::onBFGUnlockGameButtonValidated() {
-	error("TODO: Implement MainMenu function");
+	error("TODO: Implement MainMenu::onBFGUnlockGameButtonValidated");
 }
 
 void MainMenu::tryDisableButton(const Common::String &btnName) {
@@ -215,11 +218,11 @@ bool MainMenu::onDisabledTuto() {
 }
 
 bool MainMenu::onEnterGameRotateAnimFinished() {
-	error("TODO: Implement MainMenu function");
+	error("TODO: Implement MainMenu::onEnterGameRotateAnimFinished");
 }
 
 bool MainMenu::onGalleryButtonValidated() {
-	error("TODO: Implement MainMenu function");
+	error("TODO: Implement MainMenu::onGalleryButtonValidated");
 }
 
 bool MainMenu::onHowToButtonValidated() {
@@ -263,8 +266,8 @@ bool MainMenu::onQuit() {
 }
 
 bool MainMenu::onQuitButtonValidated() {
-	//Confirm::enter("menus/confirm/confirmQuit.lua", "");
-	error("TODO: Implement MainMenu::onQuitButtonValidated");
+	_quitConfirm.enter("menus/confirm/confirmQuit.lua", "");
+	return false;
 }
 
 bool MainMenu::onUnlockGameButtonValidated() {
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index eb95daa4d34..cfd6377ce96 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -31,6 +31,23 @@ namespace Tetraedge {
 Object3D::Object3D() {
 }
 
+bool Object3D::loadModel(const Common::String &name) {
+	_modelPtr = new TeModel();
+	Common::HashMap<Common::String, ObjectSettings>::iterator settings = _objectSettings->find(name);
+	if (settings != _objectSettings->end()) {
+		_modelFileName = settings->_value._modelFileName;
+		_defaultScale = settings->_value._defaultScale;
+		_modelPtr->_texturePath = Common::Path("objects/Textures");
+		bool loaded = _modelPtr->load(Common::Path("objects").join(_modelFileName));
+		if (loaded) {
+			_modelPtr->setName(name);
+			_modelPtr->setScale(_defaultScale);
+			return true;
+		}
+	}
+	return false;
+}
+
 /*static*/ bool Object3D::loadSettings(const Common::String &path) {
 	ObjectSettingsXmlParser parser;
 	parser.setAllowText();
diff --git a/engines/tetraedge/game/object3d.h b/engines/tetraedge/game/object3d.h
index ae248fd581a..d1d321bf3cb 100644
--- a/engines/tetraedge/game/object3d.h
+++ b/engines/tetraedge/game/object3d.h
@@ -47,6 +47,8 @@ public:
 
 	static bool loadSettings(const Common::String &path);
 
+	TeIntrusivePtr<TeModel> model() { return _modelPtr; }
+
 private:
 	static Common::HashMap<Common::String, ObjectSettings> *_objectSettings;
 
diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
index 44c99aba5dc..96a2731f962 100644
--- a/engines/tetraedge/game/objectif.cpp
+++ b/engines/tetraedge/game/objectif.cpp
@@ -108,6 +108,19 @@ bool Objectif::onHelpButtonValidated() {
 	return false;
 }
 
+void Objectif::pushObjectif(Common::String const &head, Common::String const &sub) {
+	for (const Task &t : _tasks) {
+		if (t._headTask == head && t._subTask == sub)
+			return;
+	}
+
+	_layoutsDirty = true;
+	_tasks.resize(_tasks.size() + 1);
+	_tasks.back()._headTask = head;
+	_tasks.back()._subTask = sub;
+	_tasks.back()._taskFlag = true;
+}
+
 void Objectif::reattachLayout(TeLayout *layout) {
 	TeButtonLayout *btn;
 
diff --git a/engines/tetraedge/game/objectif.h b/engines/tetraedge/game/objectif.h
index 9e71169ef02..44ae6050f46 100644
--- a/engines/tetraedge/game/objectif.h
+++ b/engines/tetraedge/game/objectif.h
@@ -45,6 +45,7 @@ public:
 	void leave();
 	void load();
 	bool onHelpButtonValidated();
+	void pushObjectif(Common::String const &head, Common::String const &sub);
 	void reattachLayout(TeLayout *layout);
 	void removeChildren();
 	// void save()
diff --git a/engines/tetraedge/game/owner_error_menu.cpp b/engines/tetraedge/game/owner_error_menu.cpp
index 52aef50351d..0519ab69d57 100644
--- a/engines/tetraedge/game/owner_error_menu.cpp
+++ b/engines/tetraedge/game/owner_error_menu.cpp
@@ -24,6 +24,9 @@
 #include "tetraedge/game/application.h"
 #include "tetraedge/game/owner_error_menu.h"
 
+#include "tetraedge/te/te_layout.h"
+#include "tetraedge/te/te_text_layout.h"
+
 namespace Tetraedge {
 
 OwnerErrorMenu::OwnerErrorMenu() : _entered(false) {
@@ -32,13 +35,13 @@ OwnerErrorMenu::OwnerErrorMenu() : _entered(false) {
 void OwnerErrorMenu::enter() {
 	_entered = true;
 	static const Common::Path luaPath("menus/ownerError/ownerError.lua");
-	load(luaPath.toString());
-	error("TODO: Finish implementation of OwnerErrorMenu::enter");
-	/*
+	load(luaPath);
 	Application *app = g_engine->getApplication();
-	TeLayout *menuLayout = TeLuaGUI::layout("menu");
-	 ...
-	 */
+	TeLayout *menuLayout = layoutChecked("menu");
+	app->_frontLayout.addChild(menuLayout);
+	TeTextLayout *txt = dynamic_cast<TeTextLayout*>(layoutChecked("ownerMenuText"));
+	const Common::String *locname = app->_loc.value(txt->name());
+	txt->setText(value("textAttributs").toString() + (locname ? *locname : txt->name()));
 }
 
 void OwnerErrorMenu::leave() {
diff --git a/engines/tetraedge/game/splash_screens.cpp b/engines/tetraedge/game/splash_screens.cpp
index a14519a9b96..adbd33f04d4 100644
--- a/engines/tetraedge/game/splash_screens.cpp
+++ b/engines/tetraedge/game/splash_screens.cpp
@@ -57,7 +57,7 @@ bool SplashScreens::onAlarm() {
 	const Common::String scriptName = Common::String::format("menus/splashes/splash%d.lua", _splashNo);
 	_splashNo++;
 
-	if (ConfMan.get("skipsplash") == "true") {
+	if (ConfMan.get("skip_splash") == "true") {
 		onQuitSplash();
 		return true;
 	}
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index c276c075ceb..bdb10afc5c1 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -60,6 +60,7 @@ MODULE_OBJS := \
 	te/te_i_layout.o \
 	te/te_i_loc.o \
 	te/te_image.o \
+	te/te_images_sequence.o \
 	te/te_input_mgr.o \
 	te/te_interpolation.o \
 	te/te_jpeg.o \
diff --git a/engines/tetraedge/te/te_camera.h b/engines/tetraedge/te/te_camera.h
index f6344d53a24..38dc8adfe29 100644
--- a/engines/tetraedge/te/te_camera.h
+++ b/engines/tetraedge/te/te_camera.h
@@ -43,7 +43,7 @@ public:
 	void buildPerspectiveMatrix();
 	void buildPerspectiveMatrix2();
 	void buildPerspectiveMatrix3();
-	void draw();
+	void draw() override;
 
 	void getRay(const TeVector2s32 &param_1, TeVector3f32 &out1, TeVector3f32 &out2);
 
@@ -67,6 +67,8 @@ public:
 	void viewport(int x, int y, uint width, uint height);
 	TeVector2f32 viewportSize() const { return TeVector2f32(_viewportW, _viewportH); }
 
+	TeSignal0Param &onViewportChangedSignal() { return _onViewportChangedSignal; }
+
 	int _projectionMatrixType;
 	float _orthNearVal;
 	float _orthFarVal;
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index 7ed6efa4b09..0b2ed30ef1b 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -20,11 +20,14 @@
  */
 
 #include "common/file.h"
+#include "common/fs.h"
 #include "common/debug.h"
+#include "common/config-manager.h"
 
 #include "tetraedge/te/te_core.h"
 
 #include "tetraedge/te/te_png.h"
+#include "tetraedge/te/te_images_sequence.h"
 #include "tetraedge/te/te_jpeg.h"
 #include "tetraedge/te/te_theora.h"
 #include "tetraedge/te/te_tga.h"
@@ -66,6 +69,8 @@ TeICodec *TeCore::createVideoCodec(const Common::Path &path) {
 		return new TeTheora();
 	} else if (TeTga::matchExtension(extn)) {
 		return new TeTga();
+	} else if (TeImagesSequence::matchExtension(extn)) {
+		return new TeImagesSequence();
 	}
 	error("TTeCore::createVideoCodec: Unrecognised format %s", path.toString().c_str());
 }
@@ -111,6 +116,12 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 	if (Common::File::exists(path))
 		return path;
 
+	const Common::String gamePath = ConfMan.get("path");
+	const Common::Path resPath = Common::Path(gamePath).join("Resources");
+	const Common::Path absolutePath = resPath.join(path);
+	if (Common::FSNode(absolutePath).isDirectory())
+		return absolutePath;
+
 	const Common::Path fname = path.getLastComponent();
 	const Common::Path dir = path.getParent();
 
@@ -140,7 +151,7 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 				testPath.joinInPlace(langs[langtype]);
 			}
 			testPath.joinInPlace(fname);
-			if (Common::File::exists(testPath)) {
+			if (Common::File::exists(testPath) || Common::FSNode(path).exists()) {
 				return testPath;
 			}
 		}
diff --git a/engines/tetraedge/te/te_frame_anim.cpp b/engines/tetraedge/te/te_frame_anim.cpp
index b0572730753..678d21c5508 100644
--- a/engines/tetraedge/te/te_frame_anim.cpp
+++ b/engines/tetraedge/te/te_frame_anim.cpp
@@ -30,8 +30,8 @@ _numFramesToShow(-1), _startTime(0), _endTime(FLT_MAX), _loopCount(0), _lastFram
 }
 
 void TeFrameAnim::update(double amount) {
-	int minFrame = MIN((long)_minFrame, _nbFrames);
-	int maxFrame = MIN((long)minFrame + _numFramesToShow, _nbFrames);
+	int minFrame = MIN(_minFrame, _nbFrames);
+	int maxFrame = MIN(_minFrame + _numFramesToShow, _nbFrames);
 	double frameNo = _frameRate * amount / 1000.0;
 
 	int loopsDone;
diff --git a/engines/tetraedge/te/te_frame_anim.h b/engines/tetraedge/te/te_frame_anim.h
index c2f241c81a6..c3b1c4869e9 100644
--- a/engines/tetraedge/te/te_frame_anim.h
+++ b/engines/tetraedge/te/te_frame_anim.h
@@ -35,7 +35,7 @@ public:
 
 	TeSignal0Param &frameChangedSignal() { return _frameChangedSignal; };
 
-	long _nbFrames;
+	int _nbFrames;
 	float _frameRate;
 	int _loopCount;
 	bool _reversed;
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 89999b5717e..0713b9c4341 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -27,6 +27,9 @@
 
 namespace Tetraedge {
 
+/*static*/
+//TeIntrusivePtr<TeCamera> TeFreeMoveZone::_globalCamera;
+
 class TeFreeMoveZoneGraph : micropather::Graph {
 	friend class TeFreeMoveZone;
 	TeVector2s32 _size;
@@ -57,7 +60,9 @@ _transformedVerticiesDirty(true), _bordersDirty(true), _pickMeshDirty(true), _pr
 }
 
 TeFreeMoveZone::~TeFreeMoveZone() {
-	// TODO: remove signal.
+	if (_camera) {
+		_camera->onViewportChangedSignal().remove(this, &TeFreeMoveZone::onViewportChanged);
+	}
 	delete _micropather;
 }
 
@@ -77,8 +82,10 @@ void TeFreeMoveZone::clear() {
 	setNbTriangles(0);
 	_pickMeshDirty = true;
 	_projectedPointsDirty = true;
-	// TODO: Clear 3 other point vectors here.
-	// TODO: _gridDirty = true;
+	_vectorArray.clear();
+	_uintArray2.clear();
+	// TODO: Some other point vector here.
+	_gridDirty = true;
 	_graph->_flags.clear();
 	_graph->_size = TeVector2s32(0, 0);
 	_micropather->Reset();
@@ -91,7 +98,9 @@ Common::Array<TeVector3f32> TeFreeMoveZone::collisions(const TeVector3f32 &v1, c
 }
 
 TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool f) {
-	error("TODO: Implement TeFreeMoveZone::correctCharacterPosition");
+
+	warning("TODO: Implement TeFreeMoveZone::correctCharacterPosition");
+	return pos;
 }
 
 TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4, float param_5, bool findMeshFlag) {
@@ -185,8 +194,15 @@ void TeFreeMoveZone::setBordersDistance(float dist) {
 	_graph->_bordersDistance = dist;
 }
 
-void TeFreeMoveZone::setCamera(TeIntrusivePtr<TeCamera> &cam, bool recalcProjPoints) {
-	error("TODO: Implement TeFreeMoveZone::setCamera");
+void TeFreeMoveZone::setCamera(TeIntrusivePtr<TeCamera> &cam, bool noRecalcProjPoints) {
+	if (_camera) {
+		_camera->onViewportChangedSignal().remove(this, &TeFreeMoveZone::onViewportChanged);
+	}
+	//_globalCamera = camera;  // Seems like this is never used?
+	_camera = cam;
+	cam->onViewportChangedSignal().add(this, &TeFreeMoveZone::onViewportChanged);
+	if (!noRecalcProjPoints)
+		_projectedPointsDirty = true;
 }
 
 void TeFreeMoveZone::setNbTriangles(unsigned long len) {
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index 13f0b9856ee..aa4cbd9809a 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -93,7 +93,7 @@ public:
 	TeVector3f32 projectOnAStarGrid(const TeVector3f32 &pt);
 	Common::Array<TeVector3f32> &removeInsignificantPoints(const Common::Array<TeVector3f32> &points);
 	void setBordersDistance(float dist);
-	void setCamera(TeIntrusivePtr<TeCamera> &cam, bool recalcProjPoints);
+	void setCamera(TeIntrusivePtr<TeCamera> &cam, bool noRecalcProjPoints);
 	void setNbTriangles(unsigned long len);
 	void setPathFindingOccluder(const TeOBP &occluder);
 	void setVertex(unsigned long offset, const TeVector3f32 &vertex);
@@ -129,6 +129,8 @@ private:
 	float _someGridFloat;
 
 	TeOBP _obp;
+	TeIntrusivePtr<TeCamera> _camera;
+	//static TeIntrusivePtr<TeCamera> _globalCamera;
 
 	bool _gridDirty;
 	TeFreeMoveZoneGraph *_graph;
diff --git a/engines/tetraedge/te/te_i_codec.h b/engines/tetraedge/te/te_i_codec.h
index ce3b8d26b07..f58bc0c15ff 100644
--- a/engines/tetraedge/te/te_i_codec.h
+++ b/engines/tetraedge/te/te_i_codec.h
@@ -37,7 +37,6 @@ public:
 
 	virtual ~TeICodec() {};
 	virtual bool load(const Common::Path &path) = 0;
-	virtual bool load(Common::SeekableReadStream &stream) = 0;
 	virtual uint width() = 0;
 	virtual uint height() = 0;
 	virtual int nbFrames() = 0;
diff --git a/engines/tetraedge/te/te_images_sequence.cpp b/engines/tetraedge/te/te_images_sequence.cpp
new file mode 100644
index 00000000000..0af65ad6777
--- /dev/null
+++ b/engines/tetraedge/te/te_images_sequence.cpp
@@ -0,0 +1,144 @@
+/* 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 "common/file.h"
+#include "image/png.h"
+#include "graphics/surface.h"
+
+#include "tetraedge/te/te_images_sequence.h"
+
+namespace Tetraedge {
+
+TeImagesSequence::TeImagesSequence() : _width(0), _height(0), _curFrame(0) {
+}
+
+TeImagesSequence::~TeImagesSequence() {
+}
+
+/*static*/
+bool TeImagesSequence::matchExtension(const Common::String &extn) {
+	return extn == "anim";
+}
+
+static bool compareNodes(const Common::FSNode &left, const Common::FSNode &right) {
+	return left.getPath() < right.getPath();
+}
+
+bool TeImagesSequence::load(const Common::Path &path) {
+	Common::FSNode directory(path);
+	if (!directory.isDirectory()) {
+		warning("TeImagesSequence::load:: not a directory %s", path.toString().c_str());
+		return false;
+	}
+
+	Common::FSList children;
+	if (!directory.getChildren(children, Common::FSNode::kListFilesOnly) || children.empty()) {
+		warning("TeImagesSequence::load:: couldn't get children of %s", path.toString().c_str());
+		return false;
+	}
+
+	Common::sort(children.begin(), children.end(), compareNodes);
+
+	SearchMan.addDirectory(path.toString(), directory);
+
+	for (Common::FSNode &child : children) {
+		Common::String filePathStr = child.getPath();
+
+		if (filePathStr.size() <= 10 || filePathStr.substr(filePathStr.size() - 7) != "fps.png")
+			continue;
+
+		Common::Path filePath(filePathStr);
+		Common::String fname = filePath.getLastComponent().toString();
+		Common::String fstart = fname.substr(0, fname.size() - 7);
+		unsigned int frameno = 0;
+		unsigned int fps = 0;
+		if (sscanf(fstart.c_str(), "%d-%d", &frameno, &fps) != 2) {
+			warning("TeImagesSequence::load can't match %s", fname.c_str());
+			continue;
+		}
+
+		Common::File file;
+		if (!file.open(filePath)) {
+			warning("TeImagesSequence::load can't open %s", filePath.toString().c_str());
+			continue;
+		}
+
+		// At first only do the deep check for the first file to get dimensions.
+		if (_width) {
+			_files.push_back(filePath);
+			continue;
+		}
+
+		Image::PNGDecoder png;
+		if (!png.loadStream(file)) {
+			warning("Image sequence failed to load png %s", filePath.toString().c_str());
+			return false;
+		}
+
+		const Graphics::Surface *surf = png.getSurface();
+		assert(surf);
+		_width = surf->w;
+		_height = surf->h;
+		_frameRate = fps;
+	}
+
+	return true;
+}
+
+
+bool TeImagesSequence::update(unsigned long i, TeImage &imgout) {
+	_curFrame = i;
+
+	if (i >= _files.size())
+		return false;
+
+	Common::File file;
+	if (file.open(_files[_curFrame]))
+		error("Open %s failed.. it was ok before?", _files[_curFrame].toString().c_str());
+
+	Image::PNGDecoder png;
+	if (png.loadStream(file)) {
+		warning("Image sequence failed to load png %s", _files[_curFrame].toString().c_str());
+		return false;
+	}
+
+	const Graphics::Surface *surf = png.getSurface();
+	assert(surf);
+
+	imgout.setAccessName(_files[_curFrame]);
+
+	if (imgout.w == surf->w && imgout.h == surf->h && imgout.format == surf->format) {
+		imgout.copyFrom(*surf);
+		return true;
+	}
+
+	error("TODO: Implement TeImagesSequence::update for different sizes");
+}
+
+bool TeImagesSequence::isAtEnd() {
+	return _curFrame >= _files.size();
+}
+
+TeImage::Format TeImagesSequence::imageFormat() {
+	return TeImage::RGBA8;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_images_sequence.h b/engines/tetraedge/te/te_images_sequence.h
new file mode 100644
index 00000000000..6559d8905cd
--- /dev/null
+++ b/engines/tetraedge/te/te_images_sequence.h
@@ -0,0 +1,71 @@
+/* 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_TE_TE_IMAGES_SEQUENCE_H
+#define TETRAEDGE_TE_TE_IMAGES_SEQUENCE_H
+
+#include "common/str.h"
+#include "tetraedge/te/te_i_codec.h"
+
+namespace Graphics {
+struct Surface;
+};
+
+namespace Tetraedge {
+
+class TeImagesSequence : public TeICodec {
+public:
+	TeImagesSequence();
+	virtual ~TeImagesSequence();
+
+	virtual bool load(const Common::Path &path) override;
+	virtual uint width() override { return _width; }
+	virtual uint height() override { return _height; }
+	virtual int nbFrames() override { return _files.size(); }
+	virtual void setLeftBorderSize(uint val) override { }
+	virtual uint leftBorderSize() override { return 0; }
+	virtual void setRightBorderSize(uint val) override  { }
+	virtual uint rightBorderSize() override { return 0; }
+	virtual void setBottomBorderSize(uint val) override  { }
+	virtual uint bottomBorderSize() override { return 0; }
+	virtual void setTopBorderSize(uint val) override  { }
+	virtual uint topBorderSize() override { return 0; }
+	virtual TeImage::Format imageFormat() override;
+	virtual float frameRate() override { return _frameRate; }
+	virtual bool update(unsigned long i, TeImage &imgout) override;
+	virtual bool isAtEnd() override;
+	virtual void setColorKeyActivated(bool val) override { }
+	virtual void setColorKey(const TeColor &col) override { }
+	virtual void setColorKeyTolerence(float val) override { }
+
+	static bool matchExtension(const Common::String &extn);
+
+private:
+	float _frameRate;
+	unsigned int _width;
+	unsigned int _height;
+	Common::Array<Common::Path> _files;
+	unsigned int _curFrame;
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_TE_TE_IMAGES_SEQUENCE_H
diff --git a/engines/tetraedge/te/te_lua_script.cpp b/engines/tetraedge/te/te_lua_script.cpp
index 4191bb3acad..58422d24195 100644
--- a/engines/tetraedge/te/te_lua_script.cpp
+++ b/engines/tetraedge/te/te_lua_script.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/debug.h"
 #include "common/textconsole.h"
 #include "common/file.h"
 #include "tetraedge/te/te_lua_script.h"
@@ -36,6 +37,7 @@ void TeLuaScript::attachToContext(TeLuaContext *context) {
 
 void TeLuaScript::execute() {
 	if (_luaContext) {
+		debug("TeLuaScript::execute %s", _scriptPath.toString().c_str());
 		lua_State *state = _luaContext->luaState();
 		if (state) {
 			TeLuaThread *thread = TeLuaThread::create(_luaContext);
@@ -48,6 +50,7 @@ void TeLuaScript::execute() {
 
 void TeLuaScript::execute(const Common::String &fname) {
 	if (_luaContext) {
+		debug("TeLuaScript::execute %s %s", _scriptPath.toString().c_str(), fname.c_str());
 		TeLuaThread *thread = TeLuaThread::create(_luaContext);
 		thread->execute(fname);
 		thread->release();
@@ -56,6 +59,7 @@ void TeLuaScript::execute(const Common::String &fname) {
 
 void TeLuaScript::execute(const Common::String &fname, const TeVariant &p1) {
 	if (_luaContext) {
+		debug("TeLuaScript::execute %s %s(%s)", _scriptPath.toString().c_str(), fname.c_str(), p1.toString().c_str());
 		TeLuaThread *thread = TeLuaThread::create(_luaContext);
 		thread->execute(fname, p1);
 		thread->release();
diff --git a/engines/tetraedge/te/te_lua_thread.cpp b/engines/tetraedge/te/te_lua_thread.cpp
index baa7bbadb36..a21f3ffda7d 100644
--- a/engines/tetraedge/te/te_lua_thread.cpp
+++ b/engines/tetraedge/te/te_lua_thread.cpp
@@ -125,7 +125,7 @@ void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1, cons
 	} else {
 		if (!fname.contains("Update"))
 			warning("[TeLuaThread::Execute3] La fonction : \"%s\" n'existe pas.", fname.c_str());
-		lua_settop(_luaThread, -2);
+		lua_settop(_luaThread, -4);
 	}
 }
 
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index bdf7075fda5..2c18fd9f4f8 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -72,7 +72,7 @@ void TeMesh::draw() {
 	if (_matrixForced)
 		renderer->multiplyMatrix(_forceMatrix);
 	else
-		renderer->multiplyMatrix(transformationMatrix());
+		renderer->multiplyMatrix(worldTransformationMatrix());
 
 	Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
 	Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
@@ -138,7 +138,8 @@ void TeMesh::draw() {
 	} else {
 		int totalfacecount = 0;
 		for (unsigned int i = 0; i < _materials.size(); i++) {
-			if (_faceCounts[i]) {
+			if (!_faceCounts[i])
+				continue;
 			if (!hasAlpha(i) || renderer->shadowMode() == TeRenderer::ShadowMode1 || !_shouldDraw) {
 				_materials[i].apply();
 				glDrawElements(_glMeshMode, _faceCounts[i] * 3, GL_UNSIGNED_SHORT, _indexes.data() + totalfacecount * 3);
@@ -146,7 +147,6 @@ void TeMesh::draw() {
 				renderer->disableTexture();
 			}
 			totalfacecount += _faceCounts[i];
-		  }
 		}
 	}
 
@@ -164,27 +164,11 @@ void TeMesh::draw() {
 		TeLight::disableAll();
 		glBegin(GL_LINES);
 		renderer->setCurrentColor(TeColor(255, 255, 255, 255));
-		if (!_verticies.empty()) {
-			error("TODO: Implement wire drawing here in TeMesh::draw..");
-			/*
-			offset1 = 1;
-			offset2 = 2;
-			do {
-			  i = (ulong)offset2;
-			  uVar5 = (ulong)(offset2 - 1);
-			  totalfacecount = (ulong)(offset2 - 2);
-			  glVertex3f
-						((&verticiesbuf->f1)[totalfacecount],(&verticiesbuf->f1)[uVar5],
-						 (&verticiesbuf->f1)[i]);
-			  glVertex3f
-						((&verticiesbuf->f1)[totalfacecount] + (&normalsbuf->f1)[totalfacecount],
-						 (&verticiesbuf->f1)[uVar5] + (&normalsbuf->f1)[uVar5],
-						 (&verticiesbuf->f1)[i] + (&normalsbuf->f1)[i]);
-			  offset2 = offset2 + 3;
-			  i = (ulong)offset1;
-			  offset1 = offset1 + 1;
-			} while (i < (this->verticiesArray).len);
-			 */
+		for (unsigned int i = 0; i < verticies.size(); i++) {
+			glVertex3f(verticies[i].x(), verticies[i].y(), verticies[i].z());
+			glVertex3f(verticies[i].x() + normals[i].x(),
+					verticies[i].y() + normals[i].y(),
+					verticies[i].z() + normals[i].z());
 		}
 		glEnd();
 	}
@@ -328,14 +312,17 @@ TeVector3f32 TeMesh::vertex(uint idx) const {
 		return _verticies[idx];
 }
 
-void TeMesh::attachMaterial(uint idx, const TeMaterial &material) {
-	TeMaterial &mat = _materials[idx];
-	mat._texture = material._texture;
-	mat._enableLights = material._enableLights;
-	mat._enableSomethingDefault0 = material._enableSomethingDefault0;
-	mat._emissionColor = material._emissionColor;
-	mat._diffuseColor = material._diffuseColor;
-	mat._mode = material._mode;
+void TeMesh::attachMaterial(uint idx, const TeMaterial &src) {
+	TeMaterial &dest = _materials[idx];
+	dest._texture = src._texture;
+	dest._enableLights = src._enableLights;
+	dest._enableSomethingDefault0 = src._enableSomethingDefault0;
+	dest._emissionColor = src._emissionColor;
+	dest._shininess = src._shininess;
+	dest._diffuseColor = src._diffuseColor;
+	dest._specularColor = src._specularColor;
+	dest._mode = src._mode;
+	dest._ambientColor = src._ambientColor;
 }
 
 void TeMesh::facesPerMaterial(uint idx, unsigned short value) {
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 5b22b028a4d..c5a500332e5 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -104,6 +104,16 @@ void TeModel::draw() {
 		renderer->sendModelMatrix(transform);
 		renderer->pushMatrix();
 		renderer->multiplyMatrix(transform);
+		if (name() == "Kate") {
+			debug("Draw model %p (%s, %d meshes)", this, name().empty() ? "no name" : name().c_str(), _meshes.size());
+			debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
+			debug("   position   %s", position().dump().c_str());
+			debug("   worldPos   %s", worldPosition().dump().c_str());
+			debug("   scale      %s", scale().dump().c_str());
+			debug("   worldScale %s", worldScale().dump().c_str());
+			debug("   rotation   %s", rotation().dump().c_str());
+			debug("   worldRot   %s", worldRotation().dump().c_str());
+		}
 		for (TeMesh &mesh : _meshes) {
 			// TODO: Set some flag in the mesh here to this->field_0x158??
 			mesh.draw();
@@ -118,7 +128,7 @@ void TeModel::forceMatrix(const TeMatrix4x4 &matrix) {
 	_forcedMatrix = matrix;
 }
 
-TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned long num) {
+TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num) {
 	if (anim) {
 		int bone = anim->findBone(_bones[num]._name);
 		if (bone != -1)
@@ -154,8 +164,30 @@ void TeModel::update() {
 			matricies[i] = matricies[b._x] * matrix;
 		}
 	}
-	if (_bones.size()) {
-		//warning("TODO: Finish TeModel::update. (disasm 190 ~ 697)");
+	for (unsigned int b = 0; b < _bones.size(); b++) {
+		TeTRS startTRS = getBone(_modelAnim, b);
+		for (unsigned int i = 0; i < _boneBlenders.size(); i++) {
+			BonesBlender *blender = _boneBlenders[i];
+			float complete = MIN((blender->_timer.getTimeFromStart() / 1000000.0f) / blender->_seconds, 1.0f);
+			TeTRS trs;
+			TeTRS endTRS = getBone(blender->_anim, b);
+			if (complete == 1.0f) {
+				delete blender;
+				_boneBlenders.remove_at(i);
+				trs = endTRS;
+				i--;
+			} else {
+				trs = startTRS.lerp(endTRS, complete);
+			}
+			TeMatrix4x4 matrix;
+			if (!_matrixForced) {
+				matrix = TeMatrix4x4::fromTRS(trs);
+			} else {
+				matrix = _forcedMatrix;
+				_matrixForced = false;
+			}
+		}
+		//warning("TODO: Finish TeModel::update. (disasm 295 ~ 693)");
 	}
 	for (TeMesh &mesh : _meshes) {
 		if (!_modelVertexAnim) {
@@ -211,8 +243,14 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 		error("[TeModel::load] Unsupported version %d", version);
 	}
 
-	_meshes.resize(stream.readUint32LE());
-	_weightElements.resize(stream.readUint32LE());
+	uint32 meshCount = stream.readUint32LE();
+	if (meshCount > 100000)
+		error("TeModel::load: Unexpected number of meshes %d", meshCount);
+	_meshes.resize(meshCount);
+	uint32 weightCount = stream.readUint32LE();
+	if (weightCount > 100000)
+		error("TeModel::load: Unexpected number of weights %d", weightCount);
+	_weightElements.resize(weightCount);
 	uint32 bonecount = stream.readUint32LE();
 	if (bonecount > 100000)
 		error("TeModel::load: Unexpected number of bones %d", bonecount);
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index 10166e37a2c..bdf61c9173c 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -96,7 +96,7 @@ public:
 	int findModelBone(const Common::String &name);
 	int findOrAddWeights(const Common::Array<weightElement> &weights);
 	void forceMatrix(const TeMatrix4x4 &matrix);
-	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned long num);
+	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num);
 	TeMatrix4x4 lerpElementsMatrix(unsigned long num, Common::Array<TeMatrix4x4> &matricies);
 
 	/* Align the stream to the nearest 4 byte boudary*/
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 4b279900895..09a019ec17b 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -44,7 +44,7 @@ void TePickMesh2::draw() {
 
 	TeRenderer *renderer = g_engine->getRenderer();
 
-	TeColor prevCol = renderer->currentColor();
+	const TeColor prevCol = renderer->currentColor();
 
 	renderer->setCurrentColor(TeColor(0xff, 0, 0, 0xff));
 	renderer->pushMatrix();
diff --git a/engines/tetraedge/te/te_pick_mesh2.h b/engines/tetraedge/te/te_pick_mesh2.h
index c43ee54f5ac..817b518b384 100644
--- a/engines/tetraedge/te/te_pick_mesh2.h
+++ b/engines/tetraedge/te/te_pick_mesh2.h
@@ -52,6 +52,7 @@ public:
 
 	Common::Array<TeVector3f32> &verticies() { return _verticies; }
 	const Common::Array<TeVector3f32> &verticies() const { return _verticies; }
+
 private:
 	Common::Array<TeVector3f32> _verticies;
 	unsigned long _lastTriangleHit;
diff --git a/engines/tetraedge/te/te_quaternion.cpp b/engines/tetraedge/te/te_quaternion.cpp
index 9a1c4086b2e..0ed7d3c3b9e 100644
--- a/engines/tetraedge/te/te_quaternion.cpp
+++ b/engines/tetraedge/te/te_quaternion.cpp
@@ -26,4 +26,8 @@ namespace Tetraedge {
 TeQuaternion::TeQuaternion() {
 }
 
+Common::String TeQuaternion::dump() const {
+	return Common::String::format("TeQuat(%.02f %.02f %.02f %.02f)", x(), y(), z(), w());
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_quaternion.h b/engines/tetraedge/te/te_quaternion.h
index 3cfa597a962..cc8ae96ce42 100644
--- a/engines/tetraedge/te/te_quaternion.h
+++ b/engines/tetraedge/te/te_quaternion.h
@@ -37,13 +37,37 @@ public:
 	static TeQuaternion fromAxisAndAngle(const TeVector3f32 &axis, float angle) {
 		TeQuaternion ret;
 		float f = sinf(angle * 0.5);
-		ret.w() = axis.x() * f;
-		ret.x() = axis.y() * f;
-		ret.y() = axis.z() * f;
-		ret.z() = cosf(angle * 0.5);
+		ret.x() = axis.x() * f;
+		ret.y() = axis.y() * f;
+		ret.z() = axis.z() * f;
+		ret.w() = cosf(angle * 0.5);
 		return ret;
 	}
 
+	static TeQuaternion fromEuler(const TeVector3f32 &euler) {
+		TeQuaternion rot;
+
+		rot.x() = sinf(euler.x() / 2.0);
+		rot.y() = 0.0;
+		rot.z() = 0.0;
+		rot.w() = cosf(euler.x() / 2.0);
+		TeQuaternion retval = rot;
+
+		rot.x() = 0.0;
+		rot.y() = sinf(euler.y() / 2.0);
+		rot.z() = 0.0;
+		rot.w() = cosf(euler.y() / 2.0);
+		retval *= rot;
+
+		rot.x() = 0.0;
+		rot.y() = 0.0;
+		rot.z() = sinf(euler.z() / 2.0);
+		rot.w() = cosf(euler.z() / 2.0);
+		retval *= rot;
+
+		return retval;
+	}
+
 	static void deserialize(Common::ReadStream &stream, TeQuaternion &dest) {
 		dest.value(0) = stream.readFloatLE();
 		dest.value(1) = stream.readFloatLE();
@@ -58,6 +82,7 @@ public:
 		stream.writeFloatLE(src.value(3));
 	}
 
+	Common::String dump() const;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_scene.cpp b/engines/tetraedge/te/te_scene.cpp
index 3df69b9b559..bdb826d9fc0 100644
--- a/engines/tetraedge/te/te_scene.cpp
+++ b/engines/tetraedge/te/te_scene.cpp
@@ -66,6 +66,7 @@ void TeScene::draw() {
 
 	currentCamera()->apply();
 	for (auto &m : _models) {
+		//debug("Draw scene model %s", m->name().c_str());
 		m->draw();
 	}
 	TeCamera::restore();
diff --git a/engines/tetraedge/te/te_scene.h b/engines/tetraedge/te/te_scene.h
index 4302a16c81f..0bdf12059d6 100644
--- a/engines/tetraedge/te/te_scene.h
+++ b/engines/tetraedge/te/te_scene.h
@@ -37,7 +37,7 @@ public:
 	TeScene();
 	virtual ~TeScene() {};
 
-	void close();
+	virtual void close();
 
 	TeIntrusivePtr<TeCamera> camera(const Common::String &name);
 	TeIntrusivePtr<TeModel> model(const Common::String &name);
@@ -46,7 +46,7 @@ public:
 	int currentCameraIndex() const { return _currentCameraIndex; }
 	Common::String currentCameraName() const;
 
-	void draw();
+	virtual void draw();
 	virtual bool load(const Common::Path &path) { return false; };
 
 	void removeModel(const Common::String &name);
@@ -55,7 +55,7 @@ public:
 		_currentCameraIndex = index;
 	}
 
-	void update();
+	virtual void update();
 
 	Common::Array<TeIntrusivePtr<TeModel>> &models() { return _models; }
 
diff --git a/engines/tetraedge/te/te_scummvm_codec.h b/engines/tetraedge/te/te_scummvm_codec.h
index 3fdb429d92d..dad39372c7d 100644
--- a/engines/tetraedge/te/te_scummvm_codec.h
+++ b/engines/tetraedge/te/te_scummvm_codec.h
@@ -33,7 +33,7 @@ public:
 	virtual ~TeScummvmCodec();
 
 	virtual bool load(const Common::Path &path) override;
-	virtual bool load(Common::SeekableReadStream &stream) override = 0;
+	virtual bool load(Common::SeekableReadStream &stream) = 0;
 	virtual uint width() override;
 	virtual uint height() override;
 	virtual int nbFrames() override { return 1; }
diff --git a/engines/tetraedge/te/te_sound_manager.cpp b/engines/tetraedge/te/te_sound_manager.cpp
index 6104d233c34..42588e7ed45 100644
--- a/engines/tetraedge/te/te_sound_manager.cpp
+++ b/engines/tetraedge/te/te_sound_manager.cpp
@@ -67,6 +67,13 @@ void TeSoundManager::playFreeSound(const Common::Path &path, float vol, const Co
 	mixer->playStream(Audio::Mixer::kMusicSoundType, &_handles[channel], stream, channelId, bvol);
 }
 
-// TODO: Add more functions here.
+void TeSoundManager::stopFreeSound(const Common::String &name) {
+	if (!_handles.contains(name))
+		return;
+	Audio::Mixer *mixer = g_system->getMixer();
+	mixer->stopHandle(_handles.getVal(name));
+	_handles.erase(name);
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_sound_manager.h b/engines/tetraedge/te/te_sound_manager.h
index cac071ec110..dbce36199cd 100644
--- a/engines/tetraedge/te/te_sound_manager.h
+++ b/engines/tetraedge/te/te_sound_manager.h
@@ -33,6 +33,7 @@ public:
 	TeSoundManager();
 
 	void playFreeSound(const Common::Path &path, float vol, const Common::String &channel);
+	void stopFreeSound(const Common::String &channel);
 
 private:
 	Common::HashMap<Common::String, Audio::SoundHandle> _handles;
diff --git a/engines/tetraedge/te/te_theora.cpp b/engines/tetraedge/te/te_theora.cpp
index b2947a80359..47c9b985e79 100644
--- a/engines/tetraedge/te/te_theora.cpp
+++ b/engines/tetraedge/te/te_theora.cpp
@@ -43,10 +43,6 @@ bool TeTheora::load(const Common::Path &path) {
 	return _decoder->loadFile(path);
 }
 
-bool TeTheora::load(Common::SeekableReadStream &stream) {
-	return _decoder->loadStream(&stream);
-}
-
 uint TeTheora::width() {
 	return _decoder->getWidth();
 }
diff --git a/engines/tetraedge/te/te_theora.h b/engines/tetraedge/te/te_theora.h
index b4302d5e148..517777b9dda 100644
--- a/engines/tetraedge/te/te_theora.h
+++ b/engines/tetraedge/te/te_theora.h
@@ -37,7 +37,6 @@ public:
 	virtual ~TeTheora();
 
 	virtual bool load(const Common::Path &path) override;
-	virtual bool load(Common::SeekableReadStream &stream) override;
 	virtual uint width() override;
 	virtual uint height() override;
 	virtual int nbFrames() override;
diff --git a/engines/tetraedge/te/te_tiled_surface.h b/engines/tetraedge/te/te_tiled_surface.h
index e081f5c1df9..9e9c77a42ea 100644
--- a/engines/tetraedge/te/te_tiled_surface.h
+++ b/engines/tetraedge/te/te_tiled_surface.h
@@ -41,7 +41,7 @@ public:
 
 	virtual int bufferSize() { return 1; } // unused?
 	void cont();
-	void draw();
+	void draw() override;
 	virtual void entry() {};
 	byte isLoaded();
 	bool load(const Common::Path &path);
diff --git a/engines/tetraedge/te/te_timer.cpp b/engines/tetraedge/te/te_timer.cpp
index 02b2085a9b0..79db37f7649 100644
--- a/engines/tetraedge/te/te_timer.cpp
+++ b/engines/tetraedge/te/te_timer.cpp
@@ -112,6 +112,10 @@ unsigned long TeTimer::timeElapsed() {
 	return elapsed;
 }
 
+unsigned long TeTimer::timeFromLastTimeElapsed() {
+	return realTimer()->time_() - _lastTimeElapsed;
+}
+
 unsigned long TeTimer::time_() {
 	return realTimer()->time_();
 }
diff --git a/engines/tetraedge/te/te_timer.h b/engines/tetraedge/te/te_timer.h
index fa7962516ee..6d04e5bd2d4 100644
--- a/engines/tetraedge/te/te_timer.h
+++ b/engines/tetraedge/te/te_timer.h
@@ -39,6 +39,7 @@ public:
 	unsigned long getTimeFromStart();
 	void setAlarmIn(unsigned long offset);
 	unsigned long timeElapsed();
+	unsigned long timeFromLastTimeElapsed();
 	unsigned long time_();
 	void setTime(unsigned long time);
 
diff --git a/engines/tetraedge/te/te_xml_gui.cpp b/engines/tetraedge/te/te_xml_gui.cpp
index f31d84e1866..e9ba7c0bd62 100644
--- a/engines/tetraedge/te/te_xml_gui.cpp
+++ b/engines/tetraedge/te/te_xml_gui.cpp
@@ -30,7 +30,9 @@ TeXmlGui::TeXmlGui() {
 }
 
 Common::String TeXmlGui::value(const Common::String &key) {
-	error("TODO: TeXmlGui::value Implement me.");
+	if (_map.contains(key))
+		return _map.getVal(key);
+	return "";
 }
 
 void TeXmlGui::load(const Common::Path &path) {
@@ -44,9 +46,8 @@ void TeXmlGui::load(const Common::Path &path) {
 }
 
 void TeXmlGui::clear() {
-
+	_map.clear();
+	// TODO: probably more here.
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge


Commit: 2c873918031f2ed1e436c99d3a3b4ee88f43da9d
    https://github.com/scummvm/scummvm/commit/2c873918031f2ed1e436c99d3a3b4ee88f43da9d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: WIP, fixed loads of TODOs.

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/bonus_menu.cpp
    engines/tetraedge/game/bonus_menu.h
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/game_sound.cpp
    engines/tetraedge/game/game_sound.h
    engines/tetraedge/game/how_to.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/main_menu.cpp
    engines/tetraedge/game/object3d.cpp
    engines/tetraedge/game/object3d.h
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_camera.h
    engines/tetraedge/te/te_extended_text_layout.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_i_layout.h
    engines/tetraedge/te/te_images_sequence.cpp
    engines/tetraedge/te/te_images_sequence.h
    engines/tetraedge/te/te_list_layout.h
    engines/tetraedge/te/te_lua_context.cpp
    engines/tetraedge/te/te_lua_script.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_model_animation.h
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_pick_mesh2.h
    engines/tetraedge/te/te_scene.cpp
    engines/tetraedge/te/te_scene.h
    engines/tetraedge/te/te_scrolling_layout.h
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_tiled_texture.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 174870ef10f..6fc5150494a 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -69,7 +69,7 @@ _drawShadows(true) {
 }
 
 void Application::create() {
-	warning("TODO: Move mainWindowCamera to mainWindow");
+	// TODO: Move mainWindowCamera to mainWindow?
 
 	const int winWidth = g_engine->getDefaultScreenWidth();
 	const int winHeight = g_engine->getDefaultScreenHeight();
@@ -284,7 +284,7 @@ void Application::destroy() {
 
 void Application::startGame(bool newGame, int difficulty) {
 	_appSpriteLayout.setVisible(false);
-	// TODO: there's another virtual call to appsprite surface here here.. not needed?
+	_appSpriteLayout.pause();
 	_appSpriteLayout.unload();
 	if (newGame)
 		_difficulty = difficulty;
@@ -299,8 +299,7 @@ bool Application::run() {
 	if (_created) {
 		TeTimer::updateAll();
 		if (!_dontUpdateWhenApplicationPaused) {
-			// TODO: finish commented-out bits??
-			// we run the inputmgr separately.. is that ok?
+			// Note: we run the inputmgr separately.. probably no need for this.
 			//_inputmgr->update();
 			TeAnimation::updateAll();
 			//TeVideo::updateAll();
@@ -506,6 +505,4 @@ const char *Application::inAppUnlockFullVersionID() {
 	error("TODO: Implement Application::inAppUnlockFullVersionID");
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/bonus_menu.cpp b/engines/tetraedge/game/bonus_menu.cpp
index d91c2c66437..bc734d7cc4f 100644
--- a/engines/tetraedge/game/bonus_menu.cpp
+++ b/engines/tetraedge/game/bonus_menu.cpp
@@ -24,7 +24,7 @@
 #include "common/textconsole.h"
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/application.h"
-//#include "tetraedge/te/te_input_manager"
+#include "tetraedge/te/te_input_mgr.h"
 
 namespace Tetraedge {
 
@@ -79,15 +79,11 @@ bool BonusMenu::onRightButton() {
 	return false;
 }
 
-bool BonusMenu::onSideButtonDown() {
-	/*
-	TeInputManager *inputmgr = g_engine->getInputManager();
-	 TeVector2s32 mousepos = inputmgr->getMousePos();
-	 _slideBtnStartMousePos = mousepos;
-	 buttonLayout("slideButton");
-	 // TODO set some flag in super (TeLuaGUI)
-	*/
-	error("TODO: implement me.");
+bool BonusMenu::onSlideButtonDown() {
+	TeInputMgr *inputmgr = g_engine->getInputMgr();
+	TeVector2s32 mousepos = inputmgr->lastMousePos();
+	_slideBtnStartMousePos = mousepos;
+	buttonLayoutChecked("slideButton")->setClickPassThrough(true);
 	return false;
 }
 
diff --git a/engines/tetraedge/game/bonus_menu.h b/engines/tetraedge/game/bonus_menu.h
index 0c87516159b..4cb63291fa1 100644
--- a/engines/tetraedge/game/bonus_menu.h
+++ b/engines/tetraedge/game/bonus_menu.h
@@ -53,7 +53,7 @@ public:
 	bool onPictureButton();
 	bool onQuitButton();
 	bool onRightButton();
-	bool onSideButtonDown();
+	bool onSlideButtonDown();
 
 	// empty? bool onLoadGameConfirmed() { };
 
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index e231c0c32f9..62aaff289d5 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -56,7 +56,7 @@ void Character::WalkSettings::clear() {
 
 Character::Character() : _curveOffset(0), _lastFrame(-1), _callbacksChanged(false),
 _missingCurrentAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"),
-_needsSomeUpdate(false), _positionFlag(false),
+_needsSomeUpdate(false), _positionFlag(false), _lookingAtTallThing(false),
 _stepSound1("sounds/SFX/PAS_H_BOIS1.ogg"), _stepSound2("sounds/SFX/PAS_H_BOIS2.ogg"),
 _freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(nullptr) {
 	_curModelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
@@ -125,7 +125,6 @@ float Character::animLengthFromFile(const Common::String &animname, uint *pframe
 	int frameCount = anim->lastFrame() + 1 - anim->firstFrame();
 	*pframeCount = frameCount;
 
-	// TODO: Check this maths.. is this really what it does?
 	return animLen * _model->scale().z();
 }
 
@@ -236,7 +235,7 @@ int Character::rightStepFrame(enum Character::WalkPart walkpart) {
 	return -1;
 }
 
-bool Character::loadModel(const Common::String &name, bool unused) {
+bool Character::loadModel(const Common::String &mname, bool unused) {
 	assert(_globalCharacterSettings);
 	if (_model) {
 		//TODO
@@ -245,10 +244,10 @@ bool Character::loadModel(const Common::String &name, bool unused) {
 	_model = new TeModel();
 	//_model->bonesUpdateSignal().add(this, &Character::onBonesUpdate);
 
-	if (!_globalCharacterSettings->contains(name))
+	if (!_globalCharacterSettings->contains(mname))
 		return false;
 
-	_characterSettings = _globalCharacterSettings->getVal(name);
+	_characterSettings = _globalCharacterSettings->getVal(mname);
 	_model->_texturePath = Common::Path("models/Textures");
 	_model->_enableLights = true;
 	Common::Path modelPath("models");
@@ -256,7 +255,7 @@ bool Character::loadModel(const Common::String &name, bool unused) {
 	if (!_model->load(modelPath))
 		return false;
 
-	_model->setName(name);
+	_model->setName(mname);
 	_model->setScale(_characterSettings._defaultScale);
 
 	for (auto &mesh : _model->_meshes)
@@ -270,11 +269,11 @@ bool Character::loadModel(const Common::String &name, bool unused) {
 	_model->setVisibleByName(_characterSettings._defaultMouth, true);
 	_model->setVisibleByName(_characterSettings._defaultBody, true);
 
-	setAnimation(_characterSettings._walkFileName, true, false, false, -1, 9999);
+	setAnimation(_characterSettings._walkFileName, true);
 
-	_walkPart0AnimLen = animLengthFromFile(walkAnim(WalkPart_Start), &_walkPart0AnimFrameCount, 9999);
-	_walkPart3AnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkPart3AnimFrameCount, 9999);
-	_walkPart1AnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkPart1AnimFrameCount, 9999);
+	_walkPart0AnimLen = animLengthFromFile(walkAnim(WalkPart_Start), &_walkPart0AnimFrameCount);
+	_walkPart3AnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkPart3AnimFrameCount);
+	_walkPart1AnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkPart1AnimFrameCount);
 
 	TeIntrusivePtr<Te3DTexture> shadow = new Te3DTexture();
 	shadow->load("models/Textures/simple_shadow_alpha.tga");
@@ -373,17 +372,17 @@ void Character::removeFromCurve() {
 	_curve.release();
 }
 
-bool Character::setAnimation(const Common::String &name, bool repeat, bool param_3, bool unused, int startFrame, int endFrame) {
-	if (name.empty())
+bool Character::setAnimation(const Common::String &aname, bool repeat, bool param_3, bool unused, int startFrame, int endFrame) {
+	if (aname.empty())
 		return false;
 
 	Common::Path animPath("models/Anims");
-	animPath.joinInPlace(name);
-	bool validAnim = (name.contains(_characterSettings._walkFileName) ||
-					  name.contains(walkAnim(WalkPart_Start)) ||
-					  name.contains(walkAnim(WalkPart_Loop)) ||
-					  name.contains(walkAnim(WalkPart_EndD)) ||
-					  name.contains(walkAnim(WalkPart_EndG)));
+	animPath.joinInPlace(aname);
+	bool validAnim = (aname.contains(_characterSettings._walkFileName) ||
+					  aname.contains(walkAnim(WalkPart_Start)) ||
+					  aname.contains(walkAnim(WalkPart_Loop)) ||
+					  aname.contains(walkAnim(WalkPart_EndD)) ||
+					  aname.contains(walkAnim(WalkPart_EndG)));
 	_missingCurrentAnim = !validAnim;
 
 	if (_curModelAnim) {
@@ -397,16 +396,16 @@ bool Character::setAnimation(const Common::String &name, bool repeat, bool param
 	_model->setAnim(_curModelAnim, repeat);
 	_lastFrame = -1;
 	_curModelAnim->play();
-	_setAnimName = name;
-	_curAnimName = name;
+	_setAnimName = aname;
+	_curAnimName = aname;
 	_someRepeatFlag = !(repeat | !param_3);
 
 	return true;
 }
 
-void Character::setAnimationSound(const Common::String &name, uint offset) {
+void Character::setAnimationSound(const Common::String &sname, uint offset) {
 	warning("TODO: Set field 0x2f8 to 0 in Character::setAnimationSound.");
-	_animSound = name;
+	_animSound = sname;
 	_animSoundOffset = offset;
 }
 
@@ -463,9 +462,12 @@ void Character::updateAnimFrame() {
 
 void Character::updatePosition(float curveOffset) {
 	if (!_curve->controlPoints().empty()) {
-		//TeVector3f32 pt = _curve->retrievePoint(curveOffset);
-		// add field 0x214
-		error("TODO: Implement Character::updatePosition");
+		TeVector3f32 pt = _curve->retrievePoint(curveOffset) + _curveStartLocation;
+		if (_freeMoveZone) {
+			bool flag;
+			pt = _freeMoveZone->correctCharacterPosition(pt, &flag, true);
+		}
+		_model->setPosition(pt);
 	}
 }
 
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 9e13e8b89c7..d9c005f2de4 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -99,7 +99,7 @@ public:
 	static TeIntrusivePtr<TeModelAnimation> animCacheLoad(const Common::Path &path);
 
 	float animLength(const TeModelAnimation &modelanim, long bone, long lastframe);
-	float animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe);
+	float animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe = 9999);
 	bool blendAnimation(const Common::String &animname, float amount, bool repeat, bool param_4);
 	TeVector3f32 correctPosition(const TeVector3f32 &pos);
 	float curveOffset();
@@ -126,7 +126,7 @@ public:
 	void removeFromCurve();
 	static Common::String rootBone() { return "Pere"; }
 
-	bool setAnimation(const Common::String &name, bool repeat, bool param_3, bool param_4, int startFrame, int endFrame);
+	bool setAnimation(const Common::String &name, bool repeat, bool param_3 = false, bool param_4 = false, int startFrame = -1, int endFrame = 9999);
 	void setAnimationSound(const Common::String &name, uint offset);
 	void setCurveOffset(float offset);
 	void setFreeMoveZone(TeFreeMoveZone *zone);
@@ -158,13 +158,26 @@ public:
 	bool needsSomeUpdate() const { return _needsSomeUpdate; }
 	void setNeedsSomeUpdate(bool val) { _needsSomeUpdate = val; }
 	void setCharLookingAt(Character *other) { _charLookingAt = other; }
+	const TeVector3f32 &positionCharacter() const { return _positionCharacter; }
 	void setPositionCharacter(const TeVector3f32 &val) { _positionCharacter = val; }
 	bool positionFlag() const { return _positionFlag; }
 	void setPositionFlag(bool val) { _positionFlag = val; }
+	void setCurveStartLocation(const TeVector3f32 &val) { _curveStartLocation = val; }
+	bool hasAnchor() const { return _hasAnchor; }
+	void setHasAnchor(bool val) { _hasAnchor = val; }
+	const TeVector2f32 &headRotation() const { return _headRotation; }
+	void setHeadRotation(const TeVector2f32 &val) { _headRotation = val; }
+	void setLastHeadRotation(const TeVector2f32 &val) { _lastHeadRotation = val; }
+	Character *charLookingAt() { return _charLookingAt; }
+	bool lookingAtTallThing() const { return _lookingAtTallThing; }
+	void setLookingAtTallThing(bool val) { _lookingAtTallThing = val; }
+	
 
 private:
 	float _curveOffset;
 	TeIntrusivePtr<TeBezierCurve> _curve;
+	TeVector3f32 _curveStartLocation;
+
 	TeFreeMoveZone *_freeMoveZone;
 	Common::String _freeMoveZoneName;
 	Common::String _stepSound1;
@@ -193,8 +206,13 @@ private:
 	bool _missingCurrentAnim;
 	bool _someRepeatFlag; // TODO: what is this?
 	bool _callbacksChanged;
-	bool _needsSomeUpdate; // TODO: what is this? Field 0x85.
+	bool _needsSomeUpdate;
 	bool _positionFlag;
+	bool _lookingAtTallThing;
+	
+	bool _hasAnchor;
+	TeVector2f32 _headRotation;
+	TeVector2f32 _lastHeadRotation;
 
 	TeVector3f32 _positionCharacter;
 
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 6bfc12cc8c4..4c646a33a67 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -47,10 +47,16 @@ namespace Tetraedge {
 Game::Game() : _objectsTakenVal(0), _score(0), _entered(false), _gameLoadState(0),
 _noScaleLayout(nullptr), _noScaleLayout2(nullptr), _warped(false), _saveRequested(false),
 _firstInventory(true), _movePlayerCharacterDisabled(false), _enteredFlag2(false),
-_luaShowOwnerError(false), _markersVisible(true), _running(false), _loadName("save.xml") {
+_luaShowOwnerError(false), _markersVisible(true), _running(false), _loadName("save.xml"),
+_randomSoundFinished(false), _randomSource("SyberiaGameRandom") {
 	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
 		_objectsTakenBits[i] = false;
 	}
+	_randomSound = new RandomSound();
+}
+
+Game::~Game() {
+	delete _randomSound;
 }
 
 /*static*/ const char *Game::OBJECTS_TAKEN_IDS[5] = {
@@ -137,14 +143,14 @@ void Game::addNoScaleChildren() {
 	_noScaleLayout->addChild(&_documentsBrowser.zoomedLayout());
 }
 
-void Game::addRandomSound(const Common::String &name, const Common::String &path, float f1, float f2) {
+void Game::addRandomSound(const Common::String &name, const Common::String &path, float f1, float volume) {
 	if (!_randomSounds.contains(name)) {
 		_randomSounds[name] = Common::Array<RandomSound*>();
 	}
 	RandomSound *randsound = new RandomSound();
 	randsound->_path = path;
 	randsound->_f1 = f1;
-	randsound->_f2 = f2;
+	randsound->_volume = volume;
 	randsound->_name = name;
 	_randomSounds[name].push_back(randsound);
 }
@@ -431,7 +437,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_scene._character->_model->setVisible(true);
 		_scene._character->deleteAllCallback();
 		_scene._character->stop();
-		_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true, false, false, -1, 9999);
+		_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true);
 		if (!_scene.findKate()) {
 			_scene.models().push_back(_scene._character->_model);
 			_scene.models().push_back(_scene._character->_shadowModel[0]);
@@ -526,9 +532,8 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	TeButtonLayout *vidbgbtn = _inGameGui.buttonLayout("videoBackgroundButton");
 	vidbgbtn->setVisible(false);
-	/* TODO: Restore the original behavior here (onLockVideoButtonValidated) */
-	vidbgbtn->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
-	vidbgbtn->onMouseClickValidated().add(this, &Game::onSkipVideoButtonValidated);
+	vidbgbtn->onMouseClickValidated().remove(this, &Game::onLockVideoButtonValidated);
+	vidbgbtn->onMouseClickValidated().add(this, &Game::onLockVideoButtonValidated);
 
 	TeSpriteLayout *video = _inGameGui.spriteLayout("video");
 	video->setVisible(false);
@@ -606,10 +611,11 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_luaScript.execute("OnSelectedObject", _inventory.selectedObject());
 	}
 
-	if (!_gameSounds.empty()) {
-		//for (auto & sound : _gameSounds) {
-		warning("TODO: Game::initWarp: Do game sound stuff here");
+	for (GameSound *sound : _gameSounds) {
+		sound->stop();
+		sound->deleteLater();
 	}
+	_gameSounds.clear();
 
 	for (auto &randsoundlist : _randomSounds) {
 		for (auto *randsound : randsoundlist._value) {
@@ -771,7 +777,7 @@ bool Game::onDialogFinished(const Common::String &val) {
 bool Game::onDisplacementFinished() {
 	_sceneCharacterVisibleFromLoad = true;
 	_scene._character->stop();
-	_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true, false, false, -1, 9999);
+	_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true);
 
 	// TODO: Twiddle flags 0x424b and 0x4249
 	/*
@@ -946,7 +952,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 				TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(charPos, TeVector2s32(pt), 8.0, true);
 				if (curve) {
 					_scene.setCurve(curve);
-					// TODO: set character field_0x214 to TeVector3f32(0,0,0)
+					character->setCurveStartLocation(TeVector3f32());
 					if (curve->controlPoints().size() == 1) {
 						character->endMove();
 					} else {
@@ -962,7 +968,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 						character->placeOnCurve(curve);
 						character->setCurveOffset(0.0);
 						if (charAnim != character->walkAnim(Character::WalkPart_Start)) {
-							character->setAnimation(character->walkAnim(Character::WalkPart_Start), false, false, false, -1, 9999);
+							character->setAnimation(character->walkAnim(Character::WalkPart_Start), false);
 						}
 						character->walkTo(1.0, false);
 						_sceneCharacterVisibleFromLoad = false;
@@ -972,13 +978,11 @@ bool Game::onMouseClick(const Common::Point &pt) {
 					return false;
 				}
 			}
-			// TOOD: Finis this (line ~180 onward)
-			warning("TODO: Finish Game::onMouseClick");
 		}
 		TeVector3f32 lastPoint = _scene.curve()->controlPoints().back();
-		character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true, false, false, -1, 9999);
+		character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
 		character->walkTo(1.0, false);
-		// TODO: Set app field field_0x910b
+		// TODO: Set app field field_0x424b to true
 		_posPlayer = lastPoint;
 	}
 
@@ -1173,11 +1177,86 @@ void Game::playRandomSound(const Common::String &name) {
 		warning("Game::playRandomSound: can't find sound list %s", name.c_str());
 		return;
     }
-    warning("TODO: Implemet Game::playRandomSound");
+    
+    if (!_randomSoundFinished) {
+		_randomSoundTimer.start();
+		int r = _randomSource.getRandomNumber(RAND_MAX);
+		float f = (r + 1 + (r / 100) * -100);
+		long time = 1000000;
+		if (f >= 25.0) {
+			time = f * 45000.0;
+		}
+		_randomSoundTimer.setAlarmIn(time);
+		_randomSoundTimer.alarmSignal().remove(_randomSound, &RandomSound::onSoundFinished);
+		_randomSoundTimer.alarmSignal().add(_randomSound, &RandomSound::onSoundFinished);
+		_randomSound->_name = name;
+	} else {
+		Common::Array<RandomSound *> &sndlist = _randomSounds[name];
+		float total = 0.0;
+		for (auto *snd : sndlist) {
+			total += snd->_f1;
+		}
+		int r = _randomSource.getRandomNumber(RAND_MAX);
+		float total2 = 0.0;
+		unsigned int i = 0;
+		while (i < sndlist.size() && total2 <= r * 4.656613e-10 * total) {
+			total2 += sndlist[i]->_f1;
+			i++;
+		}
+		assert(i > 0);
+		i--;
+		RandomSound *sound = sndlist[i];
+		sound->_music.volume(sound->_volume);
+		sound->_music.onStopSignal().remove(sound, &RandomSound::onSoundFinished);
+		sound->_music.onStopSignal().add(sound, &RandomSound::onSoundFinished);
+		sound->_music.load(sound->_path.toString());
+		sound->_music.repeat(false);
+		sound->_music.play();
+		// TODO: set a flag that it's playing?
+	}
 }
 
-void Game::playSound(const Common::String &name, int param_2, float param_3) {
-	warning("TODO: Implemet Game::playSound");
+void Game::playSound(const Common::String &name, int repeats, float volume) {
+	Game *game = g_engine->getGame();
+
+	assert(repeats == 1 || repeats == -1);
+	if (repeats == 1) {
+		GameSound *sound = new GameSound();
+		sound->setName(name);
+		sound->setChannelName("sfx");
+		sound->repeat(false);
+		sound->load(name);
+		sound->volume(volume);
+		if (!sound->play()) {
+			game->luaScript().execute("OnFreeSoundFinished", name);
+			game->luaScript().execute("OnCellFreeSoundFinished", name);
+			// TODO: original seems to leak sound here??
+		} else {
+			sound->onStopSignal().add(sound, &GameSound::onSoundStopped);
+			// TODO: set snd->field_0x201
+			_gameSounds.push_back(sound);
+		}
+	} else if (repeats == -1) {
+		for (GameSound *snd : _gameSounds) {
+			if (snd->getAccessName() == name) {
+				// TODO: set snd->field_0x201
+				return;
+			}
+		}
+		
+		GameSound *sound = new GameSound();
+		sound->setChannelName("sfx");
+		sound->load(name);
+		sound->volume(volume);
+		if (!sound->play()) {
+			game->luaScript().execute("OnFreeSoundFinished", name);
+			game->luaScript().execute("OnCellFreeSoundFinished", name);
+			delete sound;
+		} else {
+			// TODO: set snd->field_0x201
+			_gameSounds.push_back(sound);
+		}
+	}
 }
 
 void Game::removeNoScale2Child(TeLayout *layout) {
@@ -1341,26 +1420,34 @@ void Game::update() {
 		else
 			warning("Game::update: InventoryButton is null.");
 
-		if (_scene._character) {
-			TeIntrusivePtr<TeModel> model = _scene._character->_model;
+		Character *player = _scene._character;
+		if (player) {
+			TeIntrusivePtr<TeModel> model = player->_model;
 			bool modelVisible = model->visible();
 			if (!model->anim().get())
-				_scene._character->permanentUpdate();
+				player->permanentUpdate();
 			if (modelVisible) {
-				if (_scene._character->needsSomeUpdate()) {
+				if (player->needsSomeUpdate()) {
 					Game *game = g_engine->getGame();
 					game->_sceneCharacterVisibleFromLoad = false;
-					TeVector3f32 charPos = _scene._character->_model->position();
-					const Common::String charName = _scene._character->_model->name();
-					TeFreeMoveZone *zone = _scene.pathZone(_scene._character->freeMoveZoneName());
+					TeVector3f32 charPos = player->_model->position();
+					const Common::String charName = player->_model->name();
+					TeFreeMoveZone *zone = _scene.pathZone(player->freeMoveZoneName());
 					if (zone) {
 						TeIntrusivePtr<TeCamera> cam = _scene.currentCamera();
 						zone->setCamera(cam, false);
-						_scene._character->setFreeMoveZone(zone);
-						_scene.setPositionCharacter(charName, _scene._character->freeMoveZoneName(), charPos);
-						error("TODO: Game::update: Finish bit after permanentUpdate");
+						player->setFreeMoveZone(zone);
+						_scene.setPositionCharacter(charName, player->freeMoveZoneName(), charPos);
+						TeIntrusivePtr<TeBezierCurve> curve = zone->curve(model->position(), player->positionCharacter());
+						_scene.setCurve(curve);
+						player->setCurveStartLocation(TeVector3f32(0, 0, 0));
+						player->placeOnCurve(curve);
+						player->setCurveOffset(0.0f);
+						player->setAnimation(player->walkAnim(Character::WalkPart_Start), false);
+						player->walkTo(1.0f, false);
+						// TODO: Set app field field_0x424b
 					}
-					_scene._character->setNeedsSomeUpdate(false);
+					player->setNeedsSomeUpdate(false);
 				}
 
 				const Common::String &charAnim = _scene._character->curAnimName();
@@ -1407,22 +1494,37 @@ bool Game::HitObject::onChangeWarp() {
 
 bool Game::HitObject::onDown() {
 	_game->luaScript().execute("OnButtonDown", _name);
-	// TODO: set this field _game->field_0x4249 = 1;
+	// TODO: set this field _game->field_0x4249 = true;
 	return false;
 }
 
 bool Game::HitObject::onUp() {
 	_game->luaScript().execute("OnButtonUp", _name);
-	// TODO: set this field _game->field_0x4249 = 1;
+	// TODO: set this field _game->field_0x4249 = true;
 	return false;
 }
 
 bool Game::HitObject::onValidated() {
 	if (!g_engine->getApplication()->isLockCursor()) {
 		_game->luaScript().execute("OnWarpObjectHit", _name);
-		// TODO: set this field _game->field_0x4249 = 1;
+		// TODO: set this field _game->field_0x4249 = true;
+	}
+	return false;
+}
+
+bool Game::RandomSound::onSoundFinished() {
+	Game *game = g_engine->getGame();
+	_music.onStopSignal().remove(this, &RandomSound::onSoundFinished);
+	if (game->_randomSoundFinished) {
+		game->_randomSoundFinished = false;
+	} else {
+		game->_randomSoundFinished = true;
+		game->_randomSound->_music.onStopSignal().remove(this, &RandomSound::onSoundFinished);
+		game->_randomSoundTimer.stop();
 	}
+	game->playRandomSound(_name);
 	return false;
 }
 
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index f383f899f3a..d95807a978c 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -24,6 +24,8 @@
 
 #include "common/types.h"
 #include "common/str.h"
+#include "common/random.h"
+
 #include "tetraedge/game/documents_browser.h"
 #include "tetraedge/game/inventory.h"
 #include "tetraedge/game/inventory_menu.h"
@@ -46,6 +48,7 @@ class TeLuaThread;
 class Game {
 public:
 	Game();
+	~Game();
 
 	struct HitObject {
 		bool onChangeWarp();
@@ -65,8 +68,8 @@ public:
 		Common::String _name;
 		TeMusic _music;
 		float _f1;
-		float _f2;
-		byte onSoundFinished();
+		float _volume;
+		bool onSoundFinished();
 	};
 
 	struct YieldedCallback {
@@ -139,7 +142,7 @@ public:
 	void pauseSounds() {}; // does nothing?
 	void playMovie(const Common::String &vidPath, const Common::String &musicPath);
 	void playRandomSound(const Common::String &name);
-	void playSound(const Common::String &name, int param_2, float param_3);
+	void playSound(const Common::String &name, int param_2, float volume);
 	void removeNoScale2Child(TeLayout *layout);
 	void removeNoScale2Children();
 	void removeNoScaleChildren();
@@ -173,12 +176,13 @@ public:
 	const Common::String &currentScene() { return _currentScene; }
 	const Common::Path &sceneZonePath() { return _sceneZonePath; }
 	TeLuaScript &luaScript() { return _luaScript; }
+	TeLuaContext &luaContext() { return _luaContext; }
 	InGameScene &scene() { return _scene; }
 	Dialog2 &dialog2() { return _dialog2; }
 	Question2 &question2() { return _question2; }
 	TeLuaGUI &gui3() { return _gui3; }
 	Objectif &objectif() { return _objectif; }
-	Common::Array<YieldedCallback> yieldedCallbacks() { return _yieldedCallbacks; }
+	Common::Array<YieldedCallback> &yieldedCallbacks() { return _yieldedCallbacks; }
 	void setSaveRequested() { _saveRequested = true; }
 	bool markersVisible() const { return _markersVisible; }
 
@@ -225,7 +229,7 @@ private:
 	Common::Array<YieldedCallback> _yieldedCallbacks;
 
 	Common::HashMap<Common::String, bool> _unlockedArtwork;
-	Common::HashMap<Common::String, Common::Array<RandomSound*>> _randomSounds;
+	Common::HashMap<Common::String, Common::Array<RandomSound *>> _randomSounds;
 
 	int _gameLoadState;
 
@@ -251,6 +255,11 @@ private:
 	bool _sceneCharacterVisibleFromLoad;
 	bool _markersVisible;
 	bool _saveRequested;
+	bool _randomSoundFinished;
+	
+	Common::RandomSource _randomSource;
+	RandomSound *_randomSound;
+	TeTimer _randomSoundTimer;
 
 	TeLayout *_noScaleLayout;
 	TeLayout *_noScaleLayout2;
diff --git a/engines/tetraedge/game/game_sound.cpp b/engines/tetraedge/game/game_sound.cpp
index 257f2b89d33..16a86615ce3 100644
--- a/engines/tetraedge/game/game_sound.cpp
+++ b/engines/tetraedge/game/game_sound.cpp
@@ -20,12 +20,37 @@
  */
 
 #include "tetraedge/game/game_sound.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/tetraedge.h"
+
+#include "tetraedge/te/te_lua_thread.h"
 
 namespace Tetraedge {
 
 GameSound::GameSound() {
 }
 
-// TODO: Add more functions here.
+bool GameSound::onSoundStopped() {
+	Game *game = g_engine->getGame();
+	if (!game->luaContext().isCreated())
+		return false;
+
+	Common::Array<Game::YieldedCallback> &callbacks = game->yieldedCallbacks();
+	for (unsigned int i = 0; i < callbacks.size(); i++) {
+		if (callbacks[i]._luaFnName == "OnFreeSoundFinished" && callbacks[i]._luaParam == _name) {
+			TeLuaThread *thread = callbacks[i]._luaThread;
+			callbacks.remove_at(i);
+			if (thread) {
+				thread->resume();
+				return false;
+			}
+			break;
+		}
+	}
+	game->luaScript().execute("OnFreeSoundFinished", _name);
+	game->luaScript().execute("OnCellFreeSoundFinished", _name);
+	return false;
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/game_sound.h b/engines/tetraedge/game/game_sound.h
index 2ea73e8edfd..cd2a4177b99 100644
--- a/engines/tetraedge/game/game_sound.h
+++ b/engines/tetraedge/game/game_sound.h
@@ -31,7 +31,9 @@ class GameSound : public TeMusic {
 public:
 	GameSound();
 
-	void onSoundStopped();
+	bool onSoundStopped();
+	const Common::String &name() const { return _name; }
+	void setName(const Common::String &val) { _name = val; }
 
 private:
 	Common::String _name;
diff --git a/engines/tetraedge/game/how_to.cpp b/engines/tetraedge/game/how_to.cpp
index a863d4e8729..e0448d47923 100644
--- a/engines/tetraedge/game/how_to.cpp
+++ b/engines/tetraedge/game/how_to.cpp
@@ -27,12 +27,19 @@ HowTo::HowTo() : _entered(false) {
 }
 
 void HowTo::leave()	{
-
+	error("TODO: Implement HowTo::leave");
 }
 
 void HowTo::enter()	{
+	error("TODO: Implement HowTo::enter");
+}
+
+bool HowTo::onDefaultPadButtonUp(uint32 flags) {
+	error("TODO: Implement HowTo::onDefaultPadButtonUp");
+}
 
+bool HowTo::updateDisplay(uint oldval, uint newval) {
+	error("TODO: Implement HowTo::updateDisplay");
 }
-// TODO: Add more functions here.
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 157ef41ff73..2fb70cdbdda 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -64,7 +64,25 @@ void InGameScene::addAnchorZone(const Common::String &s1, const Common::String &
 			return;
 		}
 	}
-	warning("TODO: Finish InGameScene::addAnchorZone");
+
+	currentCamera()->apply();
+	AnchorZone *zone = new AnchorZone();
+	zone->_name = name;
+	zone->_radius = radius;
+	zone->_activated = true;
+
+	if (s1.contains("Int")) {
+		TeButtonLayout *btn = hitObjectGui().buttonLayout(name);
+		TeVector3f32 pos = btn->position();
+		pos.x() += g_engine->getDefaultScreenWidth() / 2.0f;
+		pos.y() += g_engine->getDefaultScreenHeight() / 2.0f;
+		zone->_loc = currentCamera()->worldTransformationMatrix() * currentCamera()->transformPoint2Dto3D(pos);
+	} else {
+		if (s1.contains("Dummy")) {
+			Dummy d = dummy(name);
+			zone->_loc = d._position;
+		}
+	}
 }
 
 bool InGameScene::addMarker(const Common::String &markerName, const Common::String &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal) {
@@ -219,7 +237,13 @@ TeIntrusivePtr<TeBezierCurve> InGameScene::curve(const Common::String &curveName
 }
 
 void InGameScene::deleteAllCallback() {
-	warning("TODO: implement InGameScene::deleteAllCallback");
+	for (auto &pair : _callbacks) {
+		for (auto *cb : pair._value) {
+			delete cb;
+		}
+		pair._value.clear();
+	}
+	_callbacks.clear();
 }
 
 void InGameScene::deleteMarker(const Common::String &markerName) {
@@ -530,11 +554,11 @@ bool InGameScene::load(const Common::Path &path) {
 		deserializeModel(scenefile, model, pickmesh);
 		if (modelname.contains("Clic")) {
 			_hitObjects.push_back(model);
-			// TODO: double-check this, probably right?
 			model->setVisible(false);
 			model->setColor(TeColor(0, 0xff, 0, 0xff));
 			models().push_back(model);
 			pickmesh->setName(modelname);
+			_pickMeshes.push_back(pickmesh);
 		} else {
 			delete pickmesh;
 			if (modelname.substr(0, 2) != "ZB") {
@@ -805,18 +829,17 @@ void InGameScene::moveCharacterTo(const Common::String &charName, const Common::
 	if (c != nullptr && c != _character) {
 		Game *game = g_engine->getGame();
 		if (!game->_movePlayerCharacterDisabled) {
-			// TODO: c->field_0x214 = c->characterSettings()._cutSceneCurveDemiPosition;
+			c->setCurveStartLocation(c->characterSettings()._cutSceneCurveDemiPosition);
 			TeIntrusivePtr<TeBezierCurve> crve = curve(curveName);
 			c->placeOnCurve(crve);
 			c->setCurveOffset(curveOffset);
 			const Common::String walkStartAnim = c->walkAnim(Character::WalkPart_Start);
 			if (walkStartAnim.empty()) {
-				c->setAnimation(c->walkAnim(Character::WalkPart_Loop), true, false, false, -1, 9999);
+				c->setAnimation(c->walkAnim(Character::WalkPart_Loop), true);
 			} else {
-				c->setAnimation(c->walkAnim(Character::WalkPart_Start), false, false, false, -1, 9999);
+				c->setAnimation(c->walkAnim(Character::WalkPart_Start), false);
 			}
 			c->walkTo(curveEnd, false);
-			error("TODO: Finish InGameScene::moveCharacterTo");
 		}
 	}
 }
@@ -965,12 +988,61 @@ void InGameScene::update() {
 		_bgGui.layoutChecked("background")->setZPosition(0.0f);
 	}
 	if (_character) {
-		// TODO: Do some stuff for character here.
+		_character->setHasAnchor(false);
+		for (AnchorZone *zone : _anchorZones) {
+			if (aroundAnchorZone(zone)) {
+				TeVector2f32 headRot(getHeadHorizontalRotation(_character, zone->_loc),
+					getHeadVerticalRotation(_character, zone->_loc));
+				if (headRot.getX() * 180.0 / M_PI > 90.0 || headRot.getY() * 180.0 / M_PI > 45.0) {
+					_character->setHasAnchor(false);
+					_character->setLastHeadRotation(_character->headRotation());
+				} else {
+					_character->setHeadRotation(headRot);
+					_character->setHasAnchor(true);
+				}
+			}
+		}
+		if (_character->charLookingAt()) {
+			TeVector3f32 targetpos = _character->charLookingAt()->_model->position();
+			if (_character->lookingAtTallThing())
+				targetpos.y() += 17;
+			TeVector2f32 headRot(getHeadHorizontalRotation(_character, targetpos),
+					getHeadVerticalRotation(_character, targetpos));
+			float hangle = headRot.getX() * 180.0 / M_PI;
+			if (hangle > 90)
+				headRot.setX(M_PI_2);
+			else if (hangle < -90)
+				headRot.setX(-M_PI_2);
+			_character->setHeadRotation(headRot);
+			_character->setHasAnchor(true);
+		}
 	}
 	for (Character *c : _characters) {
-		// TODO: Something with other characters.
+		if (c->charLookingAt()) {
+			TeVector3f32 targetpos = c->charLookingAt()->_model->position();
+			if (c->lookingAtTallThing())
+				targetpos.y() += 17;
+			TeVector2f32 headRot(getHeadHorizontalRotation(c, targetpos),
+					getHeadVerticalRotation(c, targetpos));
+			float hangle = headRot.getX() * 180.0 / M_PI;
+			if (hangle > 90)
+				headRot.setX(M_PI_2);
+			else if (hangle < -90)
+				headRot.setX(-M_PI_2);
+			c->setHeadRotation(headRot);
+			c->setHasAnchor(true);
+		}
 	}
+
 	// TODO: some other stuff with callbacks and spritelayouts here
+	for (auto &callback : _callbacks) {
+	
+	}
+
+	TeLuaGUI::StringMap<TeSpriteLayout *> &sprites = bgGui().spriteLayouts();
+	for (auto &sprite : sprites) {
+	
+	}
 
 	TeScene::update();
 
@@ -991,7 +1063,13 @@ void InGameScene::update() {
 	}
 
 	for (Object3D *obj : _object3Ds) {
-		// TODO: something with object3ds
+		// TODO: update object3ds if they are translating or rotating.
+		if (obj->_translateTime >= 0) {
+		
+		}
+		if (obj->_rotateTime >= 0) {
+		
+		}
 	}
 }
 
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index cc8dcf76f61..18e72dd540a 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -53,6 +53,11 @@ public:
 		Common::String _name;
 		TeSpriteLayout *_layout;
 	};
+	
+	struct Callback {
+		float _f;
+		Common::String _name;
+	};
 
 	struct SoundStep {
 		Common::String _stepSound1;
@@ -137,18 +142,18 @@ public:
 	void loadInteractions(const Common::Path &path);
 	bool loadLights(const Common::Path &path);
 	void loadMarkers(const Common::Path &path);
-	bool loadObject(const Common::String &name);
+	bool loadObject(const Common::String &oname);
 	void loadObjectMaterials(const Common::String &name);
 	void loadObjectMaterials(const Common::String &path, const Common::String &name);
-	bool loadPlayerCharacter(const Common::String &name);
+	bool loadPlayerCharacter(const Common::String &cname);
 
 	void moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd);
-	int object(const Common::String &name);
-	Object3D *object3D(const Common::String &name);
+	int object(const Common::String &oname);
+	Object3D *object3D(const Common::String &oname);
 	void onMainWindowSizeChanged();
-	TeFreeMoveZone *pathZone(const Common::String &name);
-	TeVector3f32 positionMarker(const Common::String &name);
-	void removeBlockingObject(const Common::String &name);
+	TeFreeMoveZone *pathZone(const Common::String &zname);
+	TeVector3f32 positionMarker(const Common::String &mname);
+	void removeBlockingObject(const Common::String &oname);
 
 	void reset();
 	void setImagePathMarker(const Common::String &markerName, const Common::String &path);
@@ -211,6 +216,7 @@ private:
 	Common::Array<TePickMesh2 *> _pickMeshes;
 
 	Common::HashMap<Common::String, SoundStep> _soundSteps;
+	Common::HashMap<Common::String, Common::Array<Callback*>> _callbacks;
 
 	Common::Array<TeIntrusivePtr<TeModel>> _hitObjects;
 	Common::Array<Object> _objects;
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 2ad6b17d88b..bb888e0a407 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -42,7 +42,7 @@ void Inventory::enter() {
 	Game *game = g_engine->getGame();
 	Character *character = game->scene()._character;
 	character->stop();
-	character->setAnimation(character->characterSettings()._walkFileName, true, false, false, -1, 9999);
+	character->setAnimation(character->characterSettings()._walkFileName, true);
 	_gui.layoutChecked("textObject")->setVisible(false);
 
 	if (!game->_firstInventory) {
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 88745e3c0e3..e39d09f19ef 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -270,7 +270,28 @@ static int tolua_ExportedFunctions_RequestAutoSave00(lua_State *L) {
 }
 
 static void HideObject(const Common::String &objName) {
-	warning("TODO: HideObject");
+	Game *game = g_engine->getGame();
+	TeIntrusivePtr<TeModel> model = game->scene().model(objName);
+	if (model) {
+		model->setVisible(false);
+		return;
+	}
+
+	debug("[HideObject] Object 3D \"%s\" doesn't exist.", objName.c_str());
+	TeLayout *layout = game->scene().bgGui().layout(objName);
+	if (layout) {
+		layout->setVisible(false);
+		return;
+	}
+
+	debug("[HideObject] \"Set\" Object 2D \"%s\" doesn't exist.", objName.c_str());
+	layout = game->gui3().layout(objName);
+	if (layout) {
+		layout->setVisible(false);
+		return;
+	}
+
+	debug("[HideObject] \"For\" Object 2D \"%s\" doesn't exist.", objName.c_str());
 }
 
 static int tolua_ExportedFunctions_HideObject00(lua_State *L) {
@@ -284,7 +305,28 @@ static int tolua_ExportedFunctions_HideObject00(lua_State *L) {
 }
 
 static void ShowObject(const Common::String &objName) {
-	warning("TODO: ShowObject");
+	Game *game = g_engine->getGame();
+	TeIntrusivePtr<TeModel> model = game->scene().model(objName);
+	if (model) {
+		model->setVisible(true);
+		return;
+	}
+
+	debug("[ShowObject] Object 3D \"%s\" doesn't exist.", objName.c_str());
+	TeLayout *layout = game->scene().bgGui().layout(objName);
+	if (layout) {
+		layout->setVisible(true);
+		return;
+	}
+
+	debug("[ShowObject] \"Set\" Object 2D \"%s\" doesn't exist.", objName.c_str());
+	layout = game->gui3().layout(objName);
+	if (layout) {
+		layout->setVisible(true);
+		return;
+	}
+
+	debug("[ShowObject] \"For\" Object 2D \"%s\" doesn't exist.", objName.c_str());
 }
 
 static int tolua_ExportedFunctions_ShowObject00(lua_State *L) {
@@ -375,8 +417,15 @@ static int tolua_ExportedFunctions_SetCharacterPosition00(lua_State *L) {
 	error("#ferror in function 'SetCharacterPosition': %d %d %s", err.index, err.array, err.type);
 }
 
-static void SetGroundObjectPosition(const Common::String &objname, float f1, float f2, float f3) {
-	warning("TODO: Implement SetGroundObjectPosition");
+static void SetGroundObjectPosition(const Common::String &objname, float x, float y, float z) {
+	Game *game = g_engine->getGame();
+	Object3D *obj = game->scene().object3D(objname);
+	if (!obj) {
+		warning("[SetGroundObjectPosition] Object not found %s", objname.c_str());
+		return;
+	}
+	obj->model()->setPosition(TeVector3f32(x, y, z));
+	obj->model()->setVisible(true);
 }
 
 static int tolua_ExportedFunctions_SetGroundObjectPosition00(lua_State *L) {
@@ -394,8 +443,17 @@ static int tolua_ExportedFunctions_SetGroundObjectPosition00(lua_State *L) {
 	error("#ferror in function 'SetGroundObjectPosition': %d %d %s", err.index, err.array, err.type);
 }
 
-static void SetGroundObjectRotation(const Common::String &objname, float f1, float f2, float f3) {
-	warning("TODO: Implement SetGroundObjectRotation");
+static void SetGroundObjectRotation(const Common::String &objname, float x, float y, float z) {
+	Game *game = g_engine->getGame();
+	Object3D *obj = game->scene().object3D(objname);
+	if (!obj) {
+		warning("[SetGroundObjectRotation] Object not found %s", objname.c_str());
+		return;
+	}
+	
+	TeVector3f32 rotvec(x * M_PI / 180.0, y * M_PI / 180.0, z * M_PI / 180.0);
+	obj->model()->setRotation(TeQuaternion::fromEuler(rotvec));
+	obj->model()->setVisible(true);
 }
 
 static int tolua_ExportedFunctions_SetGroundObjectRotation00(lua_State *L) {
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
index 7c56dccb83d..ec92bbee2ce 100644
--- a/engines/tetraedge/game/main_menu.cpp
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -143,14 +143,14 @@ void MainMenu::leave() {
 	_entered= false;
 }
 
-bool MainMenu::deleteFile(const Common::String &name) {
+bool MainMenu::deleteFile(const Common::String &fname) {
 	error("TODO: Implement MainMenu::deleteFile");
 }
 
 bool MainMenu::onActivedTuto() {
 	Application *app = g_engine->getApplication();
 	app->setTutoActivated(true);
-	// TODO: Set game val false too?
+	g_engine->getGame()->_firstInventory = true;
 	app->captureFade();
 	leave();
 	app->startGame(true, 1);
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index cfd6377ce96..a05d032198e 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -28,7 +28,7 @@ namespace Tetraedge {
 /*static*/ Common::HashMap<Common::String, Object3D::ObjectSettings> *Object3D::_objectSettings = nullptr;
 
 
-Object3D::Object3D() {
+Object3D::Object3D() : _translateTime(-1), _rotateTime(-1) {
 }
 
 bool Object3D::loadModel(const Common::String &name) {
diff --git a/engines/tetraedge/game/object3d.h b/engines/tetraedge/game/object3d.h
index d1d321bf3cb..b1f1e9a1e0a 100644
--- a/engines/tetraedge/game/object3d.h
+++ b/engines/tetraedge/game/object3d.h
@@ -49,6 +49,16 @@ public:
 
 	TeIntrusivePtr<TeModel> model() { return _modelPtr; }
 
+	float _rotateTime;
+	TeTimer _rotateTimer;
+	TeVector3f32 _rotateStart;
+	TeVector3f32 _rotateAmount;
+	
+	float _translateTime;
+	TeTimer _translateTimer;
+	TeVector3f32 _translateStart;
+	TeVector3f32 _translateAmount;
+
 private:
 	static Common::HashMap<Common::String, ObjectSettings> *_objectSettings;
 
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index f41baa6d4c4..65817da15fa 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -36,9 +36,9 @@ TeCamera::TeCamera() : _projectionMatrixType(0), _orthogonalParamL(1.0f),
 }
 
 void TeCamera::apply() {
-	/*debug("TeCamera::apply %13s mtype %d fov %.2f persp %.2f orth(%.2f %.2f) pos %s scale %s", name().c_str(),
-			_projectionMatrixType, _fov, _somePerspectiveVal, _orthNearVal, _orthFarVal,
-			position().dump().c_str(), scale().dump().c_str());*/
+	//debug("TeCamera::apply %13s mtype %d fov %.2f persp %.2f orth(%.2f %.2f) pos %s scale %s rot %s", name().c_str(),
+	//		_projectionMatrixType, _fov, _somePerspectiveVal, _orthNearVal, _orthFarVal,
+	//		position().dump().c_str(), scale().dump().c_str(), rotation().dump().c_str());
 	applyProjection();
 	applyTransformations();
 }
@@ -105,7 +105,7 @@ void TeCamera::buildPerspectiveMatrix() {
 	_projectionMatrix.setValue(0, 0, (1.0 / f) / ((float)_viewportW / _viewportH));
 	_projectionMatrix.setValue(1, 1, 1.0 / f);
 	_projectionMatrix.setValue(2, 2, (_orthNearVal + _orthFarVal) / (_orthNearVal - _orthFarVal));
-	_projectionMatrix.setValue(2, 2, (_orthNearVal * _orthFarVal) / (_orthNearVal - _orthFarVal));
+	_projectionMatrix.setValue(3, 2, (_orthNearVal * _orthFarVal) / (_orthNearVal - _orthFarVal));
 	_projectionMatrix.setValue(2, 3, -1);
 	_projectionMatrix.setValue(3, 3, 0.0);
 }
@@ -219,11 +219,11 @@ TeVector3f32 TeCamera::transformCoord(const TeVector3f32 &pt) {
 	return pt;
 }
 
-TeVector3f32 TeCamera::transformPoint2Dto3D(const TeVector2f32 &pt) {
+TeVector3f32 TeCamera::transformPoint2Dto3D(const TeVector3f32 &pt) {
 	TeVector3f32 retval;
 	TeVector3f32 vp_br(_viewportX + _viewportW, _viewportY + _viewportH, 0.0f);
-	float x = (pt.getX() - _viewportX) / (vp_br.x() - _viewportX);
-	float y = (pt.getY() - _viewportY) / (vp_br.y() - _viewportY);
+	float x = (pt.x() - _viewportX) / (vp_br.x() - _viewportX);
+	float y = (pt.y() - _viewportY) / (vp_br.y() - _viewportY);
 	return TeVector3f32(x * 2 - 1.0f, -(y * 2 - 1.0f), 0.0);
 }
 
diff --git a/engines/tetraedge/te/te_camera.h b/engines/tetraedge/te/te_camera.h
index 38dc8adfe29..8ab03c86b15 100644
--- a/engines/tetraedge/te/te_camera.h
+++ b/engines/tetraedge/te/te_camera.h
@@ -61,7 +61,7 @@ public:
 	static void restore();
 	TeMatrix4x4 transformationMatrix();
 	TeVector3f32 transformCoord(const TeVector3f32 &pt);
-	TeVector3f32 transformPoint2Dto3D(const TeVector2f32 &pt);
+	TeVector3f32 transformPoint2Dto3D(const TeVector3f32 &pt);
 	void updateProjectionMatrix();
 
 	void viewport(int x, int y, uint width, uint height);
diff --git a/engines/tetraedge/te/te_extended_text_layout.cpp b/engines/tetraedge/te/te_extended_text_layout.cpp
index d7e5025144c..d355b761fb8 100644
--- a/engines/tetraedge/te/te_extended_text_layout.cpp
+++ b/engines/tetraedge/te/te_extended_text_layout.cpp
@@ -35,6 +35,5 @@ void TeExtendedTextLayout::setAutoScrollSpeed(float val) {
 	_scrollingLayout.setAutoScrollAnimation2Speed(val);
 }
 
-// TODO: Add more functions here.
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 0713b9c4341..218974136e8 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -97,17 +97,26 @@ Common::Array<TeVector3f32> TeFreeMoveZone::collisions(const TeVector3f32 &v1, c
 	error("TODO: Implement TeFreeMoveZone::collisions");
 }
 
-TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool f) {
-
-	warning("TODO: Implement TeFreeMoveZone::correctCharacterPosition");
-	return pos;
+TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool intersectFlag) {
+	float f = 0.0;
+	TeVector3f32 intersectPoint;
+	if (!intersect(pos, TeVector3f32(0, -1, 0), intersectPoint, f, intersectFlag, nullptr)) {
+		if (!intersect(pos, TeVector3f32(0, 1, 0), intersectPoint, f, intersectFlag, nullptr)) {
+			if (*flagout)
+				*flagout = false;
+			return pos;
+		}
+	}
+	if (flagout)
+		*flagout = true;
+	return intersectPoint;
 }
 
 TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4, float param_5, bool findMeshFlag) {
 	error("TODO: Implement TeFreeMoveZone::curve");
 }
 
-TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4) {
+TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &param_3, const TeVector3f32 &param_4) {
 	error("TODO: Implement TeFreeMoveZone::curve");
 }
 
@@ -205,7 +214,7 @@ void TeFreeMoveZone::setCamera(TeIntrusivePtr<TeCamera> &cam, bool noRecalcProjP
 		_projectedPointsDirty = true;
 }
 
-void TeFreeMoveZone::setNbTriangles(unsigned long len) {
+void TeFreeMoveZone::setNbTriangles(unsigned int len) {
 	_freeMoveZoneVerticies.resize(len * 3);
 
 	_gridDirty = true;
@@ -219,7 +228,7 @@ void TeFreeMoveZone::setPathFindingOccluder(const TeOBP &occluder) {
 	error("TODO: Implement TeFreeMoveZone::setPathFindingOccluder");
 }
 
-void TeFreeMoveZone::setVertex(unsigned long offset, const TeVector3f32 &vertex) {
+void TeFreeMoveZone::setVertex(unsigned int offset, const TeVector3f32 &vertex) {
 	_freeMoveZoneVerticies[offset] = vertex;
 
 	_gridDirty = true;
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index aa4cbd9809a..74daafb8d23 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -72,7 +72,7 @@ public:
 	TeVector3f32 correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool f);
 
 	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4, float param_5, bool findMeshFlag);
-	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4);
+	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &param_3, const TeVector3f32 &param_4);
 
 	void draw() override;
 	TeVector3f32 findNearestPointOnBorder(const TeVector2f32 &pt);
@@ -94,9 +94,9 @@ public:
 	Common::Array<TeVector3f32> &removeInsignificantPoints(const Common::Array<TeVector3f32> &points);
 	void setBordersDistance(float dist);
 	void setCamera(TeIntrusivePtr<TeCamera> &cam, bool noRecalcProjPoints);
-	void setNbTriangles(unsigned long len);
+	void setNbTriangles(unsigned int len);
 	void setPathFindingOccluder(const TeOBP &occluder);
-	void setVertex(unsigned long offset, const TeVector3f32 &vertex);
+	void setVertex(unsigned int offset, const TeVector3f32 &vertex);
 	TeVector2s32 transformAStarGridInWorldSpace(const TeVector2s32 &gridpt);
 	float transformHeightMin(float minval);
 	TeVector3f32 transformVectorInWorldSpace(float param_3,float param_4);
diff --git a/engines/tetraedge/te/te_i_layout.h b/engines/tetraedge/te/te_i_layout.h
index 0819de8b316..cabbb55622a 100644
--- a/engines/tetraedge/te/te_i_layout.h
+++ b/engines/tetraedge/te/te_i_layout.h
@@ -45,9 +45,6 @@ public:
 		RATIO_MODE_PAN_SCAN
 	};
 
-private:
-	// TODO add private members
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_images_sequence.cpp b/engines/tetraedge/te/te_images_sequence.cpp
index 0af65ad6777..5b991656697 100644
--- a/engines/tetraedge/te/te_images_sequence.cpp
+++ b/engines/tetraedge/te/te_images_sequence.cpp
@@ -75,29 +75,30 @@ bool TeImagesSequence::load(const Common::Path &path) {
 			continue;
 		}
 
-		Common::File file;
-		if (!file.open(filePath)) {
+		Common::SeekableReadStream *stream = child.createReadStream();
+		if (!stream) {
 			warning("TeImagesSequence::load can't open %s", filePath.toString().c_str());
 			continue;
 		}
 
-		// At first only do the deep check for the first file to get dimensions.
-		if (_width) {
-			_files.push_back(filePath);
-			continue;
-		}
-
-		Image::PNGDecoder png;
-		if (!png.loadStream(file)) {
-			warning("Image sequence failed to load png %s", filePath.toString().c_str());
-			return false;
+		// Only do the deep check for the first file to get dimensions.
+		if (!_width) {
+			Image::PNGDecoder png;
+			if (!png.loadStream(*stream)) {
+				warning("Image sequence failed to load png %s", filePath.toString().c_str());
+				delete stream;
+				return false;
+			}
+
+			const Graphics::Surface *surf = png.getSurface();
+			assert(surf);
+			_width = surf->w;
+			_height = surf->h;
+			_frameRate = fps;
 		}
 
-		const Graphics::Surface *surf = png.getSurface();
-		assert(surf);
-		_width = surf->w;
-		_height = surf->h;
-		_frameRate = fps;
+		_files.push_back(child);
+		delete stream;
 	}
 
 	return true;
@@ -110,23 +111,25 @@ bool TeImagesSequence::update(unsigned long i, TeImage &imgout) {
 	if (i >= _files.size())
 		return false;
 
-	Common::File file;
-	if (file.open(_files[_curFrame]))
-		error("Open %s failed.. it was ok before?", _files[_curFrame].toString().c_str());
+	Common::SeekableReadStream *stream = _files[_curFrame].createReadStream();
+	if (!stream)
+		error("Open %s failed.. it was ok before?", _files[_curFrame].getName().c_str());
 
 	Image::PNGDecoder png;
-	if (png.loadStream(file)) {
-		warning("Image sequence failed to load png %s", _files[_curFrame].toString().c_str());
+	if (!png.loadStream(*stream)) {
+		warning("Image sequence failed to load png %s", _files[_curFrame].getName().c_str());
+		delete stream;
 		return false;
 	}
 
 	const Graphics::Surface *surf = png.getSurface();
 	assert(surf);
 
-	imgout.setAccessName(_files[_curFrame]);
+	imgout.setAccessName(_files[_curFrame].getPath());
 
 	if (imgout.w == surf->w && imgout.h == surf->h && imgout.format == surf->format) {
 		imgout.copyFrom(*surf);
+		delete stream;
 		return true;
 	}
 
diff --git a/engines/tetraedge/te/te_images_sequence.h b/engines/tetraedge/te/te_images_sequence.h
index 6559d8905cd..e8f4b386adb 100644
--- a/engines/tetraedge/te/te_images_sequence.h
+++ b/engines/tetraedge/te/te_images_sequence.h
@@ -62,7 +62,7 @@ private:
 	float _frameRate;
 	unsigned int _width;
 	unsigned int _height;
-	Common::Array<Common::Path> _files;
+	Common::Array<Common::FSNode> _files;
 	unsigned int _curFrame;
 };
 
diff --git a/engines/tetraedge/te/te_list_layout.h b/engines/tetraedge/te/te_list_layout.h
index 59e345a40f8..b631891c8b3 100644
--- a/engines/tetraedge/te/te_list_layout.h
+++ b/engines/tetraedge/te/te_list_layout.h
@@ -30,8 +30,6 @@ class TeListLayout : public TeLayout {
 public:
 	TeListLayout();
 
-	// TODO add public members
-
 	TeVector3f32 _minimumMargin;
 	TeVector3f32 _maximumMargin;
 	TeVector3f32 _direction;
diff --git a/engines/tetraedge/te/te_lua_context.cpp b/engines/tetraedge/te/te_lua_context.cpp
index 2baa54f76ea..f2df4bda2ba 100644
--- a/engines/tetraedge/te/te_lua_context.cpp
+++ b/engines/tetraedge/te/te_lua_context.cpp
@@ -75,7 +75,7 @@ TeVariant TeLuaContext::global(const Common::String &name) {
 		lua_settop(_luaState, -2);
 		return TeVariant(str);
 	}
-	warning("Unexpected type %d for global %s", type, name.c_str());
+	warning("TeLuaContext::global: Unexpected type %d for global %s", type, name.c_str());
 	return TeVariant();
 }
 
diff --git a/engines/tetraedge/te/te_lua_script.cpp b/engines/tetraedge/te/te_lua_script.cpp
index 58422d24195..c2568451de3 100644
--- a/engines/tetraedge/te/te_lua_script.cpp
+++ b/engines/tetraedge/te/te_lua_script.cpp
@@ -37,7 +37,7 @@ void TeLuaScript::attachToContext(TeLuaContext *context) {
 
 void TeLuaScript::execute() {
 	if (_luaContext) {
-		debug("TeLuaScript::execute %s", _scriptPath.toString().c_str());
+		//debug("TeLuaScript::execute %s", _scriptPath.toString().c_str());
 		lua_State *state = _luaContext->luaState();
 		if (state) {
 			TeLuaThread *thread = TeLuaThread::create(_luaContext);
@@ -50,7 +50,7 @@ void TeLuaScript::execute() {
 
 void TeLuaScript::execute(const Common::String &fname) {
 	if (_luaContext) {
-		debug("TeLuaScript::execute %s %s", _scriptPath.toString().c_str(), fname.c_str());
+		//debug("TeLuaScript::execute %s %s", _scriptPath.toString().c_str(), fname.c_str());
 		TeLuaThread *thread = TeLuaThread::create(_luaContext);
 		thread->execute(fname);
 		thread->release();
@@ -59,7 +59,7 @@ void TeLuaScript::execute(const Common::String &fname) {
 
 void TeLuaScript::execute(const Common::String &fname, const TeVariant &p1) {
 	if (_luaContext) {
-		debug("TeLuaScript::execute %s %s(%s)", _scriptPath.toString().c_str(), fname.c_str(), p1.toString().c_str());
+		//debug("TeLuaScript::execute %s %s(%s)", _scriptPath.toString().c_str(), fname.c_str(), p1.toString().c_str());
 		TeLuaThread *thread = TeLuaThread::create(_luaContext);
 		thread->execute(fname, p1);
 		thread->release();
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index c5a500332e5..2a7a0bfc8e6 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -33,7 +33,7 @@
 
 namespace Tetraedge {
 
-TeModel::TeModel() : _enableLights(false), _skipBoneMatricies(false), _matrixForced(false) {
+TeModel::TeModel() : _enableLights(false), _skipSkinOffsets(false), _matrixForced(false) {
 	// TODO: set 0x17c to 1.0
 	// TODO: set 0x178, 0x170 to 0
 	_modelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
@@ -45,27 +45,19 @@ TeModel::~TeModel() {
 	destroy();
 }
 
-void TeModel::create() {
-	// TODO: set field_0x158 to 0
-	_modelAnim.release();
-	_modelVertexAnim.release();
-	_matrixForced = false;
-	_skipBoneMatricies = false;
+void TeModel::blendAnim(TeIntrusivePtr<TeModelAnimation>& anim, float seconds, bool repeat) {
+	if (!_modelAnim) {
+		setAnim(anim, repeat);
+	} else {
+		BonesBlender *blender = new BonesBlender(anim, seconds);
+		anim->_repeatCount = (repeat ? -1 : 1);
+		anim->play();
+		_boneBlenders.push_back(blender);
+	}
 }
 
-void TeModel::destroy() {
-	_weightElements.clear();
-	// TODO: clear matrix array 0x148
-	_meshes.clear();
-	_bones.clear();
-	// TODO: clear matrix array 0x190
-	_boneMatrices.clear();
-	for (MeshBlender *blender : _meshBlenders)
-		delete blender;
-	_meshBlenders.clear();
-	for (BonesBlender *blender : _boneBlenders)
-		delete blender;
-	_boneBlenders.clear();
+void TeModel::blendMesh(const Common::String &s1, const Common::String &s2, float amount) {
+	_meshBlenders.push_back(new MeshBlender(s1, s2, amount, this));
 }
 
 int TeModel::checkFileType(Common::SeekableReadStream &instream) {
@@ -81,19 +73,27 @@ int TeModel::checkFileType(Common::SeekableReadStream &instream) {
 	return 0;
 }
 
-void TeModel::blendAnim(TeIntrusivePtr<TeModelAnimation>& anim, float seconds, bool repeat) {
-	if (!_modelAnim) {
-		setAnim(anim, repeat);
-	} else {
-		BonesBlender *blender = new BonesBlender(anim, seconds);
-		anim->_repeatCount = (repeat ? -1 : 1);
-		anim->play();
-		_boneBlenders.push_back(blender);
-	}
+void TeModel::create() {
+	// TODO: set field_0x158 to 0
+	_modelAnim.release();
+	_modelVertexAnim.release();
+	_matrixForced = false;
+	_skipSkinOffsets = false;
 }
 
-void TeModel::blendMesh(const Common::String &s1, const Common::String &s2, float amount) {
-	_meshBlenders.push_back(new MeshBlender(s1, s2, amount, this));
+void TeModel::destroy() {
+	_weightElements.clear();
+	// TODO: clear matrix array 0x148
+	_meshes.clear();
+	_bones.clear();
+	_boneMatricies.clear();
+	_skinOffsets.clear();
+	for (MeshBlender *blender : _meshBlenders)
+		delete blender;
+	_meshBlenders.clear();
+	for (BonesBlender *blender : _boneBlenders)
+		delete blender;
+	_boneBlenders.clear();
 }
 
 void TeModel::draw() {
@@ -104,16 +104,16 @@ void TeModel::draw() {
 		renderer->sendModelMatrix(transform);
 		renderer->pushMatrix();
 		renderer->multiplyMatrix(transform);
-		if (name() == "Kate") {
+		/*if (name() == "Kate") {
 			debug("Draw model %p (%s, %d meshes)", this, name().empty() ? "no name" : name().c_str(), _meshes.size());
-			debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
-			debug("   position   %s", position().dump().c_str());
+			//adebug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
+			//debug("   position   %s", position().dump().c_str());
 			debug("   worldPos   %s", worldPosition().dump().c_str());
-			debug("   scale      %s", scale().dump().c_str());
+			//debug("   scale      %s", scale().dump().c_str());
 			debug("   worldScale %s", worldScale().dump().c_str());
-			debug("   rotation   %s", rotation().dump().c_str());
+			//debug("   rotation   %s", rotation().dump().c_str());
 			debug("   worldRot   %s", worldRotation().dump().c_str());
-		}
+		}*/
 		for (TeMesh &mesh : _meshes) {
 			// TODO: Set some flag in the mesh here to this->field_0x158??
 			mesh.draw();
@@ -137,11 +137,18 @@ TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num)
 	return _bones[num]._trs;
 }
 
-void TeModel::setColor(const TeColor &col) {
-	Te3DObject2::setColor(col);
-	for (TeMesh &mesh : _meshes) {
-		mesh.setColor(col);
+TeMatrix4x4 TeModel::lerpElementsMatrix(unsigned long weightsNum, Common::Array<TeMatrix4x4> &matricies) {
+	TeMatrix4x4 retval;
+	for (unsigned int i = 0; i < 4; i++)
+		retval.setValue(i, i, 0);
+
+	// TODO: Finish this.
+	for (auto &weights : _weightElements) {
+	
 	}
+
+
+	return retval;
 }
 
 void TeModel::removeAnim() {
@@ -152,51 +159,102 @@ void TeModel::removeAnim() {
 	_modelAnim.release();
 }
 
-void TeModel::update() {
-	Common::Array<TeMatrix4x4> matricies;
-	matricies.resize(_bones.size());
-	for (unsigned int i = 0; i < _bones.size(); i++) {
-		const bone &b = _bones[i];
-		TeMatrix4x4 matrix = TeMatrix4x4::fromTRS(b._trs);
-		if (b._x == -1 || _bones.size() < 2) {
-			matricies[0] = matrix;
-		} else {
-			matricies[i] = matricies[b._x] * matrix;
-		}
+void TeModel::setColor(const TeColor &col) {
+	Te3DObject2::setColor(col);
+	for (TeMesh &mesh : _meshes) {
+		mesh.setColor(col);
 	}
-	for (unsigned int b = 0; b < _bones.size(); b++) {
-		TeTRS startTRS = getBone(_modelAnim, b);
-		for (unsigned int i = 0; i < _boneBlenders.size(); i++) {
-			BonesBlender *blender = _boneBlenders[i];
-			float complete = MIN((blender->_timer.getTimeFromStart() / 1000000.0f) / blender->_seconds, 1.0f);
-			TeTRS trs;
-			TeTRS endTRS = getBone(blender->_anim, b);
-			if (complete == 1.0f) {
-				delete blender;
-				_boneBlenders.remove_at(i);
-				trs = endTRS;
-				i--;
+}
+
+void TeModel::update() {
+	if (_bones.size()) {
+		Common::Array<TeMatrix4x4> matricies;
+		matricies.resize(_bones.size());
+		for (unsigned int i = 0; i < _bones.size(); i++) {
+			const bone &b = _bones[i];
+			TeMatrix4x4 matrix = TeMatrix4x4::fromTRS(b._trs);
+			if (b._x == -1 || _bones.size() < 2) {
+				matricies[0] = matrix;
 			} else {
-				trs = startTRS.lerp(endTRS, complete);
+				matricies[i] = matricies[b._x] * matrix;
+			}
+		}
+		
+		_boneMatricies.resize(_bones.size());
+		_lerpedElements.resize(_weightElements.size());
+
+		TeMatrix4x4 invertx;
+		invertx.scale(TeVector3f32(-1, 1, 1));
+		for (unsigned int b = 0; b < _bones.size(); b++) {
+			TeTRS trs = getBone(_modelAnim, b);
+			for (unsigned int i = 0; i < _boneBlenders.size(); i++) {
+				BonesBlender *blender = _boneBlenders[i];
+				float complete = MIN((blender->_timer.getTimeFromStart() / 1000000.0f) / blender->_seconds, 1.0f);
+				TeTRS trs;
+				TeTRS endTRS = getBone(blender->_anim, b);
+				if (complete == 1.0f) {
+					delete blender;
+					_boneBlenders.remove_at(i);
+					trs = endTRS;
+					i--;
+				} else {
+					trs = trs.lerp(endTRS, complete);
+				}
 			}
 			TeMatrix4x4 matrix;
 			if (!_matrixForced) {
 				matrix = TeMatrix4x4::fromTRS(trs);
 			} else {
-				matrix = _forcedMatrix;
+				matrix = invertx * _forcedMatrix;
 				_matrixForced = false;
 			}
+			if (_bones.size() < 2 || _bones[b]._x == -1) {
+				_boneMatricies[b] = matrix;
+				// TODO: Rotate by _field_0x170
+			} else {
+				_boneMatricies[b] = (invertx * _boneMatricies[_bones[b]._x]) * matrix;
+			}
+			_boneMatricies[b] = invertx * _boneMatricies[b];
+			// TODO: bonesUpdateSignal.call(_bones[b]._name, _boneMatricies[b]);
 		}
-		//warning("TODO: Finish TeModel::update. (disasm 295 ~ 693)");
-	}
-	for (TeMesh &mesh : _meshes) {
-		if (!_modelVertexAnim) {
-			mesh.update(nullptr, nullptr);
-		} else {
-			if (mesh.name() != _modelVertexAnim->head()) {
+		
+		if (!_skinOffsets.empty() && !_bones.empty()) {
+			for (unsigned int b = 0; b < _bones.size(); b++) {
+				_boneMatricies[b] = _boneMatricies[b] * _skinOffsets[b];
+			}
+		}
+		
+		if (_skipSkinOffsets == 0 && !_weightElements.empty()) {
+			for (unsigned int i = 0; i < _weightElements.size(); i++) {
+				_lerpedElements[i] = lerpElementsMatrix(i, _boneMatricies);
+			}
+		}
+		
+		for (unsigned int m = 0; m < _meshes.size(); m++) {
+			if (!_meshes[m].visible())
+				continue;
+			if (!_skipSkinOffsets && _bones.size() < 2 ) {
+				if (_meshes[m].name() == _modelVertexAnim->head()) {
+					_meshes[m].update(_modelVertexAnim);
+					// TODO: lines 440 - 443.. set some vals and goto LAB_doMeshBlends;
+				}
+				_meshes[m].update(&_boneMatricies, &_lerpedElements);
+				// TODO: Set some vals here..
+			} else {
+				//warning("TODO: Finish TeModel::update. (disasm 456 ~ 693)");
+			}
+		}
+	} else {
+		// No bones..
+		for (TeMesh &mesh : _meshes) {
+			if (!_modelVertexAnim) {
 				mesh.update(nullptr, nullptr);
 			} else {
-				mesh.update(_modelVertexAnim);
+				if (mesh.name() != _modelVertexAnim->head()) {
+					mesh.update(nullptr, nullptr);
+				} else {
+					mesh.update(_modelVertexAnim);
+				}
 			}
 		}
 	}
@@ -255,10 +313,10 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 	if (bonecount > 100000)
 		error("TeModel::load: Unexpected number of bones %d", bonecount);
 	_bones.resize(bonecount);
-	_boneMatrices.resize(bonecount);
+	_skinOffsets.resize(bonecount);
 
 	if (version == 13) {
-		_skipBoneMatricies = stream.readUint32LE();
+		_skipSkinOffsets = stream.readUint32LE();
 	}
 
 	if (!loadAndCheckFourCC(stream, "SKEL")) {
@@ -270,8 +328,8 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 		loadAlign(stream);
 		_bones[i]._x = stream.readUint32LE();
 		TeTRS::deserialize(stream, _bones[i]._trs);
-		if (!_skipBoneMatricies) {
-			_boneMatrices[i].deserialize(stream);
+		if (!_skipSkinOffsets) {
+			_skinOffsets[i].deserialize(stream);
 		}
 	}
 
@@ -474,7 +532,7 @@ void TeModel::setAnim(TeIntrusivePtr<TeModelAnimation> &anim, bool repeat) {
 		delete blender;
 	}
 	_boneBlenders.clear();
-	anim->_repeatCount = repeat ? -1 : 1;
+	anim->_repeatCount = (repeat ? -1 : 1);
 	_modelAnim = anim;
 }
 
@@ -492,9 +550,9 @@ void TeModel::setVisibleByName(const Common::String &name, bool vis) {
 }
 
 TeMatrix4x4 TeModel::skinOffset(unsigned long boneno) const {
-	if (boneno >= _boneMatrices.size())
+	if (boneno >= _skinOffsets.size())
 		return TeMatrix4x4();
-	return _boneMatrices[boneno];
+	return _skinOffsets[boneno];
 }
 
 TeModel::BonesBlender::BonesBlender(TeIntrusivePtr<TeModelAnimation> anim, float seconds) : _anim(anim), _seconds(seconds) {
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index bdf61c9173c..74ee90aafe9 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -93,11 +93,11 @@ public:
 
 	void draw() override;
 
-	int findModelBone(const Common::String &name);
+	int findModelBone(const Common::String &bname);
 	int findOrAddWeights(const Common::Array<weightElement> &weights);
 	void forceMatrix(const TeMatrix4x4 &matrix);
 	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num);
-	TeMatrix4x4 lerpElementsMatrix(unsigned long num, Common::Array<TeMatrix4x4> &matricies);
+	TeMatrix4x4 lerpElementsMatrix(unsigned long weightNum, Common::Array<TeMatrix4x4> &matricies);
 
 	/* Align the stream to the nearest 4 byte boudary*/
 	static void loadAlign(Common::SeekableReadStream &stream);
@@ -122,7 +122,7 @@ public:
 	virtual void setColor(const TeColor &col) override;
 	void setQuad(const TeIntrusivePtr<Te3DTexture> &tex, const Common::Array<TeVector3f32> &verts, const TeColor &col);
 	void setVertexAnim(TeIntrusivePtr<TeModelVertexAnimation> &anim, bool repeat);
-	void setVisibleByName(const Common::String &name, bool vis);
+	void setVisibleByName(const Common::String &mname, bool vis);
 
 	TeMatrix4x4 skinOffset(unsigned long boneno) const;
 
@@ -131,7 +131,7 @@ public:
 
 	Common::Path _texturePath;
 	bool _enableLights;
-	bool _skipBoneMatricies;
+	bool _skipSkinOffsets;
 
 	Common::Array<TeMesh> _meshes;
 
@@ -140,7 +140,9 @@ protected:
 	TeMatrix4x4 _forcedMatrix;
 	Common::Array<MeshBlender *> _meshBlenders;
 	Common::Array<bone> _bones;
-	Common::Array<TeMatrix4x4> _boneMatrices;
+	Common::Array<TeMatrix4x4> _skinOffsets;
+	Common::Array<TeMatrix4x4> _boneMatricies;
+	Common::Array<TeMatrix4x4> _lerpedElements;
 	Common::Array<Common::Array<weightElement>> _weightElements;
 	Common::Array<BonesBlender *> _boneBlenders;
 
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index 98a0062be70..956e470e39a 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -76,9 +76,9 @@ void TeModelAnimation::destroy() {
 	_fbxArrays.clear();
 }
 
-int TeModelAnimation::findBone(const Common::String &name) {
+int TeModelAnimation::findBone(const Common::String &bname) {
 	for (unsigned int i = 0; i < _boneNames.size(); i++) {
-		if (_boneNames[i] == name)
+		if (_boneNames[i] == bname)
 			return i;
 	}
 	return -1;
@@ -90,7 +90,7 @@ int TeModelAnimation::firstFrame() const {
 	return _firstFrame;
 }
 
-//TeMatrix4x4 TeModelAnimation::getMatrix(const Common::String &name, unsigned long frame, bool param_5);
+//TeMatrix4x4 TeModelAnimation::getMatrix(const Common::String &mname, unsigned long frame, bool param_5);
 
 TeQuaternion TeModelAnimation::getNMORotation(unsigned long boneNo, float amount) const {
 	if (boneNo < _nmoRotArrays.size()) {
@@ -273,11 +273,11 @@ void TeModelAnimation::resizeNMOArrays(unsigned long len) {
 
 //void TeModelAnimation::saveBone(Common::SeekableWriteStream &stream, uint param_2);
 
-void TeModelAnimation::setBoneName(uint boneNo, const Common::String &name) {
+void TeModelAnimation::setBoneName(uint boneNo, const Common::String &bname) {
 	if (_boneNames.size() < boneNo + 1) {
 		_boneNames.resize(boneNo + 1);
 	}
-	_boneNames[boneNo] = name;
+	_boneNames[boneNo] = bname;
 }
 
 void TeModelAnimation::setRotation(unsigned long num, float amount, const TeQuaternion &rot) {
diff --git a/engines/tetraedge/te/te_model_animation.h b/engines/tetraedge/te/te_model_animation.h
index 8765b7ab840..fb30cc66bd6 100644
--- a/engines/tetraedge/te/te_model_animation.h
+++ b/engines/tetraedge/te/te_model_animation.h
@@ -60,7 +60,7 @@ public:
 	int calcCurrentFrame(double millis);
 	void cont() override;
 	void destroy();
-	int findBone(const Common::String &name);
+	int findBone(const Common::String &bname);
 	int firstFrame() const;
 	TeMatrix4x4 getMatrix(const Common::String &name, unsigned long frame, bool param_5);
 	TeQuaternion getNMORotation(unsigned long param_3, float param_4) const;
@@ -76,7 +76,7 @@ public:
 	void resizeNMOArrays(unsigned long len);
 	void save(Common::SeekableWriteStream &stream);
 	void saveBone(Common::SeekableWriteStream &stream, uint param_2);
-	void setBoneName(uint boneNo, const Common::String &name);
+	void setBoneName(uint boneNo, const Common::String &bname);
 	void setFrameLimits(int framemin, int framemax) {
 		_firstFrame = framemin;
 		_lastFrame = framemax;
@@ -105,7 +105,7 @@ private:
 	bool _curFrameValFresh;
 	int _repeatNum;
 	bool _finishedSignalPending;
-	int _useNMOArrays; // TODO: probably a bad name?
+	int _useNMOArrays;
 	int _numNMOFrames;
 	float _speed;
 
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 09a019ec17b..32cd8e53f81 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -19,11 +19,14 @@
  *
  */
 
+#include "common/util.h"
+
 #include "tetraedge/tetraedge.h"
 
 #include "tetraedge/te/te_mesh.h"
 #include "tetraedge/te/te_pick_mesh2.h"
 #include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_ray_intersection.h"
 
 namespace Tetraedge {
 
@@ -55,6 +58,54 @@ void TePickMesh2::draw() {
 	renderer->setCurrentColor(prevCol);
 }
 
+bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 v2, TeVector3f32 &v3, float &fout, bool lastHitFirst, unsigned long *triangleHitOut) {
+	if (_verticies.size() / 3 == 0)
+		return false;
+
+	TeVector3f32 intersection;
+	float f;
+	const TeMatrix4x4 worldTrans = worldTransformationMatrix();
+	if (lastHitFirst) {
+		const TeVector3f32 triv1 = worldTrans * _verticies[_lastTriangleHit * 3 + 0];
+		const TeVector3f32 triv2 = worldTrans * _verticies[_lastTriangleHit * 3 + 1];
+		const TeVector3f32 triv3 = worldTrans * _verticies[_lastTriangleHit * 3 + 2];
+		int result = TeRayIntersection::intersect(v1, v2, triv1, triv2, triv3, intersection, f);
+		if (result == 1 && f >= 0.0 && f < FLT_MAX) {
+			v3 = v1 + v2 * f;
+			fout = f;
+			if (triangleHitOut)
+				*triangleHitOut = _lastTriangleHit;
+			return true;
+		}
+	}
+	
+	float hitf = FLT_MAX;
+	for (unsigned int i = 0; i < _verticies.size() / 3; i++) {
+		const TeVector3f32 triv1 = worldTrans * _verticies[i * 3 + 0];
+		const TeVector3f32 triv2 = worldTrans * _verticies[i * 3 + 1];
+		const TeVector3f32 triv3 = worldTrans * _verticies[i * 3 + 2];
+		int result = TeRayIntersection::intersect(v1, v2, triv1, triv2, triv3, intersection, f);
+		if (result == 1 && f >= 0.0 && f < FLT_MAX) {
+			_lastTriangleHit = i;
+			hitf = f;
+			if (lastHitFirst)
+				break;
+		}
+	}
+	if (hitf != FLT_MAX) {
+		v3 = v1 + v2 * hitf;
+		fout = hitf;
+		if (triangleHitOut)
+			*triangleHitOut = _lastTriangleHit;
+		return true;
+	}
+	return false;
+}
+
+bool TePickMesh2::intersect2D(const TeVector2f32 &pt) {
+	error("TODO: Implement TePickMesh2::intersect2D");
+}
+
 unsigned long TePickMesh2::lastTriangleHit() const {
 	if (_lastTriangleHit < _verticies.size() / 3)
 		return _lastTriangleHit;
@@ -85,12 +136,12 @@ bool TePickMesh2::pointInTriangle(const TeVector2f32 &p1, const TeVector2f32 &p2
 	return f1 != f2;
 }
 
-void TePickMesh2::setNbTriangles(unsigned long num) {
+void TePickMesh2::setNbTriangles(unsigned int num) {
 	_verticies.resize(num * 3);
 	_lastTriangleHit = 0;
 }
 
-void TePickMesh2::setTriangle(unsigned long num, const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3) {
+void TePickMesh2::setTriangle(unsigned int num, const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3) {
 	assert(num <= _verticies.size() / 3);
 	_verticies[num * 3 + 0] = v1;
 	_verticies[num * 3 + 1] = v2;
diff --git a/engines/tetraedge/te/te_pick_mesh2.h b/engines/tetraedge/te/te_pick_mesh2.h
index 817b518b384..fd823128a8a 100644
--- a/engines/tetraedge/te/te_pick_mesh2.h
+++ b/engines/tetraedge/te/te_pick_mesh2.h
@@ -35,17 +35,17 @@ public:
 
 	void draw() override;
 
-	// bool intersect(TeVector3f32 const&, TeVector3f32 const&, TeVector3f32&, float&, bool flag, unsigned long *triangleHitOut);
+	bool intersect(const TeVector3f32 &v1, const TeVector3f32 v2, TeVector3f32 &v3, float &fout, bool useLastHit, unsigned long *triangleHitOut);
 	bool intersect2D(const TeVector2f32 &pt);
 	unsigned long lastTriangleHit() const;
 
 	bool pointInTriangle(const TeVector2f32 &p1, const TeVector2f32 &p2, const TeVector2f32 &p3, const TeVector2f32 &p4) const;
 
-	void setLastTriangleHit(unsigned long lastHit) { _lastTriangleHit = lastHit; }
-	void setNbTriangles(unsigned long num);
+	void setLastTriangleHit(unsigned int lastHit) { _lastTriangleHit = lastHit; }
+	void setNbTriangles(unsigned int num);
 
-	void setTriangle(unsigned long num, const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3);
-	void triangle(unsigned long num, TeVector3f32 &v1out, TeVector3f32 &v2out, TeVector3f32 &v3out) const;
+	void setTriangle(unsigned int num, const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3);
+	void triangle(unsigned int num, TeVector3f32 &v1out, TeVector3f32 &v2out, TeVector3f32 &v3out) const;
 
 	static void serialize(Common::WriteStream &stream, const TePickMesh2 &mesh);
 	static void deserialize(Common::ReadStream &stream, TePickMesh2 &mesh);
@@ -55,7 +55,7 @@ public:
 
 private:
 	Common::Array<TeVector3f32> _verticies;
-	unsigned long _lastTriangleHit;
+	unsigned int _lastTriangleHit;
 
 };
 
diff --git a/engines/tetraedge/te/te_scene.cpp b/engines/tetraedge/te/te_scene.cpp
index bdb826d9fc0..7fd23f0b239 100644
--- a/engines/tetraedge/te/te_scene.cpp
+++ b/engines/tetraedge/te/te_scene.cpp
@@ -31,17 +31,17 @@ void TeScene::close() {
 	_models.clear();
 }
 
-TeIntrusivePtr<TeCamera> TeScene::camera(const Common::String &name) {
+TeIntrusivePtr<TeCamera> TeScene::camera(const Common::String &cname) {
 	for (auto &c : _cameras) {
-		if (c->name() == name)
+		if (c->name() == cname)
 			return c;
 	}
 	return TeIntrusivePtr<TeCamera>();
 }
 
-TeIntrusivePtr<TeModel> TeScene::model(const Common::String &name) {
+TeIntrusivePtr<TeModel> TeScene::model(const Common::String &mname) {
 	for (auto &m : _models) {
-		if (m->name() == name)
+		if (m->name() == mname)
 			return m;
 	}
 	return TeIntrusivePtr<TeModel>();
@@ -72,32 +72,32 @@ void TeScene::draw() {
 	TeCamera::restore();
 }
 
-void TeScene::removeModel(const Common::String &name) {
+void TeScene::removeModel(const Common::String &mname) {
 	uint n = _models.size();
 	for (uint i = 0; i < n; i++) {
-		if (_models[i]->name() == name) {
+		if (_models[i]->name() == mname) {
 			_models.remove_at(i);
 			break;
 		}
 	}
 }
 
-void TeScene::setCurrentCamera(const Common::String &name) {
+void TeScene::setCurrentCamera(const Common::String &cname) {
 	uint n = _cameras.size();
 	uint i = 0;
 	for (; i < n; i++) {
-		if (_cameras[i]->name() == name) {
+		if (_cameras[i]->name() == cname) {
 			break;
 		}
 	}
 	if (i == n) {
-		debug("TeScene::setCurrentCamera: Couldn't find camera %s", name.c_str());
+		debug("TeScene::setCurrentCamera: Couldn't find camera %s", cname.c_str());
 		return;
 	}
 	_currentCameraIndex = i;
 	TeCamera *c = _cameras[i].get();
 	assert(c);
-	debug("TeScene::setCurrentCamera: Set %s", c->name().c_str());
+	// debug("TeScene::setCurrentCamera: Set %s", c->name().c_str());
 }
 
 void TeScene::update() {
diff --git a/engines/tetraedge/te/te_scene.h b/engines/tetraedge/te/te_scene.h
index 0bdf12059d6..8a5174ae7f8 100644
--- a/engines/tetraedge/te/te_scene.h
+++ b/engines/tetraedge/te/te_scene.h
@@ -39,8 +39,8 @@ public:
 
 	virtual void close();
 
-	TeIntrusivePtr<TeCamera> camera(const Common::String &name);
-	TeIntrusivePtr<TeModel> model(const Common::String &name);
+	TeIntrusivePtr<TeCamera> camera(const Common::String &cname);
+	TeIntrusivePtr<TeModel> model(const Common::String &mname);
 
 	TeIntrusivePtr<TeCamera> currentCamera();
 	int currentCameraIndex() const { return _currentCameraIndex; }
@@ -49,8 +49,8 @@ public:
 	virtual void draw();
 	virtual bool load(const Common::Path &path) { return false; };
 
-	void removeModel(const Common::String &name);
-	void setCurrentCamera(const Common::String &name);
+	void removeModel(const Common::String &mname);
+	void setCurrentCamera(const Common::String &cname);
 	void setCurrentCameraIndex(uint index) {
 		_currentCameraIndex = index;
 	}
diff --git a/engines/tetraedge/te/te_scrolling_layout.h b/engines/tetraedge/te/te_scrolling_layout.h
index d4eb519f43e..25d36471d89 100644
--- a/engines/tetraedge/te/te_scrolling_layout.h
+++ b/engines/tetraedge/te/te_scrolling_layout.h
@@ -80,8 +80,6 @@ public:
 	}
 	void setContentLayout(TeLayout *layout);
 
-	// TODO add public members
-
 private:
 	int _inertiaAnimationDuration;
 	Common::Array<float> _inertiaAnimationCurve;
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index 3725be41c5a..c299df4e293 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -281,6 +281,4 @@ void TeTiledSurface::updateVideoProperties() {
 	}
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_tiled_texture.cpp b/engines/tetraedge/te/te_tiled_texture.cpp
index b6aeb9d9c78..6ae69f9dcc3 100644
--- a/engines/tetraedge/te/te_tiled_texture.cpp
+++ b/engines/tetraedge/te/te_tiled_texture.cpp
@@ -179,7 +179,4 @@ void TeTiledTexture::update(const TeImage &image) {
 	//}
 }
 
-
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge


Commit: 710a1b9f1b4ec945ee399db9c1a7672fa1795c73
    https://github.com/scummvm/scummvm/commit/710a1b9f1b4ec945ee399db9c1a7672fa1795c73
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More WIP. Idle animations work, character rotation and position now right.

z-order and objects need work.

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/document.cpp
    engines/tetraedge/game/document.h
    engines/tetraedge/game/documents_browser.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/object3d.h
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_act_zone.h
    engines/tetraedge/te/te_bezier_curve.cpp
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_interpolation.cpp
    engines/tetraedge/te/te_layout.cpp
    engines/tetraedge/te/te_lua_thread.cpp
    engines/tetraedge/te/te_matricies_stack.cpp
    engines/tetraedge/te/te_matrix4x4.cpp
    engines/tetraedge/te/te_matrix4x4.h
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_mesh.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_quaternion.h
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer.h
    engines/tetraedge/te/te_signal.h
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_trs.cpp
    engines/tetraedge/te/te_vector3f32.cpp


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 62aaff289d5..1ffe18054ab 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -24,7 +24,9 @@
 #include "common/file.h"
 #include "common/debug.h"
 
+#include "tetraedge/tetraedge.h"
 #include "tetraedge/game/character.h"
+#include "tetraedge/game/game.h"
 #include "tetraedge/game/character_settings_xml_parser.h"
 #include "tetraedge/te/te_model_animation.h"
 
@@ -62,6 +64,29 @@ _freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(n
 	_curModelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
 }
 
+Character::~Character() {
+	_model->setVisible(false);
+	_model->bonesUpdatedSignal().remove(this, &Character::onBonesUpdate);
+	deleteAnim();
+	Game *game = g_engine->getGame();
+	Common::Array<TeIntrusivePtr<TeModel>> &models = game->scene().models();
+	for (unsigned int i = 0; i < models.size(); i++) {
+		if (models[i] == _model) {
+			models.remove_at(i);
+			break;
+		}
+	}
+	removeAnim();
+	for (unsigned int s = 0; s < 2; s++) {
+		for (unsigned int i = 0; i < models.size(); i++) {
+			if (models[i] == _shadowModel[s]) {
+				models.remove_at(i);
+				break;
+			}
+		}
+	}
+}
+
 void Character::addCallback(const Common::String &key, const Common::String &s2, float f1, float f2) {
 	/*Callback *c = new Callback();
 	c->x = (int)f1;
@@ -89,7 +114,9 @@ void Character::addCallback(const Common::String &key, const Common::String &s2,
 		return _cache.getVal(pathStr);
 
 	TeIntrusivePtr<TeModelAnimation> modelAnim = new TeModelAnimation();
-	modelAnim->load(path);
+	if (!modelAnim->load(path)) {
+		warning("Failed to load anim %s", path.toString().c_str());
+	}
 
 	_cache.setVal(pathStr, modelAnim);
 	return modelAnim;
@@ -138,8 +165,10 @@ bool Character::blendAnimation(const Common::String &animname, float amount, boo
 			|| animname.contains(walkAnim(WalkPart_EndG))
 			|| animname.contains(walkAnim(WalkPart_EndD)));
 
-	if (_curModelAnim)
+	if (_curModelAnim) {
 		_curModelAnim->onFinished().remove(this, &Character::onModelAnimationFinished);
+		_curModelAnim->unbind();
+	}
 
 	_curModelAnim = animCacheLoad(animpath);
 	_curModelAnim->onFinished().add(this, &Character::onModelAnimationFinished);
@@ -238,11 +267,10 @@ int Character::rightStepFrame(enum Character::WalkPart walkpart) {
 bool Character::loadModel(const Common::String &mname, bool unused) {
 	assert(_globalCharacterSettings);
 	if (_model) {
-		//TODO
-		//_model->bonesUpdateSignal().remove(this, &Character::onBonesUpdate);
+		_model->bonesUpdatedSignal().remove(this, &Character::onBonesUpdate);
 	}
 	_model = new TeModel();
-	//_model->bonesUpdateSignal().add(this, &Character::onBonesUpdate);
+	_model->bonesUpdatedSignal().add(this, &Character::onBonesUpdate);
 
 	if (!_globalCharacterSettings->contains(mname))
 		return false;
@@ -339,9 +367,100 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 	return false;
 }
 
-bool Character::onBonesUpdate(const Common::String &boneName, const TeMatrix4x4 &param_2) {
-	error("TODO: Implement Character::onBonesUpdate");
-	return false;
+bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneMatrix) {
+	if (!_model || !_model->anim())
+		return false;
+
+	Game *game = g_engine->getGame();
+	if (boneName == "Pere") {
+		const Common::String animfile = _model->anim()->_loadedPath.getLastComponent().toString();
+		bool resetX = false;
+		if (game->scene()._character == this) {
+			for (const auto &walkSettings : _characterSettings._walkSettings) {
+				resetX |= (walkSettings._key.contains("Walk") || walkSettings._key.contains("Jog"));
+				resetX |= (walkSettings._value._walkParts[0]._file == animfile ||
+						walkSettings._value._walkParts[1]._file == animfile ||
+						walkSettings._value._walkParts[2]._file == animfile ||
+						walkSettings._value._walkParts[3]._file == animfile);
+			}
+			resetX |= animfile.contains(_characterSettings._walkFileName);
+		} else {
+			resetX = (animfile.contains(_characterSettings._walkFileName) ||
+					  animfile.contains(walkAnim(WalkPart_Start)) ||
+					  animfile.contains(walkAnim(WalkPart_Loop)) ||
+					  animfile.contains(walkAnim(WalkPart_EndD)) ||
+					  animfile.contains(walkAnim(WalkPart_EndG)));
+		}
+		if (resetX) {
+			boneMatrix.setValue(0, 3, 0.0f);
+			boneMatrix.setValue(2, 3, 0.0f);
+		}
+	}
+
+	if (boneName.contains("Bip01 Head")) {
+		if (_hasAnchor) {
+			game->scene().currentCamera()->apply();
+			_lastHeadRotation = _headRotation;
+			TeQuaternion rot1 = TeQuaternion::fromAxisAndAngle(TeVector3f32(-1, 0, 0), _lastHeadRotation.getX());
+			TeQuaternion rot2 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 0, 1), _lastHeadRotation.getY());
+			boneMatrix.rotate(rot1);
+			boneMatrix.rotate(rot2);
+		} else {
+			float lastHeadX = _lastHeadRotation.getX();
+			float minX = (lastHeadX > 0) ? -0.1 : 0.1;
+			float newX = (fabs(minX) > fabs(lastHeadX)) ? 0.0 : minX + lastHeadX;
+			_lastHeadRotation.setX(newX);
+
+			float lastHeadY = _lastHeadRotation.getY();
+			float minY = (lastHeadY > 0) ? -0.1 : 0.1;
+			float newY = (fabs(minY) > fabs(lastHeadY)) ? 0.0 : minY + lastHeadY;
+			_lastHeadRotation.setY(newY);
+
+			_headRotation.setX(_lastHeadRotation.getX());
+			_headRotation.setY(_lastHeadRotation.getY());
+
+			TeQuaternion rot1 = TeQuaternion::fromAxisAndAngle(TeVector3f32(-1, 0, 0), _lastHeadRotation.getX());
+			TeQuaternion rot2 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 0, 1), _lastHeadRotation.getY());
+			boneMatrix.rotate(rot1);
+			boneMatrix.rotate(rot2);
+			_lastHeadBoneTrans = boneMatrix.translation();
+		}
+	}
+
+	if (boneName.contains("Bip01 L Foot") || boneName.contains("Bip01 R Foot")) {
+		TeVector3f32 trans = boneMatrix.translation();
+		trans.rotate(_model->rotation());
+		const TeVector3f32 modelScale = _model->scale();
+		trans.x() *= modelScale.x();
+		trans.y() = 0.0;
+		trans.z() *= modelScale.z();
+		TeVector3f32 pos = _model->position() + trans;
+		if (_freeMoveZone) {
+			bool flag;
+			pos = _freeMoveZone->correctCharacterPosition(pos, &flag, true);
+		}
+		_shadowModel[1]->setPosition(pos);
+		_shadowModel[1]->setRotation(_model->rotation());
+		_shadowModel[1]->setScale(_model->scale());
+	}
+
+	// Move any objects attached to the bone
+	for (Object3D *obj : game->scene().object3Ds()) {
+		if (obj->_onCharName == _model->name() && boneName == obj->_onCharBone) {
+			obj->model()->setVisible(true);
+			TeMatrix4x4 objmatrix = boneMatrix;
+			objmatrix.scale(obj->_objScale);
+			objmatrix.rotate(obj->_objRotation);
+			objmatrix.scale(obj->_objScale);
+			objmatrix.translate(obj->_objTranslation);
+			obj->model()->forceMatrix(objmatrix);
+			obj->model()->setPosition(_model->position());
+			obj->model()->setRotation(_model->rotation());
+			obj->model()->setScale(_model->scale());
+		}
+	}
+
+	return true;
 }
 
 bool Character::onModelAnimationFinished() {
@@ -361,6 +480,7 @@ void Character::placeOnCurve(TeIntrusivePtr<TeBezierCurve> &curve) {
 void Character::removeAnim() {
 	if (_curModelAnim) {
 		_curModelAnim->onFinished().remove(this, &Character::onModelAnimationFinished);
+		_curModelAnim->unbind();
 	}
 	_model->removeAnim();
 	if (_curModelAnim) {
@@ -378,12 +498,12 @@ bool Character::setAnimation(const Common::String &aname, bool repeat, bool para
 
 	Common::Path animPath("models/Anims");
 	animPath.joinInPlace(aname);
-	bool validAnim = (aname.contains(_characterSettings._walkFileName) ||
+	bool isWalkAnim = (aname.contains(_characterSettings._walkFileName) ||
 					  aname.contains(walkAnim(WalkPart_Start)) ||
 					  aname.contains(walkAnim(WalkPart_Loop)) ||
 					  aname.contains(walkAnim(WalkPart_EndD)) ||
 					  aname.contains(walkAnim(WalkPart_EndG)));
-	_missingCurrentAnim = !validAnim;
+	_missingCurrentAnim = !isWalkAnim;
 
 	if (_curModelAnim) {
 		_curModelAnim->onFinished().remove(this, &Character::onModelAnimationFinished);
@@ -451,6 +571,10 @@ TeTRS Character::trsFromAnim(const TeModelAnimation &anim, long bone, long frame
 }
 
 void Character::update(double percentval) {
+	if (!_curve || !_runTimer.running())
+		return;
+	//float speed = speedFromAnim(percentval);
+
 	error("TODO: Implement Character::update");
 }
 
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index d9c005f2de4..5d3ea7c85b1 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -40,7 +40,7 @@ namespace Tetraedge {
 class Character : public TeAnimation {
 public:
 	Character();
-	virtual ~Character() {}
+	virtual ~Character();
 
 	struct AnimSettings {
 		AnimSettings() : _stepLeft(0), _stepRight(0) {};
@@ -117,7 +117,7 @@ public:
 	bool loadModel(const Common::String &name, bool param_2);
 	static bool loadSettings(const Common::String &path);
 
-	bool onBonesUpdate(const Common::String &boneName, const TeMatrix4x4 &param_2);
+	bool onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneMatrix);
 	bool onModelAnimationFinished();
 	void permanentUpdate();
 	void placeOnCurve(TeIntrusivePtr<TeBezierCurve> &curve);
@@ -137,7 +137,7 @@ public:
 	float translationFromAnim(const TeModelAnimation &anim, long bone, long frame);
 	TeVector3f32 translationVectorFromAnim(const TeModelAnimation &anim, long bone, long frame);
 	TeTRS trsFromAnim(const TeModelAnimation &anim, long bone, long frame);
-	void update(double percentval);
+	void update(double percentval) override;
 	void updateAnimFrame();
 	void updatePosition(float curveOffset);
 	Common::String walkAnim(WalkPart part);
@@ -171,7 +171,6 @@ public:
 	Character *charLookingAt() { return _charLookingAt; }
 	bool lookingAtTallThing() const { return _lookingAtTallThing; }
 	void setLookingAtTallThing(bool val) { _lookingAtTallThing = val; }
-	
 
 private:
 	float _curveOffset;
@@ -209,10 +208,11 @@ private:
 	bool _needsSomeUpdate;
 	bool _positionFlag;
 	bool _lookingAtTallThing;
-	
 	bool _hasAnchor;
+
 	TeVector2f32 _headRotation;
 	TeVector2f32 _lastHeadRotation;
+	TeVector3f32 _lastHeadBoneTrans;
 
 	TeVector3f32 _positionCharacter;
 
diff --git a/engines/tetraedge/game/document.cpp b/engines/tetraedge/game/document.cpp
index be6ccd15604..e31e7b29a44 100644
--- a/engines/tetraedge/game/document.cpp
+++ b/engines/tetraedge/game/document.cpp
@@ -23,9 +23,26 @@
 
 namespace Tetraedge {
 
-Document::Document() {
+Document::Document(DocumentsBrowser *browser) : _browser(browser) {
+
+}
+
+void Document::load(const Common::String &name) {
+	error("TODO: Implement Document::load");
+}
+
+void Document::unload() {
+	removeChild(_gui.layoutChecked("object"));
+	_gui.unload();
+}
+
+bool Document::onButtonDown() {
+	_onButtonDownSignal.call(*this);
+	return false;
 }
 
-// TODO: Add more functions here.
+Common::Path Document::spritePath() const {
+	return Common::Path("DocumentsBrowser/Documents").join(name()).append(".png");
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/document.h b/engines/tetraedge/game/document.h
index 9c6bbeb8a65..33647fe574c 100644
--- a/engines/tetraedge/game/document.h
+++ b/engines/tetraedge/game/document.h
@@ -24,28 +24,37 @@
 
 #include "common/str.h"
 
+#include "tetraedge/te/te_layout.h"
+#include "tetraedge/te/te_lua_gui.h"
+
 namespace Tetraedge {
 
-class Document {
+class DocumentsBrowser;
+
+class Document : public TeLayout {
 public:
-	Document();
+	Document(DocumentsBrowser *browser);
 	~Document() {
 		unload();
-		// TODO: check for other destructor things.
+		if (parent()) {
+			// TODO: do something with parent here.
+		}
 	}
 
 	void load(const Common::String &name);
 	//void loadFromBackup(TiXmlElement &node) {
 	// load(node->Attribute("id");
 	//}
+
 	bool onButtonDown();
 	//void saveToBackup(TiXmlElement &node);
-	Common::String spritePath() const;
+	Common::Path spritePath() const;
 	void unload();
 
 private:
-	// TODO add private members
-
+	DocumentsBrowser *_browser;
+	TeLuaGUI _gui;
+	TeSignal1Param<Document &> _onButtonDownSignal;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
index e9f29b577a2..1e37a00fad6 100644
--- a/engines/tetraedge/game/documents_browser.cpp
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -89,7 +89,13 @@ void DocumentsBrowser::currentPage(long page) {
 }
 
 bool DocumentsBrowser::onQuitDocumentDoubleClickTimer() {
-	error("TODO: Implement DocumentsBrowser::onQuitDocumentDoubleClickTimer");
+	long time = _timer.getTimeFromStart();
+	_timer.stop();
+	if (time >= 200000)
+		error("TODO: Implement DocumentsBrowser::onQuitDocumentDoubleClickTimer");
+	else
+		hideDocument();
+	return false;
 }
 
 bool DocumentsBrowser::onNextPage() {
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 4c646a33a67..b57fbeef487 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -270,7 +270,7 @@ void Game::enter(bool newgame) {
 	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, -1000.0f));
 	g_engine->getInputMgr()->_mouseLUpSignal.insert(callbackptr);
 	_movePlayerCharacterDisabled = false;
-	warning("TODO: Game::enter set some other fields here");
+	warning("TODO: Game::enter set some other field here");
 	_sceneCharacterVisibleFromLoad = false;
 	Character::loadSettings("models/ModelsSettings.xml");
 	Object3D::loadSettings("objects/ObjectsSettings.xml");
@@ -1163,7 +1163,7 @@ void Game::playMovie(const Common::String &vidPath, const Common::String &musicP
 	music.play();
 	videoSpriteLayout->play();
 
-	// FIXME TODO!! Stop the movie and soundearly for testing.
+	// Stop the movie and sound early for testing if skip_videos set
 	if (ConfMan.get("skip_videos") == "true") {
 		videoSpriteLayout->_tiledSurfacePtr->_frameAnim._nbFrames = 10;
 		music.stop();
@@ -1243,7 +1243,7 @@ void Game::playSound(const Common::String &name, int repeats, float volume) {
 				return;
 			}
 		}
-		
+
 		GameSound *sound = new GameSound();
 		sound->setChannelName("sfx");
 		sound->load(name);
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index d95807a978c..081bd5ec52b 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -256,7 +256,7 @@ private:
 	bool _markersVisible;
 	bool _saveRequested;
 	bool _randomSoundFinished;
-	
+
 	Common::RandomSource _randomSource;
 	RandomSound *_randomSound;
 	TeTimer _randomSoundTimer;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 2fb70cdbdda..705fbe78243 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -301,9 +301,13 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 	uint32 indexcount = stream.readUint32LE();
 	uint32 vertexcount = stream.readUint32LE();
 
+	if (indexcount > 100000 || vertexcount > 100000)
+		error("InGameScene::deserializeModel: Unxpected counts %d %d", indexcount, vertexcount);
+
 	mesh.setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
 	for (unsigned int i = 0; i < indexcount; i++)
 		mesh.setIndex(i, stream.readUint32LE());
+
 	for (unsigned int i = 0; i < vertexcount; i++) {
 		TeVector3f32::deserialize(stream, vec);
 		mesh.setVertex(i, vec);
@@ -527,7 +531,7 @@ bool InGameScene::load(const Common::Path &path) {
 	if (!Common::File::exists(path))
 		return false;
 
-	TeScene::close();
+	close();
 	_loadedPath = path;
 	Common::File scenefile;
 	if (!scenefile.open(path))
@@ -563,7 +567,7 @@ bool InGameScene::load(const Common::Path &path) {
 			delete pickmesh;
 			if (modelname.substr(0, 2) != "ZB") {
 				if (objname.empty()) {
-					warning("[InGameScene::load] Unknown type of object named : %s", modelname.c_str());
+					debug("[InGameScene::load] Unknown type of object named : %s", modelname.c_str());
 				} else {
 					InGameScene::Object obj;
 					obj._name = objname;
@@ -620,12 +624,6 @@ bool InGameScene::load(const Common::Path &path) {
 	_charactersShadow->create(this);
 	onMainWindowSizeChanged();
 
-	// FIXME: For some reason the game never re-adds kate's model to the list here..
-	// how does it ever get drawn???
-	if (!findKate()) {
-		models().push_back(_character->_model);
-	}
-
 	return true;
 }
 
@@ -755,6 +753,8 @@ void InGameScene::loadBlockers() {
 		blockersfile.seek(0);
 
 	uint32 nblockers = blockersfile.readUint32LE();
+	if (nblockers > 1024)
+		error("Improbable number of blockers %d", nblockers);
 	_blockers.resize(nblockers);
 	for (unsigned int i = 0; i < nblockers; i++) {
 		_blockers[i]._s = Te3DObject2::deserializeString(blockersfile);
@@ -765,6 +765,8 @@ void InGameScene::loadBlockers() {
 
 	if (hasHeader) {
 		uint32 nrectblockers = blockersfile.readUint32LE();
+		if (nrectblockers > 1024)
+			error("Improbable number of rectblockers %d", nrectblockers);
 		_rectBlockers.resize(nrectblockers);
 		for (unsigned int i = 0; i < nrectblockers; i++) {
 			_rectBlockers[i]._s = Te3DObject2::deserializeString(blockersfile);
@@ -1034,14 +1036,11 @@ void InGameScene::update() {
 		}
 	}
 
-	// TODO: some other stuff with callbacks and spritelayouts here
-	for (auto &callback : _callbacks) {
-	
-	}
-
 	TeLuaGUI::StringMap<TeSpriteLayout *> &sprites = bgGui().spriteLayouts();
 	for (auto &sprite : sprites) {
-	
+		if (_callbacks.contains(sprite._key)) {
+			error("TODO: handle sprite callback in InGameScene::update");
+		}
 	}
 
 	TeScene::update();
@@ -1065,10 +1064,10 @@ void InGameScene::update() {
 	for (Object3D *obj : _object3Ds) {
 		// TODO: update object3ds if they are translating or rotating.
 		if (obj->_translateTime >= 0) {
-		
+			error("TODO: handle _translateTime > 0 in InGameScene::update");
 		}
 		if (obj->_rotateTime >= 0) {
-		
+			error("TODO: handle _rotateTime > 0 in InGameScene::update");
 		}
 	}
 }
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 18e72dd540a..eee8bc31785 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -53,7 +53,7 @@ public:
 		Common::String _name;
 		TeSpriteLayout *_layout;
 	};
-	
+
 	struct Callback {
 		float _f;
 		Common::String _name;
@@ -193,6 +193,7 @@ public:
 	void setCurve(TeIntrusivePtr<TeBezierCurve> &c) { c = _curve; }
 	Common::Array<TeIntrusivePtr<TeModel>> &zoneModels() { return _zoneModels; }
 	Common::Array<TeRectBlocker> &rectBlockers() { return _rectBlockers; }
+	Common::Array<Object3D *> object3Ds() { return _object3Ds; }
 
 private:
 	TeColor _shadowColor;
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index bb888e0a407..5b6573564a7 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -270,7 +270,7 @@ bool Inventory::onZoomed() {
 void Inventory::pauseAnims() {
 	Game *game = g_engine->getGame();
 	if (game->scene()._character) {
-		
+
 	}
 	error("TODO: implement Inventory::pauseAnims");
 }
@@ -278,7 +278,7 @@ void Inventory::pauseAnims() {
 void Inventory::unPauseAnims() {
 	Game *game = g_engine->getGame();
 	if (game->scene()._character) {
-		
+
 	}
 	error("TODO: implement Inventory::unPauseAnims");
 }
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index e39d09f19ef..4a927e8fdf1 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -25,6 +25,7 @@
 #include "tetraedge/game/character.h"
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/lua_binds.h"
+#include "tetraedge/game/object3d.h"
 #include "tetraedge/to_lua.h"
 
 namespace Tetraedge {
@@ -41,7 +42,7 @@ static void PlayMovie(const Common::String &vidpath, const Common::String &music
 }
 
 static int tolua_ExportedFunctions_PlayMovie00(lua_State *L) {
-tolua_Error err;
+	tolua_Error err;
 	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		Common::String s2(tolua_tostring(L, 2, nullptr));
@@ -220,7 +221,7 @@ static void SetVisibleMarker(const Common::String &markerName, bool val) {
 }
 
 static int tolua_ExportedFunctions_SetVisibleMarker00(lua_State *L) {
-tolua_Error err;
+	tolua_Error err;
 	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
 		Common::String s(tolua_tostring(L, 1, nullptr));
 		bool b = tolua_toboolean(L, 2, 0);
@@ -369,9 +370,9 @@ static int tolua_ExportedFunctions_UnloadObject00(lua_State *L) {
 	error("#ferror in function 'UnloadObject': %d %d %s", err.index, err.array, err.type);
 }
 
-static void SetCharacterRotation(const Common::String &charname, float f1, float f2, float f3) {
+static void SetCharacterRotation(const Common::String &charname, float rx, float ry, float rz) {
 	// TODO: check if this is good.
-	TeQuaternion quat = TeQuaternion::fromEuler(TeVector3f32(f1 * M_PI / 180.0, f2 * M_PI / 180.0, f3 * M_PI / 180.0));
+	TeQuaternion quat = TeQuaternion::fromEuler(TeVector3f32(rx * M_PI / 180.0, ry * M_PI / 180.0, rz * M_PI / 180.0));
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charname);
 	if (c) {
@@ -389,7 +390,7 @@ static int tolua_ExportedFunctions_SetCharacterRotation00(lua_State *L) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		float f1 = tolua_tonumber(L, 2, 0.0);
 		float f2 = tolua_tonumber(L, 3, 0.0);
-		float f3 = tolua_tonumber(L, 4, 1.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
 		SetCharacterRotation(s1, f1, f2, f3);
 		return 0;
 	}
@@ -409,7 +410,7 @@ static int tolua_ExportedFunctions_SetCharacterPosition00(lua_State *L) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		Common::String s2(tolua_tostring(L, 2, nullptr));
 		float f1 = tolua_tonumber(L, 3, 0.0);
-		float f2 = tolua_tonumber(L, 4, 1.0);
+		float f2 = tolua_tonumber(L, 4, 0.0);
 		float f3 = tolua_tonumber(L, 5, 0.0);
 		SetCharacterPosition(s1, s2, f1, f2, f3);
 		return 0;
@@ -450,7 +451,7 @@ static void SetGroundObjectRotation(const Common::String &objname, float x, floa
 		warning("[SetGroundObjectRotation] Object not found %s", objname.c_str());
 		return;
 	}
-	
+
 	TeVector3f32 rotvec(x * M_PI / 180.0, y * M_PI / 180.0, z * M_PI / 180.0);
 	obj->model()->setRotation(TeQuaternion::fromEuler(rotvec));
 	obj->model()->setVisible(true);
@@ -675,6 +676,119 @@ static int tolua_ExportedFunctions_PlayMusic00(lua_State *L) {
 	error("#ferror in function 'PlayMusic': %d %d %s", err.index, err.array, err.type);
 }
 
+static void SetObjectOnCharacter(const Common::String &obj, const Common::String &charName, const Common::String &boneName) {
+	Game *game = g_engine->getGame();
+	Object3D *obj3d = game->scene().object3D(obj);
+	if (!obj3d)
+		warning("[SetObjectOnCharacter] Object not found %s", obj.c_str());
+
+	obj3d->_onCharName = charName;
+	obj3d->_onCharBone = boneName;
+}
+
+static int tolua_ExportedFunctions_SetObjectOnCharacter00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isstring(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));
+		Common::String s3(tolua_tostring(L, 3, nullptr));
+		SetObjectOnCharacter(s1, s2, s3);
+		return 0;
+	}
+	error("#ferror in function 'SetObjectOnCharacter': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetObjectRotation(const Common::String &obj, float xr, float yr, float zr) {
+	Game *game = g_engine->getGame();
+	Object3D *obj3d = game->scene().object3D(obj);
+	if (!obj3d)
+		warning("[SetObjectRotation] Object not found %s", obj.c_str());
+	const TeVector3f32 rot(xr * M_PI / 180.0, yr * M_PI / 180.0, zr * M_PI / 180.0);
+	obj3d->_objRotation = TeQuaternion::fromEuler(rot);
+}
+
+static int tolua_ExportedFunctions_SetObjectRotation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
+		SetObjectRotation(s1, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetObjectRotation': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetObjectTranslation(const Common::String &obj, float x, float y, float z) {
+	Game *game = g_engine->getGame();
+	Object3D *obj3d = game->scene().object3D(obj);
+	if (!obj3d)
+		warning("[SetObjectTranslation] Object not found %s", obj.c_str());
+	obj3d->_objTranslation = TeVector3f32(x, y, z);
+}
+
+static int tolua_ExportedFunctions_SetObjectTranslation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
+		SetObjectTranslation(s1, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetObjectTranslation': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetObjectScale(const Common::String &obj, float xs, float ys, float zs) {
+	Game *game = g_engine->getGame();
+	Object3D *obj3d = game->scene().object3D(obj);
+	if (!obj3d)
+		warning("[SetObjectScale] Object not found %s", obj.c_str());
+	obj3d->_objScale = TeVector3f32(xs, ys, zs);
+}
+
+static int tolua_ExportedFunctions_SetObjectScale00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
+		SetObjectScale(s1, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetObjectScale': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetObjectFrames(const Common::String &obj, int start, int end) {
+	Game *game = g_engine->getGame();
+	Object3D *obj3d = game->scene().object3D(obj);
+	if (!obj3d)
+		warning("[SetObjectFrames] Object not found %s", obj.c_str());
+	obj3d->_startFrame = start;
+	obj3d->_endFrame = end;
+}
+
+static int tolua_ExportedFunctions_SetObjectFrames00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		SetObjectFrames(s1, (int)f1, (int)f2);
+		return 0;
+	}
+	error("#ferror in function 'SetObjectFrames': %d %d %s", err.index, err.array, err.type);
+}
 
 // ////////////////////////////////////////////////////////////////////////
 
@@ -689,10 +803,10 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);*/
 	tolua_function(L, "HideObject", tolua_ExportedFunctions_HideObject00);
 	tolua_function(L, "ShowObject", tolua_ExportedFunctions_ShowObject00);
-	/*tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00);*/
-	tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);/*
-	tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00);
-	tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00);*/
+	// tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00); // Never used
+	tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);
+	//tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00); // Never used
+	//tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00); // Never used
 	tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
 	tolua_function(L, "PlayMovie", tolua_ExportedFunctions_PlayMovie00);
 	/*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);
@@ -775,12 +889,12 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00);
 	tolua_function(L, "DeleteCallback", tolua_ExportedFunctions_DeleteCallback00);
 	tolua_function(L, "DeleteCallbackPlayer", tolua_ExportedFunctions_DeleteCallbackPlayer00);
-	tolua_function(L, "DeleteCallbackAnimation2D", tolua_ExportedFunctions_DeleteCallbackAnimation2D00);
+	tolua_function(L, "DeleteCallbackAnimation2D", tolua_ExportedFunctions_DeleteCallbackAnimation2D00);*/
 	tolua_function(L, "SetObjectOnCharacter", tolua_ExportedFunctions_SetObjectOnCharacter00);
 	tolua_function(L, "SetObjectRotation", tolua_ExportedFunctions_SetObjectRotation00);
 	tolua_function(L, "SetObjectTranslation", tolua_ExportedFunctions_SetObjectTranslation00);
 	tolua_function(L, "SetObjectScale", tolua_ExportedFunctions_SetObjectScale00);
-	tolua_function(L, "SetObjectFrames", tolua_ExportedFunctions_SetObjectFrames00);*/
+	tolua_function(L, "SetObjectFrames", tolua_ExportedFunctions_SetObjectFrames00);
 	tolua_function(L, "LoadObject", tolua_ExportedFunctions_LoadObject00);
 	tolua_function(L, "UnloadObject", tolua_ExportedFunctions_UnloadObject00);
 	tolua_function(L, "SetGroundObjectPosition", tolua_ExportedFunctions_SetGroundObjectPosition00);
@@ -811,7 +925,7 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "BFGReportEventWithValue", tolua_ExportedFunctions_BFGReportEventWithValue00);
 	tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00);
 	tolua_function(L, "TestFileFlagSystemFlag", tolua_ExportedFunctions_TestFileFlagSystemFlag00);
-	tolua_function(L, "PrintDebugMessage", tolua_ExportedFunctions_PrintDebugMessage00);
+	// tolua_function(L, "PrintDebugMessage", tolua_ExportedFunctions_PrintDebugMessage00); // Unused
 	tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);*/
 	tolua_function(L, "EnableRectBlocker", tolua_ExportedFunctions_EnableRectBlocker00);
 	/*tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);*/
@@ -821,9 +935,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
 	tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
 	tolua_function(L, "SetCharacterMeshVisible", tolua_ExportedFunctions_SetCharacterMeshVisible00);
-	tolua_function(L, "SetRecallageY", tolua_ExportedFunctions_SetRecallageY00);
-	tolua_function(L, "IsFreemiumUnlocked", tolua_ExportedFunctions_IsFreemiumUnlocked00);
-	tolua_function(L, "ReachedFreemiumLimit", tolua_ExportedFunctions_ReachedFreemiumLimit00);*/
+	tolua_function(L, "SetRecallageY", tolua_ExportedFunctions_SetRecallageY00);*/
+	// tolua_function(L, "IsFreemiumUnlocked", tolua_ExportedFunctions_IsFreemiumUnlocked00); // Unused
+	// tolua_function(L, "ReachedFreemiumLimit", tolua_ExportedFunctions_ReachedFreemiumLimit00); // Unused
 	tolua_function(L, "AddUnrecalAnim", tolua_ExportedFunctions_AddUnrecalAnim00);
 	tolua_function(L, "UnlockArtwork", tolua_ExportedFunctions_UnlockArtwork00);
 
diff --git a/engines/tetraedge/game/object3d.h b/engines/tetraedge/game/object3d.h
index b1f1e9a1e0a..ccdffbe02b4 100644
--- a/engines/tetraedge/game/object3d.h
+++ b/engines/tetraedge/game/object3d.h
@@ -53,12 +53,23 @@ public:
 	TeTimer _rotateTimer;
 	TeVector3f32 _rotateStart;
 	TeVector3f32 _rotateAmount;
-	
+
 	float _translateTime;
 	TeTimer _translateTimer;
 	TeVector3f32 _translateStart;
 	TeVector3f32 _translateAmount;
 
+	Common::String _onCharName;
+	Common::String _onCharBone;
+
+	// TRS relative to the character this object is "on"
+	TeVector3f32 _objTranslation;
+	TeQuaternion _objRotation;
+	TeVector3f32 _objScale;
+
+	int _startFrame;
+	int _endFrame;
+
 private:
 	static Common::HashMap<Common::String, ObjectSettings> *_objectSettings;
 
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index 8c1ecb8cad0..c4ac06bbb11 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -158,8 +158,7 @@ void Te3DObject2::removeChildren() {
 }
 
 void Te3DObject2::rotate(const TeQuaternion &rot) {
-	TeQuaternion newRot = rotation();
-	newRot *= rot;
+	const TeQuaternion newRot = rotation() * rot;
 	setRotation(newRot);
 }
 
@@ -243,7 +242,7 @@ void Te3DObject2::setZPosition(float zpos) {
 TeMatrix4x4 Te3DObject2::transformationMatrix() {
 	TeMatrix4x4 retval;
 	retval.translate(position());
-	retval = retval * rotation().toMatrix();
+	retval.rotate(rotation());
 	retval.scale(scale());
 	return retval;
 }
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index ca8f6ae00f3..ac1353e59ec 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -148,7 +148,7 @@ bool Te3DTexture::load(const TeImage &img) {
 	_height = img.h;
 	_format = img._format;
 
-	// FIXME: set some other fields from the image here.
+	// TODO? set some other fields from the image here.
 	// for now just set some good defaults.
 	_flipY = true;    //img._flipY;
 	_leftBorder = 0;  //img._leftBorder;
diff --git a/engines/tetraedge/te/te_act_zone.h b/engines/tetraedge/te/te_act_zone.h
index ef4ecc34911..c3b1e427618 100644
--- a/engines/tetraedge/te/te_act_zone.h
+++ b/engines/tetraedge/te/te_act_zone.h
@@ -38,7 +38,6 @@ public:
 	bool flag2;
 
 private:
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/te/te_bezier_curve.cpp b/engines/tetraedge/te/te_bezier_curve.cpp
index d30beb5191c..2985bcb6ee6 100644
--- a/engines/tetraedge/te/te_bezier_curve.cpp
+++ b/engines/tetraedge/te/te_bezier_curve.cpp
@@ -105,6 +105,8 @@ void TeBezierCurve::deserialize(Common::ReadStream &stream, TeBezierCurve &curve
 	curve._lengthNeedsUpdate = false;
 	curve._length = stream.readFloatLE();
 	uint32 npoints = stream.readUint32LE();
+	if (npoints > 1000000)
+		error("TeBezierCurve::deserialize improbable number of control ponts %d", npoints);
 
 	for (unsigned int i = 0; i < npoints; i++) {
 		TeVector3f32 vec;
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index 65817da15fa..12ca0e7dcb5 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -152,7 +152,7 @@ void TeCamera::getRay(const TeVector2s32 &pxloc, TeVector3f32 &out1, TeVector3f3
 	TeQuaternion rot = rotation();
 	out1 = pos;
 	rot.normalize();
-	TeMatrix4x4 rotmatrix = rot.toMatrix();
+	TeMatrix4x4 rotmatrix = rot.toTeMatrix();
 	out2 = rotmatrix * out2;
 }
 
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 218974136e8..53d0779ba9d 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -317,7 +317,7 @@ void TeFreeMoveZoneGraph::deserialize(Common::ReadStream &stream) {
 	TeVector2s32::deserialize(stream, _size);
 	uint32 flaglen = stream.readUint32LE();
 	if (flaglen > 1000000 || (int)flaglen != _size._x * _size._y)
-		error("Flags unexpected size, expect %d got %d", _size._x * _size._y, flaglen);
+		error("TeFreeMoveZoneGraph: Flags unexpected size, expect %d got %d", _size._x * _size._y, flaglen);
 	_flags.resize(flaglen);
 	for (unsigned int i = 0; i < flaglen; i++) {
 		_flags[i] = stream.readByte();
diff --git a/engines/tetraedge/te/te_interpolation.cpp b/engines/tetraedge/te/te_interpolation.cpp
index 2f6402c53e8..11a82fac83c 100644
--- a/engines/tetraedge/te/te_interpolation.cpp
+++ b/engines/tetraedge/te/te_interpolation.cpp
@@ -31,7 +31,7 @@ TeInterpolation::TeInterpolation() {
 void TeInterpolation::load(Common::ReadStream &stream) {
 	uint32 len = stream.readUint32LE();
 	if (len > 1000000)
-		error("Unexpected interpolation length");
+		error("TeInterpolation: Unexpected interpolation length %d", len);
 	_array.resize(len);
 	for (uint32 i = 0; i < len && !stream.err(); i++)
 		_array[i] = stream.readFloatLE();
diff --git a/engines/tetraedge/te/te_layout.cpp b/engines/tetraedge/te/te_layout.cpp
index 02072ef0511..387e4f56256 100644
--- a/engines/tetraedge/te/te_layout.cpp
+++ b/engines/tetraedge/te/te_layout.cpp
@@ -406,13 +406,12 @@ void TeLayout::updateSize() {
 		}
 	}
 
-	// FIXME: Original doesn't call this here, but I seem to need it.
-	updateMesh();
-
 	_updatingSize = false;
 	// TODO: check this, is it the right flag to set?
 	_positionChanged = true;
 
+	updateMesh();
+
 	if (_size != oldSize) {
 		onSizeChanged().call();
 	}
diff --git a/engines/tetraedge/te/te_lua_thread.cpp b/engines/tetraedge/te/te_lua_thread.cpp
index a21f3ffda7d..928a4d8eb01 100644
--- a/engines/tetraedge/te/te_lua_thread.cpp
+++ b/engines/tetraedge/te/te_lua_thread.cpp
@@ -60,9 +60,8 @@ void TeLuaThread::_resume(int nargs) {
 		const char *msg = lua_tolstring(_luaThread, -1, nullptr);
 		warning("TeLuaThread::_resume: %s", msg);
 	}
-	// TODO: This seems suspicous... but it's what the original does.
 	if (_lastResumeResult != 1 && _released) {
-		warning("TeLuaThread:: deleting this??");
+		warning("TeLuaThread:: deleting this.");
 		delete this;
 	}
 }
diff --git a/engines/tetraedge/te/te_matricies_stack.cpp b/engines/tetraedge/te/te_matricies_stack.cpp
index 89405b873cc..8ab5e133796 100644
--- a/engines/tetraedge/te/te_matricies_stack.cpp
+++ b/engines/tetraedge/te/te_matricies_stack.cpp
@@ -57,11 +57,11 @@ void TeMatriciesStack::pushMatrix() {
 }
 
 void TeMatriciesStack::rotate(const TeQuaternion &rot) {
-	_stack.top() = _stack.top() * rot.toMatrix();
+	_stack.top() = _stack.top() * rot.toTeMatrix();
 }
 
 void TeMatriciesStack::rotate(float angle, const TeVector3f32 &axis) {
-	_stack.top() = _stack.top() * TeQuaternion::fromAxisAndAngle(axis, angle).toMatrix();
+	rotate(TeQuaternion::fromAxisAndAngle(axis, angle));
 }
 
 void TeMatriciesStack::scale(const TeVector3f32 &scale) {
diff --git a/engines/tetraedge/te/te_matrix4x4.cpp b/engines/tetraedge/te/te_matrix4x4.cpp
index 1f54fbec634..a26a2fad065 100644
--- a/engines/tetraedge/te/te_matrix4x4.cpp
+++ b/engines/tetraedge/te/te_matrix4x4.cpp
@@ -97,6 +97,15 @@ void TeMatrix4x4::translate(const TeVector3f32 &vec) {
 	*this = (*this * translMatrix);
 }
 
+void TeMatrix4x4::rotate(const TeQuaternion &rot) {
+	const TeMatrix4x4 rotMatrix = rot.toTeMatrix();
+	*this = (*this * rotMatrix);
+}
+
+TeVector3f32 TeMatrix4x4::translation() const {
+	return TeVector3f32(_data[12], _data[13], _data[14]);
+}
+
 TeVector3f32 TeMatrix4x4::mult4x3(const TeVector3f32 &vec) const {
 	const float f1 = vec.x();
 	const float f2 = vec.y();
@@ -324,21 +333,19 @@ TeMatrix4x4 TeMatrix4x4::fromTRS(const TeTRS &trs) {
 	TeMatrix4x4 result;
 	const TeVector3f32 trans = trs.getTranslation();
 	TeMatrix4x4 transm;
-	float *tm = transm.getData();
-	tm[12] = trans.x();
-	tm[13] = trans.y();
-	tm[14] = trans.z();
+	transm(0, 3) = trans.x();
+	transm(1, 3) = trans.y();
+	transm(2, 3) = trans.z();
 	result = result * transm;
 
-	const TeMatrix4x4 rotm = trs.getRotation().toMatrix();
+	const TeMatrix4x4 rotm = trs.getRotation().toTeMatrix();
 	result = result * rotm;
 
 	const TeVector3f32 scle = trs.getScale();
 	TeMatrix4x4 scalem;
-	float *sm = scalem.getData();
-	sm[0] = scle.x();
-	sm[5] = scle.y();
-	sm[10] = scle.z();
+	scalem(0, 0) = scle.x();
+	scalem(1, 1) = scle.y();
+	scalem(2, 2) = scle.z();
 	result = result * scalem;
 
 	return result;
diff --git a/engines/tetraedge/te/te_matrix4x4.h b/engines/tetraedge/te/te_matrix4x4.h
index 68159bac5bd..6d9d4599973 100644
--- a/engines/tetraedge/te/te_matrix4x4.h
+++ b/engines/tetraedge/te/te_matrix4x4.h
@@ -59,6 +59,8 @@ public:
 
 	void scale(const TeVector3f32 &vec);
 	void translate(const TeVector3f32 &vec);
+	void rotate(const TeQuaternion &rot);
+	TeVector3f32 translation() const;
 	TeVector3f32 mult3x3(const TeVector3f32 &vec) const;
 	TeVector3f32 mult4x3(const TeVector3f32 &vec) const;
 
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index 2c18fd9f4f8..d4aa310c98d 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -83,15 +83,16 @@ void TeMesh::draw() {
 				renderer->popMatrix();
 				return;
 			}
-		} else if (!_materials.empty()) {
+		} else {
+			assert(_faceCounts.size() == _materials.size());
+			int totalFaceCount = 0;
 			for (unsigned int i = 0; i < _faceCounts.size(); i++) {
-				int totalFaceCount = 0;
-				if (_faceCounts[i]) {
-					if (hasAlpha(i)) {
-						renderer->addTransparentMesh(*this, totalFaceCount, _faceCounts[i], i);
-					}
-					totalFaceCount += _faceCounts[i];
+				if (!_faceCounts[i])
+					continue;
+				if (hasAlpha(i)) {
+					renderer->addTransparentMesh(*this, totalFaceCount, _faceCounts[i], i);
 				}
+				totalFaceCount += _faceCounts[i];
 			}
 		}
 	}
@@ -136,17 +137,18 @@ void TeMesh::draw() {
 			renderer->disableTexture();
 		}
 	} else {
-		int totalfacecount = 0;
+		int totalFaceCount = 0;
+		assert(_faceCounts.size() == _materials.size());
 		for (unsigned int i = 0; i < _materials.size(); i++) {
 			if (!_faceCounts[i])
 				continue;
 			if (!hasAlpha(i) || renderer->shadowMode() == TeRenderer::ShadowMode1 || !_shouldDraw) {
 				_materials[i].apply();
-				glDrawElements(_glMeshMode, _faceCounts[i] * 3, GL_UNSIGNED_SHORT, _indexes.data() + totalfacecount * 3);
+				glDrawElements(_glMeshMode, _faceCounts[i] * 3, GL_UNSIGNED_SHORT, _indexes.data() + totalFaceCount * 3);
 				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 				renderer->disableTexture();
 			}
-			totalfacecount += _faceCounts[i];
+			totalFaceCount += _faceCounts[i];
 		}
 	}
 
@@ -179,7 +181,7 @@ void TeMesh::draw() {
 }
 
 TeMesh::Mode TeMesh::getMode() const {
-	// Do the reverse translation of setMode... why? I dunno.. the game does that..
+	// Do the reverse translation of setConf... why? I dunno.. the game does that..
 	switch(_glMeshMode) {
 	case GL_POINTS:
 		return MeshMode_Points;
@@ -365,7 +367,7 @@ void TeMesh::updateTo(const Common::Array<TeMatrix4x4> *matricies1, const Common
 				Common::Array<TeVector3f32> &verts, Common::Array<TeVector3f32> &normals) {
 	static const TeMatrix4x4 emptyMatrix;
 	for (unsigned int i = 0; i < _verticies.size(); i++) {
-		unsigned long m = _matricies[i];
+		unsigned int m = _matricies[i];
 		const TeMatrix4x4 *mat;
 		if (m < matricies1->size()) {
 			mat = &((*matricies1)[m]);
diff --git a/engines/tetraedge/te/te_mesh.h b/engines/tetraedge/te/te_mesh.h
index 9e3fa5612b9..fb0a9aa6df1 100644
--- a/engines/tetraedge/te/te_mesh.h
+++ b/engines/tetraedge/te/te_mesh.h
@@ -115,6 +115,11 @@ public:
 	void setHasAlpha(bool val) { _hasAlpha = val; }
 
 	Common::Array<TeMaterial> &materials() { return _materials; }
+	void setUpdatedVertex(unsigned int idx, const TeVector3f32 &val) { _updatedVerticies[idx] = val; }
+	void setUpdatedNormal(unsigned int idx, const TeVector3f32 &val) { _updatedNormals[idx] = val; }
+
+	const TeVector3f32 &preUpdatedVertex(unsigned int idx) const { return _verticies[idx]; }
+	const TeVector3f32 &preUpdatedNormal(unsigned int idx) const { return _normals[idx]; }
 
 private:
 	Common::Array<unsigned char> _materialIndexes;
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 2a7a0bfc8e6..5e5290e066f 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -34,8 +34,6 @@
 namespace Tetraedge {
 
 TeModel::TeModel() : _enableLights(false), _skipSkinOffsets(false), _matrixForced(false) {
-	// TODO: set 0x17c to 1.0
-	// TODO: set 0x178, 0x170 to 0
 	_modelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
 	_modelVertexAnim.setDeleteFn(&TeModelVertexAnimation::deleteLater);
 	create();
@@ -45,7 +43,7 @@ TeModel::~TeModel() {
 	destroy();
 }
 
-void TeModel::blendAnim(TeIntrusivePtr<TeModelAnimation>& anim, float seconds, bool repeat) {
+void TeModel::blendAnim(TeIntrusivePtr<TeModelAnimation> &anim, float seconds, bool repeat) {
 	if (!_modelAnim) {
 		setAnim(anim, repeat);
 	} else {
@@ -83,7 +81,7 @@ void TeModel::create() {
 
 void TeModel::destroy() {
 	_weightElements.clear();
-	// TODO: clear matrix array 0x148
+	_lerpedElements.clear();
 	_meshes.clear();
 	_bones.clear();
 	_boneMatricies.clear();
@@ -104,18 +102,18 @@ void TeModel::draw() {
 		renderer->sendModelMatrix(transform);
 		renderer->pushMatrix();
 		renderer->multiplyMatrix(transform);
-		/*if (name() == "Kate") {
+		if (name() == "Kate") {
 			debug("Draw model %p (%s, %d meshes)", this, name().empty() ? "no name" : name().c_str(), _meshes.size());
 			//adebug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
 			//debug("   position   %s", position().dump().c_str());
 			debug("   worldPos   %s", worldPosition().dump().c_str());
 			//debug("   scale      %s", scale().dump().c_str());
-			debug("   worldScale %s", worldScale().dump().c_str());
+			//debug("   worldScale %s", worldScale().dump().c_str());
 			//debug("   rotation   %s", rotation().dump().c_str());
 			debug("   worldRot   %s", worldRotation().dump().c_str());
-		}*/
+		}
 		for (TeMesh &mesh : _meshes) {
-			// TODO: Set some flag in the mesh here to this->field_0x158??
+			// TODO: Set some flag (_drawWires?) in mesh to this->field_0x158??
 			mesh.draw();
 		}
 		renderer->popMatrix();
@@ -137,17 +135,18 @@ TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num)
 	return _bones[num]._trs;
 }
 
-TeMatrix4x4 TeModel::lerpElementsMatrix(unsigned long weightsNum, Common::Array<TeMatrix4x4> &matricies) {
+TeMatrix4x4 TeModel::lerpElementsMatrix(unsigned int weightsNum, const Common::Array<TeMatrix4x4> &matricies) {
 	TeMatrix4x4 retval;
+	// Start with a 0 matrix.
 	for (unsigned int i = 0; i < 4; i++)
 		retval.setValue(i, i, 0);
 
-	// TODO: Finish this.
-	for (auto &weights : _weightElements) {
-	
+	const Common::Array<weightElement> &weights = _weightElements[weightsNum];
+	for (const auto &weight : weights) {
+		const TeMatrix4x4 offset = matricies[weight._x].meshScale(weight._weight);
+		retval.meshAdd(offset);
 	}
 
-
 	return retval;
 }
 
@@ -172,14 +171,14 @@ void TeModel::update() {
 		matricies.resize(_bones.size());
 		for (unsigned int i = 0; i < _bones.size(); i++) {
 			const bone &b = _bones[i];
-			TeMatrix4x4 matrix = TeMatrix4x4::fromTRS(b._trs);
+			const TeMatrix4x4 matrix = TeMatrix4x4::fromTRS(b._trs);
 			if (b._x == -1 || _bones.size() < 2) {
 				matricies[0] = matrix;
 			} else {
 				matricies[i] = matricies[b._x] * matrix;
 			}
 		}
-		
+
 		_boneMatricies.resize(_bones.size());
 		_lerpedElements.resize(_weightElements.size());
 
@@ -210,38 +209,83 @@ void TeModel::update() {
 			}
 			if (_bones.size() < 2 || _bones[b]._x == -1) {
 				_boneMatricies[b] = matrix;
-				// TODO: Rotate by _field_0x170
+				_boneMatricies[b].rotate(_boneRotation);
 			} else {
 				_boneMatricies[b] = (invertx * _boneMatricies[_bones[b]._x]) * matrix;
 			}
 			_boneMatricies[b] = invertx * _boneMatricies[b];
-			// TODO: bonesUpdateSignal.call(_bones[b]._name, _boneMatricies[b]);
+			_bonesUpdatedSignal.call(_bones[b]._name, _boneMatricies[b]);
 		}
-		
+
 		if (!_skinOffsets.empty() && !_bones.empty()) {
 			for (unsigned int b = 0; b < _bones.size(); b++) {
 				_boneMatricies[b] = _boneMatricies[b] * _skinOffsets[b];
 			}
 		}
-		
-		if (_skipSkinOffsets == 0 && !_weightElements.empty()) {
+
+		if (!_skipSkinOffsets && !_weightElements.empty()) {
 			for (unsigned int i = 0; i < _weightElements.size(); i++) {
 				_lerpedElements[i] = lerpElementsMatrix(i, _boneMatricies);
 			}
 		}
-		
+
 		for (unsigned int m = 0; m < _meshes.size(); m++) {
-			if (!_meshes[m].visible())
+			TeMesh &mesh = _meshes[m];
+			if (!mesh.visible())
 				continue;
+
 			if (!_skipSkinOffsets && _bones.size() < 2 ) {
-				if (_meshes[m].name() == _modelVertexAnim->head()) {
-					_meshes[m].update(_modelVertexAnim);
-					// TODO: lines 440 - 443.. set some vals and goto LAB_doMeshBlends;
+				if (_modelVertexAnim && mesh.name() == _modelVertexAnim->head()) {
+					mesh.update(_modelVertexAnim);
+					// TODO: lines 422 - 427.. set some vals and goto LAB_doMeshBlends;
 				}
-				_meshes[m].update(&_boneMatricies, &_lerpedElements);
-				// TODO: Set some vals here..
+				mesh.update(&_boneMatricies, &_lerpedElements);
 			} else {
-				//warning("TODO: Finish TeModel::update. (disasm 456 ~ 693)");
+				mesh.resizeUpdatedTables(mesh.numVerticies());
+				const Common::Array<TeVector3f32> *verticies = nullptr;
+				if (_modelVertexAnim && mesh.name() == _modelVertexAnim->head())
+					verticies = &_modelVertexAnim->getVertices();
+
+				for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
+					TeVector3f32 vertex;
+					if (!verticies) {
+						vertex = mesh.preUpdatedVertex(i);
+					} else {
+						if (i < verticies->size())
+							vertex = (*verticies)[i];
+					}
+					TeVector3f32 normal = mesh.preUpdatedNormal(i);
+					int idx = (int)mesh.matrixIndex(i);
+
+					TeVector3f32 updatedvertex;
+					TeVector3f32 updatednormal;
+
+					if (idx < (int)_bones.size()) {
+						updatedvertex = vertex;
+						if (!verticies)
+							updatedvertex = _boneMatricies[idx] * updatedvertex;
+						updatednormal = _boneMatricies[idx] * normal;
+					} else {
+						idx -= _bones.size();
+						for (unsigned int w = 0; w < _weightElements[idx].size(); w++) {
+							const TeMatrix4x4 &wmatrix = _boneMatricies[_weightElements[idx][w]._x];
+							float weight = _weightElements[idx][w]._weight;
+							updatedvertex = updatedvertex + ((wmatrix * vertex) * weight);
+							updatednormal = updatednormal + (wmatrix.mult3x3(normal) * weight);
+						}
+					}
+
+					mesh.setUpdatedVertex(i, updatedvertex);
+					mesh.setUpdatedNormal(i, updatednormal);
+				}
+			}
+
+			for (MeshBlender *mb : _meshBlenders) {
+				if (mesh.name().contains(mb->_name)) {
+					// TODO: Finish TeModel::update. (disasm 585 ~ 644), LAB_doMeshBlends
+					//float blendamount = MIN(mb->_timer.getTimeFromStart() / 1000000.0f, 1.0f);
+
+				}
 			}
 		}
 	} else {
@@ -394,7 +438,7 @@ bool TeModel::loadWeights(Common::ReadStream &stream, Common::Array<weightElemen
 		error("Improbable number of weights %d", (int)nweights);
 	weights.resize(nweights);
 	for (unsigned int i = 0; i < nweights; i++) {
-		weights[i]._w = stream.readFloatLE();
+		weights[i]._weight = stream.readFloatLE();
 		weights[i]._x = stream.readUint16LE();
 		stream.readUint16LE();
 	}
@@ -413,7 +457,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	if (vertcount > 100000 || matcount > 100000 || matidxcount > 100000 || idxcount > 100000)
 		error("Improbable mesh sizes %d %d %d %d", vertcount, matcount, matidxcount, idxcount);
 
-	mesh.setConf(vertcount, idxcount, TeMesh::MeshMode_TriangleFan, matcount, matidxcount);
+	mesh.setConf(vertcount, idxcount, TeMesh::MeshMode_Triangles, matcount, matidxcount);
 
 	uint32 flags = stream.readUint32LE();
 	if (flags & 1)
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index 74ee90aafe9..3c097b63377 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -32,6 +32,7 @@
 #include "tetraedge/te/te_model_vertex_animation.h"
 #include "tetraedge/te/te_tiled_texture.h"
 #include "tetraedge/te/te_intrusive_ptr.h"
+#include "tetraedge/te/te_quaternion.h"
 
 namespace Tetraedge {
 
@@ -55,7 +56,6 @@ public:
 	class MeshBlender {
 	public:
 		MeshBlender(const Common::String &s1, const Common::String &s2, float amount, TeModel *model);
-	private:
 		Common::String _name;
 		uint _meshNo;
 		float _amount;
@@ -69,7 +69,7 @@ public:
 	};
 
 	struct weightElement {
-		float _w;
+		float _weight;
 		unsigned short _x;
 	};
 
@@ -97,7 +97,7 @@ public:
 	int findOrAddWeights(const Common::Array<weightElement> &weights);
 	void forceMatrix(const TeMatrix4x4 &matrix);
 	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num);
-	TeMatrix4x4 lerpElementsMatrix(unsigned long weightNum, Common::Array<TeMatrix4x4> &matricies);
+	TeMatrix4x4 lerpElementsMatrix(unsigned int weightNum, const Common::Array<TeMatrix4x4> &matricies);
 
 	/* Align the stream to the nearest 4 byte boudary*/
 	static void loadAlign(Common::SeekableReadStream &stream);
@@ -126,6 +126,8 @@ public:
 
 	TeMatrix4x4 skinOffset(unsigned long boneno) const;
 
+	TeSignal2Param<const Common::String &, TeMatrix4x4 &> &bonesUpdatedSignal() { return _bonesUpdatedSignal; }
+
 	static Common::SeekableReadStream *tryLoadZlibStream(Common::SeekableReadStream &instr);
 	TeIntrusivePtr<TeTiledTexture> _tiledTexture;
 
@@ -146,9 +148,12 @@ protected:
 	Common::Array<Common::Array<weightElement>> _weightElements;
 	Common::Array<BonesBlender *> _boneBlenders;
 
+	TeQuaternion _boneRotation;
+
 	TeIntrusivePtr<TeModelAnimation> _modelAnim;
 	TeIntrusivePtr<TeModelVertexAnimation> _modelVertexAnim;
 
+	TeSignal2Param<const Common::String &, TeMatrix4x4 &> _bonesUpdatedSignal;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index 956e470e39a..e8759bb8701 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -100,6 +100,9 @@ TeQuaternion TeModelAnimation::getNMORotation(unsigned long boneNo, float amount
 			while (i < arr.size() && arr[i]._f < amount)
 				i++;
 
+			if (i == 0)
+				return arr.front()._rot;
+
 			if (i == arr.size() || arr.size() == 1 || arr[i]._f - arr[i-1]._f == 0) {
 				return arr.back()._rot;
 			}
@@ -119,6 +122,9 @@ TeVector3f32 TeModelAnimation::getNMOTranslation(unsigned long boneNo, float amo
 			while (i < arr.size() && arr[i]._f < amount)
 				i++;
 
+			if (i == 0)
+				return arr.front()._trans;
+
 			if (i == arr.size() || arr.size() == 1 || arr[i]._f - arr[i-1]._f == 0) {
 				return arr.back()._trans;
 			}
@@ -201,8 +207,12 @@ bool TeModelAnimation::load(Common::SeekableReadStream &stream) {
 	}
 
 	_useNMOArrays = stream.readUint32LE();
-	_numNMOFrames = stream.readUint32LE();
+	uint32 nmoFrames = stream.readUint32LE();
+	if (_useNMOArrays)
+		_numNMOFrames = nmoFrames;
 	uint32 numBones = stream.readUint32LE();
+	if (numBones > 100000)
+		error("TeModelAnimation::load: Improbable number of bones %d", numBones);
 
 	if (_useNMOArrays == 0) {
 		_fbxArrays.resize(numBones);
@@ -217,24 +227,29 @@ bool TeModelAnimation::load(Common::SeekableReadStream &stream) {
 		if (!Te3DObject2::loadAndCheckFourCC(stream, "BONE"))
 			return false;
 		const Common::String boneName = Te3DObject2::deserializeString(stream);
+		TeModel::loadAlign(stream);
 		setBoneName(i, boneName);
 		if (!Te3DObject2::loadAndCheckFourCC(stream, "BTRA"))
 			return false;
 		uint32 numTrans = stream.readUint32LE();
+		if (numTrans > 100000)
+			error("TeModelAnimation::load: Improbable number of bone translations %d", numTrans);
 		for (unsigned int j = 0; j < numTrans; j++) {
 			float f = stream.readFloatLE();
 			TeVector3f32 trans;
 			TeVector3f32::deserialize(stream, trans);
-			setTranslation(j, f, trans);
+			setTranslation(i, f, trans);
 		}
 		if (!Te3DObject2::loadAndCheckFourCC(stream, "BROT"))
 			return false;
 		uint32 numRots = stream.readUint32LE();
+		if (numRots > 100000)
+			error("TeModelAnimation::load: Improbable number of bone rotations %d", numRots);
 		for (unsigned int j = 0; j < numRots; j++) {
 			float f = stream.readFloatLE();
 			TeQuaternion rot;
 			TeQuaternion::deserialize(stream, rot);
-			setRotation(j, f, rot);
+			setRotation(i, f, rot);
 		}
 	}
 	return true;
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 32cd8e53f81..e28a7a1663d 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -78,7 +78,7 @@ bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 v2, TeVec
 			return true;
 		}
 	}
-	
+
 	float hitf = FLT_MAX;
 	for (unsigned int i = 0; i < _verticies.size() / 3; i++) {
 		const TeVector3f32 triv1 = worldTrans * _verticies[i * 3 + 0];
@@ -157,6 +157,9 @@ void TePickMesh2::serialize(Common::WriteStream &stream, const TePickMesh2 &mesh
 void TePickMesh2::deserialize(Common::ReadStream &stream, TePickMesh2 &mesh) {
 	Te3DObject2::deserialize(stream, mesh);
 	uint32 ntriangles = stream.readUint32LE();
+	if (ntriangles > 100000)
+		error("TePickMesh2::deserialize: Improbable number of triangles %d", ntriangles);
+
 	mesh._verticies.resize(ntriangles * 3);
 	mesh._lastTriangleHit = 0;
 
diff --git a/engines/tetraedge/te/te_quaternion.h b/engines/tetraedge/te/te_quaternion.h
index cc8ae96ce42..26ef8e5103a 100644
--- a/engines/tetraedge/te/te_quaternion.h
+++ b/engines/tetraedge/te/te_quaternion.h
@@ -25,6 +25,7 @@
 #include "math/quat.h"
 
 #include "tetraedge/te/te_vector3f32.h"
+#include "tetraedge/te/te_matrix4x4.h"
 
 namespace Tetraedge {
 
@@ -68,18 +69,23 @@ public:
 		return retval;
 	}
 
+	TeMatrix4x4 toTeMatrix() const {
+		const TeMatrix4x4 retval = toMatrix();
+		return retval.transpose();
+	}
+
 	static void deserialize(Common::ReadStream &stream, TeQuaternion &dest) {
-		dest.value(0) = stream.readFloatLE();
-		dest.value(1) = stream.readFloatLE();
-		dest.value(2) = stream.readFloatLE();
-		dest.value(3) = stream.readFloatLE();
+		dest.x() = stream.readFloatLE();
+		dest.y() = stream.readFloatLE();
+		dest.z() = stream.readFloatLE();
+		dest.w() = stream.readFloatLE();
 	}
 
 	static void serialize(Common::WriteStream &stream, const TeQuaternion &src) {
-		stream.writeFloatLE(src.value(0));
-		stream.writeFloatLE(src.value(1));
-		stream.writeFloatLE(src.value(2));
-		stream.writeFloatLE(src.value(3));
+		stream.writeFloatLE(src.x());
+		stream.writeFloatLE(src.y());
+		stream.writeFloatLE(src.z());
+		stream.writeFloatLE(src.w());
 	}
 
 	Common::String dump() const;
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 6250e74b634..8830f3bcbbe 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -326,7 +326,7 @@ void TeRenderer::multiplyMatrix(const TeMatrix4x4 &matrix) {
 
 void TeRenderer::optimiseTransparentMeshProperties() {
 	if (!_transparentMeshProperties.empty()) {
-		//warning("FIXME: Implement TeRenderer::optimiseTransparentMeshProperties.");
+		// TODO: Implement TeRenderer::optimiseTransparentMeshProperties.
 	}
 }
 
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index 09d0cbe70d8..0a8b479a34e 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -41,9 +41,9 @@ public:
 	};
 
 	enum ShadowMode {
-		ShadowMode0,
-		ShadowMode1,
-		ShadowMode2
+		ShadowMode0 = 0,
+		ShadowMode1 = 1,
+		ShadowMode2 = 2
 	};
 
 	class TransparentMeshProperties {
diff --git a/engines/tetraedge/te/te_signal.h b/engines/tetraedge/te/te_signal.h
index ea860b81efb..105c42b3199 100644
--- a/engines/tetraedge/te/te_signal.h
+++ b/engines/tetraedge/te/te_signal.h
@@ -123,7 +123,7 @@ template<class S, class T> class TeSignal2Param : public Common::SortedArray<TeI
 public:
 	TeSignal2Param() : Common::SortedArray<TeICallback2ParamPtr<S, T>, const TeICallback2ParamPtr<S, T> &>(_teCallbackSorter) {};
 
-	bool call(T t, S s) {
+	bool call(S s, T t) {
 		typename Common::Array<TeICallback2ParamPtr<S, T>>::iterator i = this->begin();
 		typename Common::Array<TeICallback2ParamPtr<S, T>>::iterator end_ = this->end();
 		for (; i < end_; i++) {
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index 8de44498a49..45376231122 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -197,7 +197,7 @@ void TeTextBase2::drawEmptyChar(unsigned int offset) {
 void TeTextBase2::drawLine(TeImage &img, const Common::String &str, int yoffset) {
 	TeIntrusivePtr<TeFont3> font = _fonts[0];
 
-	// FIXME: Add multi-color support if needed.
+	// TODO: Add multi-color support if needed.
 	font->draw(img, str, _fontSize, yoffset, _globalColor, _alignStyle);
 }
 
diff --git a/engines/tetraedge/te/te_trs.cpp b/engines/tetraedge/te/te_trs.cpp
index ca4aa134207..8e2a13e7e8c 100644
--- a/engines/tetraedge/te/te_trs.cpp
+++ b/engines/tetraedge/te/te_trs.cpp
@@ -41,8 +41,8 @@ TeTRS::TeTRS() {
 TeTRS TeTRS::lerp(const TeTRS &other, float amount) {
 	TeTRS result;
 	result._rot = _rot.slerpQuat(other._rot, amount);
-	result._trans = _trans * (1.0 - amount) + other._trans;
-	result._scale = _scale * (1.0 - amount) + other._scale;
+	result._trans = _trans * (1.0 - amount) + other._trans * amount;
+	result._scale = _scale * (1.0 - amount) + other._scale * amount;
 	return result;
 }
 
diff --git a/engines/tetraedge/te/te_vector3f32.cpp b/engines/tetraedge/te/te_vector3f32.cpp
index 07baa317294..6cebd5c3eae 100644
--- a/engines/tetraedge/te/te_vector3f32.cpp
+++ b/engines/tetraedge/te/te_vector3f32.cpp
@@ -38,8 +38,8 @@ bool TeVector3f32::parse(const Common::String &val) {
 }
 
 void TeVector3f32::rotate(const TeQuaternion &rot) {
-	Math::Matrix4 matrix = rot.toMatrix();
-	matrix.transform(this, false);
+	const TeMatrix4x4 matrix = rot.toTeMatrix();
+	*this = (matrix * *this);
 }
 
 TeVector3f32 operator^(const TeVector3f32 &left, const TeVector3f32 &right) {


Commit: a5811c653a5f86971e8817e2d963fa902765819a
    https://github.com/scummvm/scummvm/commit/a5811c653a5f86971e8817e2d963fa902765819a
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More WIP. Started work on pathfinding.

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/inventory_objects_xml_parser.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/lua_binds.h
    engines/tetraedge/te/micropather.h
    engines/tetraedge/te/te_bezier_curve.cpp
    engines/tetraedge/te/te_bezier_curve.h
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_images_sequence.cpp
    engines/tetraedge/te/te_images_sequence.h
    engines/tetraedge/te/te_jpeg.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_png.h
    engines/tetraedge/te/te_vector2s32.h
    engines/tetraedge/tetraedge.cpp
    engines/tetraedge/to_lua.cpp
    engines/tetraedge/to_lua.h


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 1ffe18054ab..c182c34b365 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -57,10 +57,11 @@ void Character::WalkSettings::clear() {
 }
 
 Character::Character() : _curveOffset(0), _lastFrame(-1), _callbacksChanged(false),
-_missingCurrentAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"),
+_notWalkAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"),
 _needsSomeUpdate(false), _positionFlag(false), _lookingAtTallThing(false),
 _stepSound1("sounds/SFX/PAS_H_BOIS1.ogg"), _stepSound2("sounds/SFX/PAS_H_BOIS2.ogg"),
-_freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(nullptr) {
+_freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(nullptr),
+_recallageY(true) {
 	_curModelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
 }
 
@@ -159,7 +160,7 @@ bool Character::blendAnimation(const Common::String &animname, float amount, boo
 	Common::Path animpath("models/Anims");
 	animpath.joinInPlace(animname);
 
-	_missingCurrentAnim = !(animname.contains(_characterSettings._walkFileName)
+	_notWalkAnim = !(animname.contains(_characterSettings._walkFileName)
 			|| animname.contains(walkAnim(WalkPart_Start))
 			|| animname.contains(walkAnim(WalkPart_Loop))
 			|| animname.contains(walkAnim(WalkPart_EndG))
@@ -239,7 +240,7 @@ bool Character::isFramePassed(int frameno) {
 }
 
 bool Character::isWalkEnd() {
-	Common::String animFile = _model->anim()->_loadedPath.getLastComponent().toString();
+	const Common::String animFile = _model->anim()->_loadedPath.getLastComponent().toString();
 	for (const auto & walkSettings : _characterSettings._walkSettings) {
 		if (walkSettings._value._walkParts[WalkPart_EndD]._file.contains(animFile)
 				|| walkSettings._value._walkParts[WalkPart_EndG]._file.contains(animFile))
@@ -358,7 +359,7 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 		}
 	}
 
-	if (!parser.loadBuffer((unsigned char *)fixedbuf.c_str(), bufsize))
+	if (!parser.loadBuffer((const byte *)fixedbuf.c_str(), bufsize))
 		error("Character::loadSettings: Can't open %s", path.c_str());
 
 	if (!parser.parse())
@@ -464,7 +465,65 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 }
 
 bool Character::onModelAnimationFinished() {
-	error("TODO: Implement Character::onModelAnimationFinished");
+	const Common::Path &loadedPath = _model->anim()->_loadedPath;
+	const Common::String animfile = loadedPath.getLastComponent().toString();
+
+	// TODO: Do something with _unrecalAnims here.
+
+	Game *game = g_engine->getGame();
+	bool isWalkAnim = false;
+	if (game->scene()._character == this) {
+		// TODO: check if this logic matches..
+		for (const auto &walkSettings : _characterSettings._walkSettings) {
+			isWalkAnim |= (walkSettings._key.contains("Walk") || walkSettings._key.contains("Jog"));
+			isWalkAnim |= (walkSettings._value._walkParts[0]._file == animfile ||
+					walkSettings._value._walkParts[1]._file == animfile ||
+					walkSettings._value._walkParts[2]._file == animfile ||
+					walkSettings._value._walkParts[3]._file == animfile);
+		}
+		isWalkAnim |= animfile.contains(_characterSettings._walkFileName);
+	} else {
+		isWalkAnim = (animfile.contains(_characterSettings._walkFileName) ||
+				  animfile.contains(walkAnim(WalkPart_Start)) ||
+				  animfile.contains(walkAnim(WalkPart_Loop)) ||
+				  animfile.contains(walkAnim(WalkPart_EndD)) ||
+				  animfile.contains(walkAnim(WalkPart_EndG)));
+	}
+
+	if (isWalkAnim) {
+		int pereBone = _curModelAnim->findBone("Pere");
+		const TeTRS endTRS = trsFromAnim(*_curModelAnim, pereBone, _curModelAnim->lastFrame());
+		TeVector3f32 trans = endTRS.getTranslation();
+		trans.x() = -trans.x();
+
+		TeVector3f32 newpos;
+		if (!_recallageY) {
+			const TeTRS startTRS = trsFromAnim(*_curModelAnim, pereBone, _curModelAnim->firstFrame());
+			trans = trans - startTRS.getTranslation();
+			const TeTRS nearEndTRS = trsFromAnim(*_curModelAnim, pereBone, _curModelAnim->lastFrame() - 1);
+			trans  = trans + (endTRS.getTranslation() - nearEndTRS.getTranslation());
+			newpos = _model->worldTransformationMatrix() * trans;
+		} else if (!_freeMoveZone) {
+			trans.x() = -trans.x();
+			newpos = _model->worldTransformationMatrix() * trans;
+		} else {
+			newpos = correctPosition(_model->worldTransformationMatrix() * trans);
+		}
+		_model->setPosition(newpos);
+	}
+
+	if (game->scene()._character == this) {
+		_characterAnimPlayerFinishedSignal.call(loadedPath.toString());
+	} else {
+		_onCharacterAnimFinishedSignal.call(_model->name());
+	}
+
+	if (_someRepeatFlag && loadedPath.toString().contains(_setAnimName)) {
+		_notWalkAnim = false;
+		_someRepeatFlag = false;
+		setAnimation(_characterSettings._walkFileName, true);
+	}
+
 	return false;
 }
 
@@ -503,7 +562,7 @@ bool Character::setAnimation(const Common::String &aname, bool repeat, bool para
 					  aname.contains(walkAnim(WalkPart_Loop)) ||
 					  aname.contains(walkAnim(WalkPart_EndD)) ||
 					  aname.contains(walkAnim(WalkPart_EndG)));
-	_missingCurrentAnim = !isWalkAnim;
+	_notWalkAnim = !isWalkAnim;
 
 	if (_curModelAnim) {
 		_curModelAnim->onFinished().remove(this, &Character::onModelAnimationFinished);
@@ -511,6 +570,7 @@ bool Character::setAnimation(const Common::String &aname, bool repeat, bool para
 	}
 
 	_curModelAnim = animCacheLoad(animPath);
+	_curModelAnim->onFinished().add(this, &Character::onModelAnimationFinished);
 	_curModelAnim->bind(_model);
 	_curModelAnim->setFrameLimits(startFrame, endFrame);
 	_model->setAnim(_curModelAnim, repeat);
@@ -585,6 +645,7 @@ void Character::updateAnimFrame() {
 }
 
 void Character::updatePosition(float curveOffset) {
+	assert(_curve);
 	if (!_curve->controlPoints().empty()) {
 		TeVector3f32 pt = _curve->retrievePoint(curveOffset) + _curveStartLocation;
 		if (_freeMoveZone) {
@@ -605,10 +666,14 @@ Common::String Character::walkAnim(Character::WalkPart part) {
 }
 
 void Character::walkMode(const Common::String &mode) {
-	error("TODO: Implement Character::walkMode");
+	if (_walkModeStr != mode)
+		_walkModeStr = mode;
+	_walkPart0AnimLen = animLengthFromFile(walkAnim(WalkPart_Start), &_walkPart0AnimFrameCount, 9999);
+	_walkPart3AnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkPart3AnimFrameCount, 9999);
+	_walkPart1AnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkPart1AnimFrameCount, 9999);
 }
 
-void Character::walkTo(float param_1, bool param_2) {
+void Character::walkTo(float curveEnd, bool walkFlag) {
 	error("TODO: Implement Character::walkTo");
 }
 
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 5d3ea7c85b1..6723305803b 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -202,13 +202,14 @@ private:
 
 	int _lastFrame;
 	int _lastAnimFrame;
-	bool _missingCurrentAnim;
+	bool _notWalkAnim;
 	bool _someRepeatFlag; // TODO: what is this?
 	bool _callbacksChanged;
 	bool _needsSomeUpdate;
 	bool _positionFlag;
 	bool _lookingAtTallThing;
 	bool _hasAnchor;
+	bool _recallageY;
 
 	TeVector2f32 _headRotation;
 	TeVector2f32 _lastHeadRotation;
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index b57fbeef487..8205097f4fc 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -204,7 +204,9 @@ bool Game::changeWarp2(const Common::String &zone, const Common::String &scene,
 	_warped = false;
 	_movePlayerCharacterDisabled = false;
 	_sceneCharacterVisibleFromLoad = false;
-	// TODO: set 3 other fields here (0x3f40 = -1, 0x4249 = 1, 0x424b = 0)
+	// TODO: set another field here (0x3f40 = -1)
+	_isCharacterIdle = true;
+	_isCharacterWalking = false;
 	Common::Path luapath("scenes");
 	luapath.joinInPlace(zone);
 	luapath.joinInPlace(scene);
@@ -457,13 +459,19 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	const Common::Path intLuaPath = scenePath.join(Common::String::format("Int%s.lua", scene.c_str()));
 	const Common::Path logicLuaPath = scenePath.join(Common::String::format("Logic%s.lua", scene.c_str()));
 	const Common::Path setLuaPath = scenePath.join(Common::String::format("Set%s.lua", scene.c_str()));
-	const Common::Path forLuaPath = scenePath.join(Common::String::format("For%s.lua", scene.c_str()));
+	Common::Path forLuaPath = scenePath.join(Common::String::format("For%s.lua", scene.c_str()));
 	const Common::Path markerLuaPath = scenePath.join(Common::String::format("Marker%s.lua", scene.c_str()));
 
 	bool intLuaExists = Common::File::exists(intLuaPath);
 	bool logicLuaExists = Common::File::exists(logicLuaPath);
 	bool setLuaExists = Common::File::exists(setLuaPath);
 	bool forLuaExists = Common::File::exists(forLuaPath);
+	if (!forLuaExists) {
+		// slight hack.. try an alternate For lua path.
+		forLuaPath = scenePath.join("Android-MacOSX").join(Common::String::format("For%s.lua", scene.c_str()));
+		forLuaExists = Common::File::exists(forLuaPath);
+		debug("searched for %s", forLuaPath.toString().c_str());
+	}
 	bool markerLuaExists = Common::File::exists(markerLuaPath);
 
 	if (!intLuaExists && !logicLuaExists && !setLuaExists && !forLuaExists && !markerLuaExists) {
@@ -751,8 +759,54 @@ bool Game::onCharacterAnimationFinished(const Common::String &val) {
 	error("TODO: Implemet Game::onCharacterAnimationFinished %s", val.c_str());
 }
 
-bool Game::onCharacterAnimationPlayerFinished(const Common::String &val) {
-	error("TODO: Implemet Game::onCharacterAnimationPlayerFinished %s", val.c_str());
+bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
+	if (_gameLoadState != 0)
+		return false;
+
+	bool callScripts = true;
+	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+		YieldedCallback &cb = _yieldedCallbacks[i];
+		if (cb._luaFnName == "OnCharacterAnimationFinished" && cb._luaParam == "Kate") {
+			TeLuaThread *lua = cb._luaThread;
+			_yieldedCallbacks.remove_at(i);
+			if (lua) {
+				lua->resume();
+				callScripts = false;
+				return false;
+			}
+			break;
+		}
+	}
+	if (callScripts) {
+		_luaScript.execute("OnCharacterAnimationFinished", "Kate");
+		_luaScript.execute("OnCellCharacterAnimationPlayerFinished", anim);
+	}
+
+	Character *character = scene()._character;
+	assert(character);
+
+	const Common::String &curAnimName = character->curAnimName();
+	if (_currentScene == _someSceneName) {
+		if (curAnimName == character->walkAnim(Character::WalkPart_Start)
+			|| curAnimName == character->walkAnim(Character::WalkPart_Loop)
+			|| curAnimName == character->walkAnim(Character::WalkPart_EndD)
+			|| curAnimName == character->walkAnim(Character::WalkPart_EndG))
+			character->stop();
+	} else {
+		if (!_sceneCharacterVisibleFromLoad && curAnimName != character->walkAnim(Character::WalkPart_Start)) {
+			character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
+			return false;
+		}
+		if (curAnimName == character->walkAnim(Character::WalkPart_EndD)
+			|| curAnimName == character->walkAnim(Character::WalkPart_EndG)) {
+			character->updatePosition(1.0);
+			character->endMove();
+			// Note: original checks walkAnim again.. is there a reason to do that?
+			character->setAnimation(character->characterSettings()._walkFileName, true);
+		}
+	}
+
+	return false;
 }
 
 bool Game::onDialogFinished(const Common::String &val) {
@@ -779,14 +833,12 @@ bool Game::onDisplacementFinished() {
 	_scene._character->stop();
 	_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true);
 
-	// TODO: Twiddle flags 0x424b and 0x4249
-	/*
-	if (!_field_0x424b) {
-		_field_0x4249 = false;
+	if (!_isCharacterWalking) {
+		_isCharacterWalking = false;
+		_isCharacterIdle = true;
 	} else {
-		_field_0x424b = false;
-		_field_0x4249 = true;
-	}*/
+		_isCharacterIdle = false;
+	}
 
 	TeLuaThread *thread = nullptr;
 
@@ -848,57 +900,6 @@ bool Game::onMarkersVisible(TeCheckboxLayout::State state) {
 	return false;
 }
 
-static
-TePickMesh2 *findNearestMesh(TeIntrusivePtr<TeCamera> &camera, const TeVector2s32 &frompt,
-			Common::Array<TePickMesh2*> &pickMeshes, TeVector3f32 *outloc, bool lastHitFirst) {
-	TeVector3f32 locresult;
-	TePickMesh2 *nearest = nullptr;
-	float furthest = camera->_orthFarVal;
-	if (!pickMeshes.empty()) {
-		TeVector3f32 v1;
-		TeVector3f32 v2;
-		for (unsigned int i = 0; i < pickMeshes.size(); i++) {
-			TePickMesh2 *mesh = pickMeshes[i];
-			const TeMatrix4x4 transform = mesh->worldTransformationMatrix();
-			if (lastHitFirst) {
-				unsigned int tricount = mesh->verticies().size() / 3;
-				unsigned int vert = mesh->lastTriangleHit() * 3;
-				if (mesh->lastTriangleHit() >= tricount)
-					vert = 0;
-				const TeVector3f32 v3 = transform * mesh->verticies()[vert];
-				const TeVector3f32 v4 = transform * mesh->verticies()[vert + 1];
-				const TeVector3f32 v5 = transform * mesh->verticies()[vert + 2];
-				TeVector3f32 result;
-				float fresult;
-				int intresult = TeRayIntersection::intersect(v1, v2, v3, v4, v5, result, fresult);
-				if (intresult == 1 && fresult < furthest && fresult >= camera->_orthNearVal)
-					return mesh;
-			}
-			for (unsigned int tri = 0; tri < mesh->verticies().size() / 3; tri++) {
-				const TeVector3f32 v3 = transform * mesh->verticies()[tri * 3];
-				const TeVector3f32 v4 = transform * mesh->verticies()[tri * 3 + 1];
-				const TeVector3f32 v5 = transform * mesh->verticies()[tri * 3 + 1];
-				camera->getRay(frompt, v1, v2);
-				TeVector3f32 result;
-				float fresult;
-				int intresult = TeRayIntersection::intersect(v1, v2, v3, v4, v5, result, fresult);
-				if (intresult == 1 && fresult < furthest && fresult >= camera->_orthNearVal) {
-					mesh->setLastTriangleHit(tri);
-					locresult = result;
-					furthest = fresult;
-					nearest = mesh;
-					if (lastHitFirst)
-						break;
-				}
-			}
-		}
-	}
-	if (outloc) {
-		*outloc = locresult;
-	}
-	return nearest;
-}
-
 bool Game::onMouseClick(const Common::Point &pt) {
 	Application *app = g_engine->getApplication();
 
@@ -928,7 +929,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 	Common::String nearestMeshName = "None";
 	TeIntrusivePtr<TeCamera> curCamera = _scene.currentCamera();
 	Common::Array<TePickMesh2*> pickMeshes = _scene.pickMeshes();
-	TePickMesh2 *nearestMesh = findNearestMesh(curCamera, pt, pickMeshes, nullptr, false);
+	TePickMesh2 *nearestMesh = TeFreeMoveZone::findNearestMesh(curCamera, pt, pickMeshes, nullptr, false);
 	if (nearestMesh) {
 		nearestMeshName = nearestMesh->name();
 		_lastCharMoveMousePos = TeVector2s32(0, 0);
@@ -982,14 +983,14 @@ bool Game::onMouseClick(const Common::Point &pt) {
 		TeVector3f32 lastPoint = _scene.curve()->controlPoints().back();
 		character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
 		character->walkTo(1.0, false);
-		// TODO: Set app field field_0x424b to true
+		_isCharacterWalking = true;
 		_posPlayer = lastPoint;
 	}
 
 	if (!_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._walkFileName)) {
 		_lastCharMoveMousePos = TeVector2s32(0, 0);
 		_movePlayerCharacterDisabled = true;
-		// TODO: Set field 	0x4249 to false
+		_isCharacterWalking = false;
 		if (nearestMesh) {
 			character->stop();
 			_luaScript.execute("OnWarpObjectHit", nearestMeshName);
@@ -1445,18 +1446,18 @@ void Game::update() {
 						player->setCurveOffset(0.0f);
 						player->setAnimation(player->walkAnim(Character::WalkPart_Start), false);
 						player->walkTo(1.0f, false);
-						// TODO: Set app field field_0x424b
+						_isCharacterWalking = true;
 					}
 					player->setNeedsSomeUpdate(false);
 				}
 
 				const Common::String &charAnim = _scene._character->curAnimName();
-				bool lockCursor = (charAnim == _scene._character->walkAnim(Character::WalkPart_Start) ||
+				bool unlockCursor = (charAnim == _scene._character->walkAnim(Character::WalkPart_Start) ||
 						charAnim == _scene._character->walkAnim(Character::WalkPart_Loop) ||
 						charAnim == _scene._character->walkAnim(Character::WalkPart_EndD) ||
 						charAnim == _scene._character->walkAnim(Character::WalkPart_EndG) ||
 						charAnim == _scene._character->characterSettings()._walkFileName);
-				app->lockCursor(lockCursor);
+				app->lockCursor(!unlockCursor);
 			}
 		}
 
@@ -1494,20 +1495,20 @@ bool Game::HitObject::onChangeWarp() {
 
 bool Game::HitObject::onDown() {
 	_game->luaScript().execute("OnButtonDown", _name);
-	// TODO: set this field _game->field_0x4249 = true;
+	_game->_isCharacterIdle = true;
 	return false;
 }
 
 bool Game::HitObject::onUp() {
 	_game->luaScript().execute("OnButtonUp", _name);
-	// TODO: set this field _game->field_0x4249 = true;
+	_game->_isCharacterIdle = true;
 	return false;
 }
 
 bool Game::HitObject::onValidated() {
 	if (!g_engine->getApplication()->isLockCursor()) {
 		_game->luaScript().execute("OnWarpObjectHit", _name);
-		// TODO: set this field _game->field_0x4249 = true;
+		_game->_isCharacterIdle = true;
 	}
 	return false;
 }
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index 081bd5ec52b..e01d0b4d27d 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -171,6 +171,9 @@ public:
 	bool _returnToMainMenu;
 	bool _firstInventory;
 	bool _movePlayerCharacterDisabled;
+	bool _sceneCharacterVisibleFromLoad;
+	bool _isCharacterWalking;
+	bool _isCharacterIdle;
 
 	const Common::String &currentZone() { return _currentZone; }
 	const Common::String &currentScene() { return _currentScene; }
@@ -185,6 +188,9 @@ public:
 	Common::Array<YieldedCallback> &yieldedCallbacks() { return _yieldedCallbacks; }
 	void setSaveRequested() { _saveRequested = true; }
 	bool markersVisible() const { return _markersVisible; }
+	const TeVector3f32 &posPlayer() const { return _posPlayer; }
+	void setPosPlayer(const TeVector3f32 &pos) { _posPlayer = pos; }
+	TeTimer &walkTimer() { return _walkTimer; }
 
 private:
 	bool _luaShowOwnerError;
@@ -219,6 +225,7 @@ private:
 	Common::String _currentScene;
 	Common::String _exitZone;
 	Common::String _prevSceneName;
+	Common::String _someSceneName;
 	Common::Path _sceneZonePath;
 
 	Common::String _loadName;
@@ -252,7 +259,6 @@ private:
 	int _objectsTakenVal;
 	int _dialogsTold;
 
-	bool _sceneCharacterVisibleFromLoad;
 	bool _markersVisible;
 	bool _saveRequested;
 	bool _randomSoundFinished;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 705fbe78243..e72fbca972b 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -95,25 +95,24 @@ bool InGameScene::addMarker(const Common::String &markerName, const Common::Stri
 		markerSprite->setAnchor(TeVector3f32(0.0f, 0.0f, 0.0f));
 		markerSprite->load(imgPath);
 		markerSprite->setSizeType(TeILayout::RELATIVE_TO_PARENT);
-			markerSprite->setPositionType(TeILayout::RELATIVE_TO_PARENT);
-		TeVector3f32 newSize;
+		markerSprite->setPositionType(TeILayout::RELATIVE_TO_PARENT);
+		TeVector3f32 newPos;
 		if (locType == "PERCENT") {
 			Application *app = g_engine->getApplication();
 			TeVector3f32 frontLayoutSize = app->_frontLayout.userSize();
-			newSize.x() = frontLayoutSize.x() * (x / 100.0);
-			newSize.y() = frontLayoutSize.y() * (y / 100.0);
+			newPos.x() = frontLayoutSize.x() * (x / 100.0);
+			newPos.y() = frontLayoutSize.y() * (y / 100.0);
 		} else {
-			newSize.x() = x / 800.0;
-			newSize.y() = y / 600.0;
+			newPos.x() = x / 800.0;
+			newPos.y() = y / 600.0;
 		}
-		markerSprite->setSize(newSize);
+		markerSprite->setPosition(newPos);
 
-		float screenWidth = (float)g_engine->getDefaultScreenWidth();
-		float screenHeight = (float)g_engine->getDefaultScreenHeight();
+		const TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
 		if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
-			markerSprite->setPosition(TeVector3f32(0.07, (4.0 / ((screenWidth / screenHeight) * 4.0)) * 0.04, 0.0));
+			markerSprite->setSize(TeVector3f32(0.07, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.07, 0.0));
 		} else {
-			markerSprite->setPosition(TeVector3f32(0.04, (4.0 / ((screenWidth / screenHeight) * 4.0)) * 0.04, 0.0));
+			markerSprite->setSize(TeVector3f32(0.04, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.04, 0.0));
 		}
 		markerSprite->setVisible(game->markersVisible());
 		markerSprite->_tiledSurfacePtr->_frameAnim._loopCount = -1;
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index eee8bc31785..cf08c1e874a 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -190,7 +190,7 @@ public:
 	int _shadowLightNo;
 	CharactersShadow *_charactersShadow;
 	TeIntrusivePtr<TeBezierCurve> curve() { return _curve; }
-	void setCurve(TeIntrusivePtr<TeBezierCurve> &c) { c = _curve; }
+	void setCurve(TeIntrusivePtr<TeBezierCurve> &c) { _curve = c; }
 	Common::Array<TeIntrusivePtr<TeModel>> &zoneModels() { return _zoneModels; }
 	Common::Array<TeRectBlocker> &rectBlockers() { return _rectBlockers; }
 	Common::Array<Object3D *> object3Ds() { return _object3Ds; }
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 5b6573564a7..792333c9e7e 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -135,7 +135,7 @@ void Inventory::loadXMLFile(const Common::Path &path) {
 	xmlfile.close();
 
 	InventoryObjectsXmlParser parser;
-	if (!parser.loadBuffer((byte *)xmlContents.c_str(), xmlContents.size()))
+	if (!parser.loadBuffer((const byte *)xmlContents.c_str(), xmlContents.size()))
 		error("Couldn't load inventory xml.");
 	if (!parser.parse())
 		error("Couldn't parse inventory xml.");
diff --git a/engines/tetraedge/game/inventory_objects_xml_parser.cpp b/engines/tetraedge/game/inventory_objects_xml_parser.cpp
index 7467dc6e490..1e366da6817 100644
--- a/engines/tetraedge/game/inventory_objects_xml_parser.cpp
+++ b/engines/tetraedge/game/inventory_objects_xml_parser.cpp
@@ -30,6 +30,6 @@ bool InventoryObjectsXmlParser::parserCallback_Object(ParserNode *node) {
 	data._isDocument = node->values.contains("isDocument");
 	_objects.setVal(data._id, data);
 	return true;
-};
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 4a927e8fdf1..a469948ccdf 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -790,6 +790,85 @@ static int tolua_ExportedFunctions_SetObjectFrames00(lua_State *L) {
 	error("#ferror in function 'SetObjectFrames': %d %d %s", err.index, err.array, err.type);
 }
 
+static bool CurrentCharacterAnimation(const Common::String &charname, const Common::String &anim) {
+	Character *character = g_engine->getGame()->scene().character(charname);
+
+	if (!character) {
+		debug("[CurrentCharacterAnimation] Character\'s\"%s\" doesn't exist", charname.c_str());
+		return true;
+	} else {
+		return anim == character->curAnimName();
+	}
+}
+
+static int tolua_ExportedFunctions_CurrentCharacterAnimation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		bool result = CurrentCharacterAnimation(s1, s2);
+		tolua_pushboolean(L, result);
+		return 1;
+	}
+	error("#ferror in function 'CurrentCharacterAnimation': %d %d %s", err.index, err.array, err.type);
+}
+
+static void MoveCharacterPlayerTo(float x, float y, float z, bool walkFlag) {
+	Game *game = g_engine->getGame();
+	if (game->_movePlayerCharacterDisabled)
+		return;
+
+	TeVector3f32 loc(x, y, z);
+	game->resetPreviousMousePos();
+
+	Character *character = game->scene()._character;
+	if (loc == game->posPlayer() && character->walkModeStr() == "Walk")
+		return;
+
+	if (game->walkTimer().running() && game->walkTimer().timeElapsed() < 300000) {
+		unsigned long elapsed = game->walkTimer().timeElapsed();
+		game->walkTimer().stop();
+		if (elapsed < 300000) {
+			character->walkMode("Jog");
+		}
+	} else {
+		game->walkTimer().stop();
+		game->walkTimer().start();
+		character->walkMode("Walk");
+	}
+
+	game->_sceneCharacterVisibleFromLoad = false;
+	TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(character->_model->position(), loc);
+	if (!curve) {
+		game->luaScript().execute("OnDisplacementFinished");
+	} else {
+		game->scene().setCurve(curve);
+		character->setCurveStartLocation(TeVector3f32(0, 0, 0));
+		character->placeOnCurve(curve);
+		character->setCurveOffset(0);
+		character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
+		character->walkTo(1.0, walkFlag);
+		game->_isCharacterWalking = true;
+		game->setPosPlayer(loc);
+	}
+}
+
+static int tolua_ExportedFunctions_MoveCharacterPlayerTo00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isboolean(L, 4, 1, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		float f1 = tolua_tonumber(L, 1, 0.0);
+		float f2 = tolua_tonumber(L, 2, 0.0);
+		float f3 = tolua_tonumber(L, 3, 0.0);
+		bool b1 = tolua_toboolean(L, 4, 0);
+		MoveCharacterPlayerTo(f1, f2, f3, b1);
+		return 0;
+	}
+	error("#ferror in function 'MoveCharacterPlayerTo': %d %d %s", err.index, err.array, err.type);
+}
+
+
 // ////////////////////////////////////////////////////////////////////////
 
 
@@ -860,9 +939,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "GetZPositionCharacter", tolua_ExportedFunctions_GetZPositionCharacter00);
 	tolua_function(L, "MoveCharacterTo", tolua_ExportedFunctions_MoveCharacterTo00);
 	tolua_function(L, "MoveCharacterToAndWaitForEnd",
-				 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);
+				 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);*/
 	tolua_function(L, "MoveCharacterPlayerTo", tolua_ExportedFunctions_MoveCharacterPlayerTo00);
-	tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
+	/*tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
 				 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00);
 	tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00);*/
 	tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
@@ -874,8 +953,8 @@ void LuaOpenBinds(lua_State *L) {
 				 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);
 	tolua_function(L, "BlendCharacterAnimation", tolua_ExportedFunctions_BlendCharacterAnimation00);
 	tolua_function(L, "BlendCharacterAnimationAndWaitForEnd",
-				 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);
-	tolua_function(L, "CurrentCharacterAnimation", tolua_ExportedFunctions_CurrentCharacterAnimation00);*/
+				 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);*/
+	tolua_function(L, "CurrentCharacterAnimation", tolua_ExportedFunctions_CurrentCharacterAnimation00);
 	tolua_function(L, "SetCharacterPlayerVisible", tolua_ExportedFunctions_SetCharacterPlayerVisible00);
 	tolua_function(L, "MoveCharacterPlayerDisabled",
 				 tolua_ExportedFunctions_MoveCharacterPlayerDisabled00);
diff --git a/engines/tetraedge/game/lua_binds.h b/engines/tetraedge/game/lua_binds.h
index c22f39eeeeb..9f6b99a82af 100644
--- a/engines/tetraedge/game/lua_binds.h
+++ b/engines/tetraedge/game/lua_binds.h
@@ -30,7 +30,7 @@ namespace LuaBinds {
 
 void LuaOpenBinds(lua_State *L);
 
-};
+} // end namespace LuaBinds
 
 } // end namespace Tetraedge
 
diff --git a/engines/tetraedge/te/micropather.h b/engines/tetraedge/te/micropather.h
index 493c035b583..fa4fe3052ec 100644
--- a/engines/tetraedge/te/micropather.h
+++ b/engines/tetraedge/te/micropather.h
@@ -453,9 +453,9 @@ namespace micropather
 		PathCache* pathCache;
 	};
 
-};	// namespace micropather
+}	// namespace micropather
 
-};	// namespace Tetraedge
+}	// namespace Tetraedge
 
 #endif // TETRAEDGE_TE_MICROPATHER
 
diff --git a/engines/tetraedge/te/te_bezier_curve.cpp b/engines/tetraedge/te/te_bezier_curve.cpp
index 2985bcb6ee6..18f15a71dd5 100644
--- a/engines/tetraedge/te/te_bezier_curve.cpp
+++ b/engines/tetraedge/te/te_bezier_curve.cpp
@@ -77,7 +77,44 @@ float TeBezierCurve::rawLength() {
 	return _rawLength;
 }
 
-TeVector3f32 TeBezierCurve::retrievePoint(float offset) const {
+TeVector3f32 TeBezierCurve::retrievePoint(float offset) {
+	const unsigned int npoints = _controlPoints.size();
+
+	// Simple cases for small numbers of points.
+	if (npoints == 0)
+		return TeVector3f32();
+	else if (npoints == 1)
+		return _controlPoints[0];
+	else if (npoints == 2)
+		return _controlPoints[0] + (_controlPoints[1] - _controlPoints[0]) * offset;
+
+	// else, there are at least 3 points so need to actually interpolate.
+	TeVector3f32 points[5];
+	const float rawlen = rawLength();
+
+	float proportion = 0.0f;
+	unsigned int i = 0;
+	while (i < npoints) {
+		proportion = _rawLengths[i] / rawlen;
+		if (proportion >= offset)
+			break;
+		i++;
+	}
+	float t;
+	if (proportion == offset) {
+		t = 0.0f;
+	} else {
+		float p1 = _rawLengths[i - 1];
+		float p2 = _rawLengths[i];
+		t = (rawlen * offset - p1) / (p2 - p1);
+		i--;
+	}
+
+	for (unsigned int p = -1; p < 3; p++) {
+		
+	}
+	// TODO: Finish this, line 77 to 129.
+
 	error("TODO: Implement TeBezierCurve::retrievePoint");
 }
 
@@ -115,4 +152,24 @@ void TeBezierCurve::deserialize(Common::ReadStream &stream, TeBezierCurve &curve
 	}
 }
 
+/*static*/
+TeVector3f32 TeBezierCurve::hermiteInterpolate(float t, const TeVector3f32 *points, float param_4, float param_5) {
+	assert(points);
+	const TeVector3f32 delta1 =  ((points[1] - points[0]) * (param_5 + 1.0) * (1.0 - param_4)) / 2.0;
+	const TeVector3f32 delta2a = ((points[2] - points[1]) * (1.0 - param_5) * (1.0 - param_4)) / 2.0;
+	const TeVector3f32 delta2b = ((points[2] - points[1]) * (param_5 + 1.0) * (1.0 - param_4)) / 2.0;
+	const TeVector3f32 delta3  = ((points[3] - points[2]) * (1.0 - param_5) * (1.0 - param_4)) / 2.0;
+
+	const TeVector3f32 x1 = delta1 + delta2a;
+	const TeVector3f32 x2 = delta2b + delta3;
+
+	const float t2 = t * t;
+	const float t3 = t * t * t;
+	const TeVector3f32 h1a = points[1] * ((t3 + t3) - t2 * 3.0);
+	const TeVector3f32 h1b = x1 * ((t3 - (t2 + t2)) + t);
+	const TeVector3f32 h1 = (h1a + h1b) + (x2 * (t3 - t2));
+	return h1 + (points[2] * (t3 * -2.0 + t2 * 3.0));
+}
+
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_bezier_curve.h b/engines/tetraedge/te/te_bezier_curve.h
index 9b201c13f50..de0ca32642b 100644
--- a/engines/tetraedge/te/te_bezier_curve.h
+++ b/engines/tetraedge/te/te_bezier_curve.h
@@ -40,10 +40,12 @@ public:
 
 	float rawLength();
 
-	TeVector3f32 retrievePoint(float offset) const;
+	TeVector3f32 retrievePoint(float offset);
 	void setControlPoints(const Common::Array<TeVector3f32> &points);
 	void setNbIterations(unsigned long iterations);
 
+	static TeVector3f32 hermiteInterpolate(float param_2, const TeVector3f32 *points, float param_4, float param_5);
+
 	static void serialize(Common::WriteStream &stream, const TeBezierCurve &curve);
 	static void deserialize(Common::ReadStream &stream, TeBezierCurve &curve);
 
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index f0235e3492c..b19681a6b30 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -40,7 +40,7 @@ namespace Tetraedge {
 }
 
 TeButtonLayout::TeButtonLayout() :
-_currentState(BUTTON_STATE_UP), _clickPassThrough(false), _validationSoundVolume(1.0),
+_currentState(BUTTON_STATE_UP), _clickPassThrough(true), _validationSoundVolume(1.0),
 _someClickFlag(false), _doubleValidationProtectionEnabled(true), _upLayout(nullptr),
 _downLayout(nullptr), _rolloverLayout(nullptr), _disabledLayout(nullptr),
 _hitZoneLayout(nullptr)
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 53d0779ba9d..c49aa2c4fef 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -24,6 +24,7 @@
 #include "tetraedge/te/te_free_move_zone.h"
 #include "tetraedge/te/micropather.h"
 #include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_ray_intersection.h"
 
 namespace Tetraedge {
 
@@ -51,7 +52,8 @@ class TeFreeMoveZoneGraph : micropather::Graph {
 
 
 TeFreeMoveZone::TeFreeMoveZone() : _actzones(nullptr), _blockers(nullptr), _rectBlockers(nullptr),
-_transformedVerticiesDirty(true), _bordersDirty(true), _pickMeshDirty(true), _projectedPointsDirty(true)
+_transformedVerticiesDirty(true), _bordersDirty(true), _pickMeshDirty(true), _projectedPointsDirty(true),
+_loadedFromBin(false)
 {
 	_graph = new TeFreeMoveZoneGraph();
 	_graph->_bordersDistance = 2048.0f;
@@ -112,12 +114,52 @@ TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, b
 	return intersectPoint;
 }
 
-TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4, float param_5, bool findMeshFlag) {
+TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt, const TeVector2s32 &endpt, float param_5, bool findMeshFlag) {
+	updateGrid(false);
 	error("TODO: Implement TeFreeMoveZone::curve");
 }
 
-TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &param_3, const TeVector3f32 &param_4) {
-	error("TODO: Implement TeFreeMoveZone::curve");
+TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt, const TeVector3f32 &endpt) {
+	updateGrid(false);
+	Common::Array<TeVector3f32> points;
+	points.push_back(startpt);
+	points.push_back(endpt);
+
+	TeVector2s32 projectedStart = projectOnAStarGrid(startpt);
+	TeVector2s32 projectedEnd = projectOnAStarGrid(endpt);
+	int xsize = _graph->_size._x;
+	float cost = 0;
+	// Passing an int to void*, yuck? but it's what the original does..
+	Common::Array<void *> path;
+	int pathResult = _micropather->Solve((void *)(xsize * projectedStart._y + projectedStart._x), (void *)(xsize * projectedEnd._y + projectedEnd._x), &path, &cost);
+
+	TeIntrusivePtr<TeBezierCurve> retval;
+
+	if (pathResult == micropather::MicroPather::SOLVED || pathResult == micropather::MicroPather::START_END_SAME) {
+		Common::Array<TeVector2s32> points;
+		points.resize(path.size());
+
+		for (auto &pathpt : path) {
+			// each path point is an array offset
+			int offset = static_cast<int>(reinterpret_cast<long>(pathpt));
+			int yoff = (offset / _graph->_size._x);
+			int xoff = offset % _graph->_size._x;
+			points.push_back(TeVector2s32(xoff, yoff));
+		}
+
+		Common::Array<TeVector3f32> pts3d;
+		for (auto &pt : points) {
+			pts3d.push_back(transformAStarGridInWorldSpace(pt));
+		}
+
+		pts3d.front() = startpt;
+		pts3d.back() = endpt;
+		removeInsignificantPoints(pts3d);
+		retval = new TeBezierCurve();
+		retval->setControlPoints(pts3d);
+	}
+
+	return retval;
 }
 
 /*static*/
@@ -191,12 +233,20 @@ void TeFreeMoveZone::preUpdateGrid() {
 	error("TODO: Implement TeFreeMoveZone::preUpdateGrid");
 }
 
-TeVector3f32 TeFreeMoveZone::projectOnAStarGrid(const TeVector3f32 &pt) {
-	error("TODO: Implement TeFreeMoveZone::projectOnAStarGrid");
+TeVector2s32 TeFreeMoveZone::projectOnAStarGrid(const TeVector3f32 &pt) {
+	TeVector2f32 otherpt;
+	if (!_loadedFromBin) {
+		otherpt = TeVector2f32(pt.x() - _someGridVec1.getX(), pt.z() - _someGridVec1.getY());
+	} else {
+		error("TODO: Implement TeFreeMoveZone::projectOnAStarGrid for _loadedFromBin");
+	}
+	TeVector2f32 projected = otherpt / _gridOffsetSomething;
+	return TeVector2s32((int)projected.getX(), (int)projected.getY());
 }
 
-Common::Array<TeVector3f32> &TeFreeMoveZone::removeInsignificantPoints(const Common::Array<TeVector3f32> &points) {
-	error("TODO: Implement TeFreeMoveZone::removeInsignificantPoints");
+Common::Array<TeVector3f32> TeFreeMoveZone::removeInsignificantPoints(const Common::Array<TeVector3f32> &points) {
+	warning("TODO: Implement TeFreeMoveZone::removeInsignificantPoints");
+	return points;
 }
 
 void TeFreeMoveZone::setBordersDistance(float dist) {
@@ -238,7 +288,14 @@ void TeFreeMoveZone::setVertex(unsigned int offset, const TeVector3f32 &vertex)
 	_projectedPointsDirty = true;
 }
 
-TeVector2s32 TeFreeMoveZone::transformAStarGridInWorldSpace(const TeVector2s32 &gridpt) {
+TeVector3f32 TeFreeMoveZone::transformAStarGridInWorldSpace(const TeVector2s32 &gridpt) {
+	float offsety = (float)gridpt._y * _gridOffsetSomething.getY() + _someGridVec1.getY() +
+				_gridOffsetSomething.getY() * 0.5;
+	float offsetx = (float)gridpt._x * _gridOffsetSomething.getX() + _someGridVec1.getX() +
+				_gridOffsetSomething.getX() * 0.5;
+	if (!_loadedFromBin) {
+		return TeVector3f32(offsetx, _someGridFloat, offsety);
+	}
 	error("TODO: Implement TeFreeMoveZone::transformAStarGridInWorldSpace");
 }
 
@@ -289,12 +346,58 @@ void TeFreeMoveZone::updateTransformedVertices() {
 
 /*========*/
 
-float TeFreeMoveZoneGraph::LeastCostEstimate(void * stateStart, void *stateEnd) {
-	error("TODO: Implement TeFreeMoveZone::TeFreeMoveZoneGraph::LeastCostEstimate");
+float TeFreeMoveZoneGraph::LeastCostEstimate(void *stateStart, void *stateEnd) {
+	int startInt = static_cast<int>(reinterpret_cast<long>(stateStart));
+	int endInt = static_cast<int>(reinterpret_cast<long>(stateEnd));
+	int starty = startInt / _size._x;
+	int endy = endInt / _size._x;
+	TeVector2s32 start(startInt - starty * _size._x, starty);
+	TeVector2s32 end(endInt - endy * _size._x, endy);
+	return (end - start).squaredLength();
 }
 
 void TeFreeMoveZoneGraph::AdjacentCost(void *state, Common::Array<micropather::StateCost> *adjacent) {
-	error("TODO: Implement TeFreeMoveZone::TeFreeMoveZoneGraph::AdjacentCost");
+	int stateInt = static_cast<int>(reinterpret_cast<long>(state));
+	int stateY = stateInt / _size._x;
+	const TeVector2s32 statept(stateInt - stateY * _size._x, stateY);
+
+	micropather::StateCost cost;
+	TeVector2s32 pt;
+
+	pt = TeVector2s32(statept._x - 1, statept._y);
+	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
+	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	adjacent->push_back(cost);
+
+	pt = TeVector2s32(statept._x - 1, statept._y + 1);
+	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
+	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	adjacent->push_back(cost);
+
+	pt = TeVector2s32(statept._x + 1, statept._y + 1);
+	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
+	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	adjacent->push_back(cost);
+
+	pt = TeVector2s32(statept._x + 1, statept._y);
+	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
+	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	adjacent->push_back(cost);
+
+	pt = TeVector2s32(statept._x + 1, statept._y - 1);
+	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
+	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	adjacent->push_back(cost);
+
+	pt = TeVector2s32(statept._x, statept._y - 1);
+	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
+	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	adjacent->push_back(cost);
+
+	pt = TeVector2s32(statept._x - 1, statept._y - 1);
+	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
+	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	adjacent->push_back(cost);
 }
 
 void TeFreeMoveZoneGraph::PrintStateInfo(void *state) {
@@ -329,5 +432,56 @@ void TeFreeMoveZoneGraph::serialize(Common::WriteStream &stream) const {
 	error("TODO: Implement TeFreeMoveZoneGraph::serialize");
 }
 
+/*static*/
+TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, const TeVector2s32 &frompt,
+			Common::Array<TePickMesh2*> &pickMeshes, TeVector3f32 *outloc, bool lastHitFirst) {
+	TeVector3f32 locresult;
+	TePickMesh2 *nearest = nullptr;
+	float furthest = camera->_orthFarVal;
+	if (!pickMeshes.empty()) {
+		TeVector3f32 v1;
+		TeVector3f32 v2;
+		for (unsigned int i = 0; i < pickMeshes.size(); i++) {
+			TePickMesh2 *mesh = pickMeshes[i];
+			const TeMatrix4x4 transform = mesh->worldTransformationMatrix();
+			if (lastHitFirst) {
+				unsigned int tricount = mesh->verticies().size() / 3;
+				unsigned int vert = mesh->lastTriangleHit() * 3;
+				if (mesh->lastTriangleHit() >= tricount)
+					vert = 0;
+				const TeVector3f32 v3 = transform * mesh->verticies()[vert];
+				const TeVector3f32 v4 = transform * mesh->verticies()[vert + 1];
+				const TeVector3f32 v5 = transform * mesh->verticies()[vert + 2];
+				TeVector3f32 result;
+				float fresult;
+				int intresult = TeRayIntersection::intersect(v1, v2, v3, v4, v5, result, fresult);
+				if (intresult == 1 && fresult < furthest && fresult >= camera->_orthNearVal)
+					return mesh;
+			}
+			for (unsigned int tri = 0; tri < mesh->verticies().size() / 3; tri++) {
+				const TeVector3f32 v3 = transform * mesh->verticies()[tri * 3];
+				const TeVector3f32 v4 = transform * mesh->verticies()[tri * 3 + 1];
+				const TeVector3f32 v5 = transform * mesh->verticies()[tri * 3 + 1];
+				camera->getRay(frompt, v1, v2);
+				TeVector3f32 result;
+				float fresult;
+				int intresult = TeRayIntersection::intersect(v1, v2, v3, v4, v5, result, fresult);
+				if (intresult == 1 && fresult < furthest && fresult >= camera->_orthNearVal) {
+					mesh->setLastTriangleHit(tri);
+					locresult = result;
+					furthest = fresult;
+					nearest = mesh;
+					if (lastHitFirst)
+						break;
+				}
+			}
+		}
+	}
+	if (outloc) {
+		*outloc = locresult;
+	}
+	return nearest;
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index 74daafb8d23..706cc679516 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -71,8 +71,8 @@ public:
 	Common::Array<TeVector3f32> collisions(const TeVector3f32 &v1, const TeVector3f32 &v2);
 	TeVector3f32 correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool f);
 
-	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &param_3, const TeVector2s32 &param_4, float param_5, bool findMeshFlag);
-	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &param_3, const TeVector3f32 &param_4);
+	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &startpt, const TeVector2s32 &endpt, float param_5, bool findMeshFlag);
+	TeIntrusivePtr<TeBezierCurve> curve(const TeVector3f32 &startpt, const TeVector3f32 &endpt);
 
 	void draw() override;
 	TeVector3f32 findNearestPointOnBorder(const TeVector2f32 &pt);
@@ -90,14 +90,14 @@ public:
 
 	bool onViewportChanged();
 	void preUpdateGrid();
-	TeVector3f32 projectOnAStarGrid(const TeVector3f32 &pt);
-	Common::Array<TeVector3f32> &removeInsignificantPoints(const Common::Array<TeVector3f32> &points);
+	TeVector2s32 projectOnAStarGrid(const TeVector3f32 &pt);
+	Common::Array<TeVector3f32> removeInsignificantPoints(const Common::Array<TeVector3f32> &points);
 	void setBordersDistance(float dist);
 	void setCamera(TeIntrusivePtr<TeCamera> &cam, bool noRecalcProjPoints);
 	void setNbTriangles(unsigned int len);
 	void setPathFindingOccluder(const TeOBP &occluder);
 	void setVertex(unsigned int offset, const TeVector3f32 &vertex);
-	TeVector2s32 transformAStarGridInWorldSpace(const TeVector2s32 &gridpt);
+	TeVector3f32 transformAStarGridInWorldSpace(const TeVector2s32 &gridpt);
 	float transformHeightMin(float minval);
 	TeVector3f32 transformVectorInWorldSpace(float param_3,float param_4);
 	void updateBorders();
@@ -111,6 +111,9 @@ public:
                Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones);
 	static void serialize(Common::WriteStream &stream, const TeFreeMoveZone &src, bool updateFirst);
 
+	static TePickMesh2 *findNearestMesh(TeIntrusivePtr<TeCamera> &camera, const TeVector2s32 &frompt,
+			Common::Array<TePickMesh2*> &pickMeshes, TeVector3f32 *outloc, bool lastHitFirst);
+
 private:
 	Common::Array<TeActZone> *_actzones;
 	Common::Array<TeBlocker> *_blockers;
@@ -132,12 +135,15 @@ private:
 	TeIntrusivePtr<TeCamera> _camera;
 	//static TeIntrusivePtr<TeCamera> _globalCamera;
 
-	bool _gridDirty;
 	TeFreeMoveZoneGraph *_graph;
+
+	bool _loadedFromBin;
+	bool _gridDirty;
 	bool _transformedVerticiesDirty;
 	bool _bordersDirty;
 	bool _pickMeshDirty;
 	bool _projectedPointsDirty;
+
 	micropather::MicroPather *_micropather;
 	TeTimer _updateTimer;
 };
diff --git a/engines/tetraedge/te/te_images_sequence.cpp b/engines/tetraedge/te/te_images_sequence.cpp
index 5b991656697..3f90c9977dc 100644
--- a/engines/tetraedge/te/te_images_sequence.cpp
+++ b/engines/tetraedge/te/te_images_sequence.cpp
@@ -68,8 +68,8 @@ bool TeImagesSequence::load(const Common::Path &path) {
 		Common::Path filePath(filePathStr);
 		Common::String fname = filePath.getLastComponent().toString();
 		Common::String fstart = fname.substr(0, fname.size() - 7);
-		unsigned int frameno = 0;
-		unsigned int fps = 0;
+		int frameno = 0;
+		int fps = 0;
 		if (sscanf(fstart.c_str(), "%d-%d", &frameno, &fps) != 2) {
 			warning("TeImagesSequence::load can't match %s", fname.c_str());
 			continue;
diff --git a/engines/tetraedge/te/te_images_sequence.h b/engines/tetraedge/te/te_images_sequence.h
index e8f4b386adb..1f0a72bd3d2 100644
--- a/engines/tetraedge/te/te_images_sequence.h
+++ b/engines/tetraedge/te/te_images_sequence.h
@@ -27,7 +27,7 @@
 
 namespace Graphics {
 struct Surface;
-};
+}
 
 namespace Tetraedge {
 
diff --git a/engines/tetraedge/te/te_jpeg.h b/engines/tetraedge/te/te_jpeg.h
index 72e98f767ad..6627ca3066b 100644
--- a/engines/tetraedge/te/te_jpeg.h
+++ b/engines/tetraedge/te/te_jpeg.h
@@ -27,7 +27,7 @@
 
 namespace Graphics {
 struct Surface;
-};
+}
 
 namespace Tetraedge {
 
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 5e5290e066f..195bd2a52a3 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -102,7 +102,7 @@ void TeModel::draw() {
 		renderer->sendModelMatrix(transform);
 		renderer->pushMatrix();
 		renderer->multiplyMatrix(transform);
-		if (name() == "Kate") {
+		/*if (name() == "Kate") {
 			debug("Draw model %p (%s, %d meshes)", this, name().empty() ? "no name" : name().c_str(), _meshes.size());
 			//adebug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
 			//debug("   position   %s", position().dump().c_str());
@@ -111,7 +111,7 @@ void TeModel::draw() {
 			//debug("   worldScale %s", worldScale().dump().c_str());
 			//debug("   rotation   %s", rotation().dump().c_str());
 			debug("   worldRot   %s", worldRotation().dump().c_str());
-		}
+		}*/
 		for (TeMesh &mesh : _meshes) {
 			// TODO: Set some flag (_drawWires?) in mesh to this->field_0x158??
 			mesh.draw();
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index e28a7a1663d..8916cf99cc3 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/util.h"
+#include "common/math.h"
 
 #include "tetraedge/tetraedge.h"
 
diff --git a/engines/tetraedge/te/te_png.h b/engines/tetraedge/te/te_png.h
index 18462b2c328..490a3c22167 100644
--- a/engines/tetraedge/te/te_png.h
+++ b/engines/tetraedge/te/te_png.h
@@ -27,7 +27,7 @@
 
 namespace Graphics {
 struct Surface;
-};
+}
 
 namespace Tetraedge {
 
diff --git a/engines/tetraedge/te/te_vector2s32.h b/engines/tetraedge/te/te_vector2s32.h
index ecaa6e05e08..ca0fdf449c6 100644
--- a/engines/tetraedge/te/te_vector2s32.h
+++ b/engines/tetraedge/te/te_vector2s32.h
@@ -46,6 +46,14 @@ public:
 		return *this;
 	}
 
+	TeVector2s32 operator-(const TeVector2s32 &other) {
+		return TeVector2s32(_x - other._x, _y - other._y);
+	}
+
+	long squaredLength() const {
+		return _x * _x + _y * _y;
+	}
+
 	static void deserialize(Common::ReadStream &stream, TeVector2s32 &dest);
 
 public:
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index d0007ee22e4..2b7329ad40b 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -127,7 +127,7 @@ Common::String TetraedgeEngine::getGameId() const {
 
 void TetraedgeEngine::configureSearchPaths() {
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
-	SearchMan.addSubDirectoryMatching(gameDataDir, "Resources", 0, 4);
+	SearchMan.addSubDirectoryMatching(gameDataDir, "Resources", 0, 5);
 }
 
 int TetraedgeEngine::getDefaultScreenWidth() const {
diff --git a/engines/tetraedge/to_lua.cpp b/engines/tetraedge/to_lua.cpp
index a9ddd9c486c..31280d99aaf 100644
--- a/engines/tetraedge/to_lua.cpp
+++ b/engines/tetraedge/to_lua.cpp
@@ -434,6 +434,10 @@ int tolua_toboolean(lua_State *L, int narg, int def) {
 	return lua_gettop(L) < abs(narg) ?  def : lua_toboolean(L,narg);
 }
 
+void tolua_pushboolean(lua_State *L, bool val) {
+	lua_pushboolean(L, val);
+}
+
 } // end namespace ToLua
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/to_lua.h b/engines/tetraedge/to_lua.h
index 842838d97dd..0cee065adbf 100644
--- a/engines/tetraedge/to_lua.h
+++ b/engines/tetraedge/to_lua.h
@@ -55,17 +55,19 @@ void tolua_open(lua_State *L);
 void tolua_module (lua_State *L, const char *name, int hasvar);
 void tolua_beginmodule (lua_State *L, const char *name);
 void tolua_endmodule(lua_State *L);
-void tolua_function (lua_State *L, const char *name, lua_CFunction func);
+void tolua_function(lua_State *L, const char *name, lua_CFunction func);
 
-int tolua_isboolean (lua_State *L, int lo, int def, tolua_Error *err);
-int tolua_isnoobj (lua_State *L, int lo, tolua_Error *err);
-int tolua_isnumber (lua_State *L, int lo, int def, tolua_Error *err);
-int tolua_isstring (lua_State *L, int lo, int def, tolua_Error *err);
+int tolua_isboolean(lua_State *L, int lo, int def, tolua_Error *err);
+int tolua_isnoobj(lua_State *L, int lo, tolua_Error *err);
+int tolua_isnumber(lua_State *L, int lo, int def, tolua_Error *err);
+int tolua_isstring(lua_State *L, int lo, int def, tolua_Error *err);
 
-double tolua_tonumber (lua_State *L, int narg, double def);
-const char* tolua_tostring (lua_State *L, int narg, const char *def);
-void* tolua_tousertype (lua_State *L, int narg, void *def);
-int tolua_toboolean (lua_State *L, int narg, int def);
+double tolua_tonumber(lua_State *L, int narg, double def);
+const char* tolua_tostring(lua_State *L, int narg, const char *def);
+void* tolua_tousertype(lua_State *L, int narg, void *def);
+int tolua_toboolean(lua_State *L, int narg, int def);
+
+void tolua_pushboolean(lua_State *L, bool val);
 
 } // end namespace ToLua
 


Commit: d28e9a2212ad6d4e139feb18d36ddfbc6d667c7f
    https://github.com/scummvm/scummvm/commit/d28e9a2212ad6d4e139feb18d36ddfbc6d667c7f
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: WIP, can almost walk around now.

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/te/te_bezier_curve.cpp
    engines/tetraedge/te/te_bezier_curve.h
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_model_animation.h
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_pick_mesh2.h
    engines/tetraedge/te/te_ray_intersection.cpp
    engines/tetraedge/te/te_resource.cpp
    engines/tetraedge/te/te_vector2f32.h


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index c182c34b365..190041788bd 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -23,6 +23,7 @@
 #include "common/path.h"
 #include "common/file.h"
 #include "common/debug.h"
+#include "common/util.h"
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/character.h"
@@ -61,7 +62,9 @@ _notWalkAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"),
 _needsSomeUpdate(false), _positionFlag(false), _lookingAtTallThing(false),
 _stepSound1("sounds/SFX/PAS_H_BOIS1.ogg"), _stepSound2("sounds/SFX/PAS_H_BOIS2.ogg"),
 _freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(nullptr),
-_recallageY(true) {
+_recallageY(true), _walkToFlag(false), _walkCurveEnd(0.0f), _walkCurveStart(0.0f),
+_walkCurveLen(0.0f), _walkCurveIncrement(0.0f), _walkEndAnimG(false), _walkTotalFrames(0),
+_walkCurveCurOffset(0.0f) {
 	_curModelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
 }
 
@@ -570,6 +573,7 @@ bool Character::setAnimation(const Common::String &aname, bool repeat, bool para
 	}
 
 	_curModelAnim = animCacheLoad(animPath);
+	_curModelAnim->reset();
 	_curModelAnim->onFinished().add(this, &Character::onModelAnimationFinished);
 	_curModelAnim->bind(_model);
 	_curModelAnim->setFrameLimits(startFrame, endFrame);
@@ -609,9 +613,39 @@ bool Character::setShadowVisible(bool visible) {
 	return false;
 }
 
-float Character::speedFromAnim(double movepercent) {
-	error("TODO: Implement Character::speedFromAnim");
-	return 0;
+float Character::speedFromAnim(double msFromStart) {
+	if (!_model)
+		return 0.0f;
+
+	TeIntrusivePtr<TeModelAnimation> modelAnim;
+	if (_model->_boneBlenders.empty()) {
+		modelAnim = _model->anim();
+	} else {
+		modelAnim = _model->_boneBlenders.back()->_anim;
+	}
+
+	if (!modelAnim)
+		return 0.0f;
+
+	const int pereBone = modelAnim->findBone("Pere");
+	int curFrame = modelAnim->calcCurrentFrame(msFromStart);
+
+	float result;
+	if (_lastFrame == -1) {
+		const TeVector3f32 nowvec = translationVectorFromAnim(*modelAnim, pereBone, 0);
+		const TeVector3f32 lastvec = translationVectorFromAnim(*modelAnim, pereBone, 1);
+		result = lastvec.z() - nowvec.z();
+	} else {
+		const TeVector3f32 nowvec = translationVectorFromAnim(*modelAnim, pereBone, curFrame);
+		const TeVector3f32 lastvec = translationVectorFromAnim(*modelAnim, pereBone, _lastFrame);
+		result = nowvec.z() - lastvec.z();
+		if (curFrame < _lastFrame) {
+			result += animLength(*modelAnim, pereBone, 9999);
+		}
+	}
+	_lastFrame = curFrame;
+	result *= _model->scale().z();
+	return result;
 }
 
 float Character::translationFromAnim(const TeModelAnimation &anim, long bone, long param_3) {
@@ -630,12 +664,116 @@ TeTRS Character::trsFromAnim(const TeModelAnimation &anim, long bone, long frame
 	return anim.getTRS(bone, frame, false);
 }
 
-void Character::update(double percentval) {
+void Character::update(double msFromStart) {
 	if (!_curve || !_runTimer.running())
 		return;
-	//float speed = speedFromAnim(percentval);
 
-	error("TODO: Implement Character::update");
+	_walkCurveCurOffset = speedFromAnim(msFromStart) * _walkCurveIncrement + _walkCurveCurOffset;
+
+	if (_curve->controlPoints().size() < 2) {
+		blendAnimation(_characterSettings._walkFileName, 0.0667, true, false);
+		endMove();
+		return;
+	}
+
+	const float baseAngle = (_curveOffset > _walkCurveEnd ? M_PI : 0);
+	const float sign = (_curveOffset > _walkCurveEnd ? -1 : 1);
+	updatePosition(_walkCurveStart);
+	const TeVector3f32 modelpos = _model->position();
+
+	float lastWalkedLength = _walkedLength;
+	TeVector3f32 lastNextPos = modelpos;
+	TeVector3f32 nextPos = modelpos;
+	TeVector3f32 newPos = modelpos;
+	float lastOffset = _walkCurveStart;
+
+	float endOffset = _walkCurveStart;
+	while (_walkedLength < _walkCurveCurOffset) {
+		lastNextPos = nextPos;
+		float nextOffset = (4.0 / _curve->numIterations() * sign + lastOffset);
+		float offset = CLIP(nextOffset, 0.0f, 1.0f);
+
+		newPos = _curve->retrievePoint(offset) + _curveStartLocation;
+		const TeVector2f32 dist = TeVector2f32(nextPos.x(), nextPos.z()) - TeVector2f32(newPos.x(), newPos.z());
+
+		lastWalkedLength = _walkedLength;
+		_walkedLength += dist.length();
+
+		nextPos = newPos;
+		endOffset = lastOffset;
+		if (offset == 1.0 || offset == 0.0)
+			break;
+		lastOffset = offset;
+	}
+
+	_walkedLength = lastWalkedLength;
+	nextPos = lastNextPos;
+
+	while (_walkedLength < _walkCurveCurOffset) {
+		float nextOffset = (1.0 / _curve->numIterations()) * sign + endOffset;
+		endOffset = CLIP(nextOffset, 0.0f, 1.0f);
+
+		newPos = _curve->retrievePoint(endOffset) + _curveStartLocation;
+		const TeVector2f32 dist = TeVector2f32(nextPos.x(), nextPos.z()) - TeVector2f32(newPos.x(), newPos.z());
+		_walkedLength += dist.length();
+		nextPos = newPos;
+		if (endOffset == 1.0 || endOffset == 0.0)
+			break;
+	}
+
+	if (_freeMoveZone) {
+		bool correctflag;
+		newPos = _freeMoveZone->correctCharacterPosition(newPos, &correctflag, true);
+	}
+
+	_walkCurveStart = endOffset;
+	_model->setPosition(newPos);
+
+	TeVector3f32 t1;
+	TeVector3f32 t2;
+	_curve->pseudoTangent(endOffset, t1, t2);
+	const TeVector3f32 normalizedTangent = (t2 - t1).getNormalized();
+	float angle = TeVector3f32(0.0, 0.0, 1.0).dotProduct(normalizedTangent);
+	TeVector3f32 crossprod = TeVector3f32::crossProduct(TeVector3f32(0.0, 0.0, 1.0), normalizedTangent);
+	angle = acos(angle);
+	if (crossprod.y() >= 0.0f) {
+		angle = -angle;
+	}
+
+	TeQuaternion rot = TeQuaternion::fromAxisAndAngle(TeVector3f32(0.0, 1.0, 0.0), baseAngle + angle);
+	_model->setRotation(rot);
+
+	const Common::String endGAnim = walkAnim(WalkPart_EndG);
+	if (_walkCurveStart == _walkCurveEnd || fabs(_walkCurveEnd - _curveOffset) < fabs(_walkCurveStart - _curveOffset)) {
+		if (_walkToFlag) {
+			_walkToFlag = false;
+			endMove();
+		}
+		if (endGAnim.empty()) {
+			blendAnimation(_characterSettings._walkFileName, 0.0667, true, false);
+			endMove();
+		}
+	}
+
+	if (!endGAnim.empty() && _curAnimName == walkAnim(WalkPart_Loop) &&
+		   ((_curModelAnim->speed() * (msFromStart / 1000.0)) >= _walkTotalFrames)) {
+		if (_walkToFlag) {
+		  _walkToFlag = false;
+		  endMove();
+		} else {
+			if (_walkEndAnimG) {
+			  setAnimation(walkAnim(WalkPart_EndG), false);
+			} else {
+			  setAnimation(walkAnim(WalkPart_EndD), false);
+			}
+		}
+	}
+
+	// Note:
+	// The game does a bunch of extra things here (line 252 on)
+	// that seem to have no actual effect??
+
+	updateAnimFrame();
 }
 
 void Character::updateAnimFrame() {
@@ -674,7 +812,87 @@ void Character::walkMode(const Common::String &mode) {
 }
 
 void Character::walkTo(float curveEnd, bool walkFlag) {
-	error("TODO: Implement Character::walkTo");
+	_walkToFlag = walkFlag;
+	stop();
+	_walkCurveEnd = curveEnd;
+	_walkCurveStart = _curveOffset;
+	_walkCurveCurOffset = 0.0f;
+	_walkedLength = 0.0f;
+	const float f = (walkFlag ? _walkPart3AnimLen : 0);
+	if (_curve->controlPoints().size()) {
+		_walkCurveLen = _curve->length();
+		_walkEndAnimG = false;
+		const float f2 = ((_walkCurveLen - f) - _walkPart0AnimLen) / _walkPart1AnimLen;
+		float animLen;
+		if (f2 >= 0) {
+			Game *game = g_engine->getGame();
+			if (game->scene()._character == this && _walkModeStr == "Walk") {
+				int part1len = (int)(f2 * _walkPart1AnimFrameCount);
+				int repeats = part1len / _walkPart1AnimFrameCount;
+				uint remainder = part1len % _walkPart1AnimFrameCount;
+
+				uint framecounts[4];
+
+				if (repeats == 0)
+					framecounts[0] = UINT_MAX;
+				else
+					framecounts[0] = (repeats - 1) * _walkPart1AnimFrameCount + 29;
+
+				framecounts[1] = _walkPart1AnimFrameCount * repeats + 13;
+				framecounts[2] = _walkPart1AnimFrameCount * repeats + 29;
+				framecounts[3] = _walkPart1AnimFrameCount * (repeats + 1) + 13;
+
+				for (int i = 0; i < 4; i++) {
+					framecounts[i] = abs((int)(framecounts[i] - (int)(f2 * _walkPart1AnimFrameCount)));
+				}
+
+				int minoffset = 0;
+				for (int i = 0; i < 4; i++) {
+					if (framecounts[i] < framecounts[minoffset])
+						minoffset = i;
+				}
+
+				switch(minoffset) {
+				case 0:
+				  remainder = 29;
+				  _walkEndAnimG = true;
+				  repeats--;
+				  break;
+				case 1:
+				  remainder = 13;
+				  break;
+				case 2:
+				  remainder = 29;
+				  _walkEndAnimG = true;
+				  break;
+				case 3:
+				  remainder = 13;
+				  repeats++;
+				}
+				_walkTotalFrames = _walkPart1AnimFrameCount * repeats + _walkPart0AnimFrameCount + remainder;
+				animLen = _walkPart1AnimLen;
+				const float loopAnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &remainder, remainder);
+				_walkCurveIncrement = _walkCurveLen / (repeats * animLen + f + _walkPart0AnimLen + loopAnimLen);
+				play();
+				return; // NOTE: early return here.
+			} else {
+				double intpart;
+				double remainder = modf(f, &intpart);
+				if (remainder >= 0.5) {
+					_walkEndAnimG = true;
+					intpart += 0.75;
+				} else {
+					intpart += 0.25;
+				}
+				_walkTotalFrames = (int)(_walkPart1AnimFrameCount * intpart) + _walkPart0AnimFrameCount;
+				animLen = f + (float)_walkPart0AnimLen + intpart * _walkPart1AnimLen;
+			}
+		} else {
+			animLen = (float)(_walkPart0AnimLen + _walkPart3AnimLen);
+		}
+		_walkCurveIncrement = _walkCurveLen / animLen;
+	}
+	play();
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 6723305803b..71508f03637 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -132,7 +132,7 @@ public:
 	void setFreeMoveZone(TeFreeMoveZone *zone);
 	bool setShadowVisible(bool visible);
 	void setStepSound(const Common::String &stepSound1, const Common::String &stepSound2);
-	float speedFromAnim(double movepercent);
+	float speedFromAnim(double amount);
 	//void stop(); // just maps to TeAnimation::stop();
 	float translationFromAnim(const TeModelAnimation &anim, long bone, long frame);
 	TeVector3f32 translationVectorFromAnim(const TeModelAnimation &anim, long bone, long frame);
@@ -174,6 +174,15 @@ public:
 
 private:
 	float _curveOffset;
+	float _walkCurveStart;
+	float _walkCurveEnd;
+	float _walkCurveLen;
+	float _walkCurveIncrement;
+	float _walkCurveCurOffset;
+	float _walkedLength;
+	int _walkTotalFrames;
+	bool _walkToFlag;
+	bool _walkEndAnimG;
 	TeIntrusivePtr<TeBezierCurve> _curve;
 	TeVector3f32 _curveStartLocation;
 
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 8205097f4fc..9c06be767a0 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -47,7 +47,7 @@ namespace Tetraedge {
 Game::Game() : _objectsTakenVal(0), _score(0), _entered(false), _gameLoadState(0),
 _noScaleLayout(nullptr), _noScaleLayout2(nullptr), _warped(false), _saveRequested(false),
 _firstInventory(true), _movePlayerCharacterDisabled(false), _enteredFlag2(false),
-_luaShowOwnerError(false), _markersVisible(true), _running(false), _loadName("save.xml"),
+_luaShowOwnerError(false), _markersVisible(false), _running(false), _loadName("save.xml"),
 _randomSoundFinished(false), _randomSource("SyberiaGameRandom") {
 	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
 		_objectsTakenBits[i] = false;
@@ -1177,9 +1177,9 @@ void Game::playRandomSound(const Common::String &name) {
 	if (!_randomSounds.contains(name)) {
 		warning("Game::playRandomSound: can't find sound list %s", name.c_str());
 		return;
-    }
-    
-    if (!_randomSoundFinished) {
+	}
+
+	if (!_randomSoundFinished) {
 		_randomSoundTimer.start();
 		int r = _randomSource.getRandomNumber(RAND_MAX);
 		float f = (r + 1 + (r / 100) * -100);
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index e72fbca972b..5be7a135c84 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -152,7 +152,7 @@ bool InGameScene::aroundAnchorZone(const AnchorZone *zone) {
 
 	float xoff = charpos.x() - zone->_loc.x();
 	float zoff = charpos.z() - zone->_loc.z();
-    return sqrt(xoff * xoff + zoff * zoff) <= zone->_radius;
+	return sqrt(xoff * xoff + zoff * zoff) <= zone->_radius;
 }
 
 TeLayout *InGameScene::background() {
diff --git a/engines/tetraedge/te/te_bezier_curve.cpp b/engines/tetraedge/te/te_bezier_curve.cpp
index 18f15a71dd5..d55a3203db0 100644
--- a/engines/tetraedge/te/te_bezier_curve.cpp
+++ b/engines/tetraedge/te/te_bezier_curve.cpp
@@ -59,6 +59,18 @@ float TeBezierCurve::length() {
 	return _length;
 }
 
+void TeBezierCurve::pseudoTangent(float f, TeVector3f32 &v1, TeVector3f32 &v2) {
+	const float numiters = _numiterations;
+
+	if (1.0 / numiters + f <= 1.0) {
+		v1 = retrievePoint(f);
+		v2 = retrievePoint(1.0 / numiters + f);
+	} else {
+		v2 = retrievePoint(f);
+		v1 = retrievePoint(f - 1.0 / numiters);
+	}
+}
+
 float TeBezierCurve::rawLength() {
 	if (_rawLengthNeedsUpdate) {
 		_rawLengthNeedsUpdate = false;
@@ -78,7 +90,7 @@ float TeBezierCurve::rawLength() {
 }
 
 TeVector3f32 TeBezierCurve::retrievePoint(float offset) {
-	const unsigned int npoints = _controlPoints.size();
+	const int npoints = _controlPoints.size();
 
 	// Simple cases for small numbers of points.
 	if (npoints == 0)
@@ -89,11 +101,10 @@ TeVector3f32 TeBezierCurve::retrievePoint(float offset) {
 		return _controlPoints[0] + (_controlPoints[1] - _controlPoints[0]) * offset;
 
 	// else, there are at least 3 points so need to actually interpolate.
-	TeVector3f32 points[5];
 	const float rawlen = rawLength();
 
 	float proportion = 0.0f;
-	unsigned int i = 0;
+	int i = 0;
 	while (i < npoints) {
 		proportion = _rawLengths[i] / rawlen;
 		if (proportion >= offset)
@@ -110,12 +121,35 @@ TeVector3f32 TeBezierCurve::retrievePoint(float offset) {
 		i--;
 	}
 
-	for (unsigned int p = -1; p < 3; p++) {
-		
+	TeVector3f32 points[4];
+	TeVector3f32 *ptbuf = points;
+	const int maxPt = _controlPoints.size() - 1;
+	int p = -1;
+	do {
+		int ptno = 0;
+		if (i + p >= 0)
+			ptno = MIN(i + p, maxPt);
+		*ptbuf = _controlPoints[ptno];
+		ptbuf = ptbuf + 1;
+		p = p + 1;
+	} while (p != 3);
+
+	if (i < 0) {
+		points[0] += (points[1] - points[2]);
+	} else {
+		int ptno = MIN(i, maxPt);
+		if (ptno == 0)
+			points[0] += (points[1] - points[2]);
 	}
-	// TODO: Finish this, line 77 to 129.
+	int ptno = 0;
+	i++;
+	if (i >= 0)
+		ptno = MIN(i, maxPt);
+
+	if (ptno == maxPt)
+		points[3] += points[2] - points[1];
 
-	error("TODO: Implement TeBezierCurve::retrievePoint");
+	return hermiteInterpolate(t, points, 0.0, 0.0);
 }
 
 void TeBezierCurve::setControlPoints(const Common::Array<TeVector3f32> &points) {
@@ -165,7 +199,7 @@ TeVector3f32 TeBezierCurve::hermiteInterpolate(float t, const TeVector3f32 *poin
 
 	const float t2 = t * t;
 	const float t3 = t * t * t;
-	const TeVector3f32 h1a = points[1] * ((t3 + t3) - t2 * 3.0);
+	const TeVector3f32 h1a = points[1] * ((t3 + t3) - t2 * 3.0 + 1.0);
 	const TeVector3f32 h1b = x1 * ((t3 - (t2 + t2)) + t);
 	const TeVector3f32 h1 = (h1a + h1b) + (x2 * (t3 - t2));
 	return h1 + (points[2] * (t3 * -2.0 + t2 * 3.0));
diff --git a/engines/tetraedge/te/te_bezier_curve.h b/engines/tetraedge/te/te_bezier_curve.h
index de0ca32642b..2f199203b2f 100644
--- a/engines/tetraedge/te/te_bezier_curve.h
+++ b/engines/tetraedge/te/te_bezier_curve.h
@@ -38,6 +38,8 @@ public:
 	void draw() override;
 	float length();
 
+	void pseudoTangent(float f, TeVector3f32 &v1, TeVector3f32 &v2);
+
 	float rawLength();
 
 	TeVector3f32 retrievePoint(float offset);
@@ -50,6 +52,7 @@ public:
 	static void deserialize(Common::ReadStream &stream, TeBezierCurve &curve);
 
 	const Common::Array<TeVector3f32> &controlPoints() { return _controlPoints; }
+	unsigned int numIterations() const { return _numiterations; }
 
 private:
 	unsigned int _numiterations;
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index b19681a6b30..f0235e3492c 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -40,7 +40,7 @@ namespace Tetraedge {
 }
 
 TeButtonLayout::TeButtonLayout() :
-_currentState(BUTTON_STATE_UP), _clickPassThrough(true), _validationSoundVolume(1.0),
+_currentState(BUTTON_STATE_UP), _clickPassThrough(false), _validationSoundVolume(1.0),
 _someClickFlag(false), _doubleValidationProtectionEnabled(true), _upLayout(nullptr),
 _downLayout(nullptr), _rolloverLayout(nullptr), _disabledLayout(nullptr),
 _hitZoneLayout(nullptr)
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index c49aa2c4fef..cb7cb4437e7 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -116,17 +116,21 @@ TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, b
 
 TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt, const TeVector2s32 &endpt, float param_5, bool findMeshFlag) {
 	updateGrid(false);
-	error("TODO: Implement TeFreeMoveZone::curve");
+	Common::Array<TePickMesh2 *> meshes;
+	TeVector3f32 newend;
+	meshes.push_back(this);
+
+	TePickMesh2 *nearest = findNearestMesh(_camera, endpt, meshes, &newend, findMeshFlag);
+	if (!nearest)
+		return TeIntrusivePtr<TeBezierCurve>();
+
+	return curve(startpt, newend);
 }
 
 TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt, const TeVector3f32 &endpt) {
 	updateGrid(false);
-	Common::Array<TeVector3f32> points;
-	points.push_back(startpt);
-	points.push_back(endpt);
-
-	TeVector2s32 projectedStart = projectOnAStarGrid(startpt);
-	TeVector2s32 projectedEnd = projectOnAStarGrid(endpt);
+	const TeVector2s32 projectedStart = projectOnAStarGrid(startpt);
+	const TeVector2s32 projectedEnd = projectOnAStarGrid(endpt);
 	int xsize = _graph->_size._x;
 	float cost = 0;
 	// Passing an int to void*, yuck? but it's what the original does..
@@ -137,14 +141,16 @@ TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt,
 
 	if (pathResult == micropather::MicroPather::SOLVED || pathResult == micropather::MicroPather::START_END_SAME) {
 		Common::Array<TeVector2s32> points;
-		points.resize(path.size());
+		points.resize(path.size() + 2);
 
-		for (auto &pathpt : path) {
+		int i = 1;
+		for (auto pathpt : path) {
 			// each path point is an array offset
-			int offset = static_cast<int>(reinterpret_cast<long>(pathpt));
-			int yoff = (offset / _graph->_size._x);
+			int offset = static_cast<int>(reinterpret_cast<size_t>(pathpt));
+			int yoff = offset / _graph->_size._x;
 			int xoff = offset % _graph->_size._x;
-			points.push_back(TeVector2s32(xoff, yoff));
+			points[i] = TeVector2s32(xoff, yoff);
+			i++;
 		}
 
 		Common::Array<TeVector3f32> pts3d;
@@ -157,6 +163,12 @@ TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt,
 		removeInsignificantPoints(pts3d);
 		retval = new TeBezierCurve();
 		retval->setControlPoints(pts3d);
+	} else {
+		Common::Array<TeVector3f32> points;
+		points.push_back(startpt);
+		points.push_back(endpt);
+		retval = new TeBezierCurve();
+		retval->setControlPoints(points);
 	}
 
 	return retval;
@@ -244,9 +256,66 @@ TeVector2s32 TeFreeMoveZone::projectOnAStarGrid(const TeVector3f32 &pt) {
 	return TeVector2s32((int)projected.getX(), (int)projected.getY());
 }
 
+static int segmentIntersection(const TeVector2f32 &s1start, const TeVector2f32 &s1end,
+						const TeVector2f32 &s2start, const TeVector2f32 &s2end,
+                       TeVector2f32 *sout, float *fout1, float *fout2) {
+	TeVector2f32 s1len = s1end - s1start;
+	TeVector2f32 s2len = s2end - s2start;
+	float squarelen = s1len.getX() * s2len.getX() + s1len.getY() * s2len.getY();
+	int result = 0;
+	if (squarelen != 0) {
+		result = 1;
+		float intersection1 = -((s1len.getY() * s1start.getX() +
+						(s1len.getX() * s2start.getY() - s1len.getX() * s1start.getY())) -
+                          s1len.getY() * s2start.getX()) / squarelen;
+		if (intersection1 >= 0.0f && intersection1 <= 1.0f) {
+			float intersection2 = -((s2len.getY() * s2start.getY() +
+						(s2len.getX() * s1start.getX() - s2len.getX() * s2start.getX())) -
+                          s2len.getY() * s1start.getY()) / squarelen;
+			if (intersection2 >= 0.0f && intersection2 <= 1.0f) {
+				result = 2;
+				if (sout || fout1 || fout2) {
+					warning("TODO: implement output in segmentIntersection");
+				}
+			}
+		}
+	}
+	return result;
+}
+
 Common::Array<TeVector3f32> TeFreeMoveZone::removeInsignificantPoints(const Common::Array<TeVector3f32> &points) {
-	warning("TODO: Implement TeFreeMoveZone::removeInsignificantPoints");
-	return points;
+	if (points.size() < 2)
+		return points;
+
+	Common::Array<TeVector3f32> result;
+	result.push_back(points[0]);
+
+	if (points.size() > 2) {
+		int point1 = 0;
+		int point2 = 2;
+        do {
+			const TeVector2f32 pt1(points[point1].x(), points[point1].z());
+			const TeVector2f32 pt2(points[point2].x(), points[point2].z());
+			for (unsigned int i = 0; i * 2 < _uintArray2.size() / 2; i++) {
+				const TeVector3f32 transpt3d1 = worldTransformationMatrix() * verticies()[_uintArray2[i * 2]];
+				const TeVector2f32 transpt1(transpt3d1.x(), transpt3d1.z());
+				const TeVector3f32 transpt3d2 = worldTransformationMatrix() * verticies()[_uintArray2[i * 2 + 1]];
+				const TeVector2f32 transpt2(transpt3d2.x(), transpt3d2.z());
+				if (segmentIntersection(pt1, pt2, transpt1, transpt2, nullptr, nullptr, nullptr) == 2)
+					break;
+			}
+			point1 = point2 - 1;
+			result.push_back(points[point1]);
+			point2++;
+		} while (point2 < points.size());
+	}
+
+	if (result.back() != points[points.size() - 2]) {
+        result.push_back(points[points.size() - 1]);
+	} else {
+        result.back() = points[points.size() - 1];
+	}
+	return result;
 }
 
 void TeFreeMoveZone::setBordersDistance(float dist) {
@@ -347,8 +416,8 @@ void TeFreeMoveZone::updateTransformedVertices() {
 /*========*/
 
 float TeFreeMoveZoneGraph::LeastCostEstimate(void *stateStart, void *stateEnd) {
-	int startInt = static_cast<int>(reinterpret_cast<long>(stateStart));
-	int endInt = static_cast<int>(reinterpret_cast<long>(stateEnd));
+	int startInt = static_cast<int>(reinterpret_cast<size_t>(stateStart));
+	int endInt = static_cast<int>(reinterpret_cast<size_t>(stateEnd));
 	int starty = startInt / _size._x;
 	int endy = endInt / _size._x;
 	TeVector2s32 start(startInt - starty * _size._x, starty);
@@ -357,7 +426,7 @@ float TeFreeMoveZoneGraph::LeastCostEstimate(void *stateStart, void *stateEnd) {
 }
 
 void TeFreeMoveZoneGraph::AdjacentCost(void *state, Common::Array<micropather::StateCost> *adjacent) {
-	int stateInt = static_cast<int>(reinterpret_cast<long>(state));
+	int stateInt = static_cast<int>(reinterpret_cast<size_t>(state));
 	int stateY = stateInt / _size._x;
 	const TeVector2s32 statept(stateInt - stateY * _size._x, stateY);
 
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index 706cc679516..b7f5743777b 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -108,7 +108,7 @@ public:
 
 	static float normalizeAngle(float angle);
 	static void deserialize(Common::ReadStream &stream, TeFreeMoveZone &dest, Common::Array<TeBlocker> *blockers,
-               Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones);
+			Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones);
 	static void serialize(Common::WriteStream &stream, const TeFreeMoveZone &src, bool updateFirst);
 
 	static TePickMesh2 *findNearestMesh(TeIntrusivePtr<TeCamera> &camera, const TeVector2s32 &frompt,
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index 3c097b63377..d9fa712e01b 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -136,6 +136,7 @@ public:
 	bool _skipSkinOffsets;
 
 	Common::Array<TeMesh> _meshes;
+	Common::Array<BonesBlender *> _boneBlenders;
 
 protected:
 	bool _matrixForced;
@@ -146,7 +147,6 @@ protected:
 	Common::Array<TeMatrix4x4> _boneMatricies;
 	Common::Array<TeMatrix4x4> _lerpedElements;
 	Common::Array<Common::Array<weightElement>> _weightElements;
-	Common::Array<BonesBlender *> _boneBlenders;
 
 	TeQuaternion _boneRotation;
 
diff --git a/engines/tetraedge/te/te_model_animation.h b/engines/tetraedge/te/te_model_animation.h
index fb30cc66bd6..5152b082bd2 100644
--- a/engines/tetraedge/te/te_model_animation.h
+++ b/engines/tetraedge/te/te_model_animation.h
@@ -54,6 +54,10 @@ public:
 
 	TeModelAnimation();
 
+	~TeModelAnimation() {
+		destroy();
+	}
+
 	void bind(const TeIntrusivePtr<TeModel> &ptr) {
 		_model = ptr;
 	};
@@ -88,6 +92,7 @@ public:
 	void update(double proportion) override;
 
 	int curFrame2() const { return _curFrame2; }
+	float speed() const { return _speed; }
 
 	TeIntrusivePtr<TeModel> _model;
 	int _firstFrame;
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 8916cf99cc3..41fb2c5cbad 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -59,7 +59,7 @@ void TePickMesh2::draw() {
 	renderer->setCurrentColor(prevCol);
 }
 
-bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 v2, TeVector3f32 &v3, float &fout, bool lastHitFirst, unsigned long *triangleHitOut) {
+bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &v3, float &fout, bool lastHitFirst, unsigned long *triangleHitOut) {
 	if (_verticies.size() / 3 == 0)
 		return false;
 
diff --git a/engines/tetraedge/te/te_pick_mesh2.h b/engines/tetraedge/te/te_pick_mesh2.h
index fd823128a8a..584a3cf88b9 100644
--- a/engines/tetraedge/te/te_pick_mesh2.h
+++ b/engines/tetraedge/te/te_pick_mesh2.h
@@ -35,7 +35,7 @@ public:
 
 	void draw() override;
 
-	bool intersect(const TeVector3f32 &v1, const TeVector3f32 v2, TeVector3f32 &v3, float &fout, bool useLastHit, unsigned long *triangleHitOut);
+	bool intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &v3, float &fout, bool useLastHit, unsigned long *triangleHitOut);
 	bool intersect2D(const TeVector2f32 &pt);
 	unsigned long lastTriangleHit() const;
 
diff --git a/engines/tetraedge/te/te_ray_intersection.cpp b/engines/tetraedge/te/te_ray_intersection.cpp
index fa1d6080a29..4cfece341c3 100644
--- a/engines/tetraedge/te/te_ray_intersection.cpp
+++ b/engines/tetraedge/te/te_ray_intersection.cpp
@@ -31,7 +31,7 @@ TePickMesh *getMesh(const TeVector3f32 &param_1, const TeVector3f32 &param_2, co
 }
 
 int intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3,
-              const TeVector3f32 &v4, const TeVector3f32 &v5, TeVector3f32 &vout, float &fout) {
+			  const TeVector3f32 &v4, const TeVector3f32 &v5, TeVector3f32 &vout, float &fout) {
 	const TeVector3f32 v4_v3 = (v4 - v3);
 	const TeVector3f32 v5_v3 = (v5 - v3);
 	TeVector3f32 v = v4_v3 ^ v5_v3;
diff --git a/engines/tetraedge/te/te_resource.cpp b/engines/tetraedge/te/te_resource.cpp
index 2584d7df2e0..b75575768fd 100644
--- a/engines/tetraedge/te/te_resource.cpp
+++ b/engines/tetraedge/te/te_resource.cpp
@@ -49,7 +49,7 @@ void TeResource::generateAccessName() {
 	name[4] = hexaChars[_idCounter >> 12 & 0xf];
 	name[5] = hexaChars[_idCounter >> 8  & 0xf];
 	name[6] = hexaChars[_idCounter >> 4  & 0xf];
-	name[7] = hexaChars[_idCounter       & 0xf];
+	name[7] = hexaChars[_idCounter		 & 0xf];
 	name[8] = '\0';
 
 	_idCounter++;
diff --git a/engines/tetraedge/te/te_vector2f32.h b/engines/tetraedge/te/te_vector2f32.h
index 6e3e7501de8..66408fce170 100644
--- a/engines/tetraedge/te/te_vector2f32.h
+++ b/engines/tetraedge/te/te_vector2f32.h
@@ -57,6 +57,10 @@ public:
 		return getX() * other.getX() - getY() * other.getY();
 	}
 
+	float length() const {
+		return sqrt(getX() * getX() + getY() * getY());
+	}
+
 	/*
 	TODO: do we need anything that isn't already in Vector2d here?
 	TeVector2f32(const TeVector2f32 &other);
@@ -64,7 +68,6 @@ public:
 	TeVector2f32(float *vals);
 
 	float dotProduct(const TeVector2f32 &other) const;
-	float length() const;
 	float squaredLength() const;
 
 	void normalize();


Commit: 036ad4bc04ba030a1b04d71ce6073f63a63bf2d9
    https://github.com/scummvm/scummvm/commit/036ad4bc04ba030a1b04d71ce6073f63a63bf2d9
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: WIP, fix suitcase and faces on the floor.

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/object3d.cpp
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_trs.cpp
    engines/tetraedge/te/te_trs.h


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 190041788bd..1a4d672c7d6 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -292,8 +292,9 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 
 	for (auto &mesh : _model->_meshes)
 		mesh.setVisible(true);
-
+	// Set all mouthes not visible by default
 	_model->setVisibleByName("_B_", false);
+	// Set all eyes not visible by default
 	_model->setVisibleByName("_Y_", false);
 
 	// Note: game loops through "faces" here, but it only ever uses the default ones.
@@ -689,8 +690,7 @@ void Character::update(double msFromStart) {
 
 	float endOffset = _walkCurveStart;
 	while (_walkedLength < _walkCurveCurOffset) {
-		lastNextPos = nextPos;
-		float nextOffset = (4.0 / _curve->numIterations() * sign + lastOffset);
+		float nextOffset = (4.0 / _curve->numIterations()) * sign + lastOffset;
 		float offset = CLIP(nextOffset, 0.0f, 1.0f);
 
 		newPos = _curve->retrievePoint(offset) + _curveStartLocation;
@@ -704,6 +704,7 @@ void Character::update(double msFromStart) {
 		if (offset == 1.0 || offset == 0.0)
 			break;
 		lastOffset = offset;
+		lastNextPos = nextPos;
 	}
 
 	_walkedLength = lastWalkedLength;
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index a469948ccdf..1c11e704b0f 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -437,7 +437,7 @@ static int tolua_ExportedFunctions_SetGroundObjectPosition00(lua_State *L) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		float f1 = tolua_tonumber(L, 2, 0.0);
 		float f2 = tolua_tonumber(L, 3, 0.0);
-		float f3 = tolua_tonumber(L, 4, 1.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
 		SetGroundObjectPosition(s1, f1, f2, f3);
 		return 0;
 	}
@@ -465,7 +465,7 @@ static int tolua_ExportedFunctions_SetGroundObjectRotation00(lua_State *L) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		float f1 = tolua_tonumber(L, 2, 0.0);
 		float f2 = tolua_tonumber(L, 3, 0.0);
-		float f3 = tolua_tonumber(L, 4, 1.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
 		SetGroundObjectRotation(s1, f1, f2, f3);
 		return 0;
 	}
@@ -545,7 +545,8 @@ static void AddAnchorZone(const Common::String &s1, const Common::String &s2, fl
 
 static int tolua_ExportedFunctions_AddAnchorZone00(lua_State *L) {
 	tolua_Error err;
-	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnumber(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+			&& tolua_isnumber(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		Common::String s2(tolua_tostring(L, 2, nullptr));
 		double d1 = tolua_tonumber(L, 3, 1.0);
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index a05d032198e..23609ec05cd 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -28,7 +28,7 @@ namespace Tetraedge {
 /*static*/ Common::HashMap<Common::String, Object3D::ObjectSettings> *Object3D::_objectSettings = nullptr;
 
 
-Object3D::Object3D() : _translateTime(-1), _rotateTime(-1) {
+Object3D::Object3D() : _translateTime(-1), _rotateTime(-1), _objScale(1.0f, 1.0f, 1.0f) {
 }
 
 bool Object3D::loadModel(const Common::String &name) {
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index d4aa310c98d..0ffbc513073 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -67,6 +67,9 @@ void TeMesh::destroy() {
 }
 
 void TeMesh::draw() {
+	if (!worldVisible())
+		return;
+
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->pushMatrix();
 	if (_matrixForced)
@@ -74,6 +77,17 @@ void TeMesh::draw() {
 	else
 		renderer->multiplyMatrix(worldTransformationMatrix());
 
+	/*
+	debug("Draw mesh %p (%s, %d verts %d norms %d indexes %d materials %d updated)", this, name().empty() ? "no name" : name().c_str(), _verticies.size(), _normals.size(), _indexes.size(), _materials.size(), _updatedVerticies.size());
+	debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
+	debug("   position   %s", position().dump().c_str());
+	debug("   worldPos   %s", worldPosition().dump().c_str());
+	debug("   scale      %s", scale().dump().c_str());
+	debug("   worldScale %s", worldScale().dump().c_str());
+	debug("   rotation   %s", rotation().dump().c_str());
+	debug("   worldRot   %s", worldRotation().dump().c_str());
+	*/
+
 	Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
 	Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
 	if (renderer->shadowMode() != TeRenderer::ShadowMode1) {
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 195bd2a52a3..60b2a7728da 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -102,13 +102,14 @@ void TeModel::draw() {
 		renderer->sendModelMatrix(transform);
 		renderer->pushMatrix();
 		renderer->multiplyMatrix(transform);
-		/*if (name() == "Kate") {
+		/*
+		if (name().contains("Kate")) {
 			debug("Draw model %p (%s, %d meshes)", this, name().empty() ? "no name" : name().c_str(), _meshes.size());
-			//adebug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
+			debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
 			//debug("   position   %s", position().dump().c_str());
 			debug("   worldPos   %s", worldPosition().dump().c_str());
 			//debug("   scale      %s", scale().dump().c_str());
-			//debug("   worldScale %s", worldScale().dump().c_str());
+			debug("   worldScale %s", worldScale().dump().c_str());
 			//debug("   rotation   %s", rotation().dump().c_str());
 			debug("   worldRot   %s", worldRotation().dump().c_str());
 		}*/
@@ -166,6 +167,8 @@ void TeModel::setColor(const TeColor &col) {
 }
 
 void TeModel::update() {
+	//if (name().contains("Kate"))
+	//	debug("TeModel::update model %s", name().c_str());
 	if (_bones.size()) {
 		Common::Array<TeMatrix4x4> matricies;
 		matricies.resize(_bones.size());
diff --git a/engines/tetraedge/te/te_trs.cpp b/engines/tetraedge/te/te_trs.cpp
index 8e2a13e7e8c..cc6f351b362 100644
--- a/engines/tetraedge/te/te_trs.cpp
+++ b/engines/tetraedge/te/te_trs.cpp
@@ -24,6 +24,13 @@
 namespace Tetraedge {
 
 TeTRS::TeTRS() {
+	setIdentity();
+}
+
+void TeTRS::setIdentity() {
+	_trans = TeVector3f32(0, 0, 0);
+	_rot = TeQuaternion();
+	_scale = TeVector3f32(1, 1, 1);
 }
 
 /*static*/ void TeTRS::deserialize(Common::ReadStream &stream, TeTRS &dest) {
diff --git a/engines/tetraedge/te/te_trs.h b/engines/tetraedge/te/te_trs.h
index d3a369098c3..041cd9d3e5e 100644
--- a/engines/tetraedge/te/te_trs.h
+++ b/engines/tetraedge/te/te_trs.h
@@ -35,6 +35,8 @@ public:
 	static void deserialize(Common::ReadStream &stream, TeTRS &dest);
 	static void serialize(Common::WriteStream &stream, const TeTRS &src);
 
+	void setIdentity();
+
 	void setRotation(const TeQuaternion &rot) {
 		_rot = rot;
 	}


Commit: 85ed7742dea857637e0f27c0849b07f7369c435f
    https://github.com/scummvm/scummvm/commit/85ed7742dea857637e0f27c0849b07f7369c435f
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More WIP.  Can now walk between screens.

Some object interactions work too.  Next need to finish
inventory.

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/character_settings_xml_parser.cpp
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/dialog2.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/inventory.h
    engines/tetraedge/game/inventory_object.cpp
    engines/tetraedge/game/inventory_object.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_bezier_curve.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_lua_thread.cpp
    engines/tetraedge/te/te_lua_thread.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_model_vertex_animation.cpp
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_ray_intersection.cpp
    engines/tetraedge/te/te_timer.cpp
    engines/tetraedge/te/te_timer.h


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 1a4d672c7d6..fd8b39755cd 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -42,7 +42,7 @@ void Character::CharacterSettings::clear() {
 	_name.clear();
 	_modelFileName.clear();
 	_defaultScale = TeVector3f32();
-	_walkFileName.clear();
+	_idleAnimFileName.clear();
 	_walkSettings.clear();
 	_walkSpeed = 0.0f;
 	_cutSceneCurveDemiPosition = TeVector3f32();
@@ -57,14 +57,14 @@ void Character::WalkSettings::clear() {
 	}
 }
 
-Character::Character() : _curveOffset(0), _lastFrame(-1), _callbacksChanged(false),
+Character::Character() : _walkCurveStart(0), _lastFrame(-1), _callbacksChanged(false),
 _notWalkAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"),
 _needsSomeUpdate(false), _positionFlag(false), _lookingAtTallThing(false),
 _stepSound1("sounds/SFX/PAS_H_BOIS1.ogg"), _stepSound2("sounds/SFX/PAS_H_BOIS2.ogg"),
 _freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(nullptr),
-_recallageY(true), _walkToFlag(false), _walkCurveEnd(0.0f), _walkCurveStart(0.0f),
+_recallageY(true), _walkToFlag(false), _walkCurveEnd(0.0f), _walkCurveLast(0.0f),
 _walkCurveLen(0.0f), _walkCurveIncrement(0.0f), _walkEndAnimG(false), _walkTotalFrames(0),
-_walkCurveCurOffset(0.0f) {
+_walkCurveNextLength(0.0f) {
 	_curModelAnim.setDeleteFn(&TeModelAnimation::deleteLater);
 }
 
@@ -92,11 +92,20 @@ Character::~Character() {
 }
 
 void Character::addCallback(const Common::String &key, const Common::String &s2, float f1, float f2) {
-	/*Callback *c = new Callback();
-	c->x = (int)f1;
-	c->y = (int)f2;
-	c->f = (f2 == -1.0 ? -NAN : 0.0f;*/
-	error("TODO: Implement Character::addCallback");
+	Callback *c = new Callback();
+	c->_s = s2;
+	c->_x = (int)f1;
+	c->_y = (int)f2;
+	c->_f = (f2 == -1.0 ? -NAN : 0.0f);
+
+	const Common::String animPath = _model->anim()->_loadedPath.toString();
+	if (_callbacks.contains(animPath)) {
+		_callbacks[animPath].push_back(c);
+	} else {
+		Common::Array<Callback *> callbacks;
+		callbacks.push_back(c);
+		_callbacks.setVal(key, callbacks);
+	}
 }
 
 /*static*/ void Character::animCacheFreeAll() {
@@ -137,7 +146,7 @@ float Character::animLength(const TeModelAnimation &modelanim, long bone, long l
 	return ((endtrans.z() - starttrans.z()) + secondtrans.z()) - starttrans.z();
 }
 
-float Character::animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe) {
+float Character::animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe /* = 9999 */) {
 	if (animname.empty()) {
 		*pframeCount = 0;
 		return 0.0f;
@@ -163,7 +172,7 @@ bool Character::blendAnimation(const Common::String &animname, float amount, boo
 	Common::Path animpath("models/Anims");
 	animpath.joinInPlace(animname);
 
-	_notWalkAnim = !(animname.contains(_characterSettings._walkFileName)
+	_notWalkAnim = !(animname.contains(_characterSettings._idleAnimFileName)
 			|| animname.contains(walkAnim(WalkPart_Start))
 			|| animname.contains(walkAnim(WalkPart_Loop))
 			|| animname.contains(walkAnim(WalkPart_EndG))
@@ -175,6 +184,7 @@ bool Character::blendAnimation(const Common::String &animname, float amount, boo
 	}
 
 	_curModelAnim = animCacheLoad(animpath);
+	assert(_curModelAnim);
 	_curModelAnim->onFinished().add(this, &Character::onModelAnimationFinished);
 
 	_curModelAnim->bind(_model);
@@ -190,12 +200,12 @@ TeVector3f32 Character::correctPosition(const TeVector3f32 &pos) {
 	bool flag;
 	TeVector3f32 result = _freeMoveZone->correctCharacterPosition(pos, &flag, true);
 	if (!flag)
-		result = _model->position();
+		result.y() = _model->position().y();
 	return result;
 }
 
 float Character::curveOffset() {
-	return _curveOffset;
+	return _walkCurveStart;
 }
 
 void Character::deleteAllCallback() {
@@ -302,11 +312,11 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 	_model->setVisibleByName(_characterSettings._defaultMouth, true);
 	_model->setVisibleByName(_characterSettings._defaultBody, true);
 
-	setAnimation(_characterSettings._walkFileName, true);
+	setAnimation(_characterSettings._idleAnimFileName, true);
 
-	_walkPart0AnimLen = animLengthFromFile(walkAnim(WalkPart_Start), &_walkPart0AnimFrameCount);
-	_walkPart3AnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkPart3AnimFrameCount);
-	_walkPart1AnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkPart1AnimFrameCount);
+	_walkStartAnimLen = animLengthFromFile(walkAnim(WalkPart_Start), &_walkStartAnimFrameCount);
+	_walkEndGAnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkEndGAnimFrameCount);
+	_walkLoopAnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkLoopAnimFrameCount);
 
 	TeIntrusivePtr<Te3DTexture> shadow = new Te3DTexture();
 	shadow->load("models/Textures/simple_shadow_alpha.tga");
@@ -388,9 +398,9 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 						walkSettings._value._walkParts[2]._file == animfile ||
 						walkSettings._value._walkParts[3]._file == animfile);
 			}
-			resetX |= animfile.contains(_characterSettings._walkFileName);
+			resetX |= animfile.contains(_characterSettings._idleAnimFileName);
 		} else {
-			resetX = (animfile.contains(_characterSettings._walkFileName) ||
+			resetX = (animfile.contains(_characterSettings._idleAnimFileName) ||
 					  animfile.contains(walkAnim(WalkPart_Start)) ||
 					  animfile.contains(walkAnim(WalkPart_Loop)) ||
 					  animfile.contains(walkAnim(WalkPart_EndD)) ||
@@ -444,9 +454,10 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 			bool flag;
 			pos = _freeMoveZone->correctCharacterPosition(pos, &flag, true);
 		}
-		_shadowModel[1]->setPosition(pos);
-		_shadowModel[1]->setRotation(_model->rotation());
-		_shadowModel[1]->setScale(_model->scale());
+		int shadowNo = boneName.contains("Bip01 L Foot") ? 0 : 1;
+		_shadowModel[shadowNo]->setPosition(pos);
+		_shadowModel[shadowNo]->setRotation(_model->rotation());
+		_shadowModel[shadowNo]->setScale(_model->scale());
 	}
 
 	// Move any objects attached to the bone
@@ -485,20 +496,21 @@ bool Character::onModelAnimationFinished() {
 					walkSettings._value._walkParts[2]._file == animfile ||
 					walkSettings._value._walkParts[3]._file == animfile);
 		}
-		isWalkAnim |= animfile.contains(_characterSettings._walkFileName);
+		isWalkAnim |= animfile.contains(_characterSettings._idleAnimFileName);
 	} else {
-		isWalkAnim = (animfile.contains(_characterSettings._walkFileName) ||
+		isWalkAnim = (animfile.contains(_characterSettings._idleAnimFileName) ||
 				  animfile.contains(walkAnim(WalkPart_Start)) ||
 				  animfile.contains(walkAnim(WalkPart_Loop)) ||
 				  animfile.contains(walkAnim(WalkPart_EndD)) ||
 				  animfile.contains(walkAnim(WalkPart_EndG)));
 	}
 
-	if (isWalkAnim) {
+	if (!isWalkAnim) {
 		int pereBone = _curModelAnim->findBone("Pere");
 		const TeTRS endTRS = trsFromAnim(*_curModelAnim, pereBone, _curModelAnim->lastFrame());
 		TeVector3f32 trans = endTRS.getTranslation();
 		trans.x() = -trans.x();
+		trans.y() = 0;
 
 		TeVector3f32 newpos;
 		if (!_recallageY) {
@@ -525,7 +537,7 @@ bool Character::onModelAnimationFinished() {
 	if (_someRepeatFlag && loadedPath.toString().contains(_setAnimName)) {
 		_notWalkAnim = false;
 		_someRepeatFlag = false;
-		setAnimation(_characterSettings._walkFileName, true);
+		setAnimation(_characterSettings._idleAnimFileName, true);
 	}
 
 	return false;
@@ -537,7 +549,7 @@ void Character::permanentUpdate() {
 
 void Character::placeOnCurve(TeIntrusivePtr<TeBezierCurve> &curve) {
 	_curve = curve;
-	updatePosition(_curveOffset);
+	updatePosition(_walkCurveStart);
 }
 
 void Character::removeAnim() {
@@ -561,7 +573,7 @@ bool Character::setAnimation(const Common::String &aname, bool repeat, bool para
 
 	Common::Path animPath("models/Anims");
 	animPath.joinInPlace(aname);
-	bool isWalkAnim = (aname.contains(_characterSettings._walkFileName) ||
+	bool isWalkAnim = (aname.contains(_characterSettings._idleAnimFileName) ||
 					  aname.contains(walkAnim(WalkPart_Start)) ||
 					  aname.contains(walkAnim(WalkPart_Loop)) ||
 					  aname.contains(walkAnim(WalkPart_EndD)) ||
@@ -595,7 +607,7 @@ void Character::setAnimationSound(const Common::String &sname, uint offset) {
 }
 
 void Character::setCurveOffset(float offset) {
-	_curveOffset = offset;
+	_walkCurveStart = offset;
 	updatePosition(offset);
 }
 
@@ -669,56 +681,60 @@ void Character::update(double msFromStart) {
 	if (!_curve || !_runTimer.running())
 		return;
 
-	_walkCurveCurOffset = speedFromAnim(msFromStart) * _walkCurveIncrement + _walkCurveCurOffset;
+	_walkCurveNextLength = speedFromAnim(msFromStart) * _walkCurveIncrement + _walkCurveNextLength;
 
 	if (_curve->controlPoints().size() < 2) {
-		blendAnimation(_characterSettings._walkFileName, 0.0667, true, false);
+		blendAnimation(_characterSettings._idleAnimFileName, 0.0667, true, false);
 		endMove();
 		return;
 	}
 
-	const float baseAngle = (_curveOffset > _walkCurveEnd ? M_PI : 0);
-	const float sign = (_curveOffset > _walkCurveEnd ? -1 : 1);
-	updatePosition(_walkCurveStart);
-	const TeVector3f32 modelpos = _model->position();
+	const float baseAngle = (_walkCurveStart > _walkCurveEnd ? M_PI : 0);
+	const float sign = (_walkCurveStart > _walkCurveEnd ? -1 : 1);
+	updatePosition(_walkCurveLast);
 
 	float lastWalkedLength = _walkedLength;
-	TeVector3f32 lastNextPos = modelpos;
-	TeVector3f32 nextPos = modelpos;
-	TeVector3f32 newPos = modelpos;
-	float lastOffset = _walkCurveStart;
+	TeVector3f32 lastNextPos = _model->position();
+	TeVector3f32 nextPos = _model->position();
+	TeVector3f32 newPos = _model->position();
+	float lastOffset = _walkCurveLast;
 
-	float endOffset = _walkCurveStart;
-	while (_walkedLength < _walkCurveCurOffset) {
-		float nextOffset = (4.0 / _curve->numIterations()) * sign + lastOffset;
-		float offset = CLIP(nextOffset, 0.0f, 1.0f);
+	// First do a coarse search for the position, then back up 1 step and do a finer
+	// search.
 
-		newPos = _curve->retrievePoint(offset) + _curveStartLocation;
-		const TeVector2f32 dist = TeVector2f32(nextPos.x(), nextPos.z()) - TeVector2f32(newPos.x(), newPos.z());
+	const float coarseStep = (4.0 / _curve->numIterations()) * sign;
+	const float fineStep = (1.0 / _curve->numIterations()) * sign;
 
+	float offset = _walkCurveLast;
+	while (_walkedLength < _walkCurveNextLength) {
+		lastOffset = offset;
 		lastWalkedLength = _walkedLength;
+		lastNextPos = nextPos;
+
+		offset = CLIP(lastOffset + coarseStep, 0.0f, 1.0f);
+
+		newPos = _curve->retrievePoint(offset) + _curveStartLocation;
+		const TeVector2f32 dist = TeVector2f32(nextPos.x(), nextPos.z()) - TeVector2f32(newPos.x(), newPos.z());
 		_walkedLength += dist.length();
 
 		nextPos = newPos;
-		endOffset = lastOffset;
 		if (offset == 1.0 || offset == 0.0)
 			break;
-		lastOffset = offset;
-		lastNextPos = nextPos;
 	}
 
 	_walkedLength = lastWalkedLength;
 	nextPos = lastNextPos;
+	offset = lastOffset;
 
-	while (_walkedLength < _walkCurveCurOffset) {
-		float nextOffset = (1.0 / _curve->numIterations()) * sign + endOffset;
-		endOffset = CLIP(nextOffset, 0.0f, 1.0f);
+	while (_walkedLength < _walkCurveNextLength) {
+		offset = CLIP(offset + fineStep, 0.0f, 1.0f);
 
-		newPos = _curve->retrievePoint(endOffset) + _curveStartLocation;
+		newPos = _curve->retrievePoint(offset) + _curveStartLocation;
 		const TeVector2f32 dist = TeVector2f32(nextPos.x(), nextPos.z()) - TeVector2f32(newPos.x(), newPos.z());
 		_walkedLength += dist.length();
+
 		nextPos = newPos;
-		if (endOffset == 1.0 || endOffset == 0.0)
+		if (offset == 1.0 || offset == 0.0)
 			break;
 	}
 
@@ -727,16 +743,19 @@ void Character::update(double msFromStart) {
 		newPos = _freeMoveZone->correctCharacterPosition(newPos, &correctflag, true);
 	}
 
-	_walkCurveStart = endOffset;
+	debug("Character::update %4d %.04f %s -> %s %.4f", (int)msFromStart, offset, _model->position().dump().c_str(),
+			newPos.dump().c_str(), (newPos - _model->position()).length());
+
+	_walkCurveLast = offset;
 	_model->setPosition(newPos);
 
 	TeVector3f32 t1;
 	TeVector3f32 t2;
-	_curve->pseudoTangent(endOffset, t1, t2);
+	_curve->pseudoTangent(offset, t1, t2);
 	const TeVector3f32 normalizedTangent = (t2 - t1).getNormalized();
 	float angle = TeVector3f32(0.0, 0.0, 1.0).dotProduct(normalizedTangent);
-	TeVector3f32 crossprod = TeVector3f32::crossProduct(TeVector3f32(0.0, 0.0, 1.0), normalizedTangent);
 	angle = acos(angle);
+	TeVector3f32 crossprod = TeVector3f32::crossProduct(TeVector3f32(0.0, 0.0, 1.0), normalizedTangent);
 	if (crossprod.y() >= 0.0f) {
 		angle = -angle;
 	}
@@ -745,13 +764,13 @@ void Character::update(double msFromStart) {
 	_model->setRotation(rot);
 
 	const Common::String endGAnim = walkAnim(WalkPart_EndG);
-	if (_walkCurveStart == _walkCurveEnd || fabs(_walkCurveEnd - _curveOffset) < fabs(_walkCurveStart - _curveOffset)) {
+	if (_walkCurveLast == _walkCurveEnd || fabs(_walkCurveEnd - _walkCurveStart) < fabs(_walkCurveLast - _walkCurveStart)) {
 		if (_walkToFlag) {
 			_walkToFlag = false;
 			endMove();
 		}
 		if (endGAnim.empty()) {
-			blendAnimation(_characterSettings._walkFileName, 0.0667, true, false);
+			blendAnimation(_characterSettings._idleAnimFileName, 0.0667, true, false);
 			endMove();
 		}
 	}
@@ -807,44 +826,45 @@ Common::String Character::walkAnim(Character::WalkPart part) {
 void Character::walkMode(const Common::String &mode) {
 	if (_walkModeStr != mode)
 		_walkModeStr = mode;
-	_walkPart0AnimLen = animLengthFromFile(walkAnim(WalkPart_Start), &_walkPart0AnimFrameCount, 9999);
-	_walkPart3AnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkPart3AnimFrameCount, 9999);
-	_walkPart1AnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkPart1AnimFrameCount, 9999);
+	_walkStartAnimLen = animLengthFromFile(walkAnim(WalkPart_Start), &_walkStartAnimFrameCount);
+	_walkEndGAnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkEndGAnimFrameCount);
+	_walkLoopAnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkLoopAnimFrameCount);
 }
 
 void Character::walkTo(float curveEnd, bool walkFlag) {
 	_walkToFlag = walkFlag;
 	stop();
 	_walkCurveEnd = curveEnd;
-	_walkCurveStart = _curveOffset;
-	_walkCurveCurOffset = 0.0f;
+	_walkCurveLast = _walkCurveStart;
+	_walkCurveNextLength = 0.0f;
 	_walkedLength = 0.0f;
-	const float f = (walkFlag ? _walkPart3AnimLen : 0);
+	assert(_curve);
 	if (_curve->controlPoints().size()) {
+		const float walkEndLen = (walkFlag ? 0 : _walkEndGAnimLen);
 		_walkCurveLen = _curve->length();
 		_walkEndAnimG = false;
-		const float f2 = ((_walkCurveLen - f) - _walkPart0AnimLen) / _walkPart1AnimLen;
+		const float nloops = (_walkCurveLen - (walkEndLen + _walkStartAnimLen)) / _walkLoopAnimLen;
 		float animLen;
-		if (f2 >= 0) {
+		if (nloops >= 0) {
 			Game *game = g_engine->getGame();
 			if (game->scene()._character == this && _walkModeStr == "Walk") {
-				int part1len = (int)(f2 * _walkPart1AnimFrameCount);
-				int repeats = part1len / _walkPart1AnimFrameCount;
-				uint remainder = part1len % _walkPart1AnimFrameCount;
+				int looplen = (int)(nloops * _walkLoopAnimFrameCount);
+				int repeats = looplen / _walkLoopAnimFrameCount;
+				uint remainder = looplen % _walkLoopAnimFrameCount;
 
 				uint framecounts[4];
 
 				if (repeats == 0)
 					framecounts[0] = UINT_MAX;
 				else
-					framecounts[0] = (repeats - 1) * _walkPart1AnimFrameCount + 29;
+					framecounts[0] = (repeats - 1) * _walkLoopAnimFrameCount + 29;
 
-				framecounts[1] = _walkPart1AnimFrameCount * repeats + 13;
-				framecounts[2] = _walkPart1AnimFrameCount * repeats + 29;
-				framecounts[3] = _walkPart1AnimFrameCount * (repeats + 1) + 13;
+				framecounts[1] = _walkLoopAnimFrameCount * repeats + 13;
+				framecounts[2] = _walkLoopAnimFrameCount * repeats + 29;
+				framecounts[3] = _walkLoopAnimFrameCount * (repeats + 1) + 13;
 
 				for (int i = 0; i < 4; i++) {
-					framecounts[i] = abs((int)(framecounts[i] - (int)(f2 * _walkPart1AnimFrameCount)));
+					framecounts[i] = abs((int)(framecounts[i] - (int)(nloops * _walkLoopAnimFrameCount)));
 				}
 
 				int minoffset = 0;
@@ -870,26 +890,25 @@ void Character::walkTo(float curveEnd, bool walkFlag) {
 				  remainder = 13;
 				  repeats++;
 				}
-				_walkTotalFrames = _walkPart1AnimFrameCount * repeats + _walkPart0AnimFrameCount + remainder;
-				animLen = _walkPart1AnimLen;
+				_walkTotalFrames = _walkLoopAnimFrameCount * repeats + _walkStartAnimFrameCount + remainder;
 				const float loopAnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &remainder, remainder);
-				_walkCurveIncrement = _walkCurveLen / (repeats * animLen + f + _walkPart0AnimLen + loopAnimLen);
+				_walkCurveIncrement = _walkCurveLen / (repeats * _walkLoopAnimLen + walkEndLen + _walkStartAnimLen + loopAnimLen);
 				play();
 				return; // NOTE: early return here.
 			} else {
 				double intpart;
-				double remainder = modf(f, &intpart);
+				double remainder = modf(walkEndLen, &intpart);
 				if (remainder >= 0.5) {
 					_walkEndAnimG = true;
 					intpart += 0.75;
 				} else {
 					intpart += 0.25;
 				}
-				_walkTotalFrames = (int)(_walkPart1AnimFrameCount * intpart) + _walkPart0AnimFrameCount;
-				animLen = f + (float)_walkPart0AnimLen + intpart * _walkPart1AnimLen;
+				_walkTotalFrames = (int)(_walkLoopAnimFrameCount * intpart) + _walkStartAnimFrameCount;
+				animLen = walkEndLen + (float)_walkStartAnimLen + intpart * _walkLoopAnimLen;
 			}
 		} else {
-			animLen = (float)(_walkPart0AnimLen + _walkPart3AnimLen);
+			animLen = (float)(_walkStartAnimLen + _walkEndGAnimLen);
 		}
 		_walkCurveIncrement = _walkCurveLen / animLen;
 	}
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 71508f03637..b8111e0cfdb 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -61,7 +61,7 @@ public:
 		Common::String _name;
 		Common::String _modelFileName;
 		TeVector3f32 _defaultScale;
-		Common::String _walkFileName;
+		Common::String _idleAnimFileName;
 		Common::HashMap<Common::String, WalkSettings> _walkSettings; // keys are "Walk", "Jog", etc
 		float _walkSpeed;
 
@@ -86,10 +86,10 @@ public:
 	};
 
 	struct Callback {
-		int x;
-		Common::String s;
-		int y;
-		float f;
+		int _x;
+		Common::String _s;
+		int _y;
+		float _f;
 	};
 
 	void addCallback(const Common::String &s1, const Common::String &s2, float f1, float f2);
@@ -171,14 +171,15 @@ public:
 	Character *charLookingAt() { return _charLookingAt; }
 	bool lookingAtTallThing() const { return _lookingAtTallThing; }
 	void setLookingAtTallThing(bool val) { _lookingAtTallThing = val; }
+	TeIntrusivePtr<TeBezierCurve> curve() { return _curve; }
 
 private:
-	float _curveOffset;
 	float _walkCurveStart;
+	float _walkCurveLast;
 	float _walkCurveEnd;
 	float _walkCurveLen;
 	float _walkCurveIncrement;
-	float _walkCurveCurOffset;
+	float _walkCurveNextLength;
 	float _walkedLength;
 	int _walkTotalFrames;
 	bool _walkToFlag;
@@ -201,13 +202,13 @@ private:
 
 	CharacterSettings _characterSettings;
 
-	int _walkPart0AnimLen;
-	int _walkPart1AnimLen;
-	int _walkPart3AnimLen;
+	float _walkStartAnimLen;
+	float _walkLoopAnimLen;
+	float _walkEndGAnimLen;
 
-	uint32 _walkPart0AnimFrameCount;
-	uint32 _walkPart1AnimFrameCount;
-	uint32 _walkPart3AnimFrameCount;
+	uint32 _walkStartAnimFrameCount;
+	uint32 _walkLoopAnimFrameCount;
+	uint32 _walkEndGAnimFrameCount;
 
 	int _lastFrame;
 	int _lastAnimFrame;
diff --git a/engines/tetraedge/game/character_settings_xml_parser.cpp b/engines/tetraedge/game/character_settings_xml_parser.cpp
index 2c306c32351..abb8fc55d68 100644
--- a/engines/tetraedge/game/character_settings_xml_parser.cpp
+++ b/engines/tetraedge/game/character_settings_xml_parser.cpp
@@ -141,7 +141,7 @@ bool CharacterSettingsXmlParser::textCallback(const Common::String &val) {
 		_curCharacter->_defaultScale.parse(val);
 		break;
 	case TagAnimationFileName:
-		_curCharacter->_walkFileName = val;
+		_curCharacter->_idleAnimFileName = val;
 		break;
 	case TagEyes:
 		_curCharacter->_defaultEyes = val;
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 07847b0845d..8b210514963 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -19,7 +19,10 @@
  *
  */
 
+#include "tetraedge/tetraedge.h"
 #include "tetraedge/game/dialog2.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/game/character.h"
 #include "tetraedge/te/te_button_layout.h"
 
 namespace Tetraedge {
@@ -39,7 +42,53 @@ bool Dialog2::isDialogPlaying() {
 }
 
 void Dialog2::launchNextDialog() {
-	error("TODO: Implement Dialog2::launchNextDialog.");
+	Game *game = g_engine->getGame();
+	if (_dialogs.empty()) {
+		game->showMarkers(false);
+		_gui.buttonLayoutChecked("dialogLockButton")->setVisible(false);
+		return;
+	}
+
+	TeButtonLayout *dialog = _gui.buttonLayoutChecked("dialog");
+	if (dialog->anchor().y() >= 1.0) {
+		TeCurveAnim2<TeLayout, TeVector3f32> *anim = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+		anim->stop();
+		anim->play();
+	} else {
+		dialog->setSizeType(CoordinatesType::ABSOLUTE);
+		TeButtonLayout *lockBtn = _gui.buttonLayoutChecked("dialogLockButton");
+		dialog->setSize(lockBtn->size());
+		_currentDialogData = _dialogs.front();
+		_dialogs.remove_at(0);
+		const Common::String formatStr = _gui.value("textFormat").toString();
+		Common::String formattedVal = Common::String::format(formatStr.c_str(), _currentDialogData._stringVal.c_str());
+		_gui.textLayout("text")->setText(formattedVal);
+		_music.load(_currentDialogData._sound.toString());
+		_music.setChannelName("dialog");
+		_music.play();
+		if (!_currentDialogData._charname.empty()) {
+			Character *c = game->scene().character(_currentDialogData._charname);
+			if (!c) {
+				error("[Dialog2::launchNextDialog] Character's \"%s\" doesn\'t exist", _currentDialogData._charname.c_str());
+			}
+
+			if (_currentDialogData._animBlend == 0.0f) {
+				if (!c->setAnimation(_currentDialogData._animfile, false))
+					error("[Dialog2::launchNextDialog] Character's animation \"%s\" doesn't exist for the character\"%s\"  \n",
+							_currentDialogData._animfile.c_str(), _currentDialogData._charname.c_str());
+			} else {
+				if (!c->blendAnimation(_currentDialogData._animfile, _currentDialogData._animBlend, false, true))
+					error("[Dialog2::launchNextDialog] Character's animation \"%s\" doesn't exist for the character\"%s\"  \n",
+							_currentDialogData._animfile.c_str(), _currentDialogData._charname.c_str());
+			}
+		}
+		_gui.buttonLayoutChecked("dialogLockButton")->setVisible(true);
+		TeCurveAnim2<TeLayout, TeVector3f32> *anim = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+		anim->stop();
+		anim->play();
+		_minimumTimeTimer.start();
+		_minimumTimeTimer.setAlarmIn(1500000);
+	}
 }
 
 void Dialog2::load() {
@@ -115,13 +164,29 @@ bool Dialog2::onSoundFinished() {
 	return false;
 }
 
-void Dialog2::pushDialog(const Common::String &param_1, const Common::String &param_2, const Common::String &param_3, int param_4) {
+void Dialog2::pushDialog(const Common::String &name, const Common::String &textVal, const Common::String &sound, int param_4) {
 	error("TODO: Implement Dialog2::pushDialog");
 }
 
-void Dialog2::pushDialog(const Common::String &param_1, const Common::String &param_2, const Common::String &param_3,
-						 const Common::String &param_4, const Common::String &param_5, float param_6) {
-	error("TODO: Implement Dialog2::pushDialog");
+void Dialog2::pushDialog(const Common::String &name, const Common::String &textVal, const Common::String &sound,
+						 const Common::String &charname, const Common::String &animfile, float animBlend) {
+	DialogData data;
+	data._name = name;
+	data._stringVal = textVal;
+	data._charname = charname;
+	data._animfile = animfile;
+	data._sound = Common::Path("sounds/Dialogs").join(sound);
+	data._animBlend = animBlend;
+	if (sound.empty()) {
+		data._sound = Common::Path("sounds/dialogs/silence5s.ogg");
+	}
+	_dialogs.push_back(data);
+	if (_dialogs.size() == 1) {
+		Game *game = g_engine->getGame();
+		game->showMarkers(true);
+	}
+	if (!_music.isPlaying())
+		launchNextDialog();
 }
 
 //void saveToBackup(TiXmlNode *node)
diff --git a/engines/tetraedge/game/dialog2.h b/engines/tetraedge/game/dialog2.h
index 9e2a8f025ad..a4c4a269a29 100644
--- a/engines/tetraedge/game/dialog2.h
+++ b/engines/tetraedge/game/dialog2.h
@@ -32,8 +32,13 @@ class Dialog2 : public TeLayout {
 public:
 	Dialog2();
 
-	class DialogData {
-		bool operator=(const DialogData &other);
+	struct DialogData {
+		Common::String _name;
+		Common::String _stringVal;
+		Common::Path _sound;
+		Common::String _charname;
+		Common::String _animfile;
+		float _animBlend;
 	};
 
 	bool isDialogPlaying();
@@ -46,9 +51,9 @@ public:
 	bool onSkipButton();
 	bool onSoundFinished();
 
-	void pushDialog(const Common::String &param_1, const Common::String &param_2, const Common::String &param_3, int param_4);
-	void pushDialog(const Common::String &param_1, const Common::String &param_2, const Common::String &param_3,
-					const Common::String &param_4, const Common::String &param_5, float param_6);
+	void pushDialog(const Common::String &name, const Common::String &textVal, const Common::String &sound, int param_4);
+	void pushDialog(const Common::String &name, const Common::String &textVal, const Common::String &sound,
+					const Common::String &charName, const Common::String &animFile, float animBlend);
 	//void saveToBackup(TiXmlNode *node)
 	void startDownAnimation();
 	void unload();
@@ -58,6 +63,8 @@ public:
 	Common::String prevSceneName() { return _prevSceneName; };
 
 private:
+	Common::Array<DialogData> _dialogs;
+
 	TeTimer _minimumTimeTimer;
 
 	Common::String _prevSceneName;
@@ -65,6 +72,8 @@ private:
 
 	TeLuaGUI _gui;
 	TeMusic _music;
+	
+	DialogData _currentDialogData;
 
 	TeSignal1Param<const Common::String &> _onAnimationDownFinishedSignal;
 };
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 9c06be767a0..b65287d92d0 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -439,7 +439,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_scene._character->_model->setVisible(true);
 		_scene._character->deleteAllCallback();
 		_scene._character->stop();
-		_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true);
+		_scene._character->setAnimation(_scene._character->characterSettings()._idleAnimFileName, true);
 		if (!_scene.findKate()) {
 			_scene.models().push_back(_scene._character->_model);
 			_scene.models().push_back(_scene._character->_shadowModel[0]);
@@ -648,10 +648,31 @@ bool Game::isMoviePlaying() {
 	return false;
 }
 
+static const char *DIALOG_IDS[20] = {
+	"KFJ1", "KH", "KJ", "KL",
+	"KO", "KS", "KCa", "KFE2",
+	"KFE3", "KG", "KMa", "KP",
+	"KR", "KCo", "KD", "KA",
+	"KFJ", "KM", "KN", "KFM"};
+
 bool Game::launchDialog(const Common::String &dname, uint param_2, const Common::String &charname,
 				  const Common::String &animfile, float param_5) {
-	error("TODO: Implemet Game::launchDialog %s %d %s %s %f", dname.c_str(),
-			param_2, charname.c_str(), animfile.c_str(), param_5);
+	Application *app = g_engine->getApplication();
+	const Common::String *locdname = app->_loc.value(dname);
+	if (!locdname)
+		locdname = &dname;
+
+	if (!locdname)
+		return false;
+
+	for (unsigned int i = 0; i < ARRAYSIZE(DIALOG_IDS); i++) {
+		if (dname.contains(Common::String::format("_%s_", DIALOG_IDS[i])))
+			_dialogsTold++;
+	}
+
+	const Common::String sndfile = dname + ".ogg";
+	_dialog2.pushDialog(*locdname, *locdname, sndfile, charname, animfile, param_5);
+	return true;
 }
 
 void Game::leave(bool flag) {
@@ -793,7 +814,7 @@ bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
 			|| curAnimName == character->walkAnim(Character::WalkPart_EndG))
 			character->stop();
 	} else {
-		if (!_sceneCharacterVisibleFromLoad && curAnimName != character->walkAnim(Character::WalkPart_Start)) {
+		if (!_sceneCharacterVisibleFromLoad && curAnimName == character->walkAnim(Character::WalkPart_Start)) {
 			character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
 			return false;
 		}
@@ -802,7 +823,7 @@ bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
 			character->updatePosition(1.0);
 			character->endMove();
 			// Note: original checks walkAnim again.. is there a reason to do that?
-			character->setAnimation(character->characterSettings()._walkFileName, true);
+			character->setAnimation(character->characterSettings()._idleAnimFileName, true);
 		}
 	}
 
@@ -831,7 +852,7 @@ bool Game::onDialogFinished(const Common::String &val) {
 bool Game::onDisplacementFinished() {
 	_sceneCharacterVisibleFromLoad = true;
 	_scene._character->stop();
-	_scene._character->setAnimation(_scene._character->characterSettings()._walkFileName, true);
+	_scene._character->setAnimation(_scene._character->characterSettings()._idleAnimFileName, true);
 
 	if (!_isCharacterWalking) {
 		_isCharacterWalking = false;
@@ -941,7 +962,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 	Character *character = _scene._character;
 	const Common::String &charAnim = character->curAnimName();
 
-	if (charAnim == character->characterSettings()._walkFileName
+	if (charAnim == character->characterSettings()._idleAnimFileName
 		|| charAnim == character->walkAnim(Character::WalkPart_Start)
 		|| charAnim == character->walkAnim(Character::WalkPart_Loop)
 		|| charAnim == character->walkAnim(Character::WalkPart_EndD)
@@ -987,7 +1008,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 		_posPlayer = lastPoint;
 	}
 
-	if (!_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._walkFileName)) {
+	if (!_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._idleAnimFileName)) {
 		_lastCharMoveMousePos = TeVector2s32(0, 0);
 		_movePlayerCharacterDisabled = true;
 		_isCharacterWalking = false;
@@ -1456,7 +1477,7 @@ void Game::update() {
 						charAnim == _scene._character->walkAnim(Character::WalkPart_Loop) ||
 						charAnim == _scene._character->walkAnim(Character::WalkPart_EndD) ||
 						charAnim == _scene._character->walkAnim(Character::WalkPart_EndG) ||
-						charAnim == _scene._character->characterSettings()._walkFileName);
+						charAnim == _scene._character->characterSettings()._idleAnimFileName);
 				app->lockCursor(!unlockCursor);
 			}
 		}
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 5be7a135c84..6df97b971d5 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -42,6 +42,8 @@
 #include "tetraedge/te/te_lua_script.h"
 #include "tetraedge/te/te_lua_thread.h"
 
+#define DEBUG_PATHFINDING 1
+
 namespace Tetraedge {
 
 InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr),
@@ -338,6 +340,19 @@ void InGameScene::draw() {
 		return;
 
 	currentCamera()->apply();
+
+#if DEBUG_PATHFINDING
+	if (_character && _character->curve()) {
+		_character->curve()->setVisible(true);
+		_character->curve()->draw();
+	}
+
+	for (TeFreeMoveZone *zone : _freeMoveZones) {
+		zone->setVisible(true);
+		zone->draw();
+	}
+#endif
+
 	TeLight::updateGlobal();
 	for (unsigned int i = 0; i < _lights.size(); i++)
 		_lights[i].update(i);
@@ -920,8 +935,8 @@ void InGameScene::setPositionCharacter(const Common::String &charName, const Com
 		TeFreeMoveZone *zone = pathZone(freeMoveZoneName);
 		if (!zone) {
 			warning("[SetCharacterPosition] PathZone not found %s", freeMoveZoneName.c_str());
-			for (TeFreeMoveZone *zone : _freeMoveZones)
-				warning("zone: %s", zone->name().c_str());
+			for (TeFreeMoveZone *z : _freeMoveZones)
+				warning("zone: %s", z->name().c_str());
 			return;
 		}
 		TeIntrusivePtr<TeCamera> cam = currentCamera();
@@ -973,7 +988,13 @@ void InGameScene::unloadCharacter(const Common::String &name) {
 }
 
 void InGameScene::unloadObject(const Common::String &name) {
-	error("TODO: InGameScene::unloadObject");
+	for (unsigned int i = 0; i < _object3Ds.size(); i++) {
+		if (_object3Ds[i]->model()->name() == name) {
+			_object3Ds[i]->deleteLater();
+			_object3Ds.remove_at(i);
+			break;
+		}
+	}
 }
 
 void InGameScene::unloadSpriteLayouts() {
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 792333c9e7e..6b42aebf130 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -42,7 +42,7 @@ void Inventory::enter() {
 	Game *game = g_engine->getGame();
 	Character *character = game->scene()._character;
 	character->stop();
-	character->setAnimation(character->characterSettings()._walkFileName, true);
+	character->setAnimation(character->characterSettings()._idleAnimFileName, true);
 	_gui.layoutChecked("textObject")->setVisible(false);
 
 	if (!game->_firstInventory) {
@@ -188,9 +188,62 @@ void Inventory::addObject(const Common::String &objId) {
 }
 
 bool Inventory::addObject(InventoryObject &obj) {
-	_invObjects.push_back(&obj);
+	_invObjects.push_front(&obj);
 	obj.selectedSignal().add(this, &Inventory::onObjectSelected);
-	error("TODO: implement Inventory::addObject.");
+	if (_invObjects.size() > 1) {
+		int pageno = 0;
+		while (true) {
+			TeLayout *page = _gui.layout(Common::String::format("page%d", pageno));
+			int slotno = 0;
+			if (!page)
+				break;
+			while (true) {
+				TeLayout *slot = _gui.layout(Common::String::format("page%dSlot%d", pageno, slotno));
+				if (!slot)
+					break;
+				for (unsigned int c = 0; c < slot->childCount(); c++) {
+					Te3DObject2 *child = slot->child(c);
+					InventoryObject *obj = dynamic_cast<InventoryObject *>(child);
+					if (obj) {
+						slot->removeChild(child);
+						c--;
+					}
+				}
+			}
+			pageno++;
+		}
+    }
+	
+	int pageno = 0;
+	unsigned int totalSlots = 0;
+	bool retval;
+	while (true) {
+		TeLayout *page = _gui.layout(Common::String::format("page%d", pageno));
+		retval = false;
+		if (!page)
+			break;
+		int slotno = 0;
+		while (true) {
+			TeLayout *slot = _gui.layout(Common::String::format("page%dSlot%d", pageno, slotno));
+			if (!slot)
+				break;
+			retval = true;
+
+			if (totalSlots == _invObjects.size()) {
+				// break from both loops (slight hack..)
+				pageno = 9999;
+				break;
+			}
+			
+			TeTextLayout *newText = new TeTextLayout();
+			error("TODO: finish Inventory::addObject.");
+
+			totalSlots++;
+			slotno++;
+		}
+		pageno++;
+	}
+	return retval;
 }
 
 bool Inventory::isDocument(const Common::String &objId) {
diff --git a/engines/tetraedge/game/inventory.h b/engines/tetraedge/game/inventory.h
index aa81722db0e..53661da3ae5 100644
--- a/engines/tetraedge/game/inventory.h
+++ b/engines/tetraedge/game/inventory.h
@@ -84,7 +84,7 @@ private:
 	void loadXMLFile(const Common::Path &path);
 
 	TeLuaGUI _gui;
-	Common::Array<InventoryObject *> _invObjects;
+	Common::List<InventoryObject *> _invObjects;
 	Cellphone *_cellphone;
 	InventoryObject *_selectedObject;
 	Common::HashMap<Common::String, InventoryObjectData> _objectData;
diff --git a/engines/tetraedge/game/inventory_object.cpp b/engines/tetraedge/game/inventory_object.cpp
index f12e169b6fb..8bf17bc5662 100644
--- a/engines/tetraedge/game/inventory_object.cpp
+++ b/engines/tetraedge/game/inventory_object.cpp
@@ -26,12 +26,12 @@ namespace Tetraedge {
 InventoryObject::InventoryObject() {
 }
 
-void InventoryObject::load(const Common::String &name) {
+void InventoryObject::load(const Common::String &newName) {
 	setSizeType(RELATIVE_TO_PARENT);
 	setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
 	_gui.load("Inventory/InventoryObject.lua");
 	addChild(_gui.layoutChecked("object"));
-	setName(name);
+	setName(newName);
 	_gui.spriteLayoutChecked("upLayout")->load(spritePath());
 	TeButtonLayout *btn = _gui.buttonLayoutChecked("object");
 	btn->onMouseClickValidated().add(this, &InventoryObject::onButtonDown);
diff --git a/engines/tetraedge/game/inventory_object.h b/engines/tetraedge/game/inventory_object.h
index 5464f813811..ccee9fe5a14 100644
--- a/engines/tetraedge/game/inventory_object.h
+++ b/engines/tetraedge/game/inventory_object.h
@@ -28,7 +28,7 @@
 
 namespace Tetraedge {
 
-class InventoryObject : TeLayout {
+class InventoryObject : public TeLayout {
 public:
 	InventoryObject();
 
@@ -37,9 +37,7 @@ public:
 	bool onButtonDown();
 	TeSignal1Param<InventoryObject&> &selectedSignal() { return _selectedSignal; };
 
-	const Common::String &name() const { return _name; }
 private:
-	Common::String _name;
 	TeLuaGUI _gui;
 	TeSignal1Param<InventoryObject&> _selectedSignal;
 };
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 1c11e704b0f..290d94b2546 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -27,6 +27,7 @@
 #include "tetraedge/game/lua_binds.h"
 #include "tetraedge/game/object3d.h"
 #include "tetraedge/to_lua.h"
+#include "tetraedge/te/te_lua_thread.h"
 
 namespace Tetraedge {
 
@@ -90,6 +91,40 @@ static int tolua_ExportedFunctions_SetSoundStep00(lua_State *L) {
 	error("#ferror in function 'SetSoundStep': %d %d %s", err.index, err.array, err.type);
 }
 
+static bool Selected(const Common::String &obj) {
+	Game *game = g_engine->getGame();
+	return game->inventory().selectedObject() == obj;
+}
+
+static int tolua_ExportedFunctions_Selected00(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));
+		bool result = Selected(s1);
+		tolua_pushboolean(L, result);
+		return 1;
+	}
+	error("#ferror in function 'Selected': %d %d %s", err.index, err.array, err.type);
+}
+
+static void TakeObject(const Common::String &obj) {
+	Game *game = g_engine->getGame();
+	warning("TODO: Set global _lastHitObjectName");
+	//game->luaContext().setGlobal(_lastHitObjectName, true);
+	if (!obj.empty())
+		game->addToBag(obj);
+}
+
+static int tolua_ExportedFunctions_TakeObject00(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));
+		TakeObject(s1);
+		return 0;
+	}
+	error("#ferror in function 'TakeObject': %d %d %s", err.index, err.array, err.type);
+}
+
 static void AddNumber(const Common::String &number) {
 	Game *game = g_engine->getGame();
 	if (!game->inventory().cellphone()->addNumber(number))
@@ -191,6 +226,32 @@ static int tolua_ExportedFunctions_MoveCharacterPlayerDisabled00(lua_State *L) {
 	error("#ferror in function 'MoveCharacterPlayerDisabled': %d %d %s", err.index, err.array, err.type);
 }
 
+static void AddCallback(const Common::String &charName, const Common::String &key, const Common::String &s1, float f1, float f2) {
+	Game *game = g_engine->getGame();
+	Character *c = game->scene().character(charName);
+	if (c) {
+		c->addCallback(key, s1, f1, f2);
+	} else {
+		warning("[AddCallback] Character's \"%s\" doesn't exist", charName.c_str());
+	}
+}
+
+static int tolua_ExportedFunctions_AddCallback00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+			&& tolua_isstring(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+			&& tolua_isnumber(L, 5, 1, &err) && tolua_isnoobj(L, 6, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		Common::String s3(tolua_tostring(L, 2, nullptr));
+		double n1 = tolua_tonumber(L, 3, 0.0);
+		double n2 = tolua_tonumber(L, 4, -1.0);
+		AddCallback(s1, s2, s3, n1, n2);
+		return 0;
+	}
+	error("#ferror in function 'AddMarker': %d %d %s", err.index, err.array, err.type);
+}
+
 static void AddMarker(const Common::String &markerName, const Common::String &imgPath, float x, float y,
 				const Common::String &loctype, const Common::String &markerVal) {
 	Game *game = g_engine->getGame();
@@ -371,7 +432,6 @@ static int tolua_ExportedFunctions_UnloadObject00(lua_State *L) {
 }
 
 static void SetCharacterRotation(const Common::String &charname, float rx, float ry, float rz) {
-	// TODO: check if this is good.
 	TeQuaternion quat = TeQuaternion::fromEuler(TeVector3f32(rx * M_PI / 180.0, ry * M_PI / 180.0, rz * M_PI / 180.0));
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charname);
@@ -397,6 +457,60 @@ static int tolua_ExportedFunctions_SetCharacterRotation00(lua_State *L) {
 	error("#ferror in function 'SetCharacterRotation': %d %d %s", err.index, err.array, err.type);
 }
 
+static void SetCharacterOrientation(const Common::String &charname, float x, float y) {
+	Game *game = g_engine->getGame();
+	Character *c = game->scene().character(charname);
+	if (c) {
+		const TeVector3f32 pos = c->_model->position();
+		TeVector3f32 euler(0, atan2f(abs(x - pos.x()), y - pos.z()), 0);
+		c->_model->setRotation(TeQuaternion::fromEuler(euler));
+	} else {
+		warning("[SetCharacterRotation] Character not found %s", charname.c_str());
+	}
+}
+
+static int tolua_ExportedFunctions_SetCharacterOrientation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		SetCharacterOrientation(s1, f1, f2);
+		return 0;
+	}
+	error("#ferror in function 'SetCharacterOrientation': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetCharacterAnimation(const Common::String &charname, const Common::String &animname, bool repeat, bool b2, int i1, int i2) {
+	Game *game = g_engine->getGame();
+	Character *c = game->scene().character(charname);
+	bool result = c->setAnimation(animname, repeat, b2, i1, i2);
+	if (!result) {
+		warning("[SetCharacterAnimation] Character's animation \"%s\" doesn't exist for the character\"%s\"  ",
+			animname.c_str(), charname.c_str());
+	}
+}
+
+static int tolua_ExportedFunctions_SetCharacterAnimation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isboolean(L, 3, 1, &err) && tolua_isboolean(L, 4, 1, &err)
+		&& tolua_isnumber(L, 5, 1, &err) && tolua_isnumber(L, 6, 1, &err)
+		&& tolua_isnoobj(L, 7, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		bool b1 = tolua_toboolean(L, 3, 1);
+		bool b2 = tolua_toboolean(L, 4, 0);
+		double f3 = tolua_tonumber(L, 5, -1.0);
+		double f4 = tolua_tonumber(L, 6, 9999.0);
+		SetCharacterAnimation(s1, s2, b1, b2, (int)f3, (int)f4);
+		return 0;
+	}
+	error("#ferror in function 'SetCharacterAnimation': %d %d %s", err.index, err.array, err.type);
+}
+
+
 static void SetCharacterPosition(const Common::String &charname, const Common::String &zonename, float f1, float f2, float f3) {
 	Game *game = g_engine->getGame();
 	game->scene().setPositionCharacter(charname, zonename, TeVector3f32(f1, f2, f3));
@@ -488,6 +602,58 @@ static int tolua_ExportedFunctions_SetBackground00(lua_State *L) {
 	error("#ferror in function 'SetBackground': %d %d %s", err.index, err.array, err.type);
 }
 
+static void LaunchDialog(const Common::String &name, uint param_2, const Common::String &charname,
+						const Common::String &animfile, float param_5) {
+	Game *game = g_engine->getGame();
+  
+	if (!game->launchDialog(name, param_2, charname, animfile, param_5))
+		warning("[LaunchDialog] Dialog \"%s\" doesn't exist.", name.c_str());
+}
+
+static int tolua_ExportedFunctions_LaunchDialog00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 1, &err)
+		&& tolua_isstring(L, 3, 1, &err) && tolua_isstring(L, 4, 1, &err)
+		&& tolua_isnumber(L, 5, 1, &err) && tolua_isnoobj(L, 6, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 1.0);
+		Common::String s2(tolua_tostring(L, 3, ""));
+		Common::String s3(tolua_tostring(L, 4, ""));
+		float f2 = tolua_tonumber(L, 5, 0.0);
+		LaunchDialog(s1, f1, s2, s3, f2);
+		return 0;
+	}
+	error("#ferror in function 'LaunchDialog': %d %d %s", err.index, err.array, err.type);
+}
+
+static int tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 1, &err)
+		&& tolua_isstring(L, 3, 1, &err) && tolua_isstring(L, 4, 1, &err)
+		&& tolua_isnumber(L, 5, 1, &err) && tolua_isnoobj(L, 6, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 1.0);
+		Common::String s2(tolua_tostring(L, 3, ""));
+		Common::String s3(tolua_tostring(L, 4, ""));
+		float f2 = tolua_tonumber(L, 5, 0.0);
+		LaunchDialog(s1, f1, s2, s3, f2);
+
+		Game::YieldedCallback callback;
+		callback._luaThread = TeLuaThread::threadFromState(L);
+		callback._luaFnName = "OnDialogFinished";
+		callback._luaParam = s1;
+
+		Game *game = g_engine->getGame();
+		for (const auto &cb : game->yieldedCallbacks()) {
+			if (cb._luaFnName == callback._luaFnName && cb._luaParam == callback._luaParam)
+				error("LaunchDialogAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+		}
+
+		return callback._luaThread->yield();
+	}
+	error("#ferror in function 'LaunchDialogAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
+}
+
 static void PushTask(const Common::String &s1, const Common::String &s2) {
 	Game *game = g_engine->getGame();
 	game->objectif().pushObjectif(s1, s2);
@@ -556,6 +722,24 @@ static int tolua_ExportedFunctions_AddAnchorZone00(lua_State *L) {
 	error("#ferror in function 'AddAnchorZone': %d %d %s", err.index, err.array, err.type);
 }
 
+static void ActivateAnchorZone(const Common::String &name, bool b) {
+	if (!name.empty()) {
+		Game *game = g_engine->getGame();
+		game->scene().activateAnchorZone(name, b);
+	}
+}
+
+static int tolua_ExportedFunctions_ActivateAnchorZone00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s(tolua_tostring(L, 1, nullptr));
+		bool b = tolua_toboolean(L, 2, 0);
+		ActivateAnchorZone(s, b);
+		return 0;
+	}
+	error("#ferror in function 'ActivateAnchorZone': %d %d %s", err.index, err.array, err.type);
+}
+
 static void DisabledZone(const Common::String &s1, bool b) {
 	Game *game = g_engine->getGame();
 	if (!game->scene().markerGui().loaded())
@@ -819,11 +1003,11 @@ static void MoveCharacterPlayerTo(float x, float y, float z, bool walkFlag) {
 	if (game->_movePlayerCharacterDisabled)
 		return;
 
-	TeVector3f32 loc(x, y, z);
+	TeVector3f32 dest(x, y, z);
 	game->resetPreviousMousePos();
 
 	Character *character = game->scene()._character;
-	if (loc == game->posPlayer() && character->walkModeStr() == "Walk")
+	if (dest == game->posPlayer() && character->walkModeStr() == "Walk")
 		return;
 
 	if (game->walkTimer().running() && game->walkTimer().timeElapsed() < 300000) {
@@ -839,7 +1023,7 @@ static void MoveCharacterPlayerTo(float x, float y, float z, bool walkFlag) {
 	}
 
 	game->_sceneCharacterVisibleFromLoad = false;
-	TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(character->_model->position(), loc);
+	TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(character->_model->position(), dest);
 	if (!curve) {
 		game->luaScript().execute("OnDisplacementFinished");
 	} else {
@@ -850,7 +1034,7 @@ static void MoveCharacterPlayerTo(float x, float y, float z, bool walkFlag) {
 		character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
 		character->walkTo(1.0, walkFlag);
 		game->_isCharacterWalking = true;
-		game->setPosPlayer(loc);
+		game->setPosPlayer(dest);
 	}
 }
 
@@ -906,10 +1090,10 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "LockCursor", tolua_ExportedFunctions_LockCursor00);
 	/*tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00);
 	tolua_function(L, "UnsetCondition", tolua_ExportedFunctions_UnsetCondition00);
-	tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00);
+	tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00);*/
 	tolua_function(L, "LaunchDialog", tolua_ExportedFunctions_LaunchDialog00);
 	tolua_function(L, "LaunchDialogAndWaitForEnd", tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00);
-	tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
+	/*tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
 	tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);*/
 	tolua_function(L, "PushTask", tolua_ExportedFunctions_PushTask00);
 	/*tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
@@ -922,9 +1106,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "PlayRandomSound", tolua_ExportedFunctions_PlayRandomSound00);
 	tolua_function(L, "PlayMusic", tolua_ExportedFunctions_PlayMusic00);
 	tolua_function(L, "SetSoundStep", tolua_ExportedFunctions_SetSoundStep00);
-	/*tolua_function(L, "Selected", tolua_ExportedFunctions_Selected00);
+	tolua_function(L, "Selected", tolua_ExportedFunctions_Selected00);
 	tolua_function(L, "TakeObject", tolua_ExportedFunctions_TakeObject00);
-	tolua_function(L, "TakeObjectInHand", tolua_ExportedFunctions_TakeObjectInHand00);
+	/*tolua_function(L, "TakeObjectInHand", tolua_ExportedFunctions_TakeObjectInHand00);
 	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
 	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject01);*/
 	tolua_function(L, "AddNumber", tolua_ExportedFunctions_AddNumber00);
@@ -948,9 +1132,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
 	/*tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);*/
 	tolua_function(L, "SetCharacterRotation", tolua_ExportedFunctions_SetCharacterRotation00);
-	/*tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
+	tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
 	tolua_function(L, "SetCharacterAnimation", tolua_ExportedFunctions_SetCharacterAnimation00);
-	tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
+	/*tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
 				 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);
 	tolua_function(L, "BlendCharacterAnimation", tolua_ExportedFunctions_BlendCharacterAnimation00);
 	tolua_function(L, "BlendCharacterAnimationAndWaitForEnd",
@@ -963,9 +1147,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200);
 	tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00);
 	tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00);
-	tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);
+	tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);*/
 	tolua_function(L, "AddCallback", tolua_ExportedFunctions_AddCallback00);
-	tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00);
+	/*tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00);
 	tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00);
 	tolua_function(L, "DeleteCallback", tolua_ExportedFunctions_DeleteCallback00);
 	tolua_function(L, "DeleteCallbackPlayer", tolua_ExportedFunctions_DeleteCallbackPlayer00);
@@ -1009,9 +1193,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);*/
 	tolua_function(L, "EnableRectBlocker", tolua_ExportedFunctions_EnableRectBlocker00);
 	/*tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);*/
-	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);/*
+	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
 	tolua_function(L, "ActivateAnchorZone", tolua_ExportedFunctions_ActivateAnchorZone00);
-	tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00);
+	/*tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00);
 	tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
 	tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
 	tolua_function(L, "SetCharacterMeshVisible", tolua_ExportedFunctions_SetCharacterMeshVisible00);
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index c4ac06bbb11..d4997914a24 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -196,6 +196,11 @@ void Te3DObject2::setPosition(const TeVector3f32 &pos) {
 	if (_position == pos)
 		return;
 
+	if ((_position - pos).length() > 2.0f && name() == "Kate" && _position != TeVector3f32()) {
+		debug("Large position move %s %s -> %s", name().c_str(),
+			_position.dump().c_str(), pos.dump().c_str());
+	}
+
 	_position = pos;
 	_onPositionChangedSignal.call();
 	_onParentWorldTransformationMatrixChangedSignal.call();
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index ac1353e59ec..ac94154adbc 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -170,17 +170,17 @@ bool Te3DTexture::load(const TeImage &img) {
 
 	const void *imgdata = img.getPixels();
 	if (_format == TeImage::RGB8) {
-		GLenum glpxformat = GL_RGB;
+		/*GLenum glpxformat = GL_RGB;
 		if (_glPixelFormat != GL_INVALID_ENUM) {
 			glpxformat = _glPixelFormat;
-		}
+		}*/
 		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 glpxformat = GL_RGBA8;
+		/*GLenum glpxformat = GL_RGBA8;
 		if (_glPixelFormat != GL_INVALID_ENUM) {
 			glpxformat = _glPixelFormat;
-		}
+		}*/
 		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);
 	} else {
diff --git a/engines/tetraedge/te/te_bezier_curve.cpp b/engines/tetraedge/te/te_bezier_curve.cpp
index d55a3203db0..8846cbbfd3a 100644
--- a/engines/tetraedge/te/te_bezier_curve.cpp
+++ b/engines/tetraedge/te/te_bezier_curve.cpp
@@ -20,6 +20,10 @@
  */
 
 #include "tetraedge/te/te_bezier_curve.h"
+#include "tetraedge/te/te_mesh.h"
+#include "tetraedge/te/te_renderer.h"
+#include "tetraedge/tetraedge.h"
+
 
 namespace Tetraedge {
 
@@ -37,7 +41,36 @@ void TeBezierCurve::clear() {
 }
 
 void TeBezierCurve::draw() {
-	error("TODO: Implement TeBezierCurve::draw");
+	if (!worldVisible() || _controlPoints.empty())
+		return;
+
+    TeMesh mesh1;
+    TeMesh mesh2;
+    unsigned int npoints = _controlPoints.size();
+
+	mesh1.setConf(npoints, npoints, TeMesh::MeshMode_Points, 0, 0);
+	for (unsigned int i = 0; i < npoints; i++) {
+		mesh1.setVertex(i, _controlPoints[i]);
+		mesh1.setIndex(i, i);
+	}
+
+	mesh2.setConf(npoints, npoints, TeMesh::MeshMode_LineStrip, 0, 0);
+	for (unsigned int i = 0; i < npoints; i++) {
+		mesh2.setVertex(i, _controlPoints[i]);
+		mesh2.setNormal(i, TeVector3f32(0.0f, 1.0f, 0.0));
+		mesh2.setIndex(i, i);
+	}
+
+	TeRenderer *renderer = g_engine->getRenderer();
+	const TeColor prevColor = renderer->currentColor();
+	renderer->pushMatrix();
+	renderer->multiplyMatrix(worldTransformationMatrix());
+	renderer->setCurrentColor(TeColor(0, 0xff, 0xff, 0xff));
+	mesh2.draw();
+	renderer->setCurrentColor(TeColor(0xff, 0, 0xff, 0xff));
+	mesh1.draw();
+	renderer->popMatrix();
+	renderer->setCurrentColor(prevColor);
 }
 
 float TeBezierCurve::length() {
@@ -47,9 +80,11 @@ float TeBezierCurve::length() {
 		_lengths.clear();
 
 		TeVector3f32 lastpt = _controlPoints[0];
+		lastpt.y() = 0;
 		for (unsigned int i = 0; i < _numiterations; i++) {
 			float amount = (float)i / _numiterations;
-			const TeVector3f32 pt = retrievePoint(amount);
+			TeVector3f32 pt = retrievePoint(amount);
+			pt.y() = 0;
 			float len = (lastpt - pt).length();
 			_length += len;
 			_lengths.push_back(_length);
@@ -59,15 +94,14 @@ float TeBezierCurve::length() {
 	return _length;
 }
 
-void TeBezierCurve::pseudoTangent(float f, TeVector3f32 &v1, TeVector3f32 &v2) {
-	const float numiters = _numiterations;
-
-	if (1.0 / numiters + f <= 1.0) {
-		v1 = retrievePoint(f);
-		v2 = retrievePoint(1.0 / numiters + f);
+void TeBezierCurve::pseudoTangent(float offset, TeVector3f32 &v1, TeVector3f32 &v2) {
+	const float step = 1.0f / _numiterations;
+	if (step + offset <= 1.0f) {
+		v1 = retrievePoint(offset);
+		v2 = retrievePoint(step + offset);
 	} else {
-		v2 = retrievePoint(f);
-		v1 = retrievePoint(f - 1.0 / numiters);
+		v1 = retrievePoint(offset - step);
+		v2 = retrievePoint(offset);
 	}
 }
 
@@ -77,13 +111,10 @@ float TeBezierCurve::rawLength() {
 		_rawLength = 0.0;
 		_rawLengths.clear();
 		_rawLengths.push_back(0.0);
-		if (_controlPoints.size() > 1) {
-			for (unsigned int i = 1; i < _controlPoints.size(); i++) {
-				const TeVector3f32 diff = _controlPoints[i] - _controlPoints[i - 1];
-				float len = diff.length();
-				_rawLength += len;
-				_rawLengths.push_back(_rawLength);
-			}
+		for (unsigned int i = 1; i < _controlPoints.size(); i++) {
+			const TeVector3f32 diff = _controlPoints[i] - _controlPoints[i - 1];
+			_rawLength += diff.length();
+			_rawLengths.push_back(_rawLength);
 		}
 	}
 	return _rawLength;
@@ -104,50 +135,40 @@ TeVector3f32 TeBezierCurve::retrievePoint(float offset) {
 	const float rawlen = rawLength();
 
 	float proportion = 0.0f;
-	int i = 0;
-	while (i < npoints) {
-		proportion = _rawLengths[i] / rawlen;
+	int startpt = 0;
+	while (startpt < npoints) {
+		proportion = _rawLengths[startpt] / rawlen;
 		if (proportion >= offset)
 			break;
-		i++;
+		startpt++;
 	}
+
 	float t;
 	if (proportion == offset) {
+		// Exactly on a point
 		t = 0.0f;
 	} else {
-		float p1 = _rawLengths[i - 1];
-		float p2 = _rawLengths[i];
+		// Proportion between two points
+		float p1 = _rawLengths[startpt - 1];
+		float p2 = _rawLengths[startpt];
 		t = (rawlen * offset - p1) / (p2 - p1);
-		i--;
+		startpt--;
 	}
 
+	// Collect 4 points around the startpt (1 before, 2 after)
 	TeVector3f32 points[4];
-	TeVector3f32 *ptbuf = points;
 	const int maxPt = _controlPoints.size() - 1;
-	int p = -1;
-	do {
-		int ptno = 0;
-		if (i + p >= 0)
-			ptno = MIN(i + p, maxPt);
-		*ptbuf = _controlPoints[ptno];
-		ptbuf = ptbuf + 1;
-		p = p + 1;
-	} while (p != 3);
-
-	if (i < 0) {
-		points[0] += (points[1] - points[2]);
-	} else {
-		int ptno = MIN(i, maxPt);
-		if (ptno == 0)
-			points[0] += (points[1] - points[2]);
+	for (int p = 0; p < 4; p++) {
+		int ptno = CLIP(startpt + p - 1, 0, maxPt);
+		points[p] = _controlPoints[ptno];
 	}
-	int ptno = 0;
-	i++;
-	if (i >= 0)
-		ptno = MIN(i, maxPt);
 
-	if (ptno == maxPt)
-		points[3] += points[2] - points[1];
+	// If we hit the end, linearly extend the last gradient.
+	if (startpt <= 0)
+		points[0] += (points[1] - points[2]);
+
+	if (startpt + 1 >= maxPt)
+		points[3] += (points[2] - points[1]);
 
 	return hermiteInterpolate(t, points, 0.0, 0.0);
 }
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index cb7cb4437e7..1703f16a61f 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -53,7 +53,7 @@ class TeFreeMoveZoneGraph : micropather::Graph {
 
 TeFreeMoveZone::TeFreeMoveZone() : _actzones(nullptr), _blockers(nullptr), _rectBlockers(nullptr),
 _transformedVerticiesDirty(true), _bordersDirty(true), _pickMeshDirty(true), _projectedPointsDirty(true),
-_loadedFromBin(false)
+_loadedFromBin(false), _gridWorldY(0.0), _gridOffsetSomething(5.0f, 5.0f), _gridDirty(true)
 {
 	_graph = new TeFreeMoveZoneGraph();
 	_graph->_bordersDistance = 2048.0f;
@@ -102,8 +102,9 @@ Common::Array<TeVector3f32> TeFreeMoveZone::collisions(const TeVector3f32 &v1, c
 TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, bool *flagout, bool intersectFlag) {
 	float f = 0.0;
 	TeVector3f32 intersectPoint;
-	if (!intersect(pos, TeVector3f32(0, -1, 0), intersectPoint, f, intersectFlag, nullptr)) {
-		if (!intersect(pos, TeVector3f32(0, 1, 0), intersectPoint, f, intersectFlag, nullptr)) {
+	TeVector3f32 testPos(pos.x(), 0, pos.z());
+	if (!intersect(testPos, TeVector3f32(0, -1, 0), intersectPoint, f, intersectFlag, nullptr)) {
+		if (!intersect(testPos, TeVector3f32(0, 1, 0), intersectPoint, f, intersectFlag, nullptr)) {
 			if (*flagout)
 				*flagout = false;
 			return pos;
@@ -131,7 +132,7 @@ TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt,
 	updateGrid(false);
 	const TeVector2s32 projectedStart = projectOnAStarGrid(startpt);
 	const TeVector2s32 projectedEnd = projectOnAStarGrid(endpt);
-	int xsize = _graph->_size._x;
+	const int xsize = _graph->_size._x;
 	float cost = 0;
 	// Passing an int to void*, yuck? but it's what the original does..
 	Common::Array<void *> path;
@@ -147,9 +148,7 @@ TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt,
 		for (auto pathpt : path) {
 			// each path point is an array offset
 			int offset = static_cast<int>(reinterpret_cast<size_t>(pathpt));
-			int yoff = offset / _graph->_size._x;
-			int xoff = offset % _graph->_size._x;
-			points[i] = TeVector2s32(xoff, yoff);
+			points[i] = TeVector2s32(offset % xsize, offset / xsize);
 			i++;
 		}
 
@@ -195,7 +194,7 @@ void TeFreeMoveZone::deserialize(Common::ReadStream &stream, TeFreeMoveZone &des
 
 	TeVector2f32::deserialize(stream, dest._someGridVec1);
 	TeVector2f32::deserialize(stream, dest._someGridVec2);
-	dest._someGridFloat = stream.readFloatLE();
+	dest._gridWorldY = stream.readFloatLE();
 
 	dest._graph->deserialize(stream);
 	if (dest.name().contains("19000")) {
@@ -216,8 +215,22 @@ void TeFreeMoveZone::draw() {
 	TePickMesh2::draw();
 	TeMesh mesh;
 	mesh.setConf(_uintArray2.size(), _uintArray2.size(), TeMesh::MeshMode_Lines, 0, 0);
+	for (unsigned int i = 0; i < _uintArray2.size(); i++) {
+		mesh.setIndex(i, i);
+		mesh.setVertex(i, verticies()[_uintArray2[i]]);
+	}
+
+	const TeColor prevColor = renderer->currentColor();
+	renderer->pushMatrix();
+	renderer->multiplyMatrix(worldTransformationMatrix());
+	renderer->setCurrentColor(TeColor(0, 0x80, 0xff, 0xff));
+	mesh.draw();
+	renderer->popMatrix();
+	renderer->setCurrentColor(prevColor);
+
+	// TODO: do a bunch of other drawing stuff here.
 
-	error("TODO: Finish TeFreeMoveZone::draw");
+	renderer->disableWireFrame();
 }
 
 TeVector3f32 TeFreeMoveZone::findNearestPointOnBorder(const TeVector2f32 &pt) {
@@ -363,7 +376,7 @@ TeVector3f32 TeFreeMoveZone::transformAStarGridInWorldSpace(const TeVector2s32 &
 	float offsetx = (float)gridpt._x * _gridOffsetSomething.getX() + _someGridVec1.getX() +
 				_gridOffsetSomething.getX() * 0.5;
 	if (!_loadedFromBin) {
-		return TeVector3f32(offsetx, _someGridFloat, offsety);
+		return TeVector3f32(offsetx, _gridWorldY, offsety);
 	}
 	error("TODO: Implement TeFreeMoveZone::transformAStarGridInWorldSpace");
 }
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index b7f5743777b..f98231f7a4a 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -129,7 +129,7 @@ private:
 	TeVector2f32 _someGridVec1;
 	TeVector2f32 _someGridVec2;
 
-	float _someGridFloat;
+	float _gridWorldY;
 
 	TeOBP _obp;
 	TeIntrusivePtr<TeCamera> _camera;
diff --git a/engines/tetraedge/te/te_lua_thread.cpp b/engines/tetraedge/te/te_lua_thread.cpp
index 928a4d8eb01..b56aab1b937 100644
--- a/engines/tetraedge/te/te_lua_thread.cpp
+++ b/engines/tetraedge/te/te_lua_thread.cpp
@@ -140,6 +140,11 @@ void TeLuaThread::executeFile(const Common::Path &path) {
 	scriptFile.read(buf, fileLen);
 	buf[fileLen] = 0;
 	scriptFile.close();
+	// WORKAROUND: Some script files have rogue ";" lines in them with nothing else, clean those up.
+	char *fixline = strstr(buf, "\n\t;");
+	if (fixline)
+		fixline[2] = '\t';
+
 	_lastResumeResult = luaL_loadbuffer(_luaThread, buf, fileLen, path.toString().c_str());
 	if (_lastResumeResult) {
 		const char *msg = lua_tostring(_luaThread, -1);
@@ -228,8 +233,8 @@ void TeLuaThread::resume(const TeVariant &p1, const TeVariant &p2, const TeVaria
 	return nullptr;
 }
 
-void TeLuaThread::yield() {
-	lua_yield(_luaThread, 0);
+int TeLuaThread::yield() {
+	return lua_yield(_luaThread, 0);
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_lua_thread.h b/engines/tetraedge/te/te_lua_thread.h
index ac2be3cd209..a7c96efffea 100644
--- a/engines/tetraedge/te/te_lua_thread.h
+++ b/engines/tetraedge/te/te_lua_thread.h
@@ -57,7 +57,7 @@ public:
 	void resume(const TeVariant &p1, const TeVariant &p2, const TeVariant &p3);
 
 	static TeLuaThread *threadFromState(lua_State *state);
-	void yield();
+	int yield();
 
 private:
 	void _resume(int nargs);
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 60b2a7728da..7f560f57f5e 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -192,7 +192,6 @@ void TeModel::update() {
 			for (unsigned int i = 0; i < _boneBlenders.size(); i++) {
 				BonesBlender *blender = _boneBlenders[i];
 				float complete = MIN((blender->_timer.getTimeFromStart() / 1000000.0f) / blender->_seconds, 1.0f);
-				TeTRS trs;
 				TeTRS endTRS = getBone(blender->_anim, b);
 				if (complete == 1.0f) {
 					delete blender;
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index e8759bb8701..85b5cd84d45 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -103,12 +103,14 @@ TeQuaternion TeModelAnimation::getNMORotation(unsigned long boneNo, float amount
 			if (i == 0)
 				return arr.front()._rot;
 
-			if (i == arr.size() || arr.size() == 1 || arr[i]._f - arr[i-1]._f == 0) {
+			if (i == arr.size() || arr.size() == 1)
 				return arr.back()._rot;
-			}
 
-			float interp = (amount - arr[i-1]._f) / (arr[i]._f - arr[i-1]._f);
-			return arr[i-1]._rot.slerpQuat(arr[i]._rot, interp);
+			if (arr[i]._f - arr[i-1]._f == 0)
+				return arr[i]._rot;
+
+			float interp = (amount - arr[i - 1]._f) / (arr[i]._f - arr[i - 1]._f);
+			return arr[i - 1]._rot.slerpQuat(arr[i]._rot, interp);
 		}
 	}
 	return TeQuaternion(0, 0, 0, 1.0);
@@ -125,12 +127,14 @@ TeVector3f32 TeModelAnimation::getNMOTranslation(unsigned long boneNo, float amo
 			if (i == 0)
 				return arr.front()._trans;
 
-			if (i == arr.size() || arr.size() == 1 || arr[i]._f - arr[i-1]._f == 0) {
+			if (i == arr.size() || arr.size() == 1)
 				return arr.back()._trans;
-			}
 
-			float interp = (amount - arr[i-1]._f) / (arr[i]._f - arr[i-1]._f);
-			return arr[i-1]._trans * (1.0 - interp) + arr[i]._trans * interp;
+			if (arr[i]._f - arr[i - 1]._f == 0)
+				return arr[i - 1]._trans;
+
+			float interp = (amount - arr[i - 1]._f) / (arr[i]._f - arr[i - 1]._f);
+			return arr[i - 1]._trans * (1.0 - interp) + arr[i]._trans * interp;
 		}
 	}
 	return TeVector3f32(0, 0, 0);
diff --git a/engines/tetraedge/te/te_model_vertex_animation.cpp b/engines/tetraedge/te/te_model_vertex_animation.cpp
index cef7f635c47..2735fba1f80 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.cpp
+++ b/engines/tetraedge/te/te_model_vertex_animation.cpp
@@ -70,7 +70,7 @@ const Common::Array<TeVector3f32> &TeModelVertexAnimation::getVertices() {
 	for (unsigned int i = 0; i < _keydata[0]._vectors.size(); i++) {
 		const TeVector3f32 prevVector = getKeyVertex(keyno, i);
 		const TeVector3f32 nextVector = getKeyVertex(keyno + 1, i);
-		lerpVtx.push_back(prevVector * (1.0 - interp) + nextVector * interp);
+		lerpVtx[i] = prevVector * (1.0 - interp) + nextVector * interp;
 	}
 
 	return lerpVtx;
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 41fb2c5cbad..91f4eb85da0 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -59,7 +59,7 @@ void TePickMesh2::draw() {
 	renderer->setCurrentColor(prevCol);
 }
 
-bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &v3, float &fout, bool lastHitFirst, unsigned long *triangleHitOut) {
+bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &vout, float &fout, bool lastHitFirst, unsigned long *triangleHitOut) {
 	if (_verticies.size() / 3 == 0)
 		return false;
 
@@ -72,7 +72,7 @@ bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, TeVe
 		const TeVector3f32 triv3 = worldTrans * _verticies[_lastTriangleHit * 3 + 2];
 		int result = TeRayIntersection::intersect(v1, v2, triv1, triv2, triv3, intersection, f);
 		if (result == 1 && f >= 0.0 && f < FLT_MAX) {
-			v3 = v1 + v2 * f;
+			vout = v1 + v2 * f;
 			fout = f;
 			if (triangleHitOut)
 				*triangleHitOut = _lastTriangleHit;
@@ -94,7 +94,7 @@ bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, TeVe
 		}
 	}
 	if (hitf != FLT_MAX) {
-		v3 = v1 + v2 * hitf;
+		vout = v1 + v2 * hitf;
 		fout = hitf;
 		if (triangleHitOut)
 			*triangleHitOut = _lastTriangleHit;
@@ -167,7 +167,7 @@ void TePickMesh2::deserialize(Common::ReadStream &stream, TePickMesh2 &mesh) {
 	for (unsigned int i = 0; i < ntriangles * 3; i++) {
 		TeVector3f32 vec;
 		TeVector3f32::deserialize(stream, vec);
-		mesh._verticies.push_back(vec);
+		mesh._verticies[i] = vec;
 	}
 }
 
diff --git a/engines/tetraedge/te/te_ray_intersection.cpp b/engines/tetraedge/te/te_ray_intersection.cpp
index 4cfece341c3..ddbbd9e6dae 100644
--- a/engines/tetraedge/te/te_ray_intersection.cpp
+++ b/engines/tetraedge/te/te_ray_intersection.cpp
@@ -45,6 +45,7 @@ int intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32
 	if (fabs(f2) > 1e-9) {
 		f2 = -f1 / f2;
 		fout = f2;
+		result = 0;
 		if (f2 >= 0.0) {
 			vout = v1 + (v2 * f2);
 			float dot1 = v4_v3.dotProduct(v4_v3);
@@ -64,6 +65,10 @@ int intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32
 				}
 			}
 		}
+	} else {
+		// Sorry about the logic.. this is what the decompiler gave me
+		// and I'm not brave enough to figure it out.
+		result = (-(uint)(f1 == -0.0) & 1) * 2;
 	}
 	return result;
 }
diff --git a/engines/tetraedge/te/te_timer.cpp b/engines/tetraedge/te/te_timer.cpp
index 79db37f7649..4802c446729 100644
--- a/engines/tetraedge/te/te_timer.cpp
+++ b/engines/tetraedge/te/te_timer.cpp
@@ -39,6 +39,17 @@ _startTime(0), _lastTimeElapsed(0), _startTimeOffset(0), _updated(false) {
 	}
 }
 
+TeTimer::~TeTimer() {
+	if (!_stopped) {
+		for (uint i = 0; i < _timers.size(); i++) {
+			if (_timers[i] == this) {
+				_timers.remove_at(i);
+				break;
+			}
+		}
+	}
+}
+
 void TeTimer::stop() {
 	pause();
 	_lastTimeElapsed = 0;
diff --git a/engines/tetraedge/te/te_timer.h b/engines/tetraedge/te/te_timer.h
index 6d04e5bd2d4..1cf59ac72ff 100644
--- a/engines/tetraedge/te/te_timer.h
+++ b/engines/tetraedge/te/te_timer.h
@@ -31,6 +31,7 @@ namespace Tetraedge {
 class TeTimer {
 public:
 	TeTimer();
+	~TeTimer();
 
 	void stop();
 	void start();


Commit: 194b1a80813f0869a5a4d1d55023721a1a001783
    https://github.com/scummvm/scummvm/commit/194b1a80813f0869a5a4d1d55023721a1a001783
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Work in progress, some inventory stuff done.

Changed paths:
    engines/tetraedge/game/billboard.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/objectif.cpp
    engines/tetraedge/game/objectif.h


diff --git a/engines/tetraedge/game/billboard.cpp b/engines/tetraedge/game/billboard.cpp
index eef7974e0b1..068abcb0f36 100644
--- a/engines/tetraedge/game/billboard.cpp
+++ b/engines/tetraedge/game/billboard.cpp
@@ -36,17 +36,17 @@ bool Billboard::load(const Common::String &path) {
 	Game *game = g_engine->getGame();
 	Common::Path texpath = game->sceneZonePath().join(path);
 	texture->load(texpath);
-	_model->setName(texpath.toString());
+	_model->setName(path);
 	Common::Array<TeVector3f32> quad;
 	quad.resize(4);
 	_model->setQuad(texture, quad, TeColor(0xff, 0xff, 0xff, 0xff));
 	game->scene().models().push_back(_model);
-	return false;
+	return true;
 }
 
 void Billboard::calcVertex() {
 	//Game *game = g_engine->getGame();
-	error("TODO: implement Billboard::calcVertex");
+	warning("TODO: implement Billboard::calcVertex");
 }
 
 void Billboard::position(const TeVector3f32 &pos) {
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index b65287d92d0..d8fc15069a6 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -155,16 +155,16 @@ void Game::addRandomSound(const Common::String &name, const Common::String &path
 	_randomSounds[name].push_back(randsound);
 }
 
-void Game::addToBag(const Common::String &objname) {
-	if (_inventory.objectCount(objname) != 0)
+void Game::addToBag(const Common::String &objid) {
+	if (_inventory.objectCount(objid) != 0)
 		return;
-	_inventory.addObject(objname);
+	_inventory.addObject(objid);
 	Common::String imgpath("Inventory/Objects/");
-	imgpath += _inventory.objectName(objname);
+	imgpath += objid;
 	imgpath += ".png";
-	_notifier.push(objname, imgpath);
+	_notifier.push(_inventory.objectName(objid), imgpath);
 	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
-		if (objname == OBJECTS_TAKEN_IDS[i] && !_objectsTakenBits[i]) {
+		if (objid == OBJECTS_TAKEN_IDS[i] && !_objectsTakenBits[i]) {
 			_objectsTakenBits[i] = true;
 			_objectsTakenVal++;
 		}
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index e01d0b4d27d..610c7e2c87d 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -191,6 +191,7 @@ public:
 	const TeVector3f32 &posPlayer() const { return _posPlayer; }
 	void setPosPlayer(const TeVector3f32 &pos) { _posPlayer = pos; }
 	TeTimer &walkTimer() { return _walkTimer; }
+	void setExitZone(const Common::String &zone) { _exitZone = zone; }
 
 private:
 	bool _luaShowOwnerError;
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 6b42aebf130..f6cd44a6b9a 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -213,10 +213,12 @@ bool Inventory::addObject(InventoryObject &obj) {
 			pageno++;
 		}
     }
-	
+
 	int pageno = 0;
 	unsigned int totalSlots = 0;
 	bool retval;
+	const Common::String newObjName = obj.name();
+	auto invObjIter = _invObjects.begin();
 	while (true) {
 		TeLayout *page = _gui.layout(Common::String::format("page%d", pageno));
 		retval = false;
@@ -236,10 +238,22 @@ bool Inventory::addObject(InventoryObject &obj) {
 			}
 			
 			TeTextLayout *newText = new TeTextLayout();
-			error("TODO: finish Inventory::addObject.");
+			newText->setSizeType(CoordinatesType::RELATIVE_TO_PARENT);
+			newText->setPosition(TeVector3f32(1.0,1.0,0.0));
+			newText->setSize(TeVector3f32(1.0,1.0,0.0));
+			newText->setTextSizeType(1);
+			newText->setTextSizeProportionalToWidth(200);
+			newText->setText(_gui.value("textAttributs").toString() + (*invObjIter)->name());
+			newText->setName((*invObjIter)->name());
+			newText->setVisible(false);
+
+			TeLayout *layout = _gui.layout("textObject");
+			layout->addChild(newText);
+			slot->addChild(*invObjIter);
 
 			totalSlots++;
 			slotno++;
+			invObjIter++;
 		}
 		pageno++;
 	}
@@ -268,7 +282,7 @@ Common::String Inventory::objectDescription(const Common::String &objId) {
 Common::String Inventory::objectName(const Common::String &objId) {
 	if (!_objectData.contains(objId))
 		return "";
-	return _objectData.getVal(objId)._name;
+	return _objectData.getVal(objId)._id;
 }
 
 bool Inventory::onMainMenuButton() {
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 290d94b2546..759af23615e 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -22,11 +22,13 @@
 #include "tetraedge/tetraedge.h"
 
 #include "tetraedge/game/application.h"
+#include "tetraedge/game/billboard.h"
 #include "tetraedge/game/character.h"
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/lua_binds.h"
 #include "tetraedge/game/object3d.h"
 #include "tetraedge/to_lua.h"
+#include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_lua_thread.h"
 
 namespace Tetraedge {
@@ -586,6 +588,133 @@ static int tolua_ExportedFunctions_SetGroundObjectRotation00(lua_State *L) {
 	error("#ferror in function 'SetGroundObjectRotation': %d %d %s", err.index, err.array, err.type);
 }
 
+static void LoadBillBoard(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	game->scene().loadBillboard(name);
+}
+
+static int tolua_ExportedFunctions_LoadBillBoard00(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));
+		LoadBillBoard(s1);
+		return 0;
+	}
+	error("#ferror in function 'LoadBillBoard': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetBillboardPosition(const Common::String &name, float x, float y, float z) {
+	Game *game = g_engine->getGame();
+	Billboard *bb = game->scene().billboard(name);
+	if (!bb) {
+		error("[SetBillboardPosition] Billboard not found %s", name.c_str());
+	}
+	bb->position(TeVector3f32(x, y, z));
+}
+
+static int tolua_ExportedFunctions_SetBillboardPosition00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
+		SetBillboardPosition(s1, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetBillboardPosition': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetBillboardPosition2(const Common::String &name, float x1, float y1, float x2, float y2, float z) {
+	Game *game = g_engine->getGame();
+	Billboard *bb = game->scene().billboard(name);
+	if (!bb) {
+		error("[SetBillboardPosition2] Billboard not found %s", name.c_str());
+	}
+	bb->position(TeVector3f32(x1, y1, 0.0));
+	bb->position2(TeVector3f32(x2, y2, z));
+}
+
+static int tolua_ExportedFunctions_SetBillboardPosition200(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnumber(L, 5, 0, &err) && tolua_isnumber(L, 6, 0, &err)
+		&& tolua_isnoobj(L, 7, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
+		float f4 = tolua_tonumber(L, 5, 0.0);
+		float f5 = tolua_tonumber(L, 6, 0.0);
+		SetBillboardPosition2(s1, f1, f2, f3, f4, f5);
+		return 0;
+	}
+	error("#ferror in function 'SetBillboardPosition2': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetBillboardSize(const Common::String &name, float xs, float ys) {
+	Game *game = g_engine->getGame();
+	Billboard *bb = game->scene().billboard(name);
+	if (!bb) {
+		error("[SetBillboardSize] Billboard not found %s", name.c_str());
+	}
+	bb->size(TeVector2f32(xs, ys));
+}
+
+static int tolua_ExportedFunctions_SetBillboardSize00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		SetBillboardSize(s1, f1, f2);
+		return 0;
+	}
+	error("#ferror in function 'SetBillboardSize': %d %d %s", err.index, err.array, err.type);
+}
+
+static void ShowBillboard(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	Billboard *bb = game->scene().billboard(name);
+	if (!bb) {
+		error("[ShowBillboard] Billboard not found %s", name.c_str());
+	}
+	bb->model()->setVisible(true);
+}
+
+static int tolua_ExportedFunctions_ShowBillboard00(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));
+		ShowBillboard(s1);
+		return 0;
+	}
+	error("#ferror in function 'ShowBillboard': %d %d %s", err.index, err.array, err.type);
+}
+
+static void HideBillboard(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	Billboard *bb = game->scene().billboard(name);
+	if (!bb) {
+		error("[HideBillboard] Billboard not found %s", name.c_str());
+	}
+	bb->model()->setVisible(false);
+}
+
+static int tolua_ExportedFunctions_HideBillboard00(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));
+		HideBillboard(s1);
+		return 0;
+	}
+	error("#ferror in function 'HideBillboard': %d %d %s", err.index, err.array, err.type);
+}
+
 static void SetBackground(const Common::String &name) {
 	Game *game = g_engine->getGame();
 	if (!game->setBackground(name))
@@ -670,6 +799,55 @@ static int tolua_ExportedFunctions_PushTask00(lua_State *L) {
 	error("#ferror in function 'PushTask': %d %d %s", err.index, err.array, err.type);
 }
 
+static void DeleteTask(const Common::String &s1, const Common::String &s2) {
+	Game *game = g_engine->getGame();
+	game->objectif().deleteObjectif(s1, s2);
+}
+
+static int tolua_ExportedFunctions_DeleteTask00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		DeleteTask(s1, s2);
+		return 0;
+	}
+	error("#ferror in function 'DeleteTask': %d %d %s", err.index, err.array, err.type);
+}
+
+static bool TestFileFlagSystemFlag(const Common::String &flagname, const Common::String &val) {
+	if (flagname == "platform" && val == "Android")
+		return true;
+	return g_engine->getCore()->fileFlagSystemFlag(flagname) == val;
+}
+
+static int tolua_ExportedFunctions_TestFileFlagSystemFlag00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		bool result = TestFileFlagSystemFlag(s1, s2);
+		tolua_pushboolean(L, result);
+		return 1;
+	}
+	error("#ferror in function 'TestFileFlagSystemFlag': %d %d %s", err.index, err.array, err.type);
+}
+
+static void ExitZone(const Common::String &zone) {
+	Game *game = g_engine->getGame();
+	game->setExitZone(zone);
+}
+
+static int tolua_ExportedFunctions_ExitZone00(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));
+		ExitZone(s1);
+		return 0;
+	}
+	error("#ferror in function 'ExitZone': %d %d %s", err.index, err.array, err.type);
+}
+
 static void EnableRectBlocker(uint offset, bool enabled) {
 	Game *game = g_engine->getGame();
 	if (game->scene().rectBlockers().size() < offset)
@@ -1022,6 +1200,7 @@ static void MoveCharacterPlayerTo(float x, float y, float z, bool walkFlag) {
 		character->walkMode("Walk");
 	}
 
+	assert(character->freeMoveZone());
 	game->_sceneCharacterVisibleFromLoad = false;
 	TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(character->_model->position(), dest);
 	if (!curve) {
@@ -1096,8 +1275,8 @@ void LuaOpenBinds(lua_State *L) {
 	/*tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
 	tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);*/
 	tolua_function(L, "PushTask", tolua_ExportedFunctions_PushTask00);
-	/*tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
-	tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);
+	tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
+	/*tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);
 	tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00);*/
 	tolua_function(L, "PlaySound", tolua_ExportedFunctions_PlaySound00);
 	/*tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);*/
@@ -1170,14 +1349,14 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "EnableLight", tolua_ExportedFunctions_EnableLight00);
 	tolua_function(L, "SetLightDiffuse", tolua_ExportedFunctions_SetLightDiffuse00);
 	tolua_function(L, "SetLightAmbient", tolua_ExportedFunctions_SetLightAmbient00);
-	tolua_function(L, "SetLightSpecular", tolua_ExportedFunctions_SetLightSpecular00);
+	tolua_function(L, "SetLightSpecular", tolua_ExportedFunctions_SetLightSpecular00);*/
 	tolua_function(L, "LoadBillBoard", tolua_ExportedFunctions_LoadBillBoard00);
 	tolua_function(L, "SetBillboardPosition", tolua_ExportedFunctions_SetBillboardPosition00);
 	tolua_function(L, "SetBillboardPosition2", tolua_ExportedFunctions_SetBillboardPosition200);
 	tolua_function(L, "SetBillboardSize", tolua_ExportedFunctions_SetBillboardSize00);
 	tolua_function(L, "ShowBillboard", tolua_ExportedFunctions_ShowBillboard00);
 	tolua_function(L, "HideBillboard", tolua_ExportedFunctions_HideBillboard00);
-	tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
+	/*tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
 	tolua_function(L, "Save", tolua_ExportedFunctions_Save00);
 	tolua_function(L, "Wait", tolua_ExportedFunctions_Wait00);
 	tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00);
@@ -1187,10 +1366,10 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "BFGRateImmediately", tolua_ExportedFunctions_BFGRateImmediately00);
 	tolua_function(L, "BFGReportEvent", tolua_ExportedFunctions_BFGReportEvent00);
 	tolua_function(L, "BFGReportEventWithValue", tolua_ExportedFunctions_BFGReportEventWithValue00);
-	tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00);
+	tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00);*/
 	tolua_function(L, "TestFileFlagSystemFlag", tolua_ExportedFunctions_TestFileFlagSystemFlag00);
 	// tolua_function(L, "PrintDebugMessage", tolua_ExportedFunctions_PrintDebugMessage00); // Unused
-	tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);*/
+	tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);
 	tolua_function(L, "EnableRectBlocker", tolua_ExportedFunctions_EnableRectBlocker00);
 	/*tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);*/
 	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
index 96a2731f962..acc0f042e87 100644
--- a/engines/tetraedge/game/objectif.cpp
+++ b/engines/tetraedge/game/objectif.cpp
@@ -121,6 +121,15 @@ void Objectif::pushObjectif(Common::String const &head, Common::String const &su
 	_tasks.back()._taskFlag = true;
 }
 
+void Objectif::deleteObjectif(Common::String const &head, Common::String const &sub) {
+	for (Task &t : _tasks) {
+		if (t._taskFlag && t._headTask == head && t._subTask == sub) {
+			t._taskFlag = false;
+			return;
+		}
+	}
+}
+
 void Objectif::reattachLayout(TeLayout *layout) {
 	TeButtonLayout *btn;
 
diff --git a/engines/tetraedge/game/objectif.h b/engines/tetraedge/game/objectif.h
index 44ae6050f46..cf0b0ca4124 100644
--- a/engines/tetraedge/game/objectif.h
+++ b/engines/tetraedge/game/objectif.h
@@ -42,6 +42,7 @@ public:
 	bool hideBouton();
 	bool isMouseIn(const TeVector2s32 &mousept);
 	bool isVisibleObjectif();
+	void deleteObjectif(Common::String const &head, Common::String const &sub);
 	void leave();
 	void load();
 	bool onHelpButtonValidated();


Commit: 64c72672834651973d7c2986be86e2a1a3713278
    https://github.com/scummvm/scummvm/commit/64c72672834651973d7c2986be86e2a1a3713278
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Many small improvements.

Fixed many things, but Z-order of objects is still wrong - UI elements not
showing correctly.

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/billboard.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/dialog2.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/inventory.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/question2.cpp
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture.h
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_frame_anim.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_layout.cpp
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_material.cpp
    engines/tetraedge/te/te_material.h
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_mesh.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer.h
    engines/tetraedge/te/te_sprite_layout.cpp
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_tiled_surface.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 6fc5150494a..37af6a70bc2 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -252,7 +252,7 @@ void Application::create() {
 	_autoSaveIcon2.setName("autosaveIcon");
 	_autoSaveIcon2.setAnchor(TeVector3f32(0.5f, 0.5f, 0.0f));
 	_autoSaveIcon2.setPosition(TeVector3f32(0.2f, 0.7f, 0.0f));
-	_autoSaveIcon2.setSize(TeVector3f32(68.0f, 86.0f, 0.0f));
+	_autoSaveIcon2.setSize(TeVector3f32(64.0f, 86.0f, 0.0f));
 	_autoSaveIcon2.load("menus/inGame/NoCel.png");
 	_autoSaveIcon2.setVisible(false);
 	_frontOrientationLayout.addChild(&_autoSaveIcon2);
diff --git a/engines/tetraedge/game/billboard.cpp b/engines/tetraedge/game/billboard.cpp
index 068abcb0f36..3e95616de21 100644
--- a/engines/tetraedge/game/billboard.cpp
+++ b/engines/tetraedge/game/billboard.cpp
@@ -27,7 +27,7 @@
 
 namespace Tetraedge {
 
-Billboard::Billboard() {
+Billboard::Billboard() : _hasPos2(false) {
 }
 
 bool Billboard::load(const Common::String &path) {
@@ -40,13 +40,52 @@ bool Billboard::load(const Common::String &path) {
 	Common::Array<TeVector3f32> quad;
 	quad.resize(4);
 	_model->setQuad(texture, quad, TeColor(0xff, 0xff, 0xff, 0xff));
+	_model->setVisible(false);
 	game->scene().models().push_back(_model);
 	return true;
 }
 
 void Billboard::calcVertex() {
-	//Game *game = g_engine->getGame();
-	warning("TODO: implement Billboard::calcVertex");
+	Game *game = g_engine->getGame();
+	TeIntrusivePtr<TeCamera> currentCam = game->scene().currentCamera();
+	assert(currentCam);
+	currentCam->apply();
+	const TeMatrix4x4 camProjMatrix = currentCam->projectionMatrix();
+	TeMatrix4x4 camWorldInverse = currentCam->worldTransformationMatrix();
+	camWorldInverse.inverse();
+	
+	const TeMatrix4x4 camTotalTransform = camProjMatrix * camWorldInverse;
+	TeMatrix4x4 camTotalInverse = camTotalTransform;
+	camTotalInverse.inverse();
+
+	TeVector3f32 posvec(0.0f, 0.0f, _pos.z());
+	if (_hasPos2) {
+		posvec = _pos2;
+	}
+	posvec = camTotalTransform * posvec;
+	
+	TeVector3f32 meshVertex;
+	float fx, fy;
+
+	fx = _pos.x();
+	fy = _pos.y();
+	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
+	_model->_meshes[0].setVertex(0, meshVertex);
+
+	fx = _pos.x();
+	fy = _pos.y() + _size.getY();
+	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
+	_model->_meshes[0].setVertex(1, meshVertex);
+
+	fx = _pos.x() + _size.getX();
+	fy = _pos.y();
+	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
+	_model->_meshes[0].setVertex(2, meshVertex);
+	
+	fx = _pos.x() + _size.getX();
+	fy = _pos.y() + _size.getY();
+	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
+	_model->_meshes[0].setVertex(3, meshVertex);
 }
 
 void Billboard::position(const TeVector3f32 &pos) {
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index fd8b39755cd..98299ae6323 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -192,7 +192,7 @@ bool Character::blendAnimation(const Common::String &animname, float amount, boo
 	_lastFrame = -1;
 	_curModelAnim->play();
 	_curAnimName = animname;
-	warning("TODO: Set field 0x2d1 in Character::blendAnimation");
+	_someRepeatFlag = !(repeat | !param_4);
 	return true;
 }
 
@@ -743,8 +743,8 @@ void Character::update(double msFromStart) {
 		newPos = _freeMoveZone->correctCharacterPosition(newPos, &correctflag, true);
 	}
 
-	debug("Character::update %4d %.04f %s -> %s %.4f", (int)msFromStart, offset, _model->position().dump().c_str(),
-			newPos.dump().c_str(), (newPos - _model->position()).length());
+	//debug("Character::update %4d %.04f %s -> %s %.4f", (int)msFromStart, offset, _model->position().dump().c_str(),
+	//		newPos.dump().c_str(), (newPos - _model->position()).length());
 
 	_walkCurveLast = offset;
 	_model->setPosition(newPos);
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 8b210514963..5437c206270 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -69,7 +69,7 @@ void Dialog2::launchNextDialog() {
 		if (!_currentDialogData._charname.empty()) {
 			Character *c = game->scene().character(_currentDialogData._charname);
 			if (!c) {
-				error("[Dialog2::launchNextDialog] Character's \"%s\" doesn\'t exist", _currentDialogData._charname.c_str());
+				error("[Dialog2::launchNextDialog] Character's \"%s\" doesn't exist", _currentDialogData._charname.c_str());
 			}
 
 			if (_currentDialogData._animBlend == 0.0f) {
@@ -82,8 +82,8 @@ void Dialog2::launchNextDialog() {
 							_currentDialogData._animfile.c_str(), _currentDialogData._charname.c_str());
 			}
 		}
-		_gui.buttonLayoutChecked("dialogLockButton")->setVisible(true);
-		TeCurveAnim2<TeLayout, TeVector3f32> *anim = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+		lockBtn->setVisible(true);
+		TeCurveAnim2<TeLayout, TeVector3f32> *anim = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
 		anim->stop();
 		anim->play();
 		_minimumTimeTimer.start();
@@ -126,7 +126,7 @@ void Dialog2::loadFromBackup() {
 }
 
 bool Dialog2::onAnimationDownFinished() {
-	Common::String param(_animDownFinishedResultString);
+	Common::String param(_currentDialogData._name);
 	launchNextDialog();
 	_onAnimationDownFinishedSignal.call(param);
 	return false;
@@ -165,7 +165,7 @@ bool Dialog2::onSoundFinished() {
 }
 
 void Dialog2::pushDialog(const Common::String &name, const Common::String &textVal, const Common::String &sound, int param_4) {
-	error("TODO: Implement Dialog2::pushDialog");
+	error("TODO: Implement Dialog2::pushDialog 3 param");
 }
 
 void Dialog2::pushDialog(const Common::String &name, const Common::String &textVal, const Common::String &sound,
@@ -204,10 +204,9 @@ void Dialog2::unload() {
 	dialogAnimDown->stop();
 	_music.close();
 	_gui.unload();
-	error("TODO: Finish Dialog2::unload");
 
-	//_dialogDataList.clear();
-	//_minimumTimeTimer.stop();
+	_dialogs.clear();
+	_minimumTimeTimer.stop();
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/dialog2.h b/engines/tetraedge/game/dialog2.h
index a4c4a269a29..5c21231e2b7 100644
--- a/engines/tetraedge/game/dialog2.h
+++ b/engines/tetraedge/game/dialog2.h
@@ -60,16 +60,11 @@ public:
 
 	TeLuaGUI &gui() { return _gui; }
 
-	Common::String prevSceneName() { return _prevSceneName; };
-
 private:
 	Common::Array<DialogData> _dialogs;
 
 	TeTimer _minimumTimeTimer;
 
-	Common::String _prevSceneName;
-	Common::String _animDownFinishedResultString;
-
 	TeLuaGUI _gui;
 	TeMusic _music;
 	
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index d8fc15069a6..407a7730b5b 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -44,11 +44,14 @@
 
 namespace Tetraedge {
 
-Game::Game() : _objectsTakenVal(0), _score(0), _entered(false), _gameLoadState(0),
-_noScaleLayout(nullptr), _noScaleLayout2(nullptr), _warped(false), _saveRequested(false),
-_firstInventory(true), _movePlayerCharacterDisabled(false), _enteredFlag2(false),
-_luaShowOwnerError(false), _markersVisible(false), _running(false), _loadName("save.xml"),
-_randomSoundFinished(false), _randomSource("SyberiaGameRandom") {
+Game::Game() : _objectsTakenVal(0), _returnToMainMenu(false), _entered(false),
+_enteredFlag2(false), _running(false), _movePlayerCharacterDisabled(false),
+_noScaleLayout(nullptr), _noScaleLayout2(nullptr), _isCharacterIdle(true),
+_sceneCharacterVisibleFromLoad(false), _isCharacterWalking(false),
+_lastCharMoveMousePos(0.0f, 0.0f), _randomSoundFinished(false),
+_previousMousePos(-1, -1), _markersVisible(true), _saveRequested(false),
+_gameLoadState(0), _luaShowOwnerError(false), _score(0), _warped(false),
+_firstInventory(true), _loadName("save.xml"), _randomSource("SyberiaGameRandom") {
 	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
 		_objectsTakenBits[i] = false;
 	}
@@ -337,7 +340,7 @@ void Game::enter(bool newgame) {
 	_sceneCharacterVisibleFromLoad = true;
 	_scene._character->onFinished().remove(this, &Game::onDisplacementFinished);
 	_scene._character->onFinished().add(this, &Game::onDisplacementFinished);
-	_dialog2.prevSceneName().clear();
+	_prevSceneName.clear();
 	_notifier.load();
 }
 
@@ -549,9 +552,9 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	video->_tiledSurfacePtr->_frameAnim.onFinished().add(this, &Game::onVideoFinished);
 
 	TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
-	invbtn->setSizeType(TeILayout::RELATIVE_TO_PARENT); // TODO: Double-check if this is the right virt fn.
 	invbtn->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
 	invbtn->onMouseClickValidated().add(this, &Game::onInventoryButtonValidated);
+	invbtn->setSizeType(TeILayout::RELATIVE_TO_PARENT);
 
 	const TeVector3f32 winSize = app->getMainWindow().size();
 	if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
@@ -1436,11 +1439,8 @@ void Game::update() {
 				app->_lockCursorButton.setVisible(false);
 		}
 
-		TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
-		if (invbtn)
-			invbtn->setVisible(!app->isLockCursor() && !_dialog2.isDialogPlaying());
-		else
-			warning("Game::update: InventoryButton is null.");
+		TeButtonLayout *invbtn = _inGameGui.buttonLayoutChecked("inventoryButton");
+		invbtn->setVisible(!app->isLockCursor() && !_dialog2.isDialogPlaying());
 
 		Character *player = _scene._character;
 		if (player) {
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 6df97b971d5..7aabee33cfe 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -700,11 +700,11 @@ bool InGameScene::loadObject(const Common::String &name) {
 	return true;
 }
 
-void InGameScene::loadObjectMaterials(const Common::String &name) {
+bool InGameScene::loadObjectMaterials(const Common::String &name) {
 	error("TODO: InGameScene::loadObjectMaterials");
 }
 
-void InGameScene::loadObjectMaterials(const Common::String &path, const Common::String &name) {
+bool InGameScene::loadObjectMaterials(const Common::String &path, const Common::String &name) {
 	error("TODO: InGameScene::loadObjectMaterials");
 }
 
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index cf08c1e874a..690e76ec861 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -143,8 +143,8 @@ public:
 	bool loadLights(const Common::Path &path);
 	void loadMarkers(const Common::Path &path);
 	bool loadObject(const Common::String &oname);
-	void loadObjectMaterials(const Common::String &name);
-	void loadObjectMaterials(const Common::String &path, const Common::String &name);
+	bool loadObjectMaterials(const Common::String &name);
+	bool loadObjectMaterials(const Common::String &path, const Common::String &name);
 	bool loadPlayerCharacter(const Common::String &cname);
 
 	void moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd);
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index f6cd44a6b9a..dc48f38fb07 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -69,9 +69,7 @@ void Inventory::load() {
 	setSizeType(RELATIVE_TO_PARENT);
 	setSize(TeVector3f32(1.0f, 1.0f, userSize().z()));
 	_gui.load("Inventory/Inventory.lua");
-	TeLayout *invlayout = _gui.layout("inventory");
-	if (!invlayout)
-		error("Inventory::load: Couldn't find inventory layout after loading");
+	TeLayout *invlayout = _gui.layoutChecked("inventory");
 	addChild(invlayout);
 
 	TeButtonLayout *btn;
@@ -83,7 +81,6 @@ void Inventory::load() {
 	btn->onMouseClickValidated().add(this, &Inventory::onTakeObjectSelected);
 
 	btn = _gui.buttonLayoutChecked("lire");
-	btn->setVisible(false);
 	btn->setEnable(false);
 	btn->onMouseClickValidated().add(this, &Inventory::onZoomed);
 
@@ -119,6 +116,7 @@ void Inventory::load() {
 
 	TeLayout *layout = _gui.layout("selectionSprite");
 	layout->setVisible(false);
+	_invObjects.clear();
 
 	setVisible(false);
 }
@@ -183,13 +181,13 @@ void Inventory::loadCellphone() {
 void Inventory::addObject(const Common::String &objId) {
 	InventoryObject *newobj = new InventoryObject();
 	newobj->load(objId);
-	if (!addObject(*newobj))
+	if (!addObject(newobj))
 		delete newobj;
 }
 
-bool Inventory::addObject(InventoryObject &obj) {
-	_invObjects.push_front(&obj);
-	obj.selectedSignal().add(this, &Inventory::onObjectSelected);
+bool Inventory::addObject(InventoryObject *obj) {
+	_invObjects.push_front(obj);
+	obj->selectedSignal().add(this, &Inventory::onObjectSelected);
 	if (_invObjects.size() > 1) {
 		int pageno = 0;
 		while (true) {
@@ -217,9 +215,10 @@ bool Inventory::addObject(InventoryObject &obj) {
 	int pageno = 0;
 	unsigned int totalSlots = 0;
 	bool retval;
-	const Common::String newObjName = obj.name();
+	const Common::String newObjName = obj->name();
 	auto invObjIter = _invObjects.begin();
-	while (true) {
+	bool finished = false;
+	while (!finished) {
 		TeLayout *page = _gui.layout(Common::String::format("page%d", pageno));
 		retval = false;
 		if (!page)
@@ -232,8 +231,7 @@ bool Inventory::addObject(InventoryObject &obj) {
 			retval = true;
 
 			if (totalSlots == _invObjects.size()) {
-				// break from both loops (slight hack..)
-				pageno = 9999;
+				finished = true;
 				break;
 			}
 			
@@ -282,7 +280,7 @@ Common::String Inventory::objectDescription(const Common::String &objId) {
 Common::String Inventory::objectName(const Common::String &objId) {
 	if (!_objectData.contains(objId))
 		return "";
-	return _objectData.getVal(objId)._id;
+	return _objectData.getVal(objId)._name;
 }
 
 bool Inventory::onMainMenuButton() {
diff --git a/engines/tetraedge/game/inventory.h b/engines/tetraedge/game/inventory.h
index 53661da3ae5..9743a8609d3 100644
--- a/engines/tetraedge/game/inventory.h
+++ b/engines/tetraedge/game/inventory.h
@@ -51,7 +51,7 @@ public:
 	//void saveToBackup(TiXmlNode *node);
 
 	void addObject(const Common::String &objname);
-	bool addObject(InventoryObject &obj);
+	bool addObject(InventoryObject *obj);
 	bool isDocument(const Common::String &objname);
 
 	int objectCount(const Common::String &objname);
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 759af23615e..e8c74ffb3dd 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -37,6 +37,44 @@ namespace LuaBinds {
 
 using namespace ToLua;
 
+static void LoadObjectMaterials(const Common::String &objname) {
+	Game *game = g_engine->getGame();
+	bool result = game->scene().loadObjectMaterials(objname);
+	if (!result)
+		error("[LoadObjectMaterials] Object \"%s\" doesn't exist or no Object in this scene.",
+				objname.c_str());
+}
+
+static int tolua_ExportedFunctions_LoadObjectMaterials00(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));
+		LoadObjectMaterials(s1);
+		return 0;
+	}
+	error("#ferror in function 'LoadObjectMaterials': %d %d %s", err.index, err.array, err.type);
+}
+
+static void LoadObjectMaterials(const Common::String &imgname, const Common::String &objname) {
+	Game *game = g_engine->getGame();
+	bool result = game->scene().loadObjectMaterials(imgname, objname);
+	if (!result)
+		error("[LoadObjectMaterials] Object \"%s\" doesn't exist in scene : \"%s\" or there is no material for this object.",
+				objname.c_str(), imgname.c_str());
+}
+
+static int tolua_ExportedFunctions_LoadObjectMaterials01(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		LoadObjectMaterials(s1, s2);
+		return 0;
+	}
+	error("#ferror in function 'LoadObjectMaterials': %d %d %s", err.index, err.array, err.type);
+}
+
+
 static void PlayMovie(const Common::String &vidpath, const Common::String &musicpath) {
 	Application *app = g_engine->getApplication();
 	app->mouseCursorLayout().load("pictures/cursor.png");
@@ -111,7 +149,7 @@ static int tolua_ExportedFunctions_Selected00(lua_State *L) {
 
 static void TakeObject(const Common::String &obj) {
 	Game *game = g_engine->getGame();
-	warning("TODO: Set global _lastHitObjectName");
+	// TODO: Set global _lastHitObjectName?? How is it used?
 	//game->luaContext().setGlobal(_lastHitObjectName, true);
 	if (!obj.empty())
 		game->addToBag(obj);
@@ -323,6 +361,31 @@ static int tolua_ExportedFunctions_SetVisibleCellphone00(lua_State *L) {
 	error("#ferror in function 'SetVisibleCellphone': %d %d %s", err.index, err.array, err.type);
 }
 
+static void ShowObject(const Common::String &objName);
+
+static void StartAnimation(const Common::String name, int loops, bool repeat) {
+	ShowObject(name);
+	Game *game = g_engine->getGame();
+	if (game->startAnimation(name, loops, repeat))
+		return;
+
+	error("[StartAnimation] Animation \"%s\" doesn't exist.", name.c_str());
+}
+
+int tolua_ExportedFunctions_StartAnimation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 1, &err)
+		&& tolua_isboolean(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		double d1 = tolua_tonumber(L, 2, -1.0);
+		bool b1 = tolua_toboolean(L, 3, 0);
+		StartAnimation(s1, d1, b1);
+		return 0;
+	}
+	error("#ferror in function 'StartAnimation': %d %d %s", err.index, err.array, err.type);
+
+}
+
 static void RequestAutoSave() {
 	Game *game = g_engine->getGame();
 	game->setSaveRequested();
@@ -433,6 +496,34 @@ static int tolua_ExportedFunctions_UnloadObject00(lua_State *L) {
 	error("#ferror in function 'UnloadObject': %d %d %s", err.index, err.array, err.type);
 }
 
+static void PlaceCharacterOnDummy(const Common::String &charname, const Common::String &dummyname, float x, float y, float z) {
+	Game *game = g_engine->getGame();
+	Character *c = game->scene().character(charname);
+	if (c) {
+		InGameScene::Dummy dummy = game->scene().dummy(dummyname);
+		c->_model->setPosition(dummy._position + TeVector3f32(x, y, z));
+	} else {
+		warning("[PlaceCharacterOnDummy] Character not found %s", charname.c_str());
+	}
+
+}
+
+static int tolua_ExportedFunctions_PlaceCharacterOnDummy00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnumber(L, 5, 0, &err) && tolua_isnoobj(L, 6, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		float f1 = tolua_tonumber(L, 3, 0.0);
+		float f2 = tolua_tonumber(L, 4, 0.0);
+		float f3 = tolua_tonumber(L, 5, 0.0);
+		PlaceCharacterOnDummy(s1, s2, f1, f2, f3);
+		return 0;
+	}
+	error("#ferror in function 'SetCharacterRotation': %d %d %s", err.index, err.array, err.type);
+}
+ 
 static void SetCharacterRotation(const Common::String &charname, float rx, float ry, float rz) {
 	TeQuaternion quat = TeQuaternion::fromEuler(TeVector3f32(rx * M_PI / 180.0, ry * M_PI / 180.0, rz * M_PI / 180.0));
 	Game *game = g_engine->getGame();
@@ -783,6 +874,41 @@ static int tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00(lua_State *L) {
 	error("#ferror in function 'LaunchDialogAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
 }
 
+static void PushAnswer(const Common::String &val, const Common::String &gui) {
+	Application *app = g_engine->getApplication();
+	const Common::String *locVal = app->_loc.value(val);
+	Common::String locValStr;
+	if (locVal) {
+		locValStr = *locVal;
+	}
+	Game *game = g_engine->getGame();
+	game->question2().pushAnswer(val, locValStr, gui);
+}
+
+static int tolua_ExportedFunctions_PushAnswer00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		PushAnswer(s1, s2);
+		return 0;
+	}
+	error("#ferror in function 'PushAnswer': %d %d %s", err.index, err.array, err.type);
+}
+
+static void HideAnswers() {
+	g_engine->getGame()->question2().leave();
+}
+
+static int tolua_ExportedFunctions_HideAnswers00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnoobj(L, 1, &err)) {
+		HideAnswers();
+		return 0;
+	}
+	error("#ferror in function 'HideAnswers': %d %d %s", err.index, err.array, err.type);
+}
+
 static void PushTask(const Common::String &s1, const Common::String &s2) {
 	Game *game = g_engine->getGame();
 	game->objectif().pushObjectif(s1, s2);
@@ -918,6 +1044,37 @@ static int tolua_ExportedFunctions_ActivateAnchorZone00(lua_State *L) {
 	error("#ferror in function 'ActivateAnchorZone': %d %d %s", err.index, err.array, err.type);
 }
 
+static void SetCharacterLookChar(const Common::String &charname, const Common::String &destname, bool tall) {
+	Game *game = g_engine->getGame();
+	Character *character = game->scene().character(charname);
+	if (!character)
+		error("[SetCharacterLookChar] Character \"%s\" doesn't exist", charname.c_str());
+	character->setLookingAtTallThing(tall);
+	if (destname.empty()) {
+		character->setCharLookingAt(nullptr);
+	} else {
+		Character *destchar = game->scene().character(destname);
+		character->setCharLookingAt(destchar);
+		if (destchar)
+			return;
+	}
+	character->setLastHeadRotation(character->headRotation());
+	character->setHasAnchor(false);
+}
+
+static int tolua_ExportedFunctions_SetCharacterLookChar00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isboolean(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		bool b = tolua_toboolean(L, 3, 1);
+		SetCharacterLookChar(s1, s2, b);
+		return 0;
+	}
+	error("#ferror in function 'SetCharacterLookChar': %d %d %s", err.index, err.array, err.type);
+}
+
 static void DisabledZone(const Common::String &s1, bool b) {
 	Game *game = g_engine->getGame();
 	if (!game->scene().markerGui().loaded())
@@ -1042,8 +1199,10 @@ static int tolua_ExportedFunctions_PlayMusic00(lua_State *L) {
 static void SetObjectOnCharacter(const Common::String &obj, const Common::String &charName, const Common::String &boneName) {
 	Game *game = g_engine->getGame();
 	Object3D *obj3d = game->scene().object3D(obj);
-	if (!obj3d)
+	if (!obj3d) {
 		warning("[SetObjectOnCharacter] Object not found %s", obj.c_str());
+		return;
+	}
 
 	obj3d->_onCharName = charName;
 	obj3d->_onCharBone = boneName;
@@ -1176,6 +1335,56 @@ static int tolua_ExportedFunctions_CurrentCharacterAnimation00(lua_State *L) {
 	error("#ferror in function 'CurrentCharacterAnimation': %d %d %s", err.index, err.array, err.type);
 }
 
+static void LoadCharacter(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	game->loadCharacter(name);
+}
+
+static int tolua_ExportedFunctions_LoadCharacter00(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));
+		LoadCharacter(s1);
+		return 0;
+	}
+	error("#ferror in function 'LoadCharacter': %d %d %s", err.index, err.array, err.type);
+}
+
+static void UnloadCharacter(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	game->unloadCharacter(name);
+}
+
+static int tolua_ExportedFunctions_UnloadCharacter00(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));
+		UnloadCharacter(s1);
+		return 0;
+	}
+	error("#ferror in function 'UnloadCharacter': %d %d %s", err.index, err.array, err.type);
+}
+
+static void MoveCharacterTo(const Common::String &charName, const Common::String &curveName, float f1,float f2) {
+	Game *game = g_engine->getGame();
+	game->scene().moveCharacterTo(charName, curveName, f1, f2);
+}
+
+static int tolua_ExportedFunctions_MoveCharacterTo00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isstring(L, 3, 0, &err) && tolua_isstring(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		float f1 = tolua_tonumber(L, 3, 0.0);
+		float f2 = tolua_tonumber(L, 4, 0.0);
+		MoveCharacterTo(s1, s2, f1, f2);
+		return 0;
+	}
+	error("#ferror in function 'MoveCharacterTo': %d %d %s", err.index, err.array, err.type);
+}
+
 static void MoveCharacterPlayerTo(float x, float y, float z, bool walkFlag) {
 	Game *game = g_engine->getGame();
 	if (game->_movePlayerCharacterDisabled)
@@ -1241,9 +1450,8 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_open(L);
 	tolua_module(L, 0, 0);
 	tolua_beginmodule(L, 0);
-	/*
 	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials00);
-	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);*/
+	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);
 	tolua_function(L, "HideObject", tolua_ExportedFunctions_HideObject00);
 	tolua_function(L, "ShowObject", tolua_ExportedFunctions_ShowObject00);
 	// tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00); // Never used
@@ -1253,9 +1461,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
 	tolua_function(L, "PlayMovie", tolua_ExportedFunctions_PlayMovie00);
 	/*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);
-	tolua_function(L, "StartAnimationPart", tolua_ExportedFunctions_StartAnimationPart00);
+	tolua_function(L, "StartAnimationPart", tolua_ExportedFunctions_StartAnimationPart00);*/
 	tolua_function(L, "StartAnimation", tolua_ExportedFunctions_StartAnimation00);
-	tolua_function(L, "StartAnimationAndWaitForEnd",
+	/*tolua_function(L, "StartAnimationAndWaitForEnd",
 				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00);
 	tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00); */
 	tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
@@ -1272,12 +1480,12 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00);*/
 	tolua_function(L, "LaunchDialog", tolua_ExportedFunctions_LaunchDialog00);
 	tolua_function(L, "LaunchDialogAndWaitForEnd", tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00);
-	/*tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
-	tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);*/
+	tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
+	tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);
 	tolua_function(L, "PushTask", tolua_ExportedFunctions_PushTask00);
 	tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
-	/*tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);
-	tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00);*/
+	/*tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);*/
+	// tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00); Not used.
 	tolua_function(L, "PlaySound", tolua_ExportedFunctions_PlaySound00);
 	/*tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);*/
 	tolua_function(L, "StopSound", tolua_ExportedFunctions_StopSound00);
@@ -1294,22 +1502,22 @@ void LuaOpenBinds(lua_State *L) {
 	/*tolua_function(L, "ShowDocument", tolua_ExportedFunctions_ShowDocument00);
 	tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00);
 	tolua_function(L, "HideDocument", tolua_ExportedFunctions_HideDocument00);
-	tolua_function(L, "AddDocument", tolua_ExportedFunctions_AddDocument00);
+	tolua_function(L, "AddDocument", tolua_ExportedFunctions_AddDocument00);*/
 	tolua_function(L, "LoadCharacter", tolua_ExportedFunctions_LoadCharacter00);
 	tolua_function(L, "UnloadCharacter", tolua_ExportedFunctions_UnloadCharacter00);
-	tolua_function(L, "GetRotationCharacter", tolua_ExportedFunctions_GetRotationCharacter00);
-	tolua_function(L, "GetXPositionCharacter", tolua_ExportedFunctions_GetXPositionCharacter00);
-	tolua_function(L, "GetYPositionCharacter", tolua_ExportedFunctions_GetYPositionCharacter00);
-	tolua_function(L, "GetZPositionCharacter", tolua_ExportedFunctions_GetZPositionCharacter00);
+	// tolua_function(L, "GetRotationCharacter", tolua_ExportedFunctions_GetRotationCharacter00); // Unused
+	// tolua_function(L, "GetXPositionCharacter", tolua_ExportedFunctions_GetXPositionCharacter00); // Unused
+	// tolua_function(L, "GetYPositionCharacter", tolua_ExportedFunctions_GetYPositionCharacter00); // Unused
+	// tolua_function(L, "GetZPositionCharacter", tolua_ExportedFunctions_GetZPositionCharacter00); // Unused
 	tolua_function(L, "MoveCharacterTo", tolua_ExportedFunctions_MoveCharacterTo00);
-	tolua_function(L, "MoveCharacterToAndWaitForEnd",
+	/*tolua_function(L, "MoveCharacterToAndWaitForEnd",
 				 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);*/
 	tolua_function(L, "MoveCharacterPlayerTo", tolua_ExportedFunctions_MoveCharacterPlayerTo00);
 	/*tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
 				 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00);
 	tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00);*/
 	tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
-	/*tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);*/
+	tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);
 	tolua_function(L, "SetCharacterRotation", tolua_ExportedFunctions_SetCharacterRotation00);
 	tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
 	tolua_function(L, "SetCharacterAnimation", tolua_ExportedFunctions_SetCharacterAnimation00);
@@ -1374,9 +1582,9 @@ void LuaOpenBinds(lua_State *L) {
 	/*tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);*/
 	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
 	tolua_function(L, "ActivateAnchorZone", tolua_ExportedFunctions_ActivateAnchorZone00);
-	/*tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00);
+	//tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00); // Unused
 	tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
-	tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
+	/*tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
 	tolua_function(L, "SetCharacterMeshVisible", tolua_ExportedFunctions_SetCharacterMeshVisible00);
 	tolua_function(L, "SetRecallageY", tolua_ExportedFunctions_SetRecallageY00);*/
 	// tolua_function(L, "IsFreemiumUnlocked", tolua_ExportedFunctions_IsFreemiumUnlocked00); // Unused
@@ -1387,7 +1595,6 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_endmodule(L);
 }
 
-
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/question2.cpp b/engines/tetraedge/game/question2.cpp
index fbc55be9189..1e86f08ca1d 100644
--- a/engines/tetraedge/game/question2.cpp
+++ b/engines/tetraedge/game/question2.cpp
@@ -88,9 +88,9 @@ bool Question2::onAnswerValidated(Answer &answer) {
 	return false;
 }
 
-void Question2::pushAnswer(const Common::String &name, const Common::String &unk, const Common::String &path) {
+void Question2::pushAnswer(const Common::String &name, const Common::String &locName, const Common::String &path) {
 	Answer *answer = new Answer();
-	answer->load(name, unk, path);
+	answer->load(name, locName, path);
 	answer->_onButtonValidatedSignal.add(this, &Question2::onAnswerValidated);
 	TeLayout *alayout = answer->layout();
 	if (!alayout)
@@ -134,7 +134,7 @@ TeLayout *Question2::Answer::layout() {
 	return _gui.layout("answer");
 }
 
-void Question2::Answer::load(const Common::String &name, const Common::String &unk, const Common::String &path) {
+void Question2::Answer::load(const Common::String &name, const Common::String &locName, const Common::String &path) {
 	_str = name;
 	_gui.load(path);
 	TeButtonLayout *answerButton = _gui.buttonLayout("answer");
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index ac94154adbc..54693085cb7 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -39,7 +39,7 @@ Te3DTexture::~Te3DTexture() {
 	destroy();
 }
 
-void Te3DTexture::bind() {
+void Te3DTexture::bind() const {
 	TeRenderer *renderer = g_engine->getRenderer();
 	glBindTexture(GL_TEXTURE_2D, _glTexture);
 	renderer->setMatrixMode(TeRenderer::MM_GL_TEXTURE);
@@ -66,11 +66,11 @@ void Te3DTexture::copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y)
 }
 
 void Te3DTexture::create() {
-	_flipY = true;
+	_flipY = false;
 	_leftBorder = _btmBorder = _texWidth = _texHeight = 0;
 	_rightBorder = _topBorder = _width = _height = 0;
 	_format = TeImage::INVALID;
-	_loaded = 0;
+	_loaded = false;
 	if (!_createdTexture)
 		glGenTextures(1, &_glTexture);
 	if (_glTexture == NO_TEXTURE) {
diff --git a/engines/tetraedge/te/te_3d_texture.h b/engines/tetraedge/te/te_3d_texture.h
index cbf228836fe..f3a57e96041 100644
--- a/engines/tetraedge/te/te_3d_texture.h
+++ b/engines/tetraedge/te/te_3d_texture.h
@@ -39,7 +39,7 @@ public:
 	Te3DTexture();
 	virtual ~Te3DTexture();
 
-	void bind();
+	void bind() const;
 	void copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y);
 	void create();
 	void destroy();
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index 12ca0e7dcb5..430d9be686f 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -28,8 +28,8 @@
 
 namespace Tetraedge {
 
-TeCamera::TeCamera() : _projectionMatrixType(0), _orthogonalParamL(1.0f),
-	_orthogonalParamR(0.0f), _orthogonalParamT(1.0f), _orthogonalParamB(0.0f),
+TeCamera::TeCamera() : _projectionMatrixType(0), _orthogonalParamL(0.0f),
+	_orthogonalParamR(1.0f), _orthogonalParamT(1.0f), _orthogonalParamB(0.0f),
 	_orthNearVal(10.0f), _orthFarVal(4000.0f), _transformA(0), _transformB(0),
 	_fov(40.0f), _somePerspectiveVal(1.0f)
 {
diff --git a/engines/tetraedge/te/te_frame_anim.cpp b/engines/tetraedge/te/te_frame_anim.cpp
index 678d21c5508..c115c8ec994 100644
--- a/engines/tetraedge/te/te_frame_anim.cpp
+++ b/engines/tetraedge/te/te_frame_anim.cpp
@@ -25,7 +25,7 @@
 
 namespace Tetraedge {
 
-TeFrameAnim::TeFrameAnim() : _nbFrames(0), _frameRate(25.0), _reversed(false), _minFrame(0),
+TeFrameAnim::TeFrameAnim() : _nbFrames(0), _frameRate(25.0f), _reversed(false), _minFrame(0),
 _numFramesToShow(-1), _startTime(0), _endTime(FLT_MAX), _loopCount(0), _lastFrameShown(-1) {
 }
 
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 1703f16a61f..98cafcda9e4 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -288,7 +288,8 @@ static int segmentIntersection(const TeVector2f32 &s1start, const TeVector2f32 &
 			if (intersection2 >= 0.0f && intersection2 <= 1.0f) {
 				result = 2;
 				if (sout || fout1 || fout2) {
-					warning("TODO: implement output in segmentIntersection");
+					// Seems like these are always null?
+					error("TODO: implement output in segmentIntersection");
 				}
 			}
 		}
diff --git a/engines/tetraedge/te/te_layout.cpp b/engines/tetraedge/te/te_layout.cpp
index 387e4f56256..733eb5ba5ac 100644
--- a/engines/tetraedge/te/te_layout.cpp
+++ b/engines/tetraedge/te/te_layout.cpp
@@ -36,7 +36,7 @@ TeLayout::TeLayout() : Te3DObject2(), _updatingZ(false), _updatingZSize(false),
 	_safeAreaRatio(1.3333334f), _ratioMode(RATIO_MODE_NONE),
 	_positionType(CoordinatesType::RELATIVE_TO_PARENT)
 {
-	_userPosition = _position = TeVector3f32(0.5f, 0.5f, 0.0f);
+	_userPosition = _position = TeVector3f32(0.5f, 0.5f, 0.5f);
 	_size = TeVector3f32(1.0f, 1.0f, 1.0f);
 	_onChildSizeChangedCallback.reset(
 		new TeCallback0Param<TeLayout>(this, &TeLayout::onChildSizeChanged));
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index 78850e02155..9d184739831 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -600,6 +600,7 @@ int layoutAnchorLinearAnimationBindings(lua_State *L) {
 				static const TeVector3f32 defaultEnd(0.0f, 0.0f, 0.0f);
 				anim->_endVal = TeLuaToTeVector3f32(L, -1, defaultEnd);
 			} else if (!strcmp(s, "layout")) {
+				// skip.
 			} else if (!strcmp(s, "curve")) {
 				const Common::Array<float> curve = TeLuaToFloatArray(L, -1);
 				anim->setCurve(curve);
diff --git a/engines/tetraedge/te/te_material.cpp b/engines/tetraedge/te/te_material.cpp
index 143eb035c1a..2f740f45ea4 100644
--- a/engines/tetraedge/te/te_material.cpp
+++ b/engines/tetraedge/te/te_material.cpp
@@ -64,7 +64,7 @@ Common::String TeMaterial::dump() const {
 			  _shininess, _enableLights ? "on" : "off");
 }
 
-void TeMaterial::apply() {
+void TeMaterial::apply() const {
 	//debug("TeMaterial::apply (%s)", dump().c_str());
 	static const float constColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
 	TeRenderer *renderer = g_engine->getRenderer();
diff --git a/engines/tetraedge/te/te_material.h b/engines/tetraedge/te/te_material.h
index b1989cdea97..4758d1f5df8 100644
--- a/engines/tetraedge/te/te_material.h
+++ b/engines/tetraedge/te/te_material.h
@@ -43,7 +43,7 @@ public:
 	TeMaterial(const TeMaterial &other) = default;
 	TeMaterial(TeIntrusivePtr<Te3DTexture> texture, Mode mode);
 
-	void apply();
+	void apply() const;
 	void defaultValues();
 	static void deserialize(Common::SeekableReadStream &stream, TeMaterial &material, const Common::Path &path);
 	static void serialize(Common::SeekableWriteStream &stream, TeMaterial &material);
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index 0ffbc513073..3b404627c4f 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -217,19 +217,19 @@ TeMesh::Mode TeMesh::getMode() const {
 }
 
 bool TeMesh::hasAlpha(uint idx) {
-	// FIXME: this logic is a bit sketchy.  Check it again.
-	if (_hasAlpha && !_colors.empty())
-		return true;
+	// Note: I don't understand this logic, but it's what the game does..
+	bool hasGlobalAlpha = _hasAlpha && !_colors.empty();
 
-	bool retval = false;
+	bool retval = hasGlobalAlpha;
 	if (idx < _materials.size()) {
 		const TeMaterial &material = _materials[idx];
-		if (material._enableSomethingDefault0)
+		if (material._enableSomethingDefault0) {
+			retval = false;
+		} else {
 			retval = true;
-		//if (material._mode == TeMaterial::MaterialMode1)
-		//	return false;
-		if (material._ambientColor.a() == 255)
-			retval = (material._diffuseColor.a() != 255);
+			if (!hasGlobalAlpha && material._mode != TeMaterial::MaterialMode1 && material._ambientColor.a() == 255)
+				retval = (material._diffuseColor.a() != 255);
+		}
 	}
 	return retval;
 }
diff --git a/engines/tetraedge/te/te_mesh.h b/engines/tetraedge/te/te_mesh.h
index fb0a9aa6df1..8d6e5cd442d 100644
--- a/engines/tetraedge/te/te_mesh.h
+++ b/engines/tetraedge/te/te_mesh.h
@@ -108,7 +108,7 @@ public:
 	uint numIndexes() const { return _indexes.size(); }
 	uint numVerticies() const { return _verticies.size(); }
 	bool shouldDrawMaybe() const { return _shouldDraw; }
-	uint gltexenvMode() const { return _gltexEnvMode; }
+	uint gltexEnvMode() const { return _gltexEnvMode; }
 
 	void setShouldDraw(bool val) { _shouldDraw = val; }
 	void setglTexEnv(unsigned int val) { _gltexEnvMode = val; }
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 7f560f57f5e..4c8bfdbb9f1 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -343,7 +343,7 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 	}
 
 	uint version = stream.readUint32LE();
-	if (!(version == 11) || (version == 13)) {
+	if (!((version == 11) || (version == 13))) {
 		error("[TeModel::load] Unsupported version %d", version);
 	}
 
@@ -430,8 +430,8 @@ Common::SeekableReadStream *TeModel::tryLoadZlibStream(Common::SeekableReadStrea
 		return nullptr;
 	}
 	uint32 uncompressedSize = stream.readUint32LE();
-	Common::SeekableSubReadStream substream(&stream, stream.pos(), stream.size());
-	return Common::wrapCompressedReadStream(&substream, uncompressedSize);
+	Common::SeekableSubReadStream *substream = new Common::SeekableSubReadStream(&stream, stream.pos(), stream.size());
+	return Common::wrapCompressedReadStream(substream, uncompressedSize);
 }
 
 bool TeModel::loadWeights(Common::ReadStream &stream, Common::Array<weightElement> &weights) {
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index 85b5cd84d45..83c1c1f2716 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -171,7 +171,8 @@ int TeModelAnimation::lastFrame() const {
 	if (!_useNMOArrays) {
 		if (_fbxArrays.empty())
 			result = 0;
-		result = _fbxArrays[0].size();
+		else
+			result = _fbxArrays[0].size();
 	} else {
 		result = _numNMOFrames;
 	}
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 8830f3bcbbe..9c8563934eb 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -33,96 +33,82 @@ TeRenderer::TeRenderer() : _textureEnabled(false), _shadowMode(ShadowMode0), _ma
 _numTransparentMeshes(0), _pendingTransparentMeshProperties(0) {
 }
 
-void TeRenderer::TransparentMeshProperties::setFromMaterial(const TeMaterial &material) {
-	_texture = material._texture;
-	_enableLights = material._enableLights;
-	_enableSomethingDefault0 = material._enableSomethingDefault0;
-	_shininess = material._shininess;
-	_emissionColor = material._emissionColor;
-	_specularColor = material._specularColor;
-	_diffuseColor = material._diffuseColor;
-	_ambientColor = material._ambientColor;
-	_materialMode = material._mode;
-}
-
-void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsigned long meshno, unsigned long materialno) {
+void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsigned long tricount, unsigned long materialno) {
 	const float orthNearVal = _currentCamera->_orthNearVal;
 	const TeMesh::Mode meshMode = mesh.getMode();
-	if (!meshno) {
+	if (!tricount) {
 		if (meshMode == TeMesh::MeshMode_TriangleStrip) {
-			meshno = mesh.numVerticies() - 2;
+			tricount = mesh.numVerticies() - 2;
 		} else if (meshMode == TeMesh::MeshMode_Triangles) {
-			meshno = mesh.numIndexes() / 3;
-		} else {
-			return;
+			tricount = mesh.numIndexes() / 3;
 		}
-		if (meshno == 0)
+		if (!tricount)
 			return;
 	}
-	_transparentMeshVertexes.resize((_numTransparentMeshes + meshno) * 3);
-	_transparentMeshNormals.resize((_numTransparentMeshes + meshno) * 3);
-	_transparentMeshCoords.resize((_numTransparentMeshes + meshno) * 3);
-	_transparentMeshColors.resize((_numTransparentMeshes + meshno) * 3);
-	_transparentMeshTriangleNums.resize((_numTransparentMeshes + meshno) * 3);
-
-	int newPropsSize = mesh.shouldDrawMaybe() ? _pendingTransparentMeshProperties + meshno : _pendingTransparentMeshProperties + 1;
-	_transparentMeshProperties.resize(newPropsSize);
-	if (meshMode == TeMesh::MeshMode_Triangles && meshno > 0) {
-		for (unsigned int i = 0; i < meshno; i++) {
+	_transparentMeshVertexes.resize((_numTransparentMeshes + tricount) * 3);
+	_transparentMeshNormals.resize((_numTransparentMeshes + tricount) * 3);
+	_transparentMeshCoords.resize((_numTransparentMeshes + tricount) * 3);
+	_transparentMeshColors.resize((_numTransparentMeshes + tricount) * 3);
+	_transparentMeshVertexNums.resize((_numTransparentMeshes + tricount) * 3);
+
+	int newPropsSize = _pendingTransparentMeshProperties + (mesh.shouldDrawMaybe() ? tricount : 1);
+	_transparentMeshProps.resize(newPropsSize);
+	if (meshMode == TeMesh::MeshMode_Triangles) {
+		for (unsigned int i = 0; i < tricount; i++) {
 			const uint meshNo0 = (i1 + i) * 3;
-			const uint meshPropNo = (_numTransparentMeshes + i) * 3;
+			const uint propNo = (_numTransparentMeshes + i) * 3;
 
-			_transparentMeshVertexes[meshPropNo] = mesh.vertex(mesh.index(meshNo0));
-			_transparentMeshVertexes[meshPropNo + 1] = mesh.vertex(mesh.index(meshNo0 + 1));
-			_transparentMeshVertexes[meshPropNo + 2] = mesh.vertex(mesh.index(meshNo0 + 2));
+			_transparentMeshVertexes[propNo] = mesh.vertex(mesh.index(meshNo0));
+			_transparentMeshVertexes[propNo + 1] = mesh.vertex(mesh.index(meshNo0 + 1));
+			_transparentMeshVertexes[propNo + 2] = mesh.vertex(mesh.index(meshNo0 + 2));
 
-			_transparentMeshNormals[meshPropNo] = mesh.normal(mesh.index(meshNo0));
-			_transparentMeshNormals[meshPropNo + 1] = mesh.normal(mesh.index(meshNo0 + 1));
-			_transparentMeshNormals[meshPropNo + 2] = mesh.normal(mesh.index(meshNo0 + 2));
+			_transparentMeshNormals[propNo] = mesh.normal(mesh.index(meshNo0));
+			_transparentMeshNormals[propNo + 1] = mesh.normal(mesh.index(meshNo0 + 1));
+			_transparentMeshNormals[propNo + 2] = mesh.normal(mesh.index(meshNo0 + 2));
 
 			if (mesh.hasUvs()) {
-				_transparentMeshCoords[meshPropNo] = mesh.textureUV(mesh.index(meshNo0));
-				_transparentMeshCoords[meshPropNo + 1] = mesh.textureUV(mesh.index(meshNo0) + 1);
-				_transparentMeshCoords[meshPropNo + 2] = mesh.textureUV(mesh.index(meshNo0) + 2);
+				_transparentMeshCoords[propNo] = mesh.textureUV(mesh.index(meshNo0));
+				_transparentMeshCoords[propNo + 1] = mesh.textureUV(mesh.index(meshNo0 + 1));
+				_transparentMeshCoords[propNo + 2] = mesh.textureUV(mesh.index(meshNo0 + 2));
 			}
 
 			if (!mesh.hasColor()) {
-				_transparentMeshColors[meshPropNo] = mesh.material(materialno)->_diffuseColor;
-				_transparentMeshColors[meshPropNo + 1] = mesh.material(materialno)->_diffuseColor;
-				_transparentMeshColors[meshPropNo + 2] = mesh.material(materialno)->_diffuseColor;
+				_transparentMeshColors[propNo] = mesh.material(materialno)->_diffuseColor;
+				_transparentMeshColors[propNo + 1] = mesh.material(materialno)->_diffuseColor;
+				_transparentMeshColors[propNo + 2] = mesh.material(materialno)->_diffuseColor;
 			} else {
-				_transparentMeshColors[meshPropNo] = mesh.color(mesh.index(meshNo0));
-				_transparentMeshColors[meshPropNo + 1] = mesh.color(mesh.index(meshNo0) + 1);
-				_transparentMeshColors[meshPropNo + 2] = mesh.color(mesh.index(meshNo0) + 2);
+				_transparentMeshColors[propNo] = mesh.color(mesh.index(meshNo0));
+				_transparentMeshColors[propNo + 1] = mesh.color(mesh.index(meshNo0 + 1));
+				_transparentMeshColors[propNo + 2] = mesh.color(mesh.index(meshNo0 + 2));
 			}
 		}
-	} else if (meshMode == TeMesh::MeshMode_TriangleStrip && meshno > 0) {
-		for (unsigned int i = 0; i < meshno; i++) {
+	} else if (meshMode == TeMesh::MeshMode_TriangleStrip && tricount > 0) {
+		for (unsigned int i = 0; i < tricount; i++) {
 			const uint meshNo0 = (i1 + i);  // TODO: This appears to be the only difference between this and the above?
-			const uint meshPropNo = (_numTransparentMeshes + i) * 3;
+			const uint propNo = (_numTransparentMeshes + i) * 3;
 
-			_transparentMeshVertexes[meshPropNo] = mesh.vertex(mesh.index(meshNo0));
-			_transparentMeshVertexes[meshPropNo + 1] = mesh.vertex(mesh.index(meshNo0 + 1));
-			_transparentMeshVertexes[meshPropNo + 2] = mesh.vertex(mesh.index(meshNo0 + 2));
+			_transparentMeshVertexes[propNo] = mesh.vertex(mesh.index(meshNo0));
+			_transparentMeshVertexes[propNo + 1] = mesh.vertex(mesh.index(meshNo0 + 1));
+			_transparentMeshVertexes[propNo + 2] = mesh.vertex(mesh.index(meshNo0 + 2));
 
-			_transparentMeshNormals[meshPropNo] = mesh.normal(mesh.index(meshNo0));
-			_transparentMeshNormals[meshPropNo + 1] = mesh.normal(mesh.index(meshNo0 + 1));
-			_transparentMeshNormals[meshPropNo + 2] = mesh.normal(mesh.index(meshNo0 + 2));
+			_transparentMeshNormals[propNo] = mesh.normal(mesh.index(meshNo0));
+			_transparentMeshNormals[propNo + 1] = mesh.normal(mesh.index(meshNo0 + 1));
+			_transparentMeshNormals[propNo + 2] = mesh.normal(mesh.index(meshNo0 + 2));
 
 			if (mesh.hasUvs()) {
-				_transparentMeshCoords[meshPropNo] = mesh.textureUV(mesh.index(meshNo0));;
-				_transparentMeshCoords[meshPropNo + 1] = mesh.textureUV(mesh.index(meshNo0 + 1));
-				_transparentMeshCoords[meshPropNo + 2] = mesh.textureUV(mesh.index(meshNo0 + 2));
+				_transparentMeshCoords[propNo] = mesh.textureUV(mesh.index(meshNo0));;
+				_transparentMeshCoords[propNo + 1] = mesh.textureUV(mesh.index(meshNo0 + 1));
+				_transparentMeshCoords[propNo + 2] = mesh.textureUV(mesh.index(meshNo0 + 2));
 			}
 
 			if (!mesh.hasColor()) {
-				_transparentMeshColors[meshPropNo] =  mesh.material(materialno)->_diffuseColor;
-				_transparentMeshColors[meshPropNo + 1] =  mesh.material(materialno)->_diffuseColor;
-				_transparentMeshColors[meshPropNo + 2] =  mesh.material(materialno)->_diffuseColor;
+				_transparentMeshColors[propNo] =  mesh.material(materialno)->_diffuseColor;
+				_transparentMeshColors[propNo + 1] =  mesh.material(materialno)->_diffuseColor;
+				_transparentMeshColors[propNo + 2] =  mesh.material(materialno)->_diffuseColor;
 			} else {
-				_transparentMeshColors[meshPropNo] = mesh.color(mesh.index(meshNo0));
-				_transparentMeshColors[meshPropNo + 1] = mesh.color(mesh.index(meshNo0 + 1));
-				_transparentMeshColors[meshPropNo + 2] = mesh.color(mesh.index(meshNo0 + 2));
+				_transparentMeshColors[propNo] = mesh.color(mesh.index(meshNo0));
+				_transparentMeshColors[propNo + 1] = mesh.color(mesh.index(meshNo0 + 1));
+				_transparentMeshColors[propNo + 2] = mesh.color(mesh.index(meshNo0 + 2));
 			}
 		}
 	}
@@ -130,34 +116,32 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 	if (!mesh.shouldDrawMaybe()) {
 		// TODO: better variable names.
 		const TeMatrix4x4 &currentMatrix = _matriciesStacks[MM_GL_MODELVIEW].currentMatrix();
-		const TeVector3f32 local_268 = currentMatrix.mult4x3(_transparentMeshVertexes[_numTransparentMeshes * 3]);
-		const TeVector3f32 local_278 = currentMatrix.mult4x3(_transparentMeshVertexes[_numTransparentMeshes * 3 + 1]);
-		const TeVector3f32 local_288 = currentMatrix.mult4x3(_transparentMeshVertexes[_numTransparentMeshes * 3 + 2]);
-		TeVector3f32 local_298 = (local_268 + local_278 + local_288) / 3.0;
+		const TeVector3f32 v1trans = currentMatrix.mult4x3(_transparentMeshVertexes[_numTransparentMeshes * 3]);
+		const TeVector3f32 v2trans = currentMatrix.mult4x3(_transparentMeshVertexes[_numTransparentMeshes * 3 + 1]);
+		const TeVector3f32 v3trans = currentMatrix.mult4x3(_transparentMeshVertexes[_numTransparentMeshes * 3 + 2]);
+		TeVector3f32 midpoint = (v1trans + v2trans + v3trans) / 3.0;
 
-		local_298.z() -= orthNearVal;
-		float length;
+		midpoint.z() -= orthNearVal;
+		float zOrder;
 		if (_currentCamera->_projectionMatrixType < 4) {
-			length = -local_298.squaredLength();
+			zOrder = -midpoint.squaredLength();
 		} else if (_currentCamera->_projectionMatrixType == 4) {
-			length = local_298.z() * local_298.z();
+			zOrder = midpoint.z() * midpoint.z();
 		} else {
-			length = local_298.squaredLength();
+			zOrder = midpoint.squaredLength();
 		}
 
-		TransparentMeshProperties &destProperties = _transparentMeshProperties[_pendingTransparentMeshProperties];
+		TransparentMeshProperties &destProperties = _transparentMeshProps[_pendingTransparentMeshProperties];
 
-		destProperties._triangleCount = meshno * 3;
+		destProperties._vertexCount = tricount * 3;
 		destProperties._camera = _currentCamera;
 
-		const TeMaterial *material = mesh.material(materialno);
-		destProperties.setFromMaterial(*material);
+		destProperties._material = *mesh.material(materialno);
 		destProperties._matrix = currentMatrix;
-
-		destProperties._glTexEnvMode = mesh.gltexenvMode();
+		destProperties._glTexEnvMode = mesh.gltexEnvMode();
 		destProperties._sourceTransparentMesh = _numTransparentMeshes * 3;
 		destProperties._hasColor = mesh.hasColor();
-		destProperties._zLength = length;
+		destProperties._zOrder = zOrder;
 		destProperties._scissorEnabled = _scissorEnabled;
 		destProperties._scissorX = _scissorX;
 		destProperties._scissorY = _scissorY;
@@ -165,7 +149,7 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 		destProperties._scissorHeight = _scissorHeight;
 		destProperties._shouldDraw = false;
 	} else {
-		for (uint i = 0; i < meshno; i++) {
+		for (uint i = 0; i < tricount; i++) {
 			const TeMatrix4x4 &currentMatrix = _matriciesStacks[MM_GL_MODELVIEW].currentMatrix();
 
 			const int meshPropNo = (_numTransparentMeshes + i) * 3;
@@ -177,28 +161,27 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 			_transparentMeshNormals[meshPropNo + 1] = currentMatrix.mult3x3(_transparentMeshNormals[meshPropNo + 1]);
 			_transparentMeshNormals[meshPropNo + 2] = currentMatrix.mult3x3(_transparentMeshNormals[meshPropNo + 2]);
 
-			TeVector3f32 local_208 = (_transparentMeshVertexes[meshPropNo] + _transparentMeshVertexes[meshPropNo + 1] + _transparentMeshVertexes[meshPropNo + 2]) / 3.0;
-			local_208.z() -= orthNearVal;
+			TeVector3f32 midpoint = (_transparentMeshVertexes[meshPropNo] + _transparentMeshVertexes[meshPropNo + 1] + _transparentMeshVertexes[meshPropNo + 2]) / 3.0;
+			midpoint.z() -= orthNearVal;
 
-			float length;
+			float zOrder;
 			if (_currentCamera->_projectionMatrixType < 4) {
-				length = -local_208.squaredLength();
+				zOrder = -midpoint.squaredLength();
 			} else if (_currentCamera->_projectionMatrixType == 4) {
-				length = local_208.z() * local_208.z();
+				zOrder = midpoint.z() * midpoint.z();
 			} else {
-				length = local_208.squaredLength();
+				zOrder = midpoint.squaredLength();
 			}
 
-			TransparentMeshProperties &destProperties = _transparentMeshProperties[_pendingTransparentMeshProperties + i];
-			destProperties._triangleCount = 3;
+			TransparentMeshProperties &destProperties = _transparentMeshProps[_pendingTransparentMeshProperties + i];
+			destProperties._vertexCount = 3;
 			destProperties._camera = _currentCamera;
 
-			const TeMaterial *material = mesh.material(materialno);
-			destProperties.setFromMaterial(*material);
-			destProperties._glTexEnvMode = mesh.gltexenvMode();
+			destProperties._material = *mesh.material(materialno);
+			destProperties._glTexEnvMode = mesh.gltexEnvMode();
 			destProperties._sourceTransparentMesh = meshPropNo;
 			destProperties._hasColor = mesh.hasColor();
-			destProperties._zLength = length;
+			destProperties._zOrder = zOrder;
 			destProperties._scissorEnabled = _scissorEnabled;
 			destProperties._scissorX = _scissorX;
 			destProperties._scissorY = _scissorY;
@@ -207,8 +190,8 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 			destProperties._shouldDraw = true;
 		}
 	}
-	_numTransparentMeshes += meshno;
-	_pendingTransparentMeshProperties = _transparentMeshProperties.size();
+	_numTransparentMeshes += tricount;
+	_pendingTransparentMeshProperties = _transparentMeshProps.size();
 }
 
 void TeRenderer::clearBuffer(TeRenderer::Buffer buf) {
@@ -325,8 +308,22 @@ void TeRenderer::multiplyMatrix(const TeMatrix4x4 &matrix) {
 }
 
 void TeRenderer::optimiseTransparentMeshProperties() {
-	if (!_transparentMeshProperties.empty()) {
-		// TODO: Implement TeRenderer::optimiseTransparentMeshProperties.
+	if (_transparentMeshProps.size() > 1) {
+		for (unsigned int i = 0; i < _transparentMeshProps.size() - 1; i++) {
+			if (_transparentMeshProps[i]._camera == _transparentMeshProps[i + 1]._camera
+				&& _transparentMeshProps[i]._material == _transparentMeshProps[i + 1]._material
+				&& _transparentMeshProps[i]._glTexEnvMode == _transparentMeshProps[i + 1]._glTexEnvMode
+				&& _transparentMeshProps[i]._matrix == _transparentMeshProps[i + 1]._matrix
+				&& _transparentMeshProps[i]._hasColor == _transparentMeshProps[i + 1]._hasColor
+				&& _transparentMeshProps[i]._scissorEnabled == _transparentMeshProps[i + 1]._scissorEnabled
+				&& _transparentMeshProps[i]._scissorX == _transparentMeshProps[i + 1]._scissorX
+				&& _transparentMeshProps[i]._scissorY == _transparentMeshProps[i + 1]._scissorY
+				&& _transparentMeshProps[i]._scissorWidth == _transparentMeshProps[i + 1]._scissorWidth
+				&& _transparentMeshProps[i]._scissorHeight == _transparentMeshProps[i + 1]._scissorHeight) {
+				_transparentMeshProps[i]._vertexCount += _transparentMeshProps[i + 1]._vertexCount;
+				_transparentMeshProps[i + 1]._shouldDraw = false;
+			}
+		}
 	}
 }
 
@@ -345,11 +342,11 @@ Common::String TeRenderer::renderer() {
 
 static int compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
 											const TeRenderer::TransparentMeshProperties &p2) {
-	if (p1._zLength < p2._zLength)
-		return -1;
-	if (p1._zLength == p2._zLength)
+	if (p1._zOrder > p2._zOrder)
+		return 1;
+	if (p1._zOrder == p2._zOrder)
 		return 0;
-	return 1;
+	return -1;
 }
 
 void TeRenderer::dumpTransparentMeshes() const {
@@ -361,7 +358,7 @@ void TeRenderer::dumpTransparentMeshes() const {
 			  _transparentMeshNormals[i].dump().c_str(),
 			  _transparentMeshCoords[i].dump().c_str(),
 			  _transparentMeshColors[i].dump().c_str(),
-			  _transparentMeshTriangleNums[i]
+			  _transparentMeshVertexNums[i]
 			  );
 	}
 }
@@ -371,15 +368,15 @@ void TeRenderer::renderTransparentMeshes() {
 		return;
 
 	glDepthMask(GL_FALSE);
-	Common::sort(_transparentMeshProperties.begin(), _transparentMeshProperties.end(),
+	Common::sort(_transparentMeshProps.begin(), _transparentMeshProps.end(),
 		 compareTransparentMeshProperties);
 
-	int triangles = 0;
-	for (unsigned int i = 0; i < _transparentMeshProperties.size(); i++) {
-		const uint tcount = _transparentMeshProperties[i]._triangleCount;
-		for (unsigned int j = 0; j < tcount; j++)
-			_transparentMeshTriangleNums[triangles + j] = (short)(_transparentMeshProperties[i]._sourceTransparentMesh + j);
-		triangles += tcount;
+	int vertsDrawn = 0;
+	for (unsigned int i = 0; i < _transparentMeshProps.size(); i++) {
+		const uint vcount = _transparentMeshProps[i]._vertexCount;
+		for (unsigned int j = 0; j < vcount; j++)
+			_transparentMeshVertexNums[vertsDrawn + j] = (short)(_transparentMeshProps[i]._sourceTransparentMesh + j);
+		vertsDrawn += vcount;
 	}
 
 	//dumpTransparentMeshes();
@@ -396,24 +393,16 @@ void TeRenderer::renderTransparentMeshes() {
 	glColorPointer(4, GL_UNSIGNED_BYTE, 4, _transparentMeshColors.data());
 
 	TeMaterial lastMaterial;
+	TeMatrix4x4 lastMatrix;
 
-	triangles = 0;
-	for (unsigned int i = 0; i < _transparentMeshProperties.size(); i++) {
-		const TransparentMeshProperties &meshProperties = _transparentMeshProperties[i];
+	vertsDrawn = 0;
+	for (unsigned int i = 0; i < _transparentMeshProps.size(); i++) {
+		const TransparentMeshProperties &meshProperties = _transparentMeshProps[i];
 		if (!meshProperties._shouldDraw)
 			continue;
 
-		const TeIntrusivePtr<Te3DTexture> &texture = meshProperties._texture;
-
-		TeMaterial material(texture, meshProperties._materialMode);
-		material._ambientColor = meshProperties._ambientColor;
-		material._diffuseColor = meshProperties._diffuseColor;
-		material._specularColor = meshProperties._specularColor;
-		material._emissionColor = meshProperties._emissionColor;
-		material._shininess = meshProperties._shininess;
-		material._enableLights = meshProperties._enableLights;
-		material._enableSomethingDefault0 = meshProperties._enableSomethingDefault0;
-
+		const TeMaterial &material = meshProperties._material;
+		
 		meshProperties._camera->applyProjection();
 		glMatrixMode(GL_MODELVIEW);
 		_matrixMode = MM_GL_MODELVIEW;
@@ -422,7 +411,7 @@ void TeRenderer::renderTransparentMeshes() {
 		_matriciesStacks[_matrixMode].loadMatrix(meshProperties._matrix);
 		glPushMatrix();
 		loadCurrentMatrixToGL();
-		if (texture) {
+		if (material._texture) {
 			glEnable(GL_TEXTURE_2D);
 			_textureEnabled = true;
 		}
@@ -444,10 +433,10 @@ void TeRenderer::renderTransparentMeshes() {
 					  meshProperties._scissorHeight);
 		}
 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, meshProperties._glTexEnvMode);
-		glDrawElements(GL_TRIANGLES, meshProperties._triangleCount, GL_UNSIGNED_SHORT,
-				   _transparentMeshTriangleNums.data() + triangles);
+		glDrawElements(GL_TRIANGLES, meshProperties._vertexCount, GL_UNSIGNED_SHORT,
+				   _transparentMeshVertexNums.data() + vertsDrawn);
 
-		triangles += meshProperties._triangleCount;
+		vertsDrawn += meshProperties._vertexCount;
 
 		if (material._enableSomethingDefault0) {
 			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -457,7 +446,7 @@ void TeRenderer::renderTransparentMeshes() {
 		if (meshProperties._scissorEnabled) {
 			glDisable(GL_SCISSOR_TEST);
 		}
-		if (texture) {
+		if (material._texture) {
 			glDisable(GL_TEXTURE_2D);
 			_textureEnabled = false;
 		}
@@ -473,7 +462,7 @@ void TeRenderer::renderTransparentMeshes() {
 	_numTransparentMeshes = 0;
 	_pendingTransparentMeshProperties = 0;
 	glDepthMask(GL_TRUE);
-	_transparentMeshProperties.clear();
+	_transparentMeshProps.clear();
 }
 
 void TeRenderer::reset() {
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index 0a8b479a34e..e211ebc3f58 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -48,27 +48,17 @@ public:
 
 	class TransparentMeshProperties {
 	public:
-		void setFromMaterial(const TeMaterial &material);
 
-		TeIntrusivePtr<Te3DTexture> _texture;
 		TeCamera *_camera;
-		int _triangleCount;
+		int _vertexCount;
 		TeMatrix4x4 _matrix;
-		bool _enableLights;
-		bool _enableSomethingDefault0;
 
-		enum TeMaterial::Mode _materialMode;
-
-		TeColor _ambientColor;
-		TeColor _diffuseColor;
-		TeColor _specularColor;
-		TeColor _emissionColor;
-		float _shininess;
+		TeMaterial _material;
 
 		uint _glTexEnvMode;
 		uint _sourceTransparentMesh;
 		bool _hasColor;
-		float _zLength;
+		float _zOrder;
 		bool _scissorEnabled;
 		int _scissorX;
 		int _scissorY;
@@ -150,10 +140,10 @@ private:
 	Common::Array<TeVector3f32> _transparentMeshNormals;
 	Common::Array<TeVector2f32> _transparentMeshCoords;
 	Common::Array<TeColor> _transparentMeshColors;
-	Common::Array<unsigned short> _transparentMeshTriangleNums;
+	Common::Array<unsigned short> _transparentMeshVertexNums;
 
 	int _pendingTransparentMeshProperties;
-	Common::Array<TransparentMeshProperties> _transparentMeshProperties;
+	Common::Array<TransparentMeshProperties> _transparentMeshProps;
 
 	TeMatriciesStack _matriciesStacks[3];  // one per matrix mode.
 
diff --git a/engines/tetraedge/te/te_sprite_layout.cpp b/engines/tetraedge/te/te_sprite_layout.cpp
index 009d370f18e..625603d8571 100644
--- a/engines/tetraedge/te/te_sprite_layout.cpp
+++ b/engines/tetraedge/te/te_sprite_layout.cpp
@@ -26,7 +26,7 @@
 
 namespace Tetraedge {
 
-TeSpriteLayout::TeSpriteLayout() : _tiledSurfacePtr(new TeTiledSurface()), _sizeSet(false), _allowFloatTranslate(false) {
+TeSpriteLayout::TeSpriteLayout() : _tiledSurfacePtr(new TeTiledSurface()), _sizeSet(false) {
 	_tiledSurfacePtr->setColor(TeColor(255, 255, 255, 255));
 	updateMesh();
 }
@@ -50,7 +50,7 @@ void TeSpriteLayout::draw() {
 		  _tiledSurfacePtr->size().x(), _tiledSurfacePtr->size().y(), color().dump().c_str());*/
 	TeMatrix4x4 matrix = worldTransformationMatrix();
 
-	if (!_allowFloatTranslate) {
+	if (sizeType() == ABSOLUTE) {
 		matrix(0, 3) = (int)matrix(0, 3);
 		matrix(1, 3) = (int)matrix(1, 3);
 	}
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index 45376231122..d03f3b4034a 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -30,8 +30,7 @@ namespace Tetraedge {
 
 TeTextBase2::TeTextBase2() : _drawRect(0, 0), _size(0, 0),
 _alignStyle(TeFont3::AlignLeft), _interLine(0.0f), _globalColor(0xff, 0xff, 0xff, 0xff),
-_wrapMode(WrapModeFixed), _strikethrough(false), _fontSize(10), _valueWasSet(true)
-{
+_wrapMode(WrapModeFixed), _strikethrough(false), _fontSize(10), _valueWasSet(true) {
 	_mesh.setglTexEnv(GL_BLEND);
 	_mesh.setShouldDraw(true);
 }
@@ -94,25 +93,24 @@ void TeTextBase2::build() {
 	_mesh.setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
 	_mesh.defaultMaterial(texture);
 
-	TeColor col(255, 255, 255, 255);
 	_mesh.setShouldDraw(true);
-	_mesh.setColor(col);
+	_mesh.setColor(_globalColor);
 	_mesh.setVertex(0, TeVector3f32(_size._x * -0.5f, _size._y * -0.5f, 0.0f));
 	_mesh.setTextureUV(0, TeVector2f32(0, 1));
 	_mesh.setNormal(0, TeVector3f32(0.0f, 0.0f, 1.0f));
-	_mesh.setColor(0, col);
+	_mesh.setColor(0, _globalColor);
 	_mesh.setVertex(1, TeVector3f32(_size._x * 0.5f, _size._y * -0.5f, 0.0f));
 	_mesh.setTextureUV(1, TeVector2f32(1, 1));
 	_mesh.setNormal(1, TeVector3f32(0.0f, 0.0f, 1.0f));
-	_mesh.setColor(1, col);
+	_mesh.setColor(1, _globalColor);
 	_mesh.setVertex(2, TeVector3f32(_size._x * 0.5f, _size._y * 0.5f, 0.0f));
 	_mesh.setTextureUV(2, TeVector2f32(1, 0));
 	_mesh.setNormal(2, TeVector3f32(0.0f, 0.0f, 1.0f));
-	_mesh.setColor(2, col);
+	_mesh.setColor(2, _globalColor);
 	_mesh.setVertex(3, TeVector3f32(_size._x * -0.5f, _size._y * 0.5f, 0.0f));
 	_mesh.setTextureUV(3, TeVector2f32(0, 0));
 	_mesh.setNormal(3, TeVector3f32(0.0f, 0.0f, 1.0f));
-	_mesh.setColor(3, col);
+	_mesh.setColor(3, _globalColor);
 	_mesh.setIndex(0, 0);
 	_mesh.setIndex(1, 1);
 	_mesh.setIndex(2, 3);
@@ -248,7 +246,7 @@ void TeTextBase2::setFont(unsigned int offset, const TeIntrusivePtr<TeFont3> &ne
 
 void TeTextBase2::setFontSize(unsigned long newSize) {
 	// Bit of a hack here to get the right font size.
-	newSize *= 1.5;
+	newSize *= 1.2;
 	if (_fontSize != newSize) {
 		_fontSize = newSize;
 		_valueWasSet = true;
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index c299df4e293..fc087c10ccf 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -32,8 +32,8 @@ static void getRangeIntersection(float start1,float end1,float start2,float end2
 	*pend = MIN(end1, end2);
 }
 
-TeTiledSurface::TeTiledSurface() : _colorKeyActive(false), _colorKeyTolerence(0),
-_bottomCrop(0), _topCrop(0), _leftCrop(0), _rightCrop(0), _codec(nullptr), _imgFormat(TeImage::INVALID), _shouldDraw(true) {
+TeTiledSurface::TeTiledSurface() : _shouldDraw(true), _codec(nullptr), _colorKeyActive(false), _colorKeyTolerence(0),
+_bottomCrop(0), _topCrop(0), _leftCrop(0), _rightCrop(0), _imgFormat(TeImage::INVALID) {
 	_frameAnim.frameChangedSignal().add<TeTiledSurface>(this, &TeTiledSurface::onFrameAnimCurrentFrameChanged);
 }
 


Commit: c27dca6b829268fdeb2c83a9d936343ab2ec8015
    https://github.com/scummvm/scummvm/commit/c27dca6b829268fdeb2c83a9d936343ab2ec8015
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More WIP. Lots of progress

Many pieces of the first room of the game are now working.

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/billboard.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/dialog2.h
    engines/tetraedge/game/documents_browser.cpp
    engines/tetraedge/game/documents_browser.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/question2.h
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture.h
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_checkbox_layout.cpp
    engines/tetraedge/te/te_curve_anim2.h
    engines/tetraedge/te/te_font3.cpp
    engines/tetraedge/te/te_font3.h
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_image.h
    engines/tetraedge/te/te_layout.cpp
    engines/tetraedge/te/te_layout.h
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_lua_thread.cpp
    engines/tetraedge/te/te_model_vertex_animation.cpp
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer.h
    engines/tetraedge/te/te_scrolling_layout.cpp
    engines/tetraedge/te/te_scrolling_layout.h
    engines/tetraedge/te/te_sprite_layout.cpp
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_tiled_texture.cpp
    engines/tetraedge/te/te_vector3f32.h
    engines/tetraedge/te/te_xml_gui.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 37af6a70bc2..f9d0573a2fb 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -37,7 +37,8 @@
 #include "tetraedge/te/te_renderer.h"
 #include "tetraedge/te/te_font3.h"
 #include "tetraedge/te/te_input_mgr.h"
-//#include "tetraedge/te/te_textbase2.h"
+
+//#define DUMP_LAYOUTS 1
 
 namespace Tetraedge {
 
@@ -82,7 +83,7 @@ void Application::create() {
 	_mainWindowCamera->_orthNearVal = -2048.0f;
 	_mainWindowCamera->_orthFarVal = 2048.0f;
 
-	_mainWindow.setSize(TeVector3f32(winWidth, winHeight, 100.0));
+	_mainWindow.setSize(TeVector3f32(winWidth, winHeight, 0.0));
 	_mainWindow.setSizeType(TeILayout::ABSOLUTE);
 	_mainWindow.setPositionType(TeILayout::ABSOLUTE);
 
@@ -162,7 +163,7 @@ void Application::create() {
 
 	_helpGui.load(helpMenuFilePath);
 
-	debug("TODO: set TeCore flags here? Do they do anything?");
+	// TODO: set TeCore field 0x74 and 0x78 to true here? Do they do anything?");
 
 	// Game calls these here but does nothing with result?
 	//TeGetDeviceDPI();
@@ -385,6 +386,22 @@ void Application::drawFront() {
 	g_engine->getRenderer()->loadIdentityMatrix();
 }
 
+#if DUMP_LAYOUTS
+static int renderCount = 0;
+static void dumpLayout(Te3DObject2 *layout, Common::String indent = "++") {
+	assert(layout);
+	if (!layout->worldVisible())
+		return;
+	debug("%s %s  pos:%s  worldPos:%s  worldScale:%s size:%s col:%s", indent.c_str(), layout->name().c_str(),
+			layout->position().dump().c_str(), layout->worldPosition().dump().c_str(),
+			layout->worldScale().dump().c_str(), layout->size().dump().c_str(),
+			layout->color().dump().c_str());
+	for (auto & child: layout->childList()) {
+		dumpLayout(child, indent + "++");
+	}
+}
+#endif
+
 void Application::performRender() {
 	Game *game = g_engine->getGame();
 	TeRenderer *renderer = g_engine->getRenderer();
@@ -422,6 +439,17 @@ void Application::performRender() {
 	renderer->renderTransparentMeshes();
 	game->scene().drawPath();
 	g_system->updateScreen();
+
+#if DUMP_LAYOUTS
+	renderCount++;
+	if (renderCount % 100 == 0) {
+		debug("\n--------------------\nFrame %d back layout: ", renderCount);
+		dumpLayout(&_backLayout);
+		debug("\n--------------------\nFrame %d front orientation layout: ", renderCount);
+		dumpLayout(&_frontOrientationLayout);
+	}
+#endif
+
 }
 
 //void Application::preloadTextrue(); does nothing..
diff --git a/engines/tetraedge/game/billboard.cpp b/engines/tetraedge/game/billboard.cpp
index 3e95616de21..2d2d99156a1 100644
--- a/engines/tetraedge/game/billboard.cpp
+++ b/engines/tetraedge/game/billboard.cpp
@@ -53,17 +53,18 @@ void Billboard::calcVertex() {
 	const TeMatrix4x4 camProjMatrix = currentCam->projectionMatrix();
 	TeMatrix4x4 camWorldInverse = currentCam->worldTransformationMatrix();
 	camWorldInverse.inverse();
-	
+
 	const TeMatrix4x4 camTotalTransform = camProjMatrix * camWorldInverse;
 	TeMatrix4x4 camTotalInverse = camTotalTransform;
 	camTotalInverse.inverse();
+	camTotalInverse.scale(TeVector3f32(-1.0, -1.0, 1.0));
 
 	TeVector3f32 posvec(0.0f, 0.0f, _pos.z());
 	if (_hasPos2) {
 		posvec = _pos2;
 	}
 	posvec = camTotalTransform * posvec;
-	
+
 	TeVector3f32 meshVertex;
 	float fx, fy;
 
@@ -81,7 +82,7 @@ void Billboard::calcVertex() {
 	fy = _pos.y();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
 	_model->_meshes[0].setVertex(2, meshVertex);
-	
+
 	fx = _pos.x() + _size.getX();
 	fy = _pos.y() + _size.getY();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 98299ae6323..b3d84b95cc9 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -91,12 +91,15 @@ Character::~Character() {
 	}
 }
 
-void Character::addCallback(const Common::String &key, const Common::String &s2, float f1, float f2) {
+void Character::addCallback(const Common::String &animKey, const Common::String &fnName, float triggerFrame, float maxCalls) {
 	Callback *c = new Callback();
-	c->_s = s2;
-	c->_x = (int)f1;
-	c->_y = (int)f2;
-	c->_f = (f2 == -1.0 ? -NAN : 0.0f);
+	c->_luaFn = fnName;
+	c->_lastCheckFrame = 0;
+	c->_triggerFrame = (int)triggerFrame;
+	c->_maxCalls = (int)maxCalls;
+	// Slight difference here to orig (that sets -NAN) because of
+	// the way this gets used later, setting large negative is more correct.
+	c->_callsMade = (maxCalls == -1.0 ? -1e9 : 0.0f);
 
 	const Common::String animPath = _model->anim()->_loadedPath.toString();
 	if (_callbacks.contains(animPath)) {
@@ -104,7 +107,7 @@ void Character::addCallback(const Common::String &key, const Common::String &s2,
 	} else {
 		Common::Array<Callback *> callbacks;
 		callbacks.push_back(c);
-		_callbacks.setVal(key, callbacks);
+		_callbacks.setVal(animKey, callbacks);
 	}
 }
 
@@ -227,8 +230,31 @@ void Character::deleteAnim() {
 	_curModelAnim.release();
 }
 
-void Character::deleteCallback(const Common::String &str1, const Common::String &str2, float f) {
-	error("TODO: Implement Character::deleteCallback");
+void Character::deleteCallback(const Common::String &key, const Common::String &fnName, float f) {
+	_callbacksChanged = true;
+	assert(_model->anim());
+	Common::String animPath = _model->anim()->_loadedPath.toString();
+	if (!_callbacks.contains(animPath))
+		return;
+
+	Common::Array<Callback *> &cbs = _callbacks.getVal(animPath);
+	for (unsigned int i = 0; i < cbs.size(); i++) {
+		if (fnName.empty()) {
+			delete cbs[i];
+			// don't remove from array, clear at the end.
+		} else if (cbs[i]->_luaFn == fnName) {
+			if (f == -1 || cbs[i]->_triggerFrame == f) {
+				delete cbs[i];
+				cbs.remove_at(i);
+				i--;
+			}
+		}
+	}
+	if (fnName.empty())
+		cbs.clear();
+
+	if (cbs.empty())
+		_callbacks.erase(animPath);
 }
 
 //static bool deserialize(TiXmlElement *param_1, Walk *param_2);
@@ -480,7 +506,7 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 }
 
 bool Character::onModelAnimationFinished() {
-	const Common::Path &loadedPath = _model->anim()->_loadedPath;
+	const Common::Path loadedPath = _model->anim()->_loadedPath;
 	const Common::String animfile = loadedPath.getLastComponent().toString();
 
 	// TODO: Do something with _unrecalAnims here.
@@ -544,7 +570,42 @@ bool Character::onModelAnimationFinished() {
 }
 
 void Character::permanentUpdate() {
-	error("TODO: Implement Character::permanentUpdate.");
+	assert(_model->anim());
+	const Common::String animPath = _model->anim()->_loadedPath.toString();
+	int curFrame = _model->anim()->curFrame2();
+	Game *game = g_engine->getGame();
+	_callbacksChanged = false;
+	if (_callbacks.contains(animPath)) {
+		Common::Array<Callback *> &cbs = _callbacks.getVal(animPath);
+		for (Callback *cb : cbs) {
+			if (cb->_triggerFrame > cb->_lastCheckFrame && curFrame >= cb->_triggerFrame){
+				int callsMade = cb->_callsMade;
+				cb->_callsMade++;
+				if (callsMade >= cb->_maxCalls)
+					continue;
+				cb->_lastCheckFrame = curFrame;
+				game->luaScript().execute(cb->_luaFn);
+				if (_callbacksChanged)
+					break;
+			}
+			cb->_lastCheckFrame = curFrame;
+		}
+	}
+
+	if (animPath.contains("ka_esc_h")) {
+		if (_lastAnimFrame < 7 && _model->anim()->curFrame2() > 6) {
+			game->playSound("sounds/SFX/PAS_F_PAVE1.ogg", 1, 1.0f);
+		} else if (_lastAnimFrame < 22 && _model->anim()->curFrame2() > 21) {
+			game->playSound("sounds/SFX/PAS_F_PAVE2.ogg", 1, 1.0f);
+		}
+	} else if (animPath.contains("ka_esc_b")) {
+		if (_lastAnimFrame < 12 && _model->anim()->curFrame2() > 11) {
+			game->playSound("sounds/SFX/PAS_F_PAVE1.ogg", 1, 1.0f);
+		} else if (_lastAnimFrame < 27 && _model->anim()->curFrame2() > 26) {
+			game->playSound("sounds/SFX/PAS_F_PAVE2.ogg", 1, 1.0f);
+		}
+	}
+	updateAnimFrame();
 }
 
 void Character::placeOnCurve(TeIntrusivePtr<TeBezierCurve> &curve) {
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index b8111e0cfdb..b524c8c8691 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -86,10 +86,11 @@ public:
 	};
 
 	struct Callback {
-		int _x;
-		Common::String _s;
-		int _y;
-		float _f;
+		Common::String _luaFn;
+		int _triggerFrame;
+		int _lastCheckFrame;
+		int _maxCalls;
+		float _callsMade;
 	};
 
 	void addCallback(const Common::String &s1, const Common::String &s2, float f1, float f2);
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 5437c206270..d583e0f3400 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -96,13 +96,14 @@ void Dialog2::load() {
 	setSizeType(RELATIVE_TO_PARENT);
 	TeVector3f32 usersz = userSize();
 	setSize(TeVector3f32(1.0f, 1.0f, usersz.z()));
-	size(); // refresh size.. seems to do nothing with result?
+	size(); // refresh size? seems to do nothing with result
 	_music.repeat(false);
 	_gui.load("menus/dialog.lua");
 	TeButtonLayout *dialogLockBtn = _gui.buttonLayoutChecked("dialogLockButton");
 
 	dialogLockBtn->setVisible(false);
 	addChild(dialogLockBtn);
+	size(); // refresh size? seems to do nothing with result again.
 
 	TeButtonLayout *dialogBtn = _gui.buttonLayoutChecked("dialog");
 	dialogBtn->onMouseClickValidated().add(this, &Dialog2::onSkipButton);
diff --git a/engines/tetraedge/game/dialog2.h b/engines/tetraedge/game/dialog2.h
index 5c21231e2b7..a1f98b3772a 100644
--- a/engines/tetraedge/game/dialog2.h
+++ b/engines/tetraedge/game/dialog2.h
@@ -59,6 +59,7 @@ public:
 	void unload();
 
 	TeLuaGUI &gui() { return _gui; }
+	TeSignal1Param<const Common::String &> &onAnimationDownFinishedSignal() { return _onAnimationDownFinishedSignal; }
 
 private:
 	Common::Array<DialogData> _dialogs;
@@ -67,7 +68,7 @@ private:
 
 	TeLuaGUI _gui;
 	TeMusic _music;
-	
+
 	DialogData _currentDialogData;
 
 	TeSignal1Param<const Common::String &> _onAnimationDownFinishedSignal;
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
index 1e37a00fad6..57c288a7ea3 100644
--- a/engines/tetraedge/game/documents_browser.cpp
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -21,9 +21,16 @@
 
 #include "tetraedge/game/documents_browser.h"
 
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/application.h"
+#include "tetraedge/game/game.h"
+#include "tetraedge/te/te_core.h"
+#include "tetraedge/te/te_lua_thread.h"
+#include "tetraedge/te/te_scrolling_layout.h"
+
 namespace Tetraedge {
 
-DocumentsBrowser::DocumentsBrowser() {
+DocumentsBrowser::DocumentsBrowser() : _startPage(0), _curPage(0), _zoomCount(0) {
 	_timer.alarmSignal().add(this, &DocumentsBrowser::onQuitDocumentDoubleClickTimer);
 }
 
@@ -33,7 +40,35 @@ void DocumentsBrowser::enter() {
 }
 
 void DocumentsBrowser::hideDocument() {
-	error("TODO: Implement DocumentsBrowser::hideDocument");
+	Common::String docName = _curDocName;
+	_curDocName.clear();
+	TeSpriteLayout *zoomedSprite = _gui1.spriteLayout("zoomedSprite");
+	if (!zoomedSprite)
+		return;
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	zoomedSprite->unload();
+	_gui1.buttonLayoutChecked("zoomed")->setVisible(false);
+	_gui2.unload();
+	Game *game = g_engine->getGame();
+
+	bool callFn = true;
+	Common::Array<Game::YieldedCallback> &yieldedcallbacks = game->yieldedCallbacks();
+	for (unsigned int i = 0; i < yieldedcallbacks.size(); i++) {
+		if (yieldedcallbacks[i]._luaFnName == "OnDocumentClosed" &&
+			yieldedcallbacks[i]._luaParam == docName) {
+			yieldedcallbacks.remove_at(i);
+			if (yieldedcallbacks[i]._luaThread) {
+				yieldedcallbacks[i]._luaThread->resume();
+				callFn = false;
+			}
+			break;
+		}
+	}
+	if (callFn)
+		game->luaScript().execute("OnDocumentClosed", docName);
+
+	app->fade();
 }
 
 void DocumentsBrowser::leave() {
@@ -49,19 +84,18 @@ void DocumentsBrowser::load() {
 	const TeVector3f32 userSz = TeLayout::userSize();
 	setSize(TeVector3f32(1.0f, 1.0f, userSz.z()));
 
-	TeLuaGUI::load("DocumentsBrowser/DocumentsBrowser.lua");
+	_gui1.load("DocumentsBrowser/DocumentsBrowser.lua");
 
-	TeLayout *docBrowser = TeLuaGUI::layout("documentBrowser");
+	TeLayout *docBrowser = _gui1.layout("documentBrowser");
 	if (docBrowser)
 		addChild(docBrowser);
 
-	TeButtonLayout *button = buttonLayout("previousPage");
+	TeButtonLayout *button = _gui1.buttonLayout("previousPage");
 	button->onMouseClickValidated().add(this, &DocumentsBrowser::onPreviousPage);
-	button = buttonLayout("nextPage");
+	button = _gui1.buttonLayout("nextPage");
 	button->onMouseClickValidated().add(this, &DocumentsBrowser::onNextPage);
-	button = TeLuaGUI::buttonLayout("zoomed");
+	button = _gui1.buttonLayout("zoomed");
 	button->onMouseClickValidated().add(this, &DocumentsBrowser::onZoomedButton);
-	button = TeLuaGUI::buttonLayout("zoomed");
 	button->setVisible(false);
 
 	// Game tries to load a file that doesn't exist..
@@ -73,28 +107,39 @@ void DocumentsBrowser::loadZoomed() {
 	_zoomedLayout.setSizeType(RELATIVE_TO_PARENT);
 	TeVector3f32 usersz = userSize();
 	_zoomedLayout.setSize(TeVector3f32(1.0f, 1.0f, usersz.z()));
-	TeLayout *zoomedChild = layout("zoomed");
+	TeLayout *zoomedChild = _gui1.layout("zoomed");
 	_zoomedLayout.addChild(zoomedChild);
 }
 
-void DocumentsBrowser::currentPage(long page) {
-	const Common::String pageName = Common::String::format("page%ld", page);
-	TeLayout *pageLayout = layout(pageName);
+void DocumentsBrowser::currentPage(long setPage) {
+	const Common::String setPageName = Common::String::format("page%ld", setPage);
+	TeLayout *pageLayout = _gui1.layout(setPageName);
 	if (!pageLayout)
 		return;
 
-	_curPage = page;
-
-	error("TODO: Implement DocumentsBrowser::currentPage");
+	_curPage = setPage;
+
+	int pageNo = 0;
+	while (true) {
+		const Common::String pageName = Common::String::format("page%d", pageNo);
+		pageLayout = _gui1.layout(pageName);
+		if (!pageLayout)
+			break;
+		pageLayout->setVisible(pageNo == setPage);
+		const Common::String diodeName = Common::String::format("diode%d", pageNo);
+		_gui1.buttonLayoutChecked(diodeName)->setEnable(pageNo == setPage);
+		pageNo++;
+	}
 }
 
 bool DocumentsBrowser::onQuitDocumentDoubleClickTimer() {
 	long time = _timer.getTimeFromStart();
 	_timer.stop();
-	if (time >= 200000)
-		error("TODO: Implement DocumentsBrowser::onQuitDocumentDoubleClickTimer");
-	else
+	if (time >= 200000) {
+		showDocument(_curDocName, _startPage + 1);
+	} else {
 		hideDocument();
+	}
 	return false;
 }
 
@@ -109,11 +154,61 @@ bool DocumentsBrowser::onPreviousPage() {
 }
 
 bool DocumentsBrowser::onZoomedButton() {
-	error("TODO: Implement DocumentsBrowser::onZoomedButton");
+	int count = _zoomCount;
+	_zoomCount++;
+	if (count == 0) {
+		_timer.start();
+		_timer.setAlarmIn(200000);
+	} else {
+		onQuitDocumentDoubleClickTimer();
+	}
+	return false;
 }
 
-void DocumentsBrowser::showDocument(const Common::String &str, long n) {
-	error("TODO: Implement DocumentsBrowser::showDocument");
+void DocumentsBrowser::showDocument(const Common::String &docName, long startPage) {
+	_curPage = startPage;
+	_startPage = startPage;
+	_curDocName = docName;
+	_gui2.unload();
+	TeCore *core = g_engine->getCore();
+	const Common::Path docPathBase(Common::String::format("DocumentsBrowser/Documents/Documents/%s_zoomed_%d", docName.c_str(), (int)startPage));
+	Common::Path docPath = docPathBase.append(".png");
+	docPath = core->findFile(docPath);
+	if (!Common::File::exists(docPath)) {
+		docPath = docPathBase.append(".jpg");
+		docPath = core->findFile(docPath);
+		if (!Common::File::exists(docPath)) {
+			// Probably the end of the doc
+			if (startPage == 0)
+				warning("Can't find first page of doc named %s", docName.c_str());
+			hideDocument();
+			return;
+		}
+	}
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	TeSpriteLayout *sprite = _gui1.spriteLayoutChecked("zoomedSprite");
+	//sprite->setSizeType(ABSOLUTE);
+	sprite->load(docPath);
+	TeVector2s32 spriteSize = sprite->_tiledSurfacePtr->_tiledTexture->_totalSize;
+	sprite->setSizeType(RELATIVE_TO_PARENT);
+	TeVector3f32 winSize = app->getMainWindow().size();
+	sprite->setSize(TeVector3f32(1.0, (4.0 / (winSize.y() / winSize.x() * 4.0)) *
+               ((float)spriteSize._y / (float)spriteSize._x), 0.0));
+	TeScrollingLayout *scroll = _gui1.scrollingLayout("scroll");
+	if (!scroll)
+		error("DocumentsBrowser::showDocument Couldn't fetch scroll object");
+	scroll->resetScrollPosition();
+	scroll->playAutoScroll();
+	Common::Path luaPath = docPathBase.append(".lua");
+	luaPath = core->findFile(luaPath);
+	if (Common::File::exists(luaPath)) {
+		_gui2.load(luaPath);
+		error("Finish DocumentsBrowser::showDocument");
+	}
+	_gui1.layoutChecked("zoomed")->setVisible(true);
+	_zoomCount = 0;
+	app->fade();
 }
 
 void DocumentsBrowser::unload() {
diff --git a/engines/tetraedge/game/documents_browser.h b/engines/tetraedge/game/documents_browser.h
index f1d4818cb2d..c2a42943507 100644
--- a/engines/tetraedge/game/documents_browser.h
+++ b/engines/tetraedge/game/documents_browser.h
@@ -28,7 +28,7 @@
 
 namespace Tetraedge {
 
-class DocumentsBrowser : public TeLuaGUI, public TeLayout {
+class DocumentsBrowser : public TeLayout {
 public:
 	DocumentsBrowser();
 
@@ -79,15 +79,23 @@ public:
 	bool onShowedDocumentButton18();
 	bool onShowedDocumentButton19();
 
-	void showDocument(const Common::String &str, long n);
+	void showDocument(const Common::String &str, long startPage);
 	void unload();
 
 	TeLayout &zoomedLayout() { return _zoomedLayout; }
 
+	TeLuaGUI &gui1() { return _gui1; }
+
 private:
 	TeTimer _timer;
 	TeLayout _zoomedLayout;
 	unsigned long _curPage;
+	unsigned long _startPage;
+	int _zoomCount;
+	Common::String _curDocName;
+
+	TeLuaGUI _gui1;
+	TeLuaGUI _gui2;
 	// TiXmlDocument _xmldoc;
 };
 
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 407a7730b5b..d9bf03e1ccc 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -56,6 +56,8 @@ _firstInventory(true), _loadName("save.xml"), _randomSource("SyberiaGameRandom")
 		_objectsTakenBits[i] = false;
 	}
 	_randomSound = new RandomSound();
+	_dialog2.onAnimationDownFinishedSignal().add(this, &Game::onDialogFinished);
+	_question2.onAnswerSignal().add(this, &Game::onAnswered);
 }
 
 Game::~Game() {
@@ -81,13 +83,13 @@ bool Game::addAnimToSet(const Common::String &anim) {
 		const Common::String layoutName = parts[1];
 		const Common::String path = Common::String("scenes/") + parts[0] + "/" + parts[1] + "/Set" + parts[1];
 
-		_gui2.load(path + ".lua");
+		_setAnimGui.load(path + ".lua");
 
 		// Note: game makes this here, but never uses it..
 		// it seems like a random memory leak??
 		// TeSpriteLayout *spritelayout = new TeSpriteLayout();
 
-		TeSpriteLayout *spritelayout = findSpriteLayoutByName(_gui2.layoutChecked("root"), layoutName);
+		TeSpriteLayout *spritelayout = findSpriteLayoutByName(_setAnimGui.layoutChecked("root"), layoutName);
 
 		_scene.bgGui().layoutChecked("root")->addChild(spritelayout);
 		return true;
@@ -237,7 +239,7 @@ bool Game::changeWarp2(const Common::String &zone, const Common::String &scene,
 		_luaScript.unload();
 	}
 
-	_gui3.unload();
+	_forGui.unload();
 	_prevSceneName = _currentScene;
 	if (fadeFlag)
 		g_engine->getApplication()->fade();
@@ -260,8 +262,8 @@ void Game::deleteNoScale() {
 
 void Game::draw() {
 	if (_running) {
-	  _frameCounter++;
-	  _scene.draw();
+		_frameCounter++;
+		_scene.draw();
 	}
 }
 
@@ -493,8 +495,8 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_luaScript.load(logicLuaPath);
 	}
 
-	if (_gui3.loaded())
-		_gui3.unload();
+	if (_forGui.loaded())
+		_forGui.unload();
 
 	_scene.reset();
 	_scene.bgGui().unload();
@@ -507,8 +509,8 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	Application *app = g_engine->getApplication();
 	if (forLuaExists) {
-		_gui3.load(forLuaPath);
-		TeLayout *bg = _gui3.layout("background");
+		_forGui.load(forLuaPath);
+		TeLayout *bg = _forGui.layout("background");
 		bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
 		app->_frontLayout.addChild(bg);
 		TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayout("background");
@@ -595,7 +597,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	app->_backLayout.addChild(_scene.background());
 
 	if (markerLuaExists) {
-		TeLayout *bg = _gui2.layout("background");
+		TeLayout *bg = _scene.markerGui().layout("background");
 		app->_frontLayout.addChild(bg);
 	}
 
@@ -641,7 +643,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 }
 
 bool Game::isDocumentOpened() {
-	return _documentsBrowser.layoutChecked("zoomed")->visible();
+	return _documentsBrowser.gui1().layoutChecked("zoomed")->visible();
 }
 
 bool Game::isMoviePlaying() {
@@ -674,7 +676,7 @@ bool Game::launchDialog(const Common::String &dname, uint param_2, const Common:
 	}
 
 	const Common::String sndfile = dname + ".ogg";
-	_dialog2.pushDialog(*locdname, *locdname, sndfile, charname, animfile, param_5);
+	_dialog2.pushDialog(dname, *locdname, sndfile, charname, animfile, param_5);
 	return true;
 }
 
@@ -695,13 +697,14 @@ void Game::leave(bool flag) {
 	_inventoryMenu.unload();
 	_gui1.unload();
 	_scene.close();
-	_gui3.unload();
+	_forGui.unload();
 	if (_scene._character) {
 		_scene._character->deleteAllCallback();
 		_scene._character->stop();
 		_scene.unloadCharacter(_scene._character->_model->name());
 	}
-	warning("TODO: Game::leave: clear game sounds");
+
+	warning("TODO: Game::leave: clear game sounds and randomsounds");
 
 	for (auto *hitobj : _gameHitObjects) {
 		delete hitobj;
@@ -718,6 +721,7 @@ void Game::leave(bool flag) {
 	_inGameGui.buttonLayoutChecked("inventoryButton")->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
 	_inGameGui.unload();
 	_playedTimer.stop();
+	_enteredFlag2 = false;
 
 	Application *app = g_engine->getApplication();
 	app->_lockCursorButton.setVisible(false);
@@ -808,8 +812,9 @@ bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
 
 	Character *character = scene()._character;
 	assert(character);
-
-	const Common::String &curAnimName = character->curAnimName();
+	// Note: the above callbacks can change the anim,
+	// so we have to fetch this *after* them.
+	const Common::String curAnimName = character->curAnimName();
 	if (_currentScene == _someSceneName) {
 		if (curAnimName == character->walkAnim(Character::WalkPart_Start)
 			|| curAnimName == character->walkAnim(Character::WalkPart_Loop)
@@ -825,8 +830,10 @@ bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
 			|| curAnimName == character->walkAnim(Character::WalkPart_EndG)) {
 			character->updatePosition(1.0);
 			character->endMove();
-			// Note: original checks walkAnim again.. is there a reason to do that?
-			character->setAnimation(character->characterSettings()._idleAnimFileName, true);
+			// endMove can result in callbacks that change the animation. check again.
+			if (character->curAnimName() == character->walkAnim(Character::WalkPart_EndD)
+				|| character->curAnimName() == character->walkAnim(Character::WalkPart_EndG))
+				character->setAnimation(character->characterSettings()._idleAnimFileName, true);
 		}
 	}
 
@@ -1352,7 +1359,7 @@ void Game::setCurrentObjectSprite(const Common::String &spritePath) {
 }
 
 bool Game::showMarkers(bool val) {
-	TeLayout *bg = _gui3.layoutChecked("background");
+	TeLayout *bg = _forGui.layoutChecked("background");
 	for (unsigned int i = 0; i < bg->childCount(); i++) {
 		const InGameScene::TeMarker *marker = _scene.findMarker(bg->child(i)->name());
 		if (marker)
@@ -1446,7 +1453,7 @@ void Game::update() {
 		if (player) {
 			TeIntrusivePtr<TeModel> model = player->_model;
 			bool modelVisible = model->visible();
-			if (!model->anim().get())
+			if (model->anim())
 				player->permanentUpdate();
 			if (modelVisible) {
 				if (player->needsSomeUpdate()) {
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index 610c7e2c87d..3481d25bf44 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -75,8 +75,9 @@ public:
 	struct YieldedCallback {
 		TeLuaThread *_luaThread;
 		Common::String _luaParam;
+		Common::String _luaParam2;
 		Common::String _luaFnName;
-		// Note: original game has more String, long, and int fields.. seem unused.
+		// Note: original game long, and int fields.. unused?
 	};
 
 	//enum EGameScoreID {}; // Not needed?
@@ -183,7 +184,7 @@ public:
 	InGameScene &scene() { return _scene; }
 	Dialog2 &dialog2() { return _dialog2; }
 	Question2 &question2() { return _question2; }
-	TeLuaGUI &gui3() { return _gui3; }
+	TeLuaGUI &forGui() { return _forGui; }
 	Objectif &objectif() { return _objectif; }
 	Common::Array<YieldedCallback> &yieldedCallbacks() { return _yieldedCallbacks; }
 	void setSaveRequested() { _saveRequested = true; }
@@ -199,9 +200,9 @@ private:
 	bool _entered;
 	bool _enteredFlag2;
 
-	TeLuaGUI _gui1; // TODO: get better names for these.
-	TeLuaGUI _gui2;
-	TeLuaGUI _gui3;
+	TeLuaGUI _gui1; // TODO: Is this ever used?
+	TeLuaGUI _setAnimGui;
+	TeLuaGUI _forGui;
 	TeLuaGUI _inGameGui;
 
 	Inventory _inventory;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 7aabee33cfe..f61c87d348c 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -42,7 +42,7 @@
 #include "tetraedge/te/te_lua_script.h"
 #include "tetraedge/te/te_lua_thread.h"
 
-#define DEBUG_PATHFINDING 1
+//#define DEBUG_PATHFINDING 1
 
 namespace Tetraedge {
 
@@ -124,7 +124,7 @@ bool InGameScene::addMarker(const Common::String &markerName, const Common::Stri
 		newMarker._name = markerName;
 		newMarker._val = markerVal;
 		_markers.push_back(newMarker);
-		TeLayout *bg = game->gui3().layout("background");
+		TeLayout *bg = game->forGui().layout("background");
 		if (bg)
 			bg->addChild(markerSprite);
 		_sprites.push_back(markerSprite);
@@ -259,7 +259,7 @@ void InGameScene::deleteMarker(const Common::String &markerName) {
 	}
 
 	Game *game = g_engine->getGame();
-	TeLayout *bg = game->gui3().layout("background");
+	TeLayout *bg = game->forGui().layout("background");
 	if (!bg)
 		return;
 	for (Te3DObject2 *child : bg->childList()) {
@@ -480,7 +480,7 @@ Common::String InGameScene::imagePathMarker(const Common::String &name) {
 	if (!isMarker(name))
 		return Common::String();
 	Game *game = g_engine->getGame();
-	TeLayout *bg = game->gui3().layoutChecked("background");
+	TeLayout *bg = game->forGui().layoutChecked("background");
 	for (Te3DObject2 *child : bg->childList()) {
 		TeSpriteLayout *spritelayout = dynamic_cast<TeSpriteLayout *>(child);
 		if (spritelayout && spritelayout->name() == name) {
@@ -907,7 +907,7 @@ void InGameScene::setImagePathMarker(const Common::String &markerName, const Com
 		return;
 
 	Game *game = g_engine->getGame();
-	TeLayout *bg = game->gui3().layoutChecked("background");
+	TeLayout *bg = game->forGui().layoutChecked("background");
 
 	for (Te3DObject2 *child : bg->childList()) {
 		if (child->name() == markerName) {
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index dc48f38fb07..2fcb923c5af 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -88,8 +88,9 @@ void Inventory::load() {
 	btn->setVisible(true);
 	btn->onMouseClickValidated().add(this, &Inventory::onQuitButton);
 
+	// FIXME: This is capturing mouse clicks.. should be set visible per original.
 	btn = _gui.buttonLayoutChecked("quitBackground");
-	btn->setVisible(true);
+	btn->setVisible(false);
 	btn->onMouseClickValidated().add(this, &Inventory::onQuitButton);
 
 	btn = _gui.buttonLayoutChecked("mainMenuButton");
@@ -207,6 +208,7 @@ bool Inventory::addObject(InventoryObject *obj) {
 						c--;
 					}
 				}
+				slotno++;
 			}
 			pageno++;
 		}
@@ -234,7 +236,7 @@ bool Inventory::addObject(InventoryObject *obj) {
 				finished = true;
 				break;
 			}
-			
+
 			TeTextLayout *newText = new TeTextLayout();
 			newText->setSizeType(CoordinatesType::RELATIVE_TO_PARENT);
 			newText->setPosition(TeVector3f32(1.0,1.0,0.0));
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index e8c74ffb3dd..514fd08bb0b 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -181,6 +181,21 @@ static int tolua_ExportedFunctions_AddNumber00(lua_State *L) {
 	error("#ferror in function 'AddNumber': %d %d %s", err.index, err.array, err.type);
 }
 
+static void ShowDocument(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	game->documentsBrowser().showDocument(name, 0);
+}
+
+static int tolua_ExportedFunctions_ShowDocument00(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));
+		ShowDocument(s1);
+		return 0;
+	}
+	error("#ferror in function 'ShowDocument': %d %d %s", err.index, err.array, err.type);
+}
+
 static void AddUnrecalAnim(const Common::String &newanim) {
 	Application *app = g_engine->getApplication();
 	Common::Array<Common::String> &anims = app->unrecalAnims();
@@ -266,11 +281,11 @@ static int tolua_ExportedFunctions_MoveCharacterPlayerDisabled00(lua_State *L) {
 	error("#ferror in function 'MoveCharacterPlayerDisabled': %d %d %s", err.index, err.array, err.type);
 }
 
-static void AddCallback(const Common::String &charName, const Common::String &key, const Common::String &s1, float f1, float f2) {
+static void AddCallback(const Common::String &charName, const Common::String &animName, const Common::String &fnName, float triggerFrame, float maxCalls) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charName);
 	if (c) {
-		c->addCallback(key, s1, f1, f2);
+		c->addCallback(animName, fnName, triggerFrame, maxCalls);
 	} else {
 		warning("[AddCallback] Character's \"%s\" doesn't exist", charName.c_str());
 	}
@@ -283,9 +298,9 @@ static int tolua_ExportedFunctions_AddCallback00(lua_State *L) {
 			&& tolua_isnumber(L, 5, 1, &err) && tolua_isnoobj(L, 6, &err)) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		Common::String s2(tolua_tostring(L, 2, nullptr));
-		Common::String s3(tolua_tostring(L, 2, nullptr));
-		double n1 = tolua_tonumber(L, 3, 0.0);
-		double n2 = tolua_tonumber(L, 4, -1.0);
+		Common::String s3(tolua_tostring(L, 3, nullptr));
+		double n1 = tolua_tonumber(L, 4, 0.0);
+		double n2 = tolua_tonumber(L, 5, -1.0);
 		AddCallback(s1, s2, s3, n1, n2);
 		return 0;
 	}
@@ -412,7 +427,7 @@ static void HideObject(const Common::String &objName) {
 	}
 
 	debug("[HideObject] \"Set\" Object 2D \"%s\" doesn't exist.", objName.c_str());
-	layout = game->gui3().layout(objName);
+	layout = game->forGui().layout(objName);
 	if (layout) {
 		layout->setVisible(false);
 		return;
@@ -447,7 +462,7 @@ static void ShowObject(const Common::String &objName) {
 	}
 
 	debug("[ShowObject] \"Set\" Object 2D \"%s\" doesn't exist.", objName.c_str());
-	layout = game->gui3().layout(objName);
+	layout = game->forGui().layout(objName);
 	if (layout) {
 		layout->setVisible(true);
 		return;
@@ -578,7 +593,7 @@ static int tolua_ExportedFunctions_SetCharacterOrientation00(lua_State *L) {
 static void SetCharacterAnimation(const Common::String &charname, const Common::String &animname, bool repeat, bool b2, int i1, int i2) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charname);
-	bool result = c->setAnimation(animname, repeat, b2, i1, i2);
+	bool result = c->setAnimation(animname, repeat, b2, true, i1, i2);
 	if (!result) {
 		warning("[SetCharacterAnimation] Character's animation \"%s\" doesn't exist for the character\"%s\"  ",
 			animname.c_str(), charname.c_str());
@@ -603,6 +618,59 @@ static int tolua_ExportedFunctions_SetCharacterAnimation00(lua_State *L) {
 	error("#ferror in function 'SetCharacterAnimation': %d %d %s", err.index, err.array, err.type);
 }
 
+static void BlendCharacterAnimation(const Common::String &charname, const Common::String &animname, float blendAmount, bool repeat, bool b2) {
+	Game *game = g_engine->getGame();
+	Character *c = game->scene().character(charname);
+	bool result = c->blendAnimation(animname, blendAmount, repeat, b2);
+	if (!result) {
+		warning("[BlendCharacterAnimation] Character's animation \"%s\" doesn't exist for the character\"%s\"  ",
+			animname.c_str(), charname.c_str());
+	}
+}
+
+static int tolua_ExportedFunctions_BlendCharacterAnimation00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isboolean(L, 4, 1, &err)
+		&& tolua_isboolean(L, 5, 1, &err) && tolua_isnoobj(L, 6, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		double f1 = tolua_tonumber(L, 3, 0.0);
+		bool b1 = tolua_toboolean(L, 4, 1);
+		bool b2 = tolua_toboolean(L, 5, 0);
+		BlendCharacterAnimation(s1, s2, f1, b1, b2);
+		return 0;
+	}
+	error("#ferror in function 'BlendCharacterAnimation': %d %d %s", err.index, err.array, err.type);
+}
+
+static int tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isboolean(L, 4, 1, &err)
+		&& tolua_isboolean(L, 5, 1, &err) && tolua_isnoobj(L, 6, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		double f1 = tolua_tonumber(L, 3, 0.0);
+		bool b1 = tolua_toboolean(L, 4, 1);
+		bool b2 = tolua_toboolean(L, 5, 0);
+		BlendCharacterAnimation(s1, s2, f1, b1, b2);
+
+		Game::YieldedCallback cb;
+		cb._luaFnName = "OnCharacterAnimationFinished";
+		cb._luaParam = s1;
+		cb._luaParam2 = s2;
+		cb._luaThread = TeLuaThread::threadFromState(L);
+		Game *game = g_engine->getGame();
+		for (const auto &gamecb : game->yieldedCallbacks()) {
+			if (gamecb._luaFnName == cb._luaFnName && gamecb._luaParam == s1 && gamecb._luaParam2 == s2)
+				error("BlendCharacterAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+		}
+		game->yieldedCallbacks().push_back(cb);
+		return cb._luaThread->yield();
+	}
+	error("#ferror in function 'BlendCharacterAnimationAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
+}
 
 static void SetCharacterPosition(const Common::String &charname, const Common::String &zonename, float f1, float f2, float f3) {
 	Game *game = g_engine->getGame();
@@ -869,6 +937,7 @@ static int tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00(lua_State *L) {
 				error("LaunchDialogAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 
+		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
 	}
 	error("#ferror in function 'LaunchDialogAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
@@ -1196,7 +1265,7 @@ static int tolua_ExportedFunctions_PlayMusic00(lua_State *L) {
 	error("#ferror in function 'PlayMusic': %d %d %s", err.index, err.array, err.type);
 }
 
-static void SetObjectOnCharacter(const Common::String &obj, const Common::String &charName, const Common::String &boneName) {
+static void SetObjectOnCharacter(const Common::String &charName, const Common::String &obj, const Common::String &boneName) {
 	Game *game = g_engine->getGame();
 	Object3D *obj3d = game->scene().object3D(obj);
 	if (!obj3d) {
@@ -1499,8 +1568,8 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
 	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject01);*/
 	tolua_function(L, "AddNumber", tolua_ExportedFunctions_AddNumber00);
-	/*tolua_function(L, "ShowDocument", tolua_ExportedFunctions_ShowDocument00);
-	tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00);
+	tolua_function(L, "ShowDocument", tolua_ExportedFunctions_ShowDocument00);
+	/*tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00);
 	tolua_function(L, "HideDocument", tolua_ExportedFunctions_HideDocument00);
 	tolua_function(L, "AddDocument", tolua_ExportedFunctions_AddDocument00);*/
 	tolua_function(L, "LoadCharacter", tolua_ExportedFunctions_LoadCharacter00);
@@ -1522,10 +1591,10 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
 	tolua_function(L, "SetCharacterAnimation", tolua_ExportedFunctions_SetCharacterAnimation00);
 	/*tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
-				 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);
+				 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);*/
 	tolua_function(L, "BlendCharacterAnimation", tolua_ExportedFunctions_BlendCharacterAnimation00);
 	tolua_function(L, "BlendCharacterAnimationAndWaitForEnd",
-				 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);*/
+				 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);
 	tolua_function(L, "CurrentCharacterAnimation", tolua_ExportedFunctions_CurrentCharacterAnimation00);
 	tolua_function(L, "SetCharacterPlayerVisible", tolua_ExportedFunctions_SetCharacterPlayerVisible00);
 	tolua_function(L, "MoveCharacterPlayerDisabled",
diff --git a/engines/tetraedge/game/question2.h b/engines/tetraedge/game/question2.h
index ca5ddd7ee6f..6468adafa8b 100644
--- a/engines/tetraedge/game/question2.h
+++ b/engines/tetraedge/game/question2.h
@@ -52,6 +52,7 @@ public:
 	void pushAnswer(const Common::String &name, const Common::String &unk, const Common::String &path);
 	void unload();
 	TeLuaGUI &gui() { return _gui; }
+	TeSignal1Param<const Common::String &> &onAnswerSignal() { return _onAnswerSignal; }
 
 private:
 	TeLuaGUI _gui;
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index d4997914a24..f18a56b5e43 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -141,6 +141,11 @@ void Te3DObject2::removeChild(Te3DObject2 *child) {
 		_children[i]->setParent(nullptr);
 		_children.remove_at(i);
 		_childListChangedSignal.call();
+	} else {
+		Common::String cname("nullptr");
+		if (child)
+			cname = child->name();
+		warning("Request to remove child (%s) which is not a child of this (%s).", cname.c_str(), name().c_str());
 	}
 }
 
@@ -196,6 +201,7 @@ void Te3DObject2::setPosition(const TeVector3f32 &pos) {
 	if (_position == pos)
 		return;
 
+	// FIXME: remove this debugging code.
 	if ((_position - pos).length() > 2.0f && name() == "Kate" && _position != TeVector3f32()) {
 		debug("Large position move %s %s -> %s", name().c_str(),
 			_position.dump().c_str(), pos.dump().c_str());
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index 54693085cb7..a129e438679 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -31,7 +31,7 @@ namespace Tetraedge {
 static const uint NO_TEXTURE = 0xffffffff;
 
 Te3DTexture::Te3DTexture() : _glTexture(NO_TEXTURE), _createdTexture(false),
-_numFrames(1), _frameRate(0), _format(TeImage::INVALID), _glPixelFormat(GL_INVALID_ENUM) {
+_numFrames(1), _frameRate(0), _format(TeImage::INVALID)/*, _glPixelFormat(GL_INVALID_ENUM)*/ {
 	create();
 }
 
@@ -49,18 +49,17 @@ void Te3DTexture::bind() const {
 }
 
 void Te3DTexture::copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y) {
-	// TODO: Get some better variable names here.
 	_matrix.setToIdentity();
-	const TeVector3f32 local_40((float)_width / _texWidth, (float)_height / _texHeight, 1.0);
-	_matrix.scale(local_40);
-	const TeVector3f32 local_50((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0);
-	_matrix.translate(local_50);
-	const TeVector3f32 local_60(
+	const TeVector3f32 texScale((float)_width / _texWidth, (float)_height / _texHeight, 1.0);
+	_matrix.scale(texScale);
+	const TeVector3f32 offset((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0);
+	_matrix.translate(offset);
+	const TeVector3f32 borderScale(
 			   1.0 - (float)(_rightBorder + _leftBorder) /
 					 (float)_width,
 			   1.0 - (float)(_topBorder + _btmBorder) /
 					 (float)_height, 1.0);
-	_matrix.scale(local_60);
+	_matrix.scale(borderScale);
 	bind();
 	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, _texWidth, _texHeight);
 }
diff --git a/engines/tetraedge/te/te_3d_texture.h b/engines/tetraedge/te/te_3d_texture.h
index f3a57e96041..1696cbaf2d1 100644
--- a/engines/tetraedge/te/te_3d_texture.h
+++ b/engines/tetraedge/te/te_3d_texture.h
@@ -69,7 +69,7 @@ private:
 	bool _createdTexture;
 	bool _loaded;
 	uint _glTexture;
-	uint _glPixelFormat;
+	//uint _glPixelFormat;
 	TeMatrix4x4 _matrix;
 
 	uint _texWidth;
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index f0235e3492c..d06768c6bc5 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -220,7 +220,8 @@ void TeButtonLayout::setDisabledLayout(TeLayout *disabledLayout) {
 	_disabledLayout = disabledLayout;
 	if (_disabledLayout) {
 		addChild(_disabledLayout);
-		_disabledLayout->setColor(TeColor(0, 0, 0, 0));
+		//_disabledLayout->setColor(TeColor(0, 0, 0, 0));
+		//_disabledLayout->setName(name() + "_disabledLayout");
 	}
 
 	setState(_currentState);
@@ -233,7 +234,8 @@ void TeButtonLayout::setHitZone(TeLayout *hitZoneLayout) {
 	_hitZoneLayout = hitZoneLayout;
 	if (_hitZoneLayout) {
 		addChild(_hitZoneLayout);
-		_hitZoneLayout->setColor(TeColor(0, 0, 0xff, 0xff));
+		//_hitZoneLayout->setColor(TeColor(0, 0, 0xff, 0xff));
+		//_hitZoneLayout->setName(name() + "_hitZoneLayout");
 	}
 }
 
@@ -251,8 +253,10 @@ void TeButtonLayout::setDownLayout(TeLayout *downLayout) {
 		setSize(_downLayout->size());
 	}
 
-	if (_downLayout)
-		_downLayout->setColor(TeColor(0, 0, 0, 0));
+	//if (_downLayout) {
+	//	_downLayout->setColor(TeColor(0, 0, 0, 0));
+		//_downLayout->setName(name() + "_downLayout");
+	//}
 
 	setState(_currentState);
 }
@@ -264,9 +268,14 @@ void TeButtonLayout::setRollOverLayout(TeLayout *rollOverLayout) {
 	_rolloverLayout = rollOverLayout;
 	if (_rolloverLayout) {
 		addChild(_rolloverLayout);
-		_rolloverLayout->setColor(TeColor(0, 0, 0, 0));
+		//_rolloverLayout->setName(name() + "_rolloverLayout");
 	}
 
+	// This is not a copy paste error, or at least, not *my*
+	// copy paste error.. it's what the original game does.
+	//if (_disabledLayout)
+	//	_disabledLayout->setColor(TeColor(0, 0, 0, 0));
+
 	setState(_currentState);
 }
 
@@ -284,8 +293,10 @@ void TeButtonLayout::setUpLayout(TeLayout *upLayout) {
 		setSize(_upLayout->size());
 	}
 
-	if (_upLayout)
-		_upLayout->setColor(TeColor(0, 0, 0, 0));
+	if (_upLayout) {
+		//_upLayout->setColor(TeColor(0, 0, 0, 0));
+		//_upLayout->setName(name() + "_upLayout");
+	}
 
 	setState(_currentState);
 }
@@ -338,7 +349,10 @@ void TeButtonLayout::setState(State newState) {
 	}
 
 	if (_upLayout)
-		_upLayout->setVisible(_currentState == BUTTON_STATE_UP);
+		_upLayout->setVisible(_currentState == BUTTON_STATE_UP
+				|| (_currentState == BUTTON_STATE_ROLLOVER && _rolloverLayout == nullptr)
+				|| (_currentState == BUTTON_STATE_DOWN && _downLayout == nullptr)
+				|| (_currentState == BUTTON_STATE_DISABLED && _disabledLayout == nullptr));
 	if (_downLayout)
 		_downLayout->setVisible(_currentState == BUTTON_STATE_DOWN);
 	if (_disabledLayout)
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index 430d9be686f..bb2eb7bf4dc 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -79,23 +79,23 @@ void TeCamera::buildOrthoMatrix() {
 	}
 
 	_projectionMatrix.setValue(0, 0, widthNorm * 2.0f);
-	_projectionMatrix.setValue(0, 1, 0.0);
-	_projectionMatrix.setValue(0, 2, 0.0);
-	_projectionMatrix.setValue(0, 3, -((_orthogonalParamR + _orthogonalParamL) * widthNorm));
-
 	_projectionMatrix.setValue(1, 0, 0.0);
-	_projectionMatrix.setValue(1, 1, heightNorm * 2.0f);
-	_projectionMatrix.setValue(1, 2, 0.0);
-	_projectionMatrix.setValue(1, 3, -((_orthogonalParamB + _orthogonalParamT) * heightNorm));
-
 	_projectionMatrix.setValue(2, 0, 0.0);
-	_projectionMatrix.setValue(2, 1, 0.0);
-	_projectionMatrix.setValue(2, 2, depthNorm * -2.0f);
-	_projectionMatrix.setValue(2, 3, -((_orthFarVal + _orthNearVal) * depthNorm));
-
 	_projectionMatrix.setValue(3, 0, 0.0);
+
+	_projectionMatrix.setValue(0, 1, 0.0);
+	_projectionMatrix.setValue(1, 1, heightNorm * 2.0f);
+	_projectionMatrix.setValue(2, 1, 0.0);
 	_projectionMatrix.setValue(3, 1, 0.0);
+
+	_projectionMatrix.setValue(0, 2, 0.0);
+	_projectionMatrix.setValue(1, 2, 0.0);
+	_projectionMatrix.setValue(2, 2, depthNorm * -2.0f);
 	_projectionMatrix.setValue(3, 2, 0.0);
+
+	_projectionMatrix.setValue(0, 3, -((_orthogonalParamR + _orthogonalParamL) * widthNorm));
+	_projectionMatrix.setValue(1, 3, -((_orthogonalParamB + _orthogonalParamT) * heightNorm));
+	_projectionMatrix.setValue(2, 3, -((_orthFarVal + _orthNearVal) * depthNorm));
 	_projectionMatrix.setValue(3, 3, 1.0);
 }
 
diff --git a/engines/tetraedge/te/te_checkbox_layout.cpp b/engines/tetraedge/te/te_checkbox_layout.cpp
index 05ad75eec21..0392205e0a7 100644
--- a/engines/tetraedge/te/te_checkbox_layout.cpp
+++ b/engines/tetraedge/te/te_checkbox_layout.cpp
@@ -71,7 +71,7 @@ void TeCheckboxLayout::setUnactiveLayout(TeLayout *layout) {
 	warning("TODO: Add extra code in TeCheckboxLayout::setUnactiveLayout.");
 	if (layout) {
 		addChild(layout);
-		layout->setColor(TeColor(0, 0, 0, 0));
+		//layout->setColor(TeColor(0, 0, 0, 0));
 	}
 	setState(_state);
 }
@@ -82,7 +82,7 @@ void TeCheckboxLayout::setActiveDisabledLayout(TeLayout *layout) {
 	_activeDisabledLayout = layout;
 	if (layout) {
 		addChild(layout);
-		layout->setColor(TeColor(0, 0, 0, 0));
+		//layout->setColor(TeColor(0, 0, 0, 0));
 	}
 	setState(_state);
 }
@@ -93,7 +93,7 @@ void TeCheckboxLayout::setUnactiveDisabledLayout(TeLayout *layout) {
 	_unactiveDisabledLayout = layout;
 	if (layout) {
 		addChild(layout);
-		layout->setColor(TeColor(0, 0, 0, 0));
+		//layout->setColor(TeColor(0, 0, 0, 0));
 	}
 	setState(_state);
 }
@@ -104,7 +104,7 @@ void TeCheckboxLayout::setActiveRollOverLayout(TeLayout *layout) {
 	_activeRollOverLayout = layout;
 	if (layout) {
 		addChild(layout);
-		layout->setColor(TeColor(0, 0, 0, 0));
+		//layout->setColor(TeColor(0, 0, 0, 0));
 	}
 	setState(_state);
 }
@@ -115,7 +115,7 @@ void TeCheckboxLayout::setUnactiveRollOverLayout(TeLayout *layout) {
 	_unactiveRollOverLayout = layout;
 	if (layout) {
 		addChild(layout);
-		layout->setColor(TeColor(0, 0, 0, 0));
+		//layout->setColor(TeColor(0, 0, 0, 0));
 	}
 	setState(_state);
 }
@@ -126,7 +126,7 @@ void TeCheckboxLayout::setHitZone(TeLayout *layout) {
 	_hitZone = layout;
 	if (layout) {
 		addChild(layout);
-		layout->setColor(TeColor(0, 0, 0xff, 0xff));
+		//layout->setColor(TeColor(0, 0, 0xff, 0xff));
 	}
 }
 
diff --git a/engines/tetraedge/te/te_curve_anim2.h b/engines/tetraedge/te/te_curve_anim2.h
index 29a30834186..f13bdae1fa2 100644
--- a/engines/tetraedge/te/te_curve_anim2.h
+++ b/engines/tetraedge/te/te_curve_anim2.h
@@ -28,6 +28,7 @@
 namespace Tetraedge {
 
 template<class T> static T linearInterpolation(T &obj1, T &obj2, double amount) {
+	amount = CLIP(amount, 0.0, 1.0);
 	return (obj1 * (1.0 - amount)) + (obj2 * amount);
 }
 
diff --git a/engines/tetraedge/te/te_font3.cpp b/engines/tetraedge/te/te_font3.cpp
index 757d1050ca3..54367dea86d 100644
--- a/engines/tetraedge/te/te_font3.cpp
+++ b/engines/tetraedge/te/te_font3.cpp
@@ -118,6 +118,11 @@ Common::Rect TeFont3::getBoundingBox(const Common::String &str, int fontSize) {
 	return font->getBoundingBox(str);
 }
 
+int TeFont3::getHeight(int fontSize) {
+	Graphics::Font *font = getAtSize(fontSize);
+	return font->getFontHeight();
+}
+
 void TeFont3::draw(TeImage &destImage, const Common::String &str, int fontSize, int yoff, const TeColor &col, TeFont3::AlignStyle align) {
 	Graphics::Font *font = getAtSize(fontSize);
 	Graphics::TextAlign talign;
@@ -135,7 +140,10 @@ void TeFont3::draw(TeImage &destImage, const Common::String &str, int fontSize,
 			talign = Graphics::kTextAlignCenter;
 			break;
 	}
-	uint32 uintcol = (col.r() << 24) | (col.r() << 16) | (col.r() << 8) | col.r();
+	const Graphics::PixelFormat &fmt = destImage.format;
+
+	uint32 uintcol = ((uint32)col.a() << fmt.aShift) | ((uint32)(col.r()) << fmt.rShift)
+						| ((uint32)(col.g()) << fmt.gShift) | ((uint32)(col.b()) << fmt.bShift);
 	font->drawString(&destImage, str, 0, yoff, destImage.w, uintcol, talign);
 }
 
diff --git a/engines/tetraedge/te/te_font3.h b/engines/tetraedge/te/te_font3.h
index a17cdd0cb1a..7350d355049 100644
--- a/engines/tetraedge/te/te_font3.h
+++ b/engines/tetraedge/te/te_font3.h
@@ -79,6 +79,7 @@ public:
 
 	int wordWrapText(const Common::String &str, int fontSize, int maxWidth, Common::Array<Common::String> &lines);
 	Common::Rect getBoundingBox(const Common::String &str, int fontSize);
+	int getHeight(int fontSize);
 
 	void draw(TeImage &destImage, const Common::String &str, int fontSize, int yoff, const TeColor &col, AlignStyle alignMode);
 
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index b59155364c8..617b44779f5 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -55,7 +55,7 @@ void TeImage::create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
 									  Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0) : Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
 
 	Graphics::Surface::create(xsize, ysize, pxformat);
-	Graphics::Surface::fillRect(Common::Rect(0, 0, xsize, ysize), 0xff883311);
+	Graphics::Surface::fillRect(Common::Rect(0, 0, xsize, ysize), 0);
 }
 
 void TeImage::deserialize(Common::ReadStream &stream) {
@@ -77,7 +77,8 @@ void TeImage::fill(byte val) {
 
 void TeImage::fill(byte r, byte g, byte b, byte a) {
 	Common::Rect wholeSurf(0, 0, w, h);
-	uint32 col = (r << 24) | (g << 16) | (b << 8) | a;
+
+	uint32 col = ((uint32)r << format.rShift) | ((uint32)g << format.gShift) | ((uint32)b << format.bShift) | (uint32)a << format.aShift;
 	Graphics::Surface::fillRect(wholeSurf, col);
 }
 
diff --git a/engines/tetraedge/te/te_image.h b/engines/tetraedge/te/te_image.h
index 995e8be2f91..ae0aa47ffb5 100644
--- a/engines/tetraedge/te/te_image.h
+++ b/engines/tetraedge/te/te_image.h
@@ -83,7 +83,7 @@ public:
 
 private:
 
-	// TODO add private members
+	// No private members?
 
 };
 
diff --git a/engines/tetraedge/te/te_layout.cpp b/engines/tetraedge/te/te_layout.cpp
index 733eb5ba5ac..be8ac7b36d2 100644
--- a/engines/tetraedge/te/te_layout.cpp
+++ b/engines/tetraedge/te/te_layout.cpp
@@ -27,14 +27,13 @@
 
 namespace Tetraedge {
 
-TeLayout::TeLayout() : Te3DObject2(), _updatingZ(false), _updatingZSize(false),
-	_updatingPosition(false), _updatingWorldMatrix(false), _updatingSize(false),
-	_autoz(true), _childOrParentChanged(true), _childChanged(true),
-	_sizeChanged(true), _positionChanged(true), _worldMatrixChanged(true),
+TeLayout::TeLayout() : Te3DObject2(), _autoz(true), _needZUpdate(true), _updatingZ(false),
+	_needZSizeUpdate(true), _updatingZSize(false), _sizeChanged(true), _updatingSize(false),
+	_positionChanged(true), _updatingPosition(false), _worldMatrixChanged(true),
+	_updatingWorldMatrix(false), _drawMode(TeILayout::DrawMode0),
 	_sizeType(CoordinatesType::ABSOLUTE), _userSize(1.0f, 1.0f, 1.0f),
-	_anchor(0.5f, 0.5f, 0.5f), _ratio(1.0f), _drawMode(TeILayout::DrawMode0),
-	_safeAreaRatio(1.3333334f), _ratioMode(RATIO_MODE_NONE),
-	_positionType(CoordinatesType::RELATIVE_TO_PARENT)
+	_anchor(0.5f, 0.5f, 0.5f), _ratio(1.0f), _safeAreaRatio(1.3333334f),
+	_ratioMode(RATIO_MODE_NONE), _positionType(CoordinatesType::RELATIVE_TO_PARENT)
 {
 	_userPosition = _position = TeVector3f32(0.5f, 0.5f, 0.5f);
 	_size = TeVector3f32(1.0f, 1.0f, 1.0f);
@@ -67,8 +66,8 @@ void TeLayout::addChild(Te3DObject2 *child) {
 	if (_onChildSizeChangedCallback) {
 		child->onSizeChanged().insert(_onChildSizeChangedCallback);
 	}
-	_childChanged = true;
-	_childOrParentChanged = true;
+	_needZSizeUpdate = true;
+	_needZUpdate = true;
 	updateZSize();
 	updateZ();
 }
@@ -78,8 +77,8 @@ void TeLayout::addChildBefore(Te3DObject2 *child, const Te3DObject2 *ref) {
 	if (_onChildSizeChangedCallback) {
 		child->onSizeChanged().insert(_onChildSizeChangedCallback);
 	}
-	_childChanged = true;
-	_childOrParentChanged = true;
+	_needZSizeUpdate = true;
+	_needZUpdate = true;
 	updateZSize();
 	updateZ();
 }
@@ -89,8 +88,8 @@ void TeLayout::removeChild(Te3DObject2 *child) {
 		child->onSizeChanged().remove(_onChildSizeChangedCallback);
 	}
 	Te3DObject2::removeChild(child);
-	_childChanged = true;
-	_childOrParentChanged = true;
+	_needZSizeUpdate = true;
+	_needZUpdate = true;
 	updateZSize();
 	updateZ();
 }
@@ -138,8 +137,8 @@ TeILayout::DrawMode TeLayout::mode() {
 }
 
 bool TeLayout::onChildSizeChanged() {
-	_childChanged = true;
-	_childOrParentChanged = true;
+	_needZSizeUpdate = true;
+	_needZUpdate = true;
 
 	updateSize();
 	if (!_updatingZSize)
@@ -205,8 +204,9 @@ void TeLayout::setParent(Te3DObject2 *parent) {
 			oldParent->onWorldTransformationMatrixChanged().remove(_onParentWorldTransformationMatrixChangedCallback);
 	}
 
-	//warning("TODO: remove callback from main window");
-	//TeMainWindow *mainWindow = g_engine->getMainWindow();
+	//
+	TeLayout &mainWindowLayout = g_engine->getApplication()->getMainWindow();
+	mainWindowLayout.onSizeChanged().remove(_onMainWindowChangedCallback);
 
 	Te3DObject2::setParent(parent);
 	if (parent) {
@@ -214,10 +214,10 @@ void TeLayout::setParent(Te3DObject2 *parent) {
 			parent->onSizeChanged().insert(_onParentSizeChangedCallback);
 		if (_onParentWorldTransformationMatrixChangedCallback)
 			parent->onWorldTransformationMatrixChanged().insert(_onParentWorldTransformationMatrixChangedCallback);
-		// TODO: add a new callback to the MainWindow.
-		//warning("TODO: update signal on main window");
+		if (_onMainWindowChangedCallback)
+			mainWindowLayout.onSizeChanged().insert(_onMainWindowChangedCallback);
 	}
-	_childOrParentChanged = true;
+	_needZUpdate = true;
 	_sizeChanged = true;
 	_positionChanged = true;
 	_worldMatrixChanged = true;
@@ -373,10 +373,9 @@ void TeLayout::updateSize() {
 
 	if (_sizeType == ABSOLUTE) {
 		TeVector3f32 newSize = _userSize;
-		newSize.x() = abs(newSize.x());
-		newSize.y() = abs(newSize.y());
-		newSize.z() = abs(newSize.z());
-		_size = newSize;
+		_size.x() = abs(newSize.x());
+		_size.y() = abs(newSize.y());
+		// don't set Z val.
 	} else if (_sizeType == RELATIVE_TO_PARENT) {
 		Te3DObject2 *parentObj = parent();
 		if (parentObj) {
@@ -400,9 +399,11 @@ void TeLayout::updateSize() {
 			  }
 			}
 
-			_size = newSize;
+			_size.x() = newSize.x();
+			_size.y() = newSize.y();
 		} else {
-			_size = TeVector3f32(0.0f, 0.0f, 0.0f);
+			_size.x() = 0.0f;
+			_size.y() = 0.0f;
 		}
 	}
 
@@ -432,24 +433,25 @@ void TeLayout::updateWorldMatrix() {
 }
 
 void TeLayout::updateZ() {
-	if (!_childOrParentChanged || !_autoz)
+	if (!_needZUpdate || !_autoz)
 		return;
 
-	_childOrParentChanged = false;
+	_needZUpdate = false;
 	_updatingZ = true;
 
-	/*float ztotal = 0.1f;*/
+	float ztotal = 0.1f;
 	for (auto &child : childList()) {
-		/*ztotal += */child->zSize();
+		child->setZPosition(ztotal);
+		ztotal += child->zSize();
 	}
 	_updatingZ = false;
 }
 
 void TeLayout::updateZSize() {
-	if (!_childChanged)
+	if (!_needZSizeUpdate)
 		return;
 
-	_childChanged = false;
+	_needZSizeUpdate = false;
 	_updatingZSize = true;
 	const TeVector3f32 oldSize = _size;
 	_size.z() = 0.1f;
@@ -478,7 +480,7 @@ TeVector3f32 TeLayout::worldPosition() {
 	if (!parent()) {
 		return position();
 	} else {
-		return parent()->position() + position();
+		return parent()->worldPosition() + position();
 	}
 }
 
@@ -499,17 +501,17 @@ bool TeLayout::worldVisible() {
 
 float TeLayout::xSize() {
 	updateSize();
-	return size().x();
+	return _size.x();
 }
 
 float TeLayout::ySize() {
 	updateSize();
-	return size().y();
+	return _size.y();
 }
 
 float TeLayout::zSize() {
 	updateZSize();
-	return size().z();
+	return _size.z();
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_layout.h b/engines/tetraedge/te/te_layout.h
index bfce5412e73..ec77facb3ba 100644
--- a/engines/tetraedge/te/te_layout.h
+++ b/engines/tetraedge/te/te_layout.h
@@ -111,8 +111,8 @@ private:
 	bool _autoz;
 	bool _positionChanged;
 	bool _worldMatrixChanged;
-	bool _childChanged;
-	bool _childOrParentChanged;
+	bool _needZSizeUpdate;
+	bool _needZUpdate;
 	bool _updatingZ;
 	bool _updatingZSize;
 	bool _updatingSize;
@@ -125,6 +125,7 @@ private:
 	TeICallback0ParamPtr _onChildSizeChangedCallback;
 	TeICallback0ParamPtr _onParentSizeChangedCallback;
 	TeICallback0ParamPtr _onParentWorldTransformationMatrixChangedCallback;
+	TeICallback0ParamPtr _onMainWindowChangedCallback;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index 9d184739831..d78d734fb01 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -389,7 +389,7 @@ int spriteLayoutBindings(lua_State *L) {
 
 	lua_pushnil(L);
 	while (lua_next(L, -2) != 0) {
-		if (lua_type(L, -2) == 3) {
+		if (lua_type(L, -2) == LUA_TNUMBER) {
 			Te3DObject2 *object = TeLuaTo<Te3DObject2*>(L, -1);
 			layout->addChild(object);
 		}
@@ -475,7 +475,7 @@ int buttonLayoutBindings(lua_State *L) {
 
 	lua_pushnil(L);
 	while (lua_next(L, -2) != 0) {
-		if (lua_type(L, -2) == 3) {
+		if (lua_type(L, -2) == LUA_TNUMBER) {
 			Te3DObject2 *object = TeLuaTo<Te3DObject2*>(L, -1);
 			layout->addChild(object);
 		}
@@ -545,7 +545,7 @@ int checkboxLayoutBindings(lua_State *L) {
 
 	lua_pushnil(L);
 	while (lua_next(L, -2) != 0) {
-		if (lua_type(L, -2) == 3) {
+		if (lua_type(L, -2) == LUA_TNUMBER) {
 			Te3DObject2 *object = TeLuaTo<Te3DObject2*>(L, -1);
 			layout->addChild(object);
 		}
diff --git a/engines/tetraedge/te/te_lua_thread.cpp b/engines/tetraedge/te/te_lua_thread.cpp
index b56aab1b937..54fe22956af 100644
--- a/engines/tetraedge/te/te_lua_thread.cpp
+++ b/engines/tetraedge/te/te_lua_thread.cpp
@@ -24,6 +24,7 @@
 #include "tetraedge/te/te_variant.h"
 
 #include "common/str.h"
+#include "common/debug.h"
 #include "common/file.h"
 #include "common/lua/lua.h"
 #include "common/lua/lauxlib.h"
@@ -75,7 +76,7 @@ void TeLuaThread::execute(const Common::String &fname) {
 		_resume(0);
 	} else {
 		if (!fname.contains("Update"))
-			warning("[TeLuaThread::Execute0] La fonction : \"%s\" n'existe pas.", fname.c_str());
+			debug("[TeLuaThread::Execute0] La fonction : \"%s\" n'existe pas.", fname.c_str());
 		lua_settop(_luaThread, -2);
 	}
 }
@@ -90,7 +91,7 @@ void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1) {
 		_resume(1);
 	} else {
 		if (!fname.contains("Update"))
-			warning("[TeLuaThread::Execute1] La fonction : \"%s\" n'existe pas.", fname.c_str());
+			debug("[TeLuaThread::Execute1] La fonction : \"%s\" n'existe pas.", fname.c_str());
 		lua_settop(_luaThread, -2);
 	}
 }
@@ -106,7 +107,7 @@ void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1, cons
 		_resume(2);
 	} else {
 		if (!fname.contains("Update"))
-			warning("[TeLuaThread::Execute2] La fonction : \"%s\" n'existe pas.", fname.c_str());
+			debug("[TeLuaThread::Execute2] La fonction : \"%s\" n'existe pas.", fname.c_str());
 		lua_settop(_luaThread, -2);
 	}
 }
@@ -123,7 +124,7 @@ void TeLuaThread::execute(const Common::String &fname, const TeVariant &p1, cons
 		_resume(3);
 	} else {
 		if (!fname.contains("Update"))
-			warning("[TeLuaThread::Execute3] La fonction : \"%s\" n'existe pas.", fname.c_str());
+			debug("[TeLuaThread::Execute3] La fonction : \"%s\" n'existe pas.", fname.c_str());
 		lua_settop(_luaThread, -4);
 	}
 }
diff --git a/engines/tetraedge/te/te_model_vertex_animation.cpp b/engines/tetraedge/te/te_model_vertex_animation.cpp
index 2735fba1f80..5b90be781c3 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.cpp
+++ b/engines/tetraedge/te/te_model_vertex_animation.cpp
@@ -20,13 +20,12 @@
  */
 
 #include "tetraedge/te/te_model_vertex_animation.h"
+#include "common/math.h"
 
 namespace Tetraedge {
 
-TeModelVertexAnimation::TeModelVertexAnimation() : _rot(1.0f, 0.0f, 0.0f, 0.0f),
-_lastMillis(0.0f), _modelAnim(nullptr) {
-	// TODO: set some other things up here.
-	_rot.fromAxisAndAngle(TeVector3f32(0.0f, 1.0f, 0.0f), -1.570796);
+TeModelVertexAnimation::TeModelVertexAnimation() : _lastMillis(0.0f), _modelAnim(nullptr) {
+	_rot.fromAxisAndAngle(TeVector3f32(0.0f, 1.0f, 0.0f), -M_PI_2);
 }
 
 void TeModelVertexAnimation::bind(TeIntrusivePtr<TeModel> &model) {
@@ -101,6 +100,4 @@ void TeModelVertexAnimation::update(double millis) {
 		_onFinishedSignal.call();
 }
 
-
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 9c8563934eb..ea5e0b0ee9d 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -282,8 +282,8 @@ void TeRenderer::loadMatrix(const TeMatrix4x4 &matrix) {
 }
 
 void TeRenderer::loadMatrixToGL(const TeMatrix4x4 &matrix) {
-	int mmode;
-	glGetIntegerv(GL_MATRIX_MODE, &mmode);
+	//int mmode;
+	//glGetIntegerv(GL_MATRIX_MODE, &mmode);
 	//debug("loadMatrixToGL[0x%x]: %s", mmode, matrix.toString().c_str());
 	glLoadMatrixf(matrix.getData());
 }
@@ -308,22 +308,27 @@ void TeRenderer::multiplyMatrix(const TeMatrix4x4 &matrix) {
 }
 
 void TeRenderer::optimiseTransparentMeshProperties() {
-	if (_transparentMeshProps.size() > 1) {
-		for (unsigned int i = 0; i < _transparentMeshProps.size() - 1; i++) {
-			if (_transparentMeshProps[i]._camera == _transparentMeshProps[i + 1]._camera
-				&& _transparentMeshProps[i]._material == _transparentMeshProps[i + 1]._material
-				&& _transparentMeshProps[i]._glTexEnvMode == _transparentMeshProps[i + 1]._glTexEnvMode
-				&& _transparentMeshProps[i]._matrix == _transparentMeshProps[i + 1]._matrix
-				&& _transparentMeshProps[i]._hasColor == _transparentMeshProps[i + 1]._hasColor
-				&& _transparentMeshProps[i]._scissorEnabled == _transparentMeshProps[i + 1]._scissorEnabled
-				&& _transparentMeshProps[i]._scissorX == _transparentMeshProps[i + 1]._scissorX
-				&& _transparentMeshProps[i]._scissorY == _transparentMeshProps[i + 1]._scissorY
-				&& _transparentMeshProps[i]._scissorWidth == _transparentMeshProps[i + 1]._scissorWidth
-				&& _transparentMeshProps[i]._scissorHeight == _transparentMeshProps[i + 1]._scissorHeight) {
-				_transparentMeshProps[i]._vertexCount += _transparentMeshProps[i + 1]._vertexCount;
-				_transparentMeshProps[i + 1]._shouldDraw = false;
-			}
+	if (_transparentMeshProps.size() <= 1)
+		return;
+
+	unsigned int i = 0;
+	for (unsigned int other = 1; other < _transparentMeshProps.size(); other++) {
+		unsigned int nextI = other;
+		if (_transparentMeshProps[i]._camera == _transparentMeshProps[other]._camera
+			&& _transparentMeshProps[i]._material == _transparentMeshProps[other]._material
+			&& _transparentMeshProps[i]._glTexEnvMode == _transparentMeshProps[other]._glTexEnvMode
+			&& _transparentMeshProps[i]._matrix == _transparentMeshProps[other]._matrix
+			&& _transparentMeshProps[i]._hasColor == _transparentMeshProps[other]._hasColor
+			&& _transparentMeshProps[i]._scissorEnabled == _transparentMeshProps[other]._scissorEnabled
+			&& _transparentMeshProps[i]._scissorX == _transparentMeshProps[other]._scissorX
+			&& _transparentMeshProps[i]._scissorY == _transparentMeshProps[other]._scissorY
+			&& _transparentMeshProps[i]._scissorWidth == _transparentMeshProps[other]._scissorWidth
+			&& _transparentMeshProps[i]._scissorHeight == _transparentMeshProps[other]._scissorHeight) {
+			_transparentMeshProps[i]._vertexCount += _transparentMeshProps[other]._vertexCount;
+			_transparentMeshProps[other]._shouldDraw = false;
+			nextI = i;
 		}
+		i = nextI;
 	}
 }
 
@@ -340,18 +345,28 @@ Common::String TeRenderer::renderer() {
 }
 
 
-static int compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
+static bool compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
 											const TeRenderer::TransparentMeshProperties &p2) {
-	if (p1._zOrder > p2._zOrder)
-		return 1;
-	if (p1._zOrder == p2._zOrder)
-		return 0;
-	return -1;
+	return (p1._zOrder < p2._zOrder);
 }
 
-void TeRenderer::dumpTransparentMeshes() const {
+void TeRenderer::dumpTransparentMeshProps() const {
+	debug("** Transparent MeshProps: num:%ld pending:%d **", _numTransparentMeshes, _pendingTransparentMeshProperties);
+	debug("draw? / nverts / source / transl / zorder");
+	for (unsigned int i = 0; i < _transparentMeshProps.size(); i++) {
+		debug("%s %d %d %s %f",
+			  _transparentMeshProps[i]._shouldDraw ? "draw" : "nodr",
+			  _transparentMeshProps[i]._vertexCount,
+			  _transparentMeshProps[i]._sourceTransparentMesh,
+			  _transparentMeshProps[i]._matrix.translation().dump().c_str(),
+			  _transparentMeshProps[i]._zOrder
+			  );
+	}
+}
+
+void TeRenderer::dumpTransparentMeshData() const {
 	debug("** Transparent Meshes: num:%ld pending:%d **", _numTransparentMeshes, _pendingTransparentMeshProperties);
-	debug("vert / normal / coord / color / trianglenum");
+	debug("vert / normal / coord / color / vertNo");
 	for (unsigned int i = 0; i < _transparentMeshVertexes.size(); i++) {
 		debug("%s %s %s %s %d",
 			  _transparentMeshVertexes[i].dump().c_str(),
@@ -368,6 +383,8 @@ void TeRenderer::renderTransparentMeshes() {
 		return;
 
 	glDepthMask(GL_FALSE);
+	//dumpTransparentMeshProps();
+
 	Common::sort(_transparentMeshProps.begin(), _transparentMeshProps.end(),
 		 compareTransparentMeshProperties);
 
@@ -379,9 +396,11 @@ void TeRenderer::renderTransparentMeshes() {
 		vertsDrawn += vcount;
 	}
 
-	//dumpTransparentMeshes();
-
 	optimiseTransparentMeshProperties();
+
+	//dumpTransparentMeshProps();
+	//dumpTransparentMeshData();
+
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -402,7 +421,7 @@ void TeRenderer::renderTransparentMeshes() {
 			continue;
 
 		const TeMaterial &material = meshProperties._material;
-		
+
 		meshProperties._camera->applyProjection();
 		glMatrixMode(GL_MODELVIEW);
 		_matrixMode = MM_GL_MODELVIEW;
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index e211ebc3f58..03ccb7ed28b 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -48,7 +48,7 @@ public:
 
 	class TransparentMeshProperties {
 	public:
-
+		TransparentMeshProperties() : _camera(nullptr), _vertexCount(0), _shouldDraw(false), _scissorEnabled(false), _hasColor(false) {}
 		TeCamera *_camera;
 		int _vertexCount;
 		TeMatrix4x4 _matrix;
@@ -117,7 +117,8 @@ public:
 	void translate(float x, float y, float z);
 	Common::String vendor();
 
-	void dumpTransparentMeshes() const;
+	void dumpTransparentMeshProps() const;
+	void dumpTransparentMeshData() const;
 	const TeColor &currentColor() const { return _currentColor; }
 
 private:
diff --git a/engines/tetraedge/te/te_scrolling_layout.cpp b/engines/tetraedge/te/te_scrolling_layout.cpp
index d4e3b334a22..9f027a624a9 100644
--- a/engines/tetraedge/te/te_scrolling_layout.cpp
+++ b/engines/tetraedge/te/te_scrolling_layout.cpp
@@ -38,6 +38,15 @@ void TeScrollingLayout::setContentLayout(TeLayout *layout) {
 	}
 }
 
+void TeScrollingLayout::resetScrollPosition() {
+	if (!_contentLayout)
+		return;
+	warning("TODO: Implement TeScrollingLayout::resetScrollPosition");
+}
+
+void TeScrollingLayout::playAutoScroll() {
+	warning("TODO: Implement TeScrollingLayout::playAutoScroll");
+}
 
 // TODO: Add more functions here.
 
diff --git a/engines/tetraedge/te/te_scrolling_layout.h b/engines/tetraedge/te/te_scrolling_layout.h
index 25d36471d89..ba64168cf57 100644
--- a/engines/tetraedge/te/te_scrolling_layout.h
+++ b/engines/tetraedge/te/te_scrolling_layout.h
@@ -80,6 +80,9 @@ public:
 	}
 	void setContentLayout(TeLayout *layout);
 
+	void resetScrollPosition();
+	void playAutoScroll();
+
 private:
 	int _inertiaAnimationDuration;
 	Common::Array<float> _inertiaAnimationCurve;
diff --git a/engines/tetraedge/te/te_sprite_layout.cpp b/engines/tetraedge/te/te_sprite_layout.cpp
index 625603d8571..7cc19c754ca 100644
--- a/engines/tetraedge/te/te_sprite_layout.cpp
+++ b/engines/tetraedge/te/te_sprite_layout.cpp
@@ -28,6 +28,8 @@ namespace Tetraedge {
 
 TeSpriteLayout::TeSpriteLayout() : _tiledSurfacePtr(new TeTiledSurface()), _sizeSet(false) {
 	_tiledSurfacePtr->setColor(TeColor(255, 255, 255, 255));
+	//_tiledSurfacePtr->_shouldDraw = true // should already be true..
+	// TODO: set some other flag in _tiledSurfacePtr?
 	updateMesh();
 }
 
@@ -44,10 +46,11 @@ void TeSpriteLayout::draw() {
 	if (!worldVisible())
 		return;
 
-	/*debug("Draw SpriteLayout %p (%s, surf %s, size %.01fx%.01f, surf %.01fx%.01f, %s)", this,
-		  name().empty() ? "no name" : name().c_str(), _tiledSurfacePtr->getAccessName().toString().c_str(),
-		  size().x(), size().y(),
-		  _tiledSurfacePtr->size().x(), _tiledSurfacePtr->size().y(), color().dump().c_str());*/
+	/*if (parent() && parent()->name() == "inventoryButton")
+		debug("Draw SpriteLayout %p (%s, surf %s, size %.01fx%.01f, surf %.01fx%.01f, %s)", this,
+			  name().empty() ? "no name" : name().c_str(), _tiledSurfacePtr->getAccessName().toString().c_str(),
+			  size().x(), size().y(),
+			  _tiledSurfacePtr->size().x(), _tiledSurfacePtr->size().y(), color().dump().c_str());*/
 	TeMatrix4x4 matrix = worldTransformationMatrix();
 
 	if (sizeType() == ABSOLUTE) {
@@ -89,6 +92,8 @@ bool TeSpriteLayout::load(const Common::Path &path) {
 			setSize(TeVector3f32(texSize._x, texSize._y, 1.0));
 		}
 		updateMesh();
+	} else {
+		debug("Failed to load TeSpriteLayout %s", path.toString().c_str());
 	}
 	return true;
 }
@@ -101,7 +106,7 @@ bool TeSpriteLayout::load(TeIntrusivePtr<Te3DTexture> &texture) {
 		if (tiledTexSize._y <= 0) {
 			setRatio(1.0);
 		} else {
-			setRatio((float)tiledTexSize._y / tiledTexSize._x);
+			setRatio((float)tiledTexSize._x / tiledTexSize._y);
 		}
 
 		if (sizeType() == CoordinatesType::ABSOLUTE && !_sizeSet) {
@@ -109,6 +114,8 @@ bool TeSpriteLayout::load(TeIntrusivePtr<Te3DTexture> &texture) {
 		}
 		updateMesh();
 		return true;
+	} else {
+		debug("Failed to load TeSpriteLayout from texture %s", texture->getAccessName().toString().c_str());
 	}
 	return false;
 }
@@ -121,7 +128,7 @@ bool TeSpriteLayout::load(TeImage &img) {
 		if (tiledTexSize._y <= 0) {
 			setRatio(1.0);
 		} else {
-			setRatio((float)tiledTexSize._y / tiledTexSize._x);
+			setRatio((float)tiledTexSize._x / tiledTexSize._y);
 		}
 
 		if (sizeType() == CoordinatesType::ABSOLUTE && !_sizeSet) {
@@ -129,6 +136,8 @@ bool TeSpriteLayout::load(TeImage &img) {
 		}
 		updateMesh();
 		return true;
+	} else {
+		debug("Failed to load TeSpriteLayout from texture %s", img.getAccessName().toString().c_str());
 	}
 	return false;
 }
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index d03f3b4034a..114012dfca7 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -56,6 +56,7 @@ void TeTextBase2::build() {
 	font->wordWrapText(_text, _fontSize, _drawRect._x, _wrappedLines);
 
 	Common::Array<float> lineoffsets;
+	float lineHeight = font->getHeight(_fontSize);
 	float height = 0;
 	for (const Common::String &line : _wrappedLines) {
 		if (_alignStyle == TeFont3::AlignJustify) {
@@ -67,7 +68,7 @@ void TeTextBase2::build() {
 			_size._x = lineSize.right;
 
 		lineoffsets.push_back(height);
-		height += lineSize.bottom + _interLine;
+		height += lineHeight + _interLine;
 	}
 	_size._y = (int)ceilf(height);
 
@@ -92,7 +93,8 @@ void TeTextBase2::build() {
 
 	_mesh.setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
 	_mesh.defaultMaterial(texture);
-
+	// FIXME: Original uses BLEND, but we need MODULATE to get right colors?
+	_mesh.setglTexEnv(GL_MODULATE);
 	_mesh.setShouldDraw(true);
 	_mesh.setColor(_globalColor);
 	_mesh.setVertex(0, TeVector3f32(_size._x * -0.5f, _size._y * -0.5f, 0.0f));
@@ -245,8 +247,6 @@ void TeTextBase2::setFont(unsigned int offset, const TeIntrusivePtr<TeFont3> &ne
 }
 
 void TeTextBase2::setFontSize(unsigned long newSize) {
-	// Bit of a hack here to get the right font size.
-	newSize *= 1.2;
 	if (_fontSize != newSize) {
 		_fontSize = newSize;
 		_valueWasSet = true;
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index fc087c10ccf..1e4245b803a 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -57,6 +57,8 @@ bool TeTiledSurface::load(const Common::Path &path) {
 	_path = path;
 
 	TeIntrusivePtr<TeTiledTexture> texture;
+	if (path.toString() == "menus/inGame/Inventory.png")
+		debug("loading inventory from path");
 	if (resmgr->exists(path.append(".tt"))) {
 		texture = resmgr->getResourceNoSearch<TeTiledTexture>(path.append(".tt"));
 		// we don't own this one..
@@ -73,7 +75,7 @@ bool TeTiledSurface::load(const Common::Path &path) {
 		texture = new TeTiledTexture();
 
 		if (_codec->load(foundPath)) {
-			texture->setAccessName(path);
+			texture->setAccessName(path.append(".tt"));
 			resmgr->addResource(texture.get());
 			_imgFormat = _codec->imageFormat();
 
@@ -93,7 +95,7 @@ bool TeTiledSurface::load(const Common::Path &path) {
 			}
 
 			Common::SharedPtr<TePalette> nullpal;
-			img.create(_codec->width(), _codec->height(), nullpal, (TeImage::Format)_imgFormat, bufx, bufy);
+			img.create(_codec->width(), _codec->height(), nullpal, _imgFormat, bufx, bufy);
 
 			if (_codec->update(0, img)) {
 				texture->load(img);
@@ -118,6 +120,8 @@ bool TeTiledSurface::load(const TeIntrusivePtr<Te3DTexture> &texture) {
 	TeIntrusivePtr<TeTiledTexture> tiledTexture;
 
 	const Common::Path ttPath = texture->getAccessName().append(".tt");
+	if (ttPath.toString() == "menus/inGame/Inventory.png.tt")
+		debug("loading inventory from texture");
 
 	if (resmgr->exists(ttPath)) {
 		tiledTexture = resmgr->getResourceNoSearch<TeTiledTexture>(ttPath);
@@ -233,7 +237,7 @@ void TeTiledSurface::updateSurface() {
 			TeTiledTexture::Tile *tile = _tiledTexture->tile(TeVector2s32(col, row));
 			mesh.defaultMaterial(tile->_texture);
 
-			const TeColor meshcol = color();
+			TeColor meshcol = color();
 
 			float left, right, top, bottom;
 			getRangeIntersection(_leftCrop, 1.0 - _rightCrop, tile->_vec1.x(), tile->_vec2.x() + tile->_vec1.x(), &left, &right);
diff --git a/engines/tetraedge/te/te_tiled_texture.cpp b/engines/tetraedge/te/te_tiled_texture.cpp
index 6ae69f9dcc3..d07804297e0 100644
--- a/engines/tetraedge/te/te_tiled_texture.cpp
+++ b/engines/tetraedge/te/te_tiled_texture.cpp
@@ -117,6 +117,8 @@ bool TeTiledTexture::load(const TeImage &img) {
 	if (rows)
 		_somethingSize._y = _somethingSize._y / rows;
 	setAccessName(img.getAccessName().append(".tt"));
+	if (img.getAccessName().toString() == "menus/inGame/Inventory.png")
+		debug("loading inventory tiled texture");
 	return true;
 }
 
@@ -131,6 +133,8 @@ bool TeTiledTexture::load(const TeIntrusivePtr<Te3DTexture> &texture) {
 	tileData->_vec2 = TeVector3f32(1.0, 1.0, 0.0);
 	tileData->_vec1 = TeVector3f32(0.0, 0.0, 0.0);
 	setAccessName(texture->getAccessName().append(".tt"));
+	if (texture->getAccessName().toString() == "menus/inGame/Inventory.png")
+		debug("loading inventory tiled texture from texture");
 	return true;
 }
 
diff --git a/engines/tetraedge/te/te_vector3f32.h b/engines/tetraedge/te/te_vector3f32.h
index 668328aa7aa..951fe0157d3 100644
--- a/engines/tetraedge/te/te_vector3f32.h
+++ b/engines/tetraedge/te/te_vector3f32.h
@@ -72,7 +72,7 @@ public:
 	bool parse(const Common::String &val);
 
 	Common::String dump() const {
-		return Common::String::format("TeVec3f32(%.02f %.02f %.02f)", x(), y(), z());
+		return Common::String::format("Vec3f(%.02f %.02f %.02f)", x(), y(), z());
 	}
 
 	void rotate(const TeQuaternion &rot);
diff --git a/engines/tetraedge/te/te_xml_gui.cpp b/engines/tetraedge/te/te_xml_gui.cpp
index e9ba7c0bd62..8d14949bf83 100644
--- a/engines/tetraedge/te/te_xml_gui.cpp
+++ b/engines/tetraedge/te/te_xml_gui.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/debug.h"
 #include "common/hash-str.h"
 #include "common/textconsole.h"
 #include "tetraedge/te/te_xml_gui.h"
@@ -47,7 +48,7 @@ void TeXmlGui::load(const Common::Path &path) {
 
 void TeXmlGui::clear() {
 	_map.clear();
-	// TODO: probably more here.
+	debug("TODO: Finish TeXmlGui.clear()");
 }
 
 } // end namespace Tetraedge


Commit: 501b00ebe79d75682db0c43770a7ec15c831e070
    https://github.com/scummvm/scummvm/commit/501b00ebe79d75682db0c43770a7ec15c831e070
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: WIP. Fixed some callbacks, more bits of the game working.

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/billboard.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/document.h
    engines/tetraedge/game/documents_browser.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.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/question2.cpp
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_button_layout.h
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_music.cpp
    engines/tetraedge/te/te_music.h
    engines/tetraedge/te/te_sound_manager.cpp
    engines/tetraedge/te/te_sound_manager.h
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_tiled_texture.cpp
    engines/tetraedge/to_lua.cpp
    engines/tetraedge/to_lua.h


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index f9d0573a2fb..dfd1386998f 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -37,6 +37,7 @@
 #include "tetraedge/te/te_renderer.h"
 #include "tetraedge/te/te_font3.h"
 #include "tetraedge/te/te_input_mgr.h"
+#include "tetraedge/te/te_sound_manager.h"
 
 //#define DUMP_LAYOUTS 1
 
@@ -312,7 +313,7 @@ bool Application::run() {
 
 		renderer->reset();
 		game->update();
-		//_soundManager->update(soundmgr);
+		g_engine->getSoundManager()->update();
 		performRender();
 		if (game->_returnToMainMenu) {
 			game->leave(true);
diff --git a/engines/tetraedge/game/billboard.cpp b/engines/tetraedge/game/billboard.cpp
index 2d2d99156a1..0d37ac6dcec 100644
--- a/engines/tetraedge/game/billboard.cpp
+++ b/engines/tetraedge/game/billboard.cpp
@@ -57,7 +57,6 @@ void Billboard::calcVertex() {
 	const TeMatrix4x4 camTotalTransform = camProjMatrix * camWorldInverse;
 	TeMatrix4x4 camTotalInverse = camTotalTransform;
 	camTotalInverse.inverse();
-	camTotalInverse.scale(TeVector3f32(-1.0, -1.0, 1.0));
 
 	TeVector3f32 posvec(0.0f, 0.0f, _pos.z());
 	if (_hasPos2) {
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index b3d84b95cc9..8ef4dac0efa 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -489,16 +489,20 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 	// Move any objects attached to the bone
 	for (Object3D *obj : game->scene().object3Ds()) {
 		if (obj->_onCharName == _model->name() && boneName == obj->_onCharBone) {
-			obj->model()->setVisible(true);
-			TeMatrix4x4 objmatrix = boneMatrix;
-			objmatrix.scale(obj->_objScale);
-			objmatrix.rotate(obj->_objRotation);
-			objmatrix.scale(obj->_objScale);
-			objmatrix.translate(obj->_objTranslation);
-			obj->model()->forceMatrix(objmatrix);
-			obj->model()->setPosition(_model->position());
-			obj->model()->setRotation(_model->rotation());
-			obj->model()->setScale(_model->scale());
+			if (_model->anim()->curFrame2() >= obj->_startFrame
+					&& _model->anim()->curFrame2() <= obj->_endFrame) {
+				obj->model()->setVisible(true);
+				TeMatrix4x4 objmatrix = boneMatrix;
+				objmatrix.scale(obj->_objScale);
+				objmatrix.rotate(obj->_objRotation);
+				objmatrix.translate(obj->_objTranslation);
+				obj->model()->forceMatrix(objmatrix);
+				obj->model()->setRotation(_model->rotation());
+				obj->model()->setPosition(_model->position());
+				obj->model()->setScale(_model->scale());
+			} else {
+				obj->model()->setVisible(false);
+			}
 		}
 	}
 
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index b524c8c8691..e403bbfd098 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -173,6 +173,7 @@ public:
 	bool lookingAtTallThing() const { return _lookingAtTallThing; }
 	void setLookingAtTallThing(bool val) { _lookingAtTallThing = val; }
 	TeIntrusivePtr<TeBezierCurve> curve() { return _curve; }
+	void setRecallageY(bool val) { _recallageY = val; }
 
 private:
 	float _walkCurveStart;
diff --git a/engines/tetraedge/game/document.h b/engines/tetraedge/game/document.h
index 33647fe574c..688fb029381 100644
--- a/engines/tetraedge/game/document.h
+++ b/engines/tetraedge/game/document.h
@@ -37,7 +37,8 @@ public:
 	~Document() {
 		unload();
 		if (parent()) {
-			// TODO: do something with parent here.
+			parent()->removeChild(this);
+			setParent(nullptr);
 		}
 	}
 
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
index 57c288a7ea3..73e2881e1e8 100644
--- a/engines/tetraedge/game/documents_browser.cpp
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -213,7 +213,29 @@ void DocumentsBrowser::showDocument(const Common::String &docName, long startPag
 
 void DocumentsBrowser::unload() {
 	hideDocument();
-	error("TODO: Implement DocumentsBrowser::unload");
+	int pageno = 0;
+	while (true) {
+		Common::String pageName = Common::String::format("page%d", pageno);
+		TeLayout *page = _gui1.layout(pageName);
+		if (!page)
+			break;
+		int slotno = 0;
+		while (true) {
+			Common::String pageSlotName = Common::String::format("page%dSlot%d", pageno, slotno);
+			TeLayout *slot = _gui1.layout(pageSlotName);
+			if (!slot)
+				break;
+			for (unsigned int i = 0; i < slot->childCount(); i++) {
+				Te3DObject2 *child = slot->child(i);
+				Document *doc = dynamic_cast<Document *>(child);
+				if (doc)
+					delete doc;
+			}
+			slotno++;
+		}
+		pageno++;
+	}
+	_gui1.unload();
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index d9bf03e1ccc..34c06024136 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -274,10 +274,11 @@ void Game::enter(bool newgame) {
 	_score = 0;
 	Application *app = g_engine->getApplication();
 	app->visualFade().init();
-	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, -1000.0f));
+	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, -10000.0f));
 	g_engine->getInputMgr()->_mouseLUpSignal.insert(callbackptr);
 	_movePlayerCharacterDisabled = false;
-	warning("TODO: Game::enter set some other field here");
+	// TODO? Set character mouse move event no to -1
+	_isCharacterIdle = true;
 	_sceneCharacterVisibleFromLoad = false;
 	Character::loadSettings("models/ModelsSettings.xml");
 	Object3D::loadSettings("objects/ObjectsSettings.xml");
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index 3481d25bf44..7d19600e196 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -193,6 +193,7 @@ public:
 	void setPosPlayer(const TeVector3f32 &pos) { _posPlayer = pos; }
 	TeTimer &walkTimer() { return _walkTimer; }
 	void setExitZone(const Common::String &zone) { _exitZone = zone; }
+	Common::RandomSource &randomSource() { return _randomSource; }
 
 private:
 	bool _luaShowOwnerError;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index f61c87d348c..bfd7d0251ab 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -774,7 +774,7 @@ void InGameScene::loadBlockers() {
 		_blockers[i]._s = Te3DObject2::deserializeString(blockersfile);
 		TeVector2f32::deserialize(blockersfile, _blockers[i]._pts[0]);
 		TeVector2f32::deserialize(blockersfile, _blockers[i]._pts[1]);
-		_blockers[i]._x = 1;
+		_blockers[i]._enabled = true;
 	}
 
 	if (hasHeader) {
@@ -787,7 +787,7 @@ void InGameScene::loadBlockers() {
 			for (unsigned int j = 0; j < 4l; j++) {
 				TeVector2f32::deserialize(blockersfile, _rectBlockers[i]._pts[j]);
 			}
-			_rectBlockers[i]._x = 1;
+			_rectBlockers[i]._enabled = true;
 		}
 	}
 }
@@ -990,6 +990,13 @@ void InGameScene::unloadCharacter(const Common::String &name) {
 void InGameScene::unloadObject(const Common::String &name) {
 	for (unsigned int i = 0; i < _object3Ds.size(); i++) {
 		if (_object3Ds[i]->model()->name() == name) {
+			// Remove from the scene models.
+			for (unsigned int j = 0; j < models().size(); j++) {
+				if (models()[j] == _object3Ds[i]->model())	{
+					models().remove_at(j);
+					break;
+				}
+			}
 			_object3Ds[i]->deleteLater();
 			_object3Ds.remove_at(i);
 			break;
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 690e76ec861..c70f0e8c57e 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -193,6 +193,7 @@ public:
 	void setCurve(TeIntrusivePtr<TeBezierCurve> &c) { _curve = c; }
 	Common::Array<TeIntrusivePtr<TeModel>> &zoneModels() { return _zoneModels; }
 	Common::Array<TeRectBlocker> &rectBlockers() { return _rectBlockers; }
+	Common::Array<TeBlocker> &blockers() { return _blockers; }
 	Common::Array<Object3D *> object3Ds() { return _object3Ds; }
 
 private:
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 514fd08bb0b..2346da3cf25 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -618,6 +618,35 @@ static int tolua_ExportedFunctions_SetCharacterAnimation00(lua_State *L) {
 	error("#ferror in function 'SetCharacterAnimation': %d %d %s", err.index, err.array, err.type);
 }
 
+static int tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isboolean(L, 3, 1, &err) && tolua_isboolean(L, 4, 1, &err)
+		&& tolua_isnumber(L, 5, 1, &err) && tolua_isnumber(L, 6, 1, &err)
+		&& tolua_isnoobj(L, 7, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		bool b1 = tolua_toboolean(L, 3, 1);
+		bool b2 = tolua_toboolean(L, 4, 0);
+		double f3 = tolua_tonumber(L, 5, -1.0);
+		double f4 = tolua_tonumber(L, 6, 9999.0);
+		SetCharacterAnimation(s1, s2, b1, b2, (int)f3, (int)f4);
+		Game::YieldedCallback cb;
+		cb._luaFnName = "OnCharacterAnimationFinished";
+		cb._luaParam = s1;
+		cb._luaParam2 = s2;
+		cb._luaThread = TeLuaThread::threadFromState(L);
+		Game *game = g_engine->getGame();
+		for (const auto &gamecb : game->yieldedCallbacks()) {
+			if (gamecb._luaFnName == cb._luaFnName && gamecb._luaParam == s1 && gamecb._luaParam2 == s2)
+				error("SetCharacterAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+		}
+		game->yieldedCallbacks().push_back(cb);
+		return cb._luaThread->yield();
+	}
+	error("#ferror in function 'SetCharacterAnimationAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
+}
+
 static void BlendCharacterAnimation(const Common::String &charname, const Common::String &animname, float blendAmount, bool repeat, bool b2) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charname);
@@ -1048,7 +1077,7 @@ static void EnableRectBlocker(uint offset, bool enabled) {
 	if (game->scene().rectBlockers().size() < offset)
 		error("invalid rectblocker offset %d", offset);
 
-	game->scene().rectBlockers()[offset]._x = enabled;
+	game->scene().rectBlockers()[offset]._enabled = enabled;
 }
 
 static int tolua_ExportedFunctions_EnableRectBlocker00(lua_State *L) {
@@ -1062,6 +1091,26 @@ static int tolua_ExportedFunctions_EnableRectBlocker00(lua_State *L) {
 	error("#ferror in function 'EnableRectBlocker': %d %d %s", err.index, err.array, err.type);
 }
 
+static void EnableBlocker(uint offset, bool enabled) {
+	Game *game = g_engine->getGame();
+	if (game->scene().blockers().size() < offset)
+		error("invalid blocker offset %d", offset);
+
+	game->scene().blockers()[offset]._enabled = enabled;
+}
+
+static int tolua_ExportedFunctions_EnableBlocker00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isboolean(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		double d1 = tolua_tonumber(L, 1, 0.0f);
+		bool b1 = tolua_toboolean(L, 2, 0);
+		EnableBlocker((uint)d1, b1);
+		return 0;
+	}
+	error("#ferror in function 'EnableBlocker': %d %d %s", err.index, err.array, err.type);
+}
+
+
 static void AddAnchorZone(const Common::String &s1, const Common::String &s2, float f1) {
 	if (s1.empty())
 		return;
@@ -1144,6 +1193,62 @@ static int tolua_ExportedFunctions_SetCharacterLookChar00(lua_State *L) {
 	error("#ferror in function 'SetCharacterLookChar': %d %d %s", err.index, err.array, err.type);
 }
 
+static uint Random(uint max) {
+	return g_engine->getGame()->randomSource().getRandomNumber(max);
+}
+
+static int tolua_ExportedFunctions_Random00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		double d1 = tolua_tonumber(L, 1, 0.0);
+		unsigned int result = Random(d1);
+		tolua_pushnumber(L, result);
+		return 1;
+	}
+	error("#ferror in function 'Random': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetCharacterMeshVisible(const Common::String &charName, const Common::String &meshName, bool val) {
+	Character *character = g_engine->getGame()->scene().character(charName);
+	if (character) {
+		character->_model->setVisibleByName(meshName, val);
+	} else {
+		error("[SetCharacterMeshVisible] Character not found %s", charName.c_str());
+	}
+}
+
+static int tolua_ExportedFunctions_SetCharacterMeshVisible00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 1, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		bool b = tolua_toboolean(L, 3, 0);
+		SetCharacterMeshVisible(s1, s2, b);
+		return 0;
+	}
+	error("#ferror in function 'SetRecallageY': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetRecallageY(const Common::String &charName, bool val) {
+	Character *character = g_engine->getGame()->scene().character(charName);
+	if (character) {
+		character->setRecallageY(val);
+	} else {
+		error("[SetRecallageY] Character not found %s", charName.c_str());
+	}
+}
+
+static int tolua_ExportedFunctions_SetRecallageY00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 1, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s(tolua_tostring(L, 1, nullptr));
+		bool b = tolua_toboolean(L, 2, 0);
+		SetRecallageY(s, b);
+		return 0;
+	}
+	error("#ferror in function 'SetRecallageY': %d %d %s", err.index, err.array, err.type);
+}
+
 static void DisabledZone(const Common::String &s1, bool b) {
 	Game *game = g_engine->getGame();
 	if (!game->scene().markerGui().loaded())
@@ -1525,8 +1630,8 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "ShowObject", tolua_ExportedFunctions_ShowObject00);
 	// tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00); // Never used
 	tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);
-	//tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00); // Never used
-	//tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00); // Never used
+	// tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00); // Never used
+	// tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00); // Never used
 	tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
 	tolua_function(L, "PlayMovie", tolua_ExportedFunctions_PlayMovie00);
 	/*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);
@@ -1590,8 +1695,8 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetCharacterRotation", tolua_ExportedFunctions_SetCharacterRotation00);
 	tolua_function(L, "SetCharacterOrientation", tolua_ExportedFunctions_SetCharacterOrientation00);
 	tolua_function(L, "SetCharacterAnimation", tolua_ExportedFunctions_SetCharacterAnimation00);
-	/*tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
-				 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);*/
+	tolua_function(L, "SetCharacterAnimationAndWaitForEnd",
+				 tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00);
 	tolua_function(L, "BlendCharacterAnimation", tolua_ExportedFunctions_BlendCharacterAnimation00);
 	tolua_function(L, "BlendCharacterAnimationAndWaitForEnd",
 				 tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00);
@@ -1637,25 +1742,25 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "Save", tolua_ExportedFunctions_Save00);
 	tolua_function(L, "Wait", tolua_ExportedFunctions_Wait00);
 	tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00);
-	tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00);
+	tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00); // Unused
 	tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
-	tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);
-	tolua_function(L, "BFGRateImmediately", tolua_ExportedFunctions_BFGRateImmediately00);
-	tolua_function(L, "BFGReportEvent", tolua_ExportedFunctions_BFGReportEvent00);
-	tolua_function(L, "BFGReportEventWithValue", tolua_ExportedFunctions_BFGReportEventWithValue00);
-	tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00);*/
+	tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);*/
+	// tolua_function(L, "BFGRateImmediately", tolua_ExportedFunctions_BFGRateImmediately00); // Unused
+	// tolua_function(L, "BFGReportEvent", tolua_ExportedFunctions_BFGReportEvent00); // Unused
+	// tolua_function(L, "BFGReportEventWithValue", tolua_ExportedFunctions_BFGReportEventWithValue00); // Unused
+	// tolua_function(L, "BFGReachedFreemiumLimit", tolua_ExportedFunctions_BFGReachedFreemiumLimit00); // Unused
 	tolua_function(L, "TestFileFlagSystemFlag", tolua_ExportedFunctions_TestFileFlagSystemFlag00);
 	// tolua_function(L, "PrintDebugMessage", tolua_ExportedFunctions_PrintDebugMessage00); // Unused
 	tolua_function(L, "ExitZone", tolua_ExportedFunctions_ExitZone00);
 	tolua_function(L, "EnableRectBlocker", tolua_ExportedFunctions_EnableRectBlocker00);
-	/*tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);*/
+	tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);
 	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
 	tolua_function(L, "ActivateAnchorZone", tolua_ExportedFunctions_ActivateAnchorZone00);
 	//tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00); // Unused
 	tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
-	/*tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
+	tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
 	tolua_function(L, "SetCharacterMeshVisible", tolua_ExportedFunctions_SetCharacterMeshVisible00);
-	tolua_function(L, "SetRecallageY", tolua_ExportedFunctions_SetRecallageY00);*/
+	tolua_function(L, "SetRecallageY", tolua_ExportedFunctions_SetRecallageY00);
 	// tolua_function(L, "IsFreemiumUnlocked", tolua_ExportedFunctions_IsFreemiumUnlocked00); // Unused
 	// tolua_function(L, "ReachedFreemiumLimit", tolua_ExportedFunctions_ReachedFreemiumLimit00); // Unused
 	tolua_function(L, "AddUnrecalAnim", tolua_ExportedFunctions_AddUnrecalAnim00);
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index 23609ec05cd..4f3b46ec179 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -28,7 +28,10 @@ namespace Tetraedge {
 /*static*/ Common::HashMap<Common::String, Object3D::ObjectSettings> *Object3D::_objectSettings = nullptr;
 
 
-Object3D::Object3D() : _translateTime(-1), _rotateTime(-1), _objScale(1.0f, 1.0f, 1.0f) {
+// start and end frames not initialized in original, but to guarantee we don't use
+// uninitialized values we set it here.
+Object3D::Object3D() : _translateTime(-1), _rotateTime(-1), _objScale(1.0f, 1.0f, 1.0f),
+_startFrame(-1), _endFrame(9999) {
 }
 
 bool Object3D::loadModel(const Common::String &name) {
diff --git a/engines/tetraedge/game/question2.cpp b/engines/tetraedge/game/question2.cpp
index 1e86f08ca1d..839fe5e5ddc 100644
--- a/engines/tetraedge/game/question2.cpp
+++ b/engines/tetraedge/game/question2.cpp
@@ -140,7 +140,7 @@ void Question2::Answer::load(const Common::String &name, const Common::String &l
 	TeButtonLayout *answerButton = _gui.buttonLayout("answer");
 	if (answerButton) {
 		answerButton->onMouseClickValidated().add(this, &Question2::Answer::onButtonValidated);
-		answerButton->_someClickFlag = false;
+		answerButton->_ignoreMouseEvents = false;
 	}
 }
 
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index d06768c6bc5..68760a98069 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -41,7 +41,7 @@ namespace Tetraedge {
 
 TeButtonLayout::TeButtonLayout() :
 _currentState(BUTTON_STATE_UP), _clickPassThrough(false), _validationSoundVolume(1.0),
-_someClickFlag(false), _doubleValidationProtectionEnabled(true), _upLayout(nullptr),
+_ignoreMouseEvents(false), _doubleValidationProtectionEnabled(true), _upLayout(nullptr),
 _downLayout(nullptr), _rolloverLayout(nullptr), _disabledLayout(nullptr),
 _hitZoneLayout(nullptr)
 {
@@ -71,7 +71,6 @@ TeButtonLayout::~TeButtonLayout() {
 	inputmgr->_mouseLDownSignal.remove(_onMouseLeftDownCallback);
 	inputmgr->_mouseLUpSignal.remove(_onMouseLeftUpCallback);
 	inputmgr->_mouseLUpSignal.remove(_onMouseLeftUpMaxPriorityCallback);
-
 }
 
 bool TeButtonLayout::isMouseIn(const TeVector2s32 &mouseloc) {
@@ -83,7 +82,7 @@ bool TeButtonLayout::isMouseIn(const TeVector2s32 &mouseloc) {
 }
 
 bool TeButtonLayout::onMouseLeftDown(const Common::Point &pt) {
-	if (!worldVisible() || _currentState == BUTTON_STATE_DISABLED)
+	if (!worldVisible() || _currentState == BUTTON_STATE_DISABLED || _ignoreMouseEvents)
 		return false;
 
 	// Note: This doesn't exactly reproduce the original behavior, it's
@@ -95,9 +94,10 @@ bool TeButtonLayout::onMouseLeftDown(const Common::Point &pt) {
 
 	enum State newState = _currentState;
 	switch (_currentState) {
-		break;
 	case BUTTON_STATE_DOWN:
 		newState = BUTTON_STATE_UP;
+		/*
+		// TODO: should this be a click?
 		if (mouseIn) {
 			debug("mouse clicked button '%s' (from leftdown)", name().c_str());
 			if (!_validationSound.empty()) {
@@ -107,11 +107,12 @@ bool TeButtonLayout::onMouseLeftDown(const Common::Point &pt) {
 			setState(newState);
 			_onMouseClickValidatedSignal.call();
 			return !_clickPassThrough;
-		}
+		}*/
 		break;
 	case BUTTON_STATE_ROLLOVER:
 	case BUTTON_STATE_UP:
-		newState = BUTTON_STATE_DOWN;
+		if (mouseIn)
+			newState = BUTTON_STATE_DOWN;
 		break;
 	case BUTTON_STATE_DISABLED:
 		break;
@@ -137,7 +138,6 @@ bool TeButtonLayout::onMouseLeftUp(const Common::Point &pt) {
 
 	enum State newState = _currentState;
 	switch (_currentState) {
-		break;
 	case BUTTON_STATE_DOWN:
 		newState = BUTTON_STATE_UP;
 		if (mouseIn) {
@@ -165,7 +165,7 @@ bool TeButtonLayout::onMouseLeftUp(const Common::Point &pt) {
 }
 
 bool TeButtonLayout::onMousePositionChanged(const Common::Point &pt) {
-	if (!worldVisible() || _someClickFlag)
+	if (!worldVisible() || _ignoreMouseEvents)
 		return false;
 
 	// Note: This doesn't exactly reproduce the original behavior, it's
@@ -176,14 +176,12 @@ bool TeButtonLayout::onMousePositionChanged(const Common::Point &pt) {
 	switch (_currentState) {
 	case BUTTON_STATE_UP:
 		if (mouseIn) {
-			//debug("mouse entered button '%s'", name().c_str());
 			newState = BUTTON_STATE_ROLLOVER;
 		}
 		break;
 	case BUTTON_STATE_DOWN:
 	case BUTTON_STATE_ROLLOVER:
 		if (!mouseIn) {
-			//debug("mouse exit button '%s'", name().c_str());
 			newState = BUTTON_STATE_UP;
 		}
 		break;
@@ -324,7 +322,7 @@ void TeButtonLayout::setPosition(const TeVector3f32 &pos) {
 			// probably not needed as we reimplememted how this works.
 			error("TODO: Implement setPosition logic for up/down state");
 		}
-		if (_someClickFlag) {
+		if (!_ignoreMouseEvents) {
 			setState(somethingCount ? BUTTON_STATE_DOWN : BUTTON_STATE_UP);
 		}
 	}
diff --git a/engines/tetraedge/te/te_button_layout.h b/engines/tetraedge/te/te_button_layout.h
index ebeaab28ae0..908e73ff1ab 100644
--- a/engines/tetraedge/te/te_button_layout.h
+++ b/engines/tetraedge/te/te_button_layout.h
@@ -92,7 +92,7 @@ public:
 	TeSignal0Param &onButtonChangedToStateDownSignal() { return _onButtonChangedToStateDownSignal; };
 	TeSignal0Param &onButtonChangedToStateRolloverSignal() { return _onButtonChangedToStateRolloverSignal; };
 
-	bool _someClickFlag;
+	bool _ignoreMouseEvents;
 	TeLayout *_upLayout;
 	TeLayout *_downLayout;
 
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index f98231f7a4a..4e3efb5fbc9 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -43,13 +43,13 @@ namespace micropather {
 struct TeBlocker {
 	Common::String _s;
 	TeVector2f32 _pts[2];
-	long _x;
+	bool _enabled;
 };
 
 struct TeRectBlocker {
 	Common::String _s;
 	TeVector2f32 _pts[4];
-	long _x;
+	bool _enabled;
 };
 
 class TeFreeMoveZoneGraph;
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index 617b44779f5..e834c50304d 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -78,7 +78,7 @@ void TeImage::fill(byte val) {
 void TeImage::fill(byte r, byte g, byte b, byte a) {
 	Common::Rect wholeSurf(0, 0, w, h);
 
-	uint32 col = ((uint32)r << format.rShift) | ((uint32)g << format.gShift) | ((uint32)b << format.bShift) | (uint32)a << format.aShift;
+	uint32 col = ((uint32)r << format.rShift) | ((uint32)g << format.gShift) | ((uint32)b << format.bShift) | ((uint32)a << format.aShift);
 	Graphics::Surface::fillRect(wholeSurf, col);
 }
 
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 4c8bfdbb9f1..111775b8e15 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -552,11 +552,11 @@ void TeModel::setQuad(const TeIntrusivePtr<Te3DTexture> &tex, const Common::Arra
 	mesh.defaultMaterial(tex);
 
 	for (int i = 0; i < 2; i++) {
-		float f = (i == 0 ? 1.0f : 0.0f);
+		float f = (i == 0 ? 0.0f : 1.0f);
 		for (int j = 0; j < 2; j++) {
 			int index = i * 2 + j;
 			mesh.setVertex(index, verts[i * 2 + j]);
-			mesh.setTextureUV(index, TeVector2f32(f, (j == 0 ? 1.0f : 0.0f)));
+			mesh.setTextureUV(index, TeVector2f32(f, (j == 0 ? 0.0f : 1.0f)));
 			mesh.setIndex(index, index);
 			if (col.a() != 0)
 				mesh.setColor(index, col);
diff --git a/engines/tetraedge/te/te_music.cpp b/engines/tetraedge/te/te_music.cpp
index 41fb9eb2f36..3f2e0e07875 100644
--- a/engines/tetraedge/te/te_music.cpp
+++ b/engines/tetraedge/te/te_music.cpp
@@ -24,6 +24,7 @@
 #include "audio/mixer.h"
 #include "audio/decoders/vorbis.h"
 #include "tetraedge/tetraedge.h"
+#include "tetraedge/te/te_sound_manager.h"
 #include "tetraedge/te/te_music.h"
 #include "tetraedge/te/te_core.h"
 
@@ -31,6 +32,18 @@ namespace Tetraedge {
 
 TeMusic::TeMusic() : _repeat(true), _isPlaying(false), _currentData(0),
 _volume(1.0), _isPaused(true), _channelName("music"), _sndHandleValid(false) {
+	g_engine->getSoundManager()->musics().push_back(this);
+}
+
+TeMusic::~TeMusic() {
+	close();
+	Common::Array<TeMusic *> &m = g_engine->getSoundManager()->musics();
+	for (unsigned int i = 0; i < m.size(); i++) {
+		if (m[i] == this) {
+			m.remove_at(i);
+			break;
+		}
+	}
 }
 
 void TeMusic::pause() {
@@ -54,13 +67,14 @@ bool TeMusic::play() {
 		delete streamfile;
 		return false;
 	}
-
 	Audio::AudioStream *stream = Audio::makeVorbisStream(streamfile, DisposeAfterUse::YES);
 	byte vol = round(_volume * 255.0);
 	int channelId = _channelName.hash();
 	Audio::Mixer *mixer = g_system->getMixer();
 	mixer->playStream(Audio::Mixer::kMusicSoundType, &_sndHandle, stream, channelId, vol);
 	_sndHandleValid = true;
+	_isPaused = false;
+	_isPlaying = true;
 	if (_repeat)
 		mixer->loopChannel(_sndHandle);
 	return true;
@@ -98,7 +112,8 @@ void TeMusic::resume() {
 
 void TeMusic::stop() {
 	_mutex.lock();
-	_isStopped = true;
+	_isPlaying = false;
+	_isPaused = false;
 	_mutex.unlock();
 	/* original does this here.. probably not needed as mixer
 	 does it for us.
@@ -130,7 +145,7 @@ void TeMusic::entry() {
 
 bool TeMusic::isPlaying() {
 	_mutex.lock();
-	bool retval = _isPlaying;
+	bool retval = _isPlaying && !_isPaused;
 	_mutex.unlock();
 	return retval;
 }
@@ -170,8 +185,22 @@ void TeMusic::setFilePath(const Common::String &name) {
 }
 
 void TeMusic::update() {
-	/* Probably not needed in ScummVM? Handled by the mixer. */
-	error("TODO: Implement me");
+	/* Do callback and update flags when sounds stop */
+	bool hasStopped = false;
+	_mutex.lock();
+	if (_isPlaying && !_isPaused && _sndHandleValid && !g_system->getMixer()->isSoundHandleActive(_sndHandle))
+		hasStopped = true;
+
+	if (hasStopped) {
+		_isPaused = false;
+		_isPlaying = false;
+		_sndHandleValid = false;
+	}
+	_mutex.unlock();
+
+	if (hasStopped) {
+		_onStopSignal.call();
+	}
 }
 
 void TeMusic::volume(float vol) {
diff --git a/engines/tetraedge/te/te_music.h b/engines/tetraedge/te/te_music.h
index 68482da2bb1..cfa0e620b0f 100644
--- a/engines/tetraedge/te/te_music.h
+++ b/engines/tetraedge/te/te_music.h
@@ -35,6 +35,7 @@ namespace Tetraedge {
 class TeMusic : public TeResource {
 public:
 	TeMusic();
+	~TeMusic();
 
 	void close() {
 		stop();
@@ -72,9 +73,10 @@ private:
 
 	bool _repeat;
 	byte _currentData;
+
 	bool _isPlaying;
 	bool _isPaused;
-	bool _isStopped;
+
 	float _volume;
 
 	Audio::SoundHandle _sndHandle;
diff --git a/engines/tetraedge/te/te_sound_manager.cpp b/engines/tetraedge/te/te_sound_manager.cpp
index 42588e7ed45..2f30ef256c3 100644
--- a/engines/tetraedge/te/te_sound_manager.cpp
+++ b/engines/tetraedge/te/te_sound_manager.cpp
@@ -29,13 +29,13 @@
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_sound_manager.h"
+#include "tetraedge/te/te_music.h"
 
 namespace Tetraedge {
 
 TeSoundManager::TeSoundManager() {
 }
 
-
 void TeSoundManager::playFreeSound(const Common::Path &path, float vol, const Common::String &channel) {
 	TeCore *core = g_engine->getCore();
 	Common::Path modPath = core->findFile(path);
@@ -75,5 +75,10 @@ void TeSoundManager::stopFreeSound(const Common::String &name) {
 	_handles.erase(name);
 }
 
+void TeSoundManager::update() {
+	for (auto &m : _musics)
+		m->update();
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_sound_manager.h b/engines/tetraedge/te/te_sound_manager.h
index dbce36199cd..227a7ce29d4 100644
--- a/engines/tetraedge/te/te_sound_manager.h
+++ b/engines/tetraedge/te/te_sound_manager.h
@@ -28,6 +28,8 @@
 
 namespace Tetraedge {
 
+class TeMusic;
+
 class TeSoundManager {
 public:
 	TeSoundManager();
@@ -35,8 +37,14 @@ public:
 	void playFreeSound(const Common::Path &path, float vol, const Common::String &channel);
 	void stopFreeSound(const Common::String &channel);
 
+	void update();
+
+	Common::Array<TeMusic *> &musics() { return _musics; }
+
 private:
 	Common::HashMap<Common::String, Audio::SoundHandle> _handles;
+
+	Common::Array<TeMusic *> _musics;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index 114012dfca7..a8da12c91ca 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -75,7 +75,7 @@ void TeTextBase2::build() {
 	TeImage img;
 	Common::SharedPtr<TePalette> nullpal;
 	img.create(_size._x, _size._y, nullpal, TeImage::RGBA8);
-	img.fill(0, 0, 0, 0);
+	img.fill(_globalColor.r(), _globalColor.g(), _globalColor.b(), 0);
 
 	for (unsigned int i = 0; i < _wrappedLines.size(); i++) {
 		drawLine(img, _wrappedLines[i], lineoffsets[i]);
@@ -94,7 +94,7 @@ void TeTextBase2::build() {
 	_mesh.setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
 	_mesh.defaultMaterial(texture);
 	// FIXME: Original uses BLEND, but we need MODULATE to get right colors?
-	_mesh.setglTexEnv(GL_MODULATE);
+	//_mesh.setglTexEnv(GL_MODULATE);
 	_mesh.setShouldDraw(true);
 	_mesh.setColor(_globalColor);
 	_mesh.setVertex(0, TeVector3f32(_size._x * -0.5f, _size._y * -0.5f, 0.0f));
@@ -197,8 +197,10 @@ void TeTextBase2::drawEmptyChar(unsigned int offset) {
 void TeTextBase2::drawLine(TeImage &img, const Common::String &str, int yoffset) {
 	TeIntrusivePtr<TeFont3> font = _fonts[0];
 
-	// TODO: Add multi-color support if needed.
-	font->draw(img, str, _fontSize, yoffset, _globalColor, _alignStyle);
+	// TODO: Add multi-color support if needed?
+	// We set black here as the color is set on the mesh and we use
+	// MODULATE blend in build()
+	font->draw(img, str, _fontSize, yoffset, TeColor(0, 0, 0, 255), _alignStyle);
 }
 
 unsigned int TeTextBase2::endOfWord(unsigned int offset) const {
diff --git a/engines/tetraedge/te/te_tiled_texture.cpp b/engines/tetraedge/te/te_tiled_texture.cpp
index d07804297e0..593055a2ff1 100644
--- a/engines/tetraedge/te/te_tiled_texture.cpp
+++ b/engines/tetraedge/te/te_tiled_texture.cpp
@@ -176,11 +176,12 @@ TeTiledTexture::Tile *TeTiledTexture::tile(const TeVector2s32 &loc) {
 }
 
 void TeTiledTexture::update(const TeImage &image) {
-	//if (image.w == _totalSize._x && image.h == _totalSize._y) {
-	//	error("FIXME: Implement fast path of TeTiledTexture::update");
-	//} else {
+	if (image.w == _totalSize._x && image.h == _totalSize._y) {
+		// TODO: Implement fast path of TeTiledTexture::update
 		load(image);
-	//}
+	} else {
+		load(image);
+	}
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/to_lua.cpp b/engines/tetraedge/to_lua.cpp
index 31280d99aaf..979732d03d4 100644
--- a/engines/tetraedge/to_lua.cpp
+++ b/engines/tetraedge/to_lua.cpp
@@ -438,6 +438,10 @@ void tolua_pushboolean(lua_State *L, bool val) {
 	lua_pushboolean(L, val);
 }
 
+void tolua_pushnumber(lua_State *L, double val) {
+	lua_pushnumber(L, val);
+}
+
 } // end namespace ToLua
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/to_lua.h b/engines/tetraedge/to_lua.h
index 0cee065adbf..bd8d8982d2a 100644
--- a/engines/tetraedge/to_lua.h
+++ b/engines/tetraedge/to_lua.h
@@ -68,6 +68,7 @@ void* tolua_tousertype(lua_State *L, int narg, void *def);
 int tolua_toboolean(lua_State *L, int narg, int def);
 
 void tolua_pushboolean(lua_State *L, bool val);
+void tolua_pushnumber(lua_State *L, double val);
 
 } // end namespace ToLua
 


Commit: 791575b0c22eb0f17df9c95d6bc30073ce80d46c
    https://github.com/scummvm/scummvm/commit/791575b0c22eb0f17df9c95d6bc30073ce80d46c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: WIP: Fix more interactions

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/object3d.cpp
    engines/tetraedge/game/object_settings_xml_parser.cpp
    engines/tetraedge/game/question2.cpp
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_lua_context.cpp
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_lua_thread.cpp
    engines/tetraedge/te/te_matrix4x4.cpp
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_mesh.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_music.cpp
    engines/tetraedge/te/te_music.h
    engines/tetraedge/te/te_sound_manager.cpp
    engines/tetraedge/te/te_sound_manager.h


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index dfd1386998f..eb7000ea85b 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -62,7 +62,11 @@ _drawShadows(true) {
 	_firstZone = tempGui.value("firstZone").toString();
 	_firstScene = tempGui.value("firstScene").toString();
 
-	// TODO: Configure sound manager here?
+	TeSoundManager *soundmgr = g_engine->getSoundManager();
+	soundmgr->setChannelVolume("sfx", 0.7);
+	soundmgr->setChannelVolume("music", 0.7);
+	soundmgr->setChannelVolume("dialog", 0.7);
+	soundmgr->setChannelVolume("video", 0.7);
 	// TODO: Configure freemium things here?
 
 	// Note: original has an app run timer, but it's never used?
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 8ef4dac0efa..a3cf7ab5da8 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -30,6 +30,7 @@
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/character_settings_xml_parser.h"
 #include "tetraedge/te/te_model_animation.h"
+#include "tetraedge/te/te_core.h"
 
 namespace Tetraedge {
 
@@ -130,8 +131,9 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 		return _cache.getVal(pathStr);
 
 	TeIntrusivePtr<TeModelAnimation> modelAnim = new TeModelAnimation();
-	if (!modelAnim->load(path)) {
-		warning("Failed to load anim %s", path.toString().c_str());
+	Common::Path foundPath = g_engine->getCore()->findFile(path);
+	if (!modelAnim->load(foundPath)) {
+		warning("Failed to load anim %s", foundPath.toString().c_str());
 	}
 
 	_cache.setVal(pathStr, modelAnim);
@@ -362,7 +364,8 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 	return true;
 }
 
-/*static*/ bool Character::loadSettings(const Common::String &path) {
+/*static*/
+bool Character::loadSettings(const Common::String &path) {
 	CharacterSettingsXmlParser parser;
 	parser.setAllowText();
 	if (_globalCharacterSettings)
@@ -405,7 +408,7 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 	if (!parser.parse())
 		error("Character::loadSettings: Can't parse %s", path.c_str());
 
-	return false;
+	return true;
 }
 
 bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneMatrix) {
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 34c06024136..8180c981bff 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -485,8 +485,9 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		return false;
 	}
 
-	if (!_gameSounds.empty())
-		warning("TODO: Game::initWarp: stop game sounds");
+	for (auto &sound : _gameSounds) {
+		sound->setRetain(false);
+	}
 
 	if (logicLuaExists) {
 		_luaContext.addBindings(LuaBinds::LuaOpenBinds);
@@ -567,7 +568,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	}
 
 	TeCheckboxLayout *markersCheckbox = _inGameGui.checkboxLayout("markersVisibleButton");
-	markersCheckbox->setVisible(!_markersVisible);
+	markersCheckbox->setState(_markersVisible? TeCheckboxLayout::CheckboxStateActive : TeCheckboxLayout::CheckboxStateUnactive);
 	markersCheckbox->onStateChangedSignal().add(this, &Game::onMarkersVisible);
 
 	initNoScale();
@@ -625,9 +626,14 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_luaScript.execute("OnSelectedObject", _inventory.selectedObject());
 	}
 
-	for (GameSound *sound : _gameSounds) {
-		sound->stop();
-		sound->deleteLater();
+	
+	for (unsigned int i = 0; i < _gameSounds.size(); i++) {
+		if (_gameSounds[i]->retain())
+			continue;
+		_gameSounds[i]->stop();
+		_gameSounds[i]->deleteLater();
+		_gameSounds.remove_at(i);
+		i--;
 	}
 	_gameSounds.clear();
 
@@ -785,7 +791,20 @@ bool Game::onCharacterAnimationFinished(const Common::String &val) {
 	if (!_scene._character)
 		return false;
 
-	error("TODO: Implemet Game::onCharacterAnimationFinished %s", val.c_str());
+	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+		YieldedCallback &cb = _yieldedCallbacks[i];
+		if (cb._luaFnName == "OnCharacterAnimationFinished" && cb._luaParam == val) {
+			TeLuaThread *lua = cb._luaThread;
+			_yieldedCallbacks.remove_at(i);
+			if (lua) {
+				lua->resume();
+				return false;
+			}
+			break;
+		}
+	}
+	_luaScript.execute("OnCharacterAnimationFinished", val);
+	return false;
 }
 
 bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
@@ -801,7 +820,6 @@ bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
 			if (lua) {
 				lua->resume();
 				callScripts = false;
-				return false;
 			}
 			break;
 		}
@@ -1266,13 +1284,13 @@ void Game::playSound(const Common::String &name, int repeats, float volume) {
 			// TODO: original seems to leak sound here??
 		} else {
 			sound->onStopSignal().add(sound, &GameSound::onSoundStopped);
-			// TODO: set snd->field_0x201
+			sound->setRetain(true);
 			_gameSounds.push_back(sound);
 		}
 	} else if (repeats == -1) {
 		for (GameSound *snd : _gameSounds) {
 			if (snd->getAccessName() == name) {
-				// TODO: set snd->field_0x201
+				snd->setRetain(true);
 				return;
 			}
 		}
@@ -1286,7 +1304,7 @@ void Game::playSound(const Common::String &name, int repeats, float volume) {
 			game->luaScript().execute("OnCellFreeSoundFinished", name);
 			delete sound;
 		} else {
-			// TODO: set snd->field_0x201
+			sound->setRetain(true);
 			_gameSounds.push_back(sound);
 		}
 	}
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 2346da3cf25..bd15a0e3128 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -593,7 +593,8 @@ static int tolua_ExportedFunctions_SetCharacterOrientation00(lua_State *L) {
 static void SetCharacterAnimation(const Common::String &charname, const Common::String &animname, bool repeat, bool b2, int i1, int i2) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charname);
-	bool result = c->setAnimation(animname, repeat, b2, true, i1, i2);
+	assert(c);
+	bool result = c->setAnimation(animname, repeat, b2, false, i1, i2);
 	if (!result) {
 		warning("[SetCharacterAnimation] Character's animation \"%s\" doesn't exist for the character\"%s\"  ",
 			animname.c_str(), charname.c_str());
@@ -1039,6 +1040,21 @@ static int tolua_ExportedFunctions_DeleteTask00(lua_State *L) {
 	error("#ferror in function 'DeleteTask': %d %d %s", err.index, err.array, err.type);
 }
 
+static void SetVisibleButtonHelp(bool val) {
+	Game *game = g_engine->getGame();
+	game->objectif().setVisibleButtonHelp(val);
+}
+
+static int tolua_ExportedFunctions_SetVisibleButtonHelp00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isboolean(L, 1, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		bool b1 = tolua_toboolean(L, 1, 0);
+		SetVisibleButtonHelp(b1);
+		return 0;
+	}
+	error("#ferror in function 'SetVisibleButtonHelp': %d %d %s", err.index, err.array, err.type);
+}
+
 static bool TestFileFlagSystemFlag(const Common::String &flagname, const Common::String &val) {
 	if (flagname == "platform" && val == "Android")
 		return true;
@@ -1322,6 +1338,28 @@ static int tolua_ExportedFunctions_PlaySound00(lua_State *L) {
 	error("#ferror in function 'PlaySound': %d %d %s", err.index, err.array, err.type);
 }
 
+static int tolua_ExportedFunctions_PlaySoundAndWaitForEnd00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 1, &err) && tolua_isnumber(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		double d1 = tolua_tonumber(L, 2, -1.0);
+		double d2 = tolua_tonumber(L, 3, 1.0);
+		PlaySound(s1, d1, d2);
+		Game::YieldedCallback cb;
+		cb._luaFnName = "OnFreeSoundFinished";
+		cb._luaParam = s1;
+		cb._luaThread = TeLuaThread::threadFromState(L);
+		Game *game = g_engine->getGame();
+		for (const auto &gamecb : game->yieldedCallbacks()) {
+			if (gamecb._luaFnName == cb._luaFnName && gamecb._luaParam == s1)
+				error("PlaySoundAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+		}
+		game->yieldedCallbacks().push_back(cb);
+		return cb._luaThread->yield();
+	}
+	error("#ferror in function 'PlaySoundAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
+}
+
 static void StopSound(const Common::String &name) {
 	Game *game = g_engine->getGame();
 	game->stopSound(name);
@@ -1559,6 +1597,30 @@ static int tolua_ExportedFunctions_MoveCharacterTo00(lua_State *L) {
 	error("#ferror in function 'MoveCharacterTo': %d %d %s", err.index, err.array, err.type);
 }
 
+static int tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+		&& tolua_isstring(L, 3, 0, &err) && tolua_isstring(L, 4, 0, &err)
+		&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		float f1 = tolua_tonumber(L, 3, 0.0);
+		float f2 = tolua_tonumber(L, 4, 0.0);
+		MoveCharacterTo(s1, s2, f1, f2);
+		Game::YieldedCallback cb;
+		cb._luaFnName = "OnDisplacementFinished";
+		cb._luaThread = TeLuaThread::threadFromState(L);
+		Game *game = g_engine->getGame();
+		for (const auto &gamecb : game->yieldedCallbacks()) {
+			if (gamecb._luaFnName == cb._luaFnName)
+				error("MoveCharacterToAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+		}
+		game->yieldedCallbacks().push_back(cb);
+		return cb._luaThread->yield();
+	}
+	error("#ferror in function 'MoveCharacterToAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
+}
+
 static void MoveCharacterPlayerTo(float x, float y, float z, bool walkFlag) {
 	Game *game = g_engine->getGame();
 	if (game->_movePlayerCharacterDisabled)
@@ -1628,18 +1690,18 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "LoadObjectMaterials", tolua_ExportedFunctions_LoadObjectMaterials01);
 	tolua_function(L, "HideObject", tolua_ExportedFunctions_HideObject00);
 	tolua_function(L, "ShowObject", tolua_ExportedFunctions_ShowObject00);
-	// tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00); // Never used
+	// tolua_function(L, "ShowAllObjects", tolua_ExportedFunctions_ShowAllObjects00); // Unused
 	tolua_function(L, "SetBackground", tolua_ExportedFunctions_SetBackground00);
-	// tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00); // Never used
-	// tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00); // Never used
+	// tolua_function(L, "AddBlockingObject", tolua_ExportedFunctions_AddBlockingObject00); // Unused
+	// tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00); // Unused
 	tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
 	tolua_function(L, "PlayMovie", tolua_ExportedFunctions_PlayMovie00);
-	/*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);
-	tolua_function(L, "StartAnimationPart", tolua_ExportedFunctions_StartAnimationPart00);*/
+	/*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);*/
+	// tolua_function(L, "StartAnimationPart", tolua_ExportedFunctions_StartAnimationPart00); // Unused
 	tolua_function(L, "StartAnimation", tolua_ExportedFunctions_StartAnimation00);
 	/*tolua_function(L, "StartAnimationAndWaitForEnd",
-				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00);
-	tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00); */
+				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00); */
+	// tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00); // Unused
 	tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
 	/*tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);*/
 	tolua_function(L, "AddMarker", tolua_ExportedFunctions_AddMarker00);
@@ -1649,19 +1711,19 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "DisabledZone", tolua_ExportedFunctions_DisabledZone00);
 	tolua_function(L, "DisabledInt", tolua_ExportedFunctions_DisabledInt00);
 	tolua_function(L, "LockCursor", tolua_ExportedFunctions_LockCursor00);
-	/*tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00);
-	tolua_function(L, "UnsetCondition", tolua_ExportedFunctions_UnsetCondition00);
-	tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00);*/
+	// tolua_function(L, "SetCondition", tolua_ExportedFunctions_SetCondition00); // Unused
+	// tolua_function(L, "UnsetCondition", tolua_ExportedFunctions_UnsetCondition00); // Unused
+	// tolua_function(L, "TutoActive", tolua_ExportedFunctions_TutoActive00); // Unused
 	tolua_function(L, "LaunchDialog", tolua_ExportedFunctions_LaunchDialog00);
 	tolua_function(L, "LaunchDialogAndWaitForEnd", tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00);
 	tolua_function(L, "PushAnswer", tolua_ExportedFunctions_PushAnswer00);
 	tolua_function(L, "HideAnswers", tolua_ExportedFunctions_HideAnswers00);
 	tolua_function(L, "PushTask", tolua_ExportedFunctions_PushTask00);
 	tolua_function(L, "DeleteTask", tolua_ExportedFunctions_DeleteTask00);
-	/*tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);*/
+	tolua_function(L, "SetVisibleButtonHelp", tolua_ExportedFunctions_SetVisibleButtonHelp00);
 	// tolua_function(L, "HideTasks", tolua_ExportedFunctions_HideTasks00); Not used.
 	tolua_function(L, "PlaySound", tolua_ExportedFunctions_PlaySound00);
-	/*tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);*/
+	tolua_function(L, "PlaySoundAndWaitForEnd", tolua_ExportedFunctions_PlaySoundAndWaitForEnd00);
 	tolua_function(L, "StopSound", tolua_ExportedFunctions_StopSound00);
 	tolua_function(L, "AddRandomSound", tolua_ExportedFunctions_AddRandomSound00);
 	tolua_function(L, "PlayRandomSound", tolua_ExportedFunctions_PlayRandomSound00);
@@ -1669,14 +1731,14 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetSoundStep", tolua_ExportedFunctions_SetSoundStep00);
 	tolua_function(L, "Selected", tolua_ExportedFunctions_Selected00);
 	tolua_function(L, "TakeObject", tolua_ExportedFunctions_TakeObject00);
-	/*tolua_function(L, "TakeObjectInHand", tolua_ExportedFunctions_TakeObjectInHand00);
-	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
+	// tolua_function(L, "TakeObjectInHand", tolua_ExportedFunctions_TakeObjectInHand00); // Unused
+	/*tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
 	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject01);*/
 	tolua_function(L, "AddNumber", tolua_ExportedFunctions_AddNumber00);
 	tolua_function(L, "ShowDocument", tolua_ExportedFunctions_ShowDocument00);
-	/*tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00);
-	tolua_function(L, "HideDocument", tolua_ExportedFunctions_HideDocument00);
-	tolua_function(L, "AddDocument", tolua_ExportedFunctions_AddDocument00);*/
+	// tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00); // Unused
+	// tolua_function(L, "HideDocument", tolua_ExportedFunctions_HideDocument00); // Unused
+	// tolua_function(L, "AddDocument", tolua_ExportedFunctions_AddDocument00); // Unused
 	tolua_function(L, "LoadCharacter", tolua_ExportedFunctions_LoadCharacter00);
 	tolua_function(L, "UnloadCharacter", tolua_ExportedFunctions_UnloadCharacter00);
 	// tolua_function(L, "GetRotationCharacter", tolua_ExportedFunctions_GetRotationCharacter00); // Unused
@@ -1684,12 +1746,12 @@ void LuaOpenBinds(lua_State *L) {
 	// tolua_function(L, "GetYPositionCharacter", tolua_ExportedFunctions_GetYPositionCharacter00); // Unused
 	// tolua_function(L, "GetZPositionCharacter", tolua_ExportedFunctions_GetZPositionCharacter00); // Unused
 	tolua_function(L, "MoveCharacterTo", tolua_ExportedFunctions_MoveCharacterTo00);
-	/*tolua_function(L, "MoveCharacterToAndWaitForEnd",
-				 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);*/
+	tolua_function(L, "MoveCharacterToAndWaitForEnd",
+				 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);
 	tolua_function(L, "MoveCharacterPlayerTo", tolua_ExportedFunctions_MoveCharacterPlayerTo00);
-	/*tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
-				 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00);
-	tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00);*/
+	// tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
+	// 			 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00); // Unused
+	// tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00); // Unused
 	tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
 	tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);
 	tolua_function(L, "SetCharacterRotation", tolua_ExportedFunctions_SetCharacterRotation00);
@@ -1705,16 +1767,16 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "MoveCharacterPlayerDisabled",
 				 tolua_ExportedFunctions_MoveCharacterPlayerDisabled00);
 	/*tolua_function(L, "SetRunMode", tolua_ExportedFunctions_SetRunMode00);
-	tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200);
-	tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00);
-	tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00);
-	tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);*/
+	tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200); */
+	// tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00); // Unused
+	// tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00); // Unused
+	/*tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);*/
 	tolua_function(L, "AddCallback", tolua_ExportedFunctions_AddCallback00);
-	/*tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00);
-	tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00);
-	tolua_function(L, "DeleteCallback", tolua_ExportedFunctions_DeleteCallback00);
-	tolua_function(L, "DeleteCallbackPlayer", tolua_ExportedFunctions_DeleteCallbackPlayer00);
-	tolua_function(L, "DeleteCallbackAnimation2D", tolua_ExportedFunctions_DeleteCallbackAnimation2D00);*/
+	/*tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00); */
+	// tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00); // Unused
+	// tolua_function(L, "DeleteCallback", tolua_ExportedFunctions_DeleteCallback00); // Unused
+	// tolua_function(L, "DeleteCallbackPlayer", tolua_ExportedFunctions_DeleteCallbackPlayer00); // Unused
+	// tolua_function(L, "DeleteCallbackAnimation2D", tolua_ExportedFunctions_DeleteCallbackAnimation2D00); // Unused
 	tolua_function(L, "SetObjectOnCharacter", tolua_ExportedFunctions_SetObjectOnCharacter00);
 	tolua_function(L, "SetObjectRotation", tolua_ExportedFunctions_SetObjectRotation00);
 	tolua_function(L, "SetObjectTranslation", tolua_ExportedFunctions_SetObjectTranslation00);
@@ -1724,14 +1786,14 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "UnloadObject", tolua_ExportedFunctions_UnloadObject00);
 	tolua_function(L, "SetGroundObjectPosition", tolua_ExportedFunctions_SetGroundObjectPosition00);
 	tolua_function(L, "SetGroundObjectRotation", tolua_ExportedFunctions_SetGroundObjectRotation00);
-	/*tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00);
-	tolua_function(L, "RotateGroundObject", tolua_ExportedFunctions_RotateGroundObject00);
-	tolua_function(L, "SetLightPlayerCharacter", tolua_ExportedFunctions_SetLightPlayerCharacter00);
-	tolua_function(L, "SetLightPos", tolua_ExportedFunctions_SetLightPos00);
-	tolua_function(L, "EnableLight", tolua_ExportedFunctions_EnableLight00);
-	tolua_function(L, "SetLightDiffuse", tolua_ExportedFunctions_SetLightDiffuse00);
-	tolua_function(L, "SetLightAmbient", tolua_ExportedFunctions_SetLightAmbient00);
-	tolua_function(L, "SetLightSpecular", tolua_ExportedFunctions_SetLightSpecular00);*/
+	/*tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00); */
+	// tolua_function(L, "RotateGroundObject", tolua_ExportedFunctions_RotateGroundObject00); // Unused
+	// tolua_function(L, "SetLightPlayerCharacter", tolua_ExportedFunctions_SetLightPlayerCharacter00); // Unused
+	// tolua_function(L, "SetLightPos", tolua_ExportedFunctions_SetLightPos00); // Unused
+	/*tolua_function(L, "EnableLight", tolua_ExportedFunctions_EnableLight00); */
+	// tolua_function(L, "SetLightDiffuse", tolua_ExportedFunctions_SetLightDiffuse00); // Unused
+	// tolua_function(L, "SetLightAmbient", tolua_ExportedFunctions_SetLightAmbient00); // Unused
+	// tolua_function(L, "SetLightSpecular", tolua_ExportedFunctions_SetLightSpecular00); // Unused
 	tolua_function(L, "LoadBillBoard", tolua_ExportedFunctions_LoadBillBoard00);
 	tolua_function(L, "SetBillboardPosition", tolua_ExportedFunctions_SetBillboardPosition00);
 	tolua_function(L, "SetBillboardPosition2", tolua_ExportedFunctions_SetBillboardPosition200);
@@ -1741,9 +1803,9 @@ void LuaOpenBinds(lua_State *L) {
 	/*tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
 	tolua_function(L, "Save", tolua_ExportedFunctions_Save00);
 	tolua_function(L, "Wait", tolua_ExportedFunctions_Wait00);
-	tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00);
-	tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00); // Unused
-	tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
+	tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00); */
+	// tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00); // Unused
+	/*tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
 	tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);*/
 	// tolua_function(L, "BFGRateImmediately", tolua_ExportedFunctions_BFGRateImmediately00); // Unused
 	// tolua_function(L, "BFGReportEvent", tolua_ExportedFunctions_BFGReportEvent00); // Unused
@@ -1756,7 +1818,7 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "EnableBlocker", tolua_ExportedFunctions_EnableBlocker00);
 	tolua_function(L, "AddAnchorZone", tolua_ExportedFunctions_AddAnchorZone00);
 	tolua_function(L, "ActivateAnchorZone", tolua_ExportedFunctions_ActivateAnchorZone00);
-	//tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00); // Unused
+	// tolua_function(L, "SetCharacterAnchor", tolua_ExportedFunctions_SetCharacterAnchor00); // Unused
 	tolua_function(L, "SetCharacterLookChar", tolua_ExportedFunctions_SetCharacterLookChar00);
 	tolua_function(L, "Random", tolua_ExportedFunctions_Random00);
 	tolua_function(L, "SetCharacterMeshVisible", tolua_ExportedFunctions_SetCharacterMeshVisible00);
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index 4f3b46ec179..9b676a57ee0 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -25,7 +25,8 @@
 
 namespace Tetraedge {
 
-/*static*/ Common::HashMap<Common::String, Object3D::ObjectSettings> *Object3D::_objectSettings = nullptr;
+/*static*/
+Common::HashMap<Common::String, Object3D::ObjectSettings> *Object3D::_objectSettings = nullptr;
 
 
 // start and end frames not initialized in original, but to guarantee we don't use
@@ -51,7 +52,8 @@ bool Object3D::loadModel(const Common::String &name) {
 	return false;
 }
 
-/*static*/ bool Object3D::loadSettings(const Common::String &path) {
+/*static*/
+bool Object3D::loadSettings(const Common::String &path) {
 	ObjectSettingsXmlParser parser;
 	parser.setAllowText();
 
@@ -65,7 +67,7 @@ bool Object3D::loadModel(const Common::String &name) {
 	if (!parser.parse())
 		error("Object3D::loadSettings: Can't parse %s", path.c_str());
 
-	return false;
+	return true;
 }
 
 void Object3D::ObjectSettings::clear() {
diff --git a/engines/tetraedge/game/object_settings_xml_parser.cpp b/engines/tetraedge/game/object_settings_xml_parser.cpp
index 906978068a1..88259dc3d14 100644
--- a/engines/tetraedge/game/object_settings_xml_parser.cpp
+++ b/engines/tetraedge/game/object_settings_xml_parser.cpp
@@ -52,8 +52,12 @@ bool ObjectSettingsXmlParser::textCallback(const Common::String &val) {
 			_curObject._modelFileName = val;
 			break;
 		case TagDefaultScale:
-			_curObject._defaultScale.parse(val);
+		{
+			bool result = _curObject._defaultScale.parse(val);
+			if (!result)
+				warning("Failed to parse Object defaultScale from %s", val.c_str());
 			break;
+		}
 		default:
 			error("should only see text for model file name or scale");
 	}
diff --git a/engines/tetraedge/game/question2.cpp b/engines/tetraedge/game/question2.cpp
index 839fe5e5ddc..356e3df2f88 100644
--- a/engines/tetraedge/game/question2.cpp
+++ b/engines/tetraedge/game/question2.cpp
@@ -37,9 +37,8 @@ Question2::~Question2() {
 }
 
 void Question2::enter() {
-	TeButtonLayout *backgroundButton = _gui.buttonLayout("background");
-	if (backgroundButton)
-		backgroundButton->setVisible(true);
+	TeButtonLayout *backgroundButton = _gui.buttonLayoutChecked("background");
+	backgroundButton->setVisible(true);
 	g_engine->getGame()->showMarkers(true);
 }
 
@@ -120,7 +119,7 @@ void Question2::pushAnswer(const Common::String &name, const Common::String &loc
 
 	TeSpriteLayout *calepinLayout = _gui.spriteLayout("Calepin");
 	if (calepinLayout)
-		calepinLayout->setParent(alayout);
+		calepinLayout->addChild(alayout);
 
 	enter();
 }
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index f18a56b5e43..9ea9e9c5b7b 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -145,7 +145,7 @@ void Te3DObject2::removeChild(Te3DObject2 *child) {
 		Common::String cname("nullptr");
 		if (child)
 			cname = child->name();
-		warning("Request to remove child (%s) which is not a child of this (%s).", cname.c_str(), name().c_str());
+		debug("Request to remove child (%s) which is not a child of this (%s).", cname.c_str(), name().c_str());
 	}
 }
 
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index 0b2ed30ef1b..a85315b77f0 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -50,7 +50,7 @@ void TeCore::create() {
 	language("en");
 	_coreNotReady = false;
 	_activityTrackingTimer.alarmSignal().add<TeCore>(this, &TeCore::onActivityTrackingAlarm);
-	warning("TeCore::create: Finish implementing me.");
+	warning("TODO: TeCore::create: Finish implementing me.");
 }
 
 TeICodec *TeCore::createVideoCodec(const Common::Path &path) {
@@ -133,6 +133,9 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 		"PC-MacOSX-PS3-Xbox360",
 		"PC-MacOSX-Xbox360-PS3/PC-MacOSX",
 		"Full",
+		"Part1-Full",
+		"Part2-Full-Part1",
+		"Part3-Full-Part1",
 		"HD",
 		"HD/PC-MacOSX-Xbox360-PS3"
 	};
diff --git a/engines/tetraedge/te/te_lua_context.cpp b/engines/tetraedge/te/te_lua_context.cpp
index f2df4bda2ba..dff65386901 100644
--- a/engines/tetraedge/te/te_lua_context.cpp
+++ b/engines/tetraedge/te/te_lua_context.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/debug.h"
 #include "common/textconsole.h"
 #include "common/lua/lua.h"
 #include "common/lua/lualib.h"
@@ -75,7 +76,10 @@ TeVariant TeLuaContext::global(const Common::String &name) {
 		lua_settop(_luaState, -2);
 		return TeVariant(str);
 	}
-	warning("TeLuaContext::global: Unexpected type %d for global %s", type, name.c_str());
+	if (type != LUA_TNIL)
+		warning("TeLuaContext::global: Unexpected type %d for global %s", type, name.c_str());
+	else
+		debug("TeLuaContext::global: Request for nil global %s", name.c_str());
 	return TeVariant();
 }
 
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index d78d734fb01..e8d8c93ad04 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -431,7 +431,7 @@ int spriteLayoutBindings(lua_State *L) {
 
 int buttonLayoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("buttonLayoutBindings:: the lua value is not a table\n");
+		warning("buttonLayoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -467,7 +467,7 @@ int buttonLayoutBindings(lua_State *L) {
 					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
 				}
 			} else {
-				warning("[TeLuaGUI.buttonLayoutBindings] Unreconized attribute : %s\n", s);
+				warning("[TeLuaGUI.buttonLayoutBindings] Unreconized attribute : %s", s);
 			}
 		}
 		lua_settop(L, -2);
@@ -491,7 +491,7 @@ int buttonLayoutBindings(lua_State *L) {
 		lua_pushlightuserdata(L, static_cast<Te3DObject2*>(layout));
 		return true;
 	} else {
-		warning("buttonLayoutBindings:: multiple objects with name %s\n", layout->name().c_str());
+		warning("buttonLayoutBindings:: multiple objects with name %s", layout->name().c_str());
 		delete layout;
 		return false;
 	}
@@ -537,7 +537,7 @@ int checkboxLayoutBindings(lua_State *L) {
 					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
 				}
 			} else {
-				warning("[TeLuaGUI.buttonLayoutBindings] Unreconized attribute : %s\n", s);
+				warning("[TeLuaGUI.checkboxLayoutBindings] Unreconized attribute : %s", s);
 			}
 		}
 		lua_settop(L, -2);
@@ -561,7 +561,7 @@ int checkboxLayoutBindings(lua_State *L) {
 		lua_pushlightuserdata(L, static_cast<Te3DObject2*>(layout));
 		return true;
 	} else {
-		warning("buttonLayoutBindings:: multiple objects with name %s\n", layout->name().c_str());
+		warning("checkboxLayoutBindings:: multiple objects with name %s", layout->name().c_str());
 		delete layout;
 		return false;
 	}
diff --git a/engines/tetraedge/te/te_lua_thread.cpp b/engines/tetraedge/te/te_lua_thread.cpp
index 54fe22956af..9b32db5908a 100644
--- a/engines/tetraedge/te/te_lua_thread.cpp
+++ b/engines/tetraedge/te/te_lua_thread.cpp
@@ -62,7 +62,7 @@ void TeLuaThread::_resume(int nargs) {
 		warning("TeLuaThread::_resume: %s", msg);
 	}
 	if (_lastResumeResult != 1 && _released) {
-		warning("TeLuaThread:: deleting this.");
+		debug("TeLuaThread:: deleting this?");
 		delete this;
 	}
 }
@@ -192,7 +192,7 @@ void TeLuaThread::pushValue(const TeVariant &val) {
 void TeLuaThread::release() {
 	_released = true;
 	if (_lastResumeResult != 1) {
-		//warning("TeLuaThread:: deleting this??");
+		//debug("TeLuaThread:: deleting this?");
 		delete this;
 	}
 }
diff --git a/engines/tetraedge/te/te_matrix4x4.cpp b/engines/tetraedge/te/te_matrix4x4.cpp
index a26a2fad065..db6440047a5 100644
--- a/engines/tetraedge/te/te_matrix4x4.cpp
+++ b/engines/tetraedge/te/te_matrix4x4.cpp
@@ -86,6 +86,7 @@ void TeMatrix4x4::scale(const TeVector3f32 &vec) {
 	scaleMatrix(0, 0) = vec.x();
 	scaleMatrix(1, 1) = vec.y();
 	scaleMatrix(2, 2) = vec.z();
+	//scaleMatrix(3, 3) = 1.0; // default.
 	*this = (*this * scaleMatrix);
 }
 
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index 3b404627c4f..51959d1fcfa 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -73,7 +73,7 @@ void TeMesh::draw() {
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->pushMatrix();
 	if (_matrixForced)
-		renderer->multiplyMatrix(_forceMatrix);
+		renderer->multiplyMatrix(_forcedMatrix);
 	else
 		renderer->multiplyMatrix(worldTransformationMatrix());
 
diff --git a/engines/tetraedge/te/te_mesh.h b/engines/tetraedge/te/te_mesh.h
index 8d6e5cd442d..f09fb8bdb10 100644
--- a/engines/tetraedge/te/te_mesh.h
+++ b/engines/tetraedge/te/te_mesh.h
@@ -137,7 +137,7 @@ private:
 	unsigned int _glMeshMode;
 
 	bool _matrixForced;
-	TeMatrix4x4 _forceMatrix;
+	TeMatrix4x4 _forcedMatrix;
 	bool _hasAlpha;
 	uint _initialMaterialIndexCount;
 	bool _drawWires;
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 111775b8e15..ebafd7b232e 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -175,10 +175,10 @@ void TeModel::update() {
 		for (unsigned int i = 0; i < _bones.size(); i++) {
 			const bone &b = _bones[i];
 			const TeMatrix4x4 matrix = TeMatrix4x4::fromTRS(b._trs);
-			if (b._x == -1 || _bones.size() < 2) {
+			if (b._parentBone == -1 || _bones.size() < 2) {
 				matricies[0] = matrix;
 			} else {
-				matricies[i] = matricies[b._x] * matrix;
+				matricies[i] = matricies[b._parentBone] * matrix;
 			}
 		}
 
@@ -202,18 +202,20 @@ void TeModel::update() {
 					trs = trs.lerp(endTRS, complete);
 				}
 			}
-			TeMatrix4x4 matrix;
+
+			TeMatrix4x4 newBoneMatrix;
 			if (!_matrixForced) {
-				matrix = TeMatrix4x4::fromTRS(trs);
+				newBoneMatrix = TeMatrix4x4::fromTRS(trs);
 			} else {
-				matrix = invertx * _forcedMatrix;
+				newBoneMatrix = invertx * _forcedMatrix;
 				_matrixForced = false;
 			}
-			if (_bones.size() < 2 || _bones[b]._x == -1) {
-				_boneMatricies[b] = matrix;
+
+			if (_bones.size() < 2 || _bones[b]._parentBone == -1) {
+				_boneMatricies[b] = newBoneMatrix;
 				_boneMatricies[b].rotate(_boneRotation);
 			} else {
-				_boneMatricies[b] = (invertx * _boneMatricies[_bones[b]._x]) * matrix;
+				_boneMatricies[b] = (invertx * _boneMatricies[_bones[b]._parentBone]) * newBoneMatrix;
 			}
 			_boneMatricies[b] = invertx * _boneMatricies[b];
 			_bonesUpdatedSignal.call(_bones[b]._name, _boneMatricies[b]);
@@ -284,6 +286,7 @@ void TeModel::update() {
 
 			for (MeshBlender *mb : _meshBlenders) {
 				if (mesh.name().contains(mb->_name)) {
+					error("TODO: Finish meshblend part of model::update");
 					// TODO: Finish TeModel::update. (disasm 585 ~ 644), LAB_doMeshBlends
 					//float blendamount = MIN(mb->_timer.getTimeFromStart() / 1000000.0f, 1.0f);
 
@@ -372,7 +375,7 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 	for (unsigned int i = 0; i < _bones.size(); i++) {
 		_bones[i]._name = Te3DObject2::deserializeString(stream);
 		loadAlign(stream);
-		_bones[i]._x = stream.readUint32LE();
+		_bones[i]._parentBone = stream.readUint32LE();
 		TeTRS::deserialize(stream, _bones[i]._trs);
 		if (!_skipSkinOffsets) {
 			_skinOffsets[i].deserialize(stream);
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index d9fa712e01b..ab27bb06384 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -64,7 +64,7 @@ public:
 
 	struct bone {
 		Common::String _name;
-		short _x;
+		short _parentBone;
 		TeTRS _trs;
 	};
 
diff --git a/engines/tetraedge/te/te_music.cpp b/engines/tetraedge/te/te_music.cpp
index 3f2e0e07875..6651f8727ed 100644
--- a/engines/tetraedge/te/te_music.cpp
+++ b/engines/tetraedge/te/te_music.cpp
@@ -31,7 +31,8 @@
 namespace Tetraedge {
 
 TeMusic::TeMusic() : _repeat(true), _isPlaying(false), _currentData(0),
-_volume(1.0), _isPaused(true), _channelName("music"), _sndHandleValid(false) {
+_volume(1.0), _isPaused(false), _channelName("music"), _sndHandleValid(false),
+_retain(false) {
 	g_engine->getSoundManager()->musics().push_back(this);
 }
 
@@ -70,8 +71,19 @@ bool TeMusic::play() {
 	Audio::AudioStream *stream = Audio::makeVorbisStream(streamfile, DisposeAfterUse::YES);
 	byte vol = round(_volume * 255.0);
 	int channelId = _channelName.hash();
+
 	Audio::Mixer *mixer = g_system->getMixer();
-	mixer->playStream(Audio::Mixer::kMusicSoundType, &_sndHandle, stream, channelId, vol);
+	Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType;
+	if (_channelName == "sfx") {
+		soundType = Audio::Mixer::kSFXSoundType;
+	} else if (_channelName == "dialog") {
+		soundType = Audio::Mixer::kSpeechSoundType;
+	} else if (_channelName == "music") {
+		soundType = Audio::Mixer::kMusicSoundType;
+	}
+
+	debug("playing %s on channel %s at vol %d", _actualPath.toString().c_str(), _channelName.c_str(), vol);
+	mixer->playStream(soundType, &_sndHandle, stream, -1, vol);
 	_sndHandleValid = true;
 	_isPaused = false;
 	_isPlaying = true;
diff --git a/engines/tetraedge/te/te_music.h b/engines/tetraedge/te/te_music.h
index cfa0e620b0f..4180f6370de 100644
--- a/engines/tetraedge/te/te_music.h
+++ b/engines/tetraedge/te/te_music.h
@@ -65,6 +65,9 @@ public:
 	float volume();
 
 	TeSignal0Param &onStopSignal() { return _onStopSignal; }
+	
+	void setRetain(bool retain) { _retain = retain; }
+	bool retain() const { return _retain; }
 
 private:
 	Common::String _rawPath; // Plain name of file requested
@@ -76,6 +79,7 @@ private:
 
 	bool _isPlaying;
 	bool _isPaused;
+	bool _retain;
 
 	float _volume;
 
diff --git a/engines/tetraedge/te/te_sound_manager.cpp b/engines/tetraedge/te/te_sound_manager.cpp
index 2f30ef256c3..e10ce8d495a 100644
--- a/engines/tetraedge/te/te_sound_manager.cpp
+++ b/engines/tetraedge/te/te_sound_manager.cpp
@@ -75,6 +75,13 @@ void TeSoundManager::stopFreeSound(const Common::String &name) {
 	_handles.erase(name);
 }
 
+void TeSoundManager::setChannelVolume(const Common::String &channel, float vol) {
+	//int channelId = channel.hash();
+	//Audio::Mixer *mixer = g_system->getMixer();
+	//mixer->setChannelVolume(handle, vol * 255);
+	// TODO: store channel volume here.
+}
+
 void TeSoundManager::update() {
 	for (auto &m : _musics)
 		m->update();
diff --git a/engines/tetraedge/te/te_sound_manager.h b/engines/tetraedge/te/te_sound_manager.h
index 227a7ce29d4..75288ca7c51 100644
--- a/engines/tetraedge/te/te_sound_manager.h
+++ b/engines/tetraedge/te/te_sound_manager.h
@@ -37,6 +37,7 @@ public:
 	void playFreeSound(const Common::Path &path, float vol, const Common::String &channel);
 	void stopFreeSound(const Common::String &channel);
 
+	void setChannelVolume(const Common::String &channel, float vol);
 	void update();
 
 	Common::Array<TeMusic *> &musics() { return _musics; }


Commit: 531b53f66a522a24c612bd0230de0cf871357188
    https://github.com/scummvm/scummvm/commit/531b53f66a522a24c612bd0230de0cf871357188
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Switch button priority to z-order

Changed paths:
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_checkbox_layout.cpp
    engines/tetraedge/te/te_layout.cpp
    engines/tetraedge/te/te_signal.h


diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 8180c981bff..1eda0a2f4d5 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -275,7 +275,7 @@ void Game::enter(bool newgame) {
 	Application *app = g_engine->getApplication();
 	app->visualFade().init();
 	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, -10000.0f));
-	g_engine->getInputMgr()->_mouseLUpSignal.insert(callbackptr);
+	g_engine->getInputMgr()->_mouseLUpSignal.push_back(callbackptr);
 	_movePlayerCharacterDisabled = false;
 	// TODO? Set character mouse move event no to -1
 	_isCharacterIdle = true;
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 2fcb923c5af..292360c9e0e 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -88,9 +88,8 @@ void Inventory::load() {
 	btn->setVisible(true);
 	btn->onMouseClickValidated().add(this, &Inventory::onQuitButton);
 
-	// FIXME: This is capturing mouse clicks.. should be set visible per original.
 	btn = _gui.buttonLayoutChecked("quitBackground");
-	btn->setVisible(false);
+	btn->setVisible(true);
 	btn->onMouseClickValidated().add(this, &Inventory::onQuitButton);
 
 	btn = _gui.buttonLayoutChecked("mainMenuButton");
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index 9ea9e9c5b7b..7519441900e 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -185,11 +185,11 @@ void Te3DObject2::setParent(Te3DObject2 *newparent) {
 	_parent = newparent;
 	if (newparent) {
 		if (_onWorldVisibleChangedParentCallback)
-			_parent->onWorldVisibleChanged().insert(_onWorldVisibleChangedParentCallback);
+			_parent->onWorldVisibleChanged().push_back(_onWorldVisibleChangedParentCallback);
 		if (_onWorldTransformationMatrixChangedParentCallback)
-			_parent->onWorldTransformationMatrixChanged().insert(_onWorldTransformationMatrixChangedParentCallback);
+			_parent->onWorldTransformationMatrixChanged().push_back(_onWorldTransformationMatrixChangedParentCallback);
 		if (_onWorldColorChangedParentCallback)
-			_parent->onWorldColorChanged().insert(_onWorldColorChangedParentCallback);
+			_parent->onWorldColorChanged().push_back(_onWorldColorChangedParentCallback);
 
 		_onWorldVisibleChangedSlotSignal.call();
 		_onParentWorldTransformationMatrixChangedSignal.call();
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index 68760a98069..ecc10794d76 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -29,6 +29,16 @@
 
 namespace Tetraedge {
 
+class TeZPriorityMouseCallback : public TeCallback1Param<TeButtonLayout, const Common::Point &> {
+public:
+	TeZPriorityMouseCallback(TeButtonLayout *layout, TMethod method) : TeCallback1Param<TeButtonLayout, const Common::Point &>(layout, method), _pri(0.0) {}
+	virtual float &priority() override {
+		_pri =_object->position().z();
+		return _pri;
+	}
+	float _pri;
+};
+
 /*static*/ bool TeButtonLayout::_mousePositionChangedCatched = false;
 /*static*/ TeTimer *TeButtonLayout::_doubleValidationProtectionTimer = nullptr;
 
@@ -47,17 +57,17 @@ _hitZoneLayout(nullptr)
 {
 	_onMousePositionChangedMaxPriorityCallback.reset(new TeCallback1Param<TeButtonLayout, const Common::Point &>(this, &TeButtonLayout::onMousePositionChangedMaxPriority, FLT_MAX));
 
-	_onMousePositionChangedCallback.reset(new TeCallback1Param<TeButtonLayout, const Common::Point &>(this, &TeButtonLayout::onMousePositionChanged));
-	_onMouseLeftDownCallback.reset(new TeCallback1Param<TeButtonLayout, const Common::Point &>(this, &TeButtonLayout::onMouseLeftDown));
+	_onMousePositionChangedCallback.reset(new TeZPriorityMouseCallback(this, &TeButtonLayout::onMousePositionChanged));
+	_onMouseLeftDownCallback.reset(new TeZPriorityMouseCallback(this, &TeButtonLayout::onMouseLeftDown));
 	_onMouseLeftUpMaxPriorityCallback.reset(new TeCallback1Param<TeButtonLayout, const Common::Point &>(this, &TeButtonLayout::onMouseLeftUpMaxPriority, FLT_MAX));
-	_onMouseLeftUpCallback.reset(new TeCallback1Param<TeButtonLayout, const Common::Point &>(this, &TeButtonLayout::onMouseLeftUp));
+	_onMouseLeftUpCallback.reset(new TeZPriorityMouseCallback(this, &TeButtonLayout::onMouseLeftUp));
 
 	TeInputMgr *inputmgr = g_engine->getInputMgr();
-	inputmgr->_mouseMoveSignal.insert(_onMousePositionChangedCallback);
-	inputmgr->_mouseMoveSignal.insert(_onMousePositionChangedMaxPriorityCallback);
-	inputmgr->_mouseLDownSignal.insert(_onMouseLeftDownCallback);
-	inputmgr->_mouseLUpSignal.insert(_onMouseLeftUpCallback);
-	inputmgr->_mouseLUpSignal.insert(_onMouseLeftUpMaxPriorityCallback);
+	inputmgr->_mouseMoveSignal.push_back(_onMousePositionChangedCallback);
+	inputmgr->_mouseMoveSignal.push_back(_onMousePositionChangedMaxPriorityCallback);
+	inputmgr->_mouseLDownSignal.push_back(_onMouseLeftDownCallback);
+	inputmgr->_mouseLUpSignal.push_back(_onMouseLeftUpCallback);
+	inputmgr->_mouseLUpSignal.push_back(_onMouseLeftUpMaxPriorityCallback);
 
 	setEditionColor(TeColor(128, 128, 128, 255));
 	if (!getDoubleValidationProtectionTimer()->running())
diff --git a/engines/tetraedge/te/te_checkbox_layout.cpp b/engines/tetraedge/te/te_checkbox_layout.cpp
index 0392205e0a7..0e1eed8682b 100644
--- a/engines/tetraedge/te/te_checkbox_layout.cpp
+++ b/engines/tetraedge/te/te_checkbox_layout.cpp
@@ -39,10 +39,10 @@ _clickPassThrough(false), _state(CheckboxState6)
 	_onMousePositionChangedCallback.reset(new TeCallback1Param<TeCheckboxLayout, const Common::Point &>(this, &TeCheckboxLayout::onMousePositionChanged));
 
 	TeInputMgr *inputmgr = g_engine->getInputMgr();
-	inputmgr->_mouseMoveSignal.insert(_onMousePositionChangedCallback);
-	inputmgr->_mouseLDownSignal.insert(_onMouseLeftDownCallback);
-	inputmgr->_mouseLUpSignal.insert(_onMouseLeftUpCallback);
-	inputmgr->_mouseLUpSignal.insert(_onMouseLeftUpMaxPriorityCallback);
+	inputmgr->_mouseMoveSignal.push_back(_onMousePositionChangedCallback);
+	inputmgr->_mouseLDownSignal.push_back(_onMouseLeftDownCallback);
+	inputmgr->_mouseLUpSignal.push_back(_onMouseLeftUpCallback);
+	inputmgr->_mouseLUpSignal.push_back(_onMouseLeftUpMaxPriorityCallback);
 }
 
 TeCheckboxLayout::~TeCheckboxLayout() {
diff --git a/engines/tetraedge/te/te_layout.cpp b/engines/tetraedge/te/te_layout.cpp
index be8ac7b36d2..f8e4e406e8b 100644
--- a/engines/tetraedge/te/te_layout.cpp
+++ b/engines/tetraedge/te/te_layout.cpp
@@ -64,7 +64,7 @@ TeLayout::~TeLayout() {
 void TeLayout::addChild(Te3DObject2 *child) {
 	Te3DObject2::addChild(child);
 	if (_onChildSizeChangedCallback) {
-		child->onSizeChanged().insert(_onChildSizeChangedCallback);
+		child->onSizeChanged().push_back(_onChildSizeChangedCallback);
 	}
 	_needZSizeUpdate = true;
 	_needZUpdate = true;
@@ -75,7 +75,7 @@ void TeLayout::addChild(Te3DObject2 *child) {
 void TeLayout::addChildBefore(Te3DObject2 *child, const Te3DObject2 *ref) {
 	Te3DObject2::addChildBefore(child, ref);
 	if (_onChildSizeChangedCallback) {
-		child->onSizeChanged().insert(_onChildSizeChangedCallback);
+		child->onSizeChanged().push_back(_onChildSizeChangedCallback);
 	}
 	_needZSizeUpdate = true;
 	_needZUpdate = true;
@@ -211,11 +211,11 @@ void TeLayout::setParent(Te3DObject2 *parent) {
 	Te3DObject2::setParent(parent);
 	if (parent) {
 		if (_onParentSizeChangedCallback)
-			parent->onSizeChanged().insert(_onParentSizeChangedCallback);
+			parent->onSizeChanged().push_back(_onParentSizeChangedCallback);
 		if (_onParentWorldTransformationMatrixChangedCallback)
-			parent->onWorldTransformationMatrixChanged().insert(_onParentWorldTransformationMatrixChangedCallback);
+			parent->onWorldTransformationMatrixChanged().push_back(_onParentWorldTransformationMatrixChangedCallback);
 		if (_onMainWindowChangedCallback)
-			mainWindowLayout.onSizeChanged().insert(_onMainWindowChangedCallback);
+			mainWindowLayout.onSizeChanged().push_back(_onMainWindowChangedCallback);
 	}
 	_needZUpdate = true;
 	_sizeChanged = true;
diff --git a/engines/tetraedge/te/te_signal.h b/engines/tetraedge/te/te_signal.h
index 105c42b3199..ebad199faa3 100644
--- a/engines/tetraedge/te/te_signal.h
+++ b/engines/tetraedge/te/te_signal.h
@@ -28,23 +28,21 @@
 
 namespace Tetraedge {
 
-template<class C> int _teCallbackSorter(const C &c1, const C &c2) {
+template<class C> bool _teCallbackSorter(const C &c1, const C &c2) {
+	// sort in *descending* priority.
 	float p1 = c1->priority();
 	float p2 = c2->priority();
-	if (p1 < p2)
-		return 1;
-	else if (p1 == p2)
-		return 0;
-	return -1;
+	return p2 < p1;
 }
 
 typedef Common::SharedPtr<TeICallback0Param> TeICallback0ParamPtr;
 
-class TeSignal0Param : public Common::SortedArray<TeICallback0ParamPtr, const TeICallback0ParamPtr &> {
+class TeSignal0Param : public Common::Array<TeICallback0ParamPtr> {
 public:
-	TeSignal0Param() : Common::SortedArray<TeICallback0ParamPtr, const TeICallback0ParamPtr &>(_teCallbackSorter) {};
+	TeSignal0Param() : Common::Array<TeICallback0ParamPtr>() {};
 
 	bool call() {
+		Common::sort(this->begin(), this->end(), _teCallbackSorter<TeICallback0ParamPtr>);
 		typename Common::Array<TeICallback0ParamPtr>::iterator i = this->begin();
 		typename Common::Array<TeICallback0ParamPtr>::iterator end_ = this->end();
 		for (; i < end_; i++) {
@@ -66,7 +64,7 @@ public:
 	}
 
 	template<class T> void add(T *obj, typename TeCallback0Param<T>::TMethod method) {
-		this->insert(TeICallback0ParamPtr(new TeCallback0Param<T>(obj, method)));
+		this->push_back(TeICallback0ParamPtr(new TeCallback0Param<T>(obj, method)));
 	}
 
 	template<class T> void remove(T *obj, typename TeCallback0Param<T>::TMethod method) {
@@ -80,11 +78,12 @@ public:
 template<class T> using TeICallback1ParamPtr = Common::SharedPtr<TeICallback1Param<T>>;
 
 /* Array of callbacks with a single parameter of type T */
-template<class T> class TeSignal1Param : public Common::SortedArray<TeICallback1ParamPtr<T>, const TeICallback1ParamPtr<T> &> {
+template<class T> class TeSignal1Param : public Common::Array<TeICallback1ParamPtr<T>> {
 public:
-	TeSignal1Param() : Common::SortedArray<TeICallback1ParamPtr<T>, const TeICallback1ParamPtr<T> &>(_teCallbackSorter) {};
+	TeSignal1Param() : Common::Array<TeICallback1ParamPtr<T>>() {};
 
 	bool call(T t) {
+		Common::sort(this->begin(), this->end(), _teCallbackSorter<TeICallback1ParamPtr<T>>);
 		typename Common::Array<TeICallback1ParamPtr<T>>::iterator i = this->begin();
 		typename Common::Array<TeICallback1ParamPtr<T>>::iterator end_ = this->end();
 		for (; i < end_; i++) {
@@ -106,7 +105,7 @@ public:
 	}
 
 	template<class S> void add(S *obj, typename TeCallback1Param<S, T>::TMethod method) {
-		this->insert(TeICallback1ParamPtr<T>(new TeCallback1Param<S, T>(obj, method)));
+		this->push_back(TeICallback1ParamPtr<T>(new TeCallback1Param<S, T>(obj, method)));
 	}
 
 	template<class S> void remove(S *obj, typename TeCallback1Param<S, T>::TMethod method) {
@@ -119,11 +118,12 @@ public:
 template<class S, class T> using TeICallback2ParamPtr = Common::SharedPtr<TeICallback2Param<S, T>>;
 
 /* Array of callbacks with a two parameters of type T */
-template<class S, class T> class TeSignal2Param : public Common::SortedArray<TeICallback2ParamPtr<S, T>, const TeICallback2ParamPtr<S, T> &> {
+template<class S, class T> class TeSignal2Param : public Common::Array<TeICallback2ParamPtr<S, T>> {
 public:
-	TeSignal2Param() : Common::SortedArray<TeICallback2ParamPtr<S, T>, const TeICallback2ParamPtr<S, T> &>(_teCallbackSorter) {};
+	TeSignal2Param() : Common::Array<TeICallback2ParamPtr<S, T>>() {};
 
 	bool call(S s, T t) {
+		Common::sort(this->begin(), this->end(), _teCallbackSorter<TeICallback2ParamPtr<S, T>>);
 		typename Common::Array<TeICallback2ParamPtr<S, T>>::iterator i = this->begin();
 		typename Common::Array<TeICallback2ParamPtr<S, T>>::iterator end_ = this->end();
 		for (; i < end_; i++) {
@@ -145,7 +145,7 @@ public:
 	}
 
 	template<class C> void add(C *obj, typename TeCallback2Param<C, S, T>::TMethod method) {
-		this->insert(TeICallback2ParamPtr<S, T>(new TeCallback2Param<C, S, T>(obj, method)));
+		this->push_back(TeICallback2ParamPtr<S, T>(new TeCallback2Param<C, S, T>(obj, method)));
 	}
 
 	template<class C> void remove(C *obj, typename TeCallback2Param<C, S, T>::TMethod method) {


Commit: a3eb8b9c1c3ca9d7143c7e72bd6d9d0341470080
    https://github.com/scummvm/scummvm/commit/a3eb8b9c1c3ca9d7143c7e72bd6d9d0341470080
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Fix inventory and other interactions

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/inventory.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/question2.cpp
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_music.cpp
    engines/tetraedge/te/te_music.h


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index eb7000ea85b..9055246d13e 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -393,16 +393,18 @@ void Application::drawFront() {
 
 #if DUMP_LAYOUTS
 static int renderCount = 0;
-static void dumpLayout(Te3DObject2 *layout, Common::String indent = "++") {
+static void dumpLayout(TeLayout *layout, Common::String indent = "++") {
 	assert(layout);
 	if (!layout->worldVisible())
 		return;
-	debug("%s %s  pos:%s  worldPos:%s  worldScale:%s size:%s col:%s", indent.c_str(), layout->name().c_str(),
-			layout->position().dump().c_str(), layout->worldPosition().dump().c_str(),
-			layout->worldScale().dump().c_str(), layout->size().dump().c_str(),
+	debug("%s %s  pos:%s  worldScale:%s  userSize:%s  size:%s  col:%s", indent.c_str(), layout->name().c_str(),
+			layout->position().dump().c_str(), layout->worldScale().dump().c_str(),
+			layout->userSize().dump().c_str(), layout->size().dump().c_str(),
 			layout->color().dump().c_str());
 	for (auto & child: layout->childList()) {
-		dumpLayout(child, indent + "++");
+		TeLayout *childLayout = dynamic_cast<TeLayout *>(child);
+		if (childLayout)
+			dumpLayout(childLayout, indent + "++");
 	}
 }
 #endif
@@ -523,11 +525,11 @@ 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.
-	warning("TODO: Implement Application::loadOptions %s", fname.c_str());
+	debug("TODO: Implement Application::loadOptions %s", fname.c_str());
 }
 
 void Application::saveOptions(const Common::String &fname) {
-	warning("TODO: Implement Application::saveOptions %s", fname.c_str());
+	debug("TODO: Implement Application::saveOptions %s", fname.c_str());
 }
 
 Common::String Application::getHelpText(const Common::String &key) {
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index a3cf7ab5da8..0b458c40f3a 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -59,7 +59,7 @@ void Character::WalkSettings::clear() {
 }
 
 Character::Character() : _walkCurveStart(0), _lastFrame(-1), _callbacksChanged(false),
-_notWalkAnim(false), _someRepeatFlag(false), _walkModeStr("Walk"),
+_notWalkAnim(false), _returnToIdleAnim(false), _walkModeStr("Walk"),
 _needsSomeUpdate(false), _positionFlag(false), _lookingAtTallThing(false),
 _stepSound1("sounds/SFX/PAS_H_BOIS1.ogg"), _stepSound2("sounds/SFX/PAS_H_BOIS2.ogg"),
 _freeMoveZone(nullptr), _animSoundOffset(0), _lastAnimFrame(0), _charLookingAt(nullptr),
@@ -173,7 +173,7 @@ float Character::animLengthFromFile(const Common::String &animname, uint *pframe
 	return animLen * _model->scale().z();
 }
 
-bool Character::blendAnimation(const Common::String &animname, float amount, bool repeat, bool param_4) {
+bool Character::blendAnimation(const Common::String &animname, float amount, bool repeat, bool returnToIdle) {
 	Common::Path animpath("models/Anims");
 	animpath.joinInPlace(animname);
 
@@ -197,7 +197,7 @@ bool Character::blendAnimation(const Common::String &animname, float amount, boo
 	_lastFrame = -1;
 	_curModelAnim->play();
 	_curAnimName = animname;
-	_someRepeatFlag = !(repeat | !param_4);
+	_returnToIdleAnim = !repeat && returnToIdle;
 	return true;
 }
 
@@ -567,9 +567,12 @@ bool Character::onModelAnimationFinished() {
 		_onCharacterAnimFinishedSignal.call(_model->name());
 	}
 
-	if (_someRepeatFlag && loadedPath.toString().contains(_setAnimName)) {
+	Common::Path setAnimNamePath = _setAnimName;
+	Common::String setAnimNameFile = setAnimNamePath.getLastComponent().toString();
+	Common::String loadedPathFile = loadedPath.getLastComponent().toString();
+	if (_returnToIdleAnim && loadedPathFile.contains(setAnimNameFile)) {
 		_notWalkAnim = false;
-		_someRepeatFlag = false;
+		_returnToIdleAnim = false;
 		setAnimation(_characterSettings._idleAnimFileName, true);
 	}
 
@@ -635,7 +638,7 @@ void Character::removeFromCurve() {
 	_curve.release();
 }
 
-bool Character::setAnimation(const Common::String &aname, bool repeat, bool param_3, bool unused, int startFrame, int endFrame) {
+bool Character::setAnimation(const Common::String &aname, bool repeat, bool returnToIdle, bool unused, int startFrame, int endFrame) {
 	if (aname.empty())
 		return false;
 
@@ -663,7 +666,7 @@ bool Character::setAnimation(const Common::String &aname, bool repeat, bool para
 	_curModelAnim->play();
 	_setAnimName = aname;
 	_curAnimName = aname;
-	_someRepeatFlag = !(repeat | !param_3);
+	_returnToIdleAnim = !repeat && returnToIdle;
 
 	return true;
 }
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index e403bbfd098..19384586a7e 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -101,7 +101,7 @@ public:
 
 	float animLength(const TeModelAnimation &modelanim, long bone, long lastframe);
 	float animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe = 9999);
-	bool blendAnimation(const Common::String &animname, float amount, bool repeat, bool param_4);
+	bool blendAnimation(const Common::String &animname, float amount, bool repeat, bool returnToIdle);
 	TeVector3f32 correctPosition(const TeVector3f32 &pos);
 	float curveOffset();
 	void deleteAllCallback();
@@ -127,7 +127,7 @@ public:
 	void removeFromCurve();
 	static Common::String rootBone() { return "Pere"; }
 
-	bool setAnimation(const Common::String &name, bool repeat, bool param_3 = false, bool param_4 = false, int startFrame = -1, int endFrame = 9999);
+	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);
 	void setCurveOffset(float offset);
 	void setFreeMoveZone(TeFreeMoveZone *zone);
@@ -169,6 +169,7 @@ public:
 	const TeVector2f32 &headRotation() const { return _headRotation; }
 	void setHeadRotation(const TeVector2f32 &val) { _headRotation = val; }
 	void setLastHeadRotation(const TeVector2f32 &val) { _lastHeadRotation = val; }
+	const TeVector3f32 &lastHeadBoneTrans() const { return _lastHeadBoneTrans; }
 	Character *charLookingAt() { return _charLookingAt; }
 	bool lookingAtTallThing() const { return _lookingAtTallThing; }
 	void setLookingAtTallThing(bool val) { _lookingAtTallThing = val; }
@@ -215,7 +216,7 @@ private:
 	int _lastFrame;
 	int _lastAnimFrame;
 	bool _notWalkAnim;
-	bool _someRepeatFlag; // TODO: what is this?
+	bool _returnToIdleAnim;
 	bool _callbacksChanged;
 	bool _needsSomeUpdate;
 	bool _positionFlag;
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index d583e0f3400..e0340bc0e59 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -99,6 +99,7 @@ void Dialog2::load() {
 	size(); // refresh size? seems to do nothing with result
 	_music.repeat(false);
 	_gui.load("menus/dialog.lua");
+	size(); // refresh size? seems to do nothing with result
 	TeButtonLayout *dialogLockBtn = _gui.buttonLayoutChecked("dialogLockButton");
 
 	dialogLockBtn->setVisible(false);
@@ -149,7 +150,7 @@ bool Dialog2::onMinimumTimeTimer() {
 
 bool Dialog2::onSkipButton() {
 	const TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
-	if (dialogAnimUp->_runTimer.running()) {
+	if (!dialogAnimUp->_runTimer.running()) {
 		const TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
 		if (!dialogAnimDown->_runTimer.running()) {
 			startDownAnimation();
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 1eda0a2f4d5..ff4faee4042 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -515,6 +515,8 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		TeLayout *bg = _forGui.layout("background");
 		bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
 		app->_frontLayout.addChild(bg);
+		// Note: Game also adds cellphone to both frontLayout *and* noScaleLayout2,
+		// so we reproduce the broken behavior exactly.
 		TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayout("background");
 		app->_frontLayout.removeChild(cellbg);
 		app->_frontLayout.addChild(cellbg);
@@ -587,7 +589,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	addNoScale2Children();
 	if (!fadeFlag) {
 		if (_inventory.selectedObject().size()) {
-			_inventory.selectedObject(*_inventory.selectedInventoryObject());
+			_inventory.selectedObject(_inventory.selectedInventoryObject());
 		}
 		_inventory.setVisible(false);
 		_objectif.setVisibleObjectif(false);
@@ -626,7 +628,6 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_luaScript.execute("OnSelectedObject", _inventory.selectedObject());
 	}
 
-	
 	for (unsigned int i = 0; i < _gameSounds.size(); i++) {
 		if (_gameSounds[i]->retain())
 			continue;
@@ -1366,10 +1367,10 @@ bool Game::setBackground(const Common::String &name) {
 	return _scene.changeBackground(name);
 }
 
-void Game::setCurrentObjectSprite(const Common::String &spritePath) {
+void Game::setCurrentObjectSprite(const Common::Path &spritePath) {
 	TeSpriteLayout *currentSprite = _inGameGui.spriteLayout("currentObjectSprite");
 	if (currentSprite) {
-		if (!spritePath.empty()) {
+		if (spritePath.empty()) {
 			currentSprite->unload();
 		} else {
 			currentSprite->load(spritePath);
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index 7d19600e196..866bebb30c8 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -152,7 +152,7 @@ public:
 	void resumeSounds() {}; // does nothing?
 	void saveBackup(const Common::String &saveName);
 	bool setBackground(const Common::String &name);
-	void setCurrentObjectSprite(const Common::String &spritePath);
+	void setCurrentObjectSprite(const Common::Path &spritePath);
 	bool showMarkers(bool val);
 	bool startAnimation(const Common::String &animName, int loopcount, bool reversed);
 	void startAnimationPart(const Common::String &param_1, int param_2, int param_3, int param_4, bool param_5) {};
@@ -185,6 +185,7 @@ public:
 	Dialog2 &dialog2() { return _dialog2; }
 	Question2 &question2() { return _question2; }
 	TeLuaGUI &forGui() { return _forGui; }
+	TeLuaGUI &inGameGui() { return _inGameGui; }
 	Objectif &objectif() { return _objectif; }
 	Common::Array<YieldedCallback> &yieldedCallbacks() { return _yieldedCallbacks; }
 	void setSaveRequested() { _saveRequested = true; }
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index bfd7d0251ab..2db2f4e6e3e 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -467,13 +467,25 @@ void InGameScene::freeSceneObjects() {
 }
 
 float InGameScene::getHeadHorizontalRotation(Character *cter, const TeVector3f32 &vec) {
-	//TeVector3f32 pos = cter->_model->position() - vec;
-	error("TODO: Implement InGameScene::getHeadHorizontalRotation");
+	TeVector3f32 pos = vec - cter->_model->position();
+	TeVector3f32 zvec = TeVector3f32(0, 0, 1.0f);
+	zvec.rotate(cter->_model->rotation());
+	float angle = atan2f(-pos.x(), pos.z()) - atan2f(-zvec.x(), zvec.z());
+	if (angle < -M_PI)
+		angle += (M_PI * 2);
+	else if (angle > M_PI)
+		angle -= (M_PI * 2);
+	return angle;
 }
 
 float InGameScene::getHeadVerticalRotation(Character *cter, const TeVector3f32 &vec) {
-	//TeVector3f32 pos = cter->_model->position() - vec;
-	error("TODO: Implement InGameScene::getHeadVerticalRotation");
+	TeVector3f32 modelPos = cter->_model->position();
+	TeVector3f32 headWorldTrans = cter->_model->worldTransformationMatrix() * cter->lastHeadBoneTrans();
+	modelPos.y() = headWorldTrans.y();
+	TeVector3f32 offsetPos = vec - modelPos;
+	currentCamera()->apply();
+	float angle = atan2f(offsetPos.y(), TeVector2f32(offsetPos.x(), offsetPos.z()).length());
+	return angle;
 }
 
 Common::String InGameScene::imagePathMarker(const Common::String &name) {
@@ -948,7 +960,7 @@ void InGameScene::setPositionCharacter(const Common::String &charName, const Com
 		const TeVector3f32 corrected = zone->correctCharacterPosition(position, &correctFlag, true);
 		c->_model->setPosition(corrected);
 		if (!correctFlag)
-			error("[SetCharacterPosition] Warning : The character is not above the ground %s", charName.c_str());
+			warning("[SetCharacterPosition] Warning : The character is not above the ground %s", charName.c_str());
 	}
 }
 
@@ -1038,9 +1050,9 @@ void InGameScene::update() {
 			TeVector2f32 headRot(getHeadHorizontalRotation(_character, targetpos),
 					getHeadVerticalRotation(_character, targetpos));
 			float hangle = headRot.getX() * 180.0 / M_PI;
-			if (hangle > 90)
+			if (hangle > 90.0f)
 				headRot.setX(M_PI_2);
-			else if (hangle < -90)
+			else if (hangle < -90.0f)
 				headRot.setX(-M_PI_2);
 			_character->setHeadRotation(headRot);
 			_character->setHasAnchor(true);
@@ -1101,7 +1113,21 @@ void InGameScene::update() {
 
 
 bool InGameScene::AnimObject::onFinished() {
-	error("TODO: Implement InGameScene::AnimObject::onFinished");
+	Game *game = g_engine->getGame();
+	for (unsigned int i = 0; i < game->yieldedCallbacks().size(); i++) {
+		Game::YieldedCallback &yc = game->yieldedCallbacks()[i];
+		if (yc._luaFnName == "OnFinishedAnim" && yc._luaParam == _name) {
+			TeLuaThread *thread = yc._luaThread;
+			game->yieldedCallbacks().remove_at(i);
+			if (thread) {
+				thread->resume();
+				return false;
+			}
+			break;
+		}
+	}
+	game->luaScript().execute("OnFinishedAnim", _name);
+	return false;
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index c70f0e8c57e..a9527ce74cd 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -195,6 +195,8 @@ public:
 	Common::Array<TeRectBlocker> &rectBlockers() { return _rectBlockers; }
 	Common::Array<TeBlocker> &blockers() { return _blockers; }
 	Common::Array<Object3D *> object3Ds() { return _object3Ds; }
+	void setWaitTime(float usecs) { _waitTime = usecs; }
+	TeTimer &waitTimeTimer() { return _waitTimeTimer; }
 
 private:
 	TeColor _shadowColor;
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 292360c9e0e..a342b2749c7 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -52,7 +52,7 @@ void Inventory::enter() {
 	}
 
 	if (_selectedObject)
-		selectedObject(*_selectedObject);
+		selectedObject(_selectedObject);
 }
 
 void Inventory::leave() {
@@ -149,19 +149,18 @@ void Inventory::unload() {
 			while (true) {
 				const Common::String slotStr = Common::String::format("page%dSlot%d", pageNo, slotNo);
 				TeLayout *slotLayout = _gui.layout(slotStr);
-				if (slotLayout) {
-					// Take a copy of the list as we may be deleting some
-					// and that removes them from the parent.
-					Common::Array<Te3DObject2 *> children = slotLayout->childList();
-					for (Te3DObject2 *child : children) {
-						InventoryObject *invObj = dynamic_cast<InventoryObject *>(child);
-						if (invObj)
-							delete invObj;
-					}
-					slotNo++;
-				} else {
+				if (!slotLayout)
 					break;
+
+				// Take a copy of the list as we may be deleting some
+				// and that removes them from the parent.
+				Common::Array<Te3DObject2 *> children = slotLayout->childList();
+				for (Te3DObject2 *child : children) {
+					InventoryObject *invObj = dynamic_cast<InventoryObject *>(child);
+					if (invObj)
+						delete invObj;
 				}
+				slotNo++;
 			}
 			pageNo++;
 		} else {
@@ -189,14 +188,14 @@ bool Inventory::addObject(InventoryObject *obj) {
 	_invObjects.push_front(obj);
 	obj->selectedSignal().add(this, &Inventory::onObjectSelected);
 	if (_invObjects.size() > 1) {
-		int pageno = 0;
+		int pageNo = 0;
 		while (true) {
-			TeLayout *page = _gui.layout(Common::String::format("page%d", pageno));
-			int slotno = 0;
+			TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
+			int slotNo = 0;
 			if (!page)
 				break;
 			while (true) {
-				TeLayout *slot = _gui.layout(Common::String::format("page%dSlot%d", pageno, slotno));
+				TeLayout *slot = _gui.layout(Common::String::format("page%dSlot%d", pageNo, slotNo));
 				if (!slot)
 					break;
 				for (unsigned int c = 0; c < slot->childCount(); c++) {
@@ -207,9 +206,9 @@ bool Inventory::addObject(InventoryObject *obj) {
 						c--;
 					}
 				}
-				slotno++;
+				slotNo++;
 			}
-			pageno++;
+			pageNo++;
 		}
     }
 
@@ -242,7 +241,7 @@ bool Inventory::addObject(InventoryObject *obj) {
 			newText->setSize(TeVector3f32(1.0,1.0,0.0));
 			newText->setTextSizeType(1);
 			newText->setTextSizeProportionalToWidth(200);
-			newText->setText(_gui.value("textAttributs").toString() + (*invObjIter)->name());
+			newText->setText(_gui.value("textAttributs").toString() + objectName((*invObjIter)->name()));
 			newText->setName((*invObjIter)->name());
 			newText->setVisible(false);
 
@@ -294,7 +293,7 @@ bool Inventory::onMainMenuButton() {
 }
 
 bool Inventory::onObjectSelected(InventoryObject &obj) {
-	selectedObject(obj);
+	selectedObject(&obj);
 	if (_selectedTimer.running()) {
 		if (_selectedTimer.timeElapsed() < 300000)
 			g_engine->getGame()->inventoryMenu().leave();
@@ -350,11 +349,48 @@ void Inventory::unPauseAnims() {
 }
 
 void Inventory::removeObject(const Common::String &objname) {
-	error("TODO: implement Inventory::removeObject");
+	int pageNo = 0;
+	bool retval;
+	bool finished = false;
+	while (!finished) {
+		TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
+		retval = false;
+		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 *childObj = dynamic_cast<InventoryObject *>(child);
+				if (childObj && childObj->name() == objname) {
+					if (_selectedObject == childObj)
+						selectedObject(nullptr);
+					for (auto &invObj : _invObjects) {
+						if (invObj->name() == objname) {
+							_invObjects.remove(invObj);
+							break;
+						}
+					}
+					delete childObj;
+					updateLayout();
+					return;
+				}
+			}
+			slotNo++;
+		}
+		pageNo++;
+	}
 }
 
 void Inventory::removeSelectedObject() {
-	error("TODO: implement Inventory::removeSelectedObject");
+	if (_selectedObject) {
+		removeObject(_selectedObject->name());
+		selectedObject(nullptr);
+	}
 }
 
 InventoryObject *Inventory::selectedInventoryObject() {
@@ -362,11 +398,45 @@ InventoryObject *Inventory::selectedInventoryObject() {
 }
 
 void Inventory::selectedObject(const Common::String &objname) {
-	error("TODO: implement Inventory::selectedObject");
+	error("TODO: implement Inventory::selectedObject('%s')", objname.c_str());
 }
 
-void Inventory::selectedObject(InventoryObject &obj) {
-	error("TODO: implement Inventory::selectedObject");
+void Inventory::selectedObject(InventoryObject *obj) {
+	Game *game = g_engine->getGame();
+	game->setCurrentObjectSprite("");
+	_gui.layoutChecked("prendre")->setVisible(false);
+	_gui.layoutChecked("textObject")->setVisible(false);
+	_selectedObject = obj;
+	if (!obj) {
+		_gui.spriteLayoutChecked("selectionSprite")->setVisible(false);
+		_gui.textLayout("text")->setText("");
+		game->inGameGui().spriteLayoutChecked("selectedObject")->unload();
+	} else {
+		TeSpriteLayout *selection = _gui.spriteLayoutChecked("selectionSprite");
+		selection->setVisible(obj->worldVisible());
+		TeLayout *parentLayout = dynamic_cast<TeLayout *>(obj->parent());
+		TeVector3f32 pos = parentLayout->position();
+		pos.z() = selection->position().z();
+		selection->setPosition(pos);
+		const Common::String &objId = obj->name();
+		static const char *textStyle = "<section style=\"center\" /><color r=\"200\" g=\"200\" b=\"200\"/><font file=\"Common/Fonts/Colaborate-Regular.otf\" size=\"24\" />";
+		Common::String text = Common::String::format("%s%s<br/>%s", textStyle,
+					objectName(objId).c_str(),
+					objectDescription(objId).c_str());
+		_gui.textLayout("text")->setText(text);
+		_gui.buttonLayoutChecked("lire")->setEnable(isDocument(objId));
+		game->setCurrentObjectSprite(obj->spritePath());
+		TeLayout *textObj = _gui.layout("textObject");
+		for (unsigned int i = 0; i < textObj->childCount(); i++) {
+			if (textObj->child(i)->name() == obj->name()) {
+				textObj->setVisible(true);
+				textObj->child(i)->setVisible(true);
+			} else {
+				textObj->child(i)->setVisible(false);
+			}
+		}
+		game->inGameGui().spriteLayoutChecked("selectedObject")->load(obj->spritePath());
+	}
 }
 
 const Common::String &Inventory::selectedObject() {
@@ -378,7 +448,53 @@ const Common::String &Inventory::selectedObject() {
 }
 
 bool Inventory::updateLayout() {
-	error("TODO: implement Inventory::updateLayout");
+	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;
+
+			// Take a copy of the list as we are deleting some
+			// and that removes them from the parent's list.
+			Common::Array<Te3DObject2 *> children = slotLayout->childList();
+			for (Te3DObject2 *child : children) {
+				InventoryObject *invObj = dynamic_cast<InventoryObject *>(child);
+				if (invObj)
+					slotLayout->removeChild(child);
+			}
+			slotNo++;
+		}
+		pageNo++;
+	}
+
+	pageNo = 0;
+	auto invObjIter = _invObjects.begin();
+	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;
+			slotLayout->addChild(*invObjIter);
+			invObjIter++;
+			slotNo++;
+			if (invObjIter == _invObjects.end())
+				return true;
+		}
+		pageNo++;
+	}
+	return false;
 }
 
 
diff --git a/engines/tetraedge/game/inventory.h b/engines/tetraedge/game/inventory.h
index 9743a8609d3..1a4b4ac04ac 100644
--- a/engines/tetraedge/game/inventory.h
+++ b/engines/tetraedge/game/inventory.h
@@ -73,7 +73,7 @@ public:
 
 	InventoryObject *selectedInventoryObject();
 	void selectedObject(const Common::String &objname);
-	void selectedObject(InventoryObject &obj);
+	void selectedObject(InventoryObject *obj);
 	const Common::String &selectedObject();
 
 	bool updateLayout();
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index bd15a0e3128..c4b156bcba6 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -165,6 +165,37 @@ static int tolua_ExportedFunctions_TakeObject00(lua_State *L) {
 	error("#ferror in function 'TakeObject': %d %d %s", err.index, err.array, err.type);
 }
 
+static void RemoveObject(const Common::String &obj) {
+	Game *game = g_engine->getGame();
+	game->inventory().removeObject(obj);
+}
+
+static int tolua_ExportedFunctions_RemoveObject00(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));
+		RemoveObject(s1);
+		return 0;
+	}
+	error("#ferror in function 'RemoveObject': %d %d %s", err.index, err.array, err.type);
+}
+
+static void RemoveObject() {
+	Game *game = g_engine->getGame();
+	game->inventory().removeSelectedObject();
+}
+
+static int tolua_ExportedFunctions_RemoveObject01(lua_State *L) {
+	tolua_Error err;
+	if (!tolua_isnoobj(L, 1, &err)) {
+		tolua_ExportedFunctions_RemoveObject00(L);
+	} else {
+		RemoveObject();
+	}
+	return 0;
+}
+
+
 static void AddNumber(const Common::String &number) {
 	Game *game = g_engine->getGame();
 	if (!game->inventory().cellphone()->addNumber(number))
@@ -281,6 +312,45 @@ static int tolua_ExportedFunctions_MoveCharacterPlayerDisabled00(lua_State *L) {
 	error("#ferror in function 'MoveCharacterPlayerDisabled': %d %d %s", err.index, err.array, err.type);
 }
 
+static void SetRunMode(bool run) {
+	Game *game = g_engine->getGame();
+	game->scene()._character->walkMode(run ? "Jog" : "Walk");
+}
+
+static int tolua_ExportedFunctions_SetRunMode00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isboolean(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		SetRunMode(tolua_toboolean(L, 1, 0));
+		return 0;
+	}
+	error("#ferror in function 'SetRunMode': %d %d %s", err.index, err.array, err.type);
+}
+
+static void SetRunMode2(const Common::String &charName, const Common::String &mode) {
+	Game *game = g_engine->getGame();
+	Character *character = game->scene().character(charName);
+	if (character == game->scene()._character)
+		return;
+
+	if (character) {
+		character->walkMode(mode);
+	} else {
+		debug("[SetRunMode2] Character not found %s", charName.c_str());
+	}
+}
+
+static int tolua_ExportedFunctions_SetRunMode200(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+			 && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		SetRunMode2(s1, s2);
+		return 0;
+	}
+	error("#ferror in function 'AddMarker': %d %d %s", err.index, err.array, err.type);
+}
+
 static void AddCallback(const Common::String &charName, const Common::String &animName, const Common::String &fnName, float triggerFrame, float maxCalls) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charName);
@@ -411,6 +481,27 @@ static int tolua_ExportedFunctions_RequestAutoSave00(lua_State *L) {
 	return 0;
 }
 
+static void SetVisibleButtonZoomed(bool val) {
+	Game *game = g_engine->getGame();
+	TeButtonLayout *btn = game->scene().hitObjectGui().buttonLayout("DeZoomedButton");
+	if (!btn) {
+		debug("[SetVisibleButtonZoomed] No \"DeZoomedButton\" in this scene");
+	} else {
+		btn->setVisible(val);
+	}
+}
+
+static int tolua_ExportedFunctions_SetVisibleButtonZoomed00(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);
+		SetVisibleButtonZoomed(b1);
+		return 0;
+	}
+	error("#ferror in function 'SetVisibleButtonZoomed': %d %d %s", err.index, err.array, err.type);
+
+}
+
 static void HideObject(const Common::String &objName) {
 	Game *game = g_engine->getGame();
 	TeIntrusivePtr<TeModel> model = game->scene().model(objName);
@@ -419,14 +510,14 @@ static void HideObject(const Common::String &objName) {
 		return;
 	}
 
-	debug("[HideObject] Object 3D \"%s\" doesn't exist.", objName.c_str());
+	//debug("[HideObject] Object 3D \"%s\" doesn't exist.", objName.c_str());
 	TeLayout *layout = game->scene().bgGui().layout(objName);
 	if (layout) {
 		layout->setVisible(false);
 		return;
 	}
 
-	debug("[HideObject] \"Set\" Object 2D \"%s\" doesn't exist.", objName.c_str());
+	//debug("[HideObject] \"Set\" Object 2D \"%s\" doesn't exist.", objName.c_str());
 	layout = game->forGui().layout(objName);
 	if (layout) {
 		layout->setVisible(false);
@@ -454,14 +545,14 @@ static void ShowObject(const Common::String &objName) {
 		return;
 	}
 
-	debug("[ShowObject] Object 3D \"%s\" doesn't exist.", objName.c_str());
+	//debug("[ShowObject] Object 3D \"%s\" doesn't exist.", objName.c_str());
 	TeLayout *layout = game->scene().bgGui().layout(objName);
 	if (layout) {
 		layout->setVisible(true);
 		return;
 	}
 
-	debug("[ShowObject] \"Set\" Object 2D \"%s\" doesn't exist.", objName.c_str());
+	//debug("[ShowObject] \"Set\" Object 2D \"%s\" doesn't exist.", objName.c_str());
 	layout = game->forGui().layout(objName);
 	if (layout) {
 		layout->setVisible(true);
@@ -520,7 +611,6 @@ static void PlaceCharacterOnDummy(const Common::String &charname, const Common::
 	} else {
 		warning("[PlaceCharacterOnDummy] Character not found %s", charname.c_str());
 	}
-
 }
 
 static int tolua_ExportedFunctions_PlaceCharacterOnDummy00(lua_State *L) {
@@ -590,11 +680,11 @@ static int tolua_ExportedFunctions_SetCharacterOrientation00(lua_State *L) {
 	error("#ferror in function 'SetCharacterOrientation': %d %d %s", err.index, err.array, err.type);
 }
 
-static void SetCharacterAnimation(const Common::String &charname, const Common::String &animname, bool repeat, bool b2, int i1, int i2) {
+static void SetCharacterAnimation(const Common::String &charname, const Common::String &animname, bool repeat, bool b2, int startframe, int endframe) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charname);
 	assert(c);
-	bool result = c->setAnimation(animname, repeat, b2, false, i1, i2);
+	bool result = c->setAnimation(animname, repeat, b2, false, startframe, endframe);
 	if (!result) {
 		warning("[SetCharacterAnimation] Character's animation \"%s\" doesn't exist for the character\"%s\"  ",
 			animname.c_str(), charname.c_str());
@@ -889,7 +979,8 @@ static void HideBillboard(const Common::String &name) {
 	Game *game = g_engine->getGame();
 	Billboard *bb = game->scene().billboard(name);
 	if (!bb) {
-		error("[HideBillboard] Billboard not found %s", name.c_str());
+		warning("[HideBillboard] Billboard not found %s", name.c_str());
+		return;
 	}
 	bb->model()->setVisible(false);
 }
@@ -904,6 +995,47 @@ static int tolua_ExportedFunctions_HideBillboard00(lua_State *L) {
 	error("#ferror in function 'HideBillboard': %d %d %s", err.index, err.array, err.type);
 }
 
+
+static void Wait(float seconds) {
+	Game *game = g_engine->getGame();
+	game->scene().waitTimeTimer().start();
+	game->scene().waitTimeTimer().stop();
+	game->scene().waitTimeTimer().start();
+	game->scene().setWaitTime(seconds * 1000000.0);
+}
+
+static int tolua_ExportedFunctions_Wait00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		double d = tolua_tonumber(L, 1, 0.0);
+		Wait(d);
+		return 0;
+	}
+	error("#ferror in function 'Wait': %d %d %s", err.index, err.array, err.type);
+}
+
+static int tolua_ExportedFunctions_WaitAndWaitForEnd00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		double d = tolua_tonumber(L, 1, 0.0);
+		Wait(d);
+
+		Game::YieldedCallback callback;
+		callback._luaThread = TeLuaThread::threadFromState(L);
+		callback._luaFnName = "OnWaitFinished";
+
+		Game *game = g_engine->getGame();
+		for (const auto &cb : game->yieldedCallbacks()) {
+			if (cb._luaFnName == callback._luaFnName)
+				error("WaitAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+		}
+
+		game->yieldedCallbacks().push_back(callback);
+		return callback._luaThread->yield();
+	}
+	error("#ferror in function 'WaitAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
+}
+
 static void SetBackground(const Common::String &name) {
 	Game *game = g_engine->getGame();
 	if (!game->setBackground(name))
@@ -1703,7 +1835,7 @@ void LuaOpenBinds(lua_State *L) {
 				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00); */
 	// tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00); // Unused
 	tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
-	/*tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);*/
+	tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);
 	tolua_function(L, "AddMarker", tolua_ExportedFunctions_AddMarker00);
 	tolua_function(L, "SetVisibleMarker", tolua_ExportedFunctions_SetVisibleMarker00);
 	tolua_function(L, "DeleteMarker", tolua_ExportedFunctions_DeleteMarker00);
@@ -1732,8 +1864,8 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "Selected", tolua_ExportedFunctions_Selected00);
 	tolua_function(L, "TakeObject", tolua_ExportedFunctions_TakeObject00);
 	// tolua_function(L, "TakeObjectInHand", tolua_ExportedFunctions_TakeObjectInHand00); // Unused
-	/*tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
-	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject01);*/
+	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject00);
+	tolua_function(L, "RemoveObject", tolua_ExportedFunctions_RemoveObject01);
 	tolua_function(L, "AddNumber", tolua_ExportedFunctions_AddNumber00);
 	tolua_function(L, "ShowDocument", tolua_ExportedFunctions_ShowDocument00);
 	// tolua_function(L, "ShowDocumentAndWaitForEnd", tolua_ExportedFunctions_ShowDocumentAndWaitForEnd00); // Unused
@@ -1766,8 +1898,8 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetCharacterPlayerVisible", tolua_ExportedFunctions_SetCharacterPlayerVisible00);
 	tolua_function(L, "MoveCharacterPlayerDisabled",
 				 tolua_ExportedFunctions_MoveCharacterPlayerDisabled00);
-	/*tolua_function(L, "SetRunMode", tolua_ExportedFunctions_SetRunMode00);
-	tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200); */
+	tolua_function(L, "SetRunMode", tolua_ExportedFunctions_SetRunMode00);
+	tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200);
 	// tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00); // Unused
 	// tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00); // Unused
 	/*tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);*/
@@ -1801,9 +1933,9 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "ShowBillboard", tolua_ExportedFunctions_ShowBillboard00);
 	tolua_function(L, "HideBillboard", tolua_ExportedFunctions_HideBillboard00);
 	/*tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
-	tolua_function(L, "Save", tolua_ExportedFunctions_Save00);
+	tolua_function(L, "Save", tolua_ExportedFunctions_Save00);*/
 	tolua_function(L, "Wait", tolua_ExportedFunctions_Wait00);
-	tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00); */
+	tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00);
 	// tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00); // Unused
 	/*tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
 	tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);*/
diff --git a/engines/tetraedge/game/question2.cpp b/engines/tetraedge/game/question2.cpp
index 356e3df2f88..469578fbb5a 100644
--- a/engines/tetraedge/game/question2.cpp
+++ b/engines/tetraedge/game/question2.cpp
@@ -67,6 +67,7 @@ void Question2::leave() {
 }
 
 void Question2::load() {
+	// TODO: set field_0xd0 = 0
 	setName("dialog2");
 	setSizeType(RELATIVE_TO_PARENT);
 	const TeVector3f32 usersz = userSize();
@@ -78,6 +79,7 @@ void Question2::load() {
 		addChild(backgroundButton);
 		backgroundButton->setVisible(false);
 	}
+	size();
 }
 
 bool Question2::onAnswerValidated(Answer &answer) {
@@ -91,10 +93,7 @@ void Question2::pushAnswer(const Common::String &name, const Common::String &loc
 	Answer *answer = new Answer();
 	answer->load(name, locName, path);
 	answer->_onButtonValidatedSignal.add(this, &Question2::onAnswerValidated);
-	TeLayout *alayout = answer->layout();
-	if (!alayout)
-		error("No Answer layout after loading %s!", path.c_str());
-	TeButtonLayout *blayout = dynamic_cast<TeButtonLayout *>(alayout);
+	TeButtonLayout *blayout = dynamic_cast<TeButtonLayout *>(answer->layout());
 	if (!blayout)
 		error("No Answer button layout after loading %s!", path.c_str());
 
@@ -102,24 +101,24 @@ void Question2::pushAnswer(const Common::String &name, const Common::String &loc
 	_answers.push_back(answer);
 
 	float xpos;
+	blayout->setSizeType(RELATIVE_TO_PARENT);
 	blayout->setPositionType(RELATIVE_TO_PARENT);
 	if (!path.contains("Cal_FIN.lua")) {
-		setSize(TeVector3f32(0.45f, 0.065f, 1.0f));
+		blayout->setSize(TeVector3f32(0.45f, 0.065f, 1.0f));
 		xpos = 0.3f;
 	} else {
-		setSize(TeVector3f32(0.15f, 0.065f, 1.0f));
+		blayout->setSize(TeVector3f32(0.15f, 0.065f, 1.0f));
 		xpos = 0.15f;
 	}
-	setPosition(TeVector3f32(xpos, _answers.size() * 0.08f + 0.06f, 1.0f));
+	blayout->setPosition(TeVector3f32(xpos, _answers.size() * 0.08f + 0.06f, 1.0f));
 
 	blayout->_upLayout->setSizeType(RELATIVE_TO_PARENT);
 	blayout->_upLayout->setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
 	blayout->_downLayout->setSizeType(RELATIVE_TO_PARENT);
 	blayout->_downLayout->setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
 
-	TeSpriteLayout *calepinLayout = _gui.spriteLayout("Calepin");
-	if (calepinLayout)
-		calepinLayout->addChild(alayout);
+	TeSpriteLayout *calepinLayout = _gui.spriteLayoutChecked("Calepin");
+	calepinLayout->addChild(blayout);
 
 	enter();
 }
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index ecc10794d76..977bd535bc8 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -33,7 +33,7 @@ class TeZPriorityMouseCallback : public TeCallback1Param<TeButtonLayout, const C
 public:
 	TeZPriorityMouseCallback(TeButtonLayout *layout, TMethod method) : TeCallback1Param<TeButtonLayout, const Common::Point &>(layout, method), _pri(0.0) {}
 	virtual float &priority() override {
-		_pri =_object->position().z();
+		_pri =_object->worldPosition().z();
 		return _pri;
 	}
 	float _pri;
diff --git a/engines/tetraedge/te/te_music.cpp b/engines/tetraedge/te/te_music.cpp
index 6651f8727ed..38ee93a16e6 100644
--- a/engines/tetraedge/te/te_music.cpp
+++ b/engines/tetraedge/te/te_music.cpp
@@ -82,7 +82,7 @@ bool TeMusic::play() {
 		soundType = Audio::Mixer::kMusicSoundType;
 	}
 
-	debug("playing %s on channel %s at vol %d", _actualPath.toString().c_str(), _channelName.c_str(), vol);
+	//debug("playing %s on channel %s at vol %d", _actualPath.toString().c_str(), _channelName.c_str(), vol);
 	mixer->playStream(soundType, &_sndHandle, stream, -1, vol);
 	_sndHandleValid = true;
 	_isPaused = false;
diff --git a/engines/tetraedge/te/te_music.h b/engines/tetraedge/te/te_music.h
index 4180f6370de..041c97594fb 100644
--- a/engines/tetraedge/te/te_music.h
+++ b/engines/tetraedge/te/te_music.h
@@ -65,7 +65,7 @@ public:
 	float volume();
 
 	TeSignal0Param &onStopSignal() { return _onStopSignal; }
-	
+
 	void setRetain(bool retain) { _retain = retain; }
 	bool retain() const { return _retain; }
 


Commit: 61e35200676c0bc545546d7dbfa75bf9ce8c5ac0
    https://github.com/scummvm/scummvm/commit/61e35200676c0bc545546d7dbfa75bf9ce8c5ac0
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More WIP. Fixed various object problems.

Changed paths:
    engines/tetraedge/game/cellphone.h
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/document.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/inventory.h
    engines/tetraedge/game/inventory_menu.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/object3d.cpp
    engines/tetraedge/game/object_settings_xml_parser.cpp
    engines/tetraedge/game/object_settings_xml_parser.h
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_music.cpp
    engines/tetraedge/te/te_music.h


diff --git a/engines/tetraedge/game/cellphone.h b/engines/tetraedge/game/cellphone.h
index 35c5e68f067..e4f93031621 100644
--- a/engines/tetraedge/game/cellphone.h
+++ b/engines/tetraedge/game/cellphone.h
@@ -36,6 +36,7 @@ namespace Tetraedge {
 class Cellphone : public TeLayout {
 public:
 	Cellphone();
+	virtual ~Cellphone() {}
 
 	bool addNumber(const Common::String &num);
 	void currentPage(int offset);
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 0b458c40f3a..18f1b483459 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -27,6 +27,7 @@
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/character.h"
+#include "tetraedge/game/application.h"
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/character_settings_xml_parser.h"
 #include "tetraedge/te/te_model_animation.h"
@@ -421,19 +422,20 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 		bool resetX = false;
 		if (game->scene()._character == this) {
 			for (const auto &walkSettings : _characterSettings._walkSettings) {
-				resetX |= (walkSettings._key.contains("Walk") || walkSettings._key.contains("Jog"));
-				resetX |= (walkSettings._value._walkParts[0]._file == animfile ||
-						walkSettings._value._walkParts[1]._file == animfile ||
-						walkSettings._value._walkParts[2]._file == animfile ||
-						walkSettings._value._walkParts[3]._file == animfile);
+				if (walkSettings._key.contains("Walk") || walkSettings._key.contains("Jog")) {
+					resetX |= (walkSettings._value._walkParts[0]._file.contains(animfile)
+								|| walkSettings._value._walkParts[1]._file.contains(animfile)
+								|| walkSettings._value._walkParts[2]._file.contains(animfile)
+								|| walkSettings._value._walkParts[3]._file.contains(animfile));
+				}
 			}
-			resetX |= animfile.contains(_characterSettings._idleAnimFileName);
+			resetX |= _characterSettings._idleAnimFileName.contains(animfile);
 		} else {
-			resetX = (animfile.contains(_characterSettings._idleAnimFileName) ||
-					  animfile.contains(walkAnim(WalkPart_Start)) ||
-					  animfile.contains(walkAnim(WalkPart_Loop)) ||
-					  animfile.contains(walkAnim(WalkPart_EndD)) ||
-					  animfile.contains(walkAnim(WalkPart_EndG)));
+			resetX = (_characterSettings._idleAnimFileName.contains(animfile) ||
+					  walkAnim(WalkPart_Start).contains(animfile) ||
+					  walkAnim(WalkPart_Loop).contains(animfile) ||
+					  walkAnim(WalkPart_EndD).contains(animfile) ||
+					  walkAnim(WalkPart_EndG).contains(animfile));
 		}
 		if (resetX) {
 			boneMatrix.setValue(0, 3, 0.0f);
@@ -516,7 +518,11 @@ bool Character::onModelAnimationFinished() {
 	const Common::Path loadedPath = _model->anim()->_loadedPath;
 	const Common::String animfile = loadedPath.getLastComponent().toString();
 
-	// TODO: Do something with _unrecalAnims here.
+	bool shouldAdjust = true;
+	for (const auto &unrecal : g_engine->getApplication()->unrecalAnims()) {
+		if (animfile.contains(unrecal))
+			shouldAdjust = false;
+	}
 
 	Game *game = g_engine->getGame();
 	bool isWalkAnim = false;
@@ -538,7 +544,7 @@ bool Character::onModelAnimationFinished() {
 				  animfile.contains(walkAnim(WalkPart_EndG)));
 	}
 
-	if (!isWalkAnim) {
+	if (!isWalkAnim && shouldAdjust) {
 		int pereBone = _curModelAnim->findBone("Pere");
 		const TeTRS endTRS = trsFromAnim(*_curModelAnim, pereBone, _curModelAnim->lastFrame());
 		TeVector3f32 trans = endTRS.getTranslation();
diff --git a/engines/tetraedge/game/document.h b/engines/tetraedge/game/document.h
index 688fb029381..307e1f4f882 100644
--- a/engines/tetraedge/game/document.h
+++ b/engines/tetraedge/game/document.h
@@ -34,7 +34,7 @@ class DocumentsBrowser;
 class Document : public TeLayout {
 public:
 	Document(DocumentsBrowser *browser);
-	~Document() {
+	virtual ~Document() {
 		unload();
 		if (parent()) {
 			parent()->removeChild(this);
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index ff4faee4042..ab688078ca8 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -636,7 +636,6 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_gameSounds.remove_at(i);
 		i--;
 	}
-	_gameSounds.clear();
 
 	for (auto &randsoundlist : _randomSounds) {
 		for (auto *randsound : randsoundlist._value) {
@@ -669,7 +668,7 @@ static const char *DIALOG_IDS[20] = {
 	"KFJ", "KM", "KN", "KFM"};
 
 bool Game::launchDialog(const Common::String &dname, uint param_2, const Common::String &charname,
-				  const Common::String &animfile, float param_5) {
+				  const Common::String &animfile, float animblend) {
 	Application *app = g_engine->getApplication();
 	const Common::String *locdname = app->_loc.value(dname);
 	if (!locdname)
@@ -684,7 +683,7 @@ bool Game::launchDialog(const Common::String &dname, uint param_2, const Common:
 	}
 
 	const Common::String sndfile = dname + ".ogg";
-	_dialog2.pushDialog(dname, *locdname, sndfile, charname, animfile, param_5);
+	_dialog2.pushDialog(dname, *locdname, sndfile, charname, animfile, animblend);
 	return true;
 }
 
@@ -698,7 +697,7 @@ void Game::leave(bool flag) {
 	_notifier.unload();
 	g_engine->getInputMgr()->_mouseLUpSignal.remove(this, &Game::onMouseClick);
 	_question2.unload();
-	_inventory.cellphone()->unload();
+	_inventory.cellphone()->leave();
 	_dialog2.unload();
 	_inventory.unload();
 	_documentsBrowser.unload();
@@ -779,12 +778,12 @@ bool Game::loadScene(const Common::String &name) {
 }
 
 bool Game::onAnswered(const Common::String &val) {
-	_luaScript.execute("OnAnswered", TeVariant(val));
+	_luaScript.execute("OnAnswered", val);
 	return false;
 }
 
 bool Game::onCallNumber(Common::String val) {
-	_luaScript.execute("OnCallNumber", TeVariant(val));
+	_luaScript.execute("OnCallNumber", val);
 	return false;
 }
 
@@ -1290,7 +1289,8 @@ void Game::playSound(const Common::String &name, int repeats, float volume) {
 		}
 	} else if (repeats == -1) {
 		for (GameSound *snd : _gameSounds) {
-			if (snd->getAccessName() == name) {
+			const Common::String accessName = snd->getAccessName().toString();
+			if (accessName == name) {
 				snd->setRetain(true);
 				return;
 			}
@@ -1401,11 +1401,12 @@ bool Game::startAnimation(const Common::String &animName, int loopcount, bool re
 void Game::stopSound(const Common::String &name) {
 	for (unsigned int i = 0; i < _gameSounds.size(); i++) {
 		GameSound *sound = _gameSounds[i];
-		if (sound->getAccessName() == name) {
+		if (sound->rawPath() == name) {
 			sound->stop();
 			sound->deleteLater();
+			_gameSounds.remove_at(i);
+			break;
 		}
-		_gameSounds.remove_at(i);
 	}
 	g_engine->getSoundManager()->stopFreeSound(name);
 }
@@ -1511,7 +1512,7 @@ void Game::update() {
 
 		Common::Array<Character *> characters = _scene._characters;
 		for (Character *c : characters) {
-			if (!c->_model->anim().get())
+			if (c->_model->anim())
 				c->permanentUpdate();
 		}
 
@@ -1529,7 +1530,12 @@ void Game::update() {
 		_objectif.update();
 		_scene.update();
 	} else {
-		warning("TODO: Game::update: Stop sounds before warping");
+		TeSoundManager *soundmgr = g_engine->getSoundManager();
+		for (auto &music : soundmgr->musics()) {
+			const Common::String &chanName = music->channelName();
+			if (chanName != "music" && chanName != "sfx" && chanName != "dialog")
+				music->stop();
+		}
 		changeWarp2(_warpZone, _warpScene, _warpFadeFlag);
 	}
 }
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 2db2f4e6e3e..651edb984cb 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -702,6 +702,7 @@ bool InGameScene::loadObject(const Common::String &name) {
 	if (!obj) {
 		obj = new Object3D();
 		if (!obj->loadModel(name)) {
+			warning("InGameScene::loadObject: Loading %s failed", name.c_str());
 			delete obj;
 			return false;
 		}
@@ -713,11 +714,25 @@ bool InGameScene::loadObject(const Common::String &name) {
 }
 
 bool InGameScene::loadObjectMaterials(const Common::String &name) {
-	error("TODO: InGameScene::loadObjectMaterials");
+	TeImage img;
+	bool retval = false;
+	for (auto &obj : _objects) {
+		if (obj._name.empty())
+			continue;
+
+		Common::Path mpath = _loadedPath.getParent().join(name).join(obj._name + ".png");
+		if (img.load(mpath)) {
+			Te3DTexture *tex = new Te3DTexture();
+			tex->load(img);
+			obj._model->_meshes[0].defaultMaterial(tex);
+			retval = true;
+		}
+	}
+	return retval;
 }
 
 bool InGameScene::loadObjectMaterials(const Common::String &path, const Common::String &name) {
-	error("TODO: InGameScene::loadObjectMaterials");
+	error("TODO: InGameScene::loadObjectMaterials(%s, %s)", path.c_str(), name.c_str());
 }
 
 bool InGameScene::loadPlayerCharacter(const Common::String &name) {
@@ -1086,6 +1101,8 @@ void InGameScene::update() {
 
 	float waitTime = _waitTimeTimer.timeFromLastTimeElapsed();
 	if (_waitTime != -1.0 && waitTime > _waitTime) {
+		_waitTime = -1.0;
+		_waitTimeTimer.stop();
 		bool resumed = false;
 		for (unsigned int i = 0; i < game->yieldedCallbacks().size(); i++) {
 			Game::YieldedCallback &yc = game->yieldedCallbacks()[i];
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index a342b2749c7..45cffe2cf24 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -167,6 +167,7 @@ void Inventory::unload() {
 			break;
 		}
 	}
+	_gui.unload();
 }
 
 void Inventory::loadCellphone() {
@@ -369,9 +370,9 @@ void Inventory::removeObject(const Common::String &objname) {
 				if (childObj && childObj->name() == objname) {
 					if (_selectedObject == childObj)
 						selectedObject(nullptr);
-					for (auto &invObj : _invObjects) {
-						if (invObj->name() == objname) {
-							_invObjects.remove(invObj);
+					for (auto iter = _invObjects.begin(); iter != _invObjects.end(); iter++) {
+						if ((*iter)->name() == objname) {
+							_invObjects.erase(iter);
 							break;
 						}
 					}
diff --git a/engines/tetraedge/game/inventory.h b/engines/tetraedge/game/inventory.h
index 1a4b4ac04ac..a6d88901ea8 100644
--- a/engines/tetraedge/game/inventory.h
+++ b/engines/tetraedge/game/inventory.h
@@ -40,6 +40,7 @@ public:
 	};
 
 	Inventory();
+	virtual ~Inventory() {}
 
 	void enter();
 	void leave();
diff --git a/engines/tetraedge/game/inventory_menu.h b/engines/tetraedge/game/inventory_menu.h
index 349a2a6105c..4225207df6d 100644
--- a/engines/tetraedge/game/inventory_menu.h
+++ b/engines/tetraedge/game/inventory_menu.h
@@ -29,6 +29,7 @@ namespace Tetraedge {
 class InventoryMenu : public TeLayout {
 public:
 	InventoryMenu();
+	virtual ~InventoryMenu() {}
 
 	void enter();
 	void leave();
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index c4b156bcba6..5d7747c38de 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -71,7 +71,7 @@ static int tolua_ExportedFunctions_LoadObjectMaterials01(lua_State *L) {
 		LoadObjectMaterials(s1, s2);
 		return 0;
 	}
-	error("#ferror in function 'LoadObjectMaterials': %d %d %s", err.index, err.array, err.type);
+	return tolua_ExportedFunctions_LoadObjectMaterials00(L);
 }
 
 
@@ -374,7 +374,29 @@ static int tolua_ExportedFunctions_AddCallback00(lua_State *L) {
 		AddCallback(s1, s2, s3, n1, n2);
 		return 0;
 	}
-	error("#ferror in function 'AddMarker': %d %d %s", err.index, err.array, err.type);
+	error("#ferror in function 'AddCallback': %d %d %s", err.index, err.array, err.type);
+}
+
+static void AddCallbackPlayer(const Common::String &animName, const Common::String &fnName, float triggerFrame, float maxCalls) {
+	Game *game = g_engine->getGame();
+	Character *c = game->scene()._character;
+	assert(c);
+	c->addCallback(animName, fnName, triggerFrame, maxCalls);
+}
+
+static int tolua_ExportedFunctions_AddCallbackPlayer00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+			&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 1, &err)
+			&& tolua_isnoobj(L, 5, &err)) {
+		Common::String s2(tolua_tostring(L, 1, nullptr));
+		Common::String s3(tolua_tostring(L, 2, nullptr));
+		double n1 = tolua_tonumber(L, 3, 0.0);
+		double n2 = tolua_tonumber(L, 4, -1.0);
+		AddCallbackPlayer(s2, s3, n1, n2);
+		return 0;
+	}
+	error("#ferror in function 'AddCallbackPlayer': %d %d %s", err.index, err.array, err.type);
 }
 
 static void AddMarker(const Common::String &markerName, const Common::String &imgPath, float x, float y,
@@ -722,18 +744,19 @@ static int tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00(lua_Stat
 		double f3 = tolua_tonumber(L, 5, -1.0);
 		double f4 = tolua_tonumber(L, 6, 9999.0);
 		SetCharacterAnimation(s1, s2, b1, b2, (int)f3, (int)f4);
-		Game::YieldedCallback cb;
-		cb._luaFnName = "OnCharacterAnimationFinished";
-		cb._luaParam = s1;
-		cb._luaParam2 = s2;
-		cb._luaThread = TeLuaThread::threadFromState(L);
+
+		Game::YieldedCallback callback;
+		callback._luaThread = TeLuaThread::threadFromState(L);
+		callback._luaFnName = "OnCharacterAnimationFinished";
+		callback._luaParam = s1;
+		callback._luaParam2 = s2;
 		Game *game = g_engine->getGame();
-		for (const auto &gamecb : game->yieldedCallbacks()) {
-			if (gamecb._luaFnName == cb._luaFnName && gamecb._luaParam == s1 && gamecb._luaParam2 == s2)
+		for (const auto &cb : game->yieldedCallbacks()) {
+			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1 && cb._luaParam2 == s2)
 				error("SetCharacterAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
-		game->yieldedCallbacks().push_back(cb);
-		return cb._luaThread->yield();
+		game->yieldedCallbacks().push_back(callback);
+		return callback._luaThread->yield();
 	}
 	error("#ferror in function 'SetCharacterAnimationAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
 }
@@ -776,18 +799,18 @@ static int tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00(lua_St
 		bool b2 = tolua_toboolean(L, 5, 0);
 		BlendCharacterAnimation(s1, s2, f1, b1, b2);
 
-		Game::YieldedCallback cb;
-		cb._luaFnName = "OnCharacterAnimationFinished";
-		cb._luaParam = s1;
-		cb._luaParam2 = s2;
-		cb._luaThread = TeLuaThread::threadFromState(L);
+		Game::YieldedCallback callback;
+		callback._luaThread = TeLuaThread::threadFromState(L);
+		callback._luaFnName = "OnCharacterAnimationFinished";
+		callback._luaParam = s1;
+		callback._luaParam2 = s2;
 		Game *game = g_engine->getGame();
-		for (const auto &gamecb : game->yieldedCallbacks()) {
-			if (gamecb._luaFnName == cb._luaFnName && gamecb._luaParam == s1 && gamecb._luaParam2 == s2)
+		for (const auto &cb : game->yieldedCallbacks()) {
+			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1 && cb._luaParam2 == s2)
 				error("BlendCharacterAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
-		game->yieldedCallbacks().push_back(cb);
-		return cb._luaThread->yield();
+		game->yieldedCallbacks().push_back(callback);
+		return callback._luaThread->yield();
 	}
 	error("#ferror in function 'BlendCharacterAnimationAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
 }
@@ -1023,13 +1046,11 @@ static int tolua_ExportedFunctions_WaitAndWaitForEnd00(lua_State *L) {
 		Game::YieldedCallback callback;
 		callback._luaThread = TeLuaThread::threadFromState(L);
 		callback._luaFnName = "OnWaitFinished";
-
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName)
 				error("WaitAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
-
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
 	}
@@ -1053,10 +1074,10 @@ static int tolua_ExportedFunctions_SetBackground00(lua_State *L) {
 }
 
 static void LaunchDialog(const Common::String &name, uint param_2, const Common::String &charname,
-						const Common::String &animfile, float param_5) {
+						const Common::String &animfile, float animblend) {
 	Game *game = g_engine->getGame();
   
-	if (!game->launchDialog(name, param_2, charname, animfile, param_5))
+	if (!game->launchDialog(name, param_2, charname, animfile, animblend))
 		warning("[LaunchDialog] Dialog \"%s\" doesn't exist.", name.c_str());
 }
 
@@ -1092,13 +1113,11 @@ static int tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00(lua_State *L) {
 		callback._luaThread = TeLuaThread::threadFromState(L);
 		callback._luaFnName = "OnDialogFinished";
 		callback._luaParam = s1;
-
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName && cb._luaParam == callback._luaParam)
 				error("LaunchDialogAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
-
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
 	}
@@ -1477,17 +1496,18 @@ static int tolua_ExportedFunctions_PlaySoundAndWaitForEnd00(lua_State *L) {
 		double d1 = tolua_tonumber(L, 2, -1.0);
 		double d2 = tolua_tonumber(L, 3, 1.0);
 		PlaySound(s1, d1, d2);
-		Game::YieldedCallback cb;
-		cb._luaFnName = "OnFreeSoundFinished";
-		cb._luaParam = s1;
-		cb._luaThread = TeLuaThread::threadFromState(L);
+
+		Game::YieldedCallback callback;
+		callback._luaThread = TeLuaThread::threadFromState(L);
+		callback._luaFnName = "OnFreeSoundFinished";
+		callback._luaParam = s1;
 		Game *game = g_engine->getGame();
-		for (const auto &gamecb : game->yieldedCallbacks()) {
-			if (gamecb._luaFnName == cb._luaFnName && gamecb._luaParam == s1)
+		for (const auto &cb : game->yieldedCallbacks()) {
+			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1)
 				error("PlaySoundAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
-		game->yieldedCallbacks().push_back(cb);
-		return cb._luaThread->yield();
+		game->yieldedCallbacks().push_back(callback);
+		return callback._luaThread->yield();
 	}
 	error("#ferror in function 'PlaySoundAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
 }
@@ -1540,11 +1560,11 @@ static int tolua_ExportedFunctions_PlayMusic00(lua_State *L) {
 	error("#ferror in function 'PlayMusic': %d %d %s", err.index, err.array, err.type);
 }
 
-static void SetObjectOnCharacter(const Common::String &charName, const Common::String &obj, const Common::String &boneName) {
+static void SetObjectOnCharacter(const Common::String &charName, const Common::String &objName, const Common::String &boneName) {
 	Game *game = g_engine->getGame();
-	Object3D *obj3d = game->scene().object3D(obj);
+	Object3D *obj3d = game->scene().object3D(objName);
 	if (!obj3d) {
-		warning("[SetObjectOnCharacter] Object not found %s", obj.c_str());
+		warning("[SetObjectOnCharacter] Object not found %s", objName.c_str());
 		return;
 	}
 
@@ -1554,7 +1574,8 @@ static void SetObjectOnCharacter(const Common::String &charName, const Common::S
 
 static int tolua_ExportedFunctions_SetObjectOnCharacter00(lua_State *L) {
 	tolua_Error err;
-	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isstring(L, 3, 0, &err) && tolua_isnoobj(L, 4, &err)) {
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err)
+			&& tolua_isstring(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));
 		Common::String s3(tolua_tostring(L, 3, nullptr));
@@ -1739,16 +1760,17 @@ static int tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00(lua_State *L)
 		float f1 = tolua_tonumber(L, 3, 0.0);
 		float f2 = tolua_tonumber(L, 4, 0.0);
 		MoveCharacterTo(s1, s2, f1, f2);
-		Game::YieldedCallback cb;
-		cb._luaFnName = "OnDisplacementFinished";
-		cb._luaThread = TeLuaThread::threadFromState(L);
+
+		Game::YieldedCallback callback;
+		callback._luaThread = TeLuaThread::threadFromState(L);
+		callback._luaFnName = "OnDisplacementFinished";
 		Game *game = g_engine->getGame();
-		for (const auto &gamecb : game->yieldedCallbacks()) {
-			if (gamecb._luaFnName == cb._luaFnName)
+		for (const auto &cb : game->yieldedCallbacks()) {
+			if (cb._luaFnName == callback._luaFnName)
 				error("MoveCharacterToAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
-		game->yieldedCallbacks().push_back(cb);
-		return cb._luaThread->yield();
+		game->yieldedCallbacks().push_back(callback);
+		return callback._luaThread->yield();
 	}
 	error("#ferror in function 'MoveCharacterToAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
 }
@@ -1904,7 +1926,7 @@ void LuaOpenBinds(lua_State *L) {
 	// tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00); // Unused
 	/*tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);*/
 	tolua_function(L, "AddCallback", tolua_ExportedFunctions_AddCallback00);
-	/*tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00); */
+	tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00);
 	// tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00); // Unused
 	// tolua_function(L, "DeleteCallback", tolua_ExportedFunctions_DeleteCallback00); // Unused
 	// tolua_function(L, "DeleteCallbackPlayer", tolua_ExportedFunctions_DeleteCallbackPlayer00); // Unused
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index 9b676a57ee0..44981d8aae9 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -66,6 +66,7 @@ bool Object3D::loadSettings(const Common::String &path) {
 		error("Object3D::loadSettings: Can't load %s", path.c_str());
 	if (!parser.parse())
 		error("Object3D::loadSettings: Can't parse %s", path.c_str());
+	parser.finalize();
 
 	return true;
 }
diff --git a/engines/tetraedge/game/object_settings_xml_parser.cpp b/engines/tetraedge/game/object_settings_xml_parser.cpp
index 88259dc3d14..d5d617212b8 100644
--- a/engines/tetraedge/game/object_settings_xml_parser.cpp
+++ b/engines/tetraedge/game/object_settings_xml_parser.cpp
@@ -29,13 +29,18 @@ bool ObjectSettingsXmlParser::parserCallback_ObjectsSettings(ParserNode *node) {
 }
 
 bool ObjectSettingsXmlParser::parserCallback_Object(ParserNode *node) {
+	// Save the last object.
+	_objectSettings->setVal(_curObject._name, _curObject);
 	const Common::String &objname = node->values["name"];
-	_curObject._name = objname;
-	_objectSettings->setVal(objname, _curObject);
 	_curObject.clear();
+	_curObject._name = objname;
 	return true;
 }
 
+void ObjectSettingsXmlParser::finalize() {
+	_objectSettings->setVal(_curObject._name, _curObject);
+}
+
 bool ObjectSettingsXmlParser::parserCallback_modelFileName(ParserNode *node) {
 	_textTagType = TagModelFileName;
 	return true;
diff --git a/engines/tetraedge/game/object_settings_xml_parser.h b/engines/tetraedge/game/object_settings_xml_parser.h
index 6328f814041..a6d7c244078 100644
--- a/engines/tetraedge/game/object_settings_xml_parser.h
+++ b/engines/tetraedge/game/object_settings_xml_parser.h
@@ -34,6 +34,8 @@ public:
 		_objectSettings = settings;
 	}
 
+	void finalize();
+
 	// Parser
 	CUSTOM_XML_PARSER(ObjectSettingsXmlParser) {
 		XML_KEY(ObjectsSettings)
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index e834c50304d..ca974f4fb04 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -94,7 +94,8 @@ bool TeImage::load(const Common::Path &path) {
 	TeCore *core = g_engine->getCore();
 	TeICodec *codec = core->createVideoCodec(path);
 	if (!codec->load(path)) {
-		error("TeImage::load: Failed to load %s.", path.toString().c_str());
+		warning("TeImage::load: Failed to load %s.", path.toString().c_str());
+		return false;
 	}
 
 	Common::SharedPtr<TePalette> nullpal;
diff --git a/engines/tetraedge/te/te_music.cpp b/engines/tetraedge/te/te_music.cpp
index 38ee93a16e6..c7ba5010e9a 100644
--- a/engines/tetraedge/te/te_music.cpp
+++ b/engines/tetraedge/te/te_music.cpp
@@ -70,7 +70,7 @@ bool TeMusic::play() {
 	}
 	Audio::AudioStream *stream = Audio::makeVorbisStream(streamfile, DisposeAfterUse::YES);
 	byte vol = round(_volume * 255.0);
-	int channelId = _channelName.hash();
+	//int channelId = _channelName.hash();
 
 	Audio::Mixer *mixer = g_system->getMixer();
 	Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType;
@@ -204,6 +204,8 @@ void TeMusic::update() {
 		hasStopped = true;
 
 	if (hasStopped) {
+		g_system->getMixer()->stopHandle(_sndHandle);
+		_sndHandle = Audio::SoundHandle();
 		_isPaused = false;
 		_isPlaying = false;
 		_sndHandleValid = false;
diff --git a/engines/tetraedge/te/te_music.h b/engines/tetraedge/te/te_music.h
index 041c97594fb..c05793f0f98 100644
--- a/engines/tetraedge/te/te_music.h
+++ b/engines/tetraedge/te/te_music.h
@@ -59,6 +59,9 @@ public:
 	void setChannelName(const Common::String &name) {
 		_channelName = name;
 	}
+	const Common::String &channelName() const {
+		return _channelName;
+	}
 	void setFilePath(const Common::String &name);
 	void update();
 	void volume(float vol);
@@ -69,6 +72,8 @@ public:
 	void setRetain(bool retain) { _retain = retain; }
 	bool retain() const { return _retain; }
 
+	const Common::String &rawPath() { return _rawPath; }
+
 private:
 	Common::String _rawPath; // Plain name of file requested
 	Common::Path _actualPath; // actual path after finding it


Commit: e3aff0967403ec0c5c85b2ef90deed4faa14595b
    https://github.com/scummvm/scummvm/commit/e3aff0967403ec0c5c85b2ef90deed4faa14595b
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Lua bind functions now complete.

Also fixed pathfinding a little.

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


diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index a9527ce74cd..17fb23d1698 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -197,6 +197,7 @@ public:
 	Common::Array<Object3D *> object3Ds() { return _object3Ds; }
 	void setWaitTime(float usecs) { _waitTime = usecs; }
 	TeTimer &waitTimeTimer() { return _waitTimeTimer; }
+	Common::Array<TeLight> &lights() { return _lights; }
 
 private:
 	TeColor _shadowColor;
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 5d7747c38de..f6efcc25c64 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -74,7 +74,6 @@ static int tolua_ExportedFunctions_LoadObjectMaterials01(lua_State *L) {
 	return tolua_ExportedFunctions_LoadObjectMaterials00(L);
 }
 
-
 static void PlayMovie(const Common::String &vidpath, const Common::String &musicpath) {
 	Application *app = g_engine->getApplication();
 	app->mouseCursorLayout().load("pictures/cursor.png");
@@ -93,6 +92,28 @@ static int tolua_ExportedFunctions_PlayMovie00(lua_State *L) {
 	error("#ferror in function 'PlayMovie': %d %d %s", err.index, err.array, err.type);
 }
 
+static int tolua_ExportedFunctions_PlayMovieAndWaitForEnd00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		Common::String s2(tolua_tostring(L, 2, nullptr));
+		PlayMovie(s1, s2);
+
+		Game::YieldedCallback callback;
+		callback._luaThread = TeLuaThread::threadFromState(L);
+		callback._luaFnName = "OnMovieFinished";
+		callback._luaParam = s1;
+		Game *game = g_engine->getGame();
+		for (const auto &cb : game->yieldedCallbacks()) {
+			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1)
+				error("PlayMovieAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+		}
+		game->yieldedCallbacks().push_back(callback);
+		return callback._luaThread->yield();
+	}
+	error("#ferror in function 'PlayMovieAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
+}
+
 static void AddRandomSound(const Common::String &s1, const Common::String &s2, float f1, float f2){
 	Game *game = g_engine->getGame();
 	game->addRandomSound(s1, s2, f1, f2);
@@ -195,7 +216,6 @@ static int tolua_ExportedFunctions_RemoveObject01(lua_State *L) {
 	return 0;
 }
 
-
 static void AddNumber(const Common::String &number) {
 	Game *game = g_engine->getGame();
 	if (!game->inventory().cellphone()->addNumber(number))
@@ -351,6 +371,24 @@ static int tolua_ExportedFunctions_SetRunMode200(lua_State *L) {
 	error("#ferror in function 'AddMarker': %d %d %s", err.index, err.array, err.type);
 }
 
+static void SetCharacterShadow(const Common::String &charName, bool val) {
+	//Game *game = g_engine->getGame();
+	//Character *character = game->scene().character(charName);
+	// Note: the game fetches the character then does nothing here??
+}
+
+static int tolua_ExportedFunctions_SetCharacterShadow00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 0, &err)
+			 && tolua_isnoobj(L, 3, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		bool b1 = tolua_toboolean(L, 2, 0);
+		SetCharacterShadow(s1, b1);
+		return 0;
+	}
+	error("#ferror in function 'SetCharacterShadow': %d %d %s", err.index, err.array, err.type);
+}
+
 static void AddCallback(const Common::String &charName, const Common::String &animName, const Common::String &fnName, float triggerFrame, float maxCalls) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charName);
@@ -493,6 +531,32 @@ int tolua_ExportedFunctions_StartAnimation00(lua_State *L) {
 
 }
 
+int tolua_ExportedFunctions_StartAnimationAndWaitForEnd00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 1, &err)
+		&& tolua_isboolean(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		double d1 = tolua_tonumber(L, 2, -1.0);
+		bool b1 = tolua_toboolean(L, 3, 0);
+		StartAnimation(s1, d1, b1);
+
+		Game::YieldedCallback callback;
+		callback._luaThread = TeLuaThread::threadFromState(L);
+		callback._luaFnName = "OnFinishedAnim";
+		callback._luaParam = s1;
+		Game *game = g_engine->getGame();
+		for (const auto &cb : game->yieldedCallbacks()) {
+			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1)
+				error("StartAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+		}
+		game->yieldedCallbacks().push_back(callback);
+		return callback._luaThread->yield();
+
+	}
+	error("#ferror in function 'StartAnimationAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
+
+}
+
 static void RequestAutoSave() {
 	Game *game = g_engine->getGame();
 	game->setSaveRequested();
@@ -890,6 +954,57 @@ static int tolua_ExportedFunctions_SetGroundObjectRotation00(lua_State *L) {
 	error("#ferror in function 'SetGroundObjectRotation': %d %d %s", err.index, err.array, err.type);
 }
 
+static void TranslateGroundObject(const Common::String &name, float x, float y, float z, float time) {
+	Game *game = g_engine->getGame();
+	Object3D *obj = game->scene().object3D(name);
+	if (!obj)
+		error("[TranslateGroundObject] Object not found %s", name.c_str());
+	TeVector3f32 pos = obj->model()->position();
+	obj->_translateStart = pos;
+	obj->_translateAmount = TeVector3f32(x, y, z);
+	obj->_translateTimer.start();
+	obj->_translateTime = time;
+}
+
+static int tolua_ExportedFunctions_TranslateGroundObject00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isnumber(L, 2, 0, &err)
+		&& tolua_isnumber(L, 3, 0, &err) && tolua_isnumber(L, 4, 0, &err)
+		&& tolua_isnumber(L, 5, 0, &err) && tolua_isnoobj(L, 6, &err)) {
+		Common::String s1(tolua_tostring(L, 1, nullptr));
+		float f1 = tolua_tonumber(L, 2, 0.0);
+		float f2 = tolua_tonumber(L, 3, 0.0);
+		float f3 = tolua_tonumber(L, 4, 0.0);
+		float f4 = tolua_tonumber(L, 5, 0.0);
+		TranslateGroundObject(s1, f1, f2, f3, f4);
+		return 0;
+	}
+	error("#ferror in function 'TranslateGroundObject': %d %d %s", err.index, err.array, err.type);
+}
+
+static void EnableLight(uint lightno, bool enable) {
+	Game *game = g_engine->getGame();
+	if (lightno > game->scene().lights().size()) {
+		error("[EnableLight] Light not found %d", lightno);
+	}
+	TeLight &light = game->scene().lights()[lightno];
+	if (enable)
+		light.enable(lightno);
+	else
+		light.disable(lightno);
+}
+
+static int tolua_ExportedFunctions_EnableLight00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isboolean(L, 2, 0, &err) && tolua_isnoobj(L, 3, &err)) {
+		float f1 = tolua_tonumber(L, 1, 0.0);
+		bool b1 = tolua_toboolean(L, 2, 0);
+		EnableLight(f1, b1);
+		return 0;
+	}
+	error("#ferror in function 'EnableLight': %d %d %s", err.index, err.array, err.type);
+}
+
 static void LoadBillBoard(const Common::String &name) {
 	Game *game = g_engine->getGame();
 	game->scene().loadBillboard(name);
@@ -1018,6 +1133,35 @@ static int tolua_ExportedFunctions_HideBillboard00(lua_State *L) {
 	error("#ferror in function 'HideBillboard': %d %d %s", err.index, err.array, err.type);
 }
 
+static void UnlockAchievement(int val) {
+	Game *game = g_engine->getGame();
+	game->addToScore(val);
+}
+
+static int tolua_ExportedFunctions_UnlockAchievement00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnumber(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
+		double d = tolua_tonumber(L, 1, 0.0);
+		UnlockAchievement(d);
+		return 0;
+	}
+	error("#ferror in function 'UnlockAchievement': %d %d %s", err.index, err.array, err.type);
+}
+
+static void Save(const Common::String &name) {
+	Game *game = g_engine->getGame();
+	game->saveBackup(name);
+}
+
+static int tolua_ExportedFunctions_Save00(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));
+		Save(s1);
+		return 0;
+	}
+	error("#ferror in function 'Save': %d %d %s", err.index, err.array, err.type);
+}
 
 static void Wait(float seconds) {
 	Game *game = g_engine->getGame();
@@ -1057,6 +1201,34 @@ static int tolua_ExportedFunctions_WaitAndWaitForEnd00(lua_State *L) {
 	error("#ferror in function 'WaitAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
 }
 
+static void FinishGame() {
+	Game *game = g_engine->getGame();
+	game->finishGame();
+}
+
+static int tolua_ExportedFunctions_FinishGame00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnoobj(L, 1, &err)) {
+		FinishGame();
+		return 0;
+	}
+	error("#ferror in function 'FinishGame': %d %d %s", err.index, err.array, err.type);
+}
+
+static void RequestMainMenu() {
+	Game *game = g_engine->getGame();
+	game->_returnToMainMenu = true;
+}
+
+static int tolua_ExportedFunctions_RequestMainMenu00(lua_State *L) {
+	tolua_Error err;
+	if (tolua_isnoobj(L, 1, &err)) {
+		RequestMainMenu();
+		return 0;
+	}
+	error("#ferror in function 'RequestMainMenu': %d %d %s", err.index, err.array, err.type);
+}
+
 static void SetBackground(const Common::String &name) {
 	Game *game = g_engine->getGame();
 	if (!game->setBackground(name))
@@ -1850,11 +2022,11 @@ void LuaOpenBinds(lua_State *L) {
 	// tolua_function(L, "RemoveBlockingObject", tolua_ExportedFunctions_RemoveBlockingObject00); // Unused
 	tolua_function(L, "ChangeWarp", tolua_ExportedFunctions_ChangeWarp00);
 	tolua_function(L, "PlayMovie", tolua_ExportedFunctions_PlayMovie00);
-	/*tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);*/
+	tolua_function(L, "PlayMovieAndWaitForEnd", tolua_ExportedFunctions_PlayMovieAndWaitForEnd00);
 	// tolua_function(L, "StartAnimationPart", tolua_ExportedFunctions_StartAnimationPart00); // Unused
 	tolua_function(L, "StartAnimation", tolua_ExportedFunctions_StartAnimation00);
-	/*tolua_function(L, "StartAnimationAndWaitForEnd",
-				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00); */
+	tolua_function(L, "StartAnimationAndWaitForEnd",
+				 tolua_ExportedFunctions_StartAnimationAndWaitForEnd00);
 	// tolua_function(L, "AddAnimToSet", tolua_ExportedFunctions_AddAnimToSet00); // Unused
 	tolua_function(L, "RequestAutoSave", tolua_ExportedFunctions_RequestAutoSave00);
 	tolua_function(L, "SetVisibleButtonZoomed", tolua_ExportedFunctions_SetVisibleButtonZoomed00);
@@ -1924,7 +2096,7 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetRunMode2", tolua_ExportedFunctions_SetRunMode200);
 	// tolua_function(L, "SetCharacterColor", tolua_ExportedFunctions_SetCharacterColor00); // Unused
 	// tolua_function(L, "SetCharacterSound", tolua_ExportedFunctions_SetCharacterSound00); // Unused
-	/*tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);*/
+	tolua_function(L, "SetCharacterShadow", tolua_ExportedFunctions_SetCharacterShadow00);
 	tolua_function(L, "AddCallback", tolua_ExportedFunctions_AddCallback00);
 	tolua_function(L, "AddCallbackPlayer", tolua_ExportedFunctions_AddCallbackPlayer00);
 	// tolua_function(L, "AddCallbackAnimation2D", tolua_ExportedFunctions_AddCallbackAnimation2D00); // Unused
@@ -1940,11 +2112,11 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "UnloadObject", tolua_ExportedFunctions_UnloadObject00);
 	tolua_function(L, "SetGroundObjectPosition", tolua_ExportedFunctions_SetGroundObjectPosition00);
 	tolua_function(L, "SetGroundObjectRotation", tolua_ExportedFunctions_SetGroundObjectRotation00);
-	/*tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00); */
+	tolua_function(L, "TranslateGroundObject", tolua_ExportedFunctions_TranslateGroundObject00);
 	// tolua_function(L, "RotateGroundObject", tolua_ExportedFunctions_RotateGroundObject00); // Unused
 	// tolua_function(L, "SetLightPlayerCharacter", tolua_ExportedFunctions_SetLightPlayerCharacter00); // Unused
 	// tolua_function(L, "SetLightPos", tolua_ExportedFunctions_SetLightPos00); // Unused
-	/*tolua_function(L, "EnableLight", tolua_ExportedFunctions_EnableLight00); */
+	tolua_function(L, "EnableLight", tolua_ExportedFunctions_EnableLight00);
 	// tolua_function(L, "SetLightDiffuse", tolua_ExportedFunctions_SetLightDiffuse00); // Unused
 	// tolua_function(L, "SetLightAmbient", tolua_ExportedFunctions_SetLightAmbient00); // Unused
 	// tolua_function(L, "SetLightSpecular", tolua_ExportedFunctions_SetLightSpecular00); // Unused
@@ -1954,13 +2126,13 @@ void LuaOpenBinds(lua_State *L) {
 	tolua_function(L, "SetBillboardSize", tolua_ExportedFunctions_SetBillboardSize00);
 	tolua_function(L, "ShowBillboard", tolua_ExportedFunctions_ShowBillboard00);
 	tolua_function(L, "HideBillboard", tolua_ExportedFunctions_HideBillboard00);
-	/*tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
-	tolua_function(L, "Save", tolua_ExportedFunctions_Save00);*/
+	tolua_function(L, "UnlockAchievement", tolua_ExportedFunctions_UnlockAchievement00);
+	tolua_function(L, "Save", tolua_ExportedFunctions_Save00);
 	tolua_function(L, "Wait", tolua_ExportedFunctions_Wait00);
 	tolua_function(L, "WaitAndWaitForEnd", tolua_ExportedFunctions_WaitAndWaitForEnd00);
 	// tolua_function(L, "OpenFinalURL", tolua_ExportedFunctions_OpenFinalURL00); // Unused
-	/*tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
-	tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);*/
+	tolua_function(L, "FinishGame", tolua_ExportedFunctions_FinishGame00);
+	tolua_function(L, "RequestMainMenu", tolua_ExportedFunctions_RequestMainMenu00);
 	// tolua_function(L, "BFGRateImmediately", tolua_ExportedFunctions_BFGRateImmediately00); // Unused
 	// tolua_function(L, "BFGReportEvent", tolua_ExportedFunctions_BFGReportEvent00); // Unused
 	// tolua_function(L, "BFGReportEventWithValue", tolua_ExportedFunctions_BFGReportEventWithValue00); // Unused
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 98cafcda9e4..41185547d95 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -48,6 +48,15 @@ class TeFreeMoveZoneGraph : micropather::Graph {
 
 	void deserialize(Common::ReadStream &stream);
 	void serialize(Common::WriteStream &stream) const;
+
+	float costForPoint(TeVector2s32 pt) {
+		int flg = flag(pt);
+		if (flg == 1)
+			return FLT_MAX;
+		if (flg == 2)
+			return _bordersDistance;
+		return 1.0;
+	}
 };
 
 
@@ -358,7 +367,10 @@ void TeFreeMoveZone::setNbTriangles(unsigned int len) {
 }
 
 void TeFreeMoveZone::setPathFindingOccluder(const TeOBP &occluder) {
-	error("TODO: Implement TeFreeMoveZone::setPathFindingOccluder");
+	_obp = occluder;
+	_projectedPointsDirty = true;
+	_bordersDirty = true;
+	_gridDirty = true;
 }
 
 void TeFreeMoveZone::setVertex(unsigned int offset, const TeVector3f32 &vertex) {
@@ -378,16 +390,20 @@ TeVector3f32 TeFreeMoveZone::transformAStarGridInWorldSpace(const TeVector2s32 &
 				_gridOffsetSomething.getX() * 0.5;
 	if (!_loadedFromBin) {
 		return TeVector3f32(offsetx, _gridWorldY, offsety);
+	} else {
+		TeVector3f32 result = _gridMatrix * TeVector3f32(offsetx, _gridWorldY, offsety);
+		return worldTransformationMatrix() * result;
 	}
-	error("TODO: Implement TeFreeMoveZone::transformAStarGridInWorldSpace");
 }
 
 float TeFreeMoveZone::transformHeightMin(float minval) {
-	error("TODO: Implement TeFreeMoveZone::transformHeightMin");
+	TeVector3f32 vec = worldTransformationMatrix() * TeVector3f32(_someGridVec1.getX(), minval, _someGridVec1.getY());
+	return vec.y();
 }
 
-TeVector3f32 TeFreeMoveZone::transformVectorInWorldSpace(float param_3,float param_4) {
-	error("TODO: Implement TeFreeMoveZone::transformVectorInWorldSpace");
+TeVector3f32 TeFreeMoveZone::transformVectorInWorldSpace(float x, float y) {
+	TeVector3f32 vec = _gridMatrix * TeVector3f32(x, _gridWorldY, y);
+	return worldTransformationMatrix() * vec;
 }
 
 void TeFreeMoveZone::updateBorders() {
@@ -449,37 +465,42 @@ void TeFreeMoveZoneGraph::AdjacentCost(void *state, Common::Array<micropather::S
 
 	pt = TeVector2s32(statept._x - 1, statept._y);
 	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
-	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	cost.cost = costForPoint(pt);
 	adjacent->push_back(cost);
 
 	pt = TeVector2s32(statept._x - 1, statept._y + 1);
 	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
-	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	cost.cost = costForPoint(pt);
+	adjacent->push_back(cost);
+
+	pt = TeVector2s32(statept._x, statept._y + 1);
+	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
+	cost.cost = costForPoint(pt);
 	adjacent->push_back(cost);
 
 	pt = TeVector2s32(statept._x + 1, statept._y + 1);
 	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
-	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	cost.cost = costForPoint(pt);
 	adjacent->push_back(cost);
 
 	pt = TeVector2s32(statept._x + 1, statept._y);
 	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
-	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	cost.cost = costForPoint(pt);
 	adjacent->push_back(cost);
 
 	pt = TeVector2s32(statept._x + 1, statept._y - 1);
 	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
-	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	cost.cost = costForPoint(pt);
 	adjacent->push_back(cost);
 
 	pt = TeVector2s32(statept._x, statept._y - 1);
 	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
-	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	cost.cost = costForPoint(pt);
 	adjacent->push_back(cost);
 
 	pt = TeVector2s32(statept._x - 1, statept._y - 1);
 	cost.state = reinterpret_cast<void *>(_size._x * pt._y + pt._x);
-	cost.cost = (flag(pt) == 1 ? FLT_MAX : _bordersDistance);
+	cost.cost = costForPoint(pt);
 	adjacent->push_back(cost);
 }
 
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index 4e3efb5fbc9..8861b007284 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -128,6 +128,7 @@ private:
 	TeVector2f32 _gridOffsetSomething;
 	TeVector2f32 _someGridVec1;
 	TeVector2f32 _someGridVec2;
+	TeMatrix4x4 _gridMatrix;
 
 	float _gridWorldY;
 


Commit: 4a8f9984d4123f29972c36d2715ab3bcaba15121
    https://github.com/scummvm/scummvm/commit/4a8f9984d4123f29972c36d2715ab3bcaba15121
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Fixed more bugs, started save/load support.

Changed paths:
    engines/tetraedge/game/application.h
    engines/tetraedge/game/cellphone.cpp
    engines/tetraedge/game/cellphone.h
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/confirm.cpp
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/dialog2.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/inventory.h
    engines/tetraedge/game/inventory_menu.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/scene_lights_xml_parser.cpp
    engines/tetraedge/game/splash_screens.cpp
    engines/tetraedge/metaengine.cpp
    engines/tetraedge/metaengine.h
    engines/tetraedge/te/te_bezier_curve.cpp
    engines/tetraedge/te/te_bezier_curve.h
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_light.cpp
    engines/tetraedge/te/te_light.h
    engines/tetraedge/te/te_lua_context.cpp
    engines/tetraedge/te/te_lua_context.h
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_name_val_xml_parser.cpp
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_text_layout.cpp
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_timer.h
    engines/tetraedge/tetraedge.cpp
    engines/tetraedge/tetraedge.h
    engines/tetraedge/to_lua.cpp


diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
index d5a43e8c21e..ce9a67715ad 100644
--- a/engines/tetraedge/game/application.h
+++ b/engines/tetraedge/game/application.h
@@ -98,7 +98,8 @@ public:
 	void setTutoActivated(bool val) { _tutoActivated = val; }
 	TeCamera *mainWindowCamera() { return _mainWindowCamera.get(); }
 	Common::Array<Common::String> &unrecalAnims() { return _unrecalAnims; }
-	int difficulty() const { return _difficulty; }
+	int &difficulty() { return _difficulty; }
+	bool &tutoActivated() { return _tutoActivated; }
 
 	// TODO: Add accessors for these and make them private.
 	bool _finishedGame;
diff --git a/engines/tetraedge/game/cellphone.cpp b/engines/tetraedge/game/cellphone.cpp
index 0228fd3a17d..d91ee5654c4 100644
--- a/engines/tetraedge/game/cellphone.cpp
+++ b/engines/tetraedge/game/cellphone.cpp
@@ -41,7 +41,8 @@ bool Cellphone::addNumber(const Common::String &num) {
 	layout->setName(namePrefix + num);
 	layout->setSizeType(RELATIVE_TO_PARENT);
 	layout->setAnchor(TeVector3f32(0.5f, 0.0f, 0.0f));
-	layout->setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
+	// WORKAROUND: Original uses 1.0,1.0,1.0 here but then the text area is too high.
+	layout->setSize(TeVector3f32(1.0f, 0.6f, 0.0f));
 	layout->setPosition(TeVector3f32(0.5f, 0.08f, 0.0f));
 	layout->setTextSizeType(1);
 	layout->setTextSizeProportionalToWidth(46);
@@ -155,4 +156,21 @@ void Cellphone::unload() {
 	_gui.unload();
 }
 
+Common::Error Cellphone::syncState(Common::Serializer &s) {
+	Common::Array<Common::String> numbers = _addedNumbers;
+	unsigned int numElems = numbers.size();
+	s.syncAsUint32LE(numElems);
+	numbers.resize(numElems);
+	for (unsigned int i = 0; i < numElems; i++) {
+		s.syncString(numbers[i]);
+	}
+	if (s.isLoading()) {
+		if (!_addedNumbers.empty())
+			leave();
+		for (auto num : numbers)
+			addNumber(num);
+	}
+	return Common::kNoError;
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/cellphone.h b/engines/tetraedge/game/cellphone.h
index e4f93031621..f9d1c8922b3 100644
--- a/engines/tetraedge/game/cellphone.h
+++ b/engines/tetraedge/game/cellphone.h
@@ -61,6 +61,8 @@ public:
 
 	void unload();
 
+	Common::Error syncState(Common::Serializer &s);
+
 	TeLuaGUI &gui() { return _gui; }
 
 private:
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 18f1b483459..1d814ec639a 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -103,13 +103,21 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 	// the way this gets used later, setting large negative is more correct.
 	c->_callsMade = (maxCalls == -1.0 ? -1e9 : 0.0f);
 
-	const Common::String animPath = _model->anim()->_loadedPath.toString();
-	if (_callbacks.contains(animPath)) {
-		_callbacks[animPath].push_back(c);
+	const Common::Path animPath = _model->anim()->_loadedPath;
+
+	// Another difference.. the original messes with paths a bit - just
+	// use the file name, since it's already limited by character.
+	Common::String animName = animPath.getLastComponent().toString();
+	if (animName.empty())
+		animName = animPath.toString();
+
+	if (_callbacks.contains(animName)) {
+		_callbacks[animName].push_back(c);
 	} else {
+		Common::Path animKeyPath(animKey);
 		Common::Array<Callback *> callbacks;
 		callbacks.push_back(c);
-		_callbacks.setVal(animKey, callbacks);
+		_callbacks.setVal(animKeyPath.getLastComponent().toString(), callbacks);
 	}
 }
 
@@ -132,9 +140,8 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 		return _cache.getVal(pathStr);
 
 	TeIntrusivePtr<TeModelAnimation> modelAnim = new TeModelAnimation();
-	Common::Path foundPath = g_engine->getCore()->findFile(path);
-	if (!modelAnim->load(foundPath)) {
-		warning("Failed to load anim %s", foundPath.toString().c_str());
+	if (!modelAnim->load(path)) {
+		warning("Failed to load anim %s", path.toString().c_str());
 	}
 
 	_cache.setVal(pathStr, modelAnim);
@@ -191,6 +198,7 @@ bool Character::blendAnimation(const Common::String &animname, float amount, boo
 
 	_curModelAnim = animCacheLoad(animpath);
 	assert(_curModelAnim);
+	_curModelAnim->reset();
 	_curModelAnim->onFinished().add(this, &Character::onModelAnimationFinished);
 
 	_curModelAnim->bind(_model);
@@ -236,11 +244,11 @@ void Character::deleteAnim() {
 void Character::deleteCallback(const Common::String &key, const Common::String &fnName, float f) {
 	_callbacksChanged = true;
 	assert(_model->anim());
-	Common::String animPath = _model->anim()->_loadedPath.toString();
-	if (!_callbacks.contains(animPath))
+	Common::String animFile = _model->anim()->_loadedPath.getLastComponent().toString();
+	if (!_callbacks.contains(animFile))
 		return;
 
-	Common::Array<Callback *> &cbs = _callbacks.getVal(animPath);
+	Common::Array<Callback *> &cbs = _callbacks.getVal(animFile);
 	for (unsigned int i = 0; i < cbs.size(); i++) {
 		if (fnName.empty()) {
 			delete cbs[i];
@@ -257,7 +265,7 @@ void Character::deleteCallback(const Common::String &key, const Common::String &
 		cbs.clear();
 
 	if (cbs.empty())
-		_callbacks.erase(animPath);
+		_callbacks.erase(animFile);
 }
 
 //static bool deserialize(TiXmlElement *param_1, Walk *param_2);
@@ -530,18 +538,18 @@ bool Character::onModelAnimationFinished() {
 		// TODO: check if this logic matches..
 		for (const auto &walkSettings : _characterSettings._walkSettings) {
 			isWalkAnim |= (walkSettings._key.contains("Walk") || walkSettings._key.contains("Jog"));
-			isWalkAnim |= (walkSettings._value._walkParts[0]._file == animfile ||
-					walkSettings._value._walkParts[1]._file == animfile ||
-					walkSettings._value._walkParts[2]._file == animfile ||
-					walkSettings._value._walkParts[3]._file == animfile);
+			isWalkAnim |= (walkSettings._value._walkParts[0]._file == animfile
+						|| walkSettings._value._walkParts[1]._file == animfile
+						|| walkSettings._value._walkParts[2]._file == animfile
+						|| walkSettings._value._walkParts[3]._file == animfile);
 		}
 		isWalkAnim |= animfile.contains(_characterSettings._idleAnimFileName);
 	} else {
-		isWalkAnim = (animfile.contains(_characterSettings._idleAnimFileName) ||
-				  animfile.contains(walkAnim(WalkPart_Start)) ||
-				  animfile.contains(walkAnim(WalkPart_Loop)) ||
-				  animfile.contains(walkAnim(WalkPart_EndD)) ||
-				  animfile.contains(walkAnim(WalkPart_EndG)));
+		isWalkAnim = (_characterSettings._idleAnimFileName.contains(animfile)
+					|| walkAnim(WalkPart_Start).contains(animfile)
+					|| walkAnim(WalkPart_Loop).contains(animfile)
+					|| walkAnim(WalkPart_EndD).contains(animfile)
+					|| walkAnim(WalkPart_EndG).contains(animfile));
 	}
 
 	if (!isWalkAnim && shouldAdjust) {
@@ -587,12 +595,15 @@ bool Character::onModelAnimationFinished() {
 
 void Character::permanentUpdate() {
 	assert(_model->anim());
-	const Common::String animPath = _model->anim()->_loadedPath.toString();
+	const Common::Path animPath = _model->anim()->_loadedPath;
 	int curFrame = _model->anim()->curFrame2();
 	Game *game = g_engine->getGame();
 	_callbacksChanged = false;
-	if (_callbacks.contains(animPath)) {
-		Common::Array<Callback *> &cbs = _callbacks.getVal(animPath);
+	// Diverge from original - just use filename for anim callbacks as the
+	// original does werid things with paths.
+	const Common::String animFile = animPath.getLastComponent().toString();
+	if (_callbacks.contains(animFile)) {
+		Common::Array<Callback *> &cbs = _callbacks.getVal(animFile);
 		for (Callback *cb : cbs) {
 			if (cb->_triggerFrame > cb->_lastCheckFrame && curFrame >= cb->_triggerFrame){
 				int callsMade = cb->_callsMade;
@@ -608,13 +619,13 @@ void Character::permanentUpdate() {
 		}
 	}
 
-	if (animPath.contains("ka_esc_h")) {
+	if (animFile.contains("ka_esc_h")) {
 		if (_lastAnimFrame < 7 && _model->anim()->curFrame2() > 6) {
 			game->playSound("sounds/SFX/PAS_F_PAVE1.ogg", 1, 1.0f);
 		} else if (_lastAnimFrame < 22 && _model->anim()->curFrame2() > 21) {
 			game->playSound("sounds/SFX/PAS_F_PAVE2.ogg", 1, 1.0f);
 		}
-	} else if (animPath.contains("ka_esc_b")) {
+	} else if (animFile.contains("ka_esc_b")) {
 		if (_lastAnimFrame < 12 && _model->anim()->curFrame2() > 11) {
 			game->playSound("sounds/SFX/PAS_F_PAVE1.ogg", 1, 1.0f);
 		} else if (_lastAnimFrame < 27 && _model->anim()->curFrame2() > 26) {
@@ -836,7 +847,7 @@ void Character::update(double msFromStart) {
 	if (crossprod.y() >= 0.0f) {
 		angle = -angle;
 	}
-
+	//debug("update: curve offset %f - angle %f (base %f)", offset, angle, baseAngle);
 	TeQuaternion rot = TeQuaternion::fromAxisAndAngle(TeVector3f32(0.0, 1.0, 0.0), baseAngle + angle);
 	_model->setRotation(rot);
 
@@ -973,8 +984,9 @@ void Character::walkTo(float curveEnd, bool walkFlag) {
 				play();
 				return; // NOTE: early return here.
 			} else {
+				// NPC walk
 				double intpart;
-				double remainder = modf(walkEndLen, &intpart);
+				double remainder = modf(nloops, &intpart);
 				if (remainder >= 0.5) {
 					_walkEndAnimG = true;
 					intpart += 0.75;
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 19384586a7e..86fc5036be4 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -151,7 +151,7 @@ public:
 	TeSignal1Param<const Common::String &> _onCharacterAnimFinishedSignal;
 
 	const CharacterSettings &characterSettings() const { return _characterSettings; }
-	const Common::String &walkModeStr() const { return _walkModeStr; }
+	Common::String &walkModeStr() { return _walkModeStr; } // writable for loading games.
 	const Common::String &curAnimName() const { return _curAnimName; }
 	TeFreeMoveZone *freeMoveZone() { return _freeMoveZone; }
 	const Common::String &freeMoveZoneName() const { return _freeMoveZoneName; }
diff --git a/engines/tetraedge/game/confirm.cpp b/engines/tetraedge/game/confirm.cpp
index b27d994b0c3..70c565aa307 100644
--- a/engines/tetraedge/game/confirm.cpp
+++ b/engines/tetraedge/game/confirm.cpp
@@ -43,11 +43,11 @@ void Confirm::enter(const Common::String &guiPath, const Common::String &y) {
 
 	TeButtonLayout *yesButtonLayout = _gui.buttonLayout("yes");
 	if (yesButtonLayout)
-		yesButtonLayout->onMouseClickValidated().add<Confirm>(this, &Confirm::onButtonYes);
+		yesButtonLayout->onMouseClickValidated().add(this, &Confirm::onButtonYes);
 
 	TeButtonLayout *noButtonLayout = _gui.buttonLayout("no");
 	if (noButtonLayout)
-		noButtonLayout->onMouseClickValidated().add<Confirm>(this, &Confirm::onButtonNo);
+		noButtonLayout->onMouseClickValidated().add(this, &Confirm::onButtonNo);
 
 	TeLayout *textLayout = _gui.layout("text");
 	if (textLayout) {
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index e0340bc0e59..5c47508c6b7 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -73,7 +73,7 @@ void Dialog2::launchNextDialog() {
 			}
 
 			if (_currentDialogData._animBlend == 0.0f) {
-				if (!c->setAnimation(_currentDialogData._animfile, false))
+				if (!c->setAnimation(_currentDialogData._animfile, false, true))
 					error("[Dialog2::launchNextDialog] Character's animation \"%s\" doesn't exist for the character\"%s\"  \n",
 							_currentDialogData._animfile.c_str(), _currentDialogData._charname.c_str());
 			} else {
@@ -200,6 +200,8 @@ void Dialog2::startDownAnimation() {
 }
 
 void Dialog2::unload() {
+	if (!_gui.loaded())
+		return;
 	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
 	dialogAnimUp->stop();
 	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
diff --git a/engines/tetraedge/game/dialog2.h b/engines/tetraedge/game/dialog2.h
index a1f98b3772a..9bc73c55580 100644
--- a/engines/tetraedge/game/dialog2.h
+++ b/engines/tetraedge/game/dialog2.h
@@ -22,6 +22,8 @@
 #ifndef TETRAEDGE_GAME_DIALOG2_H
 #define TETRAEDGE_GAME_DIALOG2_H
 
+#include "common/serializer.h"
+
 #include "tetraedge/te/te_timer.h"
 #include "tetraedge/te/te_music.h"
 #include "tetraedge/te/te_lua_gui.h"
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index ab688078ca8..fae906c6526 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -23,6 +23,7 @@
 #include "common/path.h"
 #include "common/str-array.h"
 #include "common/system.h"
+#include "common/savefile.h"
 #include "common/config-manager.h"
 
 #include "tetraedge/tetraedge.h"
@@ -388,7 +389,13 @@ void Game::initLoadedBackupData() {
 	bool warpFlag = true;
 	if (!_loadName.empty()) {
 		warpFlag = false;
-		error("TODO: Implemet Game::initLoadedBackupData loading part");
+		Common::InSaveFile *saveFile = g_engine->getSaveFileManager()->openForLoading(_loadName);
+		Common::Error result = g_engine->loadGameStream(saveFile);
+		if (result.getCode() == Common::kNoError) {
+			ExtendedSavegameHeader header;
+			if (MetaEngine::readSavegameHeader(saveFile, &header))
+				g_engine->setTotalPlayTime(header.playtime);
+		}
 	}
 	Application *app = g_engine->getApplication();
 	const Common::String firstWarpPath = app->_firstWarpPath;
@@ -737,8 +744,12 @@ void Game::leave(bool flag) {
 	Character::animCacheFreeAll();
 }
 
-bool Game::loadBackup(const Common::String &path) {
-	error("TODO: Implemet Game::loadBackup %s", path.c_str());
+void Game::loadBackup(const Common::String &path) {
+	if (_gameLoadState == 0) {
+		_gameLoadState = 1;
+		g_engine->getApplication()->showLoadingIcon(true);
+		onFinishedLoadingBackup(path);
+	}
 }
 
 bool Game::loadCharacter(const Common::String &name) {
@@ -748,9 +759,9 @@ bool Game::loadCharacter(const Common::String &name) {
 		result = _scene.loadCharacter(name);
 		if (result) {
 			character = _scene.character(name);
-			character->_onCharacterAnimFinishedSignal.remove<Game>(this, &Game::onCharacterAnimationFinished);
-			character->_onCharacterAnimFinishedSignal.add<Game>(this, &Game::onCharacterAnimationFinished);
-			character->onFinished().add<Game>(this, &Game::onDisplacementFinished);
+			character->_onCharacterAnimFinishedSignal.remove(this, &Game::onCharacterAnimationFinished);
+			character->_onCharacterAnimFinishedSignal.add(this, &Game::onCharacterAnimationFinished);
+			character->onFinished().add(this, &Game::onDisplacementFinished);
 		}
 	}
 	return result;
@@ -759,10 +770,10 @@ bool Game::loadCharacter(const Common::String &name) {
 bool Game::loadPlayerCharacter(const Common::String &name) {
 	bool result = _scene.loadPlayerCharacter(name);
 	if (result) {
-		_scene._character->_characterAnimPlayerFinishedSignal.remove<Game>(this, &Game::onCharacterAnimationPlayerFinished);
-		_scene._character->_characterAnimPlayerFinishedSignal.add<Game>(this, &Game::onCharacterAnimationPlayerFinished);
-		_scene._character->onFinished().remove<Game>(this, &Game::onDisplacementFinished);
-		_scene._character->onFinished().add<Game>(this, &Game::onDisplacementFinished);
+		_scene._character->_characterAnimPlayerFinishedSignal.remove(this, &Game::onCharacterAnimationPlayerFinished);
+		_scene._character->_characterAnimPlayerFinishedSignal.add(this, &Game::onCharacterAnimationPlayerFinished);
+		_scene._character->onFinished().remove(this, &Game::onDisplacementFinished);
+		_scene._character->onFinished().add(this, &Game::onDisplacementFinished);
 	}
 	return result;
 }
@@ -1360,7 +1371,10 @@ void Game::resumeMovie() {
 }
 
 void Game::saveBackup(const Common::String &saveName) {
-	warning("TODO: Implemet Game::saveBackup %s", saveName.c_str());
+	if (saveName == "save.xml")
+		g_engine->saveAutosaveIfEnabled();
+	else
+		warning("TODO: Implemet Game::saveBackup %s", saveName.c_str());
 }
 
 bool Game::setBackground(const Common::String &name) {
@@ -1411,6 +1425,36 @@ void Game::stopSound(const Common::String &name) {
 	g_engine->getSoundManager()->stopFreeSound(name);
 }
 
+Common::Error Game::syncGame(Common::Serializer &s) {
+	Application *app = g_engine->getApplication();
+	s.setVersion(1);
+	inventory().syncState(s);
+	inventory().cellphone()->syncState(s);
+	// dialog2().syncState(s); // game saves this here, but doesn't actually save anything
+	_luaContext.syncState(s);
+	s.syncString(_currentZone);
+	s.syncString(_currentScene);
+	s.syncAsUint32LE(app->difficulty());
+	long elapsed = _playedTimer.timeFromLastTimeElapsed(); // TODO: + _loadedPlayTime;
+	s.syncAsDoubleLE(elapsed);
+	_playedTimer.stop();
+	_playedTimer.start();
+	s.syncAsUint32LE(_objectsTakenVal);
+	for (unsigned int i = 0; i < ARRAYSIZE(_objectsTakenBits); i++)
+		s.syncAsByte(_objectsTakenBits[i]);
+	s.syncAsUint32LE(_dialogsTold);
+	s.syncString(_prevSceneName);
+	Common::String mpath = _music.rawPath();
+	s.syncString(mpath);
+	if (s.isLoading())
+		_music.load(mpath);
+	s.syncString(_scene._character->walkModeStr());
+	s.syncAsByte(_firstInventory);
+	s.syncAsByte(app->tutoActivated());
+	app->showLoadingIcon(false);
+	return Common::kNoError;
+}
+
 bool Game::unloadCharacter(const Common::String &charname) {
 	Character *c = _scene.character(charname);
 	if (!c)
@@ -1554,6 +1598,7 @@ bool Game::HitObject::onDown() {
 }
 
 bool Game::HitObject::onUp() {
+	debug("Game::HitObject mouseup: %s", _name.c_str());
 	_game->luaScript().execute("OnButtonUp", _name);
 	_game->_isCharacterIdle = true;
 	return false;
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index 866bebb30c8..2208b0d6b99 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -23,6 +23,7 @@
 #define TETRAEDGE_GAME_GAME_H
 
 #include "common/types.h"
+#include "common/serializer.h"
 #include "common/str.h"
 #include "common/random.h"
 
@@ -117,7 +118,7 @@ public:
 	bool launchDialog(const Common::String &param_1, uint param_2, const Common::String &param_3,
 					  const Common::String &param_4, float param_5);
 	void leave(bool flag);
-	bool loadBackup(const Common::String &path);
+	void loadBackup(const Common::String &path);
 	bool loadCharacter(const Common::String &name);
 	bool loadPlayerCharacter(const Common::String &name);
 	bool loadScene(const Common::String &name);
@@ -157,6 +158,7 @@ public:
 	bool startAnimation(const Common::String &animName, int loopcount, bool reversed);
 	void startAnimationPart(const Common::String &param_1, int param_2, int param_3, int param_4, bool param_5) {};
 	void stopSound(const Common::String &name);
+	Common::Error syncGame(Common::Serializer &s); // Basically replaces saveBackup from original..
 	bool unloadCharacter(const Common::String &character);
 	bool unloadCharacters();
 	bool unloadPlayerCharacter(const Common::String &character);
@@ -195,6 +197,7 @@ public:
 	TeTimer &walkTimer() { return _walkTimer; }
 	void setExitZone(const Common::String &zone) { _exitZone = zone; }
 	Common::RandomSource &randomSource() { return _randomSource; }
+	void setLoadName(const Common::String &loadName) { _loadName = loadName; }
 
 private:
 	bool _luaShowOwnerError;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 651edb984cb..6e04185834a 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -686,6 +686,11 @@ bool InGameScene::loadLights(const Common::Path &path) {
 	_shadowNearPlane = parser.getShadowNearPlane();
 	_shadowFov = parser.getShadowFov();
 
+	TeLight::enableAll();
+	for (unsigned int i = 0; i < _lights.size(); i++) {
+		_lights[i].enable(i);
+	}
+
 	return true;
 }
 
@@ -833,7 +838,7 @@ void InGameScene::loadBackground(const Common::Path &path) {
 		AnimObject *animobj = new AnimObject();
 		animobj->_name = layoutEntry._key;
 		animobj->_layout = layoutEntry._value;
-		animobj->_layout->_tiledSurfacePtr->_frameAnim.onFinished().add<AnimObject>(animobj, &AnimObject::onFinished);
+		animobj->_layout->_tiledSurfacePtr->_frameAnim.onFinished().add(animobj, &AnimObject::onFinished);
 		if (animobj->_name != "root")
 			animobj->_layout->setVisible(false);
 		_animObjects.push_back(animobj);
@@ -874,6 +879,8 @@ void InGameScene::moveCharacterTo(const Common::String &charName, const Common::
 		if (!game->_movePlayerCharacterDisabled) {
 			c->setCurveStartLocation(c->characterSettings()._cutSceneCurveDemiPosition);
 			TeIntrusivePtr<TeBezierCurve> crve = curve(curveName);
+			if (!curveName.empty() && !crve)
+				warning("moveCharacterTo: curve %s not found", curveName.c_str());
 			c->placeOnCurve(crve);
 			c->setCurveOffset(curveOffset);
 			const Common::String walkStartAnim = c->walkAnim(Character::WalkPart_Start);
@@ -1123,6 +1130,7 @@ void InGameScene::update() {
 			error("TODO: handle _translateTime > 0 in InGameScene::update");
 		}
 		if (obj->_rotateTime >= 0) {
+			// Never actually used in the game.
 			error("TODO: handle _rotateTime > 0 in InGameScene::update");
 		}
 	}
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 45cffe2cf24..40aa69fd1d3 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -498,5 +498,27 @@ bool Inventory::updateLayout() {
 	return false;
 }
 
+Common::Error Inventory::syncState(Common::Serializer &s) {
+	unsigned int nitems = _invObjects.size();
+	s.syncAsUint32LE(nitems);
+	if (s.isLoading()) {
+		for (unsigned int i = 0; i < nitems; i++) {
+			Common::String objname;
+			s.syncString(objname);
+			addObject(objname);
+		}
+	} else if (nitems) {
+		// Add items in reverse order as the "addObject" on load will
+		// add to front of list.InventoryObject
+		auto iter = _invObjects.end();
+		while (iter != _invObjects.begin()) {
+			Common::String objname = (*iter)->name();
+			s.syncString(objname);
+			iter--;
+		}
+	}
+	return Common::kNoError;
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/inventory.h b/engines/tetraedge/game/inventory.h
index a6d88901ea8..cfcdd344e5e 100644
--- a/engines/tetraedge/game/inventory.h
+++ b/engines/tetraedge/game/inventory.h
@@ -79,6 +79,8 @@ public:
 
 	bool updateLayout();
 
+	Common::Error syncState(Common::Serializer &s);
+
 	Cellphone *cellphone() { return _cellphone; }
 
 private:
diff --git a/engines/tetraedge/game/inventory_menu.cpp b/engines/tetraedge/game/inventory_menu.cpp
index a98c855c2da..9dd250b5cc1 100644
--- a/engines/tetraedge/game/inventory_menu.cpp
+++ b/engines/tetraedge/game/inventory_menu.cpp
@@ -42,9 +42,8 @@ void InventoryMenu::leave() {
 	game->inventory().leave();
 	game->documentsBrowser().leave();
 	TeLayout *invMenu = _gui.layout("inventoryMenu");
-	if (invMenu) {
+	if (invMenu)
 		invMenu->setVisible(false);
-	}
 }
 
 void InventoryMenu::load() {
@@ -52,23 +51,22 @@ void InventoryMenu::load() {
 	setSizeType(RELATIVE_TO_PARENT);
 	TeVector3f32 usersz = userSize();
 	setSize(TeVector3f32(1.0f, 1.0f, usersz.z()));
+
 	_gui.load("InventoryMenu/InventoryMenu.lua");
-	TeLayout *menu = _gui.layoutChecked("inventoryMenu");
-	addChild(menu);
-	TeButtonLayout *btn;
-	btn = _gui.buttonLayoutChecked("quitButton");
-	btn->onMouseClickValidated().add(this, &InventoryMenu::onQuitButton);
-	btn = _gui.buttonLayoutChecked("quitBackground");
-	btn->onMouseClickValidated().add(this, &InventoryMenu::onQuitButton);
-	btn = _gui.buttonLayoutChecked("mainMenuButton");
-	btn->onMouseClickValidated().add(this, &InventoryMenu::onMainMenuButton);
-	btn = _gui.buttonLayoutChecked("documentsButton");
-	btn->onMouseClickValidated().add(this, &InventoryMenu::onDocumentsButton);
-	btn = _gui.buttonLayoutChecked("inventoryButton");
-	btn->onMouseClickValidated().add(this, &InventoryMenu::onInventoryButton);
-
-	TeLayout *invmenu = _gui.layoutChecked("inventoryMenu");
-	invmenu->setVisible(false);
+	addChild(_gui.layoutChecked("inventoryMenu"));
+	_gui.buttonLayoutChecked("quitButton")->onMouseClickValidated()
+				.add(this, &InventoryMenu::onQuitButton);
+	_gui.buttonLayoutChecked("quitBackground")->onMouseClickValidated()
+				.add(this, &InventoryMenu::onQuitButton);
+	_gui.buttonLayoutChecked("mainMenuButton")->onMouseClickValidated()
+				.add(this, &InventoryMenu::onMainMenuButton);
+	_gui.buttonLayoutChecked("documentsButton")->onMouseClickValidated()
+				.add(this, &InventoryMenu::onDocumentsButton);
+	_gui.buttonLayoutChecked("inventoryButton")->onMouseClickValidated()
+				.add(this, &InventoryMenu::onInventoryButton);
+
+	_gui.layoutChecked("inventoryMenu")->setVisible(false);
+
 	setVisible(false);
 }
 
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index f6efcc25c64..8edd80c1090 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -766,11 +766,11 @@ static int tolua_ExportedFunctions_SetCharacterOrientation00(lua_State *L) {
 	error("#ferror in function 'SetCharacterOrientation': %d %d %s", err.index, err.array, err.type);
 }
 
-static void SetCharacterAnimation(const Common::String &charname, const Common::String &animname, bool repeat, bool b2, int startframe, int endframe) {
+static void SetCharacterAnimation(const Common::String &charname, const Common::String &animname, bool repeat, bool returnToIdle, int startframe, int endframe) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charname);
 	assert(c);
-	bool result = c->setAnimation(animname, repeat, b2, false, startframe, endframe);
+	bool result = c->setAnimation(animname, repeat, returnToIdle, false, startframe, endframe);
 	if (!result) {
 		warning("[SetCharacterAnimation] Character's animation \"%s\" doesn't exist for the character\"%s\"  ",
 			animname.c_str(), charname.c_str());
@@ -825,10 +825,10 @@ static int tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00(lua_Stat
 	error("#ferror in function 'SetCharacterAnimationAndWaitForEnd': %d %d %s", err.index, err.array, err.type);
 }
 
-static void BlendCharacterAnimation(const Common::String &charname, const Common::String &animname, float blendAmount, bool repeat, bool b2) {
+static void BlendCharacterAnimation(const Common::String &charname, const Common::String &animname, float blendAmount, bool repeat, bool returnToIdle) {
 	Game *game = g_engine->getGame();
 	Character *c = game->scene().character(charname);
-	bool result = c->blendAnimation(animname, blendAmount, repeat, b2);
+	bool result = c->blendAnimation(animname, blendAmount, repeat, returnToIdle);
 	if (!result) {
 		warning("[BlendCharacterAnimation] Character's animation \"%s\" doesn't exist for the character\"%s\"  ",
 			animname.c_str(), charname.c_str());
@@ -1902,9 +1902,9 @@ static int tolua_ExportedFunctions_UnloadCharacter00(lua_State *L) {
 	error("#ferror in function 'UnloadCharacter': %d %d %s", err.index, err.array, err.type);
 }
 
-static void MoveCharacterTo(const Common::String &charName, const Common::String &curveName, float f1,float f2) {
+static void MoveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveStart, float curveEnd) {
 	Game *game = g_engine->getGame();
-	game->scene().moveCharacterTo(charName, curveName, f1, f2);
+	game->scene().moveCharacterTo(charName, curveName, curveStart, curveEnd);
 }
 
 static int tolua_ExportedFunctions_MoveCharacterTo00(lua_State *L) {
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.cpp b/engines/tetraedge/game/scene_lights_xml_parser.cpp
index 2d2da20620b..d7633185fa9 100644
--- a/engines/tetraedge/game/scene_lights_xml_parser.cpp
+++ b/engines/tetraedge/game/scene_lights_xml_parser.cpp
@@ -70,6 +70,12 @@ bool SceneLightsXmlParser::parserCallback_Lights(ParserNode *node) {
 bool SceneLightsXmlParser::parserCallback_Light(ParserNode *node) {
 	_parent = Parent_Light;
 	_lights->push_back(TeLight());
+	TeLightType ltype = TeLightType::LightTypeDirectional;
+	if (node->values["Type"] == "Spot")
+		ltype = TeLightType::LightTypeSpot;
+	else if (node->values["Type"] == "Point")
+		ltype = TeLightType::LightTypePoint;
+	_lights->back().setType(ltype);
 	return true;
 }
 
@@ -82,8 +88,8 @@ bool SceneLightsXmlParser::parserCallback_Position(ParserNode *node) {
 }
 
 bool SceneLightsXmlParser::parserCallback_Direction(ParserNode *node) {
-	float h = (atof(node->values["h"].c_str()) * 3.141593) / 180.0;
-	float v = (atof(node->values["v"].c_str()) * 3.141593) / 180.0;
+	float h = (atof(node->values["h"].c_str()) * M_PI) / 180.0;
+	float v = (atof(node->values["v"].c_str()) * M_PI) / 180.0;
 	_lights->back().setPositionRadial(TeVector2f32(h, v));
 	return true;
 }
@@ -115,7 +121,7 @@ bool SceneLightsXmlParser::parserCallback_Attenuation(ParserNode *node) {
 
 bool SceneLightsXmlParser::parserCallback_Cutoff(ParserNode *node) {
 	float f = atof(node->values["value"].c_str());
-	_lights->back().setCutoff((f * 3.141593) / 180.0);
+	_lights->back().setCutoff((f * M_PI) / 180.0);
 	return true;
 }
 
diff --git a/engines/tetraedge/game/splash_screens.cpp b/engines/tetraedge/game/splash_screens.cpp
index adbd33f04d4..f27f318b0be 100644
--- a/engines/tetraedge/game/splash_screens.cpp
+++ b/engines/tetraedge/game/splash_screens.cpp
@@ -68,7 +68,7 @@ bool SplashScreens::onAlarm() {
 		load(scriptName);
 
 		TeButtonLayout *btnLayout = buttonLayout("splash");
-		btnLayout->onMouseClickValidated().add<SplashScreens>(this, &SplashScreens::onQuitSplash);
+		btnLayout->onMouseClickValidated().add(this, &SplashScreens::onQuitSplash);
 
 		TeLayout *splash = layout("splash");
 		app->_frontLayout.addChild(splash);
diff --git a/engines/tetraedge/metaengine.cpp b/engines/tetraedge/metaengine.cpp
index ea4c2035e6c..d4d1fb4b9e7 100644
--- a/engines/tetraedge/metaengine.cpp
+++ b/engines/tetraedge/metaengine.cpp
@@ -39,10 +39,15 @@ bool TetraedgeMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSupportsListSaves) ||
 	    (f == kSupportsDeleteSave) ||
 	    (f == kSavesSupportMetaInfo) ||
-	    (f == kSavesSupportThumbnail) ||
+	   // (f == kSavesSupportThumbnail) || // TODO: support save thumbnail
 	    (f == kSupportsLoadingDuringStartup);
 }
 
+void TetraedgeMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
+	thumb.create(320, 200, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+}
+
+
 #if PLUGIN_ENABLED_DYNAMIC(TETRAEDGE)
 REGISTER_PLUGIN_DYNAMIC(TETRAEDGE, PLUGIN_TYPE_ENGINE, TetraedgeMetaEngine);
 #else
diff --git a/engines/tetraedge/metaengine.h b/engines/tetraedge/metaengine.h
index 13a43c89711..a992757f82d 100644
--- a/engines/tetraedge/metaengine.h
+++ b/engines/tetraedge/metaengine.h
@@ -31,6 +31,8 @@ public:
 
 	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
 
+	void getSavegameThumbnail(Graphics::Surface &thumb) override;
+
 	/**
 	 * Determine whether the engine supports the specified MetaEngine feature.
 	 *
diff --git a/engines/tetraedge/te/te_bezier_curve.cpp b/engines/tetraedge/te/te_bezier_curve.cpp
index 8846cbbfd3a..400fbd1fc55 100644
--- a/engines/tetraedge/te/te_bezier_curve.cpp
+++ b/engines/tetraedge/te/te_bezier_curve.cpp
@@ -28,7 +28,7 @@
 namespace Tetraedge {
 
 TeBezierCurve::TeBezierCurve() : _length(0.0), _rawLength(0.0), _lengthNeedsUpdate(true),
-_rawLengthNeedsUpdate(true), _numiterations(1000) {
+_rawLengthNeedsUpdate(true), _numIterations(1000) {
 }
 
 //long TeBezierCurve::bounds(int start);
@@ -81,8 +81,8 @@ float TeBezierCurve::length() {
 
 		TeVector3f32 lastpt = _controlPoints[0];
 		lastpt.y() = 0;
-		for (unsigned int i = 0; i < _numiterations; i++) {
-			float amount = (float)i / _numiterations;
+		for (unsigned int i = 0; i < _numIterations; i++) {
+			float amount = (float)i / _numIterations;
 			TeVector3f32 pt = retrievePoint(amount);
 			pt.y() = 0;
 			float len = (lastpt - pt).length();
@@ -95,10 +95,10 @@ float TeBezierCurve::length() {
 }
 
 void TeBezierCurve::pseudoTangent(float offset, TeVector3f32 &v1, TeVector3f32 &v2) {
-	const float step = 1.0f / _numiterations;
+	const float step = 1.0f / _numIterations;
 	if (step + offset <= 1.0f) {
 		v1 = retrievePoint(offset);
-		v2 = retrievePoint(step + offset);
+		v2 = retrievePoint(offset + step);
 	} else {
 		v1 = retrievePoint(offset - step);
 		v2 = retrievePoint(offset);
@@ -182,7 +182,7 @@ void TeBezierCurve::setControlPoints(const Common::Array<TeVector3f32> &points)
 void TeBezierCurve::setNbIterations(unsigned long iterations) {
 	_lengthNeedsUpdate = true;
 	_rawLengthNeedsUpdate = true;
-	_numiterations = iterations;
+	_numIterations = iterations;
 }
 
 /*static*/
diff --git a/engines/tetraedge/te/te_bezier_curve.h b/engines/tetraedge/te/te_bezier_curve.h
index 2f199203b2f..358edb65a81 100644
--- a/engines/tetraedge/te/te_bezier_curve.h
+++ b/engines/tetraedge/te/te_bezier_curve.h
@@ -52,10 +52,10 @@ public:
 	static void deserialize(Common::ReadStream &stream, TeBezierCurve &curve);
 
 	const Common::Array<TeVector3f32> &controlPoints() { return _controlPoints; }
-	unsigned int numIterations() const { return _numiterations; }
+	unsigned int numIterations() const { return _numIterations; }
 
 private:
-	unsigned int _numiterations;
+	unsigned int _numIterations;
 	float _length;
 	float _rawLength;
 	bool _lengthNeedsUpdate;
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index 977bd535bc8..4aac314f8bd 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -105,10 +105,12 @@ bool TeButtonLayout::onMouseLeftDown(const Common::Point &pt) {
 	enum State newState = _currentState;
 	switch (_currentState) {
 	case BUTTON_STATE_DOWN:
-		newState = BUTTON_STATE_UP;
+		if (!mouseIn)
+			newState = BUTTON_STATE_UP;
 		/*
 		// TODO: should this be a click?
 		if (mouseIn) {
+			newState = BUTTON_STATE_UP;
 			debug("mouse clicked button '%s' (from leftdown)", name().c_str());
 			if (!_validationSound.empty()) {
 				TeSoundManager *sndMgr = g_engine->getSoundManager();
@@ -128,10 +130,6 @@ bool TeButtonLayout::onMouseLeftDown(const Common::Point &pt) {
 		break;
 	}
 	setState(newState);
-	// FIXME: Why does this item block the mouse events.. should be below
-	// the "yes" button but seems it's not?
-	if (name() == "menu")
-		return false;
 	return mouseIn && !_clickPassThrough;
 }
 
@@ -140,7 +138,7 @@ bool TeButtonLayout::onMouseLeftUp(const Common::Point &pt) {
 		return false;
 
 	// Note: This doesn't exactly reproduce the original behavior, it's
-	// very simplified.
+	// somewhat simplified.
 	bool mouseIn = isMouseIn(pt);
 
 	if (mouseIn)
@@ -158,10 +156,6 @@ bool TeButtonLayout::onMouseLeftUp(const Common::Point &pt) {
 			}
 			setState(newState);
 			_onMouseClickValidatedSignal.call();
-			// FIXME: Why does this item block the mouse events.. should be below
-			// the "yes" button but seems it's not?
-			if (name() == "menu")
-				return false;
 			return !_clickPassThrough;
 		}
 		break;
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index a85315b77f0..8f586a7d495 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -49,7 +49,7 @@ void TeCore::create() {
 	// TODO: Get language from the game definition.  For now just default to en.
 	language("en");
 	_coreNotReady = false;
-	_activityTrackingTimer.alarmSignal().add<TeCore>(this, &TeCore::onActivityTrackingAlarm);
+	_activityTrackingTimer.alarmSignal().add(this, &TeCore::onActivityTrackingAlarm);
 	warning("TODO: TeCore::create: Finish implementing me.");
 }
 
diff --git a/engines/tetraedge/te/te_light.cpp b/engines/tetraedge/te/te_light.cpp
index f02c6990e8b..61ed3f02339 100644
--- a/engines/tetraedge/te/te_light.cpp
+++ b/engines/tetraedge/te/te_light.cpp
@@ -38,7 +38,7 @@ static inline uint _toGlLight(uint lightno) {
 TeColor TeLight::_globalAmbientColor;
 
 TeLight::TeLight() : _colAmbient(0, 0, 0, 0xff), _colDiffuse(0, 0, 0, 0xff), _colSpecular(0xff, 0xff, 0xff, 0xff),
-_constAtten(0.0f), _linearAtten(1.0f), _quadraticAtten(0.0f), _cutoff(0.0f), _exponent(0.0f), _type(LightTypePoint),
+_constAtten(1.0f), _linearAtten(0.0f), _quadraticAtten(0.0f), _cutoff(0.0f), _exponent(0.0f), _type(LightTypePoint),
 _displaySize(3.0)
 {
 }
@@ -122,9 +122,9 @@ void TeLight::update(uint lightno) {
 		pos[2] = _position3d.z();
 		pos[3] = 1.0f;
 		glLightfv(glLight, GL_POSITION, pos);
-		glLightfv(glLight, GL_CONSTANT_ATTENUATION, &_constAtten);
-		glLightfv(glLight, GL_LINEAR_ATTENUATION, &_linearAtten);
-		glLightfv(glLight, GL_QUADRATIC_ATTENUATION, &_quadraticAtten);
+		glLightf(glLight, GL_CONSTANT_ATTENUATION, _constAtten);
+		glLightf(glLight, GL_LINEAR_ATTENUATION, _linearAtten);
+		glLightf(glLight, GL_QUADRATIC_ATTENUATION, _quadraticAtten);
 	}
 
 	if (_type == LightTypeDirectional) {
@@ -140,8 +140,6 @@ void TeLight::update(uint lightno) {
 		glLightfv(glLight, GL_POSITION, pos);
 	}
 
-	float atten;
-	uint atype;
 	if (_type == LightTypeSpot) {
 		float pos[4];
 		float cosx = cosf(_positionRadial.getX());
@@ -154,13 +152,10 @@ void TeLight::update(uint lightno) {
 		pos[3] = 0.0;
 		glLightfv(glLight, GL_SPOT_DIRECTION, pos);
 		glLightf(glLight, GL_SPOT_CUTOFF, (_cutoff * 180.0) / M_PI);
-		atten = _exponent;
-		atype = GL_SPOT_EXPONENT;
+		glLightf(glLight, GL_SPOT_EXPONENT, _exponent);
 	} else {
-		atten = 180.0;
-		atype = GL_SPOT_CUTOFF;
+		glLightf(glLight, GL_SPOT_CUTOFF, 180.0);
 	}
-	glLightf(glLight, atype, atten);
 }
 
 /*static*/
diff --git a/engines/tetraedge/te/te_light.h b/engines/tetraedge/te/te_light.h
index 72f2a05cd38..2ed67430525 100644
--- a/engines/tetraedge/te/te_light.h
+++ b/engines/tetraedge/te/te_light.h
@@ -67,6 +67,7 @@ public:
 	void setDisplaySize(float val) { _displaySize = val; }
 	void setPosition3d(const TeVector3f32 &pos) { _position3d = pos; }
 	void setPositionRadial(const TeVector2f32 &pos) { _positionRadial = pos; }
+	void setType(TeLightType ltype) { _type = ltype; }
 
 	const TeVector2f32 &positionRadial() const { return _positionRadial; }
 	const TeVector3f32 &position3d() const { return _position3d; }
diff --git a/engines/tetraedge/te/te_lua_context.cpp b/engines/tetraedge/te/te_lua_context.cpp
index dff65386901..10a2f85ca54 100644
--- a/engines/tetraedge/te/te_lua_context.cpp
+++ b/engines/tetraedge/te/te_lua_context.cpp
@@ -114,5 +114,92 @@ void TeLuaContext::setInRegistry(const Common::String &name, TeLuaGUI *gui) {
 	lua_settable(_luaState, LUA_REGISTRYINDEX);
 }
 
+// Types for save file.  Aligned with the Lua types at type of
+// writing, but don't save them just in case they could change.
+enum TeLuaSaveVarType {
+	None = 0,
+	Boolean = 1,
+	Number = 3,
+	String = 4
+};
+
+Common::Error TeLuaContext::syncState(Common::Serializer &s) {
+	// Save/Load globals.  The format of saving is:
+	// [type][name][val] [type][name][val]...
+	// The type of "None" (0) is the end of the list (and has no name/val).
+	if (s.isSaving()) {
+		lua_pushvalue(_luaState, LUA_GLOBALSINDEX);
+		lua_pushnil(_luaState);
+		int nextresult = lua_next(_luaState, -2);
+		while (true) {
+			if (nextresult == 0) {
+				TeLuaSaveVarType stype = None;
+				s.syncAsUint32LE(stype);
+				lua_settop(_luaState, -2);
+				break;
+			}
+			unsigned int vtype = lua_type(_luaState, -1);
+			if (vtype == LUA_TBOOLEAN) {
+				TeLuaSaveVarType stype = Boolean;
+				Common::String name = lua_tolstring(_luaState, -2, nullptr);
+				s.syncAsUint32LE(stype);
+				s.syncString(name);
+				bool val = lua_toboolean(_luaState, -1);
+				s.syncAsByte(val);
+			} else if (vtype == LUA_TNUMBER) {
+				TeLuaSaveVarType stype = Number;
+				Common::String name = lua_tolstring(_luaState, -2, nullptr);
+				s.syncAsUint32LE(stype);
+				s.syncString(name);
+				double val = lua_tonumber(_luaState, -1);
+				s.syncAsDoubleLE(val);
+			} else if (vtype == LUA_TSTRING) {
+				TeLuaSaveVarType stype = String;
+				Common::String name = lua_tolstring(_luaState, -2, nullptr);
+				s.syncAsUint32LE(stype);
+				s.syncString(name);
+				Common::String val = lua_tostring(_luaState, -1);
+				s.syncString(val);
+			}
+			lua_settop(_luaState, -2);
+			nextresult = lua_next(_luaState, -2);
+		}
+	} else {
+		warning("Confirm loading in TeLuaContext::syncState");
+		// loading
+		TeLuaSaveVarType vtype = None;
+		s.syncAsUint32LE(vtype);
+		while (vtype != None) {
+			switch (vtype) {
+				case Boolean: {
+					byte b;
+					s.syncAsByte(b);
+					lua_pushboolean(_luaState, b);
+					break;
+				}
+				case Number: {
+					float d;
+					s.syncAsDoubleLE(d);
+					lua_pushnumber(_luaState, d);
+					break;
+				}
+				case String: {
+					Common::String str;
+					s.syncString(str);
+					lua_pushstring(_luaState, str.c_str());
+					break;
+				}
+				default:
+					error("Unexpected lua type on load %d", (int)vtype);
+			}
+			Common::String name;
+			s.syncString(name);
+			lua_setglobal(_luaState, name.c_str());
+			s.syncAsUint32LE(vtype);
+		}
+	}
+
+	return Common::kNoError;
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_lua_context.h b/engines/tetraedge/te/te_lua_context.h
index aa62bec492f..47597ec5f59 100644
--- a/engines/tetraedge/te/te_lua_context.h
+++ b/engines/tetraedge/te/te_lua_context.h
@@ -22,7 +22,9 @@
 #ifndef TETRAEDGE_TE_TE_LUA_CONTEXT_H
 #define TETRAEDGE_TE_TE_LUA_CONTEXT_H
 
+#include "common/error.h"
 #include "common/str.h"
+#include "common/serializer.h"
 
 #include "tetraedge/te/te_variant.h"
 
@@ -67,6 +69,9 @@ public:
 	void setGlobal(const Common::String &name, const Common::String &val);
 
 	void setInRegistry(const Common::String &name, TeLuaGUI *gui);
+
+	Common::Error syncState(Common::Serializer &s);
+
 private:
 	lua_State *_luaState;
 };
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index e8d8c93ad04..a7dbead85b3 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -400,6 +400,12 @@ int spriteLayoutBindings(lua_State *L) {
 		layout->setName(Common::String::format("%p", (void *)layout));
 	}
 
+	// WORKAROUND: ValStreet scene 11070 has a misnamed animation in the script.
+	// All references to it in script refer to it with the "ab".
+	// This scene is broken in the original game too.
+	if (layout->name() == "11070-1")
+		layout->setName("ab11070-1");
+
 	if (playNow) {
 		layout->play();
 	} else {
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index ebafd7b232e..5757a99031b 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -191,7 +191,7 @@ void TeModel::update() {
 			TeTRS trs = getBone(_modelAnim, b);
 			for (unsigned int i = 0; i < _boneBlenders.size(); i++) {
 				BonesBlender *blender = _boneBlenders[i];
-				float complete = MIN((blender->_timer.getTimeFromStart() / 1000000.0f) / blender->_seconds, 1.0f);
+				float complete = blender->coef();
 				TeTRS endTRS = getBone(blender->_anim, b);
 				if (complete == 1.0f) {
 					delete blender;
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index 83c1c1f2716..effca334c15 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -25,6 +25,8 @@
 #include "common/substream.h"
 #include "common/zlib.h"
 
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_model_animation.h"
 
 namespace Tetraedge {
@@ -180,8 +182,9 @@ int TeModelAnimation::lastFrame() const {
 }
 
 bool TeModelAnimation::load(const Common::Path &path) {
+	Common::Path foundPath = g_engine->getCore()->findFile(path);
 	Common::File modelFile;
-	if (!modelFile.open(path)) {
+	if (!modelFile.open(foundPath)) {
 		warning("[TeModel::load] Can't open file : %s.", path.toString().c_str());
 		return false;
 	}
diff --git a/engines/tetraedge/te/te_name_val_xml_parser.cpp b/engines/tetraedge/te/te_name_val_xml_parser.cpp
index 50e89b8605c..c5a4d96f0be 100644
--- a/engines/tetraedge/te/te_name_val_xml_parser.cpp
+++ b/engines/tetraedge/te/te_name_val_xml_parser.cpp
@@ -25,7 +25,17 @@ namespace Tetraedge {
 
 // Parser callback methods
 bool TeNameValXmlParser::parserCallback_value(ParserNode *node) {
-	_map.setVal(node->values["name"], node->values["value"]);
+	Common::String valStr = node->values["value"];
+
+	// Replace """ with " character.  This is the only character
+	// entity used in the game files.
+	unsigned int qpos = valStr.find(""");
+	while (qpos != Common::String::npos) {
+		valStr.replace(qpos, 6, "\"");
+		qpos = valStr.find(""");
+	}
+
+	_map.setVal(node->values["name"], valStr);
 	return true;
 }
 
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index a8da12c91ca..8a0e602b69b 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -53,7 +53,10 @@ void TeTextBase2::build() {
 	_size = TeVector2s32(0, 0);
 	_wrappedLines.clear();
 
-	font->wordWrapText(_text, _fontSize, _drawRect._x, _wrappedLines);
+	if (_text.find(' ') != Common::String::npos)
+		font->wordWrapText(_text, _fontSize, _drawRect._x, _wrappedLines);
+	else
+		_wrappedLines.push_back(_text);
 
 	Common::Array<float> lineoffsets;
 	float lineHeight = font->getHeight(_fontSize);
diff --git a/engines/tetraedge/te/te_text_layout.cpp b/engines/tetraedge/te/te_text_layout.cpp
index 8fe5cbb9d11..db3a34aeadf 100644
--- a/engines/tetraedge/te/te_text_layout.cpp
+++ b/engines/tetraedge/te/te_text_layout.cpp
@@ -151,27 +151,17 @@ const TeVector2s32 &TeTextLayout::textSize() const {
 	return _base.size();
 }
 
-static int _roundVal(int i) {
-	// this is a weird/broken rounding but seems to match
-	// the original?
-	i = ((i & 1) | (i >> 1));
-	return i + i;
-}
-
 void TeTextLayout::updateSize() {
 	if (!_sizeChanged)
 		return;
 
 	TeLayout::updateSize();
 
-	TeMatrix4x4 transform = transformationMatrix();
-	static const TeVector3f32 zeroVec(0, 0, 0);
-	static const TeVector3f32 xUnitVec(1, 0, 0);
-	static const TeVector3f32 yUnitVec(0, 1, 0);
+	TeMatrix4x4 transform = worldTransformationMatrix();
 
-	const TeVector3f32 v1 = transform * zeroVec;
-	const TeVector3f32 v2 = transform * xUnitVec;
-	const TeVector3f32 v3 = transform * yUnitVec;
+	const TeVector3f32 v1 = transform * TeVector3f32(0, 0, 0);
+	const TeVector3f32 v2 = transform * TeVector3f32(1, 0, 0);
+	const TeVector3f32 v3 = transform * TeVector3f32(0, 1, 0);
 
 	const TeVector3f32 newSize((v2 - v1).length(), (v3 - v1).length(), 1.0);
 	const TeVector3f32 thisSize = size();
@@ -181,25 +171,12 @@ void TeTextLayout::updateSize() {
 
 	float newFontSize = 0;
 	if (_textSizeType == 0 || (_textSizeType == 1 && _textSizeProportionalToWidth == 0)) {
-		if (_baseFontSize < 0)
-			newFontSize = _roundVal(_baseFontSize);
-		else
-			newFontSize = _baseFontSize;
-	} else if (_textSizeType == 1) {
 		newFontSize = _baseFontSize;
-		float sizeProportionalToWidth = _textSizeProportionalToWidth;
-		if (_textSizeProportionalToWidth < 0)
-			sizeProportionalToWidth = _roundVal(_textSizeProportionalToWidth);
-		else
-			sizeProportionalToWidth = _textSizeProportionalToWidth;
-		if (_baseFontSize < 0)
-			newFontSize = _roundVal(_baseFontSize);
-		else
-			newFontSize = _baseFontSize;
-		newFontSize = (thisSize.x() / sizeProportionalToWidth) * newFontSize;
+	} else if (_textSizeType == 1) {
+		newFontSize = (thisSize.x() / _textSizeProportionalToWidth) * _baseFontSize;
 	}
 
-	//newFontSize *= thisSize.y();
+	newFontSize *= newSize.y();
 
 	_base.setFontSize(newFontSize);
 	_base.build();
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index 1e4245b803a..0b4337f9463 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -34,7 +34,7 @@ static void getRangeIntersection(float start1,float end1,float start2,float end2
 
 TeTiledSurface::TeTiledSurface() : _shouldDraw(true), _codec(nullptr), _colorKeyActive(false), _colorKeyTolerence(0),
 _bottomCrop(0), _topCrop(0), _leftCrop(0), _rightCrop(0), _imgFormat(TeImage::INVALID) {
-	_frameAnim.frameChangedSignal().add<TeTiledSurface>(this, &TeTiledSurface::onFrameAnimCurrentFrameChanged);
+	_frameAnim.frameChangedSignal().add(this, &TeTiledSurface::onFrameAnimCurrentFrameChanged);
 }
 
 void TeTiledSurface::cont() {
diff --git a/engines/tetraedge/te/te_timer.h b/engines/tetraedge/te/te_timer.h
index 1cf59ac72ff..824dfd70539 100644
--- a/engines/tetraedge/te/te_timer.h
+++ b/engines/tetraedge/te/te_timer.h
@@ -23,6 +23,7 @@
 #define TETRAEDGE_TE_TE_TIMER_H
 
 #include "common/array.h"
+
 #include "tetraedge/te/te_signal.h"
 #include "tetraedge/te/te_real_timer.h"
 
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 2b7329ad40b..8582389c303 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -27,6 +27,7 @@
 #include "common/debug-channels.h"
 #include "common/events.h"
 #include "common/system.h"
+#include "common/savefile.h"
 #include "engines/util.h"
 #include "engines/dialogs.h"
 #include "graphics/palette.h"
@@ -125,6 +126,39 @@ Common::String TetraedgeEngine::getGameId() const {
 	return _gameDescription->gameId;
 }
 
+bool TetraedgeEngine::canSaveGameStateCurrently() {
+	return canSaveAutosaveCurrently() && !_application->isLockCursor();
+}
+
+bool TetraedgeEngine::canSaveAutosaveCurrently() {
+	return _game && _application && _game->running()
+		&& !_game->currentScene().empty() && !_game->currentZone().empty();
+}
+
+Common::Error TetraedgeEngine::loadGameState(int slot) {
+	// In case autosaves are on, do a save first before loading the new save
+	saveAutosaveIfEnabled();
+
+	Common::String saveStateName = getSaveStateName(slot);
+
+	Common::InSaveFile *saveFile = getSaveFileManager()->openForLoading(saveStateName);
+
+	if (!saveFile)
+		return Common::kReadingFailed;
+
+	// The game will reopen the file and do the loading, see Game::initLoadedBackupData
+	_game->setLoadName(saveStateName);
+
+	delete saveFile;
+	return Common::kNoError;
+}
+
+Common::Error TetraedgeEngine::loadGameStream(Common::SeekableReadStream *stream) {
+	Common::Serializer s(stream, nullptr);
+	Common::Error retval = syncGame(s);
+	return retval;
+}
+
 void TetraedgeEngine::configureSearchPaths() {
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 	SearchMan.addSubDirectoryMatching(gameDataDir, "Resources", 0, 5);
@@ -167,8 +201,6 @@ Common::Error TetraedgeEngine::run() {
 
 		_application->run();
 
-		// Delay for a bit. All events loops should have a delay
-		// to prevent the system being unduly loaded
 		g_system->delayMillis(10);
 	}
 
@@ -176,14 +208,7 @@ Common::Error TetraedgeEngine::run() {
 }
 
 Common::Error TetraedgeEngine::syncGame(Common::Serializer &s) {
-	// The Serializer has methods isLoading() and isSaving()
-	// if you need to specific steps; for example setting
-	// an array size after reading it's length, whereas
-	// for saving it would write the existing array's length
-	int dummy = 0;
-	s.syncAsUint32LE(dummy);
-
-	return Common::kNoError;
+	return getGame()->syncGame(s);
 }
 
 void TetraedgeEngine::openConfigDialog() {
diff --git a/engines/tetraedge/tetraedge.h b/engines/tetraedge/tetraedge.h
index b1cd26b2948..f942697df21 100644
--- a/engines/tetraedge/tetraedge.h
+++ b/engines/tetraedge/tetraedge.h
@@ -92,9 +92,9 @@ public:
 	bool canLoadGameStateCurrently() override {
 		return true;
 	}
-	bool canSaveGameStateCurrently() override {
-		return true;
-	}
+
+	bool canSaveGameStateCurrently() override;
+	bool canSaveAutosaveCurrently() override;
 
 	/**
 	 * Uses a serializer to allow implementing savegame
@@ -106,11 +106,9 @@ public:
 		Common::Serializer s(nullptr, stream);
 		return syncGame(s);
 	}
-	Common::Error loadGameStream(Common::SeekableReadStream *stream) override {
-		Common::Serializer s(stream, nullptr);
-		return syncGame(s);
-	}
 
+	Common::Error loadGameState(int slot) override;
+	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 	int getDefaultScreenWidth() const;
 	int getDefaultScreenHeight() const;
 
diff --git a/engines/tetraedge/to_lua.cpp b/engines/tetraedge/to_lua.cpp
index 979732d03d4..ffc930156a2 100644
--- a/engines/tetraedge/to_lua.cpp
+++ b/engines/tetraedge/to_lua.cpp
@@ -32,7 +32,7 @@ static char toluaname[128] = "tolua.";
 
 static void tolua_push_globals_table(lua_State *L) {
 	/*
-	lua_pushvalue(L, LUA_REGISTRYINDEX);
+	lua_pushvalue(L, LUA_GLOBALSINDEX);
 	lua_pushnumber(L, 2.0);
 	lua_rawget(L, -2);
 	lua_remove(L, -2);


Commit: 769d8d9b42f34faccef8b90e71f79a55b783e8bb
    https://github.com/scummvm/scummvm/commit/769d8d9b42f34faccef8b90e71f79a55b783e8bb
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Fix a lot of small rendering bugs.

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_font3.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_lua_context.cpp
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_sprite_layout.cpp
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_vector2s32.cpp
    engines/tetraedge/te/te_vector2s32.h
    engines/tetraedge/te/te_visual_fade.cpp
    engines/tetraedge/tetraedge.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 9055246d13e..4f99e4aeb5f 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -91,6 +91,7 @@ void Application::create() {
 	_mainWindow.setSize(TeVector3f32(winWidth, winHeight, 0.0));
 	_mainWindow.setSizeType(TeILayout::ABSOLUTE);
 	_mainWindow.setPositionType(TeILayout::ABSOLUTE);
+	_mainWindow.setPosition(TeVector3f32(0.0f, 0.0f ,0.0f));
 
 	TeResourceManager *resmgr = g_engine->getResourceManager();
 	_fontComic = resmgr->getResourceNoSearch<TeFont3>("Common/Fonts/ComicRelief.ttf");
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index fae906c6526..846691cc696 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -387,6 +387,8 @@ void Game::finishGame() {
 
 void Game::initLoadedBackupData() {
 	bool warpFlag = true;
+	Application *app = g_engine->getApplication();
+	Common::String firstWarpPath;
 	if (!_loadName.empty()) {
 		warpFlag = false;
 		Common::InSaveFile *saveFile = g_engine->getSaveFileManager()->openForLoading(_loadName);
@@ -396,19 +398,19 @@ void Game::initLoadedBackupData() {
 			if (MetaEngine::readSavegameHeader(saveFile, &header))
 				g_engine->setTotalPlayTime(header.playtime);
 		}
+	} else {
+		firstWarpPath = app->_firstWarpPath;
+		_currentScene = app->_firstScene;
+		_currentZone = app->_firstZone;
+		_playedTimer.start();
+		_objectsTakenVal = 0;
+		for (int i = 0; i < ARRAYSIZE(_objectsTakenBits); i++) {
+			_objectsTakenBits[i] = 0;
+		}
+		_dialogsTold = 0;
+		if (_loadName == "NO_OWNER")
+			_luaShowOwnerError = true;
 	}
-	Application *app = g_engine->getApplication();
-	const Common::String firstWarpPath = app->_firstWarpPath;
-	_currentScene = app->_firstScene;
-	_currentZone = app->_firstZone;
-	_playedTimer.start();
-	_objectsTakenVal = 0;
-	for (int i = 0; i < ARRAYSIZE(_objectsTakenBits); i++) {
-		_objectsTakenBits[i] = 0;
-	}
-	_dialogsTold = 0;
-	if (_loadName == "NO_OWNER")
-		_luaShowOwnerError = true;
 	_gameLoadState = 0;
 	app->showLoadingIcon(false);
 	_loadName.clear();
@@ -798,13 +800,13 @@ bool Game::onCallNumber(Common::String val) {
 	return false;
 }
 
-bool Game::onCharacterAnimationFinished(const Common::String &val) {
+bool Game::onCharacterAnimationFinished(const Common::String &charName) {
 	if (!_scene._character)
 		return false;
 
 	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
 		YieldedCallback &cb = _yieldedCallbacks[i];
-		if (cb._luaFnName == "OnCharacterAnimationFinished" && cb._luaParam == val) {
+		if (cb._luaFnName == "OnCharacterAnimationFinished" && cb._luaParam == charName) {
 			TeLuaThread *lua = cb._luaThread;
 			_yieldedCallbacks.remove_at(i);
 			if (lua) {
@@ -814,7 +816,7 @@ bool Game::onCharacterAnimationFinished(const Common::String &val) {
 			break;
 		}
 	}
-	_luaScript.execute("OnCharacterAnimationFinished", val);
+	_luaScript.execute("OnCharacterAnimationFinished", charName);
 	return false;
 }
 
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 6e04185834a..fc9df5e7ff6 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -105,8 +105,8 @@ bool InGameScene::addMarker(const Common::String &markerName, const Common::Stri
 			newPos.x() = frontLayoutSize.x() * (x / 100.0);
 			newPos.y() = frontLayoutSize.y() * (y / 100.0);
 		} else {
-			newPos.x() = x / 800.0;
-			newPos.y() = y / 600.0;
+			newPos.x() = x / g_engine->getDefaultScreenWidth();
+			newPos.y() = y / g_engine->getDefaultScreenHeight();
 		}
 		markerSprite->setPosition(newPos);
 
@@ -865,9 +865,11 @@ void InGameScene::loadInteractions(const Common::Path &path) {
 	Game *game = g_engine->getGame();
 	TeSpriteLayout *root = game->findSpriteLayoutByName(bgbackground, "root");
 	TeLayout *background = _hitObjectGui.layoutChecked("background");
-	// TODO: For all TeButtonLayout childen of background, call
-	// setDoubleValidationProtectionEnabled(false)
-	// For now our button doesn't implement that.
+	for (auto *child : background->childList()) {
+		TeButtonLayout *btn = dynamic_cast<TeButtonLayout *>(child);
+		if (btn)
+			btn->setDoubleValidationProtectionEnabled(false);
+	}
 	background->setRatioMode(TeILayout::RATIO_MODE_NONE);
 	root->addChild(background);
 }
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 40aa69fd1d3..aaf487d9bfb 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -498,25 +498,43 @@ bool Inventory::updateLayout() {
 	return false;
 }
 
+#define DEBUG_SAVELOAD 1
+
 Common::Error Inventory::syncState(Common::Serializer &s) {
 	unsigned int nitems = _invObjects.size();
 	s.syncAsUint32LE(nitems);
 	if (s.isLoading()) {
+#if DEBUG_SAVELOAD
+		debug("Inventory::syncState: --- Loading %d inventory items: ---", nitems);
+#endif
 		for (unsigned int i = 0; i < nitems; i++) {
 			Common::String objname;
 			s.syncString(objname);
 			addObject(objname);
+#if DEBUG_SAVELOAD
+			debug("Inventory::syncState: 	%s", objname.c_str());
+#endif
 		}
 	} else if (nitems) {
+#if DEBUG_SAVELOAD
+		debug("Inventory::syncState: --- Saving %d inventory items: --- ", _invObjects.size());
+#endif
 		// Add items in reverse order as the "addObject" on load will
 		// add to front of list.InventoryObject
 		auto iter = _invObjects.end();
 		while (iter != _invObjects.begin()) {
+			iter--;
 			Common::String objname = (*iter)->name();
 			s.syncString(objname);
-			iter--;
+#if DEBUG_SAVELOAD
+			debug("Inventory::syncState: 	%s", objname.c_str());
+#endif
 		}
 	}
+
+#if DEBUG_SAVELOAD
+	debug("Inventory::syncState: -------- end --------");
+#endif
 	return Common::kNoError;
 }
 
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 8edd80c1090..0a199cffa0c 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -1716,9 +1716,13 @@ static int tolua_ExportedFunctions_PlayRandomSound00(lua_State *L) {
 
 static void PlayMusic(const Common::String &path) {
 	TeMusic &music = g_engine->getApplication()->music();
+	// Note: stop and set repeat before starting,
+	// very slightly different to original because we can't
+	// change repeat value after starting.
+	music.stop();
+	music.repeat(false);
 	music.load(path);
 	music.play();
-	music.repeat(false);
 	music.volume(1.0);
 }
 
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index a129e438679..362582758a7 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -202,7 +202,17 @@ bool Te3DTexture::load(const TeImage &img) {
 
 /*static*/
 TeVector2s32 Te3DTexture::optimisedSize(const TeVector2s32 &size) {
-	/* The maths here is a bit funky but it just picks the nearest power of 2 (up) */
+	//
+	// Note: When we enabled optimized sizes it leaves artifacts around movies
+	// etc unless the render size is exactly 800x600.
+	//
+	// This probably means there is a rounding error somewhere else, just leave
+	// off for now.
+	//
+	if (g_engine->getDefaultScreenWidth() != 800)
+		return size;
+
+	// The maths here is a bit funky but it just picks the nearest power of 2 (up)
 	int xsize = size._x - 1;
 	int ysize = size._y - 1;
 
@@ -224,6 +234,7 @@ TeVector2s32 Te3DTexture::optimisedSize(const TeVector2s32 &size) {
 		v2 = 8;
 	}
 	return TeVector2s32(v1, v2);
+	
 }
 
 /*static*/
diff --git a/engines/tetraedge/te/te_font3.cpp b/engines/tetraedge/te/te_font3.cpp
index 54367dea86d..7ce34b4fa9b 100644
--- a/engines/tetraedge/te/te_font3.cpp
+++ b/engines/tetraedge/te/te_font3.cpp
@@ -86,7 +86,7 @@ Graphics::Font *TeFont3::getAtSize(unsigned int size) {
 		error("TeFont3::: Couldn't open font file %s.", getAccessName().toString().c_str());
 
 	_fontFile.seek(0);
-	Graphics::Font *newFont = Graphics::loadTTFFont(_fontFile, size);
+	Graphics::Font *newFont = Graphics::loadTTFFont(_fontFile, size, Graphics::kTTFSizeModeCharacter, 0, Graphics::kTTFRenderModeNormal);
 	if (!newFont) {
 		error("TeFont3::: Couldn't load font %s at size %d.", _loadedPath.toString().c_str(), size);
 	}
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 41185547d95..33aac199b26 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -81,8 +81,26 @@ float TeFreeMoveZone::bordersDistance() const {
 	return _graph->_bordersDistance;
 }
 
+TeVector2s32 TeFreeMoveZone::aStarResolution() const {
+	TeVector2f32 diff = (_someGridVec2 - _someGridVec1);
+	TeVector2s32 retval = TeVector2s32(diff / _gridOffsetSomething) + TeVector2s32(1, 1);
+	if (retval._x > 2000)
+		retval._x = 200;
+	if (retval._y > 2000)
+		retval._y = 200;
+	return retval;
+}
+
 void TeFreeMoveZone::buildAStar() {
-	error("TODO: Implement TeFreeMoveZone::buildAStar");
+	preUpdateGrid();
+	TeVector2s32 resolution = aStarResolution();
+	_graph->setSize(resolution);
+	if (!_loadedFromBin) {
+		error("TODO: Implement TeFreeMoveZone::buildAStar for *not* loaded from bin case");
+	} else {
+		// Loaded from bin..
+		error("TODO: Implement TeFreeMoveZone::buildAStar for loaded from bin case");
+	}
 }
 
 void TeFreeMoveZone::calcGridMatrix() {
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index 8861b007284..0bd8c4a98b2 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -115,6 +115,8 @@ public:
 			Common::Array<TePickMesh2*> &pickMeshes, TeVector3f32 *outloc, bool lastHitFirst);
 
 private:
+	TeVector2s32 aStarResolution() const;
+
 	Common::Array<TeActZone> *_actzones;
 	Common::Array<TeBlocker> *_blockers;
 	Common::Array<TeRectBlocker> *_rectBlockers;
diff --git a/engines/tetraedge/te/te_lua_context.cpp b/engines/tetraedge/te/te_lua_context.cpp
index 10a2f85ca54..9125975b88f 100644
--- a/engines/tetraedge/te/te_lua_context.cpp
+++ b/engines/tetraedge/te/te_lua_context.cpp
@@ -123,11 +123,17 @@ enum TeLuaSaveVarType {
 	String = 4
 };
 
+#define DEBUG_SAVELOAD 1
+
 Common::Error TeLuaContext::syncState(Common::Serializer &s) {
 	// Save/Load globals.  The format of saving is:
 	// [type][name][val] [type][name][val]...
+	// Vals can be string, number (uint32), or boolean (byte)
 	// The type of "None" (0) is the end of the list (and has no name/val).
 	if (s.isSaving()) {
+#if DEBUG_SAVELOAD
+		debug("TeLuaContext::syncState: --- Saving globals: ---");
+#endif
 		lua_pushvalue(_luaState, LUA_GLOBALSINDEX);
 		lua_pushnil(_luaState);
 		int nextresult = lua_next(_luaState, -2);
@@ -139,66 +145,88 @@ Common::Error TeLuaContext::syncState(Common::Serializer &s) {
 				break;
 			}
 			unsigned int vtype = lua_type(_luaState, -1);
+			Common::String name = lua_tolstring(_luaState, -2, nullptr);
 			if (vtype == LUA_TBOOLEAN) {
 				TeLuaSaveVarType stype = Boolean;
-				Common::String name = lua_tolstring(_luaState, -2, nullptr);
 				s.syncAsUint32LE(stype);
 				s.syncString(name);
 				bool val = lua_toboolean(_luaState, -1);
 				s.syncAsByte(val);
+#if DEBUG_SAVELOAD
+				debug("TeLuaContext::syncState: bool %s = %s", name.c_str(), val ? "true" : "false");
+#endif
 			} else if (vtype == LUA_TNUMBER) {
 				TeLuaSaveVarType stype = Number;
-				Common::String name = lua_tolstring(_luaState, -2, nullptr);
 				s.syncAsUint32LE(stype);
 				s.syncString(name);
 				double val = lua_tonumber(_luaState, -1);
 				s.syncAsDoubleLE(val);
+#if DEBUG_SAVELOAD
+				debug("TeLuaContext::syncState: num %s = %f", name.c_str(), val);
+#endif
 			} else if (vtype == LUA_TSTRING) {
 				TeLuaSaveVarType stype = String;
-				Common::String name = lua_tolstring(_luaState, -2, nullptr);
 				s.syncAsUint32LE(stype);
 				s.syncString(name);
 				Common::String val = lua_tostring(_luaState, -1);
 				s.syncString(val);
+#if DEBUG_SAVELOAD
+				debug("TeLuaContext::syncState: str %s = '%s'", name.c_str(), val.c_str());
+#endif
 			}
 			lua_settop(_luaState, -2);
 			nextresult = lua_next(_luaState, -2);
 		}
 	} else {
-		warning("Confirm loading in TeLuaContext::syncState");
+#if DEBUG_SAVELOAD
+		debug("TeLuaContext::syncState: --- Loading globals: --- ");
+#endif
 		// loading
 		TeLuaSaveVarType vtype = None;
 		s.syncAsUint32LE(vtype);
 		while (vtype != None) {
+			Common::String name;
+			s.syncString(name);
 			switch (vtype) {
 				case Boolean: {
 					byte b;
 					s.syncAsByte(b);
 					lua_pushboolean(_luaState, b);
+#if DEBUG_SAVELOAD
+					debug("TeLuaContext::syncState: bool %s = %s", name.c_str(), b ? "true" : "false");
+#endif
 					break;
 				}
 				case Number: {
 					float d;
 					s.syncAsDoubleLE(d);
 					lua_pushnumber(_luaState, d);
+#if DEBUG_SAVELOAD
+					debug("TeLuaContext::syncState: num %s = %f", name.c_str(), d);
+#endif
 					break;
 				}
 				case String: {
 					Common::String str;
 					s.syncString(str);
 					lua_pushstring(_luaState, str.c_str());
+#if DEBUG_SAVELOAD
+					debug("TeLuaContext::syncState: str %s = '%s'", name.c_str(), str.c_str());
+#endif
 					break;
 				}
 				default:
 					error("Unexpected lua type on load %d", (int)vtype);
 			}
-			Common::String name;
-			s.syncString(name);
 			lua_setglobal(_luaState, name.c_str());
 			s.syncAsUint32LE(vtype);
 		}
 	}
 
+#if DEBUG_SAVELOAD
+	debug("TeLuaContext::syncState: -------- end --------");
+#endif
+
 	return Common::kNoError;
 }
 
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index 51959d1fcfa..66186576662 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -80,6 +80,8 @@ void TeMesh::draw() {
 	/*
 	debug("Draw mesh %p (%s, %d verts %d norms %d indexes %d materials %d updated)", this, name().empty() ? "no name" : name().c_str(), _verticies.size(), _normals.size(), _indexes.size(), _materials.size(), _updatedVerticies.size());
 	debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
+	if (!_materials.empty())
+		debug("   material   %s", _materials[0].dump().c_str());
 	debug("   position   %s", position().dump().c_str());
 	debug("   worldPos   %s", worldPosition().dump().c_str());
 	debug("   scale      %s", scale().dump().c_str());
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 5757a99031b..e71c4112157 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -103,7 +103,7 @@ void TeModel::draw() {
 		renderer->pushMatrix();
 		renderer->multiplyMatrix(transform);
 		/*
-		if (name().contains("Kate")) {
+		if (name().contains("DEPLIANT")) {
 			debug("Draw model %p (%s, %d meshes)", this, name().empty() ? "no name" : name().c_str(), _meshes.size());
 			debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
 			//debug("   position   %s", position().dump().c_str());
@@ -194,6 +194,7 @@ void TeModel::update() {
 				float complete = blender->coef();
 				TeTRS endTRS = getBone(blender->_anim, b);
 				if (complete == 1.0f) {
+					_modelAnim = blender->_anim;
 					delete blender;
 					_boneBlenders.remove_at(i);
 					trs = endTRS;
diff --git a/engines/tetraedge/te/te_sprite_layout.cpp b/engines/tetraedge/te/te_sprite_layout.cpp
index 7cc19c754ca..29819cf05cd 100644
--- a/engines/tetraedge/te/te_sprite_layout.cpp
+++ b/engines/tetraedge/te/te_sprite_layout.cpp
@@ -46,7 +46,8 @@ void TeSpriteLayout::draw() {
 	if (!worldVisible())
 		return;
 
-	/*if (parent() && parent()->name() == "inventoryButton")
+	/*
+	if (name() == "DEPLIANT")
 		debug("Draw SpriteLayout %p (%s, surf %s, size %.01fx%.01f, surf %.01fx%.01f, %s)", this,
 			  name().empty() ? "no name" : name().c_str(), _tiledSurfacePtr->getAccessName().toString().c_str(),
 			  size().x(), size().y(),
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index 8a0e602b69b..a5676d8e277 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -18,6 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+ 
+//#define DUMP_RENDERED_FONTS 1
 
 #ifdef DUMP_RENDERED_FONTS
 #include "image/png.h"
@@ -73,7 +75,11 @@ void TeTextBase2::build() {
 		lineoffsets.push_back(height);
 		height += lineHeight + _interLine;
 	}
-	_size._y = (int)ceilf(height);
+
+	// Round up to the nearest 2 pixels so it centres in the
+	// area without a half pixel offset.
+	_size._y = (((int)ceilf(height) + 1) / 2) * 2;
+	_size._x = ((_size._x + 1) / 2) * 2;
 
 	TeImage img;
 	Common::SharedPtr<TePalette> nullpal;
@@ -96,8 +102,7 @@ void TeTextBase2::build() {
 
 	_mesh.setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
 	_mesh.defaultMaterial(texture);
-	// FIXME: Original uses BLEND, but we need MODULATE to get right colors?
-	//_mesh.setglTexEnv(GL_MODULATE);
+	_mesh.setglTexEnv(GL_BLEND);
 	_mesh.setShouldDraw(true);
 	_mesh.setColor(_globalColor);
 	_mesh.setVertex(0, TeVector3f32(_size._x * -0.5f, _size._y * -0.5f, 0.0f));
@@ -200,10 +205,7 @@ void TeTextBase2::drawEmptyChar(unsigned int offset) {
 void TeTextBase2::drawLine(TeImage &img, const Common::String &str, int yoffset) {
 	TeIntrusivePtr<TeFont3> font = _fonts[0];
 
-	// TODO: Add multi-color support if needed?
-	// We set black here as the color is set on the mesh and we use
-	// MODULATE blend in build()
-	font->draw(img, str, _fontSize, yoffset, TeColor(0, 0, 0, 255), _alignStyle);
+	font->draw(img, str, _fontSize, yoffset, _globalColor, _alignStyle);
 }
 
 unsigned int TeTextBase2::endOfWord(unsigned int offset) const {
diff --git a/engines/tetraedge/te/te_vector2s32.cpp b/engines/tetraedge/te/te_vector2s32.cpp
index 2541e265f41..d25d145a210 100644
--- a/engines/tetraedge/te/te_vector2s32.cpp
+++ b/engines/tetraedge/te/te_vector2s32.cpp
@@ -34,4 +34,8 @@ void TeVector2s32::deserialize(Common::ReadStream &stream, TeVector2s32 &dest) {
 	dest._y = stream.readSint32LE();
 }
 
+TeVector2s32 operator+(const TeVector2s32 &left, const TeVector2s32 &right) {
+		return TeVector2s32(left._x + right._x, left._y + right._y);
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_vector2s32.h b/engines/tetraedge/te/te_vector2s32.h
index ca0fdf449c6..1c96dc406a6 100644
--- a/engines/tetraedge/te/te_vector2s32.h
+++ b/engines/tetraedge/te/te_vector2s32.h
@@ -24,6 +24,7 @@
 
 #include "common/rect.h"
 #include "common/stream.h"
+#include "math/vector2d.h"
 
 namespace Tetraedge {
 
@@ -32,6 +33,7 @@ public:
 	TeVector2s32();
 	TeVector2s32(int x_, int y_) : _x(x_), _y(y_) {};
 	TeVector2s32(const Common::Point &pt) : _x(pt.x), _y(pt.y) {};
+	explicit TeVector2s32(const Math::Vector2d &pt) : _x(pt.getX()), _y(pt.getY()) {};
 
 	bool operator!=(const TeVector2s32 &other) const {
 		return _x != other._x || _y != other._y;
@@ -62,6 +64,8 @@ public:
 
 };
 
+TeVector2s32 operator+(const TeVector2s32 &left, const TeVector2s32 &right);
+
 } // end namespace Tetraedge
 
 #endif // TETRAEDGE_TE_TE_VECTOR2S32_H
diff --git a/engines/tetraedge/te/te_visual_fade.cpp b/engines/tetraedge/te/te_visual_fade.cpp
index 4773118de7f..f63cc2167d7 100644
--- a/engines/tetraedge/te/te_visual_fade.cpp
+++ b/engines/tetraedge/te/te_visual_fade.cpp
@@ -107,7 +107,9 @@ void TeVisualFade::init() {
 	// create an image the size of the window, no palette, format 6.
 	Common::SharedPtr<TePalette> nullpal;
 	_image.destroy();
-	_image.create(1024, 768, nullpal, TeImage::RGBA8);
+	// TODO: should this get actual window size instead of default?
+	_image.create(g_engine->getDefaultScreenWidth(),
+			g_engine->getDefaultScreenHeight(), nullpal, TeImage::RGBA8);
 	_texturePtr->load(_image);
 	g_engine->getRenderer()->enableTexture();
 	_texturePtr->load(_image);
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 8582389c303..2740b6cf257 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -165,11 +165,11 @@ void TetraedgeEngine::configureSearchPaths() {
 }
 
 int TetraedgeEngine::getDefaultScreenWidth() const {
-	return 1024;
+	return 800;
 }
 
 int TetraedgeEngine::getDefaultScreenHeight() const {
-	return 768;
+	return 600;
 }
 
 Common::Error TetraedgeEngine::run() {


Commit: 8b3c8844fe116223fc90c7e0f70e2a437075d6d6
    https://github.com/scummvm/scummvm/commit/8b3c8844fe116223fc90c7e0f70e2a437075d6d6
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Fix a lot of memory cleanup

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/application.h
    engines/tetraedge/game/cellphone.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/inventory.h
    engines/tetraedge/game/loc_file.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/object3d.cpp
    engines/tetraedge/game/object3d.h
    engines/tetraedge/game/owner_error_menu.cpp
    engines/tetraedge/game/splash_screens.cpp
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture.h
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_image.h
    engines/tetraedge/te/te_images_sequence.cpp
    engines/tetraedge/te/te_images_sequence.h
    engines/tetraedge/te/te_lua_context.cpp
    engines/tetraedge/te/te_lua_context.h
    engines/tetraedge/te/te_lua_gui.h
    engines/tetraedge/te/te_tiled_texture.cpp
    engines/tetraedge/te/te_timer.cpp
    engines/tetraedge/tetraedge.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 4f99e4aeb5f..369d9a5493f 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -30,6 +30,7 @@
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/application.h"
+#include "tetraedge/game/character.h"
 #include "tetraedge/game/characters_shadow.h"
 #include "tetraedge/game/in_game_scene.h"
 #include "tetraedge/te/te_core.h"
@@ -74,6 +75,10 @@ _drawShadows(true) {
 	loadOptions("options.xml");
 }
 
+Application::~Application() {
+	destroy();
+}
+
 void Application::create() {
 	// TODO: Move mainWindowCamera to mainWindow?
 
@@ -286,7 +291,7 @@ void Application::create() {
 }
 
 void Application::destroy() {
-	error("TODO: Implement Application::destroy");
+	Character::animCacheFreeAll();
 }
 
 void Application::startGame(bool newGame, int difficulty) {
diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
index ce9a67715ad..8bd2ce06290 100644
--- a/engines/tetraedge/game/application.h
+++ b/engines/tetraedge/game/application.h
@@ -48,6 +48,7 @@ namespace Tetraedge {
 class Application {
 public:
 	Application();
+	~Application();
 
 	void create();
 	void destroy();
diff --git a/engines/tetraedge/game/cellphone.cpp b/engines/tetraedge/game/cellphone.cpp
index d91ee5654c4..3ec7dbe7d2f 100644
--- a/engines/tetraedge/game/cellphone.cpp
+++ b/engines/tetraedge/game/cellphone.cpp
@@ -37,7 +37,7 @@ bool Cellphone::addNumber(const Common::String &num) {
 	}
 
 	TeTextLayout *layout = new TeTextLayout();
-	static const Common::String namePrefix("numRepertoire");
+	const Common::String namePrefix("numRepertoire");
 	layout->setName(namePrefix + num);
 	layout->setSizeType(RELATIVE_TO_PARENT);
 	layout->setAnchor(TeVector3f32(0.5f, 0.0f, 0.0f));
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 1d814ec639a..8620831b3b4 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -38,6 +38,7 @@ namespace Tetraedge {
 /*static*/ Common::HashMap<Common::String, Character::CharacterSettings> *Character::_globalCharacterSettings = nullptr;
 
 /*static*/ Common::Array<Character::AnimCacheElement> Character::_animCache;
+/*static*/ Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>> Character::_animCacheMap;
 uint Character::_animCacheSize = 0;
 
 void Character::CharacterSettings::clear() {
@@ -93,6 +94,14 @@ Character::~Character() {
 	}
 }
 
+/*static*/
+void Character::cleanup() {
+	if (_globalCharacterSettings)
+		delete _globalCharacterSettings;
+	_globalCharacterSettings = nullptr;
+	animCacheFreeAll();
+}
+
 void Character::addCallback(const Common::String &animKey, const Common::String &fnName, float triggerFrame, float maxCalls) {
 	Callback *c = new Callback();
 	c->_luaFn = fnName;
@@ -125,26 +134,25 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 	for (const auto &entry : _animCache)
 		_animCacheSize -= entry._size;
 	_animCache.clear();
+	_animCacheMap.clear();
 }
 
 /*static*/ void Character::animCacheFreeOldest() {
-	_animCacheSize -= _animCache[_animCache.size() - 1]._size;
-	_animCache.pop_back();
+	//_animCacheSize -= _animCache[_animCache.size() - 1]._size;
+	//_animCache.pop_back();
 }
 
 /*static*/ TeIntrusivePtr<TeModelAnimation> Character::animCacheLoad(const Common::Path &path) {
-	static Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>> _cache;
-
 	const Common::String pathStr = path.toString();
-	if (_cache.contains(pathStr))
-		return _cache.getVal(pathStr);
+	if (_animCacheMap.contains(pathStr))
+		return _animCacheMap.getVal(pathStr);
 
 	TeIntrusivePtr<TeModelAnimation> modelAnim = new TeModelAnimation();
 	if (!modelAnim->load(path)) {
 		warning("Failed to load anim %s", path.toString().c_str());
 	}
 
-	_cache.setVal(pathStr, modelAnim);
+	_animCacheMap.setVal(pathStr, modelAnim);
 	return modelAnim;
 }
 
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 86fc5036be4..191b1796524 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -176,6 +176,8 @@ public:
 	TeIntrusivePtr<TeBezierCurve> curve() { return _curve; }
 	void setRecallageY(bool val) { _recallageY = val; }
 
+	static void cleanup();
+
 private:
 	float _walkCurveStart;
 	float _walkCurveLast;
@@ -237,6 +239,7 @@ private:
 	Common::HashMap<Common::String, Common::Array<Callback *>> _callbacks;
 
 	static Common::Array<AnimCacheElement> _animCache;
+	static Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>> _animCacheMap;
 	static uint _animCacheSize;
 	static Common::HashMap<Common::String, CharacterSettings> *_globalCharacterSettings;
 
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 846691cc696..2eddff29f40 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -62,6 +62,9 @@ _firstInventory(true), _loadName("save.xml"), _randomSource("SyberiaGameRandom")
 }
 
 Game::~Game() {
+	if (_entered) {
+		leave(true);
+	}
 	delete _randomSound;
 }
 
@@ -720,7 +723,18 @@ void Game::leave(bool flag) {
 		_scene.unloadCharacter(_scene._character->_model->name());
 	}
 
-	warning("TODO: Game::leave: clear game sounds and randomsounds");
+	for (auto &sound : _gameSounds) {
+		delete sound;
+	}
+	_gameSounds.clear();
+	
+	for (auto &randsoundlist : _randomSounds) {
+		for (auto *randsound : randsoundlist._value) {
+			delete randsound;
+		}
+		randsoundlist._value.clear();
+	}
+	_randomSounds.clear();
 
 	for (auto *hitobj : _gameHitObjects) {
 		delete hitobj;
@@ -1089,7 +1103,7 @@ bool Game::onMouseMove() {
 	if (!_entered)
 		return false;
 
-	static const Common::Path DEFAULT_CURSOR("pictures/cursor.png");
+	const Common::Path DEFAULT_CURSOR("pictures/cursor.png");
 
 	Application *app = g_engine->getApplication();
 
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index aaf487d9bfb..a0e694d6993 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -351,11 +351,9 @@ void Inventory::unPauseAnims() {
 
 void Inventory::removeObject(const Common::String &objname) {
 	int pageNo = 0;
-	bool retval;
 	bool finished = false;
 	while (!finished) {
 		TeLayout *page = _gui.layout(Common::String::format("page%d", pageNo));
-		retval = false;
 		if (!page)
 			break;
 		int slotNo = 0;
@@ -441,9 +439,8 @@ void Inventory::selectedObject(InventoryObject *obj) {
 }
 
 const Common::String &Inventory::selectedObject() {
-	static const Common::String blank;
 	if (_selectedObject == nullptr)
-		return blank;
+		return _blankStr;
 	else
 		return _selectedObject->name();
 }
diff --git a/engines/tetraedge/game/inventory.h b/engines/tetraedge/game/inventory.h
index cfcdd344e5e..7d92c7e987f 100644
--- a/engines/tetraedge/game/inventory.h
+++ b/engines/tetraedge/game/inventory.h
@@ -92,6 +92,9 @@ private:
 	InventoryObject *_selectedObject;
 	Common::HashMap<Common::String, InventoryObjectData> _objectData;
 
+	// This is used when we need a reference to a blank str in selectedObject()
+	const Common::String _blankStr;
+
 	TeTimer _selectedTimer;
 };
 
diff --git a/engines/tetraedge/game/loc_file.cpp b/engines/tetraedge/game/loc_file.cpp
index 3e511527187..10321ad432c 100644
--- a/engines/tetraedge/game/loc_file.cpp
+++ b/engines/tetraedge/game/loc_file.cpp
@@ -33,7 +33,7 @@ LocFile::LocFile() {
 
 void LocFile::load(const Common::Path &path) {
 	TeNameValXmlParser parser;
-	static const Common::String xmlHeader("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+	const Common::String xmlHeader("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
 	Common::File locFile;
 	if (!locFile.open(path))
 		error("LocFile::load: failed to open %s.", path.toString().c_str());
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 0a199cffa0c..4d396e24d82 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -1764,8 +1764,10 @@ static int tolua_ExportedFunctions_SetObjectOnCharacter00(lua_State *L) {
 static void SetObjectRotation(const Common::String &obj, float xr, float yr, float zr) {
 	Game *game = g_engine->getGame();
 	Object3D *obj3d = game->scene().object3D(obj);
-	if (!obj3d)
+	if (!obj3d) {
 		warning("[SetObjectRotation] Object not found %s", obj.c_str());
+		return;
+	}
 	const TeVector3f32 rot(xr * M_PI / 180.0, yr * M_PI / 180.0, zr * M_PI / 180.0);
 	obj3d->_objRotation = TeQuaternion::fromEuler(rot);
 }
@@ -1788,8 +1790,10 @@ static int tolua_ExportedFunctions_SetObjectRotation00(lua_State *L) {
 static void SetObjectTranslation(const Common::String &obj, float x, float y, float z) {
 	Game *game = g_engine->getGame();
 	Object3D *obj3d = game->scene().object3D(obj);
-	if (!obj3d)
+	if (!obj3d) {
 		warning("[SetObjectTranslation] Object not found %s", obj.c_str());
+		return;
+	}
 	obj3d->_objTranslation = TeVector3f32(x, y, z);
 }
 
@@ -1811,8 +1815,10 @@ static int tolua_ExportedFunctions_SetObjectTranslation00(lua_State *L) {
 static void SetObjectScale(const Common::String &obj, float xs, float ys, float zs) {
 	Game *game = g_engine->getGame();
 	Object3D *obj3d = game->scene().object3D(obj);
-	if (!obj3d)
+	if (!obj3d) {
 		warning("[SetObjectScale] Object not found %s", obj.c_str());
+		return;
+	}
 	obj3d->_objScale = TeVector3f32(xs, ys, zs);
 }
 
@@ -1834,8 +1840,10 @@ static int tolua_ExportedFunctions_SetObjectScale00(lua_State *L) {
 static void SetObjectFrames(const Common::String &obj, int start, int end) {
 	Game *game = g_engine->getGame();
 	Object3D *obj3d = game->scene().object3D(obj);
-	if (!obj3d)
+	if (!obj3d) {
 		warning("[SetObjectFrames] Object not found %s", obj.c_str());
+		return;
+	}
 	obj3d->_startFrame = start;
 	obj3d->_endFrame = end;
 }
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index 44981d8aae9..493a4280b7d 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -28,6 +28,13 @@ namespace Tetraedge {
 /*static*/
 Common::HashMap<Common::String, Object3D::ObjectSettings> *Object3D::_objectSettings = nullptr;
 
+/*static*/
+void Object3D::cleanup() {
+	if (_objectSettings)
+		delete _objectSettings;
+	_objectSettings = nullptr;
+}
+
 
 // start and end frames not initialized in original, but to guarantee we don't use
 // uninitialized values we set it here.
diff --git a/engines/tetraedge/game/object3d.h b/engines/tetraedge/game/object3d.h
index ccdffbe02b4..bae3d0e36cb 100644
--- a/engines/tetraedge/game/object3d.h
+++ b/engines/tetraedge/game/object3d.h
@@ -46,6 +46,7 @@ public:
 	bool loadModel(const Common::String &name);
 
 	static bool loadSettings(const Common::String &path);
+	static void cleanup();
 
 	TeIntrusivePtr<TeModel> model() { return _modelPtr; }
 
diff --git a/engines/tetraedge/game/owner_error_menu.cpp b/engines/tetraedge/game/owner_error_menu.cpp
index 0519ab69d57..ecb12101a99 100644
--- a/engines/tetraedge/game/owner_error_menu.cpp
+++ b/engines/tetraedge/game/owner_error_menu.cpp
@@ -34,7 +34,7 @@ OwnerErrorMenu::OwnerErrorMenu() : _entered(false) {
 
 void OwnerErrorMenu::enter() {
 	_entered = true;
-	static const Common::Path luaPath("menus/ownerError/ownerError.lua");
+	const Common::Path luaPath("menus/ownerError/ownerError.lua");
 	load(luaPath);
 	Application *app = g_engine->getApplication();
 	TeLayout *menuLayout = layoutChecked("menu");
diff --git a/engines/tetraedge/game/splash_screens.cpp b/engines/tetraedge/game/splash_screens.cpp
index f27f318b0be..96773bf6d47 100644
--- a/engines/tetraedge/game/splash_screens.cpp
+++ b/engines/tetraedge/game/splash_screens.cpp
@@ -37,7 +37,7 @@ void SplashScreens::enter()	{
 	if (!_entered) {
 		_entered = true;
 		_splashNo = 0;
-		static const Common::Path scriptPath("menus/splashes/splash0.lua");
+		const Common::Path scriptPath("menus/splashes/splash0.lua");
 		if (Common::File::exists(scriptPath)) {
 			TeLuaGUI::load(scriptPath.toString());
 			Application *app = g_engine->getApplication();
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index 362582758a7..8c3c515d63f 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -94,14 +94,14 @@ void Te3DTexture::destroy() {
 	_glTexture = NO_TEXTURE;
 }
 
-void Te3DTexture::forceTexData(uint gltextures, uint xsize, uint ysize) {
+void Te3DTexture::forceTexData(uint gltexture, uint xsize, uint ysize) {
 	if (_glTexture != 0xffffffff) {
 		if (_createdTexture)
 			glDeleteTextures(1, &_glTexture);
 		_createdTexture = false;
 		_loaded = false;
 	}
-	_glTexture = gltextures;
+	_glTexture = gltexture;
 	_width = xsize;
 	_height = ysize;
 	_texWidth = xsize;
diff --git a/engines/tetraedge/te/te_3d_texture.h b/engines/tetraedge/te/te_3d_texture.h
index 1696cbaf2d1..bba5ad26975 100644
--- a/engines/tetraedge/te/te_3d_texture.h
+++ b/engines/tetraedge/te/te_3d_texture.h
@@ -44,7 +44,7 @@ public:
 	void create();
 	void destroy();
 
-	void forceTexData(uint gltextures, uint xsize, uint ysize);
+	void forceTexData(uint gltexture, uint xsize, uint ysize);
 
 	TeImage::Format getFormat() const { return _format; }
 	bool hasAlpha() const;
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index ca974f4fb04..c25def49905 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -28,7 +28,7 @@
 
 namespace Tetraedge {
 
-TeImage::TeImage() : Surface(), _format(INVALID) {
+TeImage::TeImage() : ManagedSurface(), _format(INVALID) {
 }
 
 TeImage::TeImage(const TeImage &other) {
@@ -44,8 +44,8 @@ unsigned long TeImage::countPixelsOfColor(const TeColor &col) const {
 }
 
 void TeImage::create() {
-	_format = INVALID;
-	Graphics::Surface::free();
+	// Never used, but in original seems to do the same as destroy??
+	destroy();
 }
 
 void TeImage::create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
@@ -54,8 +54,8 @@ void TeImage::create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
 	Graphics::PixelFormat pxformat = ((teformat == TeImage::RGB8) ?
 									  Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0) : Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
 
-	Graphics::Surface::create(xsize, ysize, pxformat);
-	Graphics::Surface::fillRect(Common::Rect(0, 0, xsize, ysize), 0);
+	Graphics::ManagedSurface::create(xsize, ysize, pxformat);
+	Graphics::ManagedSurface::fillRect(Common::Rect(0, 0, xsize, ysize), 0);
 }
 
 void TeImage::deserialize(Common::ReadStream &stream) {
@@ -63,7 +63,7 @@ void TeImage::deserialize(Common::ReadStream &stream) {
 }
 
 void TeImage::destroy() {
-	Graphics::Surface::free();
+	Graphics::ManagedSurface::free();
 	_format = INVALID;
 }
 
@@ -79,7 +79,7 @@ void TeImage::fill(byte r, byte g, byte b, byte a) {
 	Common::Rect wholeSurf(0, 0, w, h);
 
 	uint32 col = ((uint32)r << format.rShift) | ((uint32)g << format.gShift) | ((uint32)b << format.bShift) | ((uint32)a << format.aShift);
-	Graphics::Surface::fillRect(wholeSurf, col);
+	Graphics::ManagedSurface::fillRect(wholeSurf, col);
 }
 
 void TeImage::getBuff(uint x, uint y, byte *pout, uint w_, uint h_) {
diff --git a/engines/tetraedge/te/te_image.h b/engines/tetraedge/te/te_image.h
index ae0aa47ffb5..16dacc6e9ae 100644
--- a/engines/tetraedge/te/te_image.h
+++ b/engines/tetraedge/te/te_image.h
@@ -27,7 +27,7 @@
 #include "common/types.h"
 #include "common/path.h"
 
-#include "graphics/surface.h"
+#include "graphics/managed_surface.h"
 
 #include "tetraedge/te/te_color.h"
 #include "tetraedge/te/te_palette.h"
@@ -36,12 +36,14 @@
 
 namespace Tetraedge {
 
-class TeImage : public TeResource, public Graphics::Surface {
+class TeImage : public TeResource, public Graphics::ManagedSurface {
 public:
 	TeImage();
 	TeImage(const TeImage &other);
 
-	virtual ~TeImage() {};
+	virtual ~TeImage() {
+		destroy();
+	};
 
 	enum Format {
 		RGB8 = 5,
diff --git a/engines/tetraedge/te/te_images_sequence.cpp b/engines/tetraedge/te/te_images_sequence.cpp
index 3f90c9977dc..ffe55b35745 100644
--- a/engines/tetraedge/te/te_images_sequence.cpp
+++ b/engines/tetraedge/te/te_images_sequence.cpp
@@ -22,6 +22,7 @@
 #include "common/file.h"
 #include "image/png.h"
 #include "graphics/surface.h"
+#include "graphics/managed_surface.h"
 
 #include "tetraedge/te/te_images_sequence.h"
 
@@ -31,6 +32,10 @@ TeImagesSequence::TeImagesSequence() : _width(0), _height(0), _curFrame(0) {
 }
 
 TeImagesSequence::~TeImagesSequence() {
+	for (auto surf : _cachedSurfaces) {
+		if (surf)
+			delete surf;
+	}
 }
 
 /*static*/
@@ -56,8 +61,8 @@ bool TeImagesSequence::load(const Common::Path &path) {
 	}
 
 	Common::sort(children.begin(), children.end(), compareNodes);
-
-	SearchMan.addDirectory(path.toString(), directory);
+	if (!SearchMan.hasArchive(path.toString()))
+		SearchMan.addDirectory(path.toString(), directory);
 
 	for (Common::FSNode &child : children) {
 		Common::String filePathStr = child.getPath();
@@ -82,7 +87,8 @@ bool TeImagesSequence::load(const Common::Path &path) {
 		}
 
 		// Only do the deep check for the first file to get dimensions.
-		if (!_width) {
+		// If the images are small then cache them to avoid reloading each frame each time.
+		if (!_width || (_width < 100 && _height < 100)) {
 			Image::PNGDecoder png;
 			if (!png.loadStream(*stream)) {
 				warning("Image sequence failed to load png %s", filePath.toString().c_str());
@@ -90,11 +96,20 @@ bool TeImagesSequence::load(const Common::Path &path) {
 				return false;
 			}
 
-			const Graphics::Surface *surf = png.getSurface();
-			assert(surf);
-			_width = surf->w;
-			_height = surf->h;
 			_frameRate = fps;
+			const Graphics::Surface *pngsurf = png.getSurface();
+			assert(pngsurf);
+			_width = pngsurf->w;
+			_height = pngsurf->h;
+			if (_width < 100 && _height < 100) {
+				Graphics::ManagedSurface *surf = new Graphics::ManagedSurface();
+				surf->copyFrom(pngsurf);
+				_cachedSurfaces.push_back(surf);
+			} else {
+				_cachedSurfaces.push_back(nullptr);
+			}
+		} else {
+			_cachedSurfaces.push_back(nullptr);
 		}
 
 		_files.push_back(child);
@@ -111,26 +126,35 @@ bool TeImagesSequence::update(unsigned long i, TeImage &imgout) {
 	if (i >= _files.size())
 		return false;
 
-	Common::SeekableReadStream *stream = _files[_curFrame].createReadStream();
-	if (!stream)
-		error("Open %s failed.. it was ok before?", _files[_curFrame].getName().c_str());
+	if (_cachedSurfaces[i] == nullptr) {
+		Common::SeekableReadStream *stream = _files[i].createReadStream();
+		if (!stream)
+			error("Open %s failed.. it was ok before?", _files[i].getName().c_str());
 
-	Image::PNGDecoder png;
-	if (!png.loadStream(*stream)) {
-		warning("Image sequence failed to load png %s", _files[_curFrame].getName().c_str());
-		delete stream;
-		return false;
-	}
+		Image::PNGDecoder png;
+		if (!png.loadStream(*stream)) {
+			warning("Image sequence failed to load png %s", _files[i].getName().c_str());
+			delete stream;
+			return false;
+		}
 
-	const Graphics::Surface *surf = png.getSurface();
-	assert(surf);
+		const Graphics::Surface *surf = png.getSurface();
+		assert(surf);
 
-	imgout.setAccessName(_files[_curFrame].getPath());
+		imgout.setAccessName(_files[i].getPath());
 
-	if (imgout.w == surf->w && imgout.h == surf->h && imgout.format == surf->format) {
-		imgout.copyFrom(*surf);
-		delete stream;
-		return true;
+		if (imgout.w == surf->w && imgout.h == surf->h && imgout.format == surf->format) {
+			imgout.copyFrom(*surf);
+			delete stream;
+			return true;
+		}
+	} else {
+		const Graphics::ManagedSurface *surf = _cachedSurfaces[i];
+		if (imgout.w == surf->w && imgout.h == surf->h && imgout.format == surf->format) {
+			imgout.setAccessName(_files[i].getPath());
+			imgout.copyFrom(*surf);
+			return true;
+		}
 	}
 
 	error("TODO: Implement TeImagesSequence::update for different sizes");
diff --git a/engines/tetraedge/te/te_images_sequence.h b/engines/tetraedge/te/te_images_sequence.h
index 1f0a72bd3d2..fa0a5bdcb96 100644
--- a/engines/tetraedge/te/te_images_sequence.h
+++ b/engines/tetraedge/te/te_images_sequence.h
@@ -27,6 +27,7 @@
 
 namespace Graphics {
 struct Surface;
+class ManagedSurface;
 }
 
 namespace Tetraedge {
@@ -63,6 +64,7 @@ private:
 	unsigned int _width;
 	unsigned int _height;
 	Common::Array<Common::FSNode> _files;
+	Common::Array<Graphics::ManagedSurface *> _cachedSurfaces;
 	unsigned int _curFrame;
 };
 
diff --git a/engines/tetraedge/te/te_lua_context.cpp b/engines/tetraedge/te/te_lua_context.cpp
index 9125975b88f..feb3ae66a32 100644
--- a/engines/tetraedge/te/te_lua_context.cpp
+++ b/engines/tetraedge/te/te_lua_context.cpp
@@ -44,6 +44,10 @@ TeLuaContext::TeLuaContext() : _luaState(nullptr) {
 	lua_atpanic(_luaState, luaPanicFunction);
 }
 
+TeLuaContext::~TeLuaContext() {
+	destroy();
+}
+
 void TeLuaContext::addBindings(void(*fn)(lua_State *)) {
 	fn(_luaState);
 }
@@ -57,6 +61,7 @@ void TeLuaContext::create() {
 void TeLuaContext::destroy() {
 	if (_luaState)
 		lua_close(_luaState);
+	_luaState = nullptr;
 }
 
 TeVariant TeLuaContext::global(const Common::String &name) {
@@ -189,7 +194,7 @@ Common::Error TeLuaContext::syncState(Common::Serializer &s) {
 			s.syncString(name);
 			switch (vtype) {
 				case Boolean: {
-					byte b;
+					byte b = 0;
 					s.syncAsByte(b);
 					lua_pushboolean(_luaState, b);
 #if DEBUG_SAVELOAD
@@ -198,7 +203,7 @@ Common::Error TeLuaContext::syncState(Common::Serializer &s) {
 					break;
 				}
 				case Number: {
-					float d;
+					float d = 0;
 					s.syncAsDoubleLE(d);
 					lua_pushnumber(_luaState, d);
 #if DEBUG_SAVELOAD
diff --git a/engines/tetraedge/te/te_lua_context.h b/engines/tetraedge/te/te_lua_context.h
index 47597ec5f59..e4e9856b4f7 100644
--- a/engines/tetraedge/te/te_lua_context.h
+++ b/engines/tetraedge/te/te_lua_context.h
@@ -42,6 +42,7 @@ class TeLuaGUI;
 class TeLuaContext {
 public:
 	TeLuaContext();
+	~TeLuaContext();
 
 	void addBindings(void(*fn)(lua_State *));
 	void create();
diff --git a/engines/tetraedge/te/te_lua_gui.h b/engines/tetraedge/te/te_lua_gui.h
index c9254e229df..54e272f57a9 100644
--- a/engines/tetraedge/te/te_lua_gui.h
+++ b/engines/tetraedge/te/te_lua_gui.h
@@ -50,7 +50,9 @@ namespace Tetraedge {
 class TeLuaGUI : public TeObject {
 public:
 	TeLuaGUI();
-	virtual ~TeLuaGUI() {};
+	virtual ~TeLuaGUI() {
+		unload();
+	};
 
 	virtual void enter() {};
 	virtual void leave() {};
diff --git a/engines/tetraedge/te/te_tiled_texture.cpp b/engines/tetraedge/te/te_tiled_texture.cpp
index 593055a2ff1..839cacdde03 100644
--- a/engines/tetraedge/te/te_tiled_texture.cpp
+++ b/engines/tetraedge/te/te_tiled_texture.cpp
@@ -48,8 +48,8 @@ bool TeTiledTexture::load(const Common::Path &path) {
 	if (resmgr->exists(path)) {
 		img = resmgr->getResourceNoSearch<TeImage>(path);
 	} else {
-		TeImage *newImg = new TeImage();
-		if (!newImg->load(path))
+		img = new TeImage();
+		if (!img->load(path))
 			return false;
 	}
 	load(*img);
@@ -117,8 +117,6 @@ bool TeTiledTexture::load(const TeImage &img) {
 	if (rows)
 		_somethingSize._y = _somethingSize._y / rows;
 	setAccessName(img.getAccessName().append(".tt"));
-	if (img.getAccessName().toString() == "menus/inGame/Inventory.png")
-		debug("loading inventory tiled texture");
 	return true;
 }
 
@@ -133,8 +131,6 @@ bool TeTiledTexture::load(const TeIntrusivePtr<Te3DTexture> &texture) {
 	tileData->_vec2 = TeVector3f32(1.0, 1.0, 0.0);
 	tileData->_vec1 = TeVector3f32(0.0, 0.0, 0.0);
 	setAccessName(texture->getAccessName().append(".tt"));
-	if (texture->getAccessName().toString() == "menus/inGame/Inventory.png")
-		debug("loading inventory tiled texture from texture");
 	return true;
 }
 
@@ -155,7 +151,7 @@ TeImage *TeTiledTexture::optimisedTileImage(Common::Array<TeImage> &images, cons
 		}
 	}
 	images.resize(images.size() + 1);
-	TeImage &newImg = *images.end();
+	TeImage &newImg = images.back();
 	Common::SharedPtr<TePalette> nullPal;
 	newImg.create((uint)size._x, (uint)size._y, nullPal, format);
 	return &newImg;
diff --git a/engines/tetraedge/te/te_timer.cpp b/engines/tetraedge/te/te_timer.cpp
index 4802c446729..7ca28da0095 100644
--- a/engines/tetraedge/te/te_timer.cpp
+++ b/engines/tetraedge/te/te_timer.cpp
@@ -40,12 +40,17 @@ _startTime(0), _lastTimeElapsed(0), _startTimeOffset(0), _updated(false) {
 }
 
 TeTimer::~TeTimer() {
-	if (!_stopped) {
-		for (uint i = 0; i < _timers.size(); i++) {
-			if (_timers[i] == this) {
-				_timers.remove_at(i);
-				break;
-			}
+	for (uint i = 0; i < _timers.size(); i++) {
+		if (_timers[i] == this) {
+			_timers.remove_at(i);
+			break;
+		}
+	}
+	// Not done in original, but probably should be?
+	for (uint i = 0; i < _pausedTimers.size(); i++) {
+		if (_pausedTimers[i] == this) {
+			_pausedTimers.remove_at(i);
+			break;
 		}
 	}
 }
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 2740b6cf257..811384ffa42 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -34,6 +34,7 @@
 
 #include "tetraedge/game/game.h"
 #include "tetraedge/game/application.h"
+#include "tetraedge/game/character.h"
 #include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_renderer.h"
 #include "tetraedge/te/te_resource_manager.h"
@@ -54,13 +55,15 @@ TetraedgeEngine::TetraedgeEngine(OSystem *syst, const ADGameDescription *gameDes
 }
 
 TetraedgeEngine::~TetraedgeEngine() {
-	delete _application;
-	delete _renderer;
 	delete _core;
 	delete _game;
-	//delete _soundManager;
+	delete _application;
+	delete _renderer;
+	delete _soundManager;
 	delete _resourceManager;
 	delete _inputMgr;
+	Object3D::cleanup();
+	Character::cleanup();
 }
 
 /*static*/


Commit: 4479e6e3464d4cc48da3f77b4ddf7f677b520aee
    https://github.com/scummvm/scummvm/commit/4479e6e3464d4cc48da3f77b4ddf7f677b520aee
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Implement FreeMoveZone building

Changed paths:
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_font3.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_lua_context.cpp
    engines/tetraedge/te/te_lua_gui.cpp
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_pick_mesh2.h
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_scrolling_layout.cpp
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_text_layout.cpp
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_tiled_surface.h


diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 5c47508c6b7..83cb02ef0e4 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -74,11 +74,11 @@ void Dialog2::launchNextDialog() {
 
 			if (_currentDialogData._animBlend == 0.0f) {
 				if (!c->setAnimation(_currentDialogData._animfile, false, true))
-					error("[Dialog2::launchNextDialog] Character's animation \"%s\" doesn't exist for the character\"%s\"  \n",
+					error("[Dialog2::launchNextDialog] Character's animation \"%s\" doesn't exist for the character\"%s\"",
 							_currentDialogData._animfile.c_str(), _currentDialogData._charname.c_str());
 			} else {
 				if (!c->blendAnimation(_currentDialogData._animfile, _currentDialogData._animBlend, false, true))
-					error("[Dialog2::launchNextDialog] Character's animation \"%s\" doesn't exist for the character\"%s\"  \n",
+					error("[Dialog2::launchNextDialog] Character's animation \"%s\" doesn't exist for the character\"%s\"",
 							_currentDialogData._animfile.c_str(), _currentDialogData._charname.c_str());
 			}
 		}
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 2eddff29f40..d8db0d019da 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -278,7 +278,8 @@ void Game::enter(bool newgame) {
 	_score = 0;
 	Application *app = g_engine->getApplication();
 	app->visualFade().init();
-	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, -10000.0f));
+	// FIXME: Original puts this click handler at -10000.. but then it never gets hit?
+	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, 10000.0f));
 	g_engine->getInputMgr()->_mouseLUpSignal.push_back(callbackptr);
 	_movePlayerCharacterDisabled = false;
 	// TODO? Set character mouse move event no to -1
@@ -524,12 +525,12 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	Application *app = g_engine->getApplication();
 	if (forLuaExists) {
 		_forGui.load(forLuaPath);
-		TeLayout *bg = _forGui.layout("background");
+		TeLayout *bg = _forGui.layoutChecked("background");
 		bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
 		app->_frontLayout.addChild(bg);
 		// Note: Game also adds cellphone to both frontLayout *and* noScaleLayout2,
 		// so we reproduce the broken behavior exactly.
-		TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayout("background");
+		TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayoutChecked("background");
 		app->_frontLayout.removeChild(cellbg);
 		app->_frontLayout.addChild(cellbg);
 		_objectif.reattachLayout(&app->_frontLayout);
@@ -727,7 +728,7 @@ void Game::leave(bool flag) {
 		delete sound;
 	}
 	_gameSounds.clear();
-	
+
 	for (auto &randsoundlist : _randomSounds) {
 		for (auto *randsound : randsoundlist._value) {
 			delete randsound;
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 4d396e24d82..b49cea8f36a 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -514,7 +514,10 @@ static void StartAnimation(const Common::String name, int loops, bool repeat) {
 	if (game->startAnimation(name, loops, repeat))
 		return;
 
-	error("[StartAnimation] Animation \"%s\" doesn't exist.", name.c_str());
+	// NOTE: Not error, some game scripts try to start animations that don't
+	// exist.  eg, ValVoralberg/14030 loads anim 14020, which is in a different
+	// zone
+	warning("[StartAnimation] Animation \"%s\" doesn't exist.", name.c_str());
 }
 
 int tolua_ExportedFunctions_StartAnimation00(lua_State *L) {
@@ -714,7 +717,7 @@ static int tolua_ExportedFunctions_PlaceCharacterOnDummy00(lua_State *L) {
 	}
 	error("#ferror in function 'SetCharacterRotation': %d %d %s", err.index, err.array, err.type);
 }
- 
+
 static void SetCharacterRotation(const Common::String &charname, float rx, float ry, float rz) {
 	TeQuaternion quat = TeQuaternion::fromEuler(TeVector3f32(rx * M_PI / 180.0, ry * M_PI / 180.0, rz * M_PI / 180.0));
 	Game *game = g_engine->getGame();
@@ -1248,7 +1251,7 @@ static int tolua_ExportedFunctions_SetBackground00(lua_State *L) {
 static void LaunchDialog(const Common::String &name, uint param_2, const Common::String &charname,
 						const Common::String &animfile, float animblend) {
 	Game *game = g_engine->getGame();
-  
+
 	if (!game->launchDialog(name, param_2, charname, animfile, animblend))
 		warning("[LaunchDialog] Dialog \"%s\" doesn't exist.", name.c_str());
 }
@@ -1588,12 +1591,24 @@ static int tolua_ExportedFunctions_SetRecallageY00(lua_State *L) {
 	error("#ferror in function 'SetRecallageY': %d %d %s", err.index, err.array, err.type);
 }
 
-static void DisabledZone(const Common::String &s1, bool b) {
+static void DisabledZone(const Common::String &zone, bool disable) {
 	Game *game = g_engine->getGame();
 	if (!game->scene().markerGui().loaded())
 		return;
 
-	warning("TODO: Implement DisabledZone");
+	TeLayout *bg = game->scene().markerGui().layout("background");
+	if (!bg) {
+		warning("DisabledZone(%s): No background in markerGui", zone.c_str());
+		return;
+	}
+	for (auto *child : bg->childList()) {
+		TeLayout *childLayout = dynamic_cast<TeLayout *>(child);
+		if (!childLayout)
+			continue;
+		if (child->name() == zone) {
+			child->setVisible(!disable);
+		}
+	}
 }
 
 static int tolua_ExportedFunctions_DisabledZone00(lua_State *L) {
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index 8c3c515d63f..bd9ee1b3575 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -234,7 +234,6 @@ TeVector2s32 Te3DTexture::optimisedSize(const TeVector2s32 &size) {
 		v2 = 8;
 	}
 	return TeVector2s32(v1, v2);
-	
 }
 
 /*static*/
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index 4aac314f8bd..93db4cc9fc7 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -99,8 +99,8 @@ bool TeButtonLayout::onMouseLeftDown(const Common::Point &pt) {
 	// very simplified.
 	bool mouseIn = isMouseIn(pt);
 
-	if (mouseIn)
-		debug("mouse down on button '%s' (current state %d)", name().c_str(), _currentState);
+	//if (mouseIn)
+	//	debug("mouse down on button '%s' (current state %d)", name().c_str(), _currentState);
 
 	enum State newState = _currentState;
 	switch (_currentState) {
@@ -141,8 +141,8 @@ bool TeButtonLayout::onMouseLeftUp(const Common::Point &pt) {
 	// somewhat simplified.
 	bool mouseIn = isMouseIn(pt);
 
-	if (mouseIn)
-		debug("mouse up on button '%s' (current state %d)", name().c_str(), _currentState);
+	//if (mouseIn)
+	//	debug("mouse up on button '%s' (current state %d)", name().c_str(), _currentState);
 
 	enum State newState = _currentState;
 	switch (_currentState) {
diff --git a/engines/tetraedge/te/te_font3.cpp b/engines/tetraedge/te/te_font3.cpp
index 7ce34b4fa9b..9c6e33a221f 100644
--- a/engines/tetraedge/te/te_font3.cpp
+++ b/engines/tetraedge/te/te_font3.cpp
@@ -149,6 +149,9 @@ void TeFont3::draw(TeImage &destImage, const Common::String &str, int fontSize,
 
 
 bool TeFont3::load(const Common::Path &path) {
+	if (_loadedPath == path && _fontFile.isOpen())
+		return true; // already open
+
 	setAccessName(path);
 	_loadedPath = path;
 
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 33aac199b26..ac4f09d405c 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -93,10 +93,44 @@ TeVector2s32 TeFreeMoveZone::aStarResolution() const {
 
 void TeFreeMoveZone::buildAStar() {
 	preUpdateGrid();
-	TeVector2s32 resolution = aStarResolution();
-	_graph->setSize(resolution);
+	const TeVector2s32 graphSize = aStarResolution();
+	_graph->setSize(graphSize);
+
+	// Original checks these inside the loop below, seems like a waste as they never change?
+	if (graphSize._x == 0 || graphSize._y == 0)
+		return;
+
 	if (!_loadedFromBin) {
-		error("TODO: Implement TeFreeMoveZone::buildAStar for *not* loaded from bin case");
+		for (int x = 0; x < graphSize._x; x++) {
+			for (int y = 0; y < graphSize._y; y++) {
+				byte blockerIntersection = hasBlockerIntersection(TeVector2s32(x, y));
+				if (blockerIntersection == 1) {
+					_graph->_flags[_graph->_size._x * y + x] = 1;
+				} else {
+					if (!hasCellBorderIntersection(TeVector2s32(x, y))) {
+						const float gridOffX = _gridOffsetSomething.getX();
+						const float gridOffY = _gridOffsetSomething.getY();
+						TeVector3f32 vout;
+						float fout;
+						TeVector3f32 gridPt(x * gridOffX + _someGridVec1.getX() + gridOffX * 0.5,
+										1000000.0,
+										gridOffY * 0.5 + y * gridOffY + _someGridVec1.getY());
+						bool doesIntersect = intersect(gridPt, TeVector3f32(0.0, -1.0, 0.0), vout, fout, true, nullptr);
+						if (!doesIntersect)
+							doesIntersect = intersect(gridPt, TeVector3f32(0.0, 1.0, 0.0), vout, fout, true, nullptr);
+
+						if (!doesIntersect)
+							_graph->_flags[graphSize._x * y + x] = 1;
+						else if (blockerIntersection == 2)
+							_graph->_flags[graphSize._x * y + x] = 2;
+						else
+							_graph->_flags[graphSize._x * y + x] = 0;
+					} else {
+					_graph->_flags[graphSize._x * y + x] = 2;
+					}
+				}
+			}
+		}
 	} else {
 		// Loaded from bin..
 		error("TODO: Implement TeFreeMoveZone::buildAStar for loaded from bin case");
@@ -111,8 +145,8 @@ void TeFreeMoveZone::clear() {
 	setNbTriangles(0);
 	_pickMeshDirty = true;
 	_projectedPointsDirty = true;
-	_vectorArray.clear();
-	_uintArray2.clear();
+	_transformedVerticies.clear();
+	_borders.clear();
 	// TODO: Some other point vector here.
 	_gridDirty = true;
 	_graph->_flags.clear();
@@ -213,9 +247,9 @@ void TeFreeMoveZone::deserialize(Common::ReadStream &stream, TeFreeMoveZone &des
 	dest._gridDirty = (stream.readByte() != 0);
 
 	Te3DObject2::deserializeVectorArray(stream, dest._freeMoveZoneVerticies);
-	Te3DObject2::deserializeUintArray(stream, dest._uintArray1);
-	Te3DObject2::deserializeVectorArray(stream, dest._vectorArray);
-	Te3DObject2::deserializeUintArray(stream, dest._uintArray2);
+	Te3DObject2::deserializeUintArray(stream, dest._pickMesh);
+	Te3DObject2::deserializeVectorArray(stream, dest._transformedVerticies);
+	Te3DObject2::deserializeUintArray(stream, dest._borders);
 
 	TeOBP::deserialize(stream, dest._obp);
 
@@ -241,10 +275,10 @@ void TeFreeMoveZone::draw() {
 	renderer->enableWireFrame();
 	TePickMesh2::draw();
 	TeMesh mesh;
-	mesh.setConf(_uintArray2.size(), _uintArray2.size(), TeMesh::MeshMode_Lines, 0, 0);
-	for (unsigned int i = 0; i < _uintArray2.size(); i++) {
+	mesh.setConf(_borders.size(), _borders.size(), TeMesh::MeshMode_Lines, 0, 0);
+	for (unsigned int i = 0; i < _borders.size(); i++) {
 		mesh.setIndex(i, i);
-		mesh.setVertex(i, verticies()[_uintArray2[i]]);
+		mesh.setVertex(i, verticies()[_borders[i]]);
 	}
 
 	const TeColor prevColor = renderer->currentColor();
@@ -264,12 +298,115 @@ TeVector3f32 TeFreeMoveZone::findNearestPointOnBorder(const TeVector2f32 &pt) {
 	error("TODO: Implement TeFreeMoveZone::findNearestPointOnBorder");
 }
 
+static int segmentIntersection(const TeVector2f32 &s1start, const TeVector2f32 &s1end,
+						const TeVector2f32 &s2start, const TeVector2f32 &s2end,
+                       TeVector2f32 *sout, float *fout1, float *fout2) {
+	TeVector2f32 s1len = s1end - s1start;
+	TeVector2f32 s2len = s2end - s2start;
+	float squarelen = s1len.getX() * s2len.getX() + s1len.getY() * s2len.getY();
+	int result = 0;
+	if (squarelen != 0) {
+		result = 1;
+		float intersection1 = -((s1len.getY() * s1start.getX() +
+						(s1len.getX() * s2start.getY() - s1len.getX() * s1start.getY())) -
+                          s1len.getY() * s2start.getX()) / squarelen;
+		if (intersection1 >= 0.0f && intersection1 <= 1.0f) {
+			float intersection2 = -((s2len.getY() * s2start.getY() +
+						(s2len.getX() * s1start.getX() - s2len.getX() * s2start.getX())) -
+                          s2len.getY() * s1start.getY()) / squarelen;
+			if (intersection2 >= 0.0f && intersection2 <= 1.0f) {
+				result = 2;
+				if (sout || fout1 || fout2) {
+					// Seems like these are always null?
+					error("TODO: implement output in segmentIntersection");
+				}
+			}
+		}
+	}
+	return result;
+}
+
+
 bool TeFreeMoveZone::hasBlockerIntersection(const TeVector2s32 &pt) {
-	error("TODO: Implement TeFreeMoveZone::hasBlockerIntersection");
+	TeVector2f32 borders[4];
+
+	const float gridOffsetX = _gridOffsetSomething.getX();
+	const float gridOffsetY = _gridOffsetSomething.getX();
+	borders[0] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX(),
+             pt._y * gridOffsetY + _someGridVec1.getY());
+	borders[1] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX() + gridOffsetX,
+             pt._y * gridOffsetY + _someGridVec1.getY());
+	borders[2] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX(),
+             pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
+	borders[3] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX() + gridOffsetX,
+             pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
+
+	for (unsigned int i = 0; i < _blockers->size(); i++) {
+		const TeBlocker &blocker = (*_blockers)[i];
+
+		if (blocker._s != name())
+			continue;
+
+		for (unsigned int b = 0; b < 4; b++) {
+			int si = segmentIntersection(borders[b], borders[(b + 1) % 4], blocker._pts[0],
+                                      blocker._pts[1], nullptr, nullptr, nullptr);
+			if (si == 2)
+				return 2;
+		}
+
+        TeVector2f32 borderVec = ((borders[0] + borders[3]) / 2.0) - blocker._pts[0];
+        TeVector2f32 blockerVec = blocker._pts[1] - blocker._pts[0];
+        float dotVal = borderVec.dotProduct(blockerVec.getNormalized());
+        float crosVal = borderVec.crossProduct(blockerVec);
+        if ((crosVal < 0.0) && (0.0 <= dotVal)) {
+			if (dotVal < blockerVec.length())
+				return 1;
+        }
+	}
+	return 0;
 }
 
 bool TeFreeMoveZone::hasCellBorderIntersection(const TeVector2s32 &pt) {
-	error("TODO: Implement TeFreeMoveZone::hasCellBorderIntersection");
+	TeVector2f32 borders[4];
+
+	const float gridOffsetX = _gridOffsetSomething.getX();
+	const float gridOffsetY = _gridOffsetSomething.getX();
+	borders[0] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX(),
+             pt._y * gridOffsetY + _someGridVec1.getY());
+	borders[1] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX() + gridOffsetX,
+             pt._y * gridOffsetY + _someGridVec1.getY());
+	borders[2] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX(),
+             pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
+	borders[3] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX() + gridOffsetX,
+             pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
+
+	int iresult = 0;
+	for (unsigned int border = 0; border < _borders.size() / 2; border++) {
+		TeVector2f32 v1;
+		TeVector2f32 v2;
+		unsigned int off1 = _pickMesh[_borders[border * 2]];
+		unsigned int off2 = _pickMesh[_borders[border * 2 + 1]];
+		if (!_loadedFromBin) {
+			v1 = TeVector2f32(_transformedVerticies[off1].x(), _transformedVerticies[off1].z());
+			v2 = TeVector2f32(_transformedVerticies[off2].x(), _transformedVerticies[off2].z());
+		} else {
+			TeMatrix4x4 gridInverse = _gridMatrix;
+			gridInverse.inverse();
+			const TeVector3f32 v1_inv = gridInverse * _freeMoveZoneVerticies[off1];
+			const TeVector3f32 v2_inv = gridInverse * _freeMoveZoneVerticies[off2];
+			v1 = TeVector2f32(v1_inv.x(), v1_inv.z());
+			v2 = TeVector2f32(v2_inv.x(), v2_inv.z());
+		}
+		iresult = segmentIntersection(borders[0], borders[1], v1, v2, nullptr, nullptr, nullptr);
+		if (iresult == 2) break;
+		iresult = segmentIntersection(borders[1], borders[2], v1, v2, nullptr, nullptr, nullptr);
+		if (iresult == 2) break;
+		iresult = segmentIntersection(borders[2], borders[3], v1, v2, nullptr, nullptr, nullptr);
+		if (iresult == 2) break;
+		iresult = segmentIntersection(borders[3], borders[0], v1, v2, nullptr, nullptr, nullptr);
+		if (iresult == 2) break;
+	}
+	return iresult == 2;
 }
 
 TeActZone *TeFreeMoveZone::isInZone(const TeVector3f32 &pt) {
@@ -282,7 +419,81 @@ bool TeFreeMoveZone::onViewportChanged() {
 }
 
 void TeFreeMoveZone::preUpdateGrid() {
-	error("TODO: Implement TeFreeMoveZone::preUpdateGrid");
+	updateTransformedVertices();
+	updatePickMesh();
+	updateBorders();
+	if (_loadedFromBin) {
+		calcGridMatrix();
+	}
+
+	TeMatrix4x4 gridInverse = _gridMatrix;
+	gridInverse.inverse();
+
+	TeVector3f32 newVec;
+	if (_transformedVerticies.empty() || _pickMesh.empty()) {
+		debug("[TeFreeMoveZone::buildAStar] %s have no mesh or is entierly occluded", name().c_str());
+	} else {
+		if (!_loadedFromBin) {
+			newVec = _transformedVerticies[_pickMesh[0]];
+		} else {
+			newVec = gridInverse * _freeMoveZoneVerticies[_pickMesh[0]];
+		}
+		_someGridVec1.setX(newVec.x());
+		_someGridVec1.setY(newVec.z());
+
+		_gridWorldY = newVec.y();
+	}
+	for (unsigned int i = 0; i < _pickMesh.size(); i++) {
+		  unsigned int vertNo = _pickMesh[_pickMesh[i]];
+
+		  if (!_loadedFromBin)
+			newVec = _transformedVerticies[vertNo];
+		  else
+			newVec = gridInverse * _freeMoveZoneVerticies[vertNo];
+
+		  if (_someGridVec1.getX() <= newVec.x()) {
+			if (_someGridVec2.getX() < newVec.x()) {
+			  _someGridVec2.setX(newVec.x());
+			}
+		  } else {
+			_someGridVec1.setX(newVec.x());
+		  }
+
+		  if (_someGridVec1.getY() <= newVec.z()) {
+			if (_someGridVec2.getY() < newVec.z()) {
+			  _someGridVec2.setY(newVec.z());
+			}
+		  } else {
+			_someGridVec1.setY(newVec.z());
+		  }
+
+		  if (newVec.y() < _gridWorldY) {
+			_gridWorldY = newVec.y();
+		  }
+	}
+
+	if (!_loadedFromBin) {
+		if (!name().contains("19000"))
+		  _gridOffsetSomething = TeVector2f32(5.0f, 5.0f);
+		else
+		  _gridOffsetSomething = TeVector2f32(2.0f, 2.0f);
+	} else {
+		const TeVector2f32 gridVecDiff = _someGridVec2 - _someGridVec1;
+		float minSide = MIN(gridVecDiff.getX(), gridVecDiff.getY()) / 20.0f;
+		_gridOffsetSomething.setX(minSide);
+		_gridOffsetSomething.setY(minSide);
+
+		error("FIXME: Finish preUpdateGrid for non-loaded-from-bin case.");
+		/*
+		// what's this field?
+		if (_field_0x414.x != 0.0)
+			_gridOffsetSomething = _field_0x414;
+		*/
+	}
+
+	TeMatrix4x4 worldTrans = worldTransformationMatrix();
+	worldTrans.inverse();
+	_inverseWorldTransform = worldTrans;
 }
 
 TeVector2s32 TeFreeMoveZone::projectOnAStarGrid(const TeVector3f32 &pt) {
@@ -296,34 +507,6 @@ TeVector2s32 TeFreeMoveZone::projectOnAStarGrid(const TeVector3f32 &pt) {
 	return TeVector2s32((int)projected.getX(), (int)projected.getY());
 }
 
-static int segmentIntersection(const TeVector2f32 &s1start, const TeVector2f32 &s1end,
-						const TeVector2f32 &s2start, const TeVector2f32 &s2end,
-                       TeVector2f32 *sout, float *fout1, float *fout2) {
-	TeVector2f32 s1len = s1end - s1start;
-	TeVector2f32 s2len = s2end - s2start;
-	float squarelen = s1len.getX() * s2len.getX() + s1len.getY() * s2len.getY();
-	int result = 0;
-	if (squarelen != 0) {
-		result = 1;
-		float intersection1 = -((s1len.getY() * s1start.getX() +
-						(s1len.getX() * s2start.getY() - s1len.getX() * s1start.getY())) -
-                          s1len.getY() * s2start.getX()) / squarelen;
-		if (intersection1 >= 0.0f && intersection1 <= 1.0f) {
-			float intersection2 = -((s2len.getY() * s2start.getY() +
-						(s2len.getX() * s1start.getX() - s2len.getX() * s2start.getX())) -
-                          s2len.getY() * s1start.getY()) / squarelen;
-			if (intersection2 >= 0.0f && intersection2 <= 1.0f) {
-				result = 2;
-				if (sout || fout1 || fout2) {
-					// Seems like these are always null?
-					error("TODO: implement output in segmentIntersection");
-				}
-			}
-		}
-	}
-	return result;
-}
-
 Common::Array<TeVector3f32> TeFreeMoveZone::removeInsignificantPoints(const Common::Array<TeVector3f32> &points) {
 	if (points.size() < 2)
 		return points;
@@ -337,10 +520,10 @@ Common::Array<TeVector3f32> TeFreeMoveZone::removeInsignificantPoints(const Comm
         do {
 			const TeVector2f32 pt1(points[point1].x(), points[point1].z());
 			const TeVector2f32 pt2(points[point2].x(), points[point2].z());
-			for (unsigned int i = 0; i * 2 < _uintArray2.size() / 2; i++) {
-				const TeVector3f32 transpt3d1 = worldTransformationMatrix() * verticies()[_uintArray2[i * 2]];
+			for (unsigned int i = 0; i * 2 < _borders.size() / 2; i++) {
+				const TeVector3f32 transpt3d1 = worldTransformationMatrix() * verticies()[_borders[i * 2]];
 				const TeVector2f32 transpt1(transpt3d1.x(), transpt3d1.z());
-				const TeVector3f32 transpt3d2 = worldTransformationMatrix() * verticies()[_uintArray2[i * 2 + 1]];
+				const TeVector3f32 transpt3d2 = worldTransformationMatrix() * verticies()[_borders[i * 2 + 1]];
 				const TeVector2f32 transpt2(transpt3d2.x(), transpt3d2.z());
 				if (segmentIntersection(pt1, pt2, transpt1, transpt2, nullptr, nullptr, nullptr) == 2)
 					break;
@@ -425,7 +608,45 @@ TeVector3f32 TeFreeMoveZone::transformVectorInWorldSpace(float x, float y) {
 }
 
 void TeFreeMoveZone::updateBorders() {
-	error("TODO: Implement TeFreeMoveZone::updateBorders");
+	if (!_bordersDirty)
+		return;
+
+	updatePickMesh();
+
+	if (_verticies.size() > 2) {
+		for (unsigned int triNo1 = 0; triNo1 < _verticies.size() / 3; triNo1++) {
+			for (unsigned int vecNo1 = 0; vecNo1 < 3; vecNo1++) {
+				unsigned int left1 = triNo1 * 3 + vecNo1;
+				unsigned int left2 = triNo1 * 3 + (vecNo1 == 2 ? 0 : vecNo1 + 1);
+				const TeVector3f32 vleft1 = _verticies[left1];
+				const TeVector3f32 vleft2 = _verticies[left2];
+
+				bool skip = false;
+				for (unsigned int triNo2 = 0; triNo2 < _verticies.size() / 3; triNo2++) {
+					if (skip)
+						break;
+
+					for (unsigned int vecNo2 = 0; vecNo2 < 3; vecNo2++) {
+						if (triNo2 == triNo1)
+							continue;
+						unsigned int right1 = triNo2 * 3 + vecNo2;
+						unsigned int right2 = triNo2 * 3 + (vecNo2 == 2 ? 0 : vecNo2 + 1);
+						TeVector3f32 vright1 = _verticies[right1];
+						TeVector3f32 vright2 = _verticies[right2];
+						if (vright1 == vleft1 && vright2 == vleft2 && vright1 == vleft2 && vright2 == vleft1) {
+							skip = true;
+							break;
+						}
+					}
+				}
+				if (!skip) {
+					_borders.push_back(left1);
+					_borders.push_back(left2);
+				}
+			}
+		}
+    }
+	_bordersDirty = false;
 }
 
 void TeFreeMoveZone::updateGrid(bool force) {
@@ -444,7 +665,29 @@ void TeFreeMoveZone::updatePickMesh() {
 	if (!_pickMeshDirty)
 		return;
 
-	error("TODO: Implement TeFreeMoveZone::updatePickMesh");
+	updateTransformedVertices();
+	_pickMesh.clear();
+	_pickMesh.reserve(_freeMoveZoneVerticies.size());
+	int vecNo = 0;
+    for (unsigned int tri = 0; tri < _freeMoveZoneVerticies.size() / 3; tri++) {
+        _pickMesh.push_back(vecNo);
+        _pickMesh.push_back(vecNo + 1);
+        _pickMesh.push_back(vecNo + 2);
+        vecNo += 3;
+    }
+
+    debug("[TeFreeMoveZone::updatePickMesh] %s nb triangles reduced from : %d to : %d", name().c_str(),
+             _freeMoveZoneVerticies.size() / 3, _pickMesh.size() / 3);
+
+    TePickMesh2::setNbTriangles(_pickMesh.size() / 3);
+
+    for (unsigned int i = 0; i < _pickMesh.size(); i++) {
+        _verticies[i] = _freeMoveZoneVerticies[_pickMesh[i]];
+    }
+    _bordersDirty = true;
+    _pickMeshDirty = false;
+    _projectedPointsDirty = true;
+    _gridDirty = true;
 }
 
 void TeFreeMoveZone::updateProjectedPoints() {
@@ -458,7 +701,12 @@ void TeFreeMoveZone::updateTransformedVertices() {
 	if (!_transformedVerticiesDirty)
 		return;
 
-	error("TODO: Implement TeFreeMoveZone::updateTransformedVertices");
+	const TeMatrix4x4 worldTransform = worldTransformationMatrix();
+	_transformedVerticies.resize(_freeMoveZoneVerticies.size());
+	for (unsigned int i = 0; i < _transformedVerticies.size(); i++) {
+        _transformedVerticies[i] = worldTransform * _freeMoveZoneVerticies[i];
+    }
+    _transformedVerticiesDirty = false;
 }
 
 /*========*/
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index 0bd8c4a98b2..9f6dcc6442a 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -122,15 +122,16 @@ private:
 	Common::Array<TeRectBlocker> *_rectBlockers;
 
 	Common::Array<TeVector3f32> _freeMoveZoneVerticies;
-	// TODO: Find better names..
-	Common::Array<unsigned int> _uintArray1;
-	Common::Array<TeVector3f32> _vectorArray;
-	Common::Array<unsigned int> _uintArray2;
+	Common::Array<unsigned int> _pickMesh;
+	Common::Array<TeVector3f32> _transformedVerticies;
+	Common::Array<unsigned int> _borders;
 
+	// TODO: Find better names..
 	TeVector2f32 _gridOffsetSomething;
 	TeVector2f32 _someGridVec1;
 	TeVector2f32 _someGridVec2;
 	TeMatrix4x4 _gridMatrix;
+	TeMatrix4x4 _inverseWorldTransform;
 
 	float _gridWorldY;
 
diff --git a/engines/tetraedge/te/te_lua_context.cpp b/engines/tetraedge/te/te_lua_context.cpp
index feb3ae66a32..15b7c8b880c 100644
--- a/engines/tetraedge/te/te_lua_context.cpp
+++ b/engines/tetraedge/te/te_lua_context.cpp
@@ -33,7 +33,7 @@ namespace Tetraedge {
 
 static int luaPanicFunction(lua_State *state) {
 	const char *msg = lua_tolstring(state, -1, nullptr);
-	warning("Lua: %s\n",msg);
+	warning("Lua: %s",msg);
 	lua_settop(state, -2);
 	return 1;
 }
diff --git a/engines/tetraedge/te/te_lua_gui.cpp b/engines/tetraedge/te/te_lua_gui.cpp
index e6d55d14ed5..26e60c63ee5 100644
--- a/engines/tetraedge/te/te_lua_gui.cpp
+++ b/engines/tetraedge/te/te_lua_gui.cpp
@@ -287,6 +287,7 @@ void TeLuaGUI::unload() {
 		delete iter._value;
 	}
 	_colorLinearAnimations.clear();
+	_loaded = false;
 }
 
 TeVariant TeLuaGUI::value(const Common::String &globalName) {
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index a7dbead85b3..db034856396 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -214,7 +214,7 @@ static bool _g_bWidescreen = false;
 
 int layoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("layoutBindings:: the lua value is not a table\n");
+		warning("layoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -232,7 +232,7 @@ int layoutBindings(lua_State *L) {
 					layout->setScale(TeVector3f32(0.7500001f, 1.0f ,1.0f));
 				}
 			} else {
-				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s\n", s);
+				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
 			}
 		} else if (type == LUA_TNUMBER) {
 			Te3DObject2 *obj = TeLuaTo<Te3DObject2*>(L, -1);
diff --git a/engines/tetraedge/te/te_pick_mesh2.h b/engines/tetraedge/te/te_pick_mesh2.h
index 584a3cf88b9..42c1a4235aa 100644
--- a/engines/tetraedge/te/te_pick_mesh2.h
+++ b/engines/tetraedge/te/te_pick_mesh2.h
@@ -53,7 +53,7 @@ public:
 	Common::Array<TeVector3f32> &verticies() { return _verticies; }
 	const Common::Array<TeVector3f32> &verticies() const { return _verticies; }
 
-private:
+protected:
 	Common::Array<TeVector3f32> _verticies;
 	unsigned int _lastTriangleHit;
 
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index ea5e0b0ee9d..464d6632cad 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -265,7 +265,7 @@ void TeRenderer::init() {
 	debug("[TeRenderer::init] Sentil buffer bits : %d", bits);
 	glGetIntegerv(GL_DEPTH_BITS, &bits);
 	debug("[TeRenderer::init] Depth buffer bits : %d", bits);
-	//debug("[TeRenderer::init] Extensions : %s\n", glGetString(GL_EXTENSIONS));
+	//debug("[TeRenderer::init] Extensions : %s", glGetString(GL_EXTENSIONS));
 	//TeOpenGLExtensions::loadExtensions(); // this does nothing in the game?
 	_currentColor = TeColor(255, 255, 255, 255);
 	_scissorEnabled = false;
diff --git a/engines/tetraedge/te/te_scrolling_layout.cpp b/engines/tetraedge/te/te_scrolling_layout.cpp
index 9f027a624a9..01522966b52 100644
--- a/engines/tetraedge/te/te_scrolling_layout.cpp
+++ b/engines/tetraedge/te/te_scrolling_layout.cpp
@@ -23,7 +23,12 @@
 
 namespace Tetraedge {
 
-TeScrollingLayout::TeScrollingLayout() : _autoScrollDelay(0), _contentLayout(nullptr) {
+TeScrollingLayout::TeScrollingLayout() : _contentLayout(nullptr),
+	_enclose(true), _mouseControl(true), _autoScrollLoop(-1), _autoScrollDelay(1500),
+	_autoScrollAnimation1Enabled(true), _autoScrollAnimation2Enabled(true),
+	_autoScrollAnimation1Speed(0.1), _autoScrollAnimation2Speed(0.1),
+	_autoScrollAnimation1Delay(1000), _autoScrollAnimation2Delay(1000)
+{
 }
 
 void TeScrollingLayout::setContentLayout(TeLayout *layout) {
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index a5676d8e277..deefe76c733 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -18,7 +18,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
- 
+
 //#define DUMP_RENDERED_FONTS 1
 
 #ifdef DUMP_RENDERED_FONTS
diff --git a/engines/tetraedge/te/te_text_layout.cpp b/engines/tetraedge/te/te_text_layout.cpp
index db3a34aeadf..77750a593cd 100644
--- a/engines/tetraedge/te/te_text_layout.cpp
+++ b/engines/tetraedge/te/te_text_layout.cpp
@@ -107,8 +107,9 @@ void TeTextLayout::setText(const Common::String &val) {
 
 	if (parser.fontFile().size()) {
 		Common::Path fontPath(parser.fontFile());
+		fontPath = g_engine->getCore()->findFile(fontPath);
 		TeIntrusivePtr<TeFont3> font = g_engine->getResourceManager()->getResource<TeFont3>(fontPath);
-		font->load(fontPath);
+		//font->load(fontPath); // lazy load this later.
 		_base.setFont(0, font);
 	}
 	if (parser.style().size())
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index 0b4337f9463..f2c123fe550 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -41,6 +41,10 @@ void TeTiledSurface::cont() {
 	_frameAnim.cont();
 }
 
+TeTiledSurface::~TeTiledSurface() {
+	unload();
+}
+
 void TeTiledSurface::draw() {
 	if (_tiledTexture && _tiledTexture->isLoaded())
 		TeModel::draw();
diff --git a/engines/tetraedge/te/te_tiled_surface.h b/engines/tetraedge/te/te_tiled_surface.h
index 9e9c77a42ea..85e54ead249 100644
--- a/engines/tetraedge/te/te_tiled_surface.h
+++ b/engines/tetraedge/te/te_tiled_surface.h
@@ -38,6 +38,7 @@ namespace Tetraedge {
 class TeTiledSurface : public TeModel {
 public:
 	TeTiledSurface();
+	virtual ~TeTiledSurface();
 
 	virtual int bufferSize() { return 1; } // unused?
 	void cont();


Commit: 066dd8e15584a77661f7557db1364f0448a66661
    https://github.com/scummvm/scummvm/commit/066dd8e15584a77661f7557db1364f0448a66661
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More improvements

Changed paths:
    engines/tetraedge/game/cellphone.h
    engines/tetraedge/game/character_settings_xml_parser.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/inventory_objects_xml_parser.h
    engines/tetraedge/game/loc_file.cpp
    engines/tetraedge/game/main_menu.cpp
    engines/tetraedge/game/object_settings_xml_parser.h
    engines/tetraedge/game/scene_lights_xml_parser.h
    engines/tetraedge/metaengine.h
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_name_val_xml_parser.h
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_ray_intersection.cpp
    engines/tetraedge/te/te_ray_intersection.h
    engines/tetraedge/te/te_text_layout_xml_parser.h
    engines/tetraedge/te/te_vector3f32.cpp
    engines/tetraedge/te/te_vector3f32.h


diff --git a/engines/tetraedge/game/cellphone.h b/engines/tetraedge/game/cellphone.h
index f9d1c8922b3..d23d3c80a92 100644
--- a/engines/tetraedge/game/cellphone.h
+++ b/engines/tetraedge/game/cellphone.h
@@ -25,7 +25,7 @@
 #include "common/array.h"
 #include "common/callback.h"
 #include "common/str.h"
-#include "common/xmlparser.h"
+#include "common/formats/xmlparser.h"
 
 #include "tetraedge/te/te_layout.h"
 #include "tetraedge/te/te_text_layout.h"
diff --git a/engines/tetraedge/game/character_settings_xml_parser.h b/engines/tetraedge/game/character_settings_xml_parser.h
index 1c15e95e0f9..f7b41b58402 100644
--- a/engines/tetraedge/game/character_settings_xml_parser.h
+++ b/engines/tetraedge/game/character_settings_xml_parser.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_GAME_CHARACTER_SETTINGS_XML_PARSER_H
 #define TETRAEDGE_GAME_CHARACTER_SETTINGS_XML_PARSER_H
 
-#include "common/xmlparser.h"
+#include "common/formats/xmlparser.h"
 #include "tetraedge/game/character.h"
 #include "tetraedge/te/te_vector3f32.h"
 
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index d8db0d019da..1c8444032db 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -988,16 +988,16 @@ bool Game::onMouseClick(const Common::Point &pt) {
 	if (_previousMousePos == TeVector2s32(-1, -1)) {
 		_previousMousePos = pt;
 	} else {
-		TeVector3f32 winSize = app->getMainWindow().size();
-		TeVector2s32 lastMousePos = _previousMousePos;
+		const TeVector3f32 winSize = app->getMainWindow().size();
+		const TeVector2s32 prevMousePos = _previousMousePos;
 		_previousMousePos = pt;
-		float xdist = pt.x / winSize.x() - lastMousePos._x / winSize.x();
-		float ydist = pt.y / winSize.y() - lastMousePos._y / winSize.y();
+		float xdist = (pt.x - prevMousePos._x) / winSize.x();
+		float ydist = (pt.y - prevMousePos._y) / winSize.y();
 		float sqrdist = xdist * xdist + ydist * ydist;
-		if (sqrdist > 0.0001 && (!_walkTimer.running() || _walkTimer.timeElapsed() > 300000.0
+		if (sqrdist < 0.0001 && (!_walkTimer.running() || _walkTimer.timeElapsed() > 300000.0
 						 || (_scene._character && _scene._character->walkModeStr() != "Walk"))) {
 			return false;
-			// Double-click, but already jogging
+			// Normal walk click
 		}
 	}
 
@@ -1006,11 +1006,12 @@ bool Game::onMouseClick(const Common::Point &pt) {
 
 	Common::String nearestMeshName = "None";
 	TeIntrusivePtr<TeCamera> curCamera = _scene.currentCamera();
-	Common::Array<TePickMesh2*> pickMeshes = _scene.pickMeshes();
+	Common::Array<TePickMesh2*> pickMeshes = _scene.clickMeshes();
 	TePickMesh2 *nearestMesh = TeFreeMoveZone::findNearestMesh(curCamera, pt, pickMeshes, nullptr, false);
 	if (nearestMesh) {
 		nearestMeshName = nearestMesh->name();
-		_lastCharMoveMousePos = TeVector2s32(0, 0);
+		debug("Game::onMouseClick: Click near mesh %s", nearestMeshName.c_str());
+		_lastCharMoveMousePos = TeVector2s32();
 	}
 
 	if (app->isLockCursor() || _movePlayerCharacterDisabled)
@@ -1025,49 +1026,51 @@ bool Game::onMouseClick(const Common::Point &pt) {
 		|| charAnim == character->walkAnim(Character::WalkPart_EndD)
 		|| charAnim == character->walkAnim(Character::WalkPart_EndG)) {
 		_luaScript.execute("On");
-		if (!_scene.isObjectBlocking(nearestMeshName)) {
-			if (character->freeMoveZone()) {
-				TeVector3f32 charPos = character->_model->position();
-				TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(charPos, TeVector2s32(pt), 8.0, true);
-				if (curve) {
-					_scene.setCurve(curve);
-					character->setCurveStartLocation(TeVector3f32());
-					if (curve->controlPoints().size() == 1) {
-						character->endMove();
-					} else {
-						if (!_walkTimer.running() || _walkTimer.timeElapsed() > 300000) {
-							_walkTimer.stop();
-							_walkTimer.start();
-							character->walkMode("Walk");
-						} else {
-							// Note: original checks the timer elapsed again here.. why?
-							_walkTimer.stop();
-							character->walkMode("Jog");
-						}
-						character->placeOnCurve(curve);
-						character->setCurveOffset(0.0);
-						if (charAnim != character->walkAnim(Character::WalkPart_Start)) {
-							character->setAnimation(character->walkAnim(Character::WalkPart_Start), false);
-						}
-						character->walkTo(1.0, false);
-						_sceneCharacterVisibleFromLoad = false;
-						_lastCharMoveMousePos = pt;
-					}
+		if (!_scene.isObjectBlocking(nearestMeshName) && character->freeMoveZone()) {
+			const TeVector3f32 charPos = character->_model->position();
+			TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(charPos, pt, 8.0, true);
+			if (!curve)
+				return false;
+
+			_scene.setCurve(curve);
+			character->setCurveStartLocation(TeVector3f32());
+			if (curve->controlPoints().size() == 1) {
+				character->endMove();
+			} else {
+				if (!_walkTimer.running() || _walkTimer.timeElapsed() > 300000) {
+					_walkTimer.stop();
+					_walkTimer.start();
+					character->walkMode("Walk");
 				} else {
-					return false;
+					// Note: original checks the timer elapsed again here.. why?
+					_walkTimer.stop();
+					character->walkMode("Jog");
 				}
+				character->placeOnCurve(curve);
+				character->setCurveOffset(0.0);
+				if (charAnim != character->walkAnim(Character::WalkPart_Start)) {
+					character->setAnimation(character->walkAnim(Character::WalkPart_Start), false);
+				}
+				character->walkTo(1.0, false);
+				_sceneCharacterVisibleFromLoad = false;
+				_lastCharMoveMousePos = pt;
 			}
 		}
-		TeVector3f32 lastPoint = _scene.curve()->controlPoints().back();
-		character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
-		character->walkTo(1.0, false);
-		_isCharacterWalking = true;
-		_posPlayer = lastPoint;
+		// FIXME: The original never checks for empty/null curve here.. why?
+		if (_scene.curve() && _scene.curve()->length()) {
+			TeVector3f32 lastPoint = _scene.curve()->controlPoints().back();
+			character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
+			character->walkTo(1.0, false);
+			_isCharacterWalking = true;
+			_posPlayer = lastPoint;
+		}
 	}
-
-	if (!_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._idleAnimFileName)) {
+	
+	// Note: charAnim above may no longer be valid as anim may have changed.
+	if (_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._idleAnimFileName)) {
 		_lastCharMoveMousePos = TeVector2s32(0, 0);
-		_movePlayerCharacterDisabled = true;
+		_movePlayerCharacterDisabled = false;
+		_isCharacterIdle = true;
 		_isCharacterWalking = false;
 		if (nearestMesh) {
 			character->stop();
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index fc9df5e7ff6..48ffd9592b1 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -42,7 +42,7 @@
 #include "tetraedge/te/te_lua_script.h"
 #include "tetraedge/te/te_lua_thread.h"
 
-//#define DEBUG_PATHFINDING 1
+#define DEBUG_PATHFINDING 1
 
 namespace Tetraedge {
 
@@ -204,9 +204,9 @@ void InGameScene::close() {
 		delete zone;
 	_freeMoveZones.clear();
 	_hitObjects.clear();
-	for (TePickMesh2 *mesh : _pickMeshes)
+	for (TePickMesh2 *mesh : _clickMeshes)
 		delete mesh;
-	_pickMeshes.clear();
+	_clickMeshes.clear();
 	_bezierCurves.clear();
 	_dummies.clear();
 	freeSceneObjects();
@@ -289,6 +289,8 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 	TeQuaternion rot;
 	TeColor col;
 	TeMesh mesh;
+	
+	assert(pickmesh);
 
 	TeVector3f32::deserialize(stream, vec);
 	model->setPosition(vec);
@@ -328,7 +330,7 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 	pickmesh->setNbTriangles(indexcount / 3);
 	for (unsigned int i = 0; i < indexcount; i++) {
 		vec = mesh.vertex(mesh.index(i));
-		pickmesh->verticies().push_back(vec);
+		pickmesh->verticies()[i] = vec;
 	}
 	model->addMesh(mesh);
 }
@@ -351,6 +353,11 @@ void InGameScene::draw() {
 		zone->setVisible(true);
 		zone->draw();
 	}
+	
+	for (TePickMesh2 *mesh : _clickMeshes) {
+		mesh->setVisible(true);
+		mesh->draw();
+	}
 #endif
 
 	TeLight::updateGlobal();
@@ -583,12 +590,13 @@ bool InGameScene::load(const Common::Path &path) {
 		TePickMesh2 *pickmesh = new TePickMesh2();
 		deserializeModel(scenefile, model, pickmesh);
 		if (modelname.contains("Clic")) {
+			//debug("Loaded clickMesh %s", modelname.c_str());
 			_hitObjects.push_back(model);
-			model->setVisible(false);
+			model->setVisible(true);
 			model->setColor(TeColor(0, 0xff, 0, 0xff));
 			models().push_back(model);
 			pickmesh->setName(modelname);
-			_pickMeshes.push_back(pickmesh);
+			_clickMeshes.push_back(pickmesh);
 		} else {
 			delete pickmesh;
 			if (modelname.substr(0, 2) != "ZB") {
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 17fb23d1698..7fb5318b2ed 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -180,7 +180,7 @@ public:
 	TeLuaGUI &hitObjectGui() { return _hitObjectGui; }
 	TeLuaGUI &markerGui() { return _markerGui; }
 
-	Common::Array<TePickMesh2 *> &pickMeshes() { return _pickMeshes; }
+	Common::Array<TePickMesh2 *> &clickMeshes() { return _clickMeshes; }
 
 	float shadowFarPlane() const { return _shadowFarPlane; }
 	float shadowNearPlane() const { return _shadowNearPlane; }
@@ -218,7 +218,7 @@ private:
 	Common::Array<Object3D *> _object3Ds;
 	Common::Array<Billboard *> _billboards;
 	Common::Array<TeSpriteLayout *> _sprites;
-	Common::Array<TePickMesh2 *> _pickMeshes;
+	Common::Array<TePickMesh2 *> _clickMeshes;
 
 	Common::HashMap<Common::String, SoundStep> _soundSteps;
 	Common::HashMap<Common::String, Common::Array<Callback*>> _callbacks;
diff --git a/engines/tetraedge/game/inventory_objects_xml_parser.h b/engines/tetraedge/game/inventory_objects_xml_parser.h
index 6f86575527d..904fb1d7d87 100644
--- a/engines/tetraedge/game/inventory_objects_xml_parser.h
+++ b/engines/tetraedge/game/inventory_objects_xml_parser.h
@@ -21,7 +21,7 @@
 
 #include "common/hashmap.h"
 #include "common/str.h"
-#include "common/xmlparser.h"
+#include "common/formats/xmlparser.h"
 #include "tetraedge/game/inventory.h"
 
 #ifndef TETRAEDGE_GAME_INVENTORY_OBJECTS_XML_PARSER_H
diff --git a/engines/tetraedge/game/loc_file.cpp b/engines/tetraedge/game/loc_file.cpp
index 10321ad432c..89b43242403 100644
--- a/engines/tetraedge/game/loc_file.cpp
+++ b/engines/tetraedge/game/loc_file.cpp
@@ -21,7 +21,7 @@
 
 #include "common/file.h"
 #include "common/textconsole.h"
-#include "common/xmlparser.h"
+#include "common/formats/xmlparser.h"
 
 #include "tetraedge/game/loc_file.h"
 #include "tetraedge/te/te_name_val_xml_parser.h"
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
index ec92bbee2ce..fec813c658a 100644
--- a/engines/tetraedge/game/main_menu.cpp
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -62,7 +62,7 @@ void MainMenu::enter() {
 	_entered = true;
 	load("menus/mainMenu/mainMenu.lua");
 
-	TeLayout *menuLayout = layout("menu");
+	TeLayout *menuLayout = layoutChecked("menu");
 	appSpriteLayout.addChild(menuLayout);
 
 	app->mouseCursorLayout().setVisible(true);
diff --git a/engines/tetraedge/game/object_settings_xml_parser.h b/engines/tetraedge/game/object_settings_xml_parser.h
index a6d7c244078..328b4a04236 100644
--- a/engines/tetraedge/game/object_settings_xml_parser.h
+++ b/engines/tetraedge/game/object_settings_xml_parser.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_GAME_OBJECT_SETTINGS_XML_PARSER_H
 #define TETRAEDGE_GAME_OBJECT_SETTINGS_XML_PARSER_H
 
-#include "common/xmlparser.h"
+#include "common/formats/xmlparser.h"
 #include "tetraedge/game/object3d.h"
 #include "tetraedge/te/te_vector3f32.h"
 
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.h b/engines/tetraedge/game/scene_lights_xml_parser.h
index 0b85362ec5f..a6159b0b099 100644
--- a/engines/tetraedge/game/scene_lights_xml_parser.h
+++ b/engines/tetraedge/game/scene_lights_xml_parser.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_GAME_SCENE_LIGHTS_XML_PARSER_H
 #define TETRAEDGE_GAME_SCENE_LIGHTS_XML_PARSER_H
 
-#include "common/xmlparser.h"
+#include "common/formats/xmlparser.h"
 #include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_vector3f32.h"
 
diff --git a/engines/tetraedge/metaengine.h b/engines/tetraedge/metaengine.h
index a992757f82d..08c9d062c27 100644
--- a/engines/tetraedge/metaengine.h
+++ b/engines/tetraedge/metaengine.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_METAENGINE_H
 #define TETRAEDGE_METAENGINE_H
 
-#include "common/achievements.h"
+#include "engines/achievements.h"
 #include "engines/advancedDetector.h"
 
 class TetraedgeMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index bb2eb7bf4dc..ed8c8217d4d 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -136,24 +136,25 @@ void TeCamera::draw() {
 	error("TODO: Implement TeCamera::draw");
 }
 
-void TeCamera::getRay(const TeVector2s32 &pxloc, TeVector3f32 &out1, TeVector3f32 &out2) {
+void TeCamera::getRay(const TeVector2s32 &pxloc, TeVector3f32 &raypos, TeVector3f32 &raydir) {
+	raypos = position();
+
 	float xval = (pxloc._x - _viewportX) / fabs(_viewportW);
-	out2.x() = xval * 2 - 1;
+	raydir.x() = xval * 2 - 1;
 	float yval = (pxloc._y - _viewportY) / fabs(_viewportH);
-	out2.y() = yval * 2 - 1;
-	out2.z() = 1.0;
+	raydir.y() = yval * 2 - 1;
+	raydir.z() = 1.0;
 
-	TeMatrix4x4 inverse = projectionMatrix();
-	inverse.inverse();
-	out2 = inverse * out2;
-	out2.normalize();
+	TeMatrix4x4 projInverse = _projectionMatrix;
+	projInverse.inverse();
+	raydir = projInverse * raydir;
+	raydir.normalize();
 
-	TeVector3f32 pos = position();
-	TeQuaternion rot = rotation();
-	out1 = pos;
+	TeQuaternion rot = _rotation;
 	rot.normalize();
-	TeMatrix4x4 rotmatrix = rot.toTeMatrix();
-	out2 = rotmatrix * out2;
+	const TeMatrix4x4 rotmatrix = rot.toTeMatrix();
+
+	raydir = rotmatrix * raydir;
 }
 
 void TeCamera::loadBin(const Common::String &path) {
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index 8f586a7d495..59cccba30b2 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -156,6 +156,8 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 			testPath.joinInPlace(fname);
 			if (Common::File::exists(testPath) || Common::FSNode(path).exists()) {
 				return testPath;
+			} else {
+				debug("not found %s", testPath.toString().c_str());
 			}
 		}
 	}
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index ac4f09d405c..daf300a7756 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -176,13 +176,13 @@ TeVector3f32 TeFreeMoveZone::correctCharacterPosition(const TeVector3f32 &pos, b
 	return intersectPoint;
 }
 
-TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt, const TeVector2s32 &endpt, float param_5, bool findMeshFlag) {
+TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt, const TeVector2s32 &clickPt, float param_5, bool findMeshFlag) {
 	updateGrid(false);
 	Common::Array<TePickMesh2 *> meshes;
 	TeVector3f32 newend;
 	meshes.push_back(this);
 
-	TePickMesh2 *nearest = findNearestMesh(_camera, endpt, meshes, &newend, findMeshFlag);
+	TePickMesh2 *nearest = findNearestMesh(_camera, clickPt, meshes, &newend, findMeshFlag);
 	if (!nearest)
 		return TeIntrusivePtr<TeBezierCurve>();
 
@@ -803,54 +803,52 @@ void TeFreeMoveZoneGraph::serialize(Common::WriteStream &stream) const {
 }
 
 /*static*/
-TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, const TeVector2s32 &frompt,
+TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, const TeVector2s32 &fromPt,
 			Common::Array<TePickMesh2*> &pickMeshes, TeVector3f32 *outloc, bool lastHitFirst) {
-	TeVector3f32 locresult;
-	TePickMesh2 *nearest = nullptr;
-	float furthest = camera->_orthFarVal;
-	if (!pickMeshes.empty()) {
-		TeVector3f32 v1;
-		TeVector3f32 v2;
-		for (unsigned int i = 0; i < pickMeshes.size(); i++) {
-			TePickMesh2 *mesh = pickMeshes[i];
-			const TeMatrix4x4 transform = mesh->worldTransformationMatrix();
-			if (lastHitFirst) {
-				unsigned int tricount = mesh->verticies().size() / 3;
-				unsigned int vert = mesh->lastTriangleHit() * 3;
-				if (mesh->lastTriangleHit() >= tricount)
-					vert = 0;
-				const TeVector3f32 v3 = transform * mesh->verticies()[vert];
-				const TeVector3f32 v4 = transform * mesh->verticies()[vert + 1];
-				const TeVector3f32 v5 = transform * mesh->verticies()[vert + 2];
-				TeVector3f32 result;
-				float fresult;
-				int intresult = TeRayIntersection::intersect(v1, v2, v3, v4, v5, result, fresult);
-				if (intresult == 1 && fresult < furthest && fresult >= camera->_orthNearVal)
-					return mesh;
-			}
-			for (unsigned int tri = 0; tri < mesh->verticies().size() / 3; tri++) {
-				const TeVector3f32 v3 = transform * mesh->verticies()[tri * 3];
-				const TeVector3f32 v4 = transform * mesh->verticies()[tri * 3 + 1];
-				const TeVector3f32 v5 = transform * mesh->verticies()[tri * 3 + 1];
-				camera->getRay(frompt, v1, v2);
-				TeVector3f32 result;
-				float fresult;
-				int intresult = TeRayIntersection::intersect(v1, v2, v3, v4, v5, result, fresult);
-				if (intresult == 1 && fresult < furthest && fresult >= camera->_orthNearVal) {
-					mesh->setLastTriangleHit(tri);
-					locresult = result;
-					furthest = fresult;
-					nearest = mesh;
-					if (lastHitFirst)
-						break;
-				}
+	TeVector3f32 closestLoc;
+	TePickMesh2 *nearestMesh = nullptr;
+	float closestDist = camera->_orthFarVal;
+	TeVector3f32 rayLoc;
+	TeVector3f32 rayDir;
+	for (unsigned int i = 0; i < pickMeshes.size(); i++) {
+		TePickMesh2 *mesh = pickMeshes[i];
+		const TeMatrix4x4 meshWorldTransform = mesh->worldTransformationMatrix();
+		if (lastHitFirst) {
+			unsigned int tricount = mesh->verticies().size() / 3;
+			unsigned int vert = mesh->lastTriangleHit() * 3;
+			if (mesh->lastTriangleHit() >= tricount)
+				vert = 0;
+			const TeVector3f32 v1 = meshWorldTransform * mesh->verticies()[vert];
+			const TeVector3f32 v2 = meshWorldTransform * mesh->verticies()[vert + 1];
+			const TeVector3f32 v3 = meshWorldTransform * mesh->verticies()[vert + 2];
+			TeVector3f32 intersectLoc;
+			float intersectDist;
+			int intResult = TeRayIntersection::intersect(rayLoc, rayDir, v1, v2, v3, intersectLoc, intersectDist);
+			if (intResult == 1 && intersectDist < closestDist && intersectDist >= camera->_orthNearVal)
+				return mesh;
+		}
+		for (unsigned int tri = 0; tri < mesh->verticies().size() / 3; tri++) {
+			const TeVector3f32 v1 = meshWorldTransform * mesh->verticies()[tri * 3];
+			const TeVector3f32 v2 = meshWorldTransform * mesh->verticies()[tri * 3 + 1];
+			const TeVector3f32 v3 = meshWorldTransform * mesh->verticies()[tri * 3 + 2];
+			camera->getRay(fromPt, rayLoc, rayDir);
+			TeVector3f32 intersectLoc;
+			float intersectDist;
+			int intResult = TeRayIntersection::intersect(rayLoc, rayDir, v1, v2, v3, intersectLoc, intersectDist);
+			if (intResult == 1 && intersectDist < closestDist && intersectDist >= camera->_orthNearVal) {
+				mesh->setLastTriangleHit(tri);
+				closestLoc = intersectLoc;
+				closestDist = intersectDist;
+				nearestMesh = mesh;
+				if (lastHitFirst)
+					break;
 			}
 		}
 	}
 	if (outloc) {
-		*outloc = locresult;
+		*outloc = closestLoc;
 	}
-	return nearest;
+	return nearestMesh;
 }
 
 
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index db034856396..c922904da83 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -253,7 +253,7 @@ int layoutBindings(lua_State *L) {
 		lua_pushlightuserdata(L, static_cast<Te3DObject2*>(layout));
 		return true;
 	} else {
-		warning("layoutBindings:: multiple objects with name %s\n", layout->name().c_str());
+		warning("layoutBindings:: multiple objects with name %s", layout->name().c_str());
 		delete layout;
 		return false;
 	}
@@ -261,7 +261,7 @@ int layoutBindings(lua_State *L) {
 
 int listLayoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("listLayoutBindings:: the lua value is not a table\n");
+		warning("listLayoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -292,7 +292,7 @@ int listLayoutBindings(lua_State *L) {
 					layout->setScale(TeVector3f32(0.7500001f, 1.0f ,1.0f));
 				}
 			} else {
-				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s\n", s);
+				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
 			}
 		} else if (type == LUA_TNUMBER) {
 			Te3DObject2 *obj = TeLuaTo<Te3DObject2*>(L, -1);
@@ -313,7 +313,7 @@ int listLayoutBindings(lua_State *L) {
 		lua_pushlightuserdata(L, static_cast<Te3DObject2*>(layout));
 		return true;
 	} else {
-		warning("listLayoutBindings:: multiple objects with name %s\n", layout->name().c_str());
+		warning("listLayoutBindings:: multiple objects with name %s", layout->name().c_str());
 		delete layout;
 		return false;
 	}
@@ -321,7 +321,7 @@ int listLayoutBindings(lua_State *L) {
 
 int spriteLayoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("spriteLayoutBindings:: the lua value is not a table\n");
+		warning("spriteLayoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -379,7 +379,7 @@ int spriteLayoutBindings(lua_State *L) {
 					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
 				}
 			} else {
-				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s\n", s);
+				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
 			}
 		}
 		lua_settop(L, -2);
@@ -429,7 +429,7 @@ int spriteLayoutBindings(lua_State *L) {
 		lua_pushlightuserdata(L, static_cast<Te3DObject2*>(layout));
 		return true;
 	} else {
-		warning("layoutBindings:: multiple objects with name %s\n", layout->name().c_str());
+		warning("layoutBindings:: multiple objects with name %s", layout->name().c_str());
 		delete layout;
 		return false;
 	}
@@ -505,7 +505,7 @@ int buttonLayoutBindings(lua_State *L) {
 
 int checkboxLayoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("checkboxLayoutBindings:: the lua value is not a table\n");
+		warning("checkboxLayoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -575,7 +575,7 @@ int checkboxLayoutBindings(lua_State *L) {
 
 int layoutPositionLinearAnimationBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("layoutPositionLinearAnimationBindings:: the lua value is not a table\n");
+		warning("layoutPositionLinearAnimationBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -584,7 +584,7 @@ int layoutPositionLinearAnimationBindings(lua_State *L) {
 
 int layoutAnchorLinearAnimationBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("layoutAnchorLinearAnimationBindings:: the lua value is not a table\n");
+		warning("layoutAnchorLinearAnimationBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -639,7 +639,7 @@ int layoutAnchorLinearAnimationBindings(lua_State *L) {
 
 int textLayoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("textLayoutBindings:: the lua value is not a table\n");
+		warning("textLayoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -689,7 +689,7 @@ int textLayoutBindings(lua_State *L) {
 		lua_pushlightuserdata(L, static_cast<Te3DObject2*>(layout));
 		return true;
 	} else {
-		warning("textLayoutBindings:: multiple objects with name %s\n", layout->name().c_str());
+		warning("textLayoutBindings:: multiple objects with name %s", layout->name().c_str());
 		delete layout;
 		return false;
 	}
@@ -697,7 +697,7 @@ int textLayoutBindings(lua_State *L) {
 
 int clipLayoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("clipLayoutBindings:: the lua value is not a table\n");
+		warning("clipLayoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -706,7 +706,7 @@ int clipLayoutBindings(lua_State *L) {
 
 int colorLinearAnimationBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("colorLinearAnimationBindings:: the lua value is not a table\n");
+		warning("colorLinearAnimationBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -758,7 +758,7 @@ int colorLinearAnimationBindings(lua_State *L) {
 
 int rotationLinearAnimationBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("rotationLinearAnimationBindings:: the lua value is not a table\n");
+		warning("rotationLinearAnimationBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -767,7 +767,7 @@ int rotationLinearAnimationBindings(lua_State *L) {
 
 int scrollingLayoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("scrollingLayoutBindings:: the lua value is not a table\n");
+		warning("scrollingLayoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -840,7 +840,7 @@ int scrollingLayoutBindings(lua_State *L) {
 		lua_pushlightuserdata(L, static_cast<Te3DObject2*>(layout));
 		return true;
 	} else {
-		warning("scrollingLayoutBindings:: multiple objects with name %s\n", layout->name().c_str());
+		warning("scrollingLayoutBindings:: multiple objects with name %s", layout->name().c_str());
 		delete layout;
 		return false;
 	}
@@ -848,7 +848,7 @@ int scrollingLayoutBindings(lua_State *L) {
 
 int extendedTextLayoutBindings(lua_State *L) {
 	if (lua_type(L, -1) != LUA_TTABLE) {
-		warning("extendedTextLayoutBindings:: the lua value is not a table\n");
+		warning("extendedTextLayoutBindings:: the lua value is not a table");
 		return 0;
 	}
 
@@ -902,7 +902,7 @@ int extendedTextLayoutBindings(lua_State *L) {
 		lua_pushlightuserdata(L, static_cast<Te3DObject2*>(layout));
 		return true;
 	} else {
-		warning("extendedTextLayoutBindings:: multiple objects with name %s\n", layout->name().c_str());
+		warning("extendedTextLayoutBindings:: multiple objects with name %s", layout->name().c_str());
 		delete layout;
 		return false;
 	}
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index e71c4112157..ef1de05bba7 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -21,8 +21,8 @@
 
 #include "common/file.h"
 #include "common/util.h"
-#include "common/zlib.h"
 #include "common/substream.h"
+#include "common/compression/zlib.h"
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/te/te_light.h"
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index effca334c15..5a1888be26b 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -23,7 +23,7 @@
 #include "common/file.h"
 #include "common/stream.h"
 #include "common/substream.h"
-#include "common/zlib.h"
+#include "common/compression/zlib.h"
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/te/te_core.h"
diff --git a/engines/tetraedge/te/te_name_val_xml_parser.h b/engines/tetraedge/te/te_name_val_xml_parser.h
index e014de5c478..5bdbe2257f7 100644
--- a/engines/tetraedge/te/te_name_val_xml_parser.h
+++ b/engines/tetraedge/te/te_name_val_xml_parser.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_TE_TE_NAME_VAL_XML_PARSER_H
 #define TETRAEDGE_TE_TE_NAME_VAL_XML_PARSER_H
 
-#include "common/xmlparser.h"
+#include "common/formats/xmlparser.h"
 
 namespace Tetraedge {
 
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 91f4eb85da0..6380d6d1acd 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -50,6 +50,7 @@ void TePickMesh2::draw() {
 
 	const TeColor prevCol = renderer->currentColor();
 
+	renderer->enableWireFrame(); // NOTE: added this so we can draw _clickMeshes in scene.
 	renderer->setCurrentColor(TeColor(0xff, 0, 0, 0xff));
 	renderer->pushMatrix();
 	renderer->multiplyMatrix(transformationMatrix());
@@ -57,6 +58,7 @@ void TePickMesh2::draw() {
 
 	renderer->popMatrix();
 	renderer->setCurrentColor(prevCol);
+	renderer->disableWireFrame();
 }
 
 bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &vout, float &fout, bool lastHitFirst, unsigned long *triangleHitOut) {
diff --git a/engines/tetraedge/te/te_ray_intersection.cpp b/engines/tetraedge/te/te_ray_intersection.cpp
index ddbbd9e6dae..53eab70700d 100644
--- a/engines/tetraedge/te/te_ray_intersection.cpp
+++ b/engines/tetraedge/te/te_ray_intersection.cpp
@@ -30,39 +30,74 @@ TePickMesh *getMesh(const TeVector3f32 &param_1, const TeVector3f32 &param_2, co
 	error("TODO: implement TeRayIntersection::getMesh");
 }
 
-int intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3,
-			  const TeVector3f32 &v4, const TeVector3f32 &v5, TeVector3f32 &vout, float &fout) {
-	const TeVector3f32 v4_v3 = (v4 - v3);
-	const TeVector3f32 v5_v3 = (v5 - v3);
-	TeVector3f32 v = v4_v3 ^ v5_v3;
+// This is a version from https://www.lighthouse3d.com/tutorials/maths/ray-triangle-intersection/
+int intersect(const TeVector3f32 &p, const TeVector3f32 &d, const TeVector3f32 &v0,
+			  const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &vout, float &fout) {
+	const TeVector3f32 e1 = v1 - v0;
+	const TeVector3f32 e2 = v2 - v0;
+	const TeVector3f32 h = d ^ e2;
+
+	if (h == TeVector3f32())
+		return -1;
+
+	float a = e1.dotProduct(h);
+	if (fabs(a) < 1e-6)
+		return 0;
+	
+	float f = 1 / a;
+	TeVector3f32 s = p - v0;
+	float u = f * s.dotProduct(h);
+	if (u < 0.0 || u > 1.0)
+		return 0;
+
+	TeVector3f32 q = TeVector3f32::crossProduct(s, e1);
+	float v = f * d.dotProduct(q);
+
+	if (v < 0.0 || u + v > 1.0)
+		return 0;
+		
+	float t = f * e2.dotProduct(q);
+	
+	if (t < 0.00001)
+		return 0;
+	
+	fout = t;
+	vout = p + t * d;
+	
+	return 1;
+}
+
+/*
+int intersect(const TeVector3f32 &rayPos, const TeVector3f32 &rayDir, const TeVector3f32 &v1,
+			  const TeVector3f32 &v2, const TeVector3f32 &v3, TeVector3f32 &vout, float &fout) {
+	const TeVector3f32 v2_v1 = v2 - v1;
+	const TeVector3f32 v3_v1 = v3 - v1;
+	const TeVector3f32 v = v2_v1 ^ v3_v1;
 
 	if (v == TeVector3f32(0.0f, 0.0f, 0.0f))
 		return -1;
 
 	int result = -1;
-	float f1 = v.dotProduct(v1 - v3);
-	float f2 = v.dotProduct(v2);
+	float f1 = v.dotProduct(rayPos - v1);
+	float f2 = v.dotProduct(rayDir);
 	if (fabs(f2) > 1e-9) {
 		f2 = -f1 / f2;
 		fout = f2;
 		result = 0;
 		if (f2 >= 0.0) {
-			vout = v1 + (v2 * f2);
-			float dot1 = v4_v3.dotProduct(v4_v3);
-			float dot2 = v4_v3.dotProduct(v5_v3);
-			float dot3 = v5_v3.dotProduct(v5_v3);
-			const TeVector3f32 vout_v3 = vout - v3;
-			float dots1 = (dot2 * dot2) - (dot1 * dot3);
-			float dot4 = vout_v3.dotProduct(v4_v3);
-			float dot5 = vout_v3.dotProduct(v5_v3);
-			float dots2 = ((dot2 * dot5) - (dot3 * dot4)) / dots1;
-			if (dots2 >= 0.0) {
-				result = 0;
-				if (dots2 <= 1.0) {
-					float dots3 = (dot2 * dot4 - dot1 * dot5) / dots1;
-					if (dots3 >= 0 && dots2 + dots3 <= 1.0)
-						result = 1;
-				}
+			vout = rayPos + (rayDir * f2);
+			float dot1 = v2_v1.dotProduct(v2_v1);
+			float dot2 = v2_v1.dotProduct(v3_v1);
+			float dot3 = v3_v1.dotProduct(v3_v1);
+			const TeVector3f32 vout_v1 = vout - v1;
+			float dots1 = dot2 * dot2 - dot1 * dot3;
+			float dot4 = vout_v1.dotProduct(v2_v1);
+			float dot5 = vout_v1.dotProduct(v3_v1);
+			float dots2 = (dot2 * dot5 - dot3 * dot4) / dots1;
+			if (dots2 >= 0.0 && dots2 <= 1.0) {
+				float dots3 = (dot2 * dot4 - dot1 * dot5) / dots1;
+				if (dots3 >= 0.0 && dots2 + dots3 <= 1.0)
+					result = 1;
 			}
 		}
 	} else {
@@ -72,10 +107,56 @@ int intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32
 	}
 	return result;
 }
+*/
 
+/*
+bool testIntersection(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3, const TeVector3f32 &orig, TeVector3f32 &dir) {
+	TeVector3f32 qvec,tvec;
 
-}
+	TeVector3f32 e1 = v2 - v1;
+	TeVector3f32 e2 = v3 - v1;
+
+	TeVector3f32 pvec = TeVector3f32::crossProduct(dir, e2);
 
+	dir.normalize();
+	float det = pvec.dotProduct(e1);
+//#ifdef TEST_CULL
+	if (det < FLT_EPSILON)
+		return false;
+
+	tvec = orig - v1;
+	float u = tvec.dotProduct(pvec);
+	if (u < 0.0 || u > det) {
+		return false;
+	}
+	CROSS(qvec,tvec,e1);
+	float v = dir.dotProduct(qvec);
+	if (v < 0.0f || v + u > det) {
+		return false;
+	}
+#else
+	if (det < FLT_EPSILON && det > -FLT_EPSILON ) {
+		return false;
+	}
+
+	float invDet = 1.0f / det;
+	tvec = orig - v1;
+	// NORMALIZE(tvec);
+	float u = invDet * tvec.dotProduct(pvec);
+	if (u <0.0f || u > 1.0f) {
+		return false;
+	}
+
+	qvec = TeVector3f32::crossProduct(tvec, e1);
+	float v = invDet * qvec.dotProduct(dir);
+	if (v < 0.0f || u+v > 1.0f) {
+		return false;
+	}
+#endif
+	return true;
+}
+*/
 
+} // end namespace TeRayIntersection
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_ray_intersection.h b/engines/tetraedge/te/te_ray_intersection.h
index d4f4f19c329..f628632ff52 100644
--- a/engines/tetraedge/te/te_ray_intersection.h
+++ b/engines/tetraedge/te/te_ray_intersection.h
@@ -34,8 +34,8 @@ namespace TeRayIntersection {
 TePickMesh *getMesh(const TeVector3f32 &param_1, const TeVector3f32 &param_2, const Common::Array<TePickMesh *> &pickMeshes,
 			float param_4, float param_5, TeVector3f32 *param_6);
 
-int intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3,
-              const TeVector3f32 &v4, const TeVector3f32 &v5, TeVector3f32 &vout, float &fout);
+int intersect(const TeVector3f32 &rayPos, const TeVector3f32 &rayDir, const TeVector3f32 &v1,
+              const TeVector3f32 &v2, const TeVector3f32 &v3, TeVector3f32 &vout, float &fout);
 
 } // end namespace TeRayIntersection
 
diff --git a/engines/tetraedge/te/te_text_layout_xml_parser.h b/engines/tetraedge/te/te_text_layout_xml_parser.h
index c1a5affc73a..4bab07863b6 100644
--- a/engines/tetraedge/te/te_text_layout_xml_parser.h
+++ b/engines/tetraedge/te/te_text_layout_xml_parser.h
@@ -24,7 +24,7 @@
 
 #include "common/array.h"
 #include "common/str.h"
-#include "common/xmlparser.h"
+#include "common/formats/xmlparser.h"
 
 #include "tetraedge/te/te_color.h"
 
diff --git a/engines/tetraedge/te/te_vector3f32.cpp b/engines/tetraedge/te/te_vector3f32.cpp
index 6cebd5c3eae..dc2b7d4aa0d 100644
--- a/engines/tetraedge/te/te_vector3f32.cpp
+++ b/engines/tetraedge/te/te_vector3f32.cpp
@@ -51,8 +51,8 @@ TeVector3f32 operator^(const TeVector3f32 &left, const TeVector3f32 &right) {
 	float ly = left.y();
 	float lz = left.z();
 	retval.x() = ly * rz - lz * ry;
-	retval.y() = lz * rx - rz * lx;
-	retval.z() = ry * lx - ly * rx;
+	retval.y() = lz * rx - lx * rz;
+	retval.z() = lx * ry - ly * rx;
 	return retval;
 }
 
diff --git a/engines/tetraedge/te/te_vector3f32.h b/engines/tetraedge/te/te_vector3f32.h
index 951fe0157d3..eeb839f54be 100644
--- a/engines/tetraedge/te/te_vector3f32.h
+++ b/engines/tetraedge/te/te_vector3f32.h
@@ -33,7 +33,7 @@ class TeQuaternion;
 class TeVector3f32 : public Math::Vector3d {
 
 public:
-	TeVector3f32() { };
+	TeVector3f32() { }; // Note: vector3d constructor sets 0, 0, 0
 	TeVector3f32(float x_, float y_, float z_) {
 		set(x_, y_, z_);
 	}


Commit: eba2fb9b5f151743eaeca8374cb7b1a9e5a31dd4
    https://github.com/scummvm/scummvm/commit/eba2fb9b5f151743eaeca8374cb7b1a9e5a31dd4
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
MATH: Add ray triangle intersection and tests

Changed paths:
  A test/math/ray.h
  A test/math/vector3d.h
    math/ray.cpp
    math/ray.h


diff --git a/math/ray.cpp b/math/ray.cpp
index b50cd0e594c..33f3232553f 100644
--- a/math/ray.cpp
+++ b/math/ray.cpp
@@ -45,6 +45,11 @@ void Ray::rotate(const Quaternion &rot) {
 	_direction.normalize();
 }
 
+void Ray::rotateDirection(const Quaternion &rot) {
+	rot.transform(_direction);
+	_direction.normalize();
+}
+
 void Ray::translate(const Vector3d &v) {
 	_origin += v;
 }
@@ -78,4 +83,38 @@ bool Ray::intersectAABB(const AABB &aabb) const {
 	return true;
 }
 
+// Algorithm adapted from https://www.lighthouse3d.com/tutorials/maths/ray-triangle-intersection/
+bool Ray::intersectTriangle(const Vector3d &v0, const Vector3d &v1,
+		const Vector3d &v2, Vector3d &vout, float &fout) const {
+	const Vector3d e1 = v1 - v0;
+	const Vector3d e2 = v2 - v0;
+	const Vector3d h = Vector3d::crossProduct(_direction, e2);
+
+	float a = e1.dotProduct(h);
+	if (fabs(a) < 1e-6f)
+		return false;
+
+	float f = 1.0f / a;
+	const Vector3d s = _origin - v0;
+	float u = f * s.dotProduct(h);
+	if (u < 0.0f || u > 1.0f)
+		return false;
+
+	const Vector3d q = Vector3d::crossProduct(s, e1);
+	float v = f * _direction.dotProduct(q);
+
+	if (v < 0.0f || u + v > 1.0f)
+		return false;
+
+	float t = f * e2.dotProduct(q);
+
+	if (t < 1e-6f)
+		return false;
+
+	fout = t;
+	vout = _origin + t * _direction;
+
+	return true;
+}
+
 }
diff --git a/math/ray.h b/math/ray.h
index b09f8b4af18..fb6a8293db6 100644
--- a/math/ray.h
+++ b/math/ray.h
@@ -48,10 +48,15 @@ public:
 	void transform(const Matrix4 &matrix);
 
 	/**
-	 * Rotate the ray using a quaternion
+	 * Rotate the ray using a quaternion - rotates both origin and direction
 	 */
 	void rotate(const Quaternion &rot);
 
+	/**
+	 * Rotate the ray direction only using a quaternion - origin stays fixed
+	 */
+	void rotateDirection(const Quaternion &rot);
+
 	/**
 	 * Translate the ray by a vector
 	 */
@@ -62,6 +67,17 @@ public:
 	 */
 	bool intersectAABB(const AABB &aabb) const;
 
+	/**
+	 * Test and return the intersection of the ray with a triangle defned by 3 verticies
+	 *
+	 * @param v0 first triangle vertex
+	 * @param v1 second triangle vertex
+	 * @param v2 third triangle vertex
+	 * @param loc If return is true, set to the intersection point
+	 * @param dist If return is true, set to distance along the ray (relative to direction)
+	 */
+	bool intersectTriangle(const Vector3d &v0, const Vector3d &v1, const Vector3d &v2, Vector3d &loc, float &dist) const;
+
 private:
 	Vector3d _origin;
 	Vector3d _direction;
diff --git a/test/math/ray.h b/test/math/ray.h
new file mode 100644
index 00000000000..5345583d0f3
--- /dev/null
+++ b/test/math/ray.h
@@ -0,0 +1,62 @@
+#include <cxxtest/TestSuite.h>
+
+#include "math/ray.h"
+
+class RayTestSuite : public CxxTest::TestSuite {
+public:
+	// Test Constructors
+	void test_Ray() {
+		Math::Ray r;
+
+		TS_ASSERT(r.getOrigin() == Math::Vector3d());
+		TS_ASSERT(r.getDirection() == Math::Vector3d());
+
+		Math::Vector3d o(3, 2, 1);
+		Math::Vector3d d(0, 1, 2);
+
+		Math::Ray r2(o, d);
+
+		TS_ASSERT(r2.getOrigin() == o);
+		TS_ASSERT(r2.getDirection() == d);
+	}
+
+	void test_translate() {
+		Math::Vector3d o(3, 2, 1);
+		Math::Vector3d d(0, 1, 2);
+		Math::Ray r(o, d);
+
+		r.translate(Math::Vector3d(0.5, 0.2, 0.1));
+		TS_ASSERT(r.getDirection() == d);
+		Math::Vector3d o2 = r.getOrigin();
+
+		TS_ASSERT_DELTA(o2.x(), 3.5, 0.0001);
+		TS_ASSERT_DELTA(o2.y(), 2.2, 0.0001);
+		TS_ASSERT_DELTA(o2.z(), 1.1, 0.0001);
+	}
+
+	// TODO: Add tests for transform, rotate, rotateDirection, intersectAABB
+	void test_intersectTriangle() {
+		// A triangle that covers around the origin on the y plane.
+		const Math::Vector3d v1(0, 0, -20);
+		const Math::Vector3d v2(0, -10, 20);
+		const Math::Vector3d v3(0, 10, 20);
+
+		// A ray that points along the x axis, should hit the triangle at the origin
+		Math::Ray r(Math::Vector3d(-9.5, 0, 0.7), Math::Vector3d(1, 0, 0));
+
+		Math::Vector3d loc(7, 8, 9); // add values to ensure it's changed
+		float dist = 99.0f;
+		bool result = r.intersectTriangle(v1, v2, v3, loc, dist);
+		// Should hit at the origin
+		TS_ASSERT(result);
+		TS_ASSERT_DELTA(dist, 9.5f, 0.0001);
+		TS_ASSERT_DELTA(loc.x(), 0.0f, 0.0001);
+		TS_ASSERT_DELTA(loc.y(), 0.0f, 0.0001);
+		TS_ASSERT_DELTA(loc.z(), 0.7f, 0.0001);
+
+		// A ray that points along the x axis in the opposite direction, should never hit the triangle
+		Math::Ray r2(Math::Vector3d(-1, 0, 0), Math::Vector3d(-1, 0, 0));
+		result = r2.intersectTriangle(v1, v2, v3, loc, dist);
+		TS_ASSERT(!result);
+	}
+};
diff --git a/test/math/vector3d.h b/test/math/vector3d.h
new file mode 100644
index 00000000000..8534bdc9a24
--- /dev/null
+++ b/test/math/vector3d.h
@@ -0,0 +1,144 @@
+#include <cxxtest/TestSuite.h>
+
+#include "math/vector3d.h"
+
+class Vector3dTestSuite : public CxxTest::TestSuite {
+public:
+	// Test Constructors
+	void test_Vector3d() {
+		Math::Vector3d v;
+
+		TS_ASSERT(v.x() == 0.0f);
+		TS_ASSERT(v.y() == 0.0f);
+		TS_ASSERT(v.z() == 0.0f);
+
+		Math::Vector3d v2(3, 2.2, 1);
+
+		TS_ASSERT(v2.x() == 3.0f);
+		TS_ASSERT(v2.y() == 2.2f);
+		TS_ASSERT(v2.z() == 1.0f);
+
+		Math::Vector3d v3(v2);
+
+		TS_ASSERT(v3.x() == 3.0f);
+		TS_ASSERT(v3.y() == 2.2f);
+		TS_ASSERT(v3.z() == 1.0f);
+	}
+
+	void test_set() {
+		Math::Vector3d v(2, 4, 6);
+		v.set(1, 2, 3);
+
+		TS_ASSERT(v.x() == 1.0f);
+		TS_ASSERT(v.y() == 2.0f);
+		TS_ASSERT(v.z() == 3.0f);
+	}
+
+	void test_crossProduct() {
+		Math::Vector3d v1(1, 0, 0);
+		Math::Vector3d v2(0, 1, 0);
+
+		Math::Vector3d c12 = Math::Vector3d::crossProduct(v1, v2);
+		Math::Vector3d c21 = Math::Vector3d::crossProduct(v2, v1);
+
+		TS_ASSERT(c12 == Math::Vector3d(0, 0, 1));
+		TS_ASSERT(c21 == Math::Vector3d(0, 0, -1));
+	}
+
+	void test_length() {
+		Math::Vector3d v(1, 0, 1);
+		TS_ASSERT_DELTA(v.length(), sqrt(2), 0.0001);
+
+		Math::Vector3d v2(2, 2, 2);
+		TS_ASSERT_DELTA(v2.length(), sqrt(12), 0.0001);
+
+		// check static version too
+		TS_ASSERT_DELTA(Math::Vector3d::length(v2), v2.length(), 0.0001);
+	}
+
+	void test_interpolate() {
+		Math::Vector3d v1(1, 0, 2);
+		Math::Vector3d v2(0, 5, 3);
+
+		Math::Vector3d int1 = Math::Vector3d::interpolate(v1, v2, 0.1);
+		TS_ASSERT_DELTA(int1.x(), 0.9, 0.0001);
+		TS_ASSERT_DELTA(int1.y(), 0.5, 0.0001);
+		TS_ASSERT_DELTA(int1.z(), 2.1, 0.0001);
+	}
+
+	/* all the below tests are for functions in Math::Vector, but add tests
+	 * here as we can't directly instantiate that */
+
+	void test_setValue() {
+		Math::Vector3d v(3, 7, 9);
+
+		v.setValue(0, 1);
+		TS_ASSERT(v.x() == 1.0f);
+		TS_ASSERT(v.y() == 7.0f);
+		TS_ASSERT(v.z() == 9.0f);
+
+		v.setValue(1, 3);
+		v.setValue(2, 2);
+		TS_ASSERT(v.x() == 1.0f);
+		TS_ASSERT(v.y() == 3.0f);
+		TS_ASSERT(v.z() == 2.0f);
+	}
+
+	void test_getValue() {
+		Math::Vector3d v(5, 6, 7);
+
+		TS_ASSERT(v.getValue(0) == 5.0f);
+		TS_ASSERT(v.getValue(1) == 6.0f);
+		TS_ASSERT(v.getValue(2) == 7.0f);
+	}
+
+	void test_dotProduct() {
+		Math::Vector3d v1(1, 2, 3);
+		Math::Vector3d v2(6, 5, 4);
+
+		float result = v2.dotProduct(v1);
+
+		TS_ASSERT_DELTA(result, 28.0f, 0.0001f);
+	}
+
+	void test_dotProductOrthogonal() {
+		Math::Vector3d v1(6, 0, 0);
+		Math::Vector3d v2(0, 9, 0);
+
+		float result = v2.dotProduct(v1);
+
+		TS_ASSERT_EQUALS(result, 0.0f);
+	}
+
+	void test_normalizeTrivial() {
+		Math::Vector3d v(0, 1, 0);
+
+		Math::Vector3d vn = v.getNormalized();
+
+		v.normalize();
+
+		TS_ASSERT_EQUALS(vn, v);
+		TS_ASSERT(v.x() == 0);
+		TS_ASSERT(v.y() == 1);
+		TS_ASSERT(v.z() == 0);
+	}
+
+	void test_normalize() {
+		Math::Vector3d v(2, 4, 6);
+
+		Math::Vector3d vn = v.getNormalized();
+		v.normalize();
+
+		TS_ASSERT_EQUALS(vn, v);
+		TS_ASSERT_DELTA(v.x(), 2 / sqrt(56), 0.0001);
+		TS_ASSERT_DELTA(v.y(), 4 / sqrt(56), 0.0001);
+		TS_ASSERT_DELTA(v.z(), 6 / sqrt(56), 0.0001);
+	}
+
+	void test_magnitude() {
+		Math::Vector3d v(3, 2.5, 1);
+
+		TS_ASSERT_DELTA(v.getMagnitude(), sqrt(16.25), 0.0001);
+	}
+
+};


Commit: 7424452fff76a13ae3dd705b861d5833da56963b
    https://github.com/scummvm/scummvm/commit/7424452fff76a13ae3dd705b861d5833da56963b
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More fixes, can now complete first act.

Changed paths:
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_camera.h
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_ray_intersection.cpp
    engines/tetraedge/te/te_ray_intersection.h


diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 1c8444032db..f203d9ba2bc 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -1068,7 +1068,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 	
 	// Note: charAnim above may no longer be valid as anim may have changed.
 	if (_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._idleAnimFileName)) {
-		_lastCharMoveMousePos = TeVector2s32(0, 0);
+		_lastCharMoveMousePos = TeVector2s32();
 		_movePlayerCharacterDisabled = false;
 		_isCharacterIdle = true;
 		_isCharacterWalking = false;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 48ffd9592b1..cd47e7c6040 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -42,7 +42,7 @@
 #include "tetraedge/te/te_lua_script.h"
 #include "tetraedge/te/te_lua_thread.h"
 
-#define DEBUG_PATHFINDING 1
+//#define DEBUG_PATHFINDING 1
 
 namespace Tetraedge {
 
@@ -186,6 +186,13 @@ Character *InGameScene::character(const Common::String &name) {
 			return c;
 	}
 
+	// WORKAROUND: Didn't find char, try again with case insensitive
+	// for "OScar" typo in scenes/ValTrain/19000.
+	for (Character *c : _characters) {
+		if (c->_model->name().compareToIgnoreCase(name) == 0)
+			return c;
+	}
+
 	return nullptr;
 }
 
@@ -327,6 +334,7 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 		col.deserialize(stream);
 		mesh.setColor(i, col);
 	}
+
 	pickmesh->setNbTriangles(indexcount / 3);
 	for (unsigned int i = 0; i < indexcount; i++) {
 		vec = mesh.vertex(mesh.index(i));
@@ -592,7 +600,7 @@ bool InGameScene::load(const Common::Path &path) {
 		if (modelname.contains("Clic")) {
 			//debug("Loaded clickMesh %s", modelname.c_str());
 			_hitObjects.push_back(model);
-			model->setVisible(true);
+			model->setVisible(false);
 			model->setColor(TeColor(0, 0xff, 0, 0xff));
 			models().push_back(model);
 			pickmesh->setName(modelname);
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index a0e694d6993..a70d7738648 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -495,7 +495,7 @@ bool Inventory::updateLayout() {
 	return false;
 }
 
-#define DEBUG_SAVELOAD 1
+//#define DEBUG_SAVELOAD 1
 
 Common::Error Inventory::syncState(Common::Serializer &s) {
 	unsigned int nitems = _invObjects.size();
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index b49cea8f36a..c01a0b4d757 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -40,8 +40,9 @@ using namespace ToLua;
 static void LoadObjectMaterials(const Common::String &objname) {
 	Game *game = g_engine->getGame();
 	bool result = game->scene().loadObjectMaterials(objname);
+	// Not an error if it fails, eg ValAttic/16050 calls this but has no object
 	if (!result)
-		error("[LoadObjectMaterials] Object \"%s\" doesn't exist or no Object in this scene.",
+		warning("[LoadObjectMaterials] Object \"%s\" doesn't exist or no Object in this scene.",
 				objname.c_str());
 }
 
@@ -795,7 +796,9 @@ static int tolua_ExportedFunctions_SetCharacterAnimation00(lua_State *L) {
 		SetCharacterAnimation(s1, s2, b1, b2, (int)f3, (int)f4);
 		return 0;
 	}
-	error("#ferror in function 'SetCharacterAnimation': %d %d %s", err.index, err.array, err.type);
+	// Incorrectly called in scenes/ValTrain/19000 line 305 with a 0.5 parameter instead of bool
+	warning("#ferror in function 'SetCharacterAnimation': %d %d %s", err.index, err.array, err.type);
+	return 0;
 }
 
 static int tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00(lua_State *L) {
@@ -1363,7 +1366,10 @@ static int tolua_ExportedFunctions_DeleteTask00(lua_State *L) {
 		DeleteTask(s1, s2);
 		return 0;
 	}
-	error("#ferror in function 'DeleteTask': %d %d %s", err.index, err.array, err.type);
+	// Note: There is an incorrect call to this in scenes/ValChurch/13120/Logic13120.lua
+	// which only passes a single string.
+	warning("#ferror in function 'DeleteTask': %d %d %s", err.index, err.array, err.type);
+	return 0;
 }
 
 static void SetVisibleButtonHelp(bool val) {
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index ed8c8217d4d..eee6d0af931 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/math.h"
+#include "math/ray.h"
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/te/te_camera.h"
@@ -136,25 +137,29 @@ void TeCamera::draw() {
 	error("TODO: Implement TeCamera::draw");
 }
 
-void TeCamera::getRay(const TeVector2s32 &pxloc, TeVector3f32 &raypos, TeVector3f32 &raydir) {
-	raypos = position();
+Math::Ray TeCamera::getRay(const TeVector2s32 &pxloc) {
+	const Math::Vector3d origin = position();
 
-	float xval = (pxloc._x - _viewportX) / fabs(_viewportW);
-	raydir.x() = xval * 2 - 1;
-	float yval = (pxloc._y - _viewportY) / fabs(_viewportH);
-	raydir.y() = yval * 2 - 1;
-	raydir.z() = 1.0;
+	// point offset relative to viewport
+	const float xval = (pxloc._x - _viewportX) / fabs(_viewportW);
+	const float yval = (_viewportH - (pxloc._y - _viewportY)) / fabs(_viewportH);
+
+	// normalized to -1..1
+	const TeVector3f32 projectedPoint(xval * 2.0f - 1.0f, yval * 2.0f - 1.0f, 1.0f);
 
 	TeMatrix4x4 projInverse = _projectionMatrix;
-	projInverse.inverse();
-	raydir = projInverse * raydir;
-	raydir.normalize();
+	bool inverted = projInverse.inverse();
+	if (!inverted)
+		error("failed to invert camera projection");
+	TeVector3f32 unprojectedPoint = projInverse * projectedPoint;
+	unprojectedPoint.normalize();
 
 	TeQuaternion rot = _rotation;
 	rot.normalize();
-	const TeMatrix4x4 rotmatrix = rot.toTeMatrix();
-
-	raydir = rotmatrix * raydir;
+	TeMatrix4x4 rotMatrix = rot.toTeMatrix();
+	TeVector3f32 rayDir = rotMatrix * unprojectedPoint;
+	Math::Ray ray(origin, rayDir);
+	return ray;
 }
 
 void TeCamera::loadBin(const Common::String &path) {
diff --git a/engines/tetraedge/te/te_camera.h b/engines/tetraedge/te/te_camera.h
index 8ab03c86b15..43d8e4ae854 100644
--- a/engines/tetraedge/te/te_camera.h
+++ b/engines/tetraedge/te/te_camera.h
@@ -23,6 +23,8 @@
 #define TETRAEDGE_TE_TE_CAMERA_H
 
 #include "common/str.h"
+#include "math/ray.h"
+
 #include "tetraedge/te/te_3d_object2.h"
 #include "tetraedge/te/te_matrix4x4.h"
 #include "tetraedge/te/te_references_counter.h"
@@ -45,7 +47,7 @@ public:
 	void buildPerspectiveMatrix3();
 	void draw() override;
 
-	void getRay(const TeVector2s32 &param_1, TeVector3f32 &out1, TeVector3f32 &out2);
+	Math::Ray getRay(const TeVector2s32 &pxloc);
 
 	void loadBin(const Common::String &path);
 	void loadBin(const Common::ReadStream &stream);
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index 59cccba30b2..efb5096e32e 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -154,11 +154,8 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 				testPath.joinInPlace(langs[langtype]);
 			}
 			testPath.joinInPlace(fname);
-			if (Common::File::exists(testPath) || Common::FSNode(path).exists()) {
+			if (Common::File::exists(testPath) || Common::FSNode(path).exists())
 				return testPath;
-			} else {
-				debug("not found %s", testPath.toString().c_str());
-			}
 		}
 	}
 
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index daf300a7756..81e769b2680 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -808,34 +808,40 @@ TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, c
 	TeVector3f32 closestLoc;
 	TePickMesh2 *nearestMesh = nullptr;
 	float closestDist = camera->_orthFarVal;
-	TeVector3f32 rayLoc;
-	TeVector3f32 rayDir;
+	Math::Ray camRay;
 	for (unsigned int i = 0; i < pickMeshes.size(); i++) {
 		TePickMesh2 *mesh = pickMeshes[i];
 		const TeMatrix4x4 meshWorldTransform = mesh->worldTransformationMatrix();
 		if (lastHitFirst) {
+			// Note: it seems like a bug in the original.. this never sets
+			// the ray parameters?? It should still find the right triangle below.
 			unsigned int tricount = mesh->verticies().size() / 3;
 			unsigned int vert = mesh->lastTriangleHit() * 3;
 			if (mesh->lastTriangleHit() >= tricount)
 				vert = 0;
-			const TeVector3f32 v1 = meshWorldTransform * mesh->verticies()[vert];
+			const TeVector3f32 v1 = meshWorldTransform * mesh->verticies()[vert + 0];
 			const TeVector3f32 v2 = meshWorldTransform * mesh->verticies()[vert + 1];
 			const TeVector3f32 v3 = meshWorldTransform * mesh->verticies()[vert + 2];
 			TeVector3f32 intersectLoc;
 			float intersectDist;
-			int intResult = TeRayIntersection::intersect(rayLoc, rayDir, v1, v2, v3, intersectLoc, intersectDist);
-			if (intResult == 1 && intersectDist < closestDist && intersectDist >= camera->_orthNearVal)
+			bool intResult = camRay.intersectTriangle(v1, v2, v3, intersectLoc, intersectDist);
+			if (intResult && intersectDist < closestDist && intersectDist >= camera->_orthNearVal)
 				return mesh;
 		}
 		for (unsigned int tri = 0; tri < mesh->verticies().size() / 3; tri++) {
-			const TeVector3f32 v1 = meshWorldTransform * mesh->verticies()[tri * 3];
+			const TeVector3f32 v1 = meshWorldTransform * mesh->verticies()[tri * 3 + 0];
 			const TeVector3f32 v2 = meshWorldTransform * mesh->verticies()[tri * 3 + 1];
 			const TeVector3f32 v3 = meshWorldTransform * mesh->verticies()[tri * 3 + 2];
-			camera->getRay(fromPt, rayLoc, rayDir);
+			camRay = camera->getRay(fromPt);
 			TeVector3f32 intersectLoc;
 			float intersectDist;
-			int intResult = TeRayIntersection::intersect(rayLoc, rayDir, v1, v2, v3, intersectLoc, intersectDist);
-			if (intResult == 1 && intersectDist < closestDist && intersectDist >= camera->_orthNearVal) {
+			bool intResult = camRay.intersectTriangle(v1, v2, v3, intersectLoc, intersectDist);
+			/*debug("PickMesh2 %s intersect Ray(%s, %s) Triangle(%s, %s, %s) -> %s", mesh->name().c_str(),
+					TeVector3f32(camRay.getOrigin()).dump().c_str(),
+					TeVector3f32(camRay.getDirection()).dump().c_str(),
+					v1.dump().c_str(), v2.dump().c_str(), v3.dump().c_str(),
+					intResult ? "hit!" : "no hit");*/
+			if (intResult && intersectDist < closestDist && intersectDist >= camera->_orthNearVal) {
 				mesh->setLastTriangleHit(tri);
 				closestLoc = intersectLoc;
 				closestDist = intersectDist;
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index 5a1888be26b..560ec007bd9 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -144,15 +144,13 @@ TeVector3f32 TeModelAnimation::getNMOTranslation(unsigned long boneNo, float amo
 
 //TeTRS TeModelAnimation::getTRS(const Common::String &boneName, unsigned long frame, bool param_5);
 
-TeTRS TeModelAnimation::getTRS(unsigned long boneNo, unsigned long frame, bool param_5) const {
+TeTRS TeModelAnimation::getTRS(unsigned long boneNo, unsigned long frame, bool forceUseFbx) const {
 	TeTRS retval;
 
-	if (!_useNMOArrays || param_5) {
+	if (!_useNMOArrays || forceUseFbx) {
 		unsigned int nframes = 0;
 		if (!_useNMOArrays) {
-			if (_fbxArrays.size()) {
-				nframes = _fbxArrays[0].size();
-			}
+			nframes = _fbxArrays[0].size();
 		} else {
 			nframes = _numNMOFrames;
 		}
@@ -305,7 +303,10 @@ void TeModelAnimation::setBoneName(uint boneNo, const Common::String &bname) {
 
 void TeModelAnimation::setRotation(unsigned long num, float amount, const TeQuaternion &rot) {
 	if (!_useNMOArrays) {
-		_fbxArrays[num][(int)amount].setRotation(rot);
+		uint frame = amount;
+		if (_fbxArrays[num].size() <= frame)
+			_fbxArrays[num].resize(frame + 1);
+		_fbxArrays[num][frame].setRotation(rot);
 	} else {
 		NMORotation nmorot;
 		nmorot._rot = rot;
@@ -321,7 +322,10 @@ void TeModelAnimation::setScale(unsigned long num, float amount, const TeVector3
 
 void TeModelAnimation::setTranslation(unsigned long num, float amount, const TeVector3f32 &trans) {
 	if (!_useNMOArrays) {
-		_fbxArrays[num][(int)amount].setTranslation(trans);
+		uint frame = amount;
+		if (_fbxArrays[num].size() <= frame)
+			_fbxArrays[num].resize(frame + 1);
+		_fbxArrays[num][frame].setTranslation(trans);
 	} else {
 		NMOTranslation nmotrans;
 		nmotrans._trans = trans;
@@ -336,7 +340,7 @@ void TeModelAnimation::unbind() {
 
 void TeModelAnimation::update(double proportion) {
 	int frames;
-	if (_useNMOArrays == 0) {
+	if (!_useNMOArrays) {
 		if (_fbxArrays.empty())
 			return;
 
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 6380d6d1acd..7e4f160e4b3 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -61,43 +61,44 @@ void TePickMesh2::draw() {
 	renderer->disableWireFrame();
 }
 
-bool TePickMesh2::intersect(const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &vout, float &fout, bool lastHitFirst, unsigned long *triangleHitOut) {
+bool TePickMesh2::intersect(const TeVector3f32 &origin, const TeVector3f32 &dir, TeVector3f32 &hitPtOut, float &hitDistOut, bool lastHitFirst, unsigned long *triangleHitOut) {
 	if (_verticies.size() / 3 == 0)
 		return false;
 
-	TeVector3f32 intersection;
-	float f;
+	TeVector3f32 hitPt;
+	float hitDist;
 	const TeMatrix4x4 worldTrans = worldTransformationMatrix();
+	const Math::Ray ray(origin, dir);
 	if (lastHitFirst) {
 		const TeVector3f32 triv1 = worldTrans * _verticies[_lastTriangleHit * 3 + 0];
 		const TeVector3f32 triv2 = worldTrans * _verticies[_lastTriangleHit * 3 + 1];
 		const TeVector3f32 triv3 = worldTrans * _verticies[_lastTriangleHit * 3 + 2];
-		int result = TeRayIntersection::intersect(v1, v2, triv1, triv2, triv3, intersection, f);
-		if (result == 1 && f >= 0.0 && f < FLT_MAX) {
-			vout = v1 + v2 * f;
-			fout = f;
+		bool result = ray.intersectTriangle(triv1, triv2, triv3, hitPt, hitDist);
+		if (result && hitDist >= 0.0 && hitDist < FLT_MAX) {
+			hitPtOut = origin + dir * hitDist;
+			hitDistOut = hitDist;
 			if (triangleHitOut)
 				*triangleHitOut = _lastTriangleHit;
 			return true;
 		}
 	}
 
-	float hitf = FLT_MAX;
+	float lastHitDist = FLT_MAX;
 	for (unsigned int i = 0; i < _verticies.size() / 3; i++) {
 		const TeVector3f32 triv1 = worldTrans * _verticies[i * 3 + 0];
 		const TeVector3f32 triv2 = worldTrans * _verticies[i * 3 + 1];
 		const TeVector3f32 triv3 = worldTrans * _verticies[i * 3 + 2];
-		int result = TeRayIntersection::intersect(v1, v2, triv1, triv2, triv3, intersection, f);
-		if (result == 1 && f >= 0.0 && f < FLT_MAX) {
+		bool result = ray.intersectTriangle(triv1, triv2, triv3, hitPt, hitDist);
+		if (result && hitDist >= 0.0 && hitDist < FLT_MAX) {
 			_lastTriangleHit = i;
-			hitf = f;
+			lastHitDist = hitDist;
 			if (lastHitFirst)
 				break;
 		}
 	}
-	if (hitf != FLT_MAX) {
-		vout = v1 + v2 * hitf;
-		fout = hitf;
+	if (lastHitDist != FLT_MAX) {
+		hitPtOut = origin + dir * lastHitDist;
+		hitDistOut = lastHitDist;
 		if (triangleHitOut)
 			*triangleHitOut = _lastTriangleHit;
 		return true;
diff --git a/engines/tetraedge/te/te_ray_intersection.cpp b/engines/tetraedge/te/te_ray_intersection.cpp
index 53eab70700d..9526563d69a 100644
--- a/engines/tetraedge/te/te_ray_intersection.cpp
+++ b/engines/tetraedge/te/te_ray_intersection.cpp
@@ -30,42 +30,43 @@ TePickMesh *getMesh(const TeVector3f32 &param_1, const TeVector3f32 &param_2, co
 	error("TODO: implement TeRayIntersection::getMesh");
 }
 
+/*
 // This is a version from https://www.lighthouse3d.com/tutorials/maths/ray-triangle-intersection/
 int intersect(const TeVector3f32 &p, const TeVector3f32 &d, const TeVector3f32 &v0,
-			  const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &vout, float &fout) {
+			  const TeVector3f32 &v1, const TeVector3f32 &v2, TeVector3f32 &hitPt, float &hitDist) {
 	const TeVector3f32 e1 = v1 - v0;
 	const TeVector3f32 e2 = v2 - v0;
-	const TeVector3f32 h = d ^ e2;
+	const TeVector3f32 h = TeVector3f32::crossProduct(d, e2);
 
 	if (h == TeVector3f32())
 		return -1;
 
 	float a = e1.dotProduct(h);
-	if (fabs(a) < 1e-6)
+	if (fabs(a) < 1e-6f)
 		return 0;
 	
-	float f = 1 / a;
-	TeVector3f32 s = p - v0;
+	float f = 1.0f / a;
+	const TeVector3f32 s = p - v0;
 	float u = f * s.dotProduct(h);
-	if (u < 0.0 || u > 1.0)
+	if (u < 0.0f || u > 1.0f)
 		return 0;
 
-	TeVector3f32 q = TeVector3f32::crossProduct(s, e1);
+	const TeVector3f32 q = TeVector3f32::crossProduct(s, e1);
 	float v = f * d.dotProduct(q);
 
-	if (v < 0.0 || u + v > 1.0)
+	if (v < 0.0f || u + v > 1.0f)
 		return 0;
 		
 	float t = f * e2.dotProduct(q);
 	
-	if (t < 0.00001)
+	if (t < 1e-6f)
 		return 0;
 	
-	fout = t;
-	vout = p + t * d;
+	hitDist = t;
+	hitPt = p + t * d;
 	
 	return 1;
-}
+}*/
 
 /*
 int intersect(const TeVector3f32 &rayPos, const TeVector3f32 &rayDir, const TeVector3f32 &v1,
diff --git a/engines/tetraedge/te/te_ray_intersection.h b/engines/tetraedge/te/te_ray_intersection.h
index f628632ff52..5b9a65f13e4 100644
--- a/engines/tetraedge/te/te_ray_intersection.h
+++ b/engines/tetraedge/te/te_ray_intersection.h
@@ -34,8 +34,9 @@ namespace TeRayIntersection {
 TePickMesh *getMesh(const TeVector3f32 &param_1, const TeVector3f32 &param_2, const Common::Array<TePickMesh *> &pickMeshes,
 			float param_4, float param_5, TeVector3f32 *param_6);
 
-int intersect(const TeVector3f32 &rayPos, const TeVector3f32 &rayDir, const TeVector3f32 &v1,
-              const TeVector3f32 &v2, const TeVector3f32 &v3, TeVector3f32 &vout, float &fout);
+// Replaced with Math::Ray::intersectTriangle
+//int intersect(const TeVector3f32 &rayPos, const TeVector3f32 &rayDir, const TeVector3f32 &v1,
+//              const TeVector3f32 &v2, const TeVector3f32 &v3, TeVector3f32 &vout, float &fout);
 
 } // end namespace TeRayIntersection
 


Commit: 0ba854df9ab95b2b7fc7548e9b59986e7fc969a0
    https://github.com/scummvm/scummvm/commit/0ba854df9ab95b2b7fc7548e9b59986e7fc969a0
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: More fixes, can now complete game.

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/characters_shadow.cpp
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/question2.cpp
    engines/tetraedge/game/question2.h
    engines/tetraedge/game/scene_lights_xml_parser.cpp
    engines/tetraedge/te/te_animation.cpp
    engines/tetraedge/te/te_animation.h
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_curve_anim2.h
    engines/tetraedge/te/te_frame_anim.cpp
    engines/tetraedge/te/te_frame_anim.h
    engines/tetraedge/te/te_light.cpp
    engines/tetraedge/te/te_light.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_model_animation.h
    engines/tetraedge/te/te_model_vertex_animation.h
    engines/tetraedge/te/te_ray_intersection.cpp
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_signal.h


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 8620831b3b4..3076eeb6529 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -112,6 +112,14 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 	// the way this gets used later, setting large negative is more correct.
 	c->_callsMade = (maxCalls == -1.0 ? -1e9 : 0.0f);
 
+	//
+	// WORKAROUND: This callback seems to be set too late.. frame 31, but it
+	// only gets to 15?  Some bug in the way anim blends hand off?
+	// for scenes/CitSpace2/34230/Logic34230.lua
+	//
+	if (fnName == "ChangeClef" && c->_triggerFrame == 31)
+		c->_triggerFrame = 15;
+
 	const Common::Path animPath = _model->anim()->_loadedPath;
 
 	// Another difference.. the original messes with paths a bit - just
@@ -126,6 +134,7 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 		Common::Path animKeyPath(animKey);
 		Common::Array<Callback *> callbacks;
 		callbacks.push_back(c);
+
 		_callbacks.setVal(animKeyPath.getLastComponent().toString(), callbacks);
 	}
 }
@@ -142,10 +151,13 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 	//_animCache.pop_back();
 }
 
-/*static*/ TeIntrusivePtr<TeModelAnimation> Character::animCacheLoad(const Common::Path &path) {
+/*static*/
+TeIntrusivePtr<TeModelAnimation> Character::animCacheLoad(const Common::Path &path) {
 	const Common::String pathStr = path.toString();
-	if (_animCacheMap.contains(pathStr))
-		return _animCacheMap.getVal(pathStr);
+	if (_animCacheMap.contains(pathStr)) {
+		// Copy from the cache (keep the cached instance clean)
+		return new TeModelAnimation(*_animCacheMap.getVal(pathStr));
+	}
 
 	TeIntrusivePtr<TeModelAnimation> modelAnim = new TeModelAnimation();
 	if (!modelAnim->load(path)) {
@@ -531,6 +543,10 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 }
 
 bool Character::onModelAnimationFinished() {
+	// this shouldn't happen but check to be sure..
+	if (!_model->anim())
+		return false;
+
 	const Common::Path loadedPath = _model->anim()->_loadedPath;
 	const Common::String animfile = loadedPath.getLastComponent().toString();
 
@@ -613,6 +629,8 @@ void Character::permanentUpdate() {
 	if (_callbacks.contains(animFile)) {
 		Common::Array<Callback *> &cbs = _callbacks.getVal(animFile);
 		for (Callback *cb : cbs) {
+			//debug("check cb for %s frame %d against %d. speed %.02f", animFile.c_str(),
+			//		curFrame, cb->_triggerFrame, _model->anim()->speed());
 			if (cb->_triggerFrame > cb->_lastCheckFrame && curFrame >= cb->_triggerFrame){
 				int callsMade = cb->_callsMade;
 				cb->_callsMade++;
@@ -934,8 +952,9 @@ void Character::walkTo(float curveEnd, bool walkFlag) {
 	_walkCurveLast = _walkCurveStart;
 	_walkCurveNextLength = 0.0f;
 	_walkedLength = 0.0f;
-	assert(_curve);
-	if (_curve->controlPoints().size()) {
+	if (!_curve)
+		warning("_curve not set in Character::walkTo");
+	if (_curve && _curve->controlPoints().size()) {
 		const float walkEndLen = (walkFlag ? 0 : _walkEndGAnimLen);
 		_walkCurveLen = _curve->length();
 		_walkEndAnimG = false;
diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
index 19025678183..5bde6f4dc9c 100644
--- a/engines/tetraedge/game/characters_shadow.cpp
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -64,7 +64,7 @@ void CharactersShadow::createTexture(InGameScene *scene) {
 	if (light) {
 		TeQuaternion q1 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 1, 0), light->positionRadial().getX() - M_PI_2);
 		TeQuaternion q2 = TeQuaternion::fromAxisAndAngle(TeVector3f32(1, 0, 0), light->positionRadial().getY());
-		_camera->rotate(q2 * q1);
+		_camera->setRotation(q2 * q1);
 		_camera->setPosition(light->position3d());
 	}
 	_camera->_fov = scene->shadowFov() * M_PI / 180.0;
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 83cb02ef0e4..1df74bf3ead 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -107,7 +107,10 @@ void Dialog2::load() {
 	size(); // refresh size? seems to do nothing with result again.
 
 	TeButtonLayout *dialogBtn = _gui.buttonLayoutChecked("dialog");
-	dialogBtn->onMouseClickValidated().add(this, &Dialog2::onSkipButton);
+
+	// WORKAROUND: Ensure this is always above the Game (which is set to pri 10000)
+	Common::SharedPtr<TeCallback0Param<Dialog2>> callbackptr(new TeCallback0Param<Dialog2>(this, &Dialog2::onSkipButton, 20000.0f));
+	dialogBtn->onMouseClickValidated().push_back(callbackptr);
 
 	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
 	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index f203d9ba2bc..e50ba5db78d 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -475,11 +475,13 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 	scenePath.joinInPlace(scene);
 	_sceneZonePath = scenePath;
 
-	const Common::Path intLuaPath = scenePath.join(Common::String::format("Int%s.lua", scene.c_str()));
-	const Common::Path logicLuaPath = scenePath.join(Common::String::format("Logic%s.lua", scene.c_str()));
-	const Common::Path setLuaPath = scenePath.join(Common::String::format("Set%s.lua", scene.c_str()));
-	Common::Path forLuaPath = scenePath.join(Common::String::format("For%s.lua", scene.c_str()));
-	const Common::Path markerLuaPath = scenePath.join(Common::String::format("Marker%s.lua", scene.c_str()));
+	TeCore *core = g_engine->getCore();
+
+	const Common::Path intLuaPath = core->findFile(scenePath.join(Common::String::format("Int%s.lua", scene.c_str())));
+	const Common::Path logicLuaPath = core->findFile(scenePath.join(Common::String::format("Logic%s.lua", scene.c_str())));
+	const Common::Path setLuaPath = core->findFile(scenePath.join(Common::String::format("Set%s.lua", scene.c_str())));
+	Common::Path forLuaPath = core->findFile(scenePath.join(Common::String::format("For%s.lua", scene.c_str())));
+	const Common::Path markerLuaPath = core->findFile(scenePath.join(Common::String::format("Marker%s.lua", scene.c_str())));
 
 	bool intLuaExists = Common::File::exists(intLuaPath);
 	bool logicLuaExists = Common::File::exists(logicLuaPath);
@@ -650,13 +652,16 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		i--;
 	}
 
-	for (auto &randsoundlist : _randomSounds) {
+	// Take a copy and clear the global list first, as deleting a sound can cause the
+	// next sound to trigger (which might have been deleted already).
+	Common::HashMap<Common::String, Common::Array<RandomSound *>> rsounds = _randomSounds;
+	_randomSounds.clear();
+	for (auto &randsoundlist : rsounds) {
 		for (auto *randsound : randsoundlist._value) {
 			delete randsound;
 		}
 		randsoundlist._value.clear();
 	}
-	_randomSounds.clear();
 
 	_scene.initScroll();
 	return true;
@@ -1065,7 +1070,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 			_posPlayer = lastPoint;
 		}
 	}
-	
+
 	// Note: charAnim above may no longer be valid as anim may have changed.
 	if (_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._idleAnimFileName)) {
 		_lastCharMoveMousePos = TeVector2s32();
@@ -1531,8 +1536,9 @@ void Game::update() {
 				app->_lockCursorButton.setVisible(false);
 		}
 
-		TeButtonLayout *invbtn = _inGameGui.buttonLayoutChecked("inventoryButton");
-		invbtn->setVisible(!app->isLockCursor() && !_dialog2.isDialogPlaying());
+		TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
+		if (invbtn)
+			invbtn->setVisible(!app->isLockCursor() && !_dialog2.isDialogPlaying());
 
 		Character *player = _scene._character;
 		if (player) {
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index cd47e7c6040..8ee2628edff 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -43,6 +43,7 @@
 #include "tetraedge/te/te_lua_thread.h"
 
 //#define DEBUG_PATHFINDING 1
+//#define DEBUG_LIGHTS 1
 
 namespace Tetraedge {
 
@@ -178,7 +179,7 @@ bool InGameScene::changeBackground(const Common::String &name) {
 }
 
 Character *InGameScene::character(const Common::String &name) {
-	if (_character->_model->name() == name)
+	if (_character && _character->_model->name() == name)
 		return _character;
 
 	for (Character *c : _characters) {
@@ -296,7 +297,7 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 	TeQuaternion rot;
 	TeColor col;
 	TeMesh mesh;
-	
+
 	assert(pickmesh);
 
 	TeVector3f32::deserialize(stream, vec);
@@ -361,7 +362,7 @@ void InGameScene::draw() {
 		zone->setVisible(true);
 		zone->draw();
 	}
-	
+
 	for (TePickMesh2 *mesh : _clickMeshes) {
 		mesh->setVisible(true);
 		mesh->draw();
@@ -430,6 +431,7 @@ InGameScene::SoundStep InGameScene::findSoundStep(const Common::String &name) {
 
 void InGameScene::freeGeometry() {
 	_loadedPath.set("");
+
 	for (TeFreeMoveZone *zone : _freeMoveZones)
 		delete zone;
 	_freeMoveZones.clear();
@@ -707,6 +709,16 @@ bool InGameScene::loadLights(const Common::Path &path) {
 		_lights[i].enable(i);
 	}
 
+#if DEBUG_LIGHTS
+	debug("--- Scene lights ---");
+	debug("Shadow: %s no:%d far:%.02f near:%.02f fov:%.02f", _shadowColor.dump().c_str(), _shadowLightNo, _shadowFarPlane, _shadowNearPlane, _shadowFov);
+	debug("Global: %s", TeLight::globalAmbient().dump().c_str());
+	for (unsigned int i = 0; i < _lights.size(); i++) {
+		debug("%s", _lights[i].dump().c_str());
+	}
+	debug("---  end lights  ---");
+#endif
+
 	return true;
 }
 
@@ -1015,7 +1027,17 @@ void InGameScene::setVisibleMarker(const Common::String &markerName, bool val) {
 	if (!isMarker(markerName))
 		return;
 
-	error("TODO: Implement InGameScene::setVisibleMarker");
+	Game *game = g_engine->getGame();
+	TeLayout *bg = game->forGui().layout("background");
+	if (!bg)
+		return;
+
+	for (Te3DObject2 *child : bg->childList()) {
+		if (child->name() == markerName) {
+			child->setVisible(val);
+			break;
+		}
+	}
 }
 
 void InGameScene::unloadCharacter(const Common::String &name) {
@@ -1023,6 +1045,9 @@ void InGameScene::unloadCharacter(const Common::String &name) {
 		_character->removeAnim();
 		_character->deleteAnim();
 		_character->deleteAllCallback();
+		if (_character->_model->anim())
+		_character->_model->anim()->stop(); // TODO: added this
+		_character->setFreeMoveZone(nullptr); // TODO: added this
 		// TODO: deleteLater() something here..
 		_character = nullptr;
 	}
@@ -1033,6 +1058,9 @@ void InGameScene::unloadCharacter(const Common::String &name) {
 			c->deleteAnim();
 			c->deleteAllCallback();
 			// TODO: deleteLater() something here..
+			if (c->_model->anim())
+				c->_model->anim()->stop(); // TODO: added this
+			c->setFreeMoveZone(nullptr); // TODO: added this
 			_characters.remove_at(i);
 			break;
 		}
@@ -1145,7 +1173,9 @@ void InGameScene::update() {
 	for (Object3D *obj : _object3Ds) {
 		// TODO: update object3ds if they are translating or rotating.
 		if (obj->_translateTime >= 0) {
-			error("TODO: handle _translateTime > 0 in InGameScene::update");
+			float time = MIN((float)(obj->_translateTimer.getTimeFromStart() / 1000000.0), obj->_translateTime);
+			TeVector3f32 trans = obj->_translateStart + (obj->_translateAmount * (time / obj->_translateTime));
+			obj->model()->setPosition(trans);
 		}
 		if (obj->_rotateTime >= 0) {
 			// Never actually used in the game.
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index c01a0b4d757..b06f6c3e2fe 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -107,7 +107,7 @@ static int tolua_ExportedFunctions_PlayMovieAndWaitForEnd00(lua_State *L) {
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1)
-				error("PlayMovieAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+				warning("PlayMovieAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
@@ -551,7 +551,7 @@ int tolua_ExportedFunctions_StartAnimationAndWaitForEnd00(lua_State *L) {
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1)
-				error("StartAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+				warning("StartAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
@@ -823,7 +823,7 @@ static int tolua_ExportedFunctions_SetCharacterAnimationAndWaitForEnd00(lua_Stat
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1 && cb._luaParam2 == s2)
-				error("SetCharacterAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+				warning("SetCharacterAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
@@ -877,7 +877,7 @@ static int tolua_ExportedFunctions_BlendCharacterAnimationAndWaitForEnd00(lua_St
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1 && cb._luaParam2 == s2)
-				error("BlendCharacterAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+				warning("BlendCharacterAnimationAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
@@ -1199,7 +1199,7 @@ static int tolua_ExportedFunctions_WaitAndWaitForEnd00(lua_State *L) {
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName)
-				error("WaitAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+				warning("WaitAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
@@ -1294,7 +1294,7 @@ static int tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00(lua_State *L) {
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName && cb._luaParam == callback._luaParam)
-				error("LaunchDialogAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+				warning("LaunchDialogAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
@@ -1513,8 +1513,10 @@ static int tolua_ExportedFunctions_ActivateAnchorZone00(lua_State *L) {
 static void SetCharacterLookChar(const Common::String &charname, const Common::String &destname, bool tall) {
 	Game *game = g_engine->getGame();
 	Character *character = game->scene().character(charname);
-	if (!character)
-		error("[SetCharacterLookChar] Character \"%s\" doesn't exist", charname.c_str());
+	if (!character) {
+		warning("[SetCharacterLookChar] Character \"%s\" doesn't exist", charname.c_str());
+		return;
+	}
 	character->setLookingAtTallThing(tall);
 	if (destname.empty()) {
 		character->setCharLookingAt(nullptr);
@@ -1567,14 +1569,14 @@ static void SetCharacterMeshVisible(const Common::String &charName, const Common
 
 static int tolua_ExportedFunctions_SetCharacterMeshVisible00(lua_State *L) {
 	tolua_Error err;
-	if (tolua_isstring(L, 1, 0, &err) && tolua_isboolean(L, 2, 1, &err) && tolua_isnoobj(L, 3, &err)) {
+	if (tolua_isstring(L, 1, 0, &err) && tolua_isstring(L, 2, 0, &err) && tolua_isboolean(L, 3, 1, &err) && tolua_isnoobj(L, 4, &err)) {
 		Common::String s1(tolua_tostring(L, 1, nullptr));
 		Common::String s2(tolua_tostring(L, 2, nullptr));
 		bool b = tolua_toboolean(L, 3, 0);
 		SetCharacterMeshVisible(s1, s2, b);
 		return 0;
 	}
-	error("#ferror in function 'SetRecallageY': %d %d %s", err.index, err.array, err.type);
+	error("#ferror in function 'SetCharacterMeshVisible': %d %d %s", err.index, err.array, err.type);
 }
 
 static void SetRecallageY(const Common::String &charName, bool val) {
@@ -1697,7 +1699,7 @@ static int tolua_ExportedFunctions_PlaySoundAndWaitForEnd00(lua_State *L) {
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName && cb._luaParam == s1)
-				error("PlaySoundAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+				warning("PlaySoundAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
@@ -1972,7 +1974,7 @@ static int tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00(lua_State *L)
 		Game *game = g_engine->getGame();
 		for (const auto &cb : game->yieldedCallbacks()) {
 			if (cb._luaFnName == callback._luaFnName)
-				error("MoveCharacterToAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
+				warning("MoveCharacterToAndWaitForEnd: Reentrency error, your are already in a yielded/sync function call");
 		}
 		game->yieldedCallbacks().push_back(callback);
 		return callback._luaThread->yield();
diff --git a/engines/tetraedge/game/question2.cpp b/engines/tetraedge/game/question2.cpp
index 469578fbb5a..09ce43b64ad 100644
--- a/engines/tetraedge/game/question2.cpp
+++ b/engines/tetraedge/game/question2.cpp
@@ -78,6 +78,10 @@ void Question2::load() {
 	if (backgroundButton) {
 		addChild(backgroundButton);
 		backgroundButton->setVisible(false);
+
+		// WORKAROUND: Block clicks going to the Game (which is set to pri 10000)
+		Common::SharedPtr<TeCallback0Param<Question2>> callbackptr(new TeCallback0Param<Question2>(this, &Question2::onBackgroundClick, 20000.0f));
+		backgroundButton->onMouseClickValidated().push_back(callbackptr);
 	}
 	size();
 }
diff --git a/engines/tetraedge/game/question2.h b/engines/tetraedge/game/question2.h
index 6468adafa8b..7582f06920d 100644
--- a/engines/tetraedge/game/question2.h
+++ b/engines/tetraedge/game/question2.h
@@ -48,6 +48,7 @@ public:
 	void enter();
 	void leave();
 	void load();
+	bool onBackgroundClick() {}
 	bool onAnswerValidated(Answer &answer);
 	void pushAnswer(const Common::String &name, const Common::String &unk, const Common::String &path);
 	void unload();
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.cpp b/engines/tetraedge/game/scene_lights_xml_parser.cpp
index d7633185fa9..d2f1505568a 100644
--- a/engines/tetraedge/game/scene_lights_xml_parser.cpp
+++ b/engines/tetraedge/game/scene_lights_xml_parser.cpp
@@ -113,20 +113,30 @@ bool SceneLightsXmlParser::parserCallback_Specular(ParserNode *node) {
 }
 
 bool SceneLightsXmlParser::parserCallback_Attenuation(ParserNode *node) {
-	_lights->back().setConstAtten(atof(node->values["constant"].c_str()));
-	_lights->back().setLinearAtten(atof(node->values["linear"].c_str()));
-	_lights->back().setQuadraticAtten(atof(node->values["quadratic"].c_str()));
+	float c = atof(node->values["constant"].c_str());
+	float l = atof(node->values["linear"].c_str());
+	float q = atof(node->values["quadratic"].c_str());
+	if (c < 0 || l < 0 || q < 0)
+		warning("Loaded invalid lighting attenuation vals %f %f %f", c, l, q);
+	_lights->back().setConstAtten(c);
+	_lights->back().setLinearAtten(l);
+	_lights->back().setQuadraticAtten(q);
 	return true;
 }
 
 bool SceneLightsXmlParser::parserCallback_Cutoff(ParserNode *node) {
-	float f = atof(node->values["value"].c_str());
-	_lights->back().setCutoff((f * M_PI) / 180.0);
+	float cutoff = atof(node->values["value"].c_str());
+	if (cutoff < 0.0f || (cutoff > 90.0f && cutoff != 180.0f))
+		warning("Loaded invalid lighting cutoff value %f", cutoff);
+	_lights->back().setCutoff((cutoff * M_PI) / 180.0);
 	return true;
 }
 
 bool SceneLightsXmlParser::parserCallback_Exponent(ParserNode *node) {
-	_lights->back().setExponent(atof(node->values["value"].c_str()));
+	float expon = atof(node->values["value"].c_str());
+	if (expon < 0.0f || expon > 128.0f)
+		warning("Loaded invalid lighting exponent value %f", expon);
+	_lights->back().setExponent(expon);
 	return true;
 }
 
diff --git a/engines/tetraedge/te/te_animation.cpp b/engines/tetraedge/te/te_animation.cpp
index 08b9cc9bf3d..5256ce11d5f 100644
--- a/engines/tetraedge/te/te_animation.cpp
+++ b/engines/tetraedge/te/te_animation.cpp
@@ -57,6 +57,12 @@ void TeAnimation::removeThisFromAnimations() {
 			break;
 		}
 	}
+
+	for (iter = anims->begin(); iter != anims->end(); iter++) {
+		if (*iter == this) {
+			error("anim was added twice to active anims");
+		}
+	}
 }
 
 void TeAnimation::pause() {
diff --git a/engines/tetraedge/te/te_animation.h b/engines/tetraedge/te/te_animation.h
index ed1da6436cb..41ee20eec7e 100644
--- a/engines/tetraedge/te/te_animation.h
+++ b/engines/tetraedge/te/te_animation.h
@@ -40,7 +40,7 @@ public:
 	void play() {
 		cont();
 	}
-	virtual void update(double time) = 0;
+	virtual void update(double millis) = 0;
 
 	void seekToStart();
 	//void staticDestroy();
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index efb5096e32e..f85f50ac040 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -154,7 +154,8 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 				testPath.joinInPlace(langs[langtype]);
 			}
 			testPath.joinInPlace(fname);
-			if (Common::File::exists(testPath) || Common::FSNode(path).exists())
+			//debug("check for %s", testPath.toString());
+			if (Common::File::exists(testPath) || Common::FSNode(testPath).exists())
 				return testPath;
 		}
 	}
diff --git a/engines/tetraedge/te/te_curve_anim2.h b/engines/tetraedge/te/te_curve_anim2.h
index f13bdae1fa2..5f7103f3d79 100644
--- a/engines/tetraedge/te/te_curve_anim2.h
+++ b/engines/tetraedge/te/te_curve_anim2.h
@@ -46,10 +46,10 @@ public:
 		_interp.load(curve);
 	}
 
-	void update(double time) {
-		_lastUpdateTime = time;
+	void update(double millis) {
+		_lastUpdateTime = millis;
 
-		double amount = _interp.interpole(time, _duration);
+		double amount = _interp.interpole(millis, _duration);
 
 		const S interpVal = linearInterpolation<S>(_startVal, _endVal, amount);
 		//debug("CurveAnim %.02f/%.02f (%.02f) -> %s", time, _maxTime, amount, interpVal.toString().c_str());
diff --git a/engines/tetraedge/te/te_frame_anim.cpp b/engines/tetraedge/te/te_frame_anim.cpp
index c115c8ec994..40a9bc3282b 100644
--- a/engines/tetraedge/te/te_frame_anim.cpp
+++ b/engines/tetraedge/te/te_frame_anim.cpp
@@ -29,10 +29,10 @@ TeFrameAnim::TeFrameAnim() : _nbFrames(0), _frameRate(25.0f), _reversed(false),
 _numFramesToShow(-1), _startTime(0), _endTime(FLT_MAX), _loopCount(0), _lastFrameShown(-1) {
 }
 
-void TeFrameAnim::update(double amount) {
+void TeFrameAnim::update(double millis) {
 	int minFrame = MIN(_minFrame, _nbFrames);
 	int maxFrame = MIN(_minFrame + _numFramesToShow, _nbFrames);
-	double frameNo = _frameRate * amount / 1000.0;
+	double frameNo = _frameRate * millis / 1000.0;
 
 	int loopsDone;
 	int framesToPlay = maxFrame - minFrame;
diff --git a/engines/tetraedge/te/te_frame_anim.h b/engines/tetraedge/te/te_frame_anim.h
index c3b1c4869e9..ca717ee6588 100644
--- a/engines/tetraedge/te/te_frame_anim.h
+++ b/engines/tetraedge/te/te_frame_anim.h
@@ -30,8 +30,9 @@ namespace Tetraedge {
 class TeFrameAnim : public TeAnimation {
 public:
 	TeFrameAnim();
+	virtual ~TeFrameAnim() {}
 
-	void update(double amount) override;
+	void update(double millis) override;
 
 	TeSignal0Param &frameChangedSignal() { return _frameChangedSignal; };
 
diff --git a/engines/tetraedge/te/te_light.cpp b/engines/tetraedge/te/te_light.cpp
index 61ed3f02339..55d42947e30 100644
--- a/engines/tetraedge/te/te_light.cpp
+++ b/engines/tetraedge/te/te_light.cpp
@@ -93,6 +93,8 @@ void TeLight::transformSpotPoint(TeVector3f32 &pt) {
 }
 
 void TeLight::update(uint lightno) {
+	if (lightno > GL_MAX_LIGHTS)
+		error("Invalid light no %d", lightno);
 	float col[4];
 	const uint glLight = _toGlLight(lightno);
 	col[0] = _colAmbient.r() / 255.0f;
@@ -106,7 +108,12 @@ void TeLight::update(uint lightno) {
 	col[2] = _colDiffuse.b() / 255.0f;
 	col[3] = 1.0;
 	glLightfv(glLight, GL_DIFFUSE, col);
-	if (col[0] < 0.01f && col[1] < 0.01f && col[2] < 0.01f)
+
+	// WORKAROUND: Original game sets 0.01 as threshold here to avoid enabling
+	// the "shadow" light.  However, CitStation/31130 has shadowlight with values
+	// 4,0,0 which means it gets enabled.
+
+	if (col[0] < 0.02f && col[1] < 0.02f && col[2] < 0.02f)
 		glDisable(glLight);
 
 	col[0] = _colSpecular.r() / 255.0f;
@@ -168,5 +175,28 @@ void TeLight::updateGlobal() {
 	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
 }
 
+Common::String TeLight::dump() const {
+	const char *ltype;
+	switch (_type) {
+		case LightTypeSpot:
+			ltype = "Spot";
+			break;
+		case LightTypePoint:
+			ltype = "Point";
+			break;
+		case LightTypeDirectional:
+			ltype = "Directional";
+			break;
+		default:
+			error("Invalid light type %d", (int)_type);
+	}
+
+	return Common::String::format("%sLight(\n\tamb:%s diff:%s spec:%s\n\tpos:%s posRad:%s atten:%.02f %.02f %.02f\n\tcutoff:%.02f exp:%.02f dispSz:%.02f\n)",
+		ltype, _colAmbient.dump().c_str(), _colDiffuse.dump().c_str(),
+		_colSpecular.dump().c_str(), _position3d.dump().c_str(),
+		_positionRadial.dump().c_str(), _constAtten, _linearAtten,
+		_quadraticAtten, _cutoff, _exponent, _displaySize);
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_light.h b/engines/tetraedge/te/te_light.h
index 2ed67430525..38d45756e6f 100644
--- a/engines/tetraedge/te/te_light.h
+++ b/engines/tetraedge/te/te_light.h
@@ -54,6 +54,7 @@ public:
 	void update(uint lightno);
 	static void updateGlobal();
 	static void setGlobalAmbient(const TeColor &col) { _globalAmbientColor = col; }
+	static const TeColor &globalAmbient() { return _globalAmbientColor; }
 
 	void setSpecular(const TeColor &col) { _colSpecular = col; }
 	void setDiffuse(const TeColor &col) { _colDiffuse = col; }
@@ -72,6 +73,8 @@ public:
 	const TeVector2f32 &positionRadial() const { return _positionRadial; }
 	const TeVector3f32 &position3d() const { return _position3d; }
 
+	Common::String dump() const;
+
 private:
 	TeVector3f32 _position3d;
 	TeVector2f32 _positionRadial;
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index ef1de05bba7..c5b1d4b67cc 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -118,6 +118,7 @@ void TeModel::draw() {
 			mesh.draw();
 		}
 		renderer->popMatrix();
+		// Note: no corresponding enableAll - they can be enabled inside TeMaterial::apply
 		TeLight::disableAll();
 	}
 }
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index 560ec007bd9..a5c28c8f329 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -76,6 +76,7 @@ void TeModelAnimation::destroy() {
 	_nmoRotArrays.clear();
 	_nmoScaleArrays.clear();
 	_fbxArrays.clear();
+	_numNMOFrames = 0;
 }
 
 int TeModelAnimation::findBone(const Common::String &bname) {
@@ -338,7 +339,7 @@ void TeModelAnimation::unbind() {
 	_model.release();
 }
 
-void TeModelAnimation::update(double proportion) {
+void TeModelAnimation::update(double millis) {
 	int frames;
 	if (!_useNMOArrays) {
 		if (_fbxArrays.empty())
@@ -351,7 +352,7 @@ void TeModelAnimation::update(double proportion) {
 
 	if (frames) {
 		_curFrameValFresh = false;
-		_curFrame2 = calcCurrentFrame(proportion);
+		_curFrame2 = calcCurrentFrame(millis);
 		if (_finishedSignalPending) {
 			_finishedSignalPending = false;
 			_onFinishedSignal.call();
diff --git a/engines/tetraedge/te/te_model_animation.h b/engines/tetraedge/te/te_model_animation.h
index 5152b082bd2..4604c818c76 100644
--- a/engines/tetraedge/te/te_model_animation.h
+++ b/engines/tetraedge/te/te_model_animation.h
@@ -54,7 +54,7 @@ public:
 
 	TeModelAnimation();
 
-	~TeModelAnimation() {
+	virtual ~TeModelAnimation() {
 		destroy();
 	}
 
@@ -89,7 +89,7 @@ public:
 	void setScale(unsigned long num, float amount, const TeVector3f32 &scale);
 	void setTranslation(unsigned long num, float amount, const TeVector3f32 &trans);
 	void unbind();
-	void update(double proportion) override;
+	void update(double millis) override;
 
 	int curFrame2() const { return _curFrame2; }
 	float speed() const { return _speed; }
diff --git a/engines/tetraedge/te/te_model_vertex_animation.h b/engines/tetraedge/te/te_model_vertex_animation.h
index 9e3e7ce9e76..71e29eaccdd 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.h
+++ b/engines/tetraedge/te/te_model_vertex_animation.h
@@ -47,6 +47,9 @@ public:
 	};
 
 	TeModelVertexAnimation();
+	virtual ~TeModelVertexAnimation() {
+		_keydata.clear();
+	}
 
 	void bind(TeIntrusivePtr<TeModel> &model);
 	// void deleteLater() // original overrides this, but just calls the super.
@@ -59,7 +62,7 @@ public:
 	bool load(Common::ReadStream &stream);
 	void save(Common::WriteStream &stream) const;
 
-	void update(double amount) override;
+	void update(double millis) override;
 
 private:
 	float _lastMillis;
diff --git a/engines/tetraedge/te/te_ray_intersection.cpp b/engines/tetraedge/te/te_ray_intersection.cpp
index 9526563d69a..6fcdad15a17 100644
--- a/engines/tetraedge/te/te_ray_intersection.cpp
+++ b/engines/tetraedge/te/te_ray_intersection.cpp
@@ -44,7 +44,7 @@ int intersect(const TeVector3f32 &p, const TeVector3f32 &d, const TeVector3f32 &
 	float a = e1.dotProduct(h);
 	if (fabs(a) < 1e-6f)
 		return 0;
-	
+
 	float f = 1.0f / a;
 	const TeVector3f32 s = p - v0;
 	float u = f * s.dotProduct(h);
@@ -56,15 +56,15 @@ int intersect(const TeVector3f32 &p, const TeVector3f32 &d, const TeVector3f32 &
 
 	if (v < 0.0f || u + v > 1.0f)
 		return 0;
-		
+
 	float t = f * e2.dotProduct(q);
-	
+
 	if (t < 1e-6f)
 		return 0;
-	
+
 	hitDist = t;
 	hitPt = p + t * d;
-	
+
 	return 1;
 }*/
 
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 464d6632cad..0ac997fbebe 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -272,7 +272,6 @@ void TeRenderer::init() {
 	_scissorX = _scissorY = _scissorWidth = _scissorHeight = 0;
 }
 
-
 void TeRenderer::loadIdentityMatrix() {
 	_matriciesStacks[_matrixMode].loadIdentity();
 }
diff --git a/engines/tetraedge/te/te_signal.h b/engines/tetraedge/te/te_signal.h
index ebad199faa3..bf4b93644e0 100644
--- a/engines/tetraedge/te/te_signal.h
+++ b/engines/tetraedge/te/te_signal.h
@@ -57,8 +57,7 @@ public:
 		typename Common::Array<TeICallback0ParamPtr>::iterator end_ = this->end();
 		for (; i < end_; i++) {
 			if ((*i)->equals(item.get())) {
-				this->erase(i);
-				break;
+				i = this->erase(i);
 			}
 		}
 	}
@@ -98,8 +97,7 @@ public:
 		typename Common::Array<TeICallback1ParamPtr<T>>::iterator end_ = this->end();
 		for (; i < end_; i++) {
 			if ((*i)->equals(item.get())) {
-				this->erase(i);
-				break;
+				i = this->erase(i);
 			}
 		}
 	}
@@ -138,8 +136,7 @@ public:
 		typename Common::Array<TeICallback2ParamPtr<S, T>>::iterator end_ = this->end();
 		for (; i < end_; i++) {
 			if ((*i)->equals(item.get())) {
-				this->erase(i);
-				break;
+				i = this->erase(i);
 			}
 		}
 	}


Commit: f84785ff3ae75f1fdad08f570b20a6ee026d70e4
    https://github.com/scummvm/scummvm/commit/f84785ff3ae75f1fdad08f570b20a6ee026d70e4
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Cleanups, started working on credits etc

Scrolling text now mostly works, but isn't clipped to the dialog box.

Changed paths:
  A engines/tetraedge/te/te_i_text_layout.cpp
  A engines/tetraedge/te/te_i_text_layout.h
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/application.h
    engines/tetraedge/game/bonus_menu.cpp
    engines/tetraedge/game/bonus_menu.h
    engines/tetraedge/game/cellphone.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/confirm.cpp
    engines/tetraedge/game/credits.cpp
    engines/tetraedge/game/credits.h
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/documents_browser.cpp
    engines/tetraedge/game/gallery_menu.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/global_bonus_menu.cpp
    engines/tetraedge/game/how_to.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/loc_file.h
    engines/tetraedge/game/main_menu.cpp
    engines/tetraedge/game/notifier.cpp
    engines/tetraedge/game/objectif.cpp
    engines/tetraedge/game/question2.h
    engines/tetraedge/game/scene_lights_xml_parser.cpp
    engines/tetraedge/game/splash_screens.cpp
    engines/tetraedge/metaengine.cpp
    engines/tetraedge/module.mk
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture.h
    engines/tetraedge/te/te_button_layout.cpp
    engines/tetraedge/te/te_button_layout.h
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_extended_text_layout.cpp
    engines/tetraedge/te/te_extended_text_layout.h
    engines/tetraedge/te/te_font3.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_i_3d_object2.cpp
    engines/tetraedge/te/te_i_3d_object2.h
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_layout.cpp
    engines/tetraedge/te/te_light.cpp
    engines/tetraedge/te/te_list_layout.cpp
    engines/tetraedge/te/te_lua_context.cpp
    engines/tetraedge/te/te_lua_gui.cpp
    engines/tetraedge/te/te_lua_gui.h
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_material.cpp
    engines/tetraedge/te/te_matrix4x4.cpp
    engines/tetraedge/te/te_model_vertex_animation.cpp
    engines/tetraedge/te/te_music.cpp
    engines/tetraedge/te/te_palette.cpp
    engines/tetraedge/te/te_ray_intersection.cpp
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer.h
    engines/tetraedge/te/te_scrolling_layout.cpp
    engines/tetraedge/te/te_scrolling_layout.h
    engines/tetraedge/te/te_sprite_layout.cpp
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_text_base2.h
    engines/tetraedge/te/te_text_layout.cpp
    engines/tetraedge/te/te_text_layout.h
    engines/tetraedge/te/te_text_layout_xml_parser.cpp
    engines/tetraedge/te/te_text_layout_xml_parser.h
    engines/tetraedge/te/te_theora.cpp
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_visual_fade.h
    engines/tetraedge/tetraedge.cpp
    engines/tetraedge/tetraedge.h
    engines/tetraedge/to_lua.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 369d9a5493f..e05f2bcbd40 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -26,6 +26,7 @@
 #include "common/events.h"
 
 #include "graphics/opengl/system_headers.h"
+#include "graphics/scaler.h"
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/game.h"
@@ -64,10 +65,10 @@ _drawShadows(true) {
 	_firstScene = tempGui.value("firstScene").toString();
 
 	TeSoundManager *soundmgr = g_engine->getSoundManager();
-	soundmgr->setChannelVolume("sfx", 0.7);
-	soundmgr->setChannelVolume("music", 0.7);
-	soundmgr->setChannelVolume("dialog", 0.7);
-	soundmgr->setChannelVolume("video", 0.7);
+	soundmgr->setChannelVolume("sfx", 0.7f);
+	soundmgr->setChannelVolume("music", 0.7f);
+	soundmgr->setChannelVolume("dialog", 0.7f);
+	soundmgr->setChannelVolume("video", 0.7f);
 	// TODO: Configure freemium things here?
 
 	// Note: original has an app run timer, but it's never used?
@@ -96,7 +97,7 @@ void Application::create() {
 	_mainWindow.setSize(TeVector3f32(winWidth, winHeight, 0.0));
 	_mainWindow.setSizeType(TeILayout::ABSOLUTE);
 	_mainWindow.setPositionType(TeILayout::ABSOLUTE);
-	_mainWindow.setPosition(TeVector3f32(0.0f, 0.0f ,0.0f));
+	_mainWindow.setPosition(TeVector3f32(0.0f, 0.0f, 0.0f));
 
 	TeResourceManager *resmgr = g_engine->getResourceManager();
 	_fontComic = resmgr->getResourceNoSearch<TeFont3>("Common/Fonts/ComicRelief.ttf");
@@ -483,6 +484,18 @@ void Application::captureFade() {
 	_visFade.captureFrame();
 }
 
+void Application::getSavegameThumbnail(Graphics::Surface &thumb) {
+	captureFade();
+	Graphics::Surface screen;
+	_visFade.texture()->writeTo(screen);
+	screen.flipVertical(Common::Rect(screen.w, screen.h));
+    Common::ScopedPtr<Graphics::Surface> scaledScreen(screen.scale(kThumbnailWidth, kThumbnailHeight2, true));
+    //scaledScreen->convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
+    thumb.copyFrom(*scaledScreen);
+    screen.free();
+    scaledScreen->free();
+}
+
 bool Application::isFading() {
 	return _visFade.blackFading() || _visFade.fading();
 }
diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
index 8bd2ce06290..9d88c203c76 100644
--- a/engines/tetraedge/game/application.h
+++ b/engines/tetraedge/game/application.h
@@ -82,6 +82,8 @@ public:
 	void loadOptions(const Common::String &fname);
 	void saveOptions(const Common::String &fname);
 
+	void getSavegameThumbnail(Graphics::Surface &thumb);
+
 	Common::String getHelpText(const Common::String &key);
 
 	const char *inAppUnlockFullVersionID();
diff --git a/engines/tetraedge/game/bonus_menu.cpp b/engines/tetraedge/game/bonus_menu.cpp
index bc734d7cc4f..f0ccb12b348 100644
--- a/engines/tetraedge/game/bonus_menu.cpp
+++ b/engines/tetraedge/game/bonus_menu.cpp
@@ -32,11 +32,68 @@ BonusMenu::BonusMenu() {
 }
 
 void BonusMenu::enter(const Common::String &scriptName) {
-	error("TODO: implement me.");
+	bool loaded = load(scriptName);
+	if (!loaded)
+		error("BonusMenu::enter: failed to load %s", scriptName.c_str());
+	Application *app = g_engine->getApplication();
+	app->_frontLayout.addChild(layoutChecked("menu"));
+
+	buttonLayoutChecked("quitButton")->onMouseClickValidated().add(this, &BonusMenu::onQuitButton);
+
+	int btnNo = 0;
+	while (true) {
+		const Common::String btnNoStr = Common::String::format("%d", btnNo);
+		TeButtonLayout *btn = buttonLayout(btnNoStr);
+		if (!btn)
+			break;
+		SaveButton *saveBtn = new SaveButton(btn, btnNoStr);
+		_saveButtons.push_back(saveBtn);
+
+		// TODO: Finish this.
+
+		btnNo++;
+	}
+
+	btnNo = 0;
+	while( true ) {
+		const Common::String btnNoStr = Common::String::format("slot%d", btnNo);
+		TeLayout *l = layout(btnNoStr);
+		if (!l)
+			break;
+
+		if (btnNo < (int)_saveButtons.size()) {
+			l->addChild(_saveButtons[btnNo]);
+		}
+		btnNo = btnNo + 1;
+	}
+
+	TeButtonLayout *slideBtn = buttonLayoutChecked("slideButton");
+	slideBtn->onButtonChangedToStateDownSignal().add(this, &BonusMenu::onSlideButtonDown);
+
+	TeInputMgr *inputmgr = g_engine->getInputMgr();
+	inputmgr->_mouseMoveSignal.add(this, &BonusMenu::onMouseMove);
+
+	_pageNo = 0;
+
+	TeButtonLayout *leftBtn = buttonLayout("leftButton");
+	if (leftBtn)
+		leftBtn->onMouseClickValidated().add(this, &BonusMenu::onLeftButton);
+
+	TeButtonLayout *rightBtn = buttonLayout("rightButton");
+	if (rightBtn)
+		rightBtn->onMouseClickValidated().add(this, &BonusMenu::onRightButton);
+
+	// TODO: more stuff here with "text" values (also finish loop above)
+	warning("TODO: Finish BonusMenu::enter(%s)", scriptName.c_str());
+
+	TeButtonLayout *pictureBtn = buttonLayout("fullScreenPictureButton");
+	if (pictureBtn) {
+		pictureBtn->onMouseClickValidated().add(this, &BonusMenu::onPictureButton);
+	}
 }
 
 void BonusMenu::enter() {
-	error("TODO: implement me.");
+	error("TODO: implement BonusMenu::enter()");
 }
 
 void BonusMenu::leave() {
@@ -44,23 +101,58 @@ void BonusMenu::leave() {
 		delete s;
 	}
 	_saveButtons.clear();
-	warning("TODO: remove oMouseMove signal from inputmgr.");
+	TeInputMgr *inputmgr = g_engine->getInputMgr();
+	inputmgr->_mouseMoveSignal.remove(this, &BonusMenu::onMouseMove);
 	TeLuaGUI::unload();
 }
 
 bool BonusMenu::onLeftButton() {
-	error("TODO: implement me.");
+	TeCurveAnim2<TeLayout, TeVector3f32> *slideAnim = layoutPositionLinearAnimation("slideAnimation");
+
+	if (!slideAnim->_runTimer.running() && _pageNo != 0) {
+		TeLayout *slotsLayout = layout("slots");
+		TeVector3f32 slotsPos = slotsLayout->userPosition();
+		slideAnim->_startVal = slotsPos;
+		slotsPos.x() += value("slideTranslation").toFloat64();
+		slideAnim->_endVal = slotsPos;
+
+		slideAnim->_callbackObj = layoutChecked("slots");
+		slideAnim->_callbackMethod = &TeLayout::setPosition;
+		slideAnim->play();
+
+		_pageNo--;
+
+		buttonLayoutChecked("slideButton")->reset();
+
+		warning("TODO: Finish BonusMenu::onLeftButton");
+		// TODO: Set values depending on whether saves exist (lines 95-120)
+	}
+
 	return false;
 }
 
-bool BonusMenu::onMouseMove() {
-	error("TODO: implement me.");
+bool BonusMenu::onMouseMove(const Common::Point &pt) {
+	TeButtonLayout *slideLayout = buttonLayout("slideButton");
+	if (slideLayout->state() == TeButtonLayout::BUTTON_STATE_DOWN) {
+		TeCurveAnim2<TeLayout, TeVector3f32> *slideAnim = layoutPositionLinearAnimation("slideAnimation");
+		if (!slideAnim->_runTimer.running()) {
+			warning("TODO: implement BonusMenu::onMouseMove");
+		}
+	}
+
 	return false;
 }
 
 bool BonusMenu::onPictureButton() {
-	error("TODO: implement me.");
-	return false;
+	TeButtonLayout *btn = buttonLayoutChecked("menu");
+	btn->setVisible(true);
+
+	Application *app = g_engine->getApplication();
+	TeSpriteLayout *pictureLayout = spriteLayoutChecked("fullScreenPictureLayout");
+	app->_frontLayout.removeChild(pictureLayout);
+	pictureLayout->setVisible(true);
+
+	return true;
 }
 
 bool BonusMenu::onQuitButton() {
@@ -75,7 +167,26 @@ bool BonusMenu::onQuitButton() {
 }
 
 bool BonusMenu::onRightButton() {
-	error("TODO: implement me.");
+	TeCurveAnim2<TeLayout, TeVector3f32> *slideAnim = layoutPositionLinearAnimation("slideAnimation");
+
+	if (!slideAnim->_runTimer.running() && _pageNo < (int)_saveButtons.size() - 1) {
+		TeLayout *slotsLayout = layout("slots");
+		TeVector3f32 slotsPos = slotsLayout->userPosition();
+		slideAnim->_startVal = slotsPos;
+		slotsPos.x() -= value("slideTranslation").toFloat64();
+		slideAnim->_endVal = slotsPos;
+
+		slideAnim->_callbackObj = layoutChecked("slots");
+		slideAnim->_callbackMethod = &TeLayout::setPosition;
+		slideAnim->play();
+
+		_pageNo++;
+
+		buttonLayoutChecked("slideButton")->reset();
+
+		// TODO: Set values depending on whether saves exist (lines 95-120)
+		warning("TODO: Finish BonusMenu::onRightButton");
+	}
 	return false;
 }
 
@@ -87,12 +198,19 @@ bool BonusMenu::onSlideButtonDown() {
 	return false;
 }
 
+BonusMenu::SaveButton::SaveButton(TeButtonLayout *btn, const Common::String &name) {
+	setName(name);
+	btn->setEnable(true);
+	// TODO: Add child something here?
+	btn->onMouseClickValidated().add(this, &BonusMenu::SaveButton::onLoadSave);
+}
+
 Common::String BonusMenu::SaveButton::path() const {
 	return Common::String("Backup/") + name() + ".xml";
 }
 
 bool BonusMenu::SaveButton::onLoadSave() {
-	error("TODO: implement me.");
+	error("TODO: implement BonusMenu::SaveButton::onLoadSave");
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/bonus_menu.h b/engines/tetraedge/game/bonus_menu.h
index 4cb63291fa1..5a57cdeb66a 100644
--- a/engines/tetraedge/game/bonus_menu.h
+++ b/engines/tetraedge/game/bonus_menu.h
@@ -36,6 +36,7 @@ public:
 
 	class SaveButton : public TeLayout {
 	public:
+		SaveButton(TeButtonLayout *btn, const Common::String &name);
 		bool onLoadSave();
 		Common::String path() const;
 	};
@@ -49,7 +50,7 @@ public:
 	}
 
 	bool onLeftButton();
-	bool onMouseMove();
+	bool onMouseMove(const Common::Point &pt);
 	bool onPictureButton();
 	bool onQuitButton();
 	bool onRightButton();
@@ -61,6 +62,7 @@ private:
 	Common::Array<SaveButton *> _saveButtons;
 	TeVector2s32 _slideBtnStartMousePos;
 	Common::String _gameName;
+	int _pageNo;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/cellphone.cpp b/engines/tetraedge/game/cellphone.cpp
index 3ec7dbe7d2f..f2a47e2c330 100644
--- a/engines/tetraedge/game/cellphone.cpp
+++ b/engines/tetraedge/game/cellphone.cpp
@@ -41,7 +41,7 @@ bool Cellphone::addNumber(const Common::String &num) {
 	layout->setName(namePrefix + num);
 	layout->setSizeType(RELATIVE_TO_PARENT);
 	layout->setAnchor(TeVector3f32(0.5f, 0.0f, 0.0f));
-	// WORKAROUND: Original uses 1.0,1.0,1.0 here but then the text area is too high.
+	// WORKAROUND: Original uses (1.0, 1.0, 1.0) here but then the text area is too high.
 	layout->setSize(TeVector3f32(1.0f, 0.6f, 0.0f));
 	layout->setPosition(TeVector3f32(0.5f, 0.08f, 0.0f));
 	layout->setTextSizeType(1);
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 3076eeb6529..7827158b957 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -179,7 +179,7 @@ float Character::animLength(const TeModelAnimation &modelanim, long bone, long l
 	return ((endtrans.z() - starttrans.z()) + secondtrans.z()) - starttrans.z();
 }
 
-float Character::animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe /* = 9999 */) {
+float Character::animLengthFromFile(const Common::String &animname, uint32 *pframeCount, uint lastframe /* = 9999 */) {
 	if (animname.empty()) {
 		*pframeCount = 0;
 		return 0.0f;
@@ -288,7 +288,6 @@ void Character::deleteCallback(const Common::String &key, const Common::String &
 		_callbacks.erase(animFile);
 }
 
-//static bool deserialize(TiXmlElement *param_1, Walk *param_2);
 void Character::endMove() {
 	if (_model->name() == "Kate")
 		walkMode("Walk");
@@ -388,7 +387,7 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 		arr[1] = TeVector3f32(-60.0, 0.0, 60.0);
 		arr[2] = TeVector3f32(60.0, 0.0, -60.0);
 		arr[3] = TeVector3f32(60.0, 0.0, 60.0);
-		pmodel->setQuad(shadow, arr, TeColor(0xff,0xff,0xff,0x50));
+		pmodel->setQuad(shadow, arr, TeColor(0xff, 0xff, 0xff, 0x50));
 	}
 	return true;
 }
@@ -581,7 +580,6 @@ bool Character::onModelAnimationFinished() {
 		const TeTRS endTRS = trsFromAnim(*_curModelAnim, pereBone, _curModelAnim->lastFrame());
 		TeVector3f32 trans = endTRS.getTranslation();
 		trans.x() = -trans.x();
-		trans.y() = 0;
 
 		TeVector3f32 newpos;
 		if (!_recallageY) {
@@ -592,6 +590,7 @@ bool Character::onModelAnimationFinished() {
 			newpos = _model->worldTransformationMatrix() * trans;
 		} else if (!_freeMoveZone) {
 			trans.x() = -trans.x();
+			trans.y() = 0.0;
 			newpos = _model->worldTransformationMatrix() * trans;
 		} else {
 			newpos = correctPosition(_model->worldTransformationMatrix() * trans);
@@ -798,7 +797,7 @@ void Character::update(double msFromStart) {
 	_walkCurveNextLength = speedFromAnim(msFromStart) * _walkCurveIncrement + _walkCurveNextLength;
 
 	if (_curve->controlPoints().size() < 2) {
-		blendAnimation(_characterSettings._idleAnimFileName, 0.0667, true, false);
+		blendAnimation(_characterSettings._idleAnimFileName, 0.0667f, true, false);
 		endMove();
 		return;
 	}
@@ -884,7 +883,7 @@ void Character::update(double msFromStart) {
 			endMove();
 		}
 		if (endGAnim.empty()) {
-			blendAnimation(_characterSettings._idleAnimFileName, 0.0667, true, false);
+			blendAnimation(_characterSettings._idleAnimFileName, 0.0667f, true, false);
 			endMove();
 		}
 	}
@@ -965,7 +964,7 @@ void Character::walkTo(float curveEnd, bool walkFlag) {
 			if (game->scene()._character == this && _walkModeStr == "Walk") {
 				int looplen = (int)(nloops * _walkLoopAnimFrameCount);
 				int repeats = looplen / _walkLoopAnimFrameCount;
-				uint remainder = looplen % _walkLoopAnimFrameCount;
+				uint32 remainder = looplen % _walkLoopAnimFrameCount;
 
 				uint framecounts[4];
 
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 191b1796524..8eb0b41e9da 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -100,7 +100,7 @@ public:
 	static TeIntrusivePtr<TeModelAnimation> animCacheLoad(const Common::Path &path);
 
 	float animLength(const TeModelAnimation &modelanim, long bone, long lastframe);
-	float animLengthFromFile(const Common::String &animname, uint *pframeCount, uint lastframe = 9999);
+	float animLengthFromFile(const Common::String &animname, uint32 *pframeCount, uint lastframe = 9999);
 	bool blendAnimation(const Common::String &animname, float amount, bool repeat, bool returnToIdle);
 	TeVector3f32 correctPosition(const TeVector3f32 &pos);
 	float curveOffset();
diff --git a/engines/tetraedge/game/confirm.cpp b/engines/tetraedge/game/confirm.cpp
index 70c565aa307..c535e0a7c73 100644
--- a/engines/tetraedge/game/confirm.cpp
+++ b/engines/tetraedge/game/confirm.cpp
@@ -63,27 +63,27 @@ void Confirm::enter(const Common::String &guiPath, const Common::String &y) {
 			error("Missing translations for ok and cancel");
 		}
 
-		TeTextLayout *yesUpLayout = _gui.textLayout("yesUpLayout");
+		TeITextLayout *yesUpLayout = _gui.textLayout("yesUpLayout");
 		if (yesUpLayout)
 			yesUpLayout->setText(textAttributs + *okButtonLoc);
 
-		TeTextLayout *yesDownLayout = _gui.textLayout("yesDownLayout");
+		TeITextLayout *yesDownLayout = _gui.textLayout("yesDownLayout");
 		if (yesDownLayout)
 			yesDownLayout->setText(textAttributsDown + *okButtonLoc);
 
-		TeTextLayout *yesRollOverLayout = _gui.textLayout("yesRollOverLayout");
+		TeITextLayout *yesRollOverLayout = _gui.textLayout("yesRollOverLayout");
 		if (yesRollOverLayout)
 			yesRollOverLayout->setText(textAttributs + *okButtonLoc);
 
-		TeTextLayout *noUpLayout = _gui.textLayout("noUpLayout");
+		TeITextLayout *noUpLayout = _gui.textLayout("noUpLayout");
 		if (noUpLayout)
 			noUpLayout->setText(textAttributs + *cancelButtonLoc);
 
-		TeTextLayout *noDownLayout = _gui.textLayout("noDownLayout");
+		TeITextLayout *noDownLayout = _gui.textLayout("noDownLayout");
 		if (noDownLayout)
 			noDownLayout->setText(textAttributsDown + *cancelButtonLoc);
 
-		TeTextLayout *noRollOverLayout = _gui.textLayout("noRollOverLayout");
+		TeITextLayout *noRollOverLayout = _gui.textLayout("noRollOverLayout");
 		if (noRollOverLayout)
 			noRollOverLayout->setText(textAttributs + *cancelButtonLoc);
 	}
diff --git a/engines/tetraedge/game/credits.cpp b/engines/tetraedge/game/credits.cpp
index 572acc67079..b726fdb4294 100644
--- a/engines/tetraedge/game/credits.cpp
+++ b/engines/tetraedge/game/credits.cpp
@@ -20,19 +20,113 @@
  */
 
 #include "common/textconsole.h"
+
+#include "tetraedge/tetraedge.h"
 #include "tetraedge/game/credits.h"
+#include "tetraedge/game/application.h"
 
 namespace Tetraedge {
 
-Credits::Credits() {
+Credits::Credits() : _animCounter(0), _returnToOptions(false) {
 }
 
-void Credits::enter(bool flag) {
-	error("TODO: Implement Credits::enter");
+void Credits::enter(bool returnToOptions) {
+	_returnToOptions = returnToOptions;
+	_animCounter = 0;
+	_timer.start();
+	// TODO: set _field0x50 = 0;
+	_gui.load("menus/credits/credits.lua");
+	Application *app = g_engine->getApplication();
+	app->_frontLayout.addChild(_gui.layoutChecked("menu"));
+
+	Common::String musicPath = _gui.value("musicPath").toString();
+	if (!app->music().isPlaying() || app->music().path() != musicPath) {
+		app->music().stop();
+		app->music().load(musicPath);
+		app->music().play();
+		app->music().volume(1.0f);
+	}
+
+	TeButtonLayout *bgbtn = _gui.buttonLayout("background");
+	if (bgbtn) {
+		bgbtn->onMouseClickValidated().add(this, &Credits::onQuitButton);
+	}
+
+	TeCurveAnim2<TeLayout, TeVector3f32> *posAnim = _gui.layoutPositionLinearAnimation("scrollTextPositionAnim");
+	if (!posAnim)
+		error("Credits gui - couldn't find scrollTextPositionAnim");
+	posAnim->onFinished().add(this, &Credits::onAnimFinished);
+	posAnim->_callbackObj = _gui.layoutChecked("text");
+	posAnim->_callbackMethod = &TeLayout::setPosition;
+	posAnim->play();
+
+	TeCurveAnim2<TeLayout, TeVector3f32> *anchorAnim = _gui.layoutAnchorLinearAnimation("scrollTextAnchorAnim");
+	if (!anchorAnim)
+		error("Credits gui - couldn't find scrollTextAnchorAnim");
+
+	anchorAnim->_callbackObj = _gui.layoutChecked("text");
+	anchorAnim->_callbackMethod = &TeLayout::setAnchor;
+	anchorAnim->play();
+
+	TeCurveAnim2<TeLayout, TeVector3f32> *bgPosAnim = _gui.layoutPositionLinearAnimation("scrollBackgroundPositionAnim");
+	if (!bgPosAnim)
+		error("Credits gui - couldn't find scrollBackgroundPositionAnim");
+
+	bgPosAnim->_callbackObj = _gui.layoutChecked("backgroundSprite");
+	bgPosAnim->_callbackMethod = &TeLayout::setAnchor;
+	bgPosAnim->play();
+
+	_curveAnim._runTimer.pausable(false);
+	_curveAnim.stop();
+	_curveAnim._startVal = TeColor(0xff, 0xff, 0xff, 0);
+	_curveAnim._endVal = TeColor(0xff, 0xff, 0xff, 0xff);
+	Common::Array<float> curve;
+	curve.push_back(0.0f);
+	curve.push_back(0.0f);
+	curve.push_back(0.0f);
+	curve.push_back(0.0f);
+	curve.push_back(1.0f);
+	_curveAnim._repeatCount = 1;
+	_curveAnim.setCurve(curve);
+	_curveAnim._duration = 12000;
+
+	TeLayout *backgrounds = _gui.layoutChecked("Backgrounds");
+	if (_animCounter < backgrounds->childCount()) {
+		TeSpriteLayout *bgchild = dynamic_cast<TeSpriteLayout *>(backgrounds->child(_animCounter));
+		if (!bgchild)
+			error("Child of backgrounds is not a TeSpriteLayout");
+		_curveAnim._callbackObj = bgchild;
+		_curveAnim._callbackMethod = &TeLayout::setColor;
+		_curveAnim.play();
+		const Common::String bgAnimName = bgchild->name() + "Anim";
+		bgPosAnim = _gui.layoutPositionLinearAnimation(bgAnimName);
+		if (!bgPosAnim)
+			error("Couldn't find bg position anim %s", bgAnimName.c_str());
+		bgPosAnim->_callbackObj = bgchild;
+		bgPosAnim->_callbackMethod = &TeLayout::setPosition;
+		bgPosAnim->play();
+	}
+	_curveAnim.onFinished().add(this, &Credits::onBackgroundAnimFinished);
 }
 
 void Credits::leave() {
-	error("TODO: Implement Credits::leave");
+	_curveAnim.stop();
+	for (auto anim : _gui.layoutPositionLinearAnimations()) {
+		anim._value->stop();
+	}
+	if (_gui.loaded()) {
+		Application *app = g_engine->getApplication();
+		app->captureFade();
+		app->_frontLayout.removeChild(_gui.layoutChecked("menu"));
+		_timer.stop();
+		_gui.unload();
+		if (_returnToOptions)
+			error("TODO: Implement returning to options menu");
+		else
+			app->mainMenu().enter();
+		app->fade();
+		_curveAnim.onFinished().remove(this, &Credits::onBackgroundAnimFinished);
+	}
 }
 
 bool Credits::onAnimFinished() {
@@ -41,8 +135,24 @@ bool Credits::onAnimFinished() {
 }
 
 bool Credits::onBackgroundAnimFinished() {
-	//TeLayout *buttonsLayout = _gui.layout("buttons");
-	error("TODO: Implement Credits::onBackgroundAnimFinished");
+	_animCounter++;
+	TeLayout *backgrounds = _gui.layoutChecked("Backgrounds");
+	if (_animCounter < backgrounds->childCount()) {
+		TeSpriteLayout *bgchild = dynamic_cast<TeSpriteLayout *>(backgrounds->child(_animCounter));
+		if (!bgchild)
+			error("Children of credits Backgrounds should be Sprites.");
+		_curveAnim._callbackObj = bgchild;
+		_curveAnim._callbackMethod = &TeLayout::setColor;
+		_curveAnim.play();
+		const Common::String bgAnimName = bgchild->name() + "Anim";
+		TeCurveAnim2<TeLayout, TeVector3f32> *bgposanim = _gui.layoutPositionLinearAnimation(bgAnimName);
+		if (!bgposanim)
+			error("Couldn't find bg position anim %s", bgAnimName.c_str());
+		bgposanim->_callbackObj = bgchild;
+		bgposanim->_callbackMethod = &TeLayout::setPosition;
+		bgposanim->play();
+	}
+	return false;
 }
 
 bool Credits::onPadButtonUp(uint button) {
@@ -54,13 +164,12 @@ bool Credits::onPadButtonUp(uint button) {
 }
 
 bool Credits::onQuitButton() {
-	TeCurveAnim2<TeI3DObject2, TeVector3f32> *anim1 = _gui.layoutPositionLinearAnimation("scrollTextPositionAnim");
+	TeCurveAnim2<TeLayout, TeVector3f32> *anim1 = _gui.layoutPositionLinearAnimation("scrollTextPositionAnim");
 	anim1->stop();
-	TeCurveAnim2<TeI3DObject2, TeVector3f32> *anim2 = _gui.layoutPositionLinearAnimation("scrollTextAnchorAnim");
+	TeCurveAnim2<TeLayout, TeVector3f32> *anim2 = _gui.layoutAnchorLinearAnimation("scrollTextAnchorAnim");
 	anim2->stop();
 	leave();
 	return true;
 }
 
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/credits.h b/engines/tetraedge/game/credits.h
index fada8eefdaa..c39740ba877 100644
--- a/engines/tetraedge/game/credits.h
+++ b/engines/tetraedge/game/credits.h
@@ -45,9 +45,9 @@ private:
 	TeTimer _timer;
 	TeLuaGUI _gui;
 	TeCurveAnim2<Te3DObject2, TeColor> _curveAnim;
-	TeColor _col1;
-	TeColor _col2;
 	Common::Array<double> _doubleArr;
+	int _animCounter;
+	bool _returnToOptions; // if true, return to OptionsMenu instead of MainMenu.
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 1df74bf3ead..159f076eb23 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -112,8 +112,8 @@ void Dialog2::load() {
 	Common::SharedPtr<TeCallback0Param<Dialog2>> callbackptr(new TeCallback0Param<Dialog2>(this, &Dialog2::onSkipButton, 20000.0f));
 	dialogBtn->onMouseClickValidated().push_back(callbackptr);
 
-	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
-	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+	TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
+	TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
 	if (!dialogAnimUp || !dialogAnimDown)
 		error("Dialog2::load: didn't get dialogAnimUp or dialogAnimationDown");
 
@@ -152,9 +152,9 @@ bool Dialog2::onMinimumTimeTimer() {
 }
 
 bool Dialog2::onSkipButton() {
-	const TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
+	const TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
 	if (!dialogAnimUp->_runTimer.running()) {
-		const TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+		const TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
 		if (!dialogAnimDown->_runTimer.running()) {
 			startDownAnimation();
 			_music.stop();
@@ -198,16 +198,16 @@ void Dialog2::pushDialog(const Common::String &name, const Common::String &textV
 
 void Dialog2::startDownAnimation() {
 	_minimumTimeTimer.stop();
-	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+	TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
 	dialogAnimDown->play();
 }
 
 void Dialog2::unload() {
 	if (!_gui.loaded())
 		return;
-	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
+	TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
 	dialogAnimUp->stop();
-	TeCurveAnim2<TeLayout,TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
+	TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
 	dialogAnimDown->stop();
 	_music.close();
 	_gui.unload();
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
index 73e2881e1e8..badd29b8a6e 100644
--- a/engines/tetraedge/game/documents_browser.cpp
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -193,8 +193,8 @@ void DocumentsBrowser::showDocument(const Common::String &docName, long startPag
 	TeVector2s32 spriteSize = sprite->_tiledSurfacePtr->_tiledTexture->_totalSize;
 	sprite->setSizeType(RELATIVE_TO_PARENT);
 	TeVector3f32 winSize = app->getMainWindow().size();
-	sprite->setSize(TeVector3f32(1.0, (4.0 / (winSize.y() / winSize.x() * 4.0)) *
-               ((float)spriteSize._y / (float)spriteSize._x), 0.0));
+	sprite->setSize(TeVector3f32(1.0f, (4.0f / (winSize.y() / winSize.x() * 4.0f)) *
+               ((float)spriteSize._y / (float)spriteSize._x), 0.0f));
 	TeScrollingLayout *scroll = _gui1.scrollingLayout("scroll");
 	if (!scroll)
 		error("DocumentsBrowser::showDocument Couldn't fetch scroll object");
@@ -225,9 +225,8 @@ void DocumentsBrowser::unload() {
 			TeLayout *slot = _gui1.layout(pageSlotName);
 			if (!slot)
 				break;
-			for (unsigned int i = 0; i < slot->childCount(); i++) {
-				Te3DObject2 *child = slot->child(i);
-				Document *doc = dynamic_cast<Document *>(child);
+			for (long i = 0; i < slot->childCount(); i++) {
+				Document *doc = dynamic_cast<Document *>(slot->child(i));
 				if (doc)
 					delete doc;
 			}
diff --git a/engines/tetraedge/game/gallery_menu.cpp b/engines/tetraedge/game/gallery_menu.cpp
index 7403f62ec18..2276481ab6c 100644
--- a/engines/tetraedge/game/gallery_menu.cpp
+++ b/engines/tetraedge/game/gallery_menu.cpp
@@ -43,10 +43,10 @@ bool GalleryMenu::onSkipVideoButtonValidated() {
 	Game *game = g_engine->getGame();
 
 	game->stopSound(AMBIENT_SND_BIKE);
-	game->playSound(AMBIENT_SND_BIKE, -1, 0.1);
+	game->playSound(AMBIENT_SND_BIKE, -1, 0.1f);
 
 	game->stopSound(AMBIENT_SND_ENGR);
-	game->playSound(AMBIENT_SND_ENGR, -1, 0.09);
+	game->playSound(AMBIENT_SND_ENGR, -1, 0.09f);
 
 	TeSpriteLayout *video = spriteLayoutChecked("video");
 	video->stop();
@@ -86,10 +86,10 @@ void GalleryMenu::enter() {
 	app->_frontLayout.addChild(menu);
 
 	game->stopSound(AMBIENT_SND_BIKE);
-	game->playSound(AMBIENT_SND_BIKE, -1, 0.1);
+	game->playSound(AMBIENT_SND_BIKE, -1, 0.1f);
 
 	game->stopSound(AMBIENT_SND_ENGR);
-	game->playSound(AMBIENT_SND_ENGR, -1, 0.09);
+	game->playSound(AMBIENT_SND_ENGR, -1, 0.09f);
 
 	TeButtonLayout *btn = buttonLayoutChecked("quitButton");
 	btn->onMouseClickValidated().add(this, &GalleryMenu::onQuitButton);
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index e50ba5db78d..7da4d3f43b4 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -52,7 +52,7 @@ _sceneCharacterVisibleFromLoad(false), _isCharacterWalking(false),
 _lastCharMoveMousePos(0.0f, 0.0f), _randomSoundFinished(false),
 _previousMousePos(-1, -1), _markersVisible(true), _saveRequested(false),
 _gameLoadState(0), _luaShowOwnerError(false), _score(0), _warped(false),
-_firstInventory(true), _loadName("save.xml"), _randomSource("SyberiaGameRandom") {
+_firstInventory(true), _randomSource("SyberiaGameRandom") {
 	for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
 		_objectsTakenBits[i] = false;
 	}
@@ -213,7 +213,7 @@ bool Game::changeWarp2(const Common::String &zone, const Common::String &scene,
 	_warped = false;
 	_movePlayerCharacterDisabled = false;
 	_sceneCharacterVisibleFromLoad = false;
-	// TODO: set another field here (0x3f40 = -1)
+	// TODO? _charMoveMouseEventNo = -1?
 	_isCharacterIdle = true;
 	_isCharacterWalking = false;
 	Common::Path luapath("scenes");
@@ -278,11 +278,11 @@ void Game::enter(bool newgame) {
 	_score = 0;
 	Application *app = g_engine->getApplication();
 	app->visualFade().init();
-	// FIXME: Original puts this click handler at -10000.. but then it never gets hit?
+	// TODO: Original puts this click handler at -10000.. but then it never gets hit?
 	Common::SharedPtr<TeCallback1Param<Game, const Common::Point &>> callbackptr(new TeCallback1Param<Game, const Common::Point &>(this, &Game::onMouseClick, 10000.0f));
 	g_engine->getInputMgr()->_mouseLUpSignal.push_back(callbackptr);
 	_movePlayerCharacterDisabled = false;
-	// TODO? Set character mouse move event no to -1
+	// TODO? Set _charMoveMouseEventNo = -1
 	_isCharacterIdle = true;
 	_sceneCharacterVisibleFromLoad = false;
 	Character::loadSettings("models/ModelsSettings.xml");
@@ -352,11 +352,14 @@ void Game::enter(bool newgame) {
 	_notifier.load();
 }
 
-/*static*/ TeI3DObject2 *Game::findLayoutByName(TeLayout *parent, const Common::String &name) {
-	error("TODO: Implement Game::findLayoutByName - although maybe never used?");
+/*static*/
+TeI3DObject2 *Game::findLayoutByName(TeLayout *parent, const Common::String &name) {
+	// Seems like this is never used?
+	error("TODO: Implement Game::findLayoutByName");
 }
 
-/*static*/ TeSpriteLayout *Game::findSpriteLayoutByName(TeLayout *parent, const Common::String &name) {
+/*static*/
+TeSpriteLayout *Game::findSpriteLayoutByName(TeLayout *parent, const Common::String &name) {
 	if (!parent)
 		return nullptr;
 
@@ -380,7 +383,9 @@ void Game::finishFreemium() {
 
 void Game::finishGame() {
 	Application *app = g_engine->getApplication();
-	app->_finishedGame = true;
+	// FIXME: The original sets this, but then Application::run immediately
+	// returns to the menu.. how does the original wait for the credits to end??
+	//app->_finishedGame = true;
 	_playedTimer.stop();
 	/* Game does this but does nothing with result?
 	if (app->difficulty() == 2) {
@@ -569,8 +574,8 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	TeSpriteLayout *video = _inGameGui.spriteLayout("video");
 	video->setVisible(false);
-	video->_tiledSurfacePtr->_frameAnim.onFinished().remove(this, &Game::onVideoFinished);
-	video->_tiledSurfacePtr->_frameAnim.onFinished().add(this, &Game::onVideoFinished);
+	video->_tiledSurfacePtr->_frameAnim.onStop().remove(this, &Game::onVideoFinished);
+	video->_tiledSurfacePtr->_frameAnim.onStop().add(this, &Game::onVideoFinished);
 
 	TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
 	invbtn->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
@@ -579,9 +584,9 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	const TeVector3f32 winSize = app->getMainWindow().size();
 	if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
-		invbtn->setSize(TeVector3f32(0.12, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.12, 0.0));
+		invbtn->setSize(TeVector3f32(0.12f, (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.12f, 0.0));
 	} else {
-		invbtn->setSize(TeVector3f32(0.08, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.08, 0.0));
+		invbtn->setSize(TeVector3f32(0.08f, (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.08f, 0.0));
 	}
 
 	TeCheckboxLayout *markersCheckbox = _inGameGui.checkboxLayout("markersVisibleButton");
@@ -688,20 +693,42 @@ static const char *DIALOG_IDS[20] = {
 bool Game::launchDialog(const Common::String &dname, uint param_2, const Common::String &charname,
 				  const Common::String &animfile, float animblend) {
 	Application *app = g_engine->getApplication();
-	const Common::String *locdname = app->_loc.value(dname);
-	if (!locdname)
-		locdname = &dname;
+	const Common::String *locstring = app->_loc.value(dname);
+
+	if (!locstring)
+		locstring = &dname;
 
-	if (!locdname)
+	if (!locstring) // shouldn't happen?
 		return false;
 
+	Common::String dstring = *locstring;
+
+	//
+	// WORKAROUND: Fix some errors in en version strings
+	//
+	if (g_engine->getCore()->language() == "en") {
+		if (dname == "C_OK_tel03_09") {
+			size_t rloc = dstring.find("pleased to here");
+			if (rloc != Common::String::npos)
+				dstring.replace(rloc + 11, 4, "hear");
+		} else if (dname == "B_diapo04") {
+			size_t rloc = dstring.find("little imagination ? he draws");
+			if (rloc != Common::String::npos)
+				dstring.replace(rloc + 19, 1, "-");
+		} else if (dname == "V_NK_lettre_01") {
+			size_t rloc = dstring.find("you now ? my brother");
+			if (rloc != Common::String::npos)
+				dstring.replace(rloc + 8, 1, "-");
+		}
+	}
+
 	for (unsigned int i = 0; i < ARRAYSIZE(DIALOG_IDS); i++) {
 		if (dname.contains(Common::String::format("_%s_", DIALOG_IDS[i])))
 			_dialogsTold++;
 	}
 
 	const Common::String sndfile = dname + ".ogg";
-	_dialog2.pushDialog(dname, *locdname, sndfile, charname, animfile, animblend);
+	_dialog2.pushDialog(dname, dstring, sndfile, charname, animfile, animblend);
 	return true;
 }
 
@@ -1061,7 +1088,8 @@ bool Game::onMouseClick(const Common::Point &pt) {
 				_lastCharMoveMousePos = pt;
 			}
 		}
-		// FIXME: The original never checks for empty/null curve here.. why?
+		// FIXME: The original never checks for empty/null curve here..
+		// but it seems our curve can possibly become null.
 		if (_scene.curve() && _scene.curve()->length()) {
 			TeVector3f32 lastPoint = _scene.curve()->controlPoints().back();
 			character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
@@ -1317,7 +1345,8 @@ void Game::playSound(const Common::String &name, int repeats, float volume) {
 		if (!sound->play()) {
 			game->luaScript().execute("OnFreeSoundFinished", name);
 			game->luaScript().execute("OnCellFreeSoundFinished", name);
-			// TODO: original seems to leak sound here??
+			// Note: original leaks sound here, don't do that..
+			delete sound;
 		} else {
 			sound->onStopSignal().add(sound, &GameSound::onSoundStopped);
 			sound->setRetain(true);
@@ -1396,10 +1425,13 @@ void Game::resumeMovie() {
 }
 
 void Game::saveBackup(const Common::String &saveName) {
+	Application *app = g_engine->getApplication();
+	app->showLoadingIcon(true);
 	if (saveName == "save.xml")
 		g_engine->saveAutosaveIfEnabled();
 	else
 		warning("TODO: Implemet Game::saveBackup %s", saveName.c_str());
+	app->showLoadingIcon(false);
 }
 
 bool Game::setBackground(const Common::String &name) {
@@ -1419,7 +1451,7 @@ void Game::setCurrentObjectSprite(const Common::Path &spritePath) {
 
 bool Game::showMarkers(bool val) {
 	TeLayout *bg = _forGui.layoutChecked("background");
-	for (unsigned int i = 0; i < bg->childCount(); i++) {
+	for (long i = 0; i < bg->childCount(); i++) {
 		const InGameScene::TeMarker *marker = _scene.findMarker(bg->child(i)->name());
 		if (marker)
 			bg->child(i)->setVisible(!val);
@@ -1516,7 +1548,7 @@ void Game::update() {
 	if (!_entered)
 		return;
 
-	TeTextLayout *debugTimeTextLayout = _inGameGui.textLayout("debugTimeText1");
+	TeITextLayout *debugTimeTextLayout = _inGameGui.textLayout("debugTimeText1");
 	if (debugTimeTextLayout) {
 		warning("TODO: Game::update: Fill out debugTimeTextLayout");
 	}
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index 2208b0d6b99..36979202b83 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -198,6 +198,7 @@ public:
 	void setExitZone(const Common::String &zone) { _exitZone = zone; }
 	Common::RandomSource &randomSource() { return _randomSource; }
 	void setLoadName(const Common::String &loadName) { _loadName = loadName; }
+	bool hasLoadName() const { return !_loadName.empty(); }
 
 private:
 	bool _luaShowOwnerError;
diff --git a/engines/tetraedge/game/global_bonus_menu.cpp b/engines/tetraedge/game/global_bonus_menu.cpp
index dfabd6a27dc..8bcbec1598b 100644
--- a/engines/tetraedge/game/global_bonus_menu.cpp
+++ b/engines/tetraedge/game/global_bonus_menu.cpp
@@ -39,25 +39,25 @@ void GlobalBonusMenu::enter() {
 
 	// Original checks each layout's existence
 	TeButtonLayout *btn;
-	btn = buttonLayoutChecked("Val");
+	btn = buttonLayout("Val");
 	if (btn)
 		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onValButtonValidated);
-	btn = buttonLayoutChecked("Bar");
+	btn = buttonLayout("Bar");
 	if (btn)
 		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onBarButtonValidated);
-	btn = buttonLayoutChecked("Cit");
+	btn = buttonLayout("Cit");
 	if (btn)
 		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onCitButtonValidated);
-	btn = buttonLayoutChecked("Ara");
+	btn = buttonLayout("Ara");
 	if (btn)
 		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onAraButtonValidated);
-	btn = buttonLayoutChecked("Syb2");
+	btn = buttonLayout("Syb2");
 	if (btn)
 		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onSyb2ButtonValidated);
-	btn = buttonLayoutChecked("Syb3");
+	btn = buttonLayout("Syb3");
 	if (btn)
 		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onSyb3ButtonValidated);
-	btn = buttonLayoutChecked("Back");
+	btn = buttonLayout("Back");
 	if (btn)
 		btn->onMouseClickValidated().add(this, &GlobalBonusMenu::onQuitButton);
 }
diff --git a/engines/tetraedge/game/how_to.cpp b/engines/tetraedge/game/how_to.cpp
index e0448d47923..e313a081f4f 100644
--- a/engines/tetraedge/game/how_to.cpp
+++ b/engines/tetraedge/game/how_to.cpp
@@ -19,7 +19,10 @@
  *
  */
 
+#include "tetraedge/tetraedge.h"
+
 #include "tetraedge/game/how_to.h"
+#include "tetraedge/game/application.h"
 
 namespace Tetraedge {
 
@@ -27,10 +30,20 @@ HowTo::HowTo() : _entered(false) {
 }
 
 void HowTo::leave()	{
-	error("TODO: Implement HowTo::leave");
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	unload();
+	//app->helpOptionMenu().enter();
+	app->fade();
+	_entered = false;
+	error("TODO: Finish HowTo::leave - need app->helpOptionMenu");
 }
 
 void HowTo::enter()	{
+	if (_entered)
+		return;
+
+	_entered = true;
 	error("TODO: Implement HowTo::enter");
 }
 
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 8ee2628edff..082230582b7 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -113,9 +113,9 @@ bool InGameScene::addMarker(const Common::String &markerName, const Common::Stri
 
 		const TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
 		if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
-			markerSprite->setSize(TeVector3f32(0.07, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.07, 0.0));
+			markerSprite->setSize(TeVector3f32(0.07f, (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.07f, 0.0));
 		} else {
-			markerSprite->setSize(TeVector3f32(0.04, (4.0 / ((winSize.y() / winSize.x()) * 4.0)) * 0.04, 0.0));
+			markerSprite->setSize(TeVector3f32(0.04f, (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.04f, 0.0));
 		}
 		markerSprite->setVisible(game->markersVisible());
 		markerSprite->_tiledSurfacePtr->_frameAnim._loopCount = -1;
@@ -137,13 +137,11 @@ bool InGameScene::addMarker(const Common::String &markerName, const Common::Stri
 
 /*static*/
 float InGameScene::angularDistance(float a1, float a2) {
-	float result;
-
-	result = a2 - a1;
-	if (result >= -3.141593 && result > 3.141593) {
-		result = result + -6.283185;
+	float result = a2 - a1;
+	if (result >= -M_PI && result > M_PI) {
+		result = result + -(M_PI * 2);
 	} else {
-		result = result + 6.283185;
+		result = result + (M_PI * 2);
 	}
 	return result;
 }
@@ -489,9 +487,9 @@ float InGameScene::getHeadHorizontalRotation(Character *cter, const TeVector3f32
 	zvec.rotate(cter->_model->rotation());
 	float angle = atan2f(-pos.x(), pos.z()) - atan2f(-zvec.x(), zvec.z());
 	if (angle < -M_PI)
-		angle += (M_PI * 2);
+		angle += (float)(M_PI * 2);
 	else if (angle > M_PI)
-		angle -= (M_PI * 2);
+		angle -= (float)(M_PI * 2);
 	return angle;
 }
 
@@ -1117,11 +1115,11 @@ void InGameScene::update() {
 				targetpos.y() += 17;
 			TeVector2f32 headRot(getHeadHorizontalRotation(_character, targetpos),
 					getHeadVerticalRotation(_character, targetpos));
-			float hangle = headRot.getX() * 180.0 / M_PI;
+			float hangle = headRot.getX() * 180.0f / M_PI;
 			if (hangle > 90.0f)
-				headRot.setX(M_PI_2);
+				headRot.setX((float)M_PI_2);
 			else if (hangle < -90.0f)
-				headRot.setX(-M_PI_2);
+				headRot.setX((float)-M_PI_2);
 			_character->setHeadRotation(headRot);
 			_character->setHasAnchor(true);
 		}
@@ -1133,11 +1131,11 @@ void InGameScene::update() {
 				targetpos.y() += 17;
 			TeVector2f32 headRot(getHeadHorizontalRotation(c, targetpos),
 					getHeadVerticalRotation(c, targetpos));
-			float hangle = headRot.getX() * 180.0 / M_PI;
+			float hangle = headRot.getX() * 180.0f / M_PI;
 			if (hangle > 90)
-				headRot.setX(M_PI_2);
+				headRot.setX((float)M_PI_2);
 			else if (hangle < -90)
-				headRot.setX(-M_PI_2);
+				headRot.setX((float)-M_PI_2);
 			c->setHeadRotation(headRot);
 			c->setHasAnchor(true);
 		}
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index a70d7738648..05169ca366d 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -199,10 +199,10 @@ bool Inventory::addObject(InventoryObject *obj) {
 				TeLayout *slot = _gui.layout(Common::String::format("page%dSlot%d", pageNo, slotNo));
 				if (!slot)
 					break;
-				for (unsigned int c = 0; c < slot->childCount(); c++) {
+				for (long c = 0; c < slot->childCount(); c++) {
 					Te3DObject2 *child = slot->child(c);
-					InventoryObject *obj = dynamic_cast<InventoryObject *>(child);
-					if (obj) {
+					InventoryObject *iobj = dynamic_cast<InventoryObject *>(child);
+					if (iobj) {
 						slot->removeChild(child);
 						c--;
 					}
@@ -215,7 +215,7 @@ bool Inventory::addObject(InventoryObject *obj) {
 
 	int pageno = 0;
 	unsigned int totalSlots = 0;
-	bool retval;
+	bool retval = false;
 	const Common::String newObjName = obj->name();
 	auto invObjIter = _invObjects.begin();
 	bool finished = false;
@@ -238,8 +238,8 @@ bool Inventory::addObject(InventoryObject *obj) {
 
 			TeTextLayout *newText = new TeTextLayout();
 			newText->setSizeType(CoordinatesType::RELATIVE_TO_PARENT);
-			newText->setPosition(TeVector3f32(1.0,1.0,0.0));
-			newText->setSize(TeVector3f32(1.0,1.0,0.0));
+			newText->setPosition(TeVector3f32(1.0, 1.0, 0.0));
+			newText->setSize(TeVector3f32(1.0, 1.0, 0.0));
 			newText->setTextSizeType(1);
 			newText->setTextSizeProportionalToWidth(200);
 			newText->setText(_gui.value("textAttributs").toString() + objectName((*invObjIter)->name()));
@@ -426,7 +426,7 @@ void Inventory::selectedObject(InventoryObject *obj) {
 		_gui.buttonLayoutChecked("lire")->setEnable(isDocument(objId));
 		game->setCurrentObjectSprite(obj->spritePath());
 		TeLayout *textObj = _gui.layout("textObject");
-		for (unsigned int i = 0; i < textObj->childCount(); i++) {
+		for (long i = 0; i < textObj->childCount(); i++) {
 			if (textObj->child(i)->name() == obj->name()) {
 				textObj->setVisible(true);
 				textObj->child(i)->setVisible(true);
diff --git a/engines/tetraedge/game/loc_file.h b/engines/tetraedge/game/loc_file.h
index 31c49104be6..861229b22e4 100644
--- a/engines/tetraedge/game/loc_file.h
+++ b/engines/tetraedge/game/loc_file.h
@@ -37,9 +37,6 @@ public:
 	void load(const Common::Path &path);
 	const Common::String *value(const Common::String &key);
 
-private:
-	// TODO add private members
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
index fec813c658a..243b736fd61 100644
--- a/engines/tetraedge/game/main_menu.cpp
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -118,7 +118,7 @@ void MainMenu::enter() {
 		}
 	}
 	setCenterButtonsVisibility(true);
-	TeTextLayout *versionNum = textLayout("versionNumber");
+	TeITextLayout *versionNum = textLayout("versionNumber");
 	if (versionNum) {
 		const Common::String versionSectionStr("<section style=\"left\" /><color r=\"255\" g=\"255\" b=\"255\"/><font file=\"Common/Fonts/arial.ttf\" size=\"12\" />");
 		versionNum->setText(versionSectionStr + app->getVersionString());
@@ -222,7 +222,12 @@ bool MainMenu::onEnterGameRotateAnimFinished() {
 }
 
 bool MainMenu::onGalleryButtonValidated() {
-	error("TODO: Implement MainMenu::onGalleryButtonValidated");
+	Application *app = g_engine->getApplication();
+	app->captureFade();
+	leave();
+	app->globalBonusMenu().enter();
+	app->fade();
+	return false;
 }
 
 bool MainMenu::onHowToButtonValidated() {
diff --git a/engines/tetraedge/game/notifier.cpp b/engines/tetraedge/game/notifier.cpp
index bd5085f2123..c8dd1c53f4d 100644
--- a/engines/tetraedge/game/notifier.cpp
+++ b/engines/tetraedge/game/notifier.cpp
@@ -55,7 +55,7 @@ void Notifier::launchNextnotifier() {
 	TeVariant textformat = _gui.value("textFormat");
 	Common::String formattedName = Common::String::format(textformat.toString().c_str(), _notifierDataArray[0]._name.c_str());
 
-	TeTextLayout *text = _gui.textLayout("text");
+	TeITextLayout *text = _gui.textLayout("text");
 	text->setText(formattedName);
 
 	if (!_notifierDataArray[0]._imgpath.empty()) {
diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
index acc0f042e87..1f73149148d 100644
--- a/engines/tetraedge/game/objectif.cpp
+++ b/engines/tetraedge/game/objectif.cpp
@@ -180,7 +180,7 @@ void Objectif::update() {
 			}
 		}
 
-		float z = 0.1;
+		float z = 0.1f;
 		for (Te3DObject2 *child : tasks->childList()) {
 			TeTextLayout *text = dynamic_cast<TeTextLayout *>(child);
 			/*TeVector3f32 size =*/
diff --git a/engines/tetraedge/game/question2.h b/engines/tetraedge/game/question2.h
index 7582f06920d..bc171484aae 100644
--- a/engines/tetraedge/game/question2.h
+++ b/engines/tetraedge/game/question2.h
@@ -48,7 +48,7 @@ public:
 	void enter();
 	void leave();
 	void load();
-	bool onBackgroundClick() {}
+	bool onBackgroundClick() { return false; }
 	bool onAnswerValidated(Answer &answer);
 	void pushAnswer(const Common::String &name, const Common::String &unk, const Common::String &path);
 	void unload();
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.cpp b/engines/tetraedge/game/scene_lights_xml_parser.cpp
index d2f1505568a..81043ac87a7 100644
--- a/engines/tetraedge/game/scene_lights_xml_parser.cpp
+++ b/engines/tetraedge/game/scene_lights_xml_parser.cpp
@@ -39,7 +39,7 @@ bool SceneLightsXmlParser::parseCol(ParserNode *node, TeColor &colout) {
 	else
 		a = 0xff;
 
-	if (r > 255 || g > 255 || b > 255 | a > 255) {
+	if (r > 255 || g > 255 || b > 255 || a > 255) {
 		parserError("Invalid color values");
 		return false;
 	}
@@ -47,7 +47,6 @@ bool SceneLightsXmlParser::parseCol(ParserNode *node, TeColor &colout) {
 	return true;
 }
 
-
 bool SceneLightsXmlParser::parserCallback_Ambient(ParserNode *node) {
 	// can appear under either global or light
 	TeColor col;
diff --git a/engines/tetraedge/game/splash_screens.cpp b/engines/tetraedge/game/splash_screens.cpp
index 96773bf6d47..28dad2adcf4 100644
--- a/engines/tetraedge/game/splash_screens.cpp
+++ b/engines/tetraedge/game/splash_screens.cpp
@@ -25,6 +25,7 @@
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/application.h"
+#include "tetraedge/game/game.h"
 #include "tetraedge/game/splash_screens.h"
 
 namespace Tetraedge {
@@ -87,7 +88,11 @@ bool SplashScreens::onQuitSplash() {
 	app->captureFade();
 	TeLuaGUI::unload();
 	_entered = false;
-	app->mainMenu().enter();
+	if (!g_engine->getGame()->hasLoadName()) {
+		app->mainMenu().enter();
+	} else {
+		app->startGame(false, 1);
+	}
 	app->fade();
 	return false;
 }
diff --git a/engines/tetraedge/metaengine.cpp b/engines/tetraedge/metaengine.cpp
index d4d1fb4b9e7..f88e5240f60 100644
--- a/engines/tetraedge/metaengine.cpp
+++ b/engines/tetraedge/metaengine.cpp
@@ -39,12 +39,12 @@ bool TetraedgeMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSupportsListSaves) ||
 	    (f == kSupportsDeleteSave) ||
 	    (f == kSavesSupportMetaInfo) ||
-	   // (f == kSavesSupportThumbnail) || // TODO: support save thumbnail
+	    (f == kSavesSupportThumbnail) ||
 	    (f == kSupportsLoadingDuringStartup);
 }
 
 void TetraedgeMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
-	thumb.create(320, 200, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+	Tetraedge::TetraedgeEngine::getSavegameThumbnail(thumb);
 }
 
 
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index bdb10afc5c1..12de439554e 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -59,6 +59,7 @@ MODULE_OBJS := \
 	te/te_i_3d_object2.o \
 	te/te_i_layout.o \
 	te/te_i_loc.o \
+	te/te_i_text_layout.o \
 	te/te_image.o \
 	te/te_images_sequence.o \
 	te/te_input_mgr.o \
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index bd9ee1b3575..28fb1cdb3cb 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -64,6 +64,15 @@ void Te3DTexture::copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y)
 	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, _texWidth, _texHeight);
 }
 
+void Te3DTexture::writeTo(Graphics::Surface &surf) {
+	Graphics::Surface fullTex;
+	fullTex.create(_texWidth, _texHeight, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullTex.getPixels());
+	surf.create(_width, _height, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+	surf.copyRectToSurface(fullTex, 0, 0, Common::Rect(_width, _height));
+	fullTex.free();
+}
+
 void Te3DTexture::create() {
 	_flipY = false;
 	_leftBorder = _btmBorder = _texWidth = _texHeight = 0;
diff --git a/engines/tetraedge/te/te_3d_texture.h b/engines/tetraedge/te/te_3d_texture.h
index bba5ad26975..2a37d0ad7c6 100644
--- a/engines/tetraedge/te/te_3d_texture.h
+++ b/engines/tetraedge/te/te_3d_texture.h
@@ -59,6 +59,8 @@ public:
 	bool unload();
 	void update(const TeImage &img, uint xoff, uint yoff);
 
+	void writeTo(Graphics::Surface &surf);
+
 	int _numFrames;
 	int _frameRate;
 	uint _width;
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index 93db4cc9fc7..1b935322932 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -39,21 +39,24 @@ public:
 	float _pri;
 };
 
-/*static*/ bool TeButtonLayout::_mousePositionChangedCatched = false;
-/*static*/ TeTimer *TeButtonLayout::_doubleValidationProtectionTimer = nullptr;
+/*static*/
+bool TeButtonLayout::_mousePositionChangedCatched = false;
+/*static*/
+TeTimer *TeButtonLayout::_doubleValidationProtectionTimer = nullptr;
 
-/*static*/ TeTimer *TeButtonLayout::getDoubleValidationProtectionTimer() {
+/*static*/
+TeTimer *TeButtonLayout::getDoubleValidationProtectionTimer() {
 	if (!_doubleValidationProtectionTimer) {
 		_doubleValidationProtectionTimer = new TeTimer();
 	}
 	return _doubleValidationProtectionTimer;
 }
 
-TeButtonLayout::TeButtonLayout() :
-_currentState(BUTTON_STATE_UP), _clickPassThrough(false), _validationSoundVolume(1.0),
-_ignoreMouseEvents(false), _doubleValidationProtectionEnabled(true), _upLayout(nullptr),
-_downLayout(nullptr), _rolloverLayout(nullptr), _disabledLayout(nullptr),
-_hitZoneLayout(nullptr)
+TeButtonLayout::TeButtonLayout() : _currentState(BUTTON_STATE_UP),
+_clickPassThrough(false), _validationSoundVolume(1.0),
+_ignoreMouseEvents(false), _doubleValidationProtectionEnabled(true),
+_upLayout(nullptr), _downLayout(nullptr), _rolloverLayout(nullptr),
+_disabledLayout(nullptr), _hitZoneLayout(nullptr)
 {
 	_onMousePositionChangedMaxPriorityCallback.reset(new TeCallback1Param<TeButtonLayout, const Common::Point &>(this, &TeButtonLayout::onMousePositionChangedMaxPriority, FLT_MAX));
 
diff --git a/engines/tetraedge/te/te_button_layout.h b/engines/tetraedge/te/te_button_layout.h
index 908e73ff1ab..66c3b03481e 100644
--- a/engines/tetraedge/te/te_button_layout.h
+++ b/engines/tetraedge/te/te_button_layout.h
@@ -86,6 +86,7 @@ public:
 	}
 
 	void setState(State newState);
+	State state() const { return _currentState; };
 
 	TeSignal0Param &onMouseClickValidated() { return _onMouseClickValidatedSignal; };
 	TeSignal0Param &onButtonChangedToStateUpSignal() { return _onButtonChangedToStateUpSignal; };
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index eee6d0af931..969aff846a9 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -214,14 +214,14 @@ TeMatrix4x4 TeCamera::transformationMatrix() {
 		return Te3DObject2::transformationMatrix();
 
 	TeMatrix4x4 retval;
-	warning("TODO: TeCamera::transformationMatrix Implement me.");
+	warning("TODO: Implement TeCamera::transformationMatrix");
 
 	retval.setToIdentity();
 	return retval;
 }
 
 TeVector3f32 TeCamera::transformCoord(const TeVector3f32 &pt) {
-	warning("TODO: TeCamera::transformCoord Implement me.");
+	warning("TODO: Implement TeCamera::transformCoord");
 	return pt;
 }
 
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index f85f50ac040..cc925988fbe 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -85,17 +85,17 @@ void TeCore::fileFlagSystemSetFlag(const Common::String &name, const Common::Str
 }
 
 bool TeCore::fileFlagSystemFlagsContains(const Common::String &name) const {
-	// TODO: Implement me
+	error("TODO: Implement TeCore::fileFlagSystemFlagsContains");
 	return false;
 }
 
 Common::Array<Common::String> TeCore::fileFlagSystemPossibleFlags() {
-	// TODO: Implement me
+	error("TODO: Implement TeCore::fileFlagSystemPossibleFlags");
 	return Common::Array<Common::String>();
 }
 
 bool TeCore::fileFlagSystemPossibleFlagsContains(const Common::String &name) const {
-	// TODO: Implement me
+	error("TODO: Implement TeCore::fileFlagSystemPossibleFlagsContains");
 	return false;
 }
 
@@ -108,7 +108,7 @@ void TeCore::language(const Common::String &val) {
 }
 
 bool TeCore::onActivityTrackingAlarm() {
-	error("TODO: Implement me");
+	error("TODO: Implement TeCore::onActivityTrackingAlarm");
 }
 
 
@@ -147,16 +147,32 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 		"de-es-fr-it-en"
 	};
 
+	// Note: the audio files for a few videos have a weird path
+	// structure where the language is first, followed by some other
+	// part names, followed by the file.
+	// Dialogs have part stuff followed by lang, so we have to try
+	// adding language before *and* after the suffix.
+
 	for (int langtype = 0; langtype < ARRAYSIZE(langs); langtype++) {
+		const Common::Path &lang = langs[langtype];
 		for (int i = 0; i < ARRAYSIZE(pathSuffixes); i++) {
-			Common::Path testPath = dir.join(pathSuffixes[i]);
-			if (!langs[langtype].empty()) {
-				testPath.joinInPlace(langs[langtype]);
-			}
+			const Common::Path &suffix = pathSuffixes[i];
+
+			Common::Path testPath = dir;
+			if (!suffix.empty())
+				testPath.joinInPlace(suffix);
+			if (!lang.empty())
+				testPath.joinInPlace(lang);
 			testPath.joinInPlace(fname);
-			//debug("check for %s", testPath.toString());
 			if (Common::File::exists(testPath) || Common::FSNode(testPath).exists())
 				return testPath;
+
+			// also try the other way around
+ 			if (!lang.empty() && !suffix.empty()) {
+				testPath = dir.join(lang).joinInPlace(suffix).join(fname);
+				if (Common::File::exists(testPath) || Common::FSNode(testPath).exists())
+					return testPath;
+			}
 		}
 	}
 
diff --git a/engines/tetraedge/te/te_extended_text_layout.cpp b/engines/tetraedge/te/te_extended_text_layout.cpp
index d355b761fb8..8ee803d92ec 100644
--- a/engines/tetraedge/te/te_extended_text_layout.cpp
+++ b/engines/tetraedge/te/te_extended_text_layout.cpp
@@ -24,6 +24,26 @@
 namespace Tetraedge {
 
 TeExtendedTextLayout::TeExtendedTextLayout() {
+	_textLayout.setSizeType(RELATIVE_TO_PARENT);
+	_textLayout.setAnchor(TeVector3f32(0.5, 0.0, 0.0));
+	_textLayout.setPosition(TeVector3f32(0.5, 0.0, 0.0));
+	const TeVector3f32 usersz = userSize();
+	_textLayout.setSize(TeVector3f32(1.0, 1.0, usersz.z()));
+	_scrollingLayout.setContentLayout(&_textLayout);
+	_scrollingLayout.setSizeType(RELATIVE_TO_PARENT);
+	_scrollingLayout.setSize(TeVector3f32(1.0, 1.0, usersz.z()));
+	_scrollingLayout.setDirection(TeVector3f32(0.0, 1.0, 0.0));
+	_scrollingLayout.setMouseControl(false);
+	_scrollingLayout.setEnclose(true);
+	_scrollingLayout.setAutoScrollLoop(1);
+	_scrollingLayout.setAutoScrollDelay(4000);
+	_scrollingLayout.setAutoScrollAnimation1Enabled(true);
+	_scrollingLayout.setAutoScrollAnimation1Delay(0);
+	_scrollingLayout.setAutoScrollAnimation1Speed(0.1f);
+	_scrollingLayout.setAutoScrollAnimation2Enabled(false);
+	_scrollingLayout.setAutoScrollAnimation2Delay(0);
+	_scrollingLayout.setAutoScrollAnimation2Speed(0.1f);
+	addChild(&_scrollingLayout);
 }
 
 void TeExtendedTextLayout::setAutoScrollDelay(int val) {
@@ -35,5 +55,38 @@ void TeExtendedTextLayout::setAutoScrollSpeed(float val) {
 	_scrollingLayout.setAutoScrollAnimation2Speed(val);
 }
 
+void TeExtendedTextLayout::setText(const Common::String &val) {
+	_textLayout.setText(val);
+	_scrollingLayout.resetScrollPosition();
+	_scrollingLayout.playAutoScroll();
+}
+
+void TeExtendedTextLayout::setInterLine(float val) {
+	_textLayout.setInterLine(val);
+}
+
+void TeExtendedTextLayout::setWrapMode(TeTextBase2::WrapMode mode) {
+	if (mode == TeTextBase2::WrapModeFixed) {
+		_textLayout.setAnchor(TeVector3f32(0.5f, 0.0f, 0.0f));
+		_textLayout.setPosition(TeVector3f32(0.5f, 0.0f, 0.0f));
+		_scrollingLayout.setDirection(TeVector3f32(0.0, 1.0, 0.0));
+	} else {
+		_textLayout.setAnchor(TeVector3f32(0.0f, 0.5f, 0.0f));
+		_textLayout.setPosition(TeVector3f32(0.0f, 0.5f, 0.0f));
+		_scrollingLayout.setDirection(TeVector3f32(1.0, 0.0, 0.0));
+	}
+	_scrollingLayout.setContentLayout(nullptr);
+	_scrollingLayout.setContentLayout(&_textLayout);
+	_textLayout.setWrapMode(mode);
+}
+
+void TeExtendedTextLayout::setTextSizeType(int type) {
+	_textLayout.setTextSizeType(type);
+}
+
+void TeExtendedTextLayout::setTextSizeProportionalToWidth(int val) {
+	_textLayout.setTextSizeProportionalToWidth(val);
+}
+
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_extended_text_layout.h b/engines/tetraedge/te/te_extended_text_layout.h
index 12fdd8a4f0a..82091ddf13c 100644
--- a/engines/tetraedge/te/te_extended_text_layout.h
+++ b/engines/tetraedge/te/te_extended_text_layout.h
@@ -22,24 +22,28 @@
 #ifndef TETRAEDGE_TE_TE_EXTENDED_TEXT_LAYOUT_H
 #define TETRAEDGE_TE_TE_EXTENDED_TEXT_LAYOUT_H
 
+#include "tetraedge/te/te_i_text_layout.h"
 #include "tetraedge/te/te_text_layout.h"
 #include "tetraedge/te/te_scrolling_layout.h"
 
 namespace Tetraedge {
 
-class TeExtendedTextLayout : public TeTextLayout {
+class TeExtendedTextLayout : public TeITextLayout {
 public:
 	TeExtendedTextLayout();
 
 	void setAutoScrollDelay(int val);
 	void setAutoScrollSpeed(float val);
 
-	// TODO add public members
+	void setText(const Common::String &val) override;
+	void setInterLine(float val) override;
+	void setWrapMode(TeTextBase2::WrapMode mode) override;
+	void setTextSizeType(int type) override;
+	void setTextSizeProportionalToWidth(int val) override;
 
 private:
 	TeScrollingLayout _scrollingLayout;
-	// TODO add private members
-
+	TeTextLayout _textLayout;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_font3.cpp b/engines/tetraedge/te/te_font3.cpp
index 9c6e33a221f..718084a7978 100644
--- a/engines/tetraedge/te/te_font3.cpp
+++ b/engines/tetraedge/te/te_font3.cpp
@@ -133,10 +133,10 @@ void TeFont3::draw(TeImage &destImage, const Common::String &str, int fontSize,
 		case AlignRight:
 			talign = Graphics::kTextAlignRight;
 			break;
+		// Note: we don't support justify.. just center. (justify is not used anyway)
 		case AlignJustify:
-			talign = Graphics::kTextAlignCenter;
-			break;
 		case AlignCenter:
+		default:
 			talign = Graphics::kTextAlignCenter;
 			break;
 	}
@@ -187,7 +187,7 @@ float TeFont3::ascender(unsigned int pxSize) {
 }
 
 float TeFont3::descender(unsigned int pxSize) {
-	error("TeFont3::descender: Implement me.");
+	error("TODO: Implement TeFont3::descender");
 }
 
 float TeFont3::height(unsigned int pxSize) {
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 81e769b2680..212827f9c75 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -147,7 +147,7 @@ void TeFreeMoveZone::clear() {
 	_projectedPointsDirty = true;
 	_transformedVerticies.clear();
 	_borders.clear();
-	// TODO: Some other point vector here.
+	// TODO: Clear some other TeVector2f32 list here (field_0x178)
 	_gridDirty = true;
 	_graph->_flags.clear();
 	_graph->_size = TeVector2s32(0, 0);
@@ -327,7 +327,7 @@ static int segmentIntersection(const TeVector2f32 &s1start, const TeVector2f32 &
 }
 
 
-bool TeFreeMoveZone::hasBlockerIntersection(const TeVector2s32 &pt) {
+byte TeFreeMoveZone::hasBlockerIntersection(const TeVector2s32 &pt) {
 	TeVector2f32 borders[4];
 
 	const float gridOffsetX = _gridOffsetSomething.getX();
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index 9f6dcc6442a..ec8cde052e4 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -76,7 +76,7 @@ public:
 
 	void draw() override;
 	TeVector3f32 findNearestPointOnBorder(const TeVector2f32 &pt);
-	bool hasBlockerIntersection(const TeVector2s32 &pt);
+	byte hasBlockerIntersection(const TeVector2s32 &pt);
 	bool hasCellBorderIntersection(const TeVector2s32 &pt);
 
 	TeActZone *isInZone(const TeVector3f32 &pt);
@@ -99,7 +99,7 @@ public:
 	void setVertex(unsigned int offset, const TeVector3f32 &vertex);
 	TeVector3f32 transformAStarGridInWorldSpace(const TeVector2s32 &gridpt);
 	float transformHeightMin(float minval);
-	TeVector3f32 transformVectorInWorldSpace(float param_3,float param_4);
+	TeVector3f32 transformVectorInWorldSpace(float param_3, float param_4);
 	void updateBorders();
 	void updateGrid(bool force);
 	void updatePickMesh();
diff --git a/engines/tetraedge/te/te_i_3d_object2.cpp b/engines/tetraedge/te/te_i_3d_object2.cpp
index 457b316b15a..6ae88f59c61 100644
--- a/engines/tetraedge/te/te_i_3d_object2.cpp
+++ b/engines/tetraedge/te/te_i_3d_object2.cpp
@@ -26,6 +26,4 @@ namespace Tetraedge {
 TeI3DObject2::TeI3DObject2() {
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_i_3d_object2.h b/engines/tetraedge/te/te_i_3d_object2.h
index de922feb320..d5373828025 100644
--- a/engines/tetraedge/te/te_i_3d_object2.h
+++ b/engines/tetraedge/te/te_i_3d_object2.h
@@ -29,12 +29,6 @@ public:
 	TeI3DObject2();
 
 	virtual ~TeI3DObject2() {}
-
-	// TODO add public members
-
-private:
-	// TODO add private members
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_i_text_layout.cpp b/engines/tetraedge/te/te_i_text_layout.cpp
new file mode 100644
index 00000000000..abf312df3a2
--- /dev/null
+++ b/engines/tetraedge/te/te_i_text_layout.cpp
@@ -0,0 +1,31 @@
+/* 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/te/te_i_text_layout.h"
+
+namespace Tetraedge {
+
+TeITextLayout::TeITextLayout() {
+}
+
+// TODO: Add more functions here.
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_i_text_layout.h b/engines/tetraedge/te/te_i_text_layout.h
new file mode 100644
index 00000000000..bdb987f150a
--- /dev/null
+++ b/engines/tetraedge/te/te_i_text_layout.h
@@ -0,0 +1,44 @@
+/* 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_TE_TE_I_TEXT_LAYOUT_H
+#define TETRAEDGE_TE_TE_I_TEXT_LAYOUT_H
+
+#include "tetraedge/te/te_layout.h"
+#include "tetraedge/te/te_text_base2.h"
+
+namespace Tetraedge {
+
+class TeITextLayout : public TeLayout {
+public:
+	TeITextLayout();
+
+	virtual void setText(const Common::String &val) = 0;
+	virtual void setInterLine(float val) = 0;
+	virtual void setWrapMode(TeTextBase2::WrapMode mode) = 0;
+	virtual void setTextSizeType(int type) = 0;
+	virtual void setTextSizeProportionalToWidth(int val) = 0;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // TETRAEDGE_TE_TE_I_TEXT_LAYOUT_H
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index c25def49905..49deda8b37e 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -32,15 +32,15 @@ TeImage::TeImage() : ManagedSurface(), _format(INVALID) {
 }
 
 TeImage::TeImage(const TeImage &other) {
-	error("TODO: TeImage:: copy constructor Implement me.");
+	error("TODO: Implement TeImage::TeImage copy constructor");
 }
 
 void TeImage::copy(TeImage &dest, const TeVector2s32 &vec1, const TeVector2s32 &vec2, const TeVector2s32 &vec3) const {
-	error("TODO: TeImage::copy Implement me.");
+	error("TODO: Implement TeImage::copy");
 }
 
 unsigned long TeImage::countPixelsOfColor(const TeColor &col) const {
-	error("TODO: TeImage: Implement me.");
+	error("TODO: Implement TeImage::countPixelsOfColor");
 }
 
 void TeImage::create() {
@@ -59,7 +59,7 @@ void TeImage::create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
 }
 
 void TeImage::deserialize(Common::ReadStream &stream) {
-	error("TODO: TeImage: Implement me.");
+	error("TODO: Implement TeImage::deserialize");
 }
 
 void TeImage::destroy() {
@@ -68,11 +68,11 @@ void TeImage::destroy() {
 }
 
 void TeImage::drawPlot(void *outbuf, int x, int y, const TeVector2s32 &bufsize, const TeColor &col) {
-	error("TODO: TeImage: Implement me.");
+	error("TODO: Implement TeImage::drawPlot");
 }
 
 void TeImage::fill(byte val) {
-	error("TODO: TeImage: Implement me.");
+	error("TODO: Implement TeImage::fill");
 }
 
 void TeImage::fill(byte r, byte g, byte b, byte a) {
@@ -83,11 +83,11 @@ void TeImage::fill(byte r, byte g, byte b, byte a) {
 }
 
 void TeImage::getBuff(uint x, uint y, byte *pout, uint w_, uint h_) {
-	error("TODO: TeImage: Implement me.");
+	error("TODO: Implement TeImage::getBuff");
 }
 
 bool TeImage::isExtensionSupported(const Common::Path &path) {
-	error("TODO: TeImage: Implement me.");
+	error("TODO: Implement TeImage::isExtensionSupported");
 }
 
 bool TeImage::load(const Common::Path &path) {
@@ -108,15 +108,15 @@ bool TeImage::load(const Common::Path &path) {
 }
 
 bool TeImage::load(Common::ReadStream &stream, const Common::Path &path) {
-	error("TODO: TeImage::load Implement me.");
+	error("TODO: Implement TeImage::load");
 }
 
 bool TeImage::save(const Common::Path &path, enum Type type) {
-	error("TODO: TeImage::save Implement me.");
+	error("TODO: Implement TeImage::save");
 }
 
 int TeImage::serialize(Common::WriteStream &stream) {
-	error("TODO: TeImage::serialize Implement me.");
+	error("TODO: Implement TeImage::serialize");
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_layout.cpp b/engines/tetraedge/te/te_layout.cpp
index f8e4e406e8b..3fde8410e1b 100644
--- a/engines/tetraedge/te/te_layout.cpp
+++ b/engines/tetraedge/te/te_layout.cpp
@@ -396,7 +396,7 @@ void TeLayout::updateSize() {
 						newSize.x() = _ratio * newSize.y();
 					else
 						newSize.y() = newSize.x() / _ratio;
-			  }
+				}
 			}
 
 			_size.x() = newSize.x();
diff --git a/engines/tetraedge/te/te_light.cpp b/engines/tetraedge/te/te_light.cpp
index 55d42947e30..74544399728 100644
--- a/engines/tetraedge/te/te_light.cpp
+++ b/engines/tetraedge/te/te_light.cpp
@@ -76,7 +76,7 @@ void TeLight::draw(TeCamera &camera) {
 	error("TODO: Finish TeLight::draw");
 }
 
-void TeLight::transformDirPoint(const TeVector3f32 &pt1,TeVector3f32 &pt2) {
+void TeLight::transformDirPoint(const TeVector3f32 &pt1, TeVector3f32 &pt2) {
 	const TeQuaternion q1 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 1, 0), _positionRadial.getX() + M_PI);
 	const TeQuaternion q2 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 0, -1), -_positionRadial.getY());
 	pt2.rotate(q2);
@@ -95,39 +95,29 @@ void TeLight::transformSpotPoint(TeVector3f32 &pt) {
 void TeLight::update(uint lightno) {
 	if (lightno > GL_MAX_LIGHTS)
 		error("Invalid light no %d", lightno);
-	float col[4];
 	const uint glLight = _toGlLight(lightno);
-	col[0] = _colAmbient.r() / 255.0f;
-	col[1] = _colAmbient.g() / 255.0f;
-	col[2] = _colAmbient.b() / 255.0f;
-	col[3] = 1.0;
-	glLightfv(glLight, GL_AMBIENT, col);
-
-	col[0] = _colDiffuse.r() / 255.0f;
-	col[1] = _colDiffuse.g() / 255.0f;
-	col[2] = _colDiffuse.b() / 255.0f;
-	col[3] = 1.0;
-	glLightfv(glLight, GL_DIFFUSE, col);
+
+	const float ambient[4] = {_colAmbient.r() / 255.0f, _colAmbient.g() / 255.0f,
+			_colAmbient.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_AMBIENT, ambient);
+
+	const float diff[4] = {_colDiffuse.r() / 255.0f, _colDiffuse.g() / 255.0f,
+			_colDiffuse.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_DIFFUSE, diff);
 
 	// WORKAROUND: Original game sets 0.01 as threshold here to avoid enabling
-	// the "shadow" light.  However, CitStation/31130 has shadowlight with values
-	// 4,0,0 which means it gets enabled.
+	// the "shadow" light.  However, Syberia CitStation/31130 has shadowlight with
+	// values (4, 0, 0) which means it gets enabled and everything is dark.
 
-	if (col[0] < 0.02f && col[1] < 0.02f && col[2] < 0.02f)
+	if (diff[0] < 0.02f && diff[1] < 0.02f && diff[2] < 0.02f)
 		glDisable(glLight);
 
-	col[0] = _colSpecular.r() / 255.0f;
-	col[1] = _colSpecular.g() / 255.0f;
-	col[2] = _colSpecular.b() / 255.0f;
-	col[3] = 1.0;
-	glLightfv(glLight, GL_SPECULAR, col);
+	const float spec[4] = {_colSpecular.r() / 255.0f, _colSpecular.g() / 255.0f,
+			_colSpecular.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_SPECULAR, spec);
 
 	if (_type == LightTypeSpot || _type == LightTypePoint) {
-		float pos[4];
-		pos[0] = _position3d.x();
-		pos[1] = _position3d.y();
-		pos[2] = _position3d.z();
-		pos[3] = 1.0f;
+		const float pos[4] = {_position3d.x(), _position3d.y(), _position3d.z(), 1.0f};
 		glLightfv(glLight, GL_POSITION, pos);
 		glLightf(glLight, GL_CONSTANT_ATTENUATION, _constAtten);
 		glLightf(glLight, GL_LINEAR_ATTENUATION, _linearAtten);
@@ -135,28 +125,20 @@ void TeLight::update(uint lightno) {
 	}
 
 	if (_type == LightTypeDirectional) {
-		float pos[4];
 		float cosx = cosf(_positionRadial.getX());
 		float cosy = cosf(_positionRadial.getY());
 		float sinx = sinf(_positionRadial.getX());
 		float siny = sinf(_positionRadial.getY());
-		pos[0] = cosx * cosy;
-		pos[1] = siny;
-		pos[2] = sinx * cosy;
-		pos[3] = 0.0;
+		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
 		glLightfv(glLight, GL_POSITION, pos);
 	}
 
 	if (_type == LightTypeSpot) {
-		float pos[4];
 		float cosx = cosf(_positionRadial.getX());
 		float cosy = cosf(_positionRadial.getY());
 		float sinx = sinf(_positionRadial.getX());
 		float siny = sinf(_positionRadial.getY());
-		pos[0] = cosx * cosy;
-		pos[1] = siny;
-		pos[2] = sinx * cosy;
-		pos[3] = 0.0;
+		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
 		glLightfv(glLight, GL_SPOT_DIRECTION, pos);
 		glLightf(glLight, GL_SPOT_CUTOFF, (_cutoff * 180.0) / M_PI);
 		glLightf(glLight, GL_SPOT_EXPONENT, _exponent);
@@ -167,11 +149,8 @@ void TeLight::update(uint lightno) {
 
 /*static*/
 void TeLight::updateGlobal() {
-	float col[4];
-	col[0] = _globalAmbientColor.r() / 255.0f;
-	col[1] = _globalAmbientColor.g() / 255.0f;
-	col[2] = _globalAmbientColor.b() / 255.0f;
-	col[3] = 1.0;
+	const float col[4] = {_globalAmbientColor.r() / 255.0f,
+			_globalAmbientColor.g() / 255.0f, _globalAmbientColor.b() / 255.0f, 1.0};
 	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
 }
 
diff --git a/engines/tetraedge/te/te_list_layout.cpp b/engines/tetraedge/te/te_list_layout.cpp
index 9b109879a49..9678ff8928b 100644
--- a/engines/tetraedge/te/te_list_layout.cpp
+++ b/engines/tetraedge/te/te_list_layout.cpp
@@ -26,6 +26,4 @@ namespace Tetraedge {
 TeListLayout::TeListLayout() {
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_lua_context.cpp b/engines/tetraedge/te/te_lua_context.cpp
index 15b7c8b880c..c7a928cfe12 100644
--- a/engines/tetraedge/te/te_lua_context.cpp
+++ b/engines/tetraedge/te/te_lua_context.cpp
@@ -33,7 +33,7 @@ namespace Tetraedge {
 
 static int luaPanicFunction(lua_State *state) {
 	const char *msg = lua_tolstring(state, -1, nullptr);
-	warning("Lua: %s",msg);
+	warning("Lua: %s", msg);
 	lua_settop(state, -2);
 	return 1;
 }
@@ -69,7 +69,7 @@ TeVariant TeLuaContext::global(const Common::String &name) {
 	TeVariant retval;
 	int type = lua_type(_luaState, -1);
 	if (type == LUA_TBOOLEAN) {
-		int result = lua_toboolean(_luaState,-1);
+		int result = lua_toboolean(_luaState, -1);
 		lua_settop(_luaState, -2);
 		return TeVariant(result > 0);
 	} else if (type == LUA_TNUMBER) {
diff --git a/engines/tetraedge/te/te_lua_gui.cpp b/engines/tetraedge/te/te_lua_gui.cpp
index 26e60c63ee5..84d30ee5c4a 100644
--- a/engines/tetraedge/te/te_lua_gui.cpp
+++ b/engines/tetraedge/te/te_lua_gui.cpp
@@ -110,7 +110,7 @@ TeCurveAnim2<TeLayout, TeVector3f32> *TeLuaGUI::layoutAnchorLinearAnimation(cons
 	return _layoutAnchorLinearAnimations.getVal(name);
 }
 
-TeCurveAnim2<TeI3DObject2, TeVector3f32> *TeLuaGUI::layoutPositionLinearAnimation(const Common::String &name) {
+TeCurveAnim2<TeLayout, TeVector3f32> *TeLuaGUI::layoutPositionLinearAnimation(const Common::String &name) {
 	return _layoutPositionLinearAnimations.getVal(name);
 }
 
@@ -122,7 +122,7 @@ TeListLayout *TeLuaGUI::listLayout(const Common::String &name) {
 }
 
 TeCurveAnim2<TeI3DObject2, TeQuaternion> *TeLuaGUI::rotationLinearAnimation(const Common::String &name) {
-	error("TODO: Implement me.");
+	error("TODO: Implement TeLuaGUI::rotationLinearAnimation.");
 	return nullptr;
 }
 
@@ -140,7 +140,7 @@ TeSpriteLayout *TeLuaGUI::spriteLayout(const Common::String &name) {
 	return nullptr;
 }
 
-TeTextLayout *TeLuaGUI::textLayout(const Common::String &name) {
+TeITextLayout *TeLuaGUI::textLayout(const Common::String &name) {
 	StringMap<TeTextLayout *>::iterator iter = _textLayouts.find(name);
 	if (iter != _textLayouts.end())
 		return iter->_value;
@@ -206,7 +206,7 @@ bool TeLuaGUI::load(const Common::Path &path_) {
 	_luaContext.registerCFunction("TeRotationLinearAnimation", rotationLinearAnimationBindings);
 	_luaContext.registerCFunction("TeScrollingLayout", scrollingLayoutBindings);
 	_luaContext.registerCFunction("TeExtendedTextLayout", extendedTextLayoutBindings);
-	_luaContext.setInRegistry("__TeLuaGUIThis",this);
+	_luaContext.setInRegistry("__TeLuaGUIThis", this);
 	_luaScript.attachToContext(&_luaContext);
 	_luaScript.load(path);
 	_luaScript.execute();
diff --git a/engines/tetraedge/te/te_lua_gui.h b/engines/tetraedge/te/te_lua_gui.h
index 54e272f57a9..d5eccc4efbf 100644
--- a/engines/tetraedge/te/te_lua_gui.h
+++ b/engines/tetraedge/te/te_lua_gui.h
@@ -33,6 +33,7 @@
 #include "tetraedge/te/te_extended_text_layout.h"
 #include "tetraedge/te/te_i_3d_object2.h"
 #include "tetraedge/te/te_i_layout.h"
+#include "tetraedge/te/te_i_text_layout.h"
 #include "tetraedge/te/te_layout.h"
 #include "tetraedge/te/te_list_layout.h"
 #include "tetraedge/te/te_lua_context.h"
@@ -64,12 +65,12 @@ public:
 	TeCurveAnim2<Te3DObject2, TeColor> *colorLinearAnimation(const Common::String &name);
 	TeExtendedTextLayout *extendedTextLayout(const Common::String &name);
 	TeCurveAnim2<TeLayout, TeVector3f32> *layoutAnchorLinearAnimation(const Common::String &name);
-	TeCurveAnim2<TeI3DObject2, TeVector3f32> *layoutPositionLinearAnimation(const Common::String &name);
+	TeCurveAnim2<TeLayout, TeVector3f32> *layoutPositionLinearAnimation(const Common::String &name);
 	TeListLayout *listLayout(const Common::String &name);
 	TeCurveAnim2<TeI3DObject2, TeQuaternion> *rotationLinearAnimation(const Common::String &name);
 	TeScrollingLayout *scrollingLayout(const Common::String &name);
 	TeSpriteLayout *spriteLayout(const Common::String &name);
-	TeTextLayout *textLayout(const Common::String &name);
+	TeITextLayout *textLayout(const Common::String &name);
 
 	// Same as the above functions, but call error() if the result is null.
 	TeLayout *layoutChecked(const Common::String &name);
@@ -94,7 +95,7 @@ public:
 	StringMap<TeClipLayout *> &clipLayouts() { return _clipLayouts; }
 	StringMap<TeExtendedTextLayout *> &extendedTextLayouts() { return _extendedTextLayouts; }
 	StringMap<TeCurveAnim2<TeLayout, TeVector3f32> *> &layoutAnchorLinearAnimations() { return _layoutAnchorLinearAnimations; }
-	StringMap<TeCurveAnim2<TeI3DObject2, TeVector3f32> *> &layoutPositionLinearAnimations() { return _layoutPositionLinearAnimations; }
+	StringMap<TeCurveAnim2<TeLayout, TeVector3f32> *> &layoutPositionLinearAnimations() { return _layoutPositionLinearAnimations; }
 	StringMap<TeCurveAnim2<Te3DObject2, TeColor> *> &colorLinearAnimations() { return _colorLinearAnimations; }
 
 	bool loaded() const { return _loaded; }
@@ -119,7 +120,7 @@ private:
 	StringMap<TeClipLayout *> _clipLayouts;
 	StringMap<TeExtendedTextLayout *> _extendedTextLayouts;
 	StringMap<TeCurveAnim2<TeLayout, TeVector3f32> *> _layoutAnchorLinearAnimations;
-	StringMap<TeCurveAnim2<TeI3DObject2, TeVector3f32> *> _layoutPositionLinearAnimations;
+	StringMap<TeCurveAnim2<TeLayout, TeVector3f32> *> _layoutPositionLinearAnimations;
 	StringMap<TeCurveAnim2<Te3DObject2, TeColor> *> _colorLinearAnimations;
 };
 
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index c922904da83..84cc5a90c54 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -74,7 +74,7 @@ static float TeLuaToF32(lua_State *L, int index) {
 	}
 }
 
-static bool TeLuaToBool(lua_State *L,int index) {
+static bool TeLuaToBool(lua_State *L, int index) {
 	if (lua_type(L, index) != LUA_TBOOLEAN) {
 		warning("TeLuaToBool:: not a bool");
 		return false;
@@ -107,7 +107,7 @@ static TeColor TeLuaToTeColor(lua_State *L, int index) {
 
 	lua_settop(L, -2);
 	lua_pushinteger(L, 3);
-	lua_gettable(L,index);
+	lua_gettable(L, index);
 	if (lua_isnumber(L, -1)) {
 		retval.b() = TeLuaToU32(L, -1);
 	}
@@ -209,7 +209,7 @@ static bool loadCommonLayoutItems(lua_State *L, const char *s, TeLayout *layout)
 }
 
 
-// TODO: Fix this.
+// TODO: Fix this widescreen flag
 static bool _g_bWidescreen = false;
 
 int layoutBindings(lua_State *L) {
@@ -229,7 +229,7 @@ int layoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f ,1.0f));
+					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
@@ -246,7 +246,7 @@ int layoutBindings(lua_State *L) {
 	}
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeLayout *> &layouts = gui->layouts();
 	if (!layouts.contains(layout->name())) {
 		layouts.setVal(layout->name(), layout);
@@ -289,7 +289,7 @@ int listLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f ,1.0f));
+					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
@@ -306,7 +306,7 @@ int listLayoutBindings(lua_State *L) {
 	}
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeListLayout *> &layouts = gui->listLayouts();
 	if (!layouts.contains(layout->name())) {
 		layouts.setVal(layout->name(), layout);
@@ -376,7 +376,7 @@ int spriteLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
+					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
@@ -470,7 +470,7 @@ int buttonLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
+					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.buttonLayoutBindings] Unreconized attribute : %s", s);
@@ -490,7 +490,7 @@ int buttonLayoutBindings(lua_State *L) {
 
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeButtonLayout *> &blayouts = gui->buttonLayouts();
 	if (!blayouts.contains(layout->name())) {
 		blayouts.setVal(layout->name(), layout);
@@ -540,7 +540,7 @@ int checkboxLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
+					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.checkboxLayoutBindings] Unreconized attribute : %s", s);
@@ -560,7 +560,7 @@ int checkboxLayoutBindings(lua_State *L) {
 
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeCheckboxLayout *> &blayouts = gui->checkboxLayouts();
 	if (!blayouts.contains(layout->name())) {
 		blayouts.setVal(layout->name(), layout);
@@ -579,7 +579,53 @@ int layoutPositionLinearAnimationBindings(lua_State *L) {
 		return 0;
 	}
 
-	error("TODO: Implement layoutPositionLinearAnimationBindings.");
+	TeCurveAnim2<TeLayout, TeVector3f32> *anim = new TeCurveAnim2<TeLayout, TeVector3f32>();
+	lua_pushnil(L);
+	Common::String name;
+	while (lua_next(L, -2) != 0) {
+		int type = lua_type(L, -2);
+		if (type == LUA_TSTRING) {
+			const char *s = lua_tolstring(L, -2, nullptr);
+			if (!strcmp(s, "name")) {
+				name = TeLuaToTeString(L, -1);
+			} else if (!strcmp(s, "duration")) {
+				anim->_duration = TeLuaToF32(L, -1);
+			} else if (!strcmp(s, "startValue")) {
+				static const TeVector3f32 defaultStart(0.0f, 0.0f, 0.0f);
+				anim->_startVal = TeLuaToTeVector3f32(L, -1, defaultStart);
+			} else if (!strcmp(s, "endValue")) {
+				static const TeVector3f32 defaultEnd(0.0f, 0.0f, 0.0f);
+				anim->_endVal = TeLuaToTeVector3f32(L, -1, defaultEnd);
+			} else if (!strcmp(s, "layout")) {
+				// skip.
+			} else if (!strcmp(s, "curve")) {
+				const Common::Array<float> curve = TeLuaToFloatArray(L, -1);
+				anim->setCurve(curve);
+			} else {
+				error("[TeLuaGUI.layoutPositionLinearAnimationBindings] Unreconized attribute : %s", s);
+			}
+			lua_settop(L, -2);
+		}
+	}
+
+	if (name.empty()) {
+		name = Common::String::format("%p", (void *)anim);
+	}
+	anim->_callbackMethod = &TeLayout::setPosition;
+	lua_pushstring(L, "__TeLuaGUIThis");
+	lua_gettable(L, LUA_REGISTRYINDEX);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
+	TeLuaGUI::StringMap<TeCurveAnim2<TeLayout, TeVector3f32> *> &anims = gui->layoutPositionLinearAnimations();
+	if (!anims.contains(name)) {
+		anims.setVal(name, anim);
+		lua_pushlightuserdata(L, (void *)(anim));
+		return true;
+	} else {
+		warning("layoutPositionLinearAnimationBindings:: multiple objects with name %s", name.c_str());
+		delete anim;
+		return false;
+	}
+	return true;
 }
 
 int layoutAnchorLinearAnimationBindings(lua_State *L) {
@@ -588,7 +634,7 @@ int layoutAnchorLinearAnimationBindings(lua_State *L) {
 		return 0;
 	}
 
-	TeCurveAnim2<TeLayout,TeVector3f32> *anim = new TeCurveAnim2<TeLayout,TeVector3f32>();
+	TeCurveAnim2<TeLayout, TeVector3f32> *anim = new TeCurveAnim2<TeLayout, TeVector3f32>();
 	lua_pushnil(L);
 	Common::String name;
 	while (lua_next(L, -2) != 0) {
@@ -613,7 +659,7 @@ int layoutAnchorLinearAnimationBindings(lua_State *L) {
 			} else {
 				error("[TeLuaGUI.layoutAnchorLinearAnimationBindings] Unreconized attribute : %s", s);
 			}
-			lua_settop(L,-2);
+			lua_settop(L, -2);
 		}
 	}
 
@@ -623,7 +669,7 @@ int layoutAnchorLinearAnimationBindings(lua_State *L) {
 	anim->_callbackMethod = &TeLayout::setAnchor;
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeCurveAnim2<TeLayout, TeVector3f32> *> &anims = gui->layoutAnchorLinearAnimations();
 	if (!anims.contains(name)) {
 		anims.setVal(name, anim);
@@ -664,7 +710,7 @@ int textLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
+					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.textLayoutBindings] Unreconized attribute : %s", s);
@@ -682,7 +728,7 @@ int textLayoutBindings(lua_State *L) {
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
 
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeTextLayout *> &layouts = gui->textLayouts();
 	if (!layouts.contains(layout->name())) {
 		layouts.setVal(layout->name(), layout);
@@ -710,7 +756,7 @@ int colorLinearAnimationBindings(lua_State *L) {
 		return 0;
 	}
 
-	TeCurveAnim2<Te3DObject2,TeColor> *anim = new TeCurveAnim2<Te3DObject2, TeColor>();
+	TeCurveAnim2<Te3DObject2, TeColor> *anim = new TeCurveAnim2<Te3DObject2, TeColor>();
 	lua_pushnil(L);
 	Common::String name;
 	while (lua_next(L, -2) != 0) {
@@ -732,7 +778,7 @@ int colorLinearAnimationBindings(lua_State *L) {
 			} else {
 				error("[TeLuaGUI.colorLinearAnimationBindings] Unreconized attribute : %s", s);
 			}
-			lua_settop(L,-2);
+			lua_settop(L, -2);
 		}
 	}
 
@@ -742,7 +788,7 @@ int colorLinearAnimationBindings(lua_State *L) {
 	anim->_callbackMethod = &Te3DObject2::setColor;
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeCurveAnim2<Te3DObject2, TeColor> *> &anims = gui->colorLinearAnimations();
 	if (!anims.contains(name)) {
 		anims.setVal(name, anim);
@@ -782,7 +828,7 @@ int scrollingLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "inertiaAnimationDuration")) {
 				layout->setInertiaAnimationDuration(TeLuaToU32(L, -1));
 			} else if (!strcmp(s, "inertiaAnimationCurve")) {
-				layout->setInertiaAnimationCurve(TeLuaToFloatArray(L ,-1));
+				layout->setInertiaAnimationCurve(TeLuaToFloatArray(L, -1));
 			} else if (!strcmp(s, "direction")) {
 				TeVector3f32 newdir = TeLuaToTeVector3f32(L, -1, layout->direction());
 				layout->setDirection(newdir);
@@ -815,7 +861,7 @@ int scrollingLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
+					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.scrollingLayoutBindings] Unreconized attribute : %s", s);
@@ -833,7 +879,7 @@ int scrollingLayoutBindings(lua_State *L) {
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
 
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeScrollingLayout *> &layouts = gui->scrollingLayouts();
 	if (!layouts.contains(layout->name())) {
 		layouts.setVal(layout->name(), layout);
@@ -869,7 +915,7 @@ int extendedTextLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "autoScrollDelay")) {
 				layout->setAutoScrollDelay(TeLuaToS32(L, -1));
 			} else if (!strcmp(s, "autoScrollSpeed")) {
-				layout->setAutoScrollSpeed(TeLuaToS32(L, -1));
+				layout->setAutoScrollSpeed(TeLuaToF32(L, -1));
 			} else if (!strcmp(s, "textSizeType")) {
 				layout->setTextSizeType(TeLuaToS32(L, -1));
 			} else if (!strcmp(s, "textSizeProportionalToWidth")) {
@@ -877,7 +923,7 @@ int extendedTextLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001,1.0,1.0));
+					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.textLayoutBindings] Unreconized attribute : %s", s);
@@ -895,7 +941,7 @@ int extendedTextLayoutBindings(lua_State *L) {
 	lua_pushstring(L, "__TeLuaGUIThis");
 	lua_gettable(L, LUA_REGISTRYINDEX);
 
-	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L,-1);
+	TeLuaGUI *gui = TeLuaTo<TeLuaGUI*>(L, -1);
 	TeLuaGUI::StringMap<TeExtendedTextLayout *> &layouts = gui->extendedTextLayouts();
 	if (!layouts.contains(layout->name())) {
 		layouts.setVal(layout->name(), layout);
diff --git a/engines/tetraedge/te/te_material.cpp b/engines/tetraedge/te/te_material.cpp
index 2f740f45ea4..af940165a7a 100644
--- a/engines/tetraedge/te/te_material.cpp
+++ b/engines/tetraedge/te/te_material.cpp
@@ -91,7 +91,7 @@ void TeMaterial::apply() const {
 			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
 		} else {
-			glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_MODULATE);
+			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 			if (_mode != MaterialMode1) {
 				glEnable(GL_ALPHA_TEST);
 				glAlphaFunc(GL_GREATER, 0.5);
diff --git a/engines/tetraedge/te/te_matrix4x4.cpp b/engines/tetraedge/te/te_matrix4x4.cpp
index db6440047a5..c9fe992bb65 100644
--- a/engines/tetraedge/te/te_matrix4x4.cpp
+++ b/engines/tetraedge/te/te_matrix4x4.cpp
@@ -151,7 +151,7 @@ TeVector3f32 TeMatrix4x4::operator*(const TeVector3f32 &mul) const {
 	const float *d = getData();
 	float w = d[3] * x + d[7] * y + d[11] * z + d[15];
 	if (w == 0.0)
-	  w = 1e-09;
+	  w = 1e-09f;
 
 	return TeVector3f32
 			  ((d[0] * x + d[4] * y + d[8] *  z + d[12]) / w,
diff --git a/engines/tetraedge/te/te_model_vertex_animation.cpp b/engines/tetraedge/te/te_model_vertex_animation.cpp
index 5b90be781c3..17479130bdc 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.cpp
+++ b/engines/tetraedge/te/te_model_vertex_animation.cpp
@@ -25,7 +25,7 @@
 namespace Tetraedge {
 
 TeModelVertexAnimation::TeModelVertexAnimation() : _lastMillis(0.0f), _modelAnim(nullptr) {
-	_rot.fromAxisAndAngle(TeVector3f32(0.0f, 1.0f, 0.0f), -M_PI_2);
+	_rot.fromAxisAndAngle(TeVector3f32(0.0f, 1.0f, 0.0f), (float)-M_PI_2);
 }
 
 void TeModelVertexAnimation::bind(TeIntrusivePtr<TeModel> &model) {
diff --git a/engines/tetraedge/te/te_music.cpp b/engines/tetraedge/te/te_music.cpp
index c7ba5010e9a..b3fbd96f681 100644
--- a/engines/tetraedge/te/te_music.cpp
+++ b/engines/tetraedge/te/te_music.cpp
@@ -148,11 +148,11 @@ byte TeMusic::currentData() {
 	return retval;
 }
 
-/*
- This is probably not needed - it's the thread function
- which is handled by the mixer in ScummVM */
+
+// This is probably not needed - it's the thread function
+// which is handled by the mixer in ScummVM
 void TeMusic::entry() {
-	error("TODO: Implement me");
+	error("TODO: Implement TeMusic::entry");
 }
 
 bool TeMusic::isPlaying() {
diff --git a/engines/tetraedge/te/te_palette.cpp b/engines/tetraedge/te/te_palette.cpp
index 778608c4706..47b6816dd64 100644
--- a/engines/tetraedge/te/te_palette.cpp
+++ b/engines/tetraedge/te/te_palette.cpp
@@ -26,6 +26,4 @@ namespace Tetraedge {
 TePalette::TePalette() {
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_ray_intersection.cpp b/engines/tetraedge/te/te_ray_intersection.cpp
index 6fcdad15a17..fd645bf1c37 100644
--- a/engines/tetraedge/te/te_ray_intersection.cpp
+++ b/engines/tetraedge/te/te_ray_intersection.cpp
@@ -110,54 +110,6 @@ int intersect(const TeVector3f32 &rayPos, const TeVector3f32 &rayDir, const TeVe
 }
 */
 
-/*
-bool testIntersection(const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3, const TeVector3f32 &orig, TeVector3f32 &dir) {
-	TeVector3f32 qvec,tvec;
-
-	TeVector3f32 e1 = v2 - v1;
-	TeVector3f32 e2 = v3 - v1;
-
-	TeVector3f32 pvec = TeVector3f32::crossProduct(dir, e2);
-
-	dir.normalize();
-	float det = pvec.dotProduct(e1);
-//#ifdef TEST_CULL
-	if (det < FLT_EPSILON)
-		return false;
-
-	tvec = orig - v1;
-	float u = tvec.dotProduct(pvec);
-	if (u < 0.0 || u > det) {
-		return false;
-	}
-	CROSS(qvec,tvec,e1);
-	float v = dir.dotProduct(qvec);
-	if (v < 0.0f || v + u > det) {
-		return false;
-	}
-#else
-	if (det < FLT_EPSILON && det > -FLT_EPSILON ) {
-		return false;
-	}
-
-	float invDet = 1.0f / det;
-	tvec = orig - v1;
-	// NORMALIZE(tvec);
-	float u = invDet * tvec.dotProduct(pvec);
-	if (u <0.0f || u > 1.0f) {
-		return false;
-	}
-
-	qvec = TeVector3f32::crossProduct(tvec, e1);
-	float v = invDet * qvec.dotProduct(dir);
-	if (v < 0.0f || u+v > 1.0f) {
-		return false;
-	}
-#endif
-	return true;
-}
-*/
-
 } // end namespace TeRayIntersection
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 0ac997fbebe..518b8578e31 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -225,7 +225,7 @@ void TeRenderer::disableZBuffer() {
 }
 
 void TeRenderer::drawLine(const TeVector3f32 &from, const TeVector3f32 &to) {
-	error("TODO: TeRenderer::drawLine Implement me.");
+	error("TODO: Implement TeRenderer::drawLine");
 }
 
 void TeRenderer::enableTexture() {
@@ -494,15 +494,15 @@ void TeRenderer::reset() {
 }
 
 void TeRenderer::rotate(const TeQuaternion &rot) {
-	error("TODO: TeRenderer::rotate Implement me.");
+	_matriciesStacks[_matrixMode].rotate(rot);
 }
 
-void TeRenderer::rotate(float f1, float f2, float f3, float f4) {
-	error("TODO: TeRenderer::rotate Implement me.");
+void TeRenderer::rotate(float angle, float rx, float ry, float rz) {
+	_matriciesStacks[_matrixMode].rotate(angle, TeVector3f32(rx, ry, rz));
 }
 
 void TeRenderer::scale(float xs, float ys, float zs) {
-	error("TODO: TeRenderer::scale Implement me.");
+	_matriciesStacks[_matrixMode].scale(TeVector3f32(xs, ys, zs));
 }
 
 void TeRenderer::setClearColor(const TeColor &col) {
@@ -564,7 +564,7 @@ void TeRenderer::shadowMode(enum ShadowMode mode) {
 }
 
 void TeRenderer::translate(float x, float y, float z) {
-	error("TODO: TeRenderer::translate Implement me.");
+	_matriciesStacks[_matrixMode].translate(TeVector3f32(x, y, z));
 }
 
 Common::String TeRenderer::vendor() {
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index 03ccb7ed28b..61cb82a5904 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -95,7 +95,7 @@ public:
 	void renderTransparentMeshes();
 	void reset();
 	void rotate(const TeQuaternion &rot);
-	void rotate(float f1, float f2, float f3, float f4);
+	void rotate(float angle, float rx, float ry, float rz);
 	void scale(float xs, float ys, float zs);
 	bool scissorEnabled() const { return _scissorEnabled; }
 	int scissorHeight() const { return _scissorHeight; }
diff --git a/engines/tetraedge/te/te_scrolling_layout.cpp b/engines/tetraedge/te/te_scrolling_layout.cpp
index 01522966b52..bd15fffc8a8 100644
--- a/engines/tetraedge/te/te_scrolling_layout.cpp
+++ b/engines/tetraedge/te/te_scrolling_layout.cpp
@@ -21,14 +21,54 @@
 
 #include "tetraedge/te/te_scrolling_layout.h"
 
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/te/te_input_mgr.h"
+
+#include "common/math.h"
+
 namespace Tetraedge {
 
 TeScrollingLayout::TeScrollingLayout() : _contentLayout(nullptr),
 	_enclose(true), _mouseControl(true), _autoScrollLoop(-1), _autoScrollDelay(1500),
 	_autoScrollAnimation1Enabled(true), _autoScrollAnimation2Enabled(true),
-	_autoScrollAnimation1Speed(0.1), _autoScrollAnimation2Speed(0.1),
-	_autoScrollAnimation1Delay(1000), _autoScrollAnimation2Delay(1000)
+	_autoScrollAnimation1Speed(0.1f), _autoScrollAnimation2Speed(0.1f),
+	_autoScrollAnimation1Delay(1000), _autoScrollAnimation2Delay(1000),
+	_currentScrollLoopNo(0), _inertiaAnimationDuration(500),
+	_mouseMoveThreshold(30.0), _insideMouseThreshold(true),
+	_direction(0.0, 1.0, 0.0)
 {
+	setSizeType(RELATIVE_TO_PARENT);
+	setSize(TeVector3f32(1.0, 1.0, 1.0));
+	// TODO: original does addChild on something here.
+	// FIXME: This doesn't seem right...
+	//onWorldTransformationMatrixChanged().add(this, &TeScrollingLayout::onSlideButtonDown);
+	Common::Array<float> curve;
+	curve.push_back(0.0f);
+	curve.push_back(1.0f);
+	_autoScrollAnimation1Curve = curve;
+	_autoScrollAnimation2Curve = curve;
+	_autoScrollDelayTimer.alarmSignal().add(this, &TeScrollingLayout::onAutoScrollDelayTimer);
+	_autoScrollAnimation1Timer.alarmSignal().add(this, &TeScrollingLayout::onAutoScrollAnimation1DelayTimer);
+	_autoScrollAnimation2Timer.alarmSignal().add(this, &TeScrollingLayout::onAutoScrollAnimation2DelayTimer);
+	_autoScrollAnimation1.onFinished().add(this, &TeScrollingLayout::onAutoScrollAnimation1Finished);
+	_autoScrollAnimation2.onFinished().add(this, &TeScrollingLayout::onAutoScrollAnimation2Finished);
+	Common::Array<float> curve2;
+	curve2.push_back(0.0f);
+	curve2.push_back(0.35f);
+	curve2.push_back(0.68f);
+	curve2.push_back(0.85f);
+	curve2.push_back(0.93f);
+	curve2.push_back(0.97f);
+	curve2.push_back(1.0f);
+	_inertiaAnimationCurve = curve2;
+	_scrollTimer.start();
+	playAutoScroll();
+}
+
+TeScrollingLayout::~TeScrollingLayout() {
+	TeInputMgr *inputmgr = g_engine->getInputMgr();
+	inputmgr->_mouseMoveSignal.remove<TeScrollingLayout>(this, &TeScrollingLayout::onMouseMove);
+	inputmgr->_mouseLUpSignal.remove<TeScrollingLayout>(this, &TeScrollingLayout::onMouseLeftUp);
 }
 
 void TeScrollingLayout::setContentLayout(TeLayout *layout) {
@@ -43,16 +83,316 @@ void TeScrollingLayout::setContentLayout(TeLayout *layout) {
 	}
 }
 
+void TeScrollingLayout::setSpeed(const TeVector3f32 &speed) {
+	_speed = speed;
+	TeVector3f32 newpos = scrollPosition() + _speed * (float)(_scrollTimer.timeElapsed() / 1000000.0);
+	setScrollPosition(newpos);
+}
+
+void TeScrollingLayout::setScrollPosition(const TeVector3f32 &scrPos) {
+	if (!_contentLayout)
+		return;
+
+	TeVector3f32 pos = scrPos;
+	pos.x() = CLIP(pos.x(), 0.0f, 1.0f);
+	pos.y() = CLIP(pos.y(), 0.0f, 1.0f);
+
+    const TeVector3f32 thisSize(xSize(), ySize(), 1.0);
+    const TeVector3f32 contentSize(_contentLayout->xSize(), _contentLayout->ySize(), 1.0);
+    TeVector3f32 sizeRatio;
+
+    if (thisSize.x() == 0.0 || thisSize.y() == 0.0) {
+		sizeRatio = TeVector3f32(1.0, 1.0, 1.0);
+    } else {
+		sizeRatio = contentSize / thisSize;
+    }
+
+    TeVector3f32 posToSet = _contentLayout->userPosition();
+    const TeVector3f32 contentAnchor = _contentLayout->anchor();
+    if (!_enclose) {
+		if (thisSize.x() < contentSize.x()) {
+			float offset = (sizeRatio.x() + 1.0) * pos.x();
+			posToSet.x() = contentAnchor.x() * sizeRatio.x() + (1.0 - offset);
+		}
+		if (thisSize.y() < contentSize.y()) {
+			float offset = (sizeRatio.y() + 1.0) * pos.y();
+			posToSet.y() = contentAnchor.y() * sizeRatio.y() + (1.0 - offset);
+		}
+    } else {
+		if (thisSize.x() < contentSize.x()) {
+			float offset = (sizeRatio.x() - 1.0) * pos.x();
+			posToSet.x() = contentAnchor.x() * sizeRatio.x() - offset;
+		}
+		if (thisSize.y() < contentSize.y()) {
+			float offset = (sizeRatio.y() - 1.0) * pos.y();
+			posToSet.y() = contentAnchor.y() * sizeRatio.y() - offset;
+		}
+    }
+
+    _contentLayout->setPosition(posToSet);
+    _posUpdatedSignal.call();
+}
+
+TeVector3f32 TeScrollingLayout::scrollPosition() {
+	if (!_contentLayout)
+		return TeVector3f32();
+
+    const TeVector3f32 thisSize(xSize(), ySize(), 1.0);
+    const TeVector3f32 contentSize(_contentLayout->xSize(), _contentLayout->ySize(), 1.0);
+
+	TeVector3f32 sizeRatio;
+    if (thisSize.x() == 0.0 || thisSize.y() == 0.0) {
+		sizeRatio = TeVector3f32(1.0, 1.0, 1.0);
+    } else {
+		sizeRatio = contentSize / thisSize;
+    }
+
+	TeVector3f32 result(0.0, 0.0, 0.0);
+	if (_enclose) {
+		TeVector3f32 contentAnchor = _contentLayout->anchor();
+		TeVector3f32 contentPos = _contentLayout->userPosition();
+		if (sizeRatio.x() > 1.0) {
+			result.x() = (-(int)(contentPos.x() - contentAnchor.x() * sizeRatio.x())) / (sizeRatio.x() - 1.0);
+		}
+		if (sizeRatio.y() > 1.0) {
+			result.y() = (-(int)(contentPos.y() - contentAnchor.y() * sizeRatio.y())) / (sizeRatio.y() - 1.0);
+		}
+	} else {
+		TeVector3f32 offsetPos = _contentLayout->position() - TeVector3f32(1.0, 1.0, 1.0);
+		result = ((_contentLayout->anchor() * sizeRatio) - offsetPos) / (sizeRatio + TeVector3f32(1.0, 1.0, 1.0));
+	}
+	return result;
+}
+
+bool TeScrollingLayout::onAutoScrollDelayTimer() {
+  	_autoScrollDelayTimer.stop();
+	playAutoScrollAnimation1();
+	return false;
+}
+
+bool TeScrollingLayout::onAutoScrollAnimation1DelayTimer() {
+	_autoScrollAnimation1Timer.stop();
+	_autoScrollAnimation1.setCurve(_autoScrollAnimation1Curve);
+	const TeVector3f32 startPos = scrollPosition();
+	_autoScrollAnimation1._startVal = startPos;
+	TeVector3f32 endPos = startPos + TeVector3f32(1.0, 1.0, 0.0) * _direction;
+	endPos.x() = CLIP(endPos.x(), 0.0f, 1.0f);
+	endPos.y() = CLIP(endPos.y(), 0.0f, 1.0f);
+	_autoScrollAnimation1._endVal = endPos;
+
+	TeVector3f32 sizeRatio(1.0, 1.0, 0.0);
+	if (_contentLayout) {
+		sizeRatio = _contentLayout->userSize() / size();
+	}
+
+	float duration = 0.0;
+	if (_autoScrollAnimation1Speed != 0.0) {
+		const TeVector3f32 dist = endPos - startPos;
+		if (_enclose) {
+			sizeRatio = dist * (sizeRatio - TeVector3f32(1.0, 1.0, 0.0));
+		} else {
+			sizeRatio = dist * (sizeRatio + TeVector3f32(1.0, 1.0, 0.0));
+		}
+		duration = (sizeRatio * _direction).length() / (_autoScrollAnimation1Speed / 1000.0);
+	}
+
+	_autoScrollAnimation1._duration = duration;
+	_autoScrollAnimation1._callbackObj = this;
+	_autoScrollAnimation1._callbackMethod = &TeScrollingLayout::setScrollPosition;
+	_autoScrollAnimation1.play();
+	return false;
+}
+
+bool TeScrollingLayout::onAutoScrollAnimation2DelayTimer() {
+	_autoScrollAnimation2Timer.stop();
+	_autoScrollAnimation2.setCurve(_autoScrollAnimation2Curve);
+	const TeVector3f32 startPos = scrollPosition();
+	_autoScrollAnimation2._startVal = startPos;
+	// Note: this is the only real difference between this and the "1" version
+	// of the function.. - instead of +
+	TeVector3f32 endPos = startPos - TeVector3f32(1.0, 1.0, 0.0) * _direction;
+	endPos.x() = CLIP(endPos.x(), 0.0f, 1.0f);
+	endPos.y() = CLIP(endPos.y(), 0.0f, 1.0f);
+	_autoScrollAnimation2._endVal = endPos;
+
+	TeVector3f32 sizeRatio(1.0, 1.0, 0.0);
+	if (_contentLayout) {
+		sizeRatio = _contentLayout->size() / size();
+	}
+
+	float duration = 0.0;
+	if (_autoScrollAnimation2Speed != 0.0) {
+		const TeVector3f32 dist = endPos - startPos;
+		if (_enclose) {
+			sizeRatio = dist * (sizeRatio - TeVector3f32(1.0, 1.0, 0.0));
+		} else {
+			sizeRatio = dist * (sizeRatio + TeVector3f32(1.0, 1.0, 0.0));
+		}
+		duration = (sizeRatio * _direction).length() / (_autoScrollAnimation2Speed / 1000.0);
+	}
+
+	_autoScrollAnimation2._duration = duration;
+	_autoScrollAnimation2._callbackObj = this;
+	_autoScrollAnimation2._callbackMethod = &TeScrollingLayout::setScrollPosition;
+	_autoScrollAnimation2.play();
+	return false;
+}
+
+bool TeScrollingLayout::onAutoScrollAnimation1Finished() {
+	playAutoScrollAnimation2();
+	return false;
+}
+
+bool TeScrollingLayout::onAutoScrollAnimation2Finished() {
+	_currentScrollLoopNo++;
+	playAutoScrollAnimation1();
+	return false;
+}
+
+bool TeScrollingLayout::onMouseMove(const Common::Point &pt) {
+	_inertiaAnimation.stop();
+	const TeVector3f32 scrollPos = scrollPosition();
+	TeVector3f32 offset;
+	TeInputMgr *inputmgr = g_engine->getInputMgr();
+	if (_contentLayout) {
+		const TeVector3f32 thisUserSz = userSize();
+		const TeVector3f32 contentUserSz = _contentLayout->userSize();
+		if (contentUserSz.y() <= thisUserSz.y())
+			return false;
+
+		const TeVector2s32 lastMouse = inputmgr->lastMousePos();
+		if (!_enclose) {
+			offset.x() = (-(int)(_direction.x() * (lastMouse._x - _slideDownMousePos._x))) /
+						(xSize() + _contentLayout->xSize());
+			offset.y() = ((lastMouse._y - _slideDownMousePos._y) * _direction.y()) / (ySize() + _contentLayout->ySize());
+		} else {
+			float xdiff = xSize() - _contentLayout->xSize();
+			if (xdiff)
+				offset.x() = (-(int)(_direction.x() * (lastMouse._x - _slideDownMousePos._x))) / xdiff;
+			float ydiff = ySize() - _contentLayout->ySize();
+			if (ydiff)
+				offset.y() = ((lastMouse._y - _slideDownMousePos._y) * _direction.y()) / ydiff;
+		}
+	}
+
+    setScrollPosition(scrollPos + offset);
+    _slideDownMousePos = inputmgr->lastMousePos();
+    TeVector3f32 nowMousePos(inputmgr->lastMousePos());
+    _insideMouseThreshold = (_lastMouseDownPos - nowMousePos).length() <= _mouseMoveThreshold;
+    long elapsed = _scrollTimer.timeElapsed();
+    if (elapsed > 0) {
+		_speed = offset / (float)(elapsed / 1000000.0);
+    }
+    return false;
+}
+
+bool TeScrollingLayout::onMouseLeftUp(const Common::Point &pt) {
+	_inertiaAnimation.stop();
+	if (_contentLayout) {
+		_inertiaAnimation.setCurve(_inertiaAnimationCurve);
+		_inertiaAnimation._duration = _inertiaAnimationDuration;
+		_inertiaAnimation._startVal = _speed;
+		_inertiaAnimation._endVal = TeVector3f32(0.0, 0.0, 0.0);
+		_inertiaAnimation._callbackObj = this;
+		_inertiaAnimation._callbackMethod = &TeScrollingLayout::setSpeed;
+		_inertiaAnimation.play();
+	}
+
+	TeInputMgr *inputmgr = g_engine->getInputMgr();
+	inputmgr->_mouseMoveSignal.remove<TeScrollingLayout>(this, &TeScrollingLayout::onMouseMove);
+	inputmgr->_mouseLUpSignal.remove<TeScrollingLayout>(this, &TeScrollingLayout::onMouseLeftUp);
+
+	if (_autoScrollLoop == -1 || _currentScrollLoopNo < _autoScrollLoop) {
+		_autoScrollDelayTimer.start();
+		_autoScrollDelayTimer.setAlarmIn(_autoScrollDelay * 1000);
+	}
+	return false;
+}
+
+bool TeScrollingLayout::onSlideButtonDown() {
+	_currentScrollLoopNo = 0;
+	_inertiaAnimation.stop();
+	_autoScrollDelayTimer.stop();
+	_autoScrollAnimation1Timer.stop();
+	_autoScrollAnimation2Timer.stop();
+	_autoScrollAnimation1.stop();
+	_autoScrollAnimation2.stop();
+
+	_slideDownMousePos = g_engine->getInputMgr()->lastMousePos();
+
+	_lastMouseDownPos = TeVector3f32(_slideDownMousePos);
+	_insideMouseThreshold = true;
+
+	TeInputMgr *inputmgr = g_engine->getInputMgr();
+
+	Common::SharedPtr<TeCallback1Param<TeScrollingLayout, const Common::Point &>> callback;
+
+	inputmgr->_mouseMoveSignal.remove(this, &TeScrollingLayout::onMouseMove);
+	callback.reset(new TeCallback1Param<TeScrollingLayout, const Common::Point &>(
+						this, &TeScrollingLayout::onMouseMove, FLT_MAX));
+	inputmgr->_mouseMoveSignal.push_back(callback);
+
+	inputmgr->_mouseLUpSignal.remove(this, &TeScrollingLayout::onMouseLeftUp);
+	callback.reset(new TeCallback1Param<TeScrollingLayout, const Common::Point &>(
+						this, &TeScrollingLayout::onMouseLeftUp, FLT_MAX));
+	inputmgr->_mouseLUpSignal.push_back(callback);
+	return false;
+}
+
+void TeScrollingLayout::playAutoScrollAnimation1() {
+	if (!_autoScrollAnimation1Enabled) {
+		playAutoScrollAnimation2();
+		return;
+	}
+
+	if (_autoScrollLoop != -1 && _currentScrollLoopNo >= _autoScrollLoop)
+		return;
+
+	_autoScrollAnimation1Timer.start();
+	_autoScrollAnimation1Timer.setAlarmIn(_autoScrollAnimation1Delay * 1000);
+	return;
+}
+
+void TeScrollingLayout::playAutoScrollAnimation2() {
+	if (!_autoScrollAnimation2Enabled) {
+		_currentScrollLoopNo++;
+		playAutoScrollAnimation1();
+		return;
+	}
+
+	if (_autoScrollLoop != -1 && _currentScrollLoopNo >= _autoScrollLoop)
+		return;
+
+	_autoScrollAnimation2Timer.start();
+	_autoScrollAnimation2Timer.setAlarmIn(_autoScrollAnimation2Delay * 1000);
+}
+
 void TeScrollingLayout::resetScrollPosition() {
 	if (!_contentLayout)
 		return;
-	warning("TODO: Implement TeScrollingLayout::resetScrollPosition");
+
+	_inertiaAnimation.stop();
+	_autoScrollDelayTimer.stop();
+	_autoScrollAnimation1Timer.stop();
+	_autoScrollAnimation2Timer.stop();
+	_autoScrollAnimation1.stop();
+	_autoScrollAnimation2.stop();
+	_contentLayout->setPosition(_contentLayoutUserPos);
+	_posUpdatedSignal.call();
 }
 
 void TeScrollingLayout::playAutoScroll() {
-	warning("TODO: Implement TeScrollingLayout::playAutoScroll");
+	_currentScrollLoopNo = 0;
+	if (_autoScrollLoop < 1 && _autoScrollLoop != -1)
+		return;
+	_inertiaAnimation.stop();
+	_autoScrollDelayTimer.stop();
+	_autoScrollAnimation1Timer.stop();
+	_autoScrollAnimation2Timer.stop();
+	_autoScrollAnimation1.stop();
+	_autoScrollAnimation2.stop();
+	_autoScrollDelayTimer.start();
+	_autoScrollDelayTimer.setAlarmIn(_autoScrollDelay * 1000);
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_scrolling_layout.h b/engines/tetraedge/te/te_scrolling_layout.h
index ba64168cf57..528019bf8c4 100644
--- a/engines/tetraedge/te/te_scrolling_layout.h
+++ b/engines/tetraedge/te/te_scrolling_layout.h
@@ -22,13 +22,18 @@
 #ifndef TETRAEDGE_TE_TE_SCROLLING_LAYOUT_H
 #define TETRAEDGE_TE_TE_SCROLLING_LAYOUT_H
 
+#include "tetraedge/te/te_animation.h"
+#include "tetraedge/te/te_curve_anim2.h"
 #include "tetraedge/te/te_layout.h"
+#include "tetraedge/te/te_timer.h"
+#include "tetraedge/te/te_signal.h"
 
 namespace Tetraedge {
 
 class TeScrollingLayout : public TeLayout {
 public:
 	TeScrollingLayout();
+	virtual ~TeScrollingLayout();
 
 	void setInertiaAnimationDuration(int duration) {
 		_inertiaAnimationDuration = duration;
@@ -79,28 +84,64 @@ public:
 		_enclose = val;
 	}
 	void setContentLayout(TeLayout *layout);
+	void setSpeed(const TeVector3f32 &speed);
+
+	bool onAutoScrollDelayTimer();
+	bool onAutoScrollAnimation1DelayTimer();
+	bool onAutoScrollAnimation2DelayTimer();
+	bool onAutoScrollAnimation1Finished();
+	bool onAutoScrollAnimation2Finished();
+	bool onMouseMove(const Common::Point &pt);
+	bool onSlideButtonDown();
+	bool onMouseLeftUp(const Common::Point &pt);
+
+	void playAutoScrollAnimation1();
+	void playAutoScrollAnimation2();
 
 	void resetScrollPosition();
 	void playAutoScroll();
+	TeVector3f32 scrollPosition();
+	void setScrollPosition(const TeVector3f32 &newpos);
 
 private:
 	int _inertiaAnimationDuration;
 	Common::Array<float> _inertiaAnimationCurve;
-	uint _autoScrollDelay;
+	TeCurveAnim2<TeScrollingLayout, TeVector3f32> _inertiaAnimation;
+
 	int _autoScrollLoop;
+	int _currentScrollLoopNo;
+
+	uint _autoScrollDelay;
+	TeTimer _autoScrollDelayTimer;
+
 	float _autoScrollAnimation1Speed;
 	float _autoScrollAnimation2Speed;
 	bool _autoScrollAnimation1Enabled;
 	bool _autoScrollAnimation2Enabled;
 	int _autoScrollAnimation1Delay;
 	int _autoScrollAnimation2Delay;
+	TeTimer _autoScrollAnimation1Timer;
+	TeTimer _autoScrollAnimation2Timer;
 	Common::Array<float> _autoScrollAnimation1Curve;
 	Common::Array<float> _autoScrollAnimation2Curve;
+	TeCurveAnim2<TeScrollingLayout, TeVector3f32> _autoScrollAnimation1;
+	TeCurveAnim2<TeScrollingLayout, TeVector3f32> _autoScrollAnimation2;
+
 	TeVector3f32 _direction;
+	TeVector3f32 _speed;
+	TeTimer _scrollTimer;
 	bool _mouseControl;
 	bool _enclose;
 	TeLayout *_contentLayout;
 	TeVector3f32 _contentLayoutUserPos;
+
+	TeVector2s32 _slideDownMousePos;
+	float _mouseMoveThreshold;
+
+	TeVector3f32 _lastMouseDownPos;
+	bool _insideMouseThreshold;
+
+	TeSignal0Param _posUpdatedSignal;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_sprite_layout.cpp b/engines/tetraedge/te/te_sprite_layout.cpp
index 29819cf05cd..b3f30780fa1 100644
--- a/engines/tetraedge/te/te_sprite_layout.cpp
+++ b/engines/tetraedge/te/te_sprite_layout.cpp
@@ -29,11 +29,10 @@ namespace Tetraedge {
 TeSpriteLayout::TeSpriteLayout() : _tiledSurfacePtr(new TeTiledSurface()), _sizeSet(false) {
 	_tiledSurfacePtr->setColor(TeColor(255, 255, 255, 255));
 	//_tiledSurfacePtr->_shouldDraw = true // should already be true..
-	// TODO: set some other flag in _tiledSurfacePtr?
+
 	updateMesh();
 }
 
-
 int TeSpriteLayout::bufferSize() {
 	return _tiledSurfacePtr->bufferSize();
 }
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index deefe76c733..b78c8721d1a 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -55,10 +55,20 @@ void TeTextBase2::build() {
 	_size = TeVector2s32(0, 0);
 	_wrappedLines.clear();
 
-	if (_text.find(' ') != Common::String::npos)
-		font->wordWrapText(_text, _fontSize, _drawRect._x, _wrappedLines);
-	else
-		_wrappedLines.push_back(_text);
+	Common::Array<uint32> endPts = _lineBreaks;
+	uint32 npos = Common::String::npos;
+	endPts.push_back(npos);
+	uint32 start = 0;
+	for (uint32 end : endPts) {
+		Common::Array<Common::String> lines;
+		Common::String txt = _text.substr(start, end - start);
+		if (txt.find(' ') != Common::String::npos)
+			font->wordWrapText(txt, _fontSize, _drawRect._x, lines);
+		else
+			lines.push_back(txt);
+		_wrappedLines.push_back(lines);
+		start = end;
+	}
 
 	Common::Array<float> lineoffsets;
 	float lineHeight = font->getHeight(_fontSize);
@@ -95,7 +105,7 @@ void TeTextBase2::build() {
 
 #if DUMP_RENDERED_FONTS
 	Common::DumpFile dumpFile;
-	dumpFile.open(Common::String::format("/Users/stauff/tmp/%04d.png", dumpCount));
+	dumpFile.open(Common::String::format("/tmp/rendered-font-dump-%04d.png", dumpCount));
 	dumpCount++;
 	Image::writePNG(dumpFile, img);
 #endif
@@ -125,7 +135,7 @@ void TeTextBase2::build() {
 	_mesh.setIndex(1, 1);
 	_mesh.setIndex(2, 3);
 	_mesh.setIndex(3, 2);
-	//_mesh.setHasAlpha(true);
+	_mesh.setHasAlpha(true);
 }
 
 void TeTextBase2::clear() {
diff --git a/engines/tetraedge/te/te_text_base2.h b/engines/tetraedge/te/te_text_base2.h
index fe92f0bac4b..3033669b7ff 100644
--- a/engines/tetraedge/te/te_text_base2.h
+++ b/engines/tetraedge/te/te_text_base2.h
@@ -98,7 +98,7 @@ private:
 
 	Common::Array<Common::String> _wrappedLines;
 
-	Common::Array<unsigned int> _lineBreaks;
+	Common::Array<uint32> _lineBreaks;
 	Common::HashMap<unsigned int, TeColor> _colors;
 	Common::HashMap<unsigned int, TeIntrusivePtr<TeFont3>> _fonts;
 };
diff --git a/engines/tetraedge/te/te_text_layout.cpp b/engines/tetraedge/te/te_text_layout.cpp
index 77750a593cd..b448b96144f 100644
--- a/engines/tetraedge/te/te_text_layout.cpp
+++ b/engines/tetraedge/te/te_text_layout.cpp
@@ -71,6 +71,7 @@ static TeFont3::AlignStyle _alignNameToEnum(const Common::String &name) {
 void TeTextLayout::setText(const Common::String &val) {
 	if (!val.size()) {
 		clear();
+		_sizeChanged = true;
 		return;
 	}
 
@@ -89,6 +90,16 @@ void TeTextLayout::setText(const Common::String &val) {
 		bstart = replaced.find("$(", bstart + 1);
 	}
 
+	//
+	// WORKAROUND: The Syberia credits xml has an unmatched "</t>" at the end..
+	// just delete it.
+	//
+	// Note there is another workaround for the credits xml in the parser.
+	//
+	size_t tagstart = replaced.find("</t>");
+	if (tagstart != Common::String::npos)
+		replaced.replace(tagstart, 4, "    ");
+
 	const Common::String xmlDocStr = Common::String::format("<?xml version=\"1.0\" encoding=\"UTF-8\"?><document>%s</document>", replaced.c_str());
 
 	TeTextLayoutXmlParser parser;
@@ -116,6 +127,7 @@ void TeTextLayout::setText(const Common::String &val) {
 		_base.setAlignStyle(_alignNameToEnum(parser.style()));
 	for (unsigned int offset : parser.lineBreaks())
 		_base.insertNewLine(offset);
+	_sizeChanged = true;
 }
 
 void TeTextLayout::setTextSizeType(int type) {
@@ -158,15 +170,15 @@ void TeTextLayout::updateSize() {
 
 	TeLayout::updateSize();
 
-	TeMatrix4x4 transform = worldTransformationMatrix();
+	const TeMatrix4x4 transform = worldTransformationMatrix();
 
 	const TeVector3f32 v1 = transform * TeVector3f32(0, 0, 0);
 	const TeVector3f32 v2 = transform * TeVector3f32(1, 0, 0);
 	const TeVector3f32 v3 = transform * TeVector3f32(0, 1, 0);
 
-	const TeVector3f32 newSize((v2 - v1).length(), (v3 - v1).length(), 1.0);
+	const TeVector3f32 transformVec((v2 - v1).length(), (v3 - v1).length(), 1.0);
 	const TeVector3f32 thisSize = size();
-	const TeVector3f32 textSize = thisSize * newSize;
+	const TeVector3f32 textSize = thisSize * transformVec;
 	const TeVector2s32 textSizeI(textSize.x(), textSize.y());
 	_base.setRect(textSizeI);
 
@@ -177,15 +189,29 @@ void TeTextLayout::updateSize() {
 		newFontSize = (thisSize.x() / _textSizeProportionalToWidth) * _baseFontSize;
 	}
 
-	newFontSize *= newSize.y();
+	newFontSize *= transformVec.y();
 
 	_base.setFontSize(newFontSize);
 	_base.build();
-	//TeVector2s32 renderedSize = _base.size();
 
-	/*const TeVector3f32 thisUserSize = */TeLayout::userSize();
+	TeVector3f32 userSz = userSize();
+	const TeVector2s32 baseSz = _base.size();
+
+	if (sizeType() == RELATIVE_TO_PARENT && parent()) {
+		if (wrapMode() != TeTextBase2::WrapModeFixed) {
+			if (parent()->xSize() != 0.0)
+				userSz.x() = ((float)baseSz._x / transformVec.y()) / parent()->xSize();
+		}
+		if (parent()->ySize() != 0.0)
+			userSz.y() = ((float)baseSz._y / transformVec.y()) / parent()->ySize();
+	} else if (sizeType() == ABSOLUTE) {
+		if (wrapMode() != TeTextBase2::WrapModeFixed) {
+			userSz.x() = baseSz._x / transformVec.y();
+		}
+		userSz.y() = baseSz._y / transformVec.y();
+	}
 
-	//warning("TODO: finish the last bit of TeTextLayout::updateSize");
+	setSize(userSz);
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_text_layout.h b/engines/tetraedge/te/te_text_layout.h
index d889f0d8d63..0992dbc7676 100644
--- a/engines/tetraedge/te/te_text_layout.h
+++ b/engines/tetraedge/te/te_text_layout.h
@@ -23,11 +23,12 @@
 #define TETRAEDGE_TE_TE_TEXT_LAYOUT_H
 
 #include "tetraedge/te/te_layout.h"
+#include "tetraedge/te/te_i_text_layout.h"
 #include "tetraedge/te/te_text_base2.h"
 
 namespace Tetraedge {
 
-class TeTextLayout : public TeLayout {
+class TeTextLayout : public TeITextLayout {
 public:
 	TeTextLayout();
 
@@ -38,11 +39,11 @@ public:
 	}
 
 	void draw() override;
-	void setText(const Common::String &val);
-	void setInterLine(float val);
-	void setWrapMode(TeTextBase2::WrapMode mode);
-	void setTextSizeType(int type);
-	void setTextSizeProportionalToWidth(int val);
+	void setText(const Common::String &val) override;
+	void setInterLine(float val) override;
+	void setWrapMode(TeTextBase2::WrapMode mode) override;
+	void setTextSizeType(int type) override;
+	void setTextSizeProportionalToWidth(int val) override;
 	void strikethrough(bool val);
 	bool strikethrough() const;
 	const Common::String &text() const;
diff --git a/engines/tetraedge/te/te_text_layout_xml_parser.cpp b/engines/tetraedge/te/te_text_layout_xml_parser.cpp
index f71b05f5152..cc3d11cb591 100644
--- a/engines/tetraedge/te/te_text_layout_xml_parser.cpp
+++ b/engines/tetraedge/te/te_text_layout_xml_parser.cpp
@@ -52,4 +52,13 @@ bool TeTextLayoutXmlParser::parserCallback_br(ParserNode *node) {
 	return true;
 }
 
+bool TeTextLayoutXmlParser::parserCallback_b(ParserNode *node) {
+	//
+	// WORKAROUND: There is a <b /> in the The Syberia credits text xml.
+	// It's almost certainly a typo for <br />, bold fonts are not supported.
+	//
+	_lineBreaks.push_back(_textContent.size());
+	return true;
+}
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_text_layout_xml_parser.h b/engines/tetraedge/te/te_text_layout_xml_parser.h
index 4bab07863b6..c913b0c92cd 100644
--- a/engines/tetraedge/te/te_text_layout_xml_parser.h
+++ b/engines/tetraedge/te/te_text_layout_xml_parser.h
@@ -49,6 +49,8 @@ public:
 			KEY_END()
 			XML_KEY(br)
 			KEY_END()
+			XML_KEY(b)
+			KEY_END()
 		KEY_END()
 	} PARSER_END()
 
@@ -59,6 +61,7 @@ private:
 	bool parserCallback_color(ParserNode *node);
 	bool parserCallback_font(ParserNode *node);
 	bool parserCallback_br(ParserNode *node);
+	bool parserCallback_b(ParserNode *node);
 
 	virtual bool textCallback(const Common::String &str) override;
 
diff --git a/engines/tetraedge/te/te_theora.cpp b/engines/tetraedge/te/te_theora.cpp
index 47c9b985e79..a8cbbffa42e 100644
--- a/engines/tetraedge/te/te_theora.cpp
+++ b/engines/tetraedge/te/te_theora.cpp
@@ -99,8 +99,11 @@ float TeTheora::frameRate() {
 
 bool TeTheora::update(unsigned long i, TeImage &imgout) {
 	// TODO: Should this seek to frame i? Currently just continues.
-	const Graphics::Surface *frame = _decoder->decodeNextFrame();
-	if (frame) {
+	const Graphics::Surface *frame = nullptr;
+	while (_decoder->getCurFrame() < (int)i && !_decoder->endOfVideo())
+		frame = _decoder->decodeNextFrame();
+
+	if (frame && frame->getPixels()) {
 		//debug("TeTheora: %s %ld", _path.toString().c_str(), i);
 		imgout.copyFrom(*frame);
 		return true;
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index f2c123fe550..6c7554f1f7f 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -25,9 +25,15 @@
 #include "tetraedge/te/te_frame_anim.h"
 #include "tetraedge/te/te_resource_manager.h"
 
+//#define DUMP_LOADED_IMAGES 1
+
+#ifdef DUMP_LOADED_IMAGES
+#include "image/png.h"
+#endif
+
 namespace Tetraedge {
 
-static void getRangeIntersection(float start1,float end1,float start2,float end2,float *pstart,float *pend) {
+static void getRangeIntersection(float start1, float end1, float start2, float end2, float *pstart, float *pend) {
 	*pstart = MAX(start1, start2);
 	*pend = MIN(end1, end2);
 }
@@ -61,8 +67,6 @@ bool TeTiledSurface::load(const Common::Path &path) {
 	_path = path;
 
 	TeIntrusivePtr<TeTiledTexture> texture;
-	if (path.toString() == "menus/inGame/Inventory.png")
-		debug("loading inventory from path");
 	if (resmgr->exists(path.append(".tt"))) {
 		texture = resmgr->getResourceNoSearch<TeTiledTexture>(path.append(".tt"));
 		// we don't own this one..
@@ -102,6 +106,13 @@ bool TeTiledSurface::load(const Common::Path &path) {
 			img.create(_codec->width(), _codec->height(), nullpal, _imgFormat, bufx, bufy);
 
 			if (_codec->update(0, img)) {
+#if DUMP_LOADED_IMAGES
+				static int dumpCount = 0;
+				Common::DumpFile dumpFile;
+				dumpFile.open(Common::String::format("/tmp/dump-tiledsurf-%s-%04d.png", name().c_str(), dumpCount));
+				dumpCount++;
+				Image::writePNG(dumpFile, img);
+#endif
 				texture->load(img);
 			}
 		} else {
@@ -114,7 +125,7 @@ bool TeTiledSurface::load(const Common::Path &path) {
 }
 
 bool TeTiledSurface::load(const TeImage &image) {
-	error("TODO: Implement me TeTiledSurface::load(image)");
+	error("TODO: Implement TeTiledSurface::load(image)");
 }
 
 bool TeTiledSurface::load(const TeIntrusivePtr<Te3DTexture> &texture) {
@@ -124,8 +135,6 @@ bool TeTiledSurface::load(const TeIntrusivePtr<Te3DTexture> &texture) {
 	TeIntrusivePtr<TeTiledTexture> tiledTexture;
 
 	const Common::Path ttPath = texture->getAccessName().append(".tt");
-	if (ttPath.toString() == "menus/inGame/Inventory.png.tt")
-		debug("loading inventory from texture");
 
 	if (resmgr->exists(ttPath)) {
 		tiledTexture = resmgr->getResourceNoSearch<TeTiledTexture>(ttPath);
diff --git a/engines/tetraedge/te/te_visual_fade.h b/engines/tetraedge/te/te_visual_fade.h
index 307c2d95983..6a9f363b610 100644
--- a/engines/tetraedge/te/te_visual_fade.h
+++ b/engines/tetraedge/te/te_visual_fade.h
@@ -50,6 +50,8 @@ public:
 
 	TeCurveAnim2<Te3DObject2, TeColor> blackFadeCurveAnim() { return _blackFadeCurveAnim; }
 
+	TeIntrusivePtr<Te3DTexture> texture() { return _texturePtr; }
+
 private:
 
 	TeIntrusivePtr<Te3DTexture> _texturePtr;
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 811384ffa42..5b8276b160f 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -223,4 +223,10 @@ void TetraedgeEngine::openConfigDialog() {
 	syncSoundSettings();
 }
 
+/*static*/
+void TetraedgeEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
+	g_engine->getApplication()->getSavegameThumbnail(thumb);
+}
+
+
 } // namespace Tetraedge
diff --git a/engines/tetraedge/tetraedge.h b/engines/tetraedge/tetraedge.h
index f942697df21..0e2c6de5ab0 100644
--- a/engines/tetraedge/tetraedge.h
+++ b/engines/tetraedge/tetraedge.h
@@ -106,6 +106,8 @@ public:
 		Common::Serializer s(nullptr, stream);
 		return syncGame(s);
 	}
+	
+	static void getSavegameThumbnail(Graphics::Surface &thumb);
 
 	Common::Error loadGameState(int slot) override;
 	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
diff --git a/engines/tetraedge/to_lua.cpp b/engines/tetraedge/to_lua.cpp
index ffc930156a2..a8b57973000 100644
--- a/engines/tetraedge/to_lua.cpp
+++ b/engines/tetraedge/to_lua.cpp
@@ -28,6 +28,8 @@ namespace Tetraedge {
 
 namespace ToLua {
 
+// Also see the tolua copyright notice in to_lua.h.
+
 static char toluaname[128] = "tolua.";
 
 static void tolua_push_globals_table(lua_State *L) {
@@ -41,76 +43,76 @@ static void tolua_push_globals_table(lua_State *L) {
 }
 
 static int class_index_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_index_event");
+	error("TODO: Implement ToLua::class_index_event");
 }
 
 static int class_newindex_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_newindex_event");
+	error("TODO: Implement ToLua::class_newindex_event");
 }
 
 static int class_add_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_add_event");
+	error("TODO: Implement ToLua::class_add_event");
 }
 
 static int class_sub_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_sub_event");
+	error("TODO: Implement ToLua::class_sub_event");
 }
 
 static int class_mul_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_mul_event");
+	error("TODO: Implement ToLua::class_mul_event");
 }
 
 static int class_div_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_div_event");
+	error("TODO: Implement ToLua::class_div_event");
 }
 
 static int class_lt_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_lt_event");
+	error("TODO: Implement ToLua::class_lt_event");
 }
 
 static int class_le_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_le_event");
+	error("TODO: Implement ToLua::class_le_event");
 }
 
 static int class_eq_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_eq_event");
+	error("TODO: Implement ToLua::class_eq_event");
 }
 
 static int class_gc_event(lua_State *L) {
-	error ("TODO: Implement ToLua::class_gc_event");
+	error("TODO: Implement ToLua::class_gc_event");
 }
 
 static void tolua_classevents(lua_State *L) {
-	lua_pushstring(L,"__index");
-	lua_pushcclosure(L,class_index_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__newindex");
-	lua_pushcclosure(L,class_newindex_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__add");
-	lua_pushcclosure(L,class_add_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__sub");
-	lua_pushcclosure(L,class_sub_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__mul");
-	lua_pushcclosure(L,class_mul_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__div");
-	lua_pushcclosure(L,class_div_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__lt");
-	lua_pushcclosure(L,class_lt_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__le");
-	lua_pushcclosure(L,class_le_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__eq");
-	lua_pushcclosure(L,class_eq_event,0);
-	lua_rawset(L,-3);
-	lua_pushstring(L,"__gc");
-	lua_pushcclosure(L,class_gc_event,0);
-	lua_rawset(L,-3);
+	lua_pushstring(L, "__index");
+	lua_pushcclosure(L, class_index_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__newindex");
+	lua_pushcclosure(L, class_newindex_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__add");
+	lua_pushcclosure(L, class_add_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__sub");
+	lua_pushcclosure(L, class_sub_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__mul");
+	lua_pushcclosure(L, class_mul_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__div");
+	lua_pushcclosure(L, class_div_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__lt");
+	lua_pushcclosure(L, class_lt_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__le");
+	lua_pushcclosure(L, class_le_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__eq");
+	lua_pushcclosure(L, class_eq_event, 0);
+	lua_rawset(L, -3);
+	lua_pushstring(L, "__gc");
+	lua_pushcclosure(L, class_gc_event, 0);
+	lua_rawset(L, -3);
 }
 
 static void tolua_newmetatable(lua_State *L, const char *name) {
@@ -154,7 +156,7 @@ static const char* tolua_typename(lua_State *L, int lo) {
 			lua_concat(L, 2);
 		}
 	}
-	return lua_tostring(L,-1);
+	return lua_tostring(L, -1);
 }
 
 static int tolua_bnd_type(lua_State *L) {
@@ -167,8 +169,8 @@ static void *tolua_clone(lua_State *L, void *dest, lua_CFunction fn) {
 	lua_rawget(L, LUA_REGISTRYINDEX);
 	lua_pushlightuserdata(L, dest);
 	lua_pushcclosure(L, fn, 0);
-	lua_rawset(L,-3);
-	lua_settop(L,-2);
+	lua_rawset(L, -3);
+	lua_settop(L, -2);
 	return dest;
 }
 
@@ -177,12 +179,12 @@ static int tolua_bnd_takeownership(lua_State *L) {
 	lua_CFunction fn = nullptr;
 	if (lua_isuserdata(L, 1)) {
 		if (lua_getmetatable(L, 1)) {
-			lua_pushstring(L,".collector");
-			lua_rawget(L,-2);
-			if (lua_iscfunction(L,-1)) {
+			lua_pushstring(L, ".collector");
+			lua_rawget(L, -2);
+			if (lua_iscfunction(L, -1)) {
 				fn = lua_tocfunction(L, -1);
 			}
-			lua_settop(L,-3);
+			lua_settop(L, -3);
 			void *data = lua_touserdata(L, 1);
 			tolua_clone(L, data, fn);
 		}
@@ -230,7 +232,7 @@ static int tolua_bnd_cast(lua_State *L) {
 }
 
 static void tolua_release(lua_State *L, void *p) {
-	lua_pushstring(L,"tolua_ubox");
+	lua_pushstring(L, "tolua_ubox");
 	lua_rawget(L, LUA_REGISTRYINDEX);
 	lua_pushlightuserdata(L, p);
 	lua_rawget(L, -2);
@@ -431,7 +433,7 @@ void *tolua_tousertype(lua_State *L, int narg, void* def) {
 }
 
 int tolua_toboolean(lua_State *L, int narg, int def) {
-	return lua_gettop(L) < abs(narg) ?  def : lua_toboolean(L,narg);
+	return lua_gettop(L) < abs(narg) ?  def : lua_toboolean(L, narg);
 }
 
 void tolua_pushboolean(lua_State *L, bool val) {


Commit: 9dab12cf3ccef9cbf348a01d3a313370cb1eb6fa
    https://github.com/scummvm/scummvm/commit/9dab12cf3ccef9cbf348a01d3a313370cb1eb6fa
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Misc code cleanups. Less public members.

Changed paths:
  R engines/tetraedge/game/loading_menu.cpp
  R engines/tetraedge/game/loading_menu.h
  R engines/tetraedge/te/te_screen.cpp
  R engines/tetraedge/te/te_screen.h
  R engines/tetraedge/te/te_sfx.cpp
  R engines/tetraedge/te/te_sfx.h
    engines/tetraedge/credits.pl
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/application.h
    engines/tetraedge/game/billboard.cpp
    engines/tetraedge/game/bonus_menu.cpp
    engines/tetraedge/game/cellphone.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/characters_shadow.cpp
    engines/tetraedge/game/confirm.cpp
    engines/tetraedge/game/confirm.h
    engines/tetraedge/game/credits.cpp
    engines/tetraedge/game/documents_browser.cpp
    engines/tetraedge/game/gallery_menu.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game_achievements.h
    engines/tetraedge/game/global_bonus_menu.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/loc_file.cpp
    engines/tetraedge/game/loc_file.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/main_menu.cpp
    engines/tetraedge/game/object3d.cpp
    engines/tetraedge/game/objectif.cpp
    engines/tetraedge/game/owner_error_menu.cpp
    engines/tetraedge/game/question2.cpp
    engines/tetraedge/game/question2.h
    engines/tetraedge/game/splash_screens.cpp
    engines/tetraedge/game/splash_screens.h
    engines/tetraedge/module.mk
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture.h
    engines/tetraedge/te/te_act_zone.cpp
    engines/tetraedge/te/te_animation.h
    engines/tetraedge/te/te_button_layout.h
    engines/tetraedge/te/te_camera.h
    engines/tetraedge/te/te_checkbox_layout.h
    engines/tetraedge/te/te_clip_layout.cpp
    engines/tetraedge/te/te_clip_layout.h
    engines/tetraedge/te/te_core.h
    engines/tetraedge/te/te_font3.h
    engines/tetraedge/te/te_frame_anim.h
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_i_loc.cpp
    engines/tetraedge/te/te_i_loc.h
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_image.h
    engines/tetraedge/te/te_list_layout.h
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_material.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_palette.h
    engines/tetraedge/te/te_real_timer.h
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_sprite_layout.cpp
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_tiled_surface.h
    engines/tetraedge/te/te_tiled_texture.cpp
    engines/tetraedge/te/te_tiled_texture.h
    engines/tetraedge/te/te_timer.cpp
    engines/tetraedge/tetraedge.h


diff --git a/engines/tetraedge/credits.pl b/engines/tetraedge/credits.pl
index a14c198dbb6..c5d7254ef82 100644
--- a/engines/tetraedge/credits.pl
+++ b/engines/tetraedge/credits.pl
@@ -1,3 +1,3 @@
 begin_section("Tetraedge");
-	add_person("Matthew Duggan", "Handle 1", "");
+	add_person("Matthew Duggan", "stauff", "");
 end_section();
diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index e05f2bcbd40..493a24fdfd8 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -88,11 +88,10 @@ void Application::create() {
 	// See TeMainWindowBase::initCamera
 	_mainWindowCamera.reset(new TeCamera());
 	_mainWindowCamera->setName("_mainWinCam");
-	_mainWindowCamera->_projectionMatrixType = 4;
+	_mainWindowCamera->setProjMatrixType(4);
 	_mainWindowCamera->viewport(0, 0, winWidth, winHeight);
 	_mainWindowCamera->orthogonalParams(winWidth * -0.5f, winWidth * 0.5f, winHeight * 0.5f, winHeight * -0.5f);
-	_mainWindowCamera->_orthNearVal = -2048.0f;
-	_mainWindowCamera->_orthFarVal = 2048.0f;
+	_mainWindowCamera->setOrthoPlanes(-2048.0f, 2048.0f);
 
 	_mainWindow.setSize(TeVector3f32(winWidth, winHeight, 0.0));
 	_mainWindow.setSizeType(TeILayout::ABSOLUTE);
@@ -421,10 +420,10 @@ void Application::performRender() {
 	TeRenderer *renderer = g_engine->getRenderer();
 
 	if (_drawShadows && game->running() && game->scene()._character
-			&& game->scene()._shadowLightNo != -1
-			&& game->scene()._charactersShadow != nullptr) {
+			&& game->scene().shadowLightNo() != -1
+			&& game->scene().charactersShadow() != nullptr) {
 		renderer->shadowMode(TeRenderer::ShadowMode1);
-		game->scene()._charactersShadow->createTexture(&game->scene());
+		game->scene().charactersShadow()->createTexture(&game->scene());
 		renderer->shadowMode(TeRenderer::ShadowMode0);
 	}
 
@@ -434,13 +433,13 @@ void Application::performRender() {
 	renderer->clearBuffer(GL_ACCUM);
 	if (game->running()) {
 		if (_drawShadows && game->scene()._character
-			&& game->scene()._shadowLightNo != -1
-			&& game->scene()._charactersShadow != nullptr) {
+			&& game->scene().shadowLightNo() != -1
+			&& game->scene().charactersShadow() != nullptr) {
 			TeIntrusivePtr<TeCamera> currentCamera = game->scene().currentCamera();
 			if (currentCamera) {
 				currentCamera->apply();
 				renderer->shadowMode(TeRenderer::ShadowMode2);
-				game->scene()._charactersShadow->draw(&game->scene());
+				game->scene().charactersShadow()->draw(&game->scene());
 				renderer->shadowMode(TeRenderer::ShadowMode0);
 			}
 		}
@@ -489,8 +488,7 @@ void Application::getSavegameThumbnail(Graphics::Surface &thumb) {
 	Graphics::Surface screen;
 	_visFade.texture()->writeTo(screen);
 	screen.flipVertical(Common::Rect(screen.w, screen.h));
-    Common::ScopedPtr<Graphics::Surface> scaledScreen(screen.scale(kThumbnailWidth, kThumbnailHeight2, true));
-    //scaledScreen->convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
+    Common::ScopedPtr<Graphics::Surface> scaledScreen(screen.scale(kThumbnailWidth, kThumbnailHeight2));
     thumb.copyFrom(*scaledScreen);
     screen.free();
     scaledScreen->free();
diff --git a/engines/tetraedge/game/application.h b/engines/tetraedge/game/application.h
index 9d88c203c76..37afa2d40e0 100644
--- a/engines/tetraedge/game/application.h
+++ b/engines/tetraedge/game/application.h
@@ -104,20 +104,20 @@ public:
 	int &difficulty() { return _difficulty; }
 	bool &tutoActivated() { return _tutoActivated; }
 
-	// TODO: Add accessors for these and make them private.
+	void setFinishedGame(bool val) { _finishedGame = val; }
+	void setFinishedFremium(bool val) { _finishedFremium = val; }
+	const Common::String &firstWarpPath() { return _firstWarpPath; }
+	const Common::String &firstZone() { return _firstZone; }
+	const Common::String &firstScene() { return _firstScene; }
+	TeLayout &frontLayout() { return _frontLayout; };
+	TeLayout &frontOrientationLayout() { return _frontOrientationLayout; }
+	TeLayout &backLayout() { return _backLayout; }
+	LocFile &loc() { return _loc; }
+
+private:
 	bool _finishedGame;
 	bool _finishedFremium;
-	TeLayout _frontLayout;
-	TeLayout _frontOrientationLayout;
-	TeLayout _backLayout;
-	TeButtonLayout _lockCursorButton;
-	TeButtonLayout _lockCursorFromActionButton;
-	LocFile _loc;
-	Common::String _firstWarpPath;
-	Common::String _firstZone;
-	Common::String _firstScene;
 
-private:
 	TeVisualFade _visFade;
 	TeMusic _music;
 	TeSpriteLayout _appSpriteLayout;
@@ -125,8 +125,20 @@ private:
 	TeSpriteLayout _autoSaveIcon1;
 	TeSpriteLayout _autoSaveIcon2;
 
+	TeButtonLayout _lockCursorButton;
+	TeButtonLayout _lockCursorFromActionButton;
+
+	TeLayout _frontLayout;
+	TeLayout _frontOrientationLayout;
+	TeLayout _backLayout;
+
+	LocFile _loc;
+
 	Common::String _applicationTitle;
 	Common::String _versionString;
+	Common::String _firstWarpPath;
+	Common::String _firstZone;
+	Common::String _firstScene;
 
 	Common::Array<Common::String> _unrecalAnims;
 
diff --git a/engines/tetraedge/game/billboard.cpp b/engines/tetraedge/game/billboard.cpp
index 0d37ac6dcec..d3c8f002568 100644
--- a/engines/tetraedge/game/billboard.cpp
+++ b/engines/tetraedge/game/billboard.cpp
@@ -70,22 +70,22 @@ void Billboard::calcVertex() {
 	fx = _pos.x();
 	fy = _pos.y();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
-	_model->_meshes[0].setVertex(0, meshVertex);
+	_model->meshes()[0].setVertex(0, meshVertex);
 
 	fx = _pos.x();
 	fy = _pos.y() + _size.getY();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
-	_model->_meshes[0].setVertex(1, meshVertex);
+	_model->meshes()[0].setVertex(1, meshVertex);
 
 	fx = _pos.x() + _size.getX();
 	fy = _pos.y();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
-	_model->_meshes[0].setVertex(2, meshVertex);
+	_model->meshes()[0].setVertex(2, meshVertex);
 
 	fx = _pos.x() + _size.getX();
 	fy = _pos.y() + _size.getY();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
-	_model->_meshes[0].setVertex(3, meshVertex);
+	_model->meshes()[0].setVertex(3, meshVertex);
 }
 
 void Billboard::position(const TeVector3f32 &pos) {
diff --git a/engines/tetraedge/game/bonus_menu.cpp b/engines/tetraedge/game/bonus_menu.cpp
index f0ccb12b348..4dc9bdfb12c 100644
--- a/engines/tetraedge/game/bonus_menu.cpp
+++ b/engines/tetraedge/game/bonus_menu.cpp
@@ -36,7 +36,7 @@ void BonusMenu::enter(const Common::String &scriptName) {
 	if (!loaded)
 		error("BonusMenu::enter: failed to load %s", scriptName.c_str());
 	Application *app = g_engine->getApplication();
-	app->_frontLayout.addChild(layoutChecked("menu"));
+	app->frontLayout().addChild(layoutChecked("menu"));
 
 	buttonLayoutChecked("quitButton")->onMouseClickValidated().add(this, &BonusMenu::onQuitButton);
 
@@ -149,7 +149,7 @@ bool BonusMenu::onPictureButton() {
 
 	Application *app = g_engine->getApplication();
 	TeSpriteLayout *pictureLayout = spriteLayoutChecked("fullScreenPictureLayout");
-	app->_frontLayout.removeChild(pictureLayout);
+	app->frontLayout().removeChild(pictureLayout);
 	pictureLayout->setVisible(true);
 
 	return true;
diff --git a/engines/tetraedge/game/cellphone.cpp b/engines/tetraedge/game/cellphone.cpp
index f2a47e2c330..1fbc2a719ef 100644
--- a/engines/tetraedge/game/cellphone.cpp
+++ b/engines/tetraedge/game/cellphone.cpp
@@ -47,7 +47,7 @@ bool Cellphone::addNumber(const Common::String &num) {
 	layout->setTextSizeType(1);
 	layout->setTextSizeProportionalToWidth(46);
 	Common::String val("Unknown");
-	Common::String *locNum = g_engine->getCore()->loc()->text(num);
+	const Common::String *locNum = g_engine->getCore()->loc()->text(num);
 	if (locNum)
 		val = *locNum;
 
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 7827158b957..2ba77527a05 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -346,8 +346,8 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 		return false;
 
 	_characterSettings = _globalCharacterSettings->getVal(mname);
-	_model->_texturePath = Common::Path("models/Textures");
-	_model->_enableLights = true;
+	_model->setTexturePath("models/Textures");
+	_model->setEnableLights(true);
 	Common::Path modelPath("models");
 	modelPath.joinInPlace(_characterSettings._modelFileName);
 	if (!_model->load(modelPath))
@@ -356,8 +356,9 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 	_model->setName(mname);
 	_model->setScale(_characterSettings._defaultScale);
 
-	for (auto &mesh : _model->_meshes)
+	for (auto &mesh : _model->meshes())
 		mesh.setVisible(true);
+
 	// Set all mouthes not visible by default
 	_model->setVisibleByName("_B_", false);
 	// Set all eyes not visible by default
@@ -744,10 +745,10 @@ float Character::speedFromAnim(double msFromStart) {
 		return 0.0f;
 
 	TeIntrusivePtr<TeModelAnimation> modelAnim;
-	if (_model->_boneBlenders.empty()) {
+	if (_model->boneBlenders().empty()) {
 		modelAnim = _model->anim();
 	} else {
-		modelAnim = _model->_boneBlenders.back()->_anim;
+		modelAnim = _model->boneBlenders().back()->_anim;
 	}
 
 	if (!modelAnim)
diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
index 5bde6f4dc9c..e422b41def9 100644
--- a/engines/tetraedge/game/characters_shadow.cpp
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -42,8 +42,8 @@ void CharactersShadow::create(InGameScene *scene) {
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->enableTexture();
 	_camera = new TeCamera();
-	_camera->_projectionMatrixType = 2;
-	_camera->_somePerspectiveVal = 1.0;
+	_camera->setProjMatrixType(2);
+	_camera->setPerspectiveVal(1.0);
 	_camera->setName("_shadowCam");
 	_camera->viewport(0, 0, _texSize, _texSize);
 	Te3DTexture::unbind();
@@ -67,9 +67,8 @@ void CharactersShadow::createTexture(InGameScene *scene) {
 		_camera->setRotation(q2 * q1);
 		_camera->setPosition(light->position3d());
 	}
-	_camera->_fov = scene->shadowFov() * M_PI / 180.0;
-	_camera->_orthNearVal = scene->shadowNearPlane();
-	_camera->_orthFarVal = scene->shadowFarPlane();
+	_camera->setFov((float)(scene->shadowFov() * M_PI / 180.0));
+	_camera->setOrthoPlanes(scene->shadowNearPlane(), scene->shadowFarPlane());
 	_camera->apply();
 
 	glClearColor(0.0, 0.0, 0.0, 0.0);
@@ -154,10 +153,10 @@ void CharactersShadow::draw(InGameScene *scene) {
 	renderer->setCurrentColor(scene->shadowColor());
 
 	for (TeIntrusivePtr<TeModel> model : scene->zoneModels()) {
-		if (model->_meshes.size() > 0 && model->_meshes[0].materials().empty()) {
-			model->_meshes[0].defaultMaterial(TeIntrusivePtr<Te3DTexture>());
-			model->_meshes[0].materials()[0]._enableSomethingDefault0 = true;
-			model->_meshes[0].materials()[0]._diffuseColor = scene->shadowColor();
+		if (model->meshes().size() > 0 && model->meshes()[0].materials().empty()) {
+			model->meshes()[0].defaultMaterial(TeIntrusivePtr<Te3DTexture>());
+			model->meshes()[0].materials()[0]._enableSomethingDefault0 = true;
+			model->meshes()[0].materials()[0]._diffuseColor = scene->shadowColor();
 		}
 		model->draw();
 	}
diff --git a/engines/tetraedge/game/confirm.cpp b/engines/tetraedge/game/confirm.cpp
index c535e0a7c73..ff1bbdf6da4 100644
--- a/engines/tetraedge/game/confirm.cpp
+++ b/engines/tetraedge/game/confirm.cpp
@@ -39,7 +39,7 @@ void Confirm::enter(const Common::String &guiPath, const Common::String &y) {
 
 	Application *app = g_engine->getApplication();
 	TeButtonLayout *confirmButtonLayout = _gui.buttonLayout("confirm");
-	app->_frontOrientationLayout.addChild(confirmButtonLayout);
+	app->frontOrientationLayout().addChild(confirmButtonLayout);
 
 	TeButtonLayout *yesButtonLayout = _gui.buttonLayout("yes");
 	if (yesButtonLayout)
@@ -53,11 +53,11 @@ void Confirm::enter(const Common::String &guiPath, const Common::String &y) {
 	if (textLayout) {
 		const Common::String textAttributs = _gui.value("textAttributs").toString();
 		const Common::String textAttributsDown = _gui.value("textAttributsDown").toString();
-		const Common::String *okButtonLoc = app->_loc.value("okButton");
-		const Common::String *cancelButtonLoc = app->_loc.value("cancelButton");
+		const Common::String *okButtonLoc = app->loc().value("okButton");
+		const Common::String *cancelButtonLoc = app->loc().value("cancelButton");
 
 		TeTextLayout *textTextLayout = dynamic_cast<TeTextLayout *>(textLayout->child(0));
-		textTextLayout->setText(textAttributs + *app->_loc.value(textTextLayout->name()));
+		textTextLayout->setText(textAttributs + *app->loc().value(textTextLayout->name()));
 
 		if (!okButtonLoc || !cancelButtonLoc) {
 			error("Missing translations for ok and cancel");
@@ -89,8 +89,8 @@ void Confirm::enter(const Common::String &guiPath, const Common::String &y) {
 	}
 
 	// Make sure the mouse cursor is back on top.
-	app->_frontOrientationLayout.removeChild(&app->mouseCursorLayout());
-	app->_frontOrientationLayout.addChild(&app->mouseCursorLayout());
+	app->frontOrientationLayout().removeChild(&app->mouseCursorLayout());
+	app->frontOrientationLayout().addChild(&app->mouseCursorLayout());
 
 	if (ConfMan.get("skip_confirm") == "true") {
 		onButtonYes();
@@ -101,7 +101,7 @@ void Confirm::leave() {
 	Application *app = g_engine->getApplication();
 	TeButtonLayout *confirmButtonLayout = _gui.buttonLayout("confirm");
 	if (confirmButtonLayout) {
-		app->_frontLayout.removeChild(confirmButtonLayout);
+		app->frontLayout().removeChild(confirmButtonLayout);
 	}
 	_gui.unload();
 }
diff --git a/engines/tetraedge/game/confirm.h b/engines/tetraedge/game/confirm.h
index af21e62d15c..7e3e5a4a1ff 100644
--- a/engines/tetraedge/game/confirm.h
+++ b/engines/tetraedge/game/confirm.h
@@ -39,9 +39,13 @@ public:
 	bool onButtonNo();
 	bool onButtonYes();
 
+	TeSignal0Param &onButtonNoSignal() { return _onButtonNoSignal; }
+	TeSignal0Param &onButtonYesSignal() { return _onButtonYesSignal; }
+
+private:
+	TeLuaGUI _gui;
 	TeSignal0Param _onButtonNoSignal;
 	TeSignal0Param _onButtonYesSignal;
-	TeLuaGUI _gui;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/credits.cpp b/engines/tetraedge/game/credits.cpp
index b726fdb4294..ce784ad63be 100644
--- a/engines/tetraedge/game/credits.cpp
+++ b/engines/tetraedge/game/credits.cpp
@@ -37,7 +37,7 @@ void Credits::enter(bool returnToOptions) {
 	// TODO: set _field0x50 = 0;
 	_gui.load("menus/credits/credits.lua");
 	Application *app = g_engine->getApplication();
-	app->_frontLayout.addChild(_gui.layoutChecked("menu"));
+	app->frontLayout().addChild(_gui.layoutChecked("menu"));
 
 	Common::String musicPath = _gui.value("musicPath").toString();
 	if (!app->music().isPlaying() || app->music().path() != musicPath) {
@@ -117,7 +117,7 @@ void Credits::leave() {
 	if (_gui.loaded()) {
 		Application *app = g_engine->getApplication();
 		app->captureFade();
-		app->_frontLayout.removeChild(_gui.layoutChecked("menu"));
+		app->frontLayout().removeChild(_gui.layoutChecked("menu"));
 		_timer.stop();
 		_gui.unload();
 		if (_returnToOptions)
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
index badd29b8a6e..a99f231c728 100644
--- a/engines/tetraedge/game/documents_browser.cpp
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -190,7 +190,7 @@ void DocumentsBrowser::showDocument(const Common::String &docName, long startPag
 	TeSpriteLayout *sprite = _gui1.spriteLayoutChecked("zoomedSprite");
 	//sprite->setSizeType(ABSOLUTE);
 	sprite->load(docPath);
-	TeVector2s32 spriteSize = sprite->_tiledSurfacePtr->_tiledTexture->_totalSize;
+	TeVector2s32 spriteSize = sprite->_tiledSurfacePtr->tiledTexture()->totalSize();
 	sprite->setSizeType(RELATIVE_TO_PARENT);
 	TeVector3f32 winSize = app->getMainWindow().size();
 	sprite->setSize(TeVector3f32(1.0f, (4.0f / (winSize.y() / winSize.x() * 4.0f)) *
diff --git a/engines/tetraedge/game/gallery_menu.cpp b/engines/tetraedge/game/gallery_menu.cpp
index 2276481ab6c..d18e76147e5 100644
--- a/engines/tetraedge/game/gallery_menu.cpp
+++ b/engines/tetraedge/game/gallery_menu.cpp
@@ -83,7 +83,7 @@ void GalleryMenu::enter() {
 
 	load("menus/galleryMenu/galleryMenu.lua");
 	TeLayout *menu = layoutChecked("galleryMenu");
-	app->_frontLayout.addChild(menu);
+	app->frontLayout().addChild(menu);
 
 	game->stopSound(AMBIENT_SND_BIKE);
 	game->playSound(AMBIENT_SND_BIKE, -1, 0.1f);
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 7da4d3f43b4..fc631015e28 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -144,7 +144,7 @@ void Game::addNoScaleChildren() {
 	_noScaleLayout->addChild(&_question2);
 
 	Application *app = g_engine->getApplication();
-	app->_frontLayout.addChild(&_dialog2);
+	app->frontLayout().addChild(&_dialog2);
 
 	_noScaleLayout->addChild(&_inventory);
 	_noScaleLayout->addChild(&_inventoryMenu);
@@ -377,8 +377,8 @@ TeSpriteLayout *Game::findSpriteLayoutByName(TeLayout *parent, const Common::Str
 
 void Game::finishFreemium() {
 	Application *app = g_engine->getApplication();
-	app->_finishedGame = true;
-	app->_finishedFremium = true;
+	app->setFinishedGame(true);
+	app->setFinishedFremium(true);
 }
 
 void Game::finishGame() {
@@ -408,9 +408,9 @@ void Game::initLoadedBackupData() {
 				g_engine->setTotalPlayTime(header.playtime);
 		}
 	} else {
-		firstWarpPath = app->_firstWarpPath;
-		_currentScene = app->_firstScene;
-		_currentZone = app->_firstZone;
+		firstWarpPath = app->firstWarpPath();
+		_currentScene = app->firstScene();
+		_currentZone = app->firstZone();
 		_playedTimer.start();
 		_objectsTakenVal = 0;
 		for (int i = 0; i < ARRAYSIZE(_objectsTakenBits); i++) {
@@ -534,13 +534,13 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_forGui.load(forLuaPath);
 		TeLayout *bg = _forGui.layoutChecked("background");
 		bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
-		app->_frontLayout.addChild(bg);
+		app->frontLayout().addChild(bg);
 		// Note: Game also adds cellphone to both frontLayout *and* noScaleLayout2,
 		// so we reproduce the broken behavior exactly.
 		TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayoutChecked("background");
-		app->_frontLayout.removeChild(cellbg);
-		app->_frontLayout.addChild(cellbg);
-		_objectif.reattachLayout(&app->_frontLayout);
+		app->frontLayout().removeChild(cellbg);
+		app->frontLayout().addChild(cellbg);
+		_objectif.reattachLayout(&app->frontLayout());
 	}
 
 	if (intLuaExists) {
@@ -595,17 +595,17 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 
 	initNoScale();
 	removeNoScale2Children();
-	app->_frontLayout.removeChild(_noScaleLayout2);
+	app->frontLayout().removeChild(_noScaleLayout2);
 
 	TeLayout *vidLayout = _inGameGui.layout("videoLayout");
-	app->_frontLayout.removeChild(vidLayout);
+	app->frontLayout().removeChild(vidLayout);
 	removeNoScaleChildren();
-	app->_frontLayout.removeChild(_noScaleLayout);
+	app->frontLayout().removeChild(_noScaleLayout);
 
-	app->_frontLayout.addChild(_noScaleLayout);
+	app->frontLayout().addChild(_noScaleLayout);
 	addNoScaleChildren();
-	app->_frontLayout.addChild(vidLayout);
-	app->_frontLayout.addChild(_noScaleLayout2);
+	app->frontLayout().addChild(vidLayout);
+	app->frontLayout().addChild(_noScaleLayout2);
 	addNoScale2Children();
 	if (!fadeFlag) {
 		if (_inventory.selectedObject().size()) {
@@ -618,11 +618,11 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		loadScene("save.xml");
 	}
 
-	app->_backLayout.addChild(_scene.background());
+	app->backLayout().addChild(_scene.background());
 
 	if (markerLuaExists) {
 		TeLayout *bg = _scene.markerGui().layout("background");
-		app->_frontLayout.addChild(bg);
+		app->frontLayout().addChild(bg);
 	}
 
 	Common::String camname = Common::String("Camera") + scene;
@@ -693,7 +693,7 @@ static const char *DIALOG_IDS[20] = {
 bool Game::launchDialog(const Common::String &dname, uint param_2, const Common::String &charname,
 				  const Common::String &animfile, float animblend) {
 	Application *app = g_engine->getApplication();
-	const Common::String *locstring = app->_loc.value(dname);
+	const Common::String *locstring = app->loc().value(dname);
 
 	if (!locstring)
 		locstring = &dname;
@@ -787,8 +787,8 @@ void Game::leave(bool flag) {
 	_enteredFlag2 = false;
 
 	Application *app = g_engine->getApplication();
-	app->_lockCursorButton.setVisible(false);
-	app->_lockCursorFromActionButton.setVisible(false);
+	app->lockCursor(false);
+	app->lockCursorFromAction(false);
 	// TODO: Set some inputmgr flag here?
 	Character::animCacheFreeAll();
 }
@@ -1033,7 +1033,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 		}
 	}
 
-	if (!app->_frontLayout.isMouseIn(pt))
+	if (!app->frontLayout().isMouseIn(pt))
 		return false;
 
 	Common::String nearestMeshName = "None";
@@ -1280,7 +1280,7 @@ void Game::playMovie(const Common::String &vidPath, const Common::String &musicP
 
 	// Stop the movie and sound early for testing if skip_videos set
 	if (ConfMan.get("skip_videos") == "true") {
-		videoSpriteLayout->_tiledSurfacePtr->_frameAnim._nbFrames = 10;
+		videoSpriteLayout->_tiledSurfacePtr->_frameAnim.setNbFrames(10);
 		music.stop();
 	}
 
@@ -1408,7 +1408,7 @@ void Game::removeNoScaleChildren() {
 		return;
 	_noScaleLayout->removeChild(&_question2);
 	Application *app = g_engine->getApplication();
-	app->_frontLayout.removeChild(&_dialog2);
+	app->frontLayout().removeChild(&_dialog2);
 	_noScaleLayout->removeChild(&_inventory);
 	_noScaleLayout->removeChild(&_inventoryMenu);
 	_noScaleLayout->removeChild(&_documentsBrowser);
@@ -1450,6 +1450,9 @@ void Game::setCurrentObjectSprite(const Common::Path &spritePath) {
 }
 
 bool Game::showMarkers(bool val) {
+	if (!_forGui.loaded())
+		return false;
+
 	TeLayout *bg = _forGui.layoutChecked("background");
 	for (long i = 0; i < bg->childCount(); i++) {
 		const InGameScene::TeMarker *marker = _scene.findMarker(bg->child(i)->name());
@@ -1462,8 +1465,8 @@ bool Game::showMarkers(bool val) {
 bool Game::startAnimation(const Common::String &animName, int loopcount, bool reversed) {
 	TeSpriteLayout *layout = _scene.bgGui().spriteLayout(animName);
 	if (layout) {
-		layout->_tiledSurfacePtr->_frameAnim._loopCount = loopcount;
-		layout->_tiledSurfacePtr->_frameAnim._reversed = reversed;
+		layout->_tiledSurfacePtr->_frameAnim.setLoopCount(loopcount);
+		layout->_tiledSurfacePtr->_frameAnim.setReversed(reversed);
 		layout->_tiledSurfacePtr->play();
 	}
 	return layout != nullptr;
@@ -1565,7 +1568,7 @@ void Game::update() {
 
 		if (_scene._character) {
 			if (!_scene._character->_model->visible())
-				app->_lockCursorButton.setVisible(false);
+				app->lockCursor(false);
 		}
 
 		TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
diff --git a/engines/tetraedge/game/game_achievements.h b/engines/tetraedge/game/game_achievements.h
index c9cf6b9e75d..b1f3ba98567 100644
--- a/engines/tetraedge/game/game_achievements.h
+++ b/engines/tetraedge/game/game_achievements.h
@@ -32,10 +32,6 @@ public:
 	GameAchievements();
 
 	static void registerAchievements(TeLuaContext &context);
-	// TODO add public members
-
-private:
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/game/global_bonus_menu.cpp b/engines/tetraedge/game/global_bonus_menu.cpp
index 8bcbec1598b..d92e78aaf3a 100644
--- a/engines/tetraedge/game/global_bonus_menu.cpp
+++ b/engines/tetraedge/game/global_bonus_menu.cpp
@@ -35,7 +35,7 @@ void GlobalBonusMenu::enter() {
 	_entered = true;
 	load("menus/bonusmenu/GlobalBonusMenu.lua");
 	TeLayout *menu = layoutChecked("menu");
-	app->_frontLayout.addChild(menu);
+	app->frontLayout().addChild(menu);
 
 	// Original checks each layout's existence
 	TeButtonLayout *btn;
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 082230582b7..ec3d32449dd 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -102,7 +102,7 @@ bool InGameScene::addMarker(const Common::String &markerName, const Common::Stri
 		TeVector3f32 newPos;
 		if (locType == "PERCENT") {
 			Application *app = g_engine->getApplication();
-			TeVector3f32 frontLayoutSize = app->_frontLayout.userSize();
+			TeVector3f32 frontLayoutSize = app->frontLayout().userSize();
 			newPos.x() = frontLayoutSize.x() * (x / 100.0);
 			newPos.y() = frontLayoutSize.y() * (y / 100.0);
 		} else {
@@ -118,7 +118,7 @@ bool InGameScene::addMarker(const Common::String &markerName, const Common::Stri
 			markerSprite->setSize(TeVector3f32(0.04f, (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.04f, 0.0));
 		}
 		markerSprite->setVisible(game->markersVisible());
-		markerSprite->_tiledSurfacePtr->_frameAnim._loopCount = -1;
+		markerSprite->_tiledSurfacePtr->_frameAnim.setLoopCount(-1);
 		markerSprite->play();
 
 		TeMarker newMarker;
@@ -220,17 +220,17 @@ void InGameScene::close() {
 
 void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
 	TeIntrusivePtr<TeModel> model = new TeModel();
-	model->_meshes.resize(1);
+	model->meshes().resize(1);
 	model->setName("shadowReceiving");
 	model->setPosition(zone->position());
 	model->setRotation(zone->rotation());
 	model->setScale(zone->scale());
 	unsigned long nverticies = zone->verticies().size();
-	model->_meshes[0].setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
+	model->meshes()[0].setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
 	for (unsigned int i = 0; i < nverticies; i++) {
-		model->_meshes[0].setIndex(i, i);
-		model->_meshes[0].setVertex(i, zone->verticies()[i]);
-		model->_meshes[0].setNormal(i, TeVector3f32(0, 0, 1));
+		model->meshes()[0].setIndex(i, i);
+		model->meshes()[0].setVertex(i, zone->verticies()[i]);
+		model->meshes()[0].setNormal(i, TeVector3f32(0, 0, 1));
 	}
 	_zoneModels.push_back(model);
 }
@@ -277,16 +277,15 @@ void InGameScene::deleteMarker(const Common::String &markerName) {
 }
 
 void InGameScene::deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam) {
-	cam->_projectionMatrixType = 2;
+	cam->setProjMatrixType(2);
 	cam->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
 	// load name/position/rotation/scale
 	Te3DObject2::deserialize(stream, *cam);
-	cam->_fov = stream.readFloatLE();
-	cam->_somePerspectiveVal = stream.readFloatLE();
-	cam->_orthNearVal = stream.readFloatLE();
-	// Original loads the val then ignores it and sets 3000.
+	cam->setFov(stream.readFloatLE());
+	cam->setPerspectiveVal(stream.readFloatLE());
+	// Original loads the second val then ignores it and sets 3000.
+	cam->setOrthoPlanes(stream.readFloatLE(), 3000.0);
 	stream.readFloatLE();
-	cam->_orthFarVal = 3000.0;
 }
 
 void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<TeModel> &model, TePickMesh2 *pickmesh) {
@@ -755,7 +754,7 @@ bool InGameScene::loadObjectMaterials(const Common::String &name) {
 		if (img.load(mpath)) {
 			Te3DTexture *tex = new Te3DTexture();
 			tex->load(img);
-			obj._model->_meshes[0].defaultMaterial(tex);
+			obj._model->meshes()[0].defaultMaterial(tex);
 			retval = true;
 		}
 	}
@@ -858,7 +857,7 @@ void InGameScene::loadBackground(const Common::Path &path) {
 	root->setRatioMode(TeILayout::RATIO_MODE_NONE);
 	TeCamera *wincam = g_engine->getApplication()->mainWindowCamera();
 	bg->disableAutoZ();
-	bg->setZPosition(wincam->_orthNearVal);
+	bg->setZPosition(wincam->orthoNearPlane());
 
 	for (auto layoutEntry : _bgGui.spriteLayouts()) {
 		AnimObject *animobj = new AnimObject();
@@ -976,7 +975,7 @@ void InGameScene::setImagePathMarker(const Common::String &markerName, const Com
 			TeSpriteLayout *sprite = dynamic_cast<TeSpriteLayout *>(child);
 			if (sprite) {
 				sprite->load(path);
-				sprite->_tiledSurfacePtr->_frameAnim._loopCount = -1;
+				sprite->_tiledSurfacePtr->_frameAnim.setLoopCount(-1);
 				sprite->play();
 			}
 		}
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 7fb5318b2ed..2d6fdc46eb0 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -186,9 +186,9 @@ public:
 	float shadowNearPlane() const { return _shadowNearPlane; }
 	float shadowFov() const { return _shadowFov; }
 	const TeColor &shadowColor() const { return _shadowColor; }
+	int shadowLightNo() const { return _shadowLightNo; }
+	CharactersShadow *charactersShadow() { return _charactersShadow; }
 
-	int _shadowLightNo;
-	CharactersShadow *_charactersShadow;
 	TeIntrusivePtr<TeBezierCurve> curve() { return _curve; }
 	void setCurve(TeIntrusivePtr<TeBezierCurve> &c) { _curve = c; }
 	Common::Array<TeIntrusivePtr<TeModel>> &zoneModels() { return _zoneModels; }
@@ -199,7 +199,10 @@ public:
 	TeTimer &waitTimeTimer() { return _waitTimeTimer; }
 	Common::Array<TeLight> &lights() { return _lights; }
 
+
 private:
+	int _shadowLightNo;
+	CharactersShadow *_charactersShadow;
 	TeColor _shadowColor;
 	float _shadowFarPlane;
 	float _shadowNearPlane;
diff --git a/engines/tetraedge/game/loading_menu.cpp b/engines/tetraedge/game/loading_menu.cpp
deleted file mode 100644
index d8bc4a53afb..00000000000
--- a/engines/tetraedge/game/loading_menu.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/* 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/game/loading_menu.h"
-
-namespace Tetraedge {
-
-LoadingMenu::LoadingMenu() {
-}
-
-// TODO: Add more functions here.
-
-} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/loading_menu.h b/engines/tetraedge/game/loading_menu.h
deleted file mode 100644
index 34021f76d43..00000000000
--- a/engines/tetraedge/game/loading_menu.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* 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_LOADING_MENU_H
-#define TETRAEDGE_GAME_LOADING_MENU_H
-
-namespace Tetraedge {
-
-class LoadingMenu {
-public:
-	LoadingMenu();
-
-	// TODO add public members
-
-private:
-	// TODO add private members
-
-};
-
-} // end namespace Tetraedge
-
-#endif // TETRAEDGE_GAME_LOADING_MENU_H
diff --git a/engines/tetraedge/game/loc_file.cpp b/engines/tetraedge/game/loc_file.cpp
index 89b43242403..c89f69502d9 100644
--- a/engines/tetraedge/game/loc_file.cpp
+++ b/engines/tetraedge/game/loc_file.cpp
@@ -54,7 +54,7 @@ void LocFile::load(const Common::Path &path) {
 	_map = parser.getMap();
 }
 
-const Common::String *LocFile::value(const Common::String &key) {
+const Common::String *LocFile::value(const Common::String &key) const {
 	return text(key);
 }
 
diff --git a/engines/tetraedge/game/loc_file.h b/engines/tetraedge/game/loc_file.h
index 861229b22e4..77051a5b2f4 100644
--- a/engines/tetraedge/game/loc_file.h
+++ b/engines/tetraedge/game/loc_file.h
@@ -35,7 +35,7 @@ public:
 
 	//const Common::String *avatar(const Common::String &key);
 	void load(const Common::Path &path);
-	const Common::String *value(const Common::String &key);
+	const Common::String *value(const Common::String &key) const;
 
 };
 
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index b06f6c3e2fe..52156f6c68d 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -1304,7 +1304,7 @@ static int tolua_ExportedFunctions_LaunchDialogAndWaitForEnd00(lua_State *L) {
 
 static void PushAnswer(const Common::String &val, const Common::String &gui) {
 	Application *app = g_engine->getApplication();
-	const Common::String *locVal = app->_loc.value(val);
+	const Common::String *locVal = app->loc().value(val);
 	Common::String locValStr;
 	if (locVal) {
 		locValStr = *locVal;
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
index 243b736fd61..a3deb90f071 100644
--- a/engines/tetraedge/game/main_menu.cpp
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -41,10 +41,10 @@ namespace Tetraedge {
 static const char *LAST_SAVE_CONF = "lastSaveSlot";
 
 MainMenu::MainMenu() : _entered(false), _confirmingTuto(false) {
-	_newGameConfirm._onButtonYesSignal.add(this, &MainMenu::onNewGameConfirmed);
-	_tutoConfirm._onButtonYesSignal.add(this, &MainMenu::onActivedTuto);
-	_tutoConfirm._onButtonNoSignal.add(this, &MainMenu::onDisabledTuto);
-	_quitConfirm._onButtonYesSignal.add(this, &MainMenu::onQuit);
+	_newGameConfirm.onButtonYesSignal().add(this, &MainMenu::onNewGameConfirmed);
+	_tutoConfirm.onButtonYesSignal().add(this, &MainMenu::onActivedTuto);
+	_tutoConfirm.onButtonNoSignal().add(this, &MainMenu::onDisabledTuto);
+	_quitConfirm.onButtonYesSignal().add(this, &MainMenu::onQuit);
 	onFacebookLoggedSignal.add(this, &MainMenu::onFacebookLogged);
 }
 
@@ -54,7 +54,7 @@ void MainMenu::enter() {
 	appSpriteLayout.setVisible(true);
 	if (!appSpriteLayout._tiledSurfacePtr->_frameAnim._runTimer.running()) {
 		appSpriteLayout.load("menus/menu.ogv");
-		appSpriteLayout._tiledSurfacePtr->_frameAnim._loopCount = -1;
+		appSpriteLayout._tiledSurfacePtr->_frameAnim.setLoopCount(-1);
 		appSpriteLayout._tiledSurfacePtr->play();
 	}
 	app->captureFade();
diff --git a/engines/tetraedge/game/object3d.cpp b/engines/tetraedge/game/object3d.cpp
index 493a4280b7d..4006e12ee71 100644
--- a/engines/tetraedge/game/object3d.cpp
+++ b/engines/tetraedge/game/object3d.cpp
@@ -48,7 +48,7 @@ bool Object3D::loadModel(const Common::String &name) {
 	if (settings != _objectSettings->end()) {
 		_modelFileName = settings->_value._modelFileName;
 		_defaultScale = settings->_value._defaultScale;
-		_modelPtr->_texturePath = Common::Path("objects/Textures");
+		_modelPtr->setTexturePath("objects/Textures");
 		bool loaded = _modelPtr->load(Common::Path("objects").join(_modelFileName));
 		if (loaded) {
 			_modelPtr->setName(name);
diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
index 1f73149148d..6d274095cb0 100644
--- a/engines/tetraedge/game/objectif.cpp
+++ b/engines/tetraedge/game/objectif.cpp
@@ -61,7 +61,7 @@ void Objectif::load() {
 	_gui2.load("menus/helpButton.lua");
 
 	TeButtonLayout *btn = _gui2.buttonLayoutChecked("helpButton");
-	app->_frontLayout.addChild(btn);
+	app->frontLayout().addChild(btn);
 	btn->setVisible(true);
 	_helpButtonVisible = true;
 	btn->onMouseClickValidated().add(this, &Objectif::onHelpButtonValidated);
diff --git a/engines/tetraedge/game/owner_error_menu.cpp b/engines/tetraedge/game/owner_error_menu.cpp
index ecb12101a99..4694698a4b5 100644
--- a/engines/tetraedge/game/owner_error_menu.cpp
+++ b/engines/tetraedge/game/owner_error_menu.cpp
@@ -38,9 +38,9 @@ void OwnerErrorMenu::enter() {
 	load(luaPath);
 	Application *app = g_engine->getApplication();
 	TeLayout *menuLayout = layoutChecked("menu");
-	app->_frontLayout.addChild(menuLayout);
+	app->frontLayout().addChild(menuLayout);
 	TeTextLayout *txt = dynamic_cast<TeTextLayout*>(layoutChecked("ownerMenuText"));
-	const Common::String *locname = app->_loc.value(txt->name());
+	const Common::String *locname = app->loc().value(txt->name());
 	txt->setText(value("textAttributs").toString() + (locname ? *locname : txt->name()));
 }
 
diff --git a/engines/tetraedge/game/question2.cpp b/engines/tetraedge/game/question2.cpp
index 09ce43b64ad..29e17670877 100644
--- a/engines/tetraedge/game/question2.cpp
+++ b/engines/tetraedge/game/question2.cpp
@@ -115,11 +115,10 @@ void Question2::pushAnswer(const Common::String &name, const Common::String &loc
 		xpos = 0.15f;
 	}
 	blayout->setPosition(TeVector3f32(xpos, _answers.size() * 0.08f + 0.06f, 1.0f));
-
-	blayout->_upLayout->setSizeType(RELATIVE_TO_PARENT);
-	blayout->_upLayout->setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
-	blayout->_downLayout->setSizeType(RELATIVE_TO_PARENT);
-	blayout->_downLayout->setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
+	blayout->upLayout()->setSizeType(RELATIVE_TO_PARENT);
+	blayout->upLayout()->setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
+	blayout->downLayout()->setSizeType(RELATIVE_TO_PARENT);
+	blayout->downLayout()->setSize(TeVector3f32(1.0f, 1.0f, 1.0f));
 
 	TeSpriteLayout *calepinLayout = _gui.spriteLayoutChecked("Calepin");
 	calepinLayout->addChild(blayout);
@@ -142,7 +141,7 @@ void Question2::Answer::load(const Common::String &name, const Common::String &l
 	TeButtonLayout *answerButton = _gui.buttonLayout("answer");
 	if (answerButton) {
 		answerButton->onMouseClickValidated().add(this, &Question2::Answer::onButtonValidated);
-		answerButton->_ignoreMouseEvents = false;
+		answerButton->setIgnoreMouseEvents(false);
 	}
 }
 
diff --git a/engines/tetraedge/game/question2.h b/engines/tetraedge/game/question2.h
index bc171484aae..86201e499d5 100644
--- a/engines/tetraedge/game/question2.h
+++ b/engines/tetraedge/game/question2.h
@@ -56,8 +56,8 @@ public:
 	TeSignal1Param<const Common::String &> &onAnswerSignal() { return _onAnswerSignal; }
 
 private:
-	TeLuaGUI _gui;
 	Common::Array<Answer *> _answers;
+	TeLuaGUI _gui;
 	TeSignal1Param<const Common::String &> _onAnswerSignal;
 
 };
diff --git a/engines/tetraedge/game/splash_screens.cpp b/engines/tetraedge/game/splash_screens.cpp
index 28dad2adcf4..22c88aed33d 100644
--- a/engines/tetraedge/game/splash_screens.cpp
+++ b/engines/tetraedge/game/splash_screens.cpp
@@ -43,7 +43,7 @@ void SplashScreens::enter()	{
 			TeLuaGUI::load(scriptPath.toString());
 			Application *app = g_engine->getApplication();
 			TeLayout *splash = layout("splash");
-			app->_frontLayout.addChild(splash);
+			app->frontLayout().addChild(splash);
 			app->performRender();
 		}
 		onAlarm();
@@ -72,7 +72,7 @@ bool SplashScreens::onAlarm() {
 		btnLayout->onMouseClickValidated().add(this, &SplashScreens::onQuitSplash);
 
 		TeLayout *splash = layout("splash");
-		app->_frontLayout.addChild(splash);
+		app->frontLayout().addChild(splash);
 
 		_timer.start();
 		_timer.setAlarmIn(1500000);
diff --git a/engines/tetraedge/game/splash_screens.h b/engines/tetraedge/game/splash_screens.h
index b393dca3991..b89025eb8e1 100644
--- a/engines/tetraedge/game/splash_screens.h
+++ b/engines/tetraedge/game/splash_screens.h
@@ -41,8 +41,8 @@ public:
 
 private:
 	bool _entered;
-	TeTimer _timer;
 	int _splashNo;
+	TeTimer _timer;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index 12de439554e..01df59acb24 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -27,7 +27,6 @@ MODULE_OBJS := \
 	game/inventory_menu.o \
 	game/inventory_object.o \
 	game/inventory_objects_xml_parser.o \
-	game/loading_menu.o \
 	game/loc_file.o \
 	game/lua_binds.o \
 	game/main_menu.o \
@@ -94,10 +93,8 @@ MODULE_OBJS := \
 	te/te_resource.o \
 	te/te_resource_manager.o \
 	te/te_scene.o \
-	te/te_screen.o \
 	te/te_scrolling_layout.o \
 	te/te_scummvm_codec.o \
-	te/te_sfx.o \
 	te/te_sound_manager.o \
 	te/te_sprite_layout.o \
 	te/te_text_base2.o \
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index 28fb1cdb3cb..a40b475343d 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -154,7 +154,7 @@ bool Te3DTexture::load(const TeImage &img) {
 
 	_width = img.w;
 	_height = img.h;
-	_format = img._format;
+	_format = img.teFormat();
 
 	// TODO? set some other fields from the image here.
 	// for now just set some good defaults.
diff --git a/engines/tetraedge/te/te_3d_texture.h b/engines/tetraedge/te/te_3d_texture.h
index 2a37d0ad7c6..b217d8cf6b8 100644
--- a/engines/tetraedge/te/te_3d_texture.h
+++ b/engines/tetraedge/te/te_3d_texture.h
@@ -61,12 +61,14 @@ public:
 
 	void writeTo(Graphics::Surface &surf);
 
-	int _numFrames;
-	int _frameRate;
-	uint _width;
-	uint _height;
+	uint width() const { return _width; }
+	uint height() const { return _height; }
 
 private:
+	uint _width;
+	uint _height;
+	int _numFrames;
+	int _frameRate;
 	TeImage::Format _format;
 	bool _createdTexture;
 	bool _loaded;
diff --git a/engines/tetraedge/te/te_act_zone.cpp b/engines/tetraedge/te/te_act_zone.cpp
index e6ec2ca3b09..092717f4441 100644
--- a/engines/tetraedge/te/te_act_zone.cpp
+++ b/engines/tetraedge/te/te_act_zone.cpp
@@ -26,6 +26,4 @@ namespace Tetraedge {
 TeActZone::TeActZone() {
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_animation.h b/engines/tetraedge/te/te_animation.h
index 41ee20eec7e..576b0688776 100644
--- a/engines/tetraedge/te/te_animation.h
+++ b/engines/tetraedge/te/te_animation.h
@@ -55,9 +55,9 @@ public:
 
 	TeTimer _runTimer;
 	int _repeatCount;
-	bool _dontRepeat;
 
 protected:
+	bool _dontRepeat;
 	TeSignal0Param _onStopSignal;
 	TeSignal0Param _onFinishedSignal;
 
diff --git a/engines/tetraedge/te/te_button_layout.h b/engines/tetraedge/te/te_button_layout.h
index 66c3b03481e..74abb0d398e 100644
--- a/engines/tetraedge/te/te_button_layout.h
+++ b/engines/tetraedge/te/te_button_layout.h
@@ -93,16 +93,18 @@ public:
 	TeSignal0Param &onButtonChangedToStateDownSignal() { return _onButtonChangedToStateDownSignal; };
 	TeSignal0Param &onButtonChangedToStateRolloverSignal() { return _onButtonChangedToStateRolloverSignal; };
 
-	bool _ignoreMouseEvents;
-	TeLayout *_upLayout;
-	TeLayout *_downLayout;
+	TeLayout *upLayout() { return _upLayout; }
+	TeLayout *downLayout() { return _downLayout; }
+	void setIgnoreMouseEvents(bool val) { _ignoreMouseEvents = val; }
 
 private:
 	static bool _mousePositionChangedCatched;
-	bool _doubleValidationProtectionEnabled;
 	static TeTimer *getDoubleValidationProtectionTimer();
 	static TeTimer *_doubleValidationProtectionTimer;
 
+	bool _doubleValidationProtectionEnabled;
+	bool _ignoreMouseEvents;
+
 	State _currentState;
 	bool _clickPassThrough;
 	Common::String _validationSound;
@@ -119,6 +121,8 @@ private:
 	TeLayout *_rolloverLayout;
 	TeLayout *_disabledLayout;
 	TeLayout *_hitZoneLayout;
+	TeLayout *_upLayout;
+	TeLayout *_downLayout;
 
 	TeSignal0Param _onMouseClickValidatedSignal;
 	TeSignal0Param _onButtonChangedToStateUpSignal;
diff --git a/engines/tetraedge/te/te_camera.h b/engines/tetraedge/te/te_camera.h
index 43d8e4ae854..0d73a0a1859 100644
--- a/engines/tetraedge/te/te_camera.h
+++ b/engines/tetraedge/te/te_camera.h
@@ -64,20 +64,32 @@ public:
 	TeMatrix4x4 transformationMatrix();
 	TeVector3f32 transformCoord(const TeVector3f32 &pt);
 	TeVector3f32 transformPoint2Dto3D(const TeVector3f32 &pt);
-	void updateProjectionMatrix();
 
 	void viewport(int x, int y, uint width, uint height);
 	TeVector2f32 viewportSize() const { return TeVector2f32(_viewportW, _viewportH); }
 
 	TeSignal0Param &onViewportChangedSignal() { return _onViewportChangedSignal; }
 
-	int _projectionMatrixType;
+	void setFov(float fov) { _fov = fov; }
+	void setOrthoPlanes(float near, float far) {
+		_orthFarVal = far;
+		_orthNearVal = near;
+	}
+	void setProjMatrixType(int matrixType) { _projectionMatrixType = matrixType; }
+	int projMatrixType() const { return _projectionMatrixType; }
+	void setPerspectiveVal(float val) { _somePerspectiveVal = val; }
+	float orthoNearPlane() const { return _orthNearVal; }
+	float orthoFarPlane() const { return _orthNearVal; }
+
+private:
+	void updateProjectionMatrix();
+
+	int _projectionMatrixType; // TODO: Should be an enum.
 	float _orthNearVal;
 	float _orthFarVal;
 	float _fov;
 	float _somePerspectiveVal;
 
-private:
 	int _viewportX;
 	int _viewportY;
 	uint _viewportW;
diff --git a/engines/tetraedge/te/te_checkbox_layout.h b/engines/tetraedge/te/te_checkbox_layout.h
index 11cb9def573..c5352a98a8a 100644
--- a/engines/tetraedge/te/te_checkbox_layout.h
+++ b/engines/tetraedge/te/te_checkbox_layout.h
@@ -70,6 +70,7 @@ private:
 	TeLayout *_activeRollOverLayout;
 	TeLayout *_unactiveRollOverLayout;
 	TeLayout *_hitZone;
+
 	bool _clickPassThrough;
 	Common::String _activationSound;
 	Common::String _unactivationSound;
diff --git a/engines/tetraedge/te/te_clip_layout.cpp b/engines/tetraedge/te/te_clip_layout.cpp
index e5a3e42cbf2..c3bb6ee8454 100644
--- a/engines/tetraedge/te/te_clip_layout.cpp
+++ b/engines/tetraedge/te/te_clip_layout.cpp
@@ -26,6 +26,4 @@ namespace Tetraedge {
 TeClipLayout::TeClipLayout() {
 }
 
-// TODO: Add more functions here.
-
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_clip_layout.h b/engines/tetraedge/te/te_clip_layout.h
index 6d788adb44a..3df4d741f80 100644
--- a/engines/tetraedge/te/te_clip_layout.h
+++ b/engines/tetraedge/te/te_clip_layout.h
@@ -26,15 +26,11 @@
 
 namespace Tetraedge {
 
+// Not used in Syberia1
 class TeClipLayout : public TeLayout {
 public:
 	TeClipLayout();
 
-	// TODO add public members
-
-private:
-	// TODO add private members
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_core.h b/engines/tetraedge/te/te_core.h
index 8785a29f040..ec0f518d981 100644
--- a/engines/tetraedge/te/te_core.h
+++ b/engines/tetraedge/te/te_core.h
@@ -64,6 +64,7 @@ public:
 	Common::Path findFile(const Common::Path &path);
 
 	bool _coreNotReady;
+
 private:
 	TeILoc *_loc;
 
diff --git a/engines/tetraedge/te/te_font3.h b/engines/tetraedge/te/te_font3.h
index 7350d355049..e30918d66f1 100644
--- a/engines/tetraedge/te/te_font3.h
+++ b/engines/tetraedge/te/te_font3.h
@@ -56,7 +56,6 @@ public:
 		AlignCenter
 	};
 
-	class FontSizeData {};
 	struct GlyphData {
 		uint32 _charcode;
 		Common::Rect _bitmapSize;
@@ -65,7 +64,6 @@ public:
 
 	bool load(const Common::Path &path);
 	void unload();
-	void init();
 
 	GlyphData glyph(unsigned int size, unsigned int charcode);
 
@@ -77,13 +75,15 @@ public:
 		return _fontSizeData[size];
 	}
 
-	int wordWrapText(const Common::String &str, int fontSize, int maxWidth, Common::Array<Common::String> &lines);
 	Common::Rect getBoundingBox(const Common::String &str, int fontSize);
 	int getHeight(int fontSize);
 
 	void draw(TeImage &destImage, const Common::String &str, int fontSize, int yoff, const TeColor &col, AlignStyle alignMode);
 
+	int wordWrapText(const Common::String &str, int fontSize, int maxWidth, Common::Array<Common::String> &lines);
+
 private:
+	void init();
 	Graphics::Font *getAtSize(unsigned int size);
 
 	Common::File _fontFile;
diff --git a/engines/tetraedge/te/te_frame_anim.h b/engines/tetraedge/te/te_frame_anim.h
index ca717ee6588..cfe2cb5fc54 100644
--- a/engines/tetraedge/te/te_frame_anim.h
+++ b/engines/tetraedge/te/te_frame_anim.h
@@ -36,20 +36,31 @@ public:
 
 	TeSignal0Param &frameChangedSignal() { return _frameChangedSignal; };
 
-	int _nbFrames;
+	void setFrameRate(float rate) { _frameRate = rate; }
+	void setNbFrames(int frames) { _nbFrames = frames; }
+	void setLoopCount(int count) { _loopCount = count; }
+	void setReversed(bool reverse) { _reversed = reverse; }
+
+	void setStartTime(double start) { _startTime = start; }
+	void setEndTime(double end) { _endTime = end; }
+
+	int lastFrameShown() const { return _lastFrameShown; }
+
+private:
 	float _frameRate;
 	int _loopCount;
+	int _nbFrames;
+	int _numFramesToShow;
+
 	bool _reversed;
+
 	int _lastFrameShown;
 	int _minFrame;
-	int _numFramesToShow;
 
 	double _startTime;
 	double _endTime;
 
-private:
 	TeSignal0Param _frameChangedSignal;
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 212827f9c75..77b5be41363 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -807,7 +807,7 @@ TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, c
 			Common::Array<TePickMesh2*> &pickMeshes, TeVector3f32 *outloc, bool lastHitFirst) {
 	TeVector3f32 closestLoc;
 	TePickMesh2 *nearestMesh = nullptr;
-	float closestDist = camera->_orthFarVal;
+	float closestDist = camera->orthoFarPlane();
 	Math::Ray camRay;
 	for (unsigned int i = 0; i < pickMeshes.size(); i++) {
 		TePickMesh2 *mesh = pickMeshes[i];
@@ -825,7 +825,7 @@ TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, c
 			TeVector3f32 intersectLoc;
 			float intersectDist;
 			bool intResult = camRay.intersectTriangle(v1, v2, v3, intersectLoc, intersectDist);
-			if (intResult && intersectDist < closestDist && intersectDist >= camera->_orthNearVal)
+			if (intResult && intersectDist < closestDist && intersectDist >= camera->orthoNearPlane())
 				return mesh;
 		}
 		for (unsigned int tri = 0; tri < mesh->verticies().size() / 3; tri++) {
@@ -841,7 +841,7 @@ TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, c
 					TeVector3f32(camRay.getDirection()).dump().c_str(),
 					v1.dump().c_str(), v2.dump().c_str(), v3.dump().c_str(),
 					intResult ? "hit!" : "no hit");*/
-			if (intResult && intersectDist < closestDist && intersectDist >= camera->_orthNearVal) {
+			if (intResult && intersectDist < closestDist && intersectDist >= camera->orthoNearPlane()) {
 				mesh->setLastTriangleHit(tri);
 				closestLoc = intersectLoc;
 				closestDist = intersectDist;
diff --git a/engines/tetraedge/te/te_i_loc.cpp b/engines/tetraedge/te/te_i_loc.cpp
index 4857660beff..54fc157b5f6 100644
--- a/engines/tetraedge/te/te_i_loc.cpp
+++ b/engines/tetraedge/te/te_i_loc.cpp
@@ -26,7 +26,7 @@ namespace Tetraedge {
 TeILoc::TeILoc() {
 }
 
-Common::String *TeILoc::text(const Common::String &key) {
+const Common::String *TeILoc::text(const Common::String &key) const {
 	if (!_map.contains(key)) {
 		return nullptr;
 	}
diff --git a/engines/tetraedge/te/te_i_loc.h b/engines/tetraedge/te/te_i_loc.h
index 3293520332f..3296a5b74e4 100644
--- a/engines/tetraedge/te/te_i_loc.h
+++ b/engines/tetraedge/te/te_i_loc.h
@@ -32,7 +32,7 @@ public:
 	TeILoc();
 	virtual ~TeILoc() {};
 
-	virtual Common::String *text(const Common::String &key);
+	virtual const Common::String *text(const Common::String &key) const;
 
 protected:
 	Common::StringMap _map;
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index 49deda8b37e..01d5a638eaf 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -28,7 +28,7 @@
 
 namespace Tetraedge {
 
-TeImage::TeImage() : ManagedSurface(), _format(INVALID) {
+TeImage::TeImage() : ManagedSurface(), _teFormat(INVALID) {
 }
 
 TeImage::TeImage(const TeImage &other) {
@@ -50,7 +50,7 @@ void TeImage::create() {
 
 void TeImage::create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
 			Format teformat, uint bufxsize, uint bufysize) {
-	_format = teformat;
+	_teFormat = teformat;
 	Graphics::PixelFormat pxformat = ((teformat == TeImage::RGB8) ?
 									  Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0) : Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
 
@@ -64,7 +64,7 @@ void TeImage::deserialize(Common::ReadStream &stream) {
 
 void TeImage::destroy() {
 	Graphics::ManagedSurface::free();
-	_format = INVALID;
+	_teFormat = INVALID;
 }
 
 void TeImage::drawPlot(void *outbuf, int x, int y, const TeVector2s32 &bufsize, const TeColor &col) {
diff --git a/engines/tetraedge/te/te_image.h b/engines/tetraedge/te/te_image.h
index 16dacc6e9ae..e363ef3d574 100644
--- a/engines/tetraedge/te/te_image.h
+++ b/engines/tetraedge/te/te_image.h
@@ -80,13 +80,10 @@ public:
 	TeVector2s32 bufSize() const {
 		return TeVector2s32(pitch / format.bytesPerPixel, h);
 	}
-
-	Format _format;
+	Format teFormat() const { return _teFormat; }
 
 private:
-
-	// No private members?
-
+	Format _teFormat;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_list_layout.h b/engines/tetraedge/te/te_list_layout.h
index b631891c8b3..15d10a60540 100644
--- a/engines/tetraedge/te/te_list_layout.h
+++ b/engines/tetraedge/te/te_list_layout.h
@@ -30,11 +30,18 @@ class TeListLayout : public TeLayout {
 public:
 	TeListLayout();
 
+	void setMinimumMargin(const TeVector3f32 &val) { _minimumMargin = val; }
+	void setMaximumMargin(const TeVector3f32 &val) { _maximumMargin = val; }
+	void setDirection(const TeVector3f32 &val) { _direction = val; }
+
+	const TeVector3f32 &minimumMargin() { return _minimumMargin; }
+	const TeVector3f32 &maximumMargin() { return _maximumMargin; }
+	const TeVector3f32 &direction() { return _direction; }
+
+private:
 	TeVector3f32 _minimumMargin;
 	TeVector3f32 _maximumMargin;
 	TeVector3f32 _direction;
-private:
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index 84cc5a90c54..c76e1316041 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -47,7 +47,7 @@ static Common::String TeLuaToTeString(lua_State *L, int index) {
 	}
 }
 
-long TeLuaToS32(lua_State *L, int index) {
+static long TeLuaToS32(lua_State *L, int index) {
 	if (!lua_isnumber(L, index)) {
 		warning("TeLuaToS32:: not a number");
 		return 0;
@@ -83,7 +83,6 @@ static bool TeLuaToBool(lua_State *L, int index) {
 	}
 }
 
-
 static TeColor TeLuaToTeColor(lua_State *L, int index) {
 	TeColor retval(255, 255, 255, 255);
 	if (lua_type(L, index) != LUA_TTABLE) {
@@ -124,7 +123,6 @@ static TeColor TeLuaToTeColor(lua_State *L, int index) {
 	return retval;
 }
 
-
 static TeVector3f32 TeLuaToTeVector3f32(lua_State *L, int index, TeVector3f32 defaultVal) {
 	TeVector3f32 retval = defaultVal;
 	if (lua_type(L, index) != LUA_TTABLE) {
@@ -173,6 +171,7 @@ static Common::Array<float> TeLuaToFloatArray(lua_State *L, int index) {
 	return result;
 }
 
+
 static bool loadCommonLayoutItems(lua_State *L, const char *s, TeLayout *layout) {
 	if (!strcmp(s, "name")) {
 		layout->setName(TeLuaToTeString(L, -1));
@@ -229,7 +228,7 @@ int layoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
+					layout->setScale(TeVector3f32(0.75f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
@@ -275,21 +274,21 @@ int listLayoutBindings(lua_State *L) {
 			if (loadCommonLayoutItems(L, s, layout)) {
 				// do nothing.
 			} else if (!strcmp(s, "direction")) {
-				const TeVector3f32 lastDirection = layout->_direction;
+				const TeVector3f32 lastDirection = layout->direction();
 				const TeVector3f32 direction = TeLuaToTeVector3f32(L, -1, lastDirection);
-				layout->_direction = direction;
+				layout->setDirection(direction);
 			} else if (!strcmp(s, "minimumMargin")) {
-				const TeVector3f32 lastMinimumMargin = layout->_minimumMargin;
+				const TeVector3f32 lastMinimumMargin = layout->minimumMargin();
 				const TeVector3f32 minimumMargin = TeLuaToTeVector3f32(L, -1, lastMinimumMargin);
-				layout->_minimumMargin = minimumMargin;
+				layout->setMinimumMargin(minimumMargin);
 			} else if (!strcmp(s, "maximumMargin")) {
-				const TeVector3f32 lastMaximumMargin = layout->_maximumMargin;
+				const TeVector3f32 lastMaximumMargin = layout->maximumMargin();
 				const TeVector3f32 maximumMargin = TeLuaToTeVector3f32(L, -1, lastMaximumMargin);
-				layout->_maximumMargin = maximumMargin;
+				layout->setMaximumMargin(maximumMargin);
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
+					layout->setScale(TeVector3f32(0.75f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
@@ -364,11 +363,11 @@ int spriteLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "bottomCropping")) {
 				layout->_tiledSurfacePtr->setBottomCropping(TeLuaToF32(L, -1));
 			} else if (!strcmp(s, "loopCount")) {
-				layout->_tiledSurfacePtr->_frameAnim._loopCount = TeLuaToS32(L, -1);
+				layout->_tiledSurfacePtr->_frameAnim.setLoopCount(TeLuaToS32(L, -1));
 			} else if (!strcmp(s, "play")) {
 				playNow = TeLuaToBool(L, -1);
 			} else if (!strcmp(s, "reversed")) {
-				layout->_tiledSurfacePtr->_frameAnim._reversed = TeLuaToBool(L, -1);
+				layout->_tiledSurfacePtr->_frameAnim.setReversed(TeLuaToBool(L, -1));
 			} else if (!strcmp(s, "startingFrame")) {
 				startingFrame = TeLuaToU32(L, -1);
 			} else if (!strcmp(s, "endingFrame")) {
@@ -376,7 +375,7 @@ int spriteLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
+					layout->setScale(TeVector3f32(0.75f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.layoutBindings] Unreconized attribute : %s", s);
@@ -412,14 +411,14 @@ int spriteLayoutBindings(lua_State *L) {
 		layout->stop();
 	}
 
-	TeICodec *codec = layout->_tiledSurfacePtr->_codec;
+	TeICodec *codec = layout->_tiledSurfacePtr->codec();
 	if (codec) {
 		float frameRate = codec->frameRate();
-		layout->_tiledSurfacePtr->_frameAnim._startTime = (startingFrame / frameRate) * 1000.0 * 1000.0;
+		layout->_tiledSurfacePtr->_frameAnim.setStartTime((startingFrame / frameRate) * 1000.0 * 1000.0);
 		if (endingFrame == -1) {
-			layout->_tiledSurfacePtr->_frameAnim._endTime = FLT_MAX;
+			layout->_tiledSurfacePtr->_frameAnim.setEndTime(FLT_MAX);
 		} else {
-			layout->_tiledSurfacePtr->_frameAnim._endTime = (endingFrame / frameRate) * 1000.0 * 1000.0;
+			layout->_tiledSurfacePtr->_frameAnim.setEndTime((endingFrame / frameRate) * 1000.0 * 1000.0);
 		}
 	}
 
@@ -470,7 +469,7 @@ int buttonLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
+					layout->setScale(TeVector3f32(0.75f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.buttonLayoutBindings] Unreconized attribute : %s", s);
@@ -540,7 +539,7 @@ int checkboxLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
+					layout->setScale(TeVector3f32(0.75f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.checkboxLayoutBindings] Unreconized attribute : %s", s);
@@ -710,7 +709,7 @@ int textLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
+					layout->setScale(TeVector3f32(0.75f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.textLayoutBindings] Unreconized attribute : %s", s);
@@ -861,7 +860,7 @@ int scrollingLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
+					layout->setScale(TeVector3f32(0.75f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.scrollingLayoutBindings] Unreconized attribute : %s", s);
@@ -923,7 +922,7 @@ int extendedTextLayoutBindings(lua_State *L) {
 			} else if (!strcmp(s, "consoleNoStretch")) {
 				warning("TODO: Handle _g_bWidescreen");
 				if (_g_bWidescreen) {
-					layout->setScale(TeVector3f32(0.7500001f, 1.0f, 1.0f));
+					layout->setScale(TeVector3f32(0.75f, 1.0f, 1.0f));
 				}
 			} else {
 				warning("[TeLuaGUI.textLayoutBindings] Unreconized attribute : %s", s);
diff --git a/engines/tetraedge/te/te_material.h b/engines/tetraedge/te/te_material.h
index 4758d1f5df8..7e0f3eec87d 100644
--- a/engines/tetraedge/te/te_material.h
+++ b/engines/tetraedge/te/te_material.h
@@ -64,9 +64,8 @@ public:
 	TeColor _specularColor;
 	TeColor _emissionColor;
 	float _shininess;
-	bool _enableLights;
 	bool _enableSomethingDefault0;
-private:
+	bool _enableLights;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index c5b1d4b67cc..f2c1460302f 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -58,7 +58,7 @@ void TeModel::blendMesh(const Common::String &s1, const Common::String &s2, floa
 	_meshBlenders.push_back(new MeshBlender(s1, s2, amount, this));
 }
 
-int TeModel::checkFileType(Common::SeekableReadStream &instream) {
+int TeModel::checkFileType(Common::SeekableReadStream &instream) const {
 	char buf[4];
 	instream.seek(0);
 	int sz = instream.read(buf, 4);
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index ab27bb06384..ef9cf6da215 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -86,7 +86,7 @@ public:
 	void blendAnim(TeIntrusivePtr<TeModelAnimation>& anim, float amount, bool repeat);
 	void blendMesh(const Common::String &s1, const Common::String &s2, float amount);
 
-	int checkFileType(Common::SeekableReadStream &instream);
+	int checkFileType(Common::SeekableReadStream &instream) const;
 
 	void create();
 	void destroy();
@@ -97,7 +97,6 @@ public:
 	int findOrAddWeights(const Common::Array<weightElement> &weights);
 	void forceMatrix(const TeMatrix4x4 &matrix);
 	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num);
-	TeMatrix4x4 lerpElementsMatrix(unsigned int weightNum, const Common::Array<TeMatrix4x4> &matricies);
 
 	/* Align the stream to the nearest 4 byte boudary*/
 	static void loadAlign(Common::SeekableReadStream &stream);
@@ -109,9 +108,8 @@ public:
 	bool loadWeights(Common::ReadStream &stream, Common::Array<weightElement> &weights);
 	bool loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh);
 
-	void optimize();
-	void update();
 	void removeAnim();
+	void update();
 
 	void saveBone(Common::SeekableWriteStream &stream, unsigned long boneno);
 	void saveMesh(Common::SeekableWriteStream &stream, const TeMesh &mesh);
@@ -126,27 +124,36 @@ public:
 
 	TeMatrix4x4 skinOffset(unsigned long boneno) const;
 
+	static Common::SeekableReadStream *tryLoadZlibStream(Common::SeekableReadStream &instr);
+
 	TeSignal2Param<const Common::String &, TeMatrix4x4 &> &bonesUpdatedSignal() { return _bonesUpdatedSignal; }
+	Common::Array<BonesBlender *> &boneBlenders() { return _boneBlenders; }
+	Common::Array<TeMesh> &meshes() { return _meshes; }
+	TeIntrusivePtr<TeTiledTexture> tiledTexture() { return _tiledTexture; }
 
-	static Common::SeekableReadStream *tryLoadZlibStream(Common::SeekableReadStream &instr);
-	TeIntrusivePtr<TeTiledTexture> _tiledTexture;
+	void setEnableLights(bool val) { _enableLights = val; }
+	void setTexturePath(const Common::Path &path) { _texturePath = path; }
+
+protected:
+	TeMatrix4x4 lerpElementsMatrix(unsigned int weightNum, const Common::Array<TeMatrix4x4> &matricies);
+	void optimize();
 
 	Common::Path _texturePath;
+	TeIntrusivePtr<TeTiledTexture> _tiledTexture;
+
 	bool _enableLights;
+	bool _matrixForced;
 	bool _skipSkinOffsets;
 
-	Common::Array<TeMesh> _meshes;
-	Common::Array<BonesBlender *> _boneBlenders;
-
-protected:
-	bool _matrixForced;
 	TeMatrix4x4 _forcedMatrix;
+	Common::Array<BonesBlender *> _boneBlenders;
 	Common::Array<MeshBlender *> _meshBlenders;
 	Common::Array<bone> _bones;
 	Common::Array<TeMatrix4x4> _skinOffsets;
 	Common::Array<TeMatrix4x4> _boneMatricies;
 	Common::Array<TeMatrix4x4> _lerpedElements;
 	Common::Array<Common::Array<weightElement>> _weightElements;
+	Common::Array<TeMesh> _meshes;
 
 	TeQuaternion _boneRotation;
 
diff --git a/engines/tetraedge/te/te_palette.h b/engines/tetraedge/te/te_palette.h
index 4a93ab09e6b..816b4e305ff 100644
--- a/engines/tetraedge/te/te_palette.h
+++ b/engines/tetraedge/te/te_palette.h
@@ -28,11 +28,6 @@ class TePalette {
 public:
 	TePalette();
 
-	// TODO add public members
-
-private:
-	// TODO add private members
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_real_timer.h b/engines/tetraedge/te/te_real_timer.h
index 184e29c182a..78020556952 100644
--- a/engines/tetraedge/te/te_real_timer.h
+++ b/engines/tetraedge/te/te_real_timer.h
@@ -39,14 +39,14 @@ public:
 	unsigned long timeElapsed();
 	unsigned long timeFromLastTimeElapsed();
 
-	bool _paused;
+	bool isPaused() const { return _paused; }
 
 private:
+	bool _paused;
 	unsigned long _startTime;
 	unsigned long _startTime2;
 	unsigned long _pausedTime;
 	unsigned long _maxTimeSeen;
-	// TODO add private members
 
 };
 
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 518b8578e31..0716a62e5e4 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -34,7 +34,7 @@ _numTransparentMeshes(0), _pendingTransparentMeshProperties(0) {
 }
 
 void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsigned long tricount, unsigned long materialno) {
-	const float orthNearVal = _currentCamera->_orthNearVal;
+	const float orthNearVal = _currentCamera->orthoNearPlane();
 	const TeMesh::Mode meshMode = mesh.getMode();
 	if (!tricount) {
 		if (meshMode == TeMesh::MeshMode_TriangleStrip) {
@@ -123,9 +123,9 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 
 		midpoint.z() -= orthNearVal;
 		float zOrder;
-		if (_currentCamera->_projectionMatrixType < 4) {
+		if (_currentCamera->projMatrixType() < 4) {
 			zOrder = -midpoint.squaredLength();
-		} else if (_currentCamera->_projectionMatrixType == 4) {
+		} else if (_currentCamera->projMatrixType() == 4) {
 			zOrder = midpoint.z() * midpoint.z();
 		} else {
 			zOrder = midpoint.squaredLength();
@@ -165,9 +165,9 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 			midpoint.z() -= orthNearVal;
 
 			float zOrder;
-			if (_currentCamera->_projectionMatrixType < 4) {
+			if (_currentCamera->projMatrixType() < 4) {
 				zOrder = -midpoint.squaredLength();
-			} else if (_currentCamera->_projectionMatrixType == 4) {
+			} else if (_currentCamera->projMatrixType() == 4) {
 				zOrder = midpoint.z() * midpoint.z();
 			} else {
 				zOrder = midpoint.squaredLength();
diff --git a/engines/tetraedge/te/te_screen.cpp b/engines/tetraedge/te/te_screen.cpp
deleted file mode 100644
index 34f4a4de4b2..00000000000
--- a/engines/tetraedge/te/te_screen.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/* 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/te/te_screen.h"
-
-namespace Tetraedge {
-
-TeScreen::TeScreen() {
-}
-
-// TODO: Add more functions here.
-
-} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_screen.h b/engines/tetraedge/te/te_screen.h
deleted file mode 100644
index b00bc539664..00000000000
--- a/engines/tetraedge/te/te_screen.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* 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_TE_TE_SCREEN_H
-#define TETRAEDGE_TE_TE_SCREEN_H
-
-namespace Tetraedge {
-
-class TeScreen {
-public:
-	TeScreen();
-
-	// TODO add public members
-
-private:
-	// TODO add private members
-
-};
-
-} // end namespace Tetraedge
-
-#endif // TETRAEDGE_TE_TE_SCREEN_H
diff --git a/engines/tetraedge/te/te_sfx.cpp b/engines/tetraedge/te/te_sfx.cpp
deleted file mode 100644
index cfb507ca39a..00000000000
--- a/engines/tetraedge/te/te_sfx.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/* 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/te/te_sfx.h"
-
-namespace Tetraedge {
-
-TeSFX::TeSFX() {
-}
-
-// TODO: Add more functions here.
-
-} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_sfx.h b/engines/tetraedge/te/te_sfx.h
deleted file mode 100644
index c8d7eaa8361..00000000000
--- a/engines/tetraedge/te/te_sfx.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* 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_TE_TE_S_F_X_H
-#define TETRAEDGE_TE_TE_S_F_X_H
-
-namespace Tetraedge {
-
-class TeSFX {
-public:
-	TeSFX();
-
-	// TODO add public members
-
-private:
-	// TODO add private members
-
-};
-
-} // end namespace Tetraedge
-
-#endif // TETRAEDGE_TE_TE_S_F_X_H
diff --git a/engines/tetraedge/te/te_sprite_layout.cpp b/engines/tetraedge/te/te_sprite_layout.cpp
index b3f30780fa1..6826cca91c2 100644
--- a/engines/tetraedge/te/te_sprite_layout.cpp
+++ b/engines/tetraedge/te/te_sprite_layout.cpp
@@ -82,7 +82,7 @@ bool TeSpriteLayout::load(const Common::Path &path) {
 	unload();
 
 	if (_tiledSurfacePtr->load(path)) {
-		const TeVector2s32 texSize = _tiledSurfacePtr->_tiledTexture->_totalSize;
+		const TeVector2s32 texSize = _tiledSurfacePtr->tiledTexture()->totalSize();
 		if (texSize._y <= 0) {
 			setRatio(1.0);
 		} else {
@@ -102,7 +102,7 @@ bool TeSpriteLayout::load(TeIntrusivePtr<Te3DTexture> &texture) {
 	unload();
 
 	if (_tiledSurfacePtr->load(texture)) {
-		const TeVector2s32 tiledTexSize = _tiledSurfacePtr->_tiledTexture->_totalSize;
+		const TeVector2s32 tiledTexSize = _tiledSurfacePtr->tiledTexture()->totalSize();
 		if (tiledTexSize._y <= 0) {
 			setRatio(1.0);
 		} else {
@@ -124,7 +124,7 @@ bool TeSpriteLayout::load(TeImage &img) {
 	unload();
 
 	if (_tiledSurfacePtr->load(img)) {
-		const TeVector2s32 tiledTexSize = _tiledSurfacePtr->_tiledTexture->_totalSize;
+		const TeVector2s32 tiledTexSize = _tiledSurfacePtr->tiledTexture()->totalSize();
 		if (tiledTexSize._y <= 0) {
 			setRatio(1.0);
 		} else {
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index 6c7554f1f7f..5f60feec8b3 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -169,7 +169,7 @@ bool TeTiledSurface::onFrameAnimCurrentFrameChanged() {
 
 	Common::SharedPtr<TePalette> nullPal;
 	img.create(vidSize._x, vidSize._y, nullPal, _imgFormat, bufxsize, bufysize);
-	if (_codec->update(_frameAnim._lastFrameShown, img))
+	if (_codec->update(_frameAnim.lastFrameShown(), img))
 		update(img);
 	return _codec->isAtEnd();
 }
@@ -180,8 +180,8 @@ void TeTiledSurface::pause() {
 
 void TeTiledSurface::play() {
 	if (_codec) {
-		_frameAnim._nbFrames = _codec->nbFrames();
-		_frameAnim._frameRate = _codec->frameRate();
+		_frameAnim.setNbFrames(_codec->nbFrames());
+		_frameAnim.setFrameRate(_codec->frameRate());
 		_frameAnim.play();
 	}
 }
diff --git a/engines/tetraedge/te/te_tiled_surface.h b/engines/tetraedge/te/te_tiled_surface.h
index 85e54ead249..d9c9d14e9e7 100644
--- a/engines/tetraedge/te/te_tiled_surface.h
+++ b/engines/tetraedge/te/te_tiled_surface.h
@@ -86,7 +86,8 @@ public:
 	const Common::Path &path() const { return _path; }
 
 	TeFrameAnim _frameAnim;
-	TeICodec *_codec;
+
+	TeICodec *codec() { return _codec; }
 
 private:
 	float _bottomCrop;
@@ -94,6 +95,8 @@ private:
 	float _rightCrop;
 	float _topCrop;
 
+	TeICodec *_codec;
+
 	TeColor _colorKey;
 	bool _colorKeyActive;
 	float _colorKeyTolerence;
diff --git a/engines/tetraedge/te/te_tiled_texture.cpp b/engines/tetraedge/te/te_tiled_texture.cpp
index 839cacdde03..b6438ea57b1 100644
--- a/engines/tetraedge/te/te_tiled_texture.cpp
+++ b/engines/tetraedge/te/te_tiled_texture.cpp
@@ -83,7 +83,7 @@ bool TeTiledTexture::load(const TeImage &img) {
 
 			const TeImage *tileimage;
 			if (newTileSize != _totalSize) {
-				TeImage *optimizedimg = optimisedTileImage(imgArray, newTileSize, Common::SharedPtr<TePalette>(), img._format);
+				TeImage *optimizedimg = optimisedTileImage(imgArray, newTileSize, Common::SharedPtr<TePalette>(), img.teFormat());
 				img.copy(*optimizedimg, TeVector2s32(0, 0), TeVector2s32(_tileSize._x * row, _tileSize._y * col), newTileSize);
 				//optimizedimg->_flipY = img._flipY;
 				Common::String accessName = Common::String::format("%s.Tile%dx%d", img.getAccessName().toString().c_str(), row, col);
@@ -98,9 +98,9 @@ bool TeTiledTexture::load(const TeImage &img) {
 				tiledata->_texture = new Te3DTexture();
 				tiledata->_texture->load(*tileimage);
 				tiledata->_vec2 = TeVector3f32
-						((float)tiledata->_texture->_width / (float)_totalSize._x,
-						 (float)tiledata->_texture->_height / (float)_totalSize._y, 0.0);
-				_somethingSize += TeVector2s32(tiledata->_texture->_width, tiledata->_texture->_height);
+						((float)tiledata->_texture->width() / (float)_totalSize._x,
+						 (float)tiledata->_texture->height() / (float)_totalSize._y, 0.0);
+				_somethingSize += TeVector2s32(tiledata->_texture->width(), tiledata->_texture->height());
 			} else {
 				tiledata->_texture.release();
 				tiledata->_vec2 = TeVector3f32(0.0, 0.0, 0.0);
@@ -122,8 +122,8 @@ bool TeTiledTexture::load(const TeImage &img) {
 
 bool TeTiledTexture::load(const TeIntrusivePtr<Te3DTexture> &texture) {
 	release();
-	_tileSize._x = texture->_width;
-	_tileSize._y = texture->_height;
+	_tileSize._x = texture->width();
+	_tileSize._y = texture->height();
 	_totalSize = _tileSize;
 	_tileArray.resize(1);
 	Tile *tileData = tile(TeVector2s32(0, 0));
@@ -146,7 +146,7 @@ long TeTiledTexture::numberOfRow() const {
 TeImage *TeTiledTexture::optimisedTileImage(Common::Array<TeImage> &images, const TeVector2s32 &size,
 									   const Common::SharedPtr<TePalette> &pal, enum TeImage::Format format) {
 	for (TeImage &image : images) {
-		if (image.w == size._x && image.h == size._y && image._format == format) {
+		if (image.w == size._x && image.h == size._y && image.teFormat() == format) {
 			return ℑ
 		}
 	}
diff --git a/engines/tetraedge/te/te_tiled_texture.h b/engines/tetraedge/te/te_tiled_texture.h
index 8b2aa599af0..402a6d1aa21 100644
--- a/engines/tetraedge/te/te_tiled_texture.h
+++ b/engines/tetraedge/te/te_tiled_texture.h
@@ -52,7 +52,7 @@ public:
 	long numberOfColumns() const;
 	long numberOfRow() const;
 
-	/*static*/ TeImage *optimisedTileImage(Common::Array<TeImage> &images, const TeVector2s32 &size,
+	TeImage *optimisedTileImage(Common::Array<TeImage> &images, const TeVector2s32 &size,
 								  const Common::SharedPtr<TePalette> &pal, enum TeImage::Format format);
 
 	void release();
@@ -60,9 +60,10 @@ public:
 	Tile *tile(const TeVector2s32 &loc);
 	void update(const TeImage &image);
 
-	TeVector2s32 _totalSize;
+	TeVector2s32 totalSize() const { return _totalSize; }
 
 private:
+	TeVector2s32 _totalSize;
 	Common::Array<Tile> _tileArray;
 	TeVector2s32 _tileSize;
 	TeVector2s32 _somethingSize;
diff --git a/engines/tetraedge/te/te_timer.cpp b/engines/tetraedge/te/te_timer.cpp
index 7ca28da0095..97e62ec6a21 100644
--- a/engines/tetraedge/te/te_timer.cpp
+++ b/engines/tetraedge/te/te_timer.cpp
@@ -33,7 +33,7 @@ namespace Tetraedge {
 
 TeTimer::TeTimer() : _stopped(true), _pausable(true), _alarmTime(0),
 _startTime(0), _lastTimeElapsed(0), _startTimeOffset(0), _updated(false) {
-	if (realTimer()->_paused) {
+	if (realTimer()->isPaused()) {
 		realTimer()->start();
 		_realTime = realTimer()->getTimeFromStart();
 	}
diff --git a/engines/tetraedge/tetraedge.h b/engines/tetraedge/tetraedge.h
index 0e2c6de5ab0..0afb7ec4cdc 100644
--- a/engines/tetraedge/tetraedge.h
+++ b/engines/tetraedge/tetraedge.h
@@ -106,7 +106,7 @@ public:
 		Common::Serializer s(nullptr, stream);
 		return syncGame(s);
 	}
-	
+
 	static void getSavegameThumbnail(Graphics::Surface &thumb);
 
 	Common::Error loadGameState(int slot) override;
@@ -128,7 +128,6 @@ public:
 
 private:
 	void configureSearchPaths();
-
 };
 
 extern TetraedgeEngine *g_engine;


Commit: 0d981983fceb19781888d241a4660e1a3f874fad
    https://github.com/scummvm/scummvm/commit/0d981983fceb19781888d241a4660e1a3f874fad
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: add ability to save/load any time

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


diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 5b8276b160f..32c0e25af12 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -175,6 +175,17 @@ int TetraedgeEngine::getDefaultScreenHeight() const {
 	return 600;
 }
 
+bool TetraedgeEngine::onKeyUp(const Common::KeyState &state) {
+	if (state.keycode == Common::KEYCODE_l) {
+		if (loadGameDialog())
+			_game->initLoadedBackupData();
+	} else if (state.keycode == Common::KEYCODE_s) {
+		saveGameDialog();
+	}
+
+	return false;
+}
+
 Common::Error TetraedgeEngine::run() {
 	initGraphics3d(getDefaultScreenWidth(), getDefaultScreenHeight());
 
@@ -189,12 +200,13 @@ Common::Error TetraedgeEngine::run() {
 	// Set the engine's debugger console
 	setDebugger(new Console());
 
+	getInputMgr()->_keyUpSignal.add(this, &TetraedgeEngine::onKeyUp);
+
 	// If a savegame was selected from the launcher, load it
 	int saveSlot = ConfMan.getInt("save_slot");
 	if (saveSlot != -1)
 		(void)loadGameState(saveSlot);
 
-	// Simple event handling loop
 	Common::Event e;
 
 	while (!shouldQuit()) {
diff --git a/engines/tetraedge/tetraedge.h b/engines/tetraedge/tetraedge.h
index 0afb7ec4cdc..290638adbc5 100644
--- a/engines/tetraedge/tetraedge.h
+++ b/engines/tetraedge/tetraedge.h
@@ -25,6 +25,7 @@
 #include "common/scummsys.h"
 #include "common/system.h"
 #include "common/error.h"
+#include "common/events.h"
 #include "common/fs.h"
 #include "common/hash-str.h"
 #include "common/random.h"
@@ -123,6 +124,7 @@ public:
 	TeInputMgr *getInputMgr();
 
 	void openConfigDialog();
+	bool onKeyUp(const Common::KeyState &state);
 
 	static Common::StringArray splitString(const Common::String &text, char c);
 


Commit: 87bbf9660e409615d49551a1a2738d6b579aa2c9
    https://github.com/scummvm/scummvm/commit/87bbf9660e409615d49551a1a2738d6b579aa2c9
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Reduce dependency on OpenGL headers

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_mesh.h
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer.h
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/tetraedge.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 493a24fdfd8..98ed7325b04 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -25,7 +25,6 @@
 #include "common/util.h"
 #include "common/events.h"
 
-#include "graphics/opengl/system_headers.h"
 #include "graphics/scaler.h"
 
 #include "tetraedge/tetraedge.h"
@@ -430,7 +429,7 @@ void Application::performRender() {
 	drawBack();
 
 	renderer->renderTransparentMeshes();
-	renderer->clearBuffer(GL_ACCUM);
+	renderer->clearBuffer(TeRenderer::DepthBuffer);
 	if (game->running()) {
 		if (_drawShadows && game->scene()._character
 			&& game->scene().shadowLightNo() != -1
@@ -447,7 +446,7 @@ void Application::performRender() {
 	}
 
 	renderer->renderTransparentMeshes();
-	renderer->clearBuffer(GL_ACCUM);
+	renderer->clearBuffer(TeRenderer::DepthBuffer);
 	drawFront();
 	renderer->renderTransparentMeshes();
 	game->scene().drawPath();
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index 66186576662..c368fc5501b 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -248,6 +248,10 @@ void TeMesh::resizeUpdatedTables(unsigned long newSize) {
 	_updatedNormals.resize(newSize);
 }
 
+void TeMesh::setglTexEnvBlend() {
+	_gltexEnvMode = GL_BLEND;
+}
+
 void TeMesh::setColor(const TeColor &col) {
 	Te3DObject2::setColor(col);
 
diff --git a/engines/tetraedge/te/te_mesh.h b/engines/tetraedge/te/te_mesh.h
index f09fb8bdb10..1399d88f858 100644
--- a/engines/tetraedge/te/te_mesh.h
+++ b/engines/tetraedge/te/te_mesh.h
@@ -108,10 +108,10 @@ public:
 	uint numIndexes() const { return _indexes.size(); }
 	uint numVerticies() const { return _verticies.size(); }
 	bool shouldDrawMaybe() const { return _shouldDraw; }
-	uint gltexEnvMode() const { return _gltexEnvMode; }
+	uint32 gltexEnvMode() const { return _gltexEnvMode; }
 
 	void setShouldDraw(bool val) { _shouldDraw = val; }
-	void setglTexEnv(unsigned int val) { _gltexEnvMode = val; }
+	void setglTexEnvBlend();
 	void setHasAlpha(bool val) { _hasAlpha = val; }
 
 	Common::Array<TeMaterial> &materials() { return _materials; }
@@ -143,7 +143,7 @@ private:
 	bool _drawWires;
 	bool _shouldDraw;
 
-	unsigned int _gltexEnvMode;
+	uint32 _gltexEnvMode;
 
 };
 
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 0716a62e5e4..3da806d02d5 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -195,7 +195,14 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 }
 
 void TeRenderer::clearBuffer(TeRenderer::Buffer buf) {
-	glClear(buf);
+	GLenum glBuf = 0;
+	if (buf & StencilBuffer)
+		glBuf |= GL_STENCIL_BUFFER_BIT;
+	if (buf & DepthBuffer)
+		glBuf |= GL_DEPTH_BUFFER_BIT;
+	if (buf & ColorBuffer)
+		glBuf |= GL_COLOR_BUFFER_BIT;
+	glClear(glBuf);
 }
 
 void TeRenderer::create() {
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index 61cb82a5904..752e29b8f55 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -55,7 +55,7 @@ public:
 
 		TeMaterial _material;
 
-		uint _glTexEnvMode;
+		uint32 _glTexEnvMode;
 		uint _sourceTransparentMesh;
 		bool _hasColor;
 		float _zOrder;
@@ -67,7 +67,11 @@ public:
 		bool _shouldDraw;
 	};
 
-	typedef uint32 Buffer;
+	enum Buffer {
+		DepthBuffer = 1,
+		ColorBuffer = 2,
+		StencilBuffer = 4
+	};
 
 	void addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsigned long i2, unsigned long i3);
 	void checkError(const Common::String &str) {};
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index b78c8721d1a..19794a82b44 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -26,14 +26,13 @@
 #endif
 
 #include "tetraedge/te/te_text_base2.h"
-#include "graphics/opengl/system_headers.h"
 
 namespace Tetraedge {
 
 TeTextBase2::TeTextBase2() : _drawRect(0, 0), _size(0, 0),
 _alignStyle(TeFont3::AlignLeft), _interLine(0.0f), _globalColor(0xff, 0xff, 0xff, 0xff),
 _wrapMode(WrapModeFixed), _strikethrough(false), _fontSize(10), _valueWasSet(true) {
-	_mesh.setglTexEnv(GL_BLEND);
+	_mesh.setglTexEnvBlend();
 	_mesh.setShouldDraw(true);
 }
 
@@ -112,7 +111,7 @@ void TeTextBase2::build() {
 
 	_mesh.setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
 	_mesh.defaultMaterial(texture);
-	_mesh.setglTexEnv(GL_BLEND);
+	_mesh.setglTexEnvBlend();
 	_mesh.setShouldDraw(true);
 	_mesh.setColor(_globalColor);
 	_mesh.setVertex(0, TeVector3f32(_size._x * -0.5f, _size._y * -0.5f, 0.0f));
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 32c0e25af12..d2afbf479a6 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -41,8 +41,6 @@
 #include "tetraedge/te/te_sound_manager.h"
 #include "tetraedge/te/te_input_mgr.h"
 
-#include "graphics/opengl/system_headers.h"
-
 namespace Tetraedge {
 
 TetraedgeEngine *g_engine;


Commit: 48015aa751d94e2e2ec05aa24ea1791bae9c2ccd
    https://github.com/scummvm/scummvm/commit/48015aa751d94e2e2ec05aa24ea1791bae9c2ccd
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Small fixes.

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game.h
    engines/tetraedge/game/main_menu.cpp
    engines/tetraedge/te/te_camera.h
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/tetraedge.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 98ed7325b04..284cd0a6c90 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -299,7 +299,7 @@ void Application::startGame(bool newGame, int difficulty) {
 	_appSpriteLayout.unload();
 	if (newGame)
 		_difficulty = difficulty;
-	g_engine->getGame()->enter(newGame);
+	g_engine->getGame()->enter();
 }
 
 void Application::resume() {
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index fc631015e28..8b66666b570 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -271,7 +271,7 @@ void Game::draw() {
 	}
 }
 
-void Game::enter(bool newgame) {
+void Game::enter() {
 	_enteredFlag2 = true;
 	_entered = true;
 	_luaShowOwnerError = false;
@@ -339,7 +339,7 @@ void Game::enter(bool newgame) {
 
 	_inventory.cellphone()->onCallNumber().add(this, &Game::onCallNumber);
 
-	if (!newgame) {
+	if (hasLoadName()) {
 		loadBackup(_loadName);
 	} else {
 		_gameLoadState = 1;
@@ -1016,6 +1016,10 @@ bool Game::onMouseClick(const Common::Point &pt) {
 	if (app->isFading())
 		return true;
 
+	// In case we capture a click during a video..
+	if (!_scene.currentCamera())
+		return false;
+
 	_posPlayer = TeVector3f32(-1.0f, -1.0f, -1.0f);
 	if (_previousMousePos == TeVector2s32(-1, -1)) {
 		_previousMousePos = pt;
diff --git a/engines/tetraedge/game/game.h b/engines/tetraedge/game/game.h
index 36979202b83..9674923b5e5 100644
--- a/engines/tetraedge/game/game.h
+++ b/engines/tetraedge/game/game.h
@@ -101,7 +101,7 @@ public:
 
 	void deleteNoScale();
 	void draw();
-	void enter(bool newgame);
+	void enter(); // will load game if _loadName is set.
 	// Note: game uses ILayouts here..
 	static TeI3DObject2 *findLayoutByName(TeLayout *parent, const Common::String &name);
 	static TeSpriteLayout *findSpriteLayoutByName(TeLayout *parent, const Common::String &name);
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
index a3deb90f071..9a02a952ac0 100644
--- a/engines/tetraedge/game/main_menu.cpp
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -124,7 +124,9 @@ void MainMenu::enter() {
 		versionNum->setText(versionSectionStr + app->getVersionString());
 	}
 
-	if (ConfMan.get("skip_mainmenu") == "true") {
+	// Skip the menu if we are loading.
+	Game *game = g_engine->getGame();
+	if (game->hasLoadName() || ConfMan.get("skip_mainmenu") == "true") {
 		onNewGameConfirmed();
 	}
 }
diff --git a/engines/tetraedge/te/te_camera.h b/engines/tetraedge/te/te_camera.h
index 0d73a0a1859..1afc99d426c 100644
--- a/engines/tetraedge/te/te_camera.h
+++ b/engines/tetraedge/te/te_camera.h
@@ -79,7 +79,7 @@ public:
 	int projMatrixType() const { return _projectionMatrixType; }
 	void setPerspectiveVal(float val) { _somePerspectiveVal = val; }
 	float orthoNearPlane() const { return _orthNearVal; }
-	float orthoFarPlane() const { return _orthNearVal; }
+	float orthoFarPlane() const { return _orthFarVal; }
 
 private:
 	void updateProjectionMatrix();
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 77b5be41363..ba4dd874364 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -807,6 +807,8 @@ TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, c
 			Common::Array<TePickMesh2*> &pickMeshes, TeVector3f32 *outloc, bool lastHitFirst) {
 	TeVector3f32 closestLoc;
 	TePickMesh2 *nearestMesh = nullptr;
+	if (!camera)
+		return nullptr;
 	float closestDist = camera->orthoFarPlane();
 	Math::Ray camRay;
 	for (unsigned int i = 0; i < pickMeshes.size(); i++) {
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index 19794a82b44..ed5015a16c7 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -214,7 +214,9 @@ void TeTextBase2::drawEmptyChar(unsigned int offset) {
 void TeTextBase2::drawLine(TeImage &img, const Common::String &str, int yoffset) {
 	TeIntrusivePtr<TeFont3> font = _fonts[0];
 
-	font->draw(img, str, _fontSize, yoffset, _globalColor, _alignStyle);
+	// Note: We draw this with black because the global color will be applied on
+	// the mesh.
+	font->draw(img, str, _fontSize, yoffset, TeColor(0, 0, 0, 255), _alignStyle);
 }
 
 unsigned int TeTextBase2::endOfWord(unsigned int offset) const {
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index d2afbf479a6..05bf6b71680 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -148,7 +148,7 @@ Common::Error TetraedgeEngine::loadGameState(int slot) {
 		return Common::kReadingFailed;
 
 	// The game will reopen the file and do the loading, see Game::initLoadedBackupData
-	_game->setLoadName(saveStateName);
+	getGame()->setLoadName(saveStateName);
 
 	delete saveFile;
 	return Common::kNoError;
@@ -193,18 +193,22 @@ Common::Error TetraedgeEngine::run() {
 	_renderer = new TeRenderer();
 	_renderer->init();
 	_renderer->reset();
-	_application->create();
 
 	// Set the engine's debugger console
 	setDebugger(new Console());
 
 	getInputMgr()->_keyUpSignal.add(this, &TetraedgeEngine::onKeyUp);
 
-	// If a savegame was selected from the launcher, load it
+	// If a savegame was selected from the launcher, load it.
+	// Should be before application->create() because it only
+	// sets the game name to load inside the Game object.  It will
+	// actually be loaded when the application is created.
 	int saveSlot = ConfMan.getInt("save_slot");
 	if (saveSlot != -1)
 		(void)loadGameState(saveSlot);
 
+	_application->create();
+
 	Common::Event e;
 
 	while (!shouldQuit()) {


Commit: d0d67e91f8b0479dda8a76c9e43374ad3b46932b
    https://github.com/scummvm/scummvm/commit/d0d67e91f8b0479dda8a76c9e43374ad3b46932b
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Reduce movement janks and accidental moves

Changed paths:
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/dialog2.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/question2.cpp
    engines/tetraedge/game/question2.h
    engines/tetraedge/te/te_button_layout.cpp


diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 2ba77527a05..757f9282216 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -174,7 +174,7 @@ float Character::animLength(const TeModelAnimation &modelanim, long bone, long l
 		lastframe = last;
 	int first = modelanim.firstFrame();
 	const TeVector3f32 starttrans = translationVectorFromAnim(modelanim, bone, first);
-	const TeVector3f32 endtrans = translationVectorFromAnim(modelanim, bone, last);
+	const TeVector3f32 endtrans = translationVectorFromAnim(modelanim, bone, lastframe);
 	const TeVector3f32 secondtrans = translationVectorFromAnim(modelanim, bone, first + 1);
 	return ((endtrans.z() - starttrans.z()) + secondtrans.z()) - starttrans.z();
 }
@@ -970,7 +970,7 @@ void Character::walkTo(float curveEnd, bool walkFlag) {
 				uint framecounts[4];
 
 				if (repeats == 0)
-					framecounts[0] = UINT_MAX;
+					framecounts[0] = INT_MAX;
 				else
 					framecounts[0] = (repeats - 1) * _walkLoopAnimFrameCount + 29;
 
@@ -1011,7 +1011,7 @@ void Character::walkTo(float curveEnd, bool walkFlag) {
 				play();
 				return; // NOTE: early return here.
 			} else {
-				// NPC walk
+				// Run or NPC walk
 				double intpart;
 				double remainder = modf(nloops, &intpart);
 				if (remainder >= 0.5) {
diff --git a/engines/tetraedge/game/dialog2.cpp b/engines/tetraedge/game/dialog2.cpp
index 159f076eb23..dd0e0f9a0d3 100644
--- a/engines/tetraedge/game/dialog2.cpp
+++ b/engines/tetraedge/game/dialog2.cpp
@@ -108,9 +108,7 @@ void Dialog2::load() {
 
 	TeButtonLayout *dialogBtn = _gui.buttonLayoutChecked("dialog");
 
-	// WORKAROUND: Ensure this is always above the Game (which is set to pri 10000)
-	Common::SharedPtr<TeCallback0Param<Dialog2>> callbackptr(new TeCallback0Param<Dialog2>(this, &Dialog2::onSkipButton, 20000.0f));
-	dialogBtn->onMouseClickValidated().push_back(callbackptr);
+	dialogBtn->onMouseClickValidated().add(this, &Dialog2::onSkipButton);
 
 	TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimUp = _gui.layoutAnchorLinearAnimation("dialogAnimationUp");
 	TeCurveAnim2<TeLayout, TeVector3f32> *dialogAnimDown = _gui.layoutAnchorLinearAnimation("dialogAnimationDown");
@@ -160,7 +158,8 @@ bool Dialog2::onSkipButton() {
 			_music.stop();
 		}
 	}
-	return false;
+	// Divergence from original: don't let clicks through on skip operation.
+	return true;
 }
 
 bool Dialog2::onSoundFinished() {
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index 8b66666b570..fa7ccca400c 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -1016,8 +1016,8 @@ bool Game::onMouseClick(const Common::Point &pt) {
 	if (app->isFading())
 		return true;
 
-	// In case we capture a click during a video..
-	if (!_scene.currentCamera())
+	// In case we capture a click during a video or dialog (shouldn't happen?)
+	if (!_scene.currentCamera() || _dialog2.isDialogPlaying() || _question2.isEntered())
 		return false;
 
 	_posPlayer = TeVector3f32(-1.0f, -1.0f, -1.0f);
diff --git a/engines/tetraedge/game/question2.cpp b/engines/tetraedge/game/question2.cpp
index 29e17670877..0d7394df0de 100644
--- a/engines/tetraedge/game/question2.cpp
+++ b/engines/tetraedge/game/question2.cpp
@@ -26,7 +26,7 @@
 
 namespace Tetraedge {
 
-Question2::Question2() {
+Question2::Question2() : _entered(false) {
 }
 
 Question2::~Question2() {
@@ -40,9 +40,11 @@ void Question2::enter() {
 	TeButtonLayout *backgroundButton = _gui.buttonLayoutChecked("background");
 	backgroundButton->setVisible(true);
 	g_engine->getGame()->showMarkers(true);
+	_entered = true;
 }
 
 void Question2::leave() {
+	_entered = false;
 	TeLayout *background = _gui.layout("background");
 	if (!background)
 		return;
@@ -78,10 +80,6 @@ void Question2::load() {
 	if (backgroundButton) {
 		addChild(backgroundButton);
 		backgroundButton->setVisible(false);
-
-		// WORKAROUND: Block clicks going to the Game (which is set to pri 10000)
-		Common::SharedPtr<TeCallback0Param<Question2>> callbackptr(new TeCallback0Param<Question2>(this, &Question2::onBackgroundClick, 20000.0f));
-		backgroundButton->onMouseClickValidated().push_back(callbackptr);
 	}
 	size();
 }
diff --git a/engines/tetraedge/game/question2.h b/engines/tetraedge/game/question2.h
index 86201e499d5..4a2e9ad0f04 100644
--- a/engines/tetraedge/game/question2.h
+++ b/engines/tetraedge/game/question2.h
@@ -48,7 +48,7 @@ public:
 	void enter();
 	void leave();
 	void load();
-	bool onBackgroundClick() { return false; }
+	bool isEntered() const { return _entered; }
 	bool onAnswerValidated(Answer &answer);
 	void pushAnswer(const Common::String &name, const Common::String &unk, const Common::String &path);
 	void unload();
@@ -56,6 +56,7 @@ public:
 	TeSignal1Param<const Common::String &> &onAnswerSignal() { return _onAnswerSignal; }
 
 private:
+	bool _entered;
 	Common::Array<Answer *> _answers;
 	TeLuaGUI _gui;
 	TeSignal1Param<const Common::String &> _onAnswerSignal;
diff --git a/engines/tetraedge/te/te_button_layout.cpp b/engines/tetraedge/te/te_button_layout.cpp
index 1b935322932..39f11995cd9 100644
--- a/engines/tetraedge/te/te_button_layout.cpp
+++ b/engines/tetraedge/te/te_button_layout.cpp
@@ -158,8 +158,8 @@ bool TeButtonLayout::onMouseLeftUp(const Common::Point &pt) {
 				sndMgr->playFreeSound(_validationSound, _validationSoundVolume, "sfx");
 			}
 			setState(newState);
-			_onMouseClickValidatedSignal.call();
-			return !_clickPassThrough;
+			bool stopProcessing = _onMouseClickValidatedSignal.call();
+			return !_clickPassThrough || stopProcessing;
 		}
 		break;
 	case BUTTON_STATE_ROLLOVER:


Commit: 9cdfb0deee3d3c8c4815fb497b3b217d85ff9107
    https://github.com/scummvm/scummvm/commit/9cdfb0deee3d3c8c4815fb497b3b217d85ff9107
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Clean up includes

Changed paths:
    engines/tetraedge/detection.cpp


diff --git a/engines/tetraedge/detection.cpp b/engines/tetraedge/detection.cpp
index 938be16631e..e019e66eb61 100644
--- a/engines/tetraedge/detection.cpp
+++ b/engines/tetraedge/detection.cpp
@@ -19,13 +19,6 @@
  *
  */
 
-#include "base/plugins.h"
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "common/md5.h"
-#include "common/str-array.h"
-#include "common/translation.h"
-#include "common/util.h"
 #include "tetraedge/detection.h"
 #include "tetraedge/detection_tables.h"
 


Commit: c4869ff02ffb3659172e1f1c0077770696cd342c
    https://github.com/scummvm/scummvm/commit/c4869ff02ffb3659172e1f1c0077770696cd342c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Fix whitespace and use uint consistently

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/cellphone.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/characters_shadow.cpp
    engines/tetraedge/game/documents_browser.cpp
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/game_sound.cpp
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/inventory.cpp
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/main_menu.cpp
    engines/tetraedge/game/objectif.cpp
    engines/tetraedge/metaengine.cpp
    engines/tetraedge/te/micropather.cpp
    engines/tetraedge/te/micropather.h
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_3d_object2.h
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_animation.cpp
    engines/tetraedge/te/te_bezier_curve.cpp
    engines/tetraedge/te/te_bezier_curve.h
    engines/tetraedge/te/te_button_layout.h
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_color.h
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_font3.cpp
    engines/tetraedge/te/te_font3.h
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_free_move_zone.h
    engines/tetraedge/te/te_images_sequence.h
    engines/tetraedge/te/te_layout.cpp
    engines/tetraedge/te/te_lua_context.cpp
    engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
    engines/tetraedge/te/te_lua_thread.cpp
    engines/tetraedge/te/te_matrix4x4.cpp
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_mesh.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_model_animation.cpp
    engines/tetraedge/te/te_model_animation.h
    engines/tetraedge/te/te_model_vertex_animation.cpp
    engines/tetraedge/te/te_model_vertex_animation.h
    engines/tetraedge/te/te_music.cpp
    engines/tetraedge/te/te_name_val_xml_parser.cpp
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_pick_mesh2.h
    engines/tetraedge/te/te_ray_intersection.h
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer.h
    engines/tetraedge/te/te_scrolling_layout.cpp
    engines/tetraedge/te/te_scrolling_layout.h
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_text_base2.h
    engines/tetraedge/te/te_text_layout.cpp
    engines/tetraedge/te/te_text_layout_xml_parser.h
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_tiled_texture.cpp
    engines/tetraedge/te/te_tiled_texture.h
    engines/tetraedge/tetraedge.h


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index 284cd0a6c90..bd0b9e3e526 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -345,9 +345,9 @@ bool Application::run() {
 				finalGui.unload();
 			}
 			_finishedGame = false;
-	  }
-	  InGameScene::updateScroll();
-	  TeObject::deleteNow();
+		}
+		InGameScene::updateScroll();
+		TeObject::deleteNow();
 	}
 	return true;
 }
@@ -487,10 +487,10 @@ void Application::getSavegameThumbnail(Graphics::Surface &thumb) {
 	Graphics::Surface screen;
 	_visFade.texture()->writeTo(screen);
 	screen.flipVertical(Common::Rect(screen.w, screen.h));
-    Common::ScopedPtr<Graphics::Surface> scaledScreen(screen.scale(kThumbnailWidth, kThumbnailHeight2));
-    thumb.copyFrom(*scaledScreen);
-    screen.free();
-    scaledScreen->free();
+	Common::ScopedPtr<Graphics::Surface> scaledScreen(screen.scale(kThumbnailWidth, kThumbnailHeight2));
+	thumb.copyFrom(*scaledScreen);
+	screen.free();
+	scaledScreen->free();
 }
 
 bool Application::isFading() {
diff --git a/engines/tetraedge/game/cellphone.cpp b/engines/tetraedge/game/cellphone.cpp
index 1fbc2a719ef..bcfeb661b2e 100644
--- a/engines/tetraedge/game/cellphone.cpp
+++ b/engines/tetraedge/game/cellphone.cpp
@@ -128,7 +128,7 @@ bool Cellphone::onCloseButtonValidated() {
 }
 
 bool Cellphone::onNextNumber() {
-	unsigned int numoffset = _nextNumber + 1;
+	uint numoffset = _nextNumber + 1;
 	if (numoffset < _textLayoutArray.size()) {
 		currentPage(numoffset);
 	}
@@ -137,9 +137,8 @@ bool Cellphone::onNextNumber() {
 
 bool Cellphone::onPreviousNumber() {
 	int numoffset = _nextNumber - 1;
-	if (numoffset >= 0) {
-	  currentPage(numoffset);
-	}
+	if (numoffset >= 0)
+		currentPage(numoffset);
 	return false;
 }
 
@@ -158,10 +157,10 @@ void Cellphone::unload() {
 
 Common::Error Cellphone::syncState(Common::Serializer &s) {
 	Common::Array<Common::String> numbers = _addedNumbers;
-	unsigned int numElems = numbers.size();
+	uint numElems = numbers.size();
 	s.syncAsUint32LE(numElems);
 	numbers.resize(numElems);
-	for (unsigned int i = 0; i < numElems; i++) {
+	for (uint i = 0; i < numElems; i++) {
 		s.syncString(numbers[i]);
 	}
 	if (s.isLoading()) {
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 757f9282216..ae9600d3edc 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -77,15 +77,15 @@ Character::~Character() {
 	deleteAnim();
 	Game *game = g_engine->getGame();
 	Common::Array<TeIntrusivePtr<TeModel>> &models = game->scene().models();
-	for (unsigned int i = 0; i < models.size(); i++) {
+	for (uint i = 0; i < models.size(); i++) {
 		if (models[i] == _model) {
 			models.remove_at(i);
 			break;
 		}
 	}
 	removeAnim();
-	for (unsigned int s = 0; s < 2; s++) {
-		for (unsigned int i = 0; i < models.size(); i++) {
+	for (uint s = 0; s < 2; s++) {
+		for (uint i = 0; i < models.size(); i++) {
 			if (models[i] == _shadowModel[s]) {
 				models.remove_at(i);
 				break;
@@ -120,7 +120,7 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 	if (fnName == "ChangeClef" && c->_triggerFrame == 31)
 		c->_triggerFrame = 15;
 
-	const Common::Path animPath = _model->anim()->_loadedPath;
+	const Common::Path animPath = _model->anim()->loadedPath();
 
 	// Another difference.. the original messes with paths a bit - just
 	// use the file name, since it's already limited by character.
@@ -185,7 +185,7 @@ float Character::animLengthFromFile(const Common::String &animname, uint32 *pfra
 		return 0.0f;
 	}
 	TeIntrusivePtr<TeModelAnimation> anim = _model->anim();
-	if (!anim->_loadedPath.toString().contains(animname)) {
+	if (!anim->loadedPath().toString().contains(animname)) {
 		Common::Path animpath("models/Anims");
 		animpath.joinInPlace(animname);
 		anim = animCacheLoad(animpath);
@@ -264,12 +264,12 @@ void Character::deleteAnim() {
 void Character::deleteCallback(const Common::String &key, const Common::String &fnName, float f) {
 	_callbacksChanged = true;
 	assert(_model->anim());
-	Common::String animFile = _model->anim()->_loadedPath.getLastComponent().toString();
+	Common::String animFile = _model->anim()->loadedPath().getLastComponent().toString();
 	if (!_callbacks.contains(animFile))
 		return;
 
 	Common::Array<Callback *> &cbs = _callbacks.getVal(animFile);
-	for (unsigned int i = 0; i < cbs.size(); i++) {
+	for (uint i = 0; i < cbs.size(); i++) {
 		if (fnName.empty()) {
 			delete cbs[i];
 			// don't remove from array, clear at the end.
@@ -309,7 +309,7 @@ bool Character::isFramePassed(int frameno) {
 }
 
 bool Character::isWalkEnd() {
-	const Common::String animFile = _model->anim()->_loadedPath.getLastComponent().toString();
+	const Common::String animFile = _model->anim()->loadedPath().getLastComponent().toString();
 	for (const auto & walkSettings : _characterSettings._walkSettings) {
 		if (walkSettings._value._walkParts[WalkPart_EndD]._file.contains(animFile)
 				|| walkSettings._value._walkParts[WalkPart_EndG]._file.contains(animFile))
@@ -446,7 +446,7 @@ bool Character::onBonesUpdate(const Common::String &boneName, TeMatrix4x4 &boneM
 
 	Game *game = g_engine->getGame();
 	if (boneName == "Pere") {
-		const Common::String animfile = _model->anim()->_loadedPath.getLastComponent().toString();
+		const Common::String animfile = _model->anim()->loadedPath().getLastComponent().toString();
 		bool resetX = false;
 		if (game->scene()._character == this) {
 			for (const auto &walkSettings : _characterSettings._walkSettings) {
@@ -547,7 +547,7 @@ bool Character::onModelAnimationFinished() {
 	if (!_model->anim())
 		return false;
 
-	const Common::Path loadedPath = _model->anim()->_loadedPath;
+	const Common::Path loadedPath = _model->anim()->loadedPath();
 	const Common::String animfile = loadedPath.getLastComponent().toString();
 
 	bool shouldAdjust = true;
@@ -619,7 +619,7 @@ bool Character::onModelAnimationFinished() {
 
 void Character::permanentUpdate() {
 	assert(_model->anim());
-	const Common::Path animPath = _model->anim()->_loadedPath;
+	const Common::Path &animPath = _model->anim()->loadedPath();
 	int curFrame = _model->anim()->curFrame2();
 	Game *game = g_engine->getGame();
 	_callbacksChanged = false;
@@ -892,14 +892,13 @@ void Character::update(double msFromStart) {
 	if (!endGAnim.empty() && _curAnimName == walkAnim(WalkPart_Loop) &&
 		   ((_curModelAnim->speed() * (msFromStart / 1000.0)) >= _walkTotalFrames)) {
 		if (_walkToFlag) {
-		  _walkToFlag = false;
-		  endMove();
+			_walkToFlag = false;
+			endMove();
 		} else {
-			if (_walkEndAnimG) {
-			  setAnimation(walkAnim(WalkPart_EndG), false);
-			} else {
-			  setAnimation(walkAnim(WalkPart_EndD), false);
-			}
+			if (_walkEndAnimG)
+				setAnimation(walkAnim(WalkPart_EndG), false);
+			else
+				setAnimation(walkAnim(WalkPart_EndD), false);
 		}
 	}
 
@@ -990,20 +989,21 @@ void Character::walkTo(float curveEnd, bool walkFlag) {
 
 				switch(minoffset) {
 				case 0:
-				  remainder = 29;
-				  _walkEndAnimG = true;
-				  repeats--;
-				  break;
+					remainder = 29;
+					_walkEndAnimG = true;
+					repeats--;
+					break;
 				case 1:
-				  remainder = 13;
-				  break;
+					remainder = 13;
+					break;
 				case 2:
-				  remainder = 29;
-				  _walkEndAnimG = true;
-				  break;
+					remainder = 29;
+					_walkEndAnimG = true;
+					break;
 				case 3:
-				  remainder = 13;
-				  repeats++;
+					remainder = 13;
+					repeats++;
+					break;
 				}
 				_walkTotalFrames = _walkLoopAnimFrameCount * repeats + _walkStartAnimFrameCount + remainder;
 				const float loopAnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &remainder, remainder);
diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
index e422b41def9..3593488eedb 100644
--- a/engines/tetraedge/game/characters_shadow.cpp
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -72,7 +72,7 @@ void CharactersShadow::createTexture(InGameScene *scene) {
 	_camera->apply();
 
 	glClearColor(0.0, 0.0, 0.0, 0.0);
-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	renderer->clearBuffer(TeRenderer::ColorAndDepth);
 
 	for (Character *character : scene->_characters) {
 		character->_model->draw();
@@ -81,7 +81,7 @@ void CharactersShadow::createTexture(InGameScene *scene) {
 	Te3DTexture::unbind();
 	glBindTexture(GL_TEXTURE_2D, _glTex);
 	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _texSize, _texSize);
-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	renderer->clearBuffer(TeRenderer::ColorAndDepth);
 
 	TeCamera::restore();
 	TeCamera::restore();
@@ -124,25 +124,25 @@ void CharactersShadow::draw(InGameScene *scene) {
 	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
 
 	float f[4];
-	for (unsigned int i = 0; i < 4; i++)
+	for (uint i = 0; i < 4; i++)
 		f[i] = matrix(i, 0);
 
 	glTexGenfv(GL_S, GL_EYE_PLANE, f);
 	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
 
-	for (unsigned int i = 0; i < 4; i++)
+	for (uint i = 0; i < 4; i++)
 		f[i] = matrix(i, 1);
 
 	glTexGenfv(GL_T, GL_EYE_PLANE, f);
 	glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
 
-	for (unsigned int i = 0; i < 4; i++)
+	for (uint i = 0; i < 4; i++)
 		f[i] = matrix(i, 2);
 
 	glTexGenfv(GL_R, GL_EYE_PLANE, f);
 	glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
 
-	for (unsigned int i = 0; i < 4; i++)
+	for (uint i = 0; i < 4; i++)
 		f[i] = matrix(i, 3);
 
 	glTexGenfv(GL_Q, GL_EYE_PLANE, f);
diff --git a/engines/tetraedge/game/documents_browser.cpp b/engines/tetraedge/game/documents_browser.cpp
index a99f231c728..a876feda3a0 100644
--- a/engines/tetraedge/game/documents_browser.cpp
+++ b/engines/tetraedge/game/documents_browser.cpp
@@ -54,7 +54,7 @@ void DocumentsBrowser::hideDocument() {
 
 	bool callFn = true;
 	Common::Array<Game::YieldedCallback> &yieldedcallbacks = game->yieldedCallbacks();
-	for (unsigned int i = 0; i < yieldedcallbacks.size(); i++) {
+	for (uint i = 0; i < yieldedcallbacks.size(); i++) {
 		if (yieldedcallbacks[i]._luaFnName == "OnDocumentClosed" &&
 			yieldedcallbacks[i]._luaParam == docName) {
 			yieldedcallbacks.remove_at(i);
@@ -194,7 +194,7 @@ void DocumentsBrowser::showDocument(const Common::String &docName, long startPag
 	sprite->setSizeType(RELATIVE_TO_PARENT);
 	TeVector3f32 winSize = app->getMainWindow().size();
 	sprite->setSize(TeVector3f32(1.0f, (4.0f / (winSize.y() / winSize.x() * 4.0f)) *
-               ((float)spriteSize._y / (float)spriteSize._x), 0.0f));
+							((float)spriteSize._y / (float)spriteSize._x), 0.0f));
 	TeScrollingLayout *scroll = _gui1.scrollingLayout("scroll");
 	if (!scroll)
 		error("DocumentsBrowser::showDocument Couldn't fetch scroll object");
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index fa7ccca400c..ec45dbf41e4 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -389,7 +389,7 @@ void Game::finishGame() {
 	_playedTimer.stop();
 	/* Game does this but does nothing with result?
 	if (app->difficulty() == 2) {
-	  _playedTimer.getTimeFromStart();
+		_playedTimer.getTimeFromStart();
 	} */
 	app->credits().enter(false);
 }
@@ -648,7 +648,7 @@ bool Game::initWarp(const Common::String &zone, const Common::String &scene, boo
 		_luaScript.execute("OnSelectedObject", _inventory.selectedObject());
 	}
 
-	for (unsigned int i = 0; i < _gameSounds.size(); i++) {
+	for (uint i = 0; i < _gameSounds.size(); i++) {
 		if (_gameSounds[i]->retain())
 			continue;
 		_gameSounds[i]->stop();
@@ -691,7 +691,7 @@ static const char *DIALOG_IDS[20] = {
 	"KFJ", "KM", "KN", "KFM"};
 
 bool Game::launchDialog(const Common::String &dname, uint param_2, const Common::String &charname,
-				  const Common::String &animfile, float animblend) {
+						const Common::String &animfile, float animblend) {
 	Application *app = g_engine->getApplication();
 	const Common::String *locstring = app->loc().value(dname);
 
@@ -722,7 +722,7 @@ bool Game::launchDialog(const Common::String &dname, uint param_2, const Common:
 		}
 	}
 
-	for (unsigned int i = 0; i < ARRAYSIZE(DIALOG_IDS); i++) {
+	for (uint i = 0; i < ARRAYSIZE(DIALOG_IDS); i++) {
 		if (dname.contains(Common::String::format("_%s_", DIALOG_IDS[i])))
 			_dialogsTold++;
 	}
@@ -851,7 +851,7 @@ bool Game::onCharacterAnimationFinished(const Common::String &charName) {
 	if (!_scene._character)
 		return false;
 
-	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+	for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
 		YieldedCallback &cb = _yieldedCallbacks[i];
 		if (cb._luaFnName == "OnCharacterAnimationFinished" && cb._luaParam == charName) {
 			TeLuaThread *lua = cb._luaThread;
@@ -872,7 +872,7 @@ bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
 		return false;
 
 	bool callScripts = true;
-	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+	for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
 		YieldedCallback &cb = _yieldedCallbacks[i];
 		if (cb._luaFnName == "OnCharacterAnimationFinished" && cb._luaParam == "Kate") {
 			TeLuaThread *lua = cb._luaThread;
@@ -920,7 +920,7 @@ bool Game::onCharacterAnimationPlayerFinished(const Common::String &anim) {
 }
 
 bool Game::onDialogFinished(const Common::String &val) {
-	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+	for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
 		YieldedCallback &cb = _yieldedCallbacks[i];
 		if (cb._luaFnName == "OnDialogFinished" && cb._luaParam == val) {
 			TeLuaThread *lua = cb._luaThread;
@@ -952,7 +952,7 @@ bool Game::onDisplacementFinished() {
 
 	TeLuaThread *thread = nullptr;
 
-	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+	for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
 		YieldedCallback &cb = _yieldedCallbacks[i];
 		if (cb._luaFnName == "OnDisplacementFinished") {
 			thread = cb._luaThread;
@@ -1186,7 +1186,7 @@ bool Game::onMouseMove() {
 	bool checkedCursor = false;
 	if (!skipFullSearch && _scene.gui2().loaded()) {
 		TeLayout *bglayout = _scene.gui2().layoutChecked("background");
-		for (unsigned int i = 0; i < bglayout->childCount(); i++) {
+		for (uint i = 0; i < bglayout->childCount(); i++) {
 			TeLayout *childlayout = dynamic_cast<TeLayout *>(bglayout->child(i));
 			if (childlayout && childlayout->isMouseIn(mouseLoc) && childlayout->visible()) {
 				for (int i = 0; i < ARRAYSIZE(cursorsTable); i++) {
@@ -1235,7 +1235,7 @@ bool Game::onVideoFinished() {
 	_music.stop();
 	_running = true;
 	bool resumed = false;
-	for (unsigned int i = 0; i < _yieldedCallbacks.size(); i++) {
+	for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
 		YieldedCallback &cb = _yieldedCallbacks[i];
 		if (cb._luaFnName == "OnMovieFinished" && cb._luaParam == vidPath) {
 			TeLuaThread *lua = cb._luaThread;
@@ -1317,7 +1317,7 @@ void Game::playRandomSound(const Common::String &name) {
 		}
 		int r = _randomSource.getRandomNumber(RAND_MAX);
 		float total2 = 0.0;
-		unsigned int i = 0;
+		uint i = 0;
 		while (i < sndlist.size() && total2 <= r * 4.656613e-10 * total) {
 			total2 += sndlist[i]->_f1;
 			i++;
@@ -1445,11 +1445,10 @@ bool Game::setBackground(const Common::String &name) {
 void Game::setCurrentObjectSprite(const Common::Path &spritePath) {
 	TeSpriteLayout *currentSprite = _inGameGui.spriteLayout("currentObjectSprite");
 	if (currentSprite) {
-		if (spritePath.empty()) {
+		if (spritePath.empty())
 			currentSprite->unload();
-		} else {
+		else
 			currentSprite->load(spritePath);
-	  }
 	}
 }
 
@@ -1477,7 +1476,7 @@ bool Game::startAnimation(const Common::String &animName, int loopcount, bool re
 }
 
 void Game::stopSound(const Common::String &name) {
-	for (unsigned int i = 0; i < _gameSounds.size(); i++) {
+	for (uint i = 0; i < _gameSounds.size(); i++) {
 		GameSound *sound = _gameSounds[i];
 		if (sound->rawPath() == name) {
 			sound->stop();
@@ -1504,7 +1503,7 @@ Common::Error Game::syncGame(Common::Serializer &s) {
 	_playedTimer.stop();
 	_playedTimer.start();
 	s.syncAsUint32LE(_objectsTakenVal);
-	for (unsigned int i = 0; i < ARRAYSIZE(_objectsTakenBits); i++)
+	for (uint i = 0; i < ARRAYSIZE(_objectsTakenBits); i++)
 		s.syncAsByte(_objectsTakenBits[i]);
 	s.syncAsUint32LE(_dialogsTold);
 	s.syncString(_prevSceneName);
@@ -1524,7 +1523,7 @@ bool Game::unloadCharacter(const Common::String &charname) {
 	if (!c)
 		return false;
 
-	for (unsigned int i = 0; i < _scene.models().size(); i++) {
+	for (uint i = 0; i < _scene.models().size(); i++) {
 		if (_scene.models()[i] == c->_model) {
 			_scene.models().remove_at(i);
 			break;
diff --git a/engines/tetraedge/game/game_sound.cpp b/engines/tetraedge/game/game_sound.cpp
index 16a86615ce3..b7f45c3cee1 100644
--- a/engines/tetraedge/game/game_sound.cpp
+++ b/engines/tetraedge/game/game_sound.cpp
@@ -36,7 +36,7 @@ bool GameSound::onSoundStopped() {
 		return false;
 
 	Common::Array<Game::YieldedCallback> &callbacks = game->yieldedCallbacks();
-	for (unsigned int i = 0; i < callbacks.size(); i++) {
+	for (uint i = 0; i < callbacks.size(); i++) {
 		if (callbacks[i]._luaFnName == "OnFreeSoundFinished" && callbacks[i]._luaParam == _name) {
 			TeLuaThread *thread = callbacks[i]._luaThread;
 			callbacks.remove_at(i);
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index ec3d32449dd..4270975a61e 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -227,7 +227,7 @@ void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
 	model->setScale(zone->scale());
 	unsigned long nverticies = zone->verticies().size();
 	model->meshes()[0].setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
-	for (unsigned int i = 0; i < nverticies; i++) {
+	for (uint i = 0; i < nverticies; i++) {
 		model->meshes()[0].setIndex(i, i);
 		model->meshes()[0].setVertex(i, zone->verticies()[i]);
 		model->meshes()[0].setNormal(i, TeVector3f32(0, 0, 1));
@@ -257,7 +257,7 @@ void InGameScene::deleteMarker(const Common::String &markerName) {
 	if (!isMarker(markerName))
 		return;
 
-	for (unsigned int i = 0; i < _markers.size(); i++) {
+	for (uint i = 0; i < _markers.size(); i++) {
 		if (_markers[i]._name == markerName) {
 			_markers.remove_at(i);
 			break;
@@ -313,28 +313,28 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 		error("InGameScene::deserializeModel: Unxpected counts %d %d", indexcount, vertexcount);
 
 	mesh.setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
-	for (unsigned int i = 0; i < indexcount; i++)
+	for (uint i = 0; i < indexcount; i++)
 		mesh.setIndex(i, stream.readUint32LE());
 
-	for (unsigned int i = 0; i < vertexcount; i++) {
+	for (uint i = 0; i < vertexcount; i++) {
 		TeVector3f32::deserialize(stream, vec);
 		mesh.setVertex(i, vec);
 	}
-	for (unsigned int i = 0; i < vertexcount; i++) {
+	for (uint i = 0; i < vertexcount; i++) {
 		TeVector3f32::deserialize(stream, vec);
 		mesh.setNormal(i, vec);
 	}
-	for (unsigned int i = 0; i < vertexcount; i++) {
+	for (uint i = 0; i < vertexcount; i++) {
 		TeVector2f32::deserialize(stream, vec2);
 		mesh.setTextureUV(i, vec2);
 	}
-	for (unsigned int i = 0; i < vertexcount; i++) {
+	for (uint i = 0; i < vertexcount; i++) {
 		col.deserialize(stream);
 		mesh.setColor(i, col);
 	}
 
 	pickmesh->setNbTriangles(indexcount / 3);
-	for (unsigned int i = 0; i < indexcount; i++) {
+	for (uint i = 0; i < indexcount; i++) {
 		vec = mesh.vertex(mesh.index(i));
 		pickmesh->verticies()[i] = vec;
 	}
@@ -367,7 +367,7 @@ void InGameScene::draw() {
 #endif
 
 	TeLight::updateGlobal();
-	for (unsigned int i = 0; i < _lights.size(); i++)
+	for (uint i = 0; i < _lights.size(); i++)
 		_lights[i].update(i);
 
 	TeCamera::restore();
@@ -380,7 +380,7 @@ void InGameScene::drawPath() {
 	currentCamera()->apply();
 	g_engine->getRenderer()->disableZBuffer();
 
-	for (unsigned int i = 0; i < _freeMoveZones.size(); i++)
+	for (uint i = 0; i < _freeMoveZones.size(); i++)
 		_freeMoveZones[i]->draw();
 
 	g_engine->getRenderer()->enableZBuffer();
@@ -545,7 +545,7 @@ bool InGameScene::load(const Common::Path &path) {
 			if (count > 1000000)
 				error("Improbable number of actzones %d", count);
 			_actZones.resize(count);
-			for (unsigned int i = 0; i < _actZones.size(); i++) {
+			for (uint i = 0; i < _actZones.size(); i++) {
 				_actZones[i].s1 = Te3DObject2::deserializeString(actzonefile);
 				_actZones[i].s2 = Te3DObject2::deserializeString(actzonefile);
 				for (int j = 0; j < 4; j++)
@@ -557,7 +557,7 @@ bool InGameScene::load(const Common::Path &path) {
 	}
 	if (!_lights.empty()) {
 		TeLight::disableAll();
-		for (unsigned int i = 0; i < _lights.size(); i++) {
+		for (uint i = 0; i < _lights.size(); i++) {
 			_lights[i].disable(i);
 		}
 		_lights.clear();
@@ -580,7 +580,7 @@ bool InGameScene::load(const Common::Path &path) {
 	uint32 ncameras = scenefile.readUint32LE();
 	if (ncameras > 1024)
 		error("Improbable number of cameras %d", ncameras);
-	for (unsigned int i = 0; i < ncameras; i++) {
+	for (uint i = 0; i < ncameras; i++) {
 		TeIntrusivePtr<TeCamera> cam = new TeCamera();
 		deserializeCam(scenefile, cam);
 		cameras().push_back(cam);
@@ -589,7 +589,7 @@ bool InGameScene::load(const Common::Path &path) {
 	uint32 nobjects = scenefile.readUint32LE();
 	if (nobjects > 1024)
 		error("Improbable number of objects %d", nobjects);
-	for (unsigned int i = 0; i < nobjects; i++) {
+	for (uint i = 0; i < nobjects; i++) {
 		TeIntrusivePtr<TeModel> model = new TeModel();
 		const Common::String modelname = Te3DObject2::deserializeString(scenefile);
 		model->setName(modelname);
@@ -624,7 +624,7 @@ bool InGameScene::load(const Common::Path &path) {
 	uint32 nfreemovezones = scenefile.readUint32LE();
 	if (nfreemovezones > 1024)
 		error("Improbable number of free move zones %d", nfreemovezones);
-	for (unsigned int i = 0; i < nfreemovezones; i++) {
+	for (uint i = 0; i < nfreemovezones; i++) {
 		TeFreeMoveZone *zone = new TeFreeMoveZone();
 		TeFreeMoveZone::deserialize(scenefile, *zone, &_blockers, &_rectBlockers, &_actZones);
 		_freeMoveZones.push_back(zone);
@@ -634,7 +634,7 @@ bool InGameScene::load(const Common::Path &path) {
 	uint32 ncurves = scenefile.readUint32LE();
 	if (ncurves > 1024)
 		error("Improbable number of curves %d", ncurves);
-	for (unsigned int i = 0; i < ncurves; i++) {
+	for (uint i = 0; i < ncurves; i++) {
 		TeIntrusivePtr<TeBezierCurve> curve = new TeBezierCurve();
 		TeBezierCurve::deserialize(scenefile, *curve);
 		curve->setVisible(true);
@@ -644,7 +644,7 @@ bool InGameScene::load(const Common::Path &path) {
 	uint32 ndummies = scenefile.readUint32LE();
 	if (ndummies > 1024)
 		error("Improbable number of dummies %d", ndummies);
-	for (unsigned int i = 0; i < ndummies; i++) {
+	for (uint i = 0; i < ndummies; i++) {
 		InGameScene::Dummy dummy;
 		TeVector3f32 vec;
 		TeQuaternion rot;
@@ -702,7 +702,7 @@ bool InGameScene::loadLights(const Common::Path &path) {
 	_shadowFov = parser.getShadowFov();
 
 	TeLight::enableAll();
-	for (unsigned int i = 0; i < _lights.size(); i++) {
+	for (uint i = 0; i < _lights.size(); i++) {
 		_lights[i].enable(i);
 	}
 
@@ -710,7 +710,7 @@ bool InGameScene::loadLights(const Common::Path &path) {
 	debug("--- Scene lights ---");
 	debug("Shadow: %s no:%d far:%.02f near:%.02f fov:%.02f", _shadowColor.dump().c_str(), _shadowLightNo, _shadowFarPlane, _shadowNearPlane, _shadowFov);
 	debug("Global: %s", TeLight::globalAmbient().dump().c_str());
-	for (unsigned int i = 0; i < _lights.size(); i++) {
+	for (uint i = 0; i < _lights.size(); i++) {
 		debug("%s", _lights[i].dump().c_str());
 	}
 	debug("---  end lights  ---");
@@ -827,7 +827,7 @@ void InGameScene::loadBlockers() {
 	if (nblockers > 1024)
 		error("Improbable number of blockers %d", nblockers);
 	_blockers.resize(nblockers);
-	for (unsigned int i = 0; i < nblockers; i++) {
+	for (uint i = 0; i < nblockers; i++) {
 		_blockers[i]._s = Te3DObject2::deserializeString(blockersfile);
 		TeVector2f32::deserialize(blockersfile, _blockers[i]._pts[0]);
 		TeVector2f32::deserialize(blockersfile, _blockers[i]._pts[1]);
@@ -839,9 +839,9 @@ void InGameScene::loadBlockers() {
 		if (nrectblockers > 1024)
 			error("Improbable number of rectblockers %d", nrectblockers);
 		_rectBlockers.resize(nrectblockers);
-		for (unsigned int i = 0; i < nrectblockers; i++) {
+		for (uint i = 0; i < nrectblockers; i++) {
 			_rectBlockers[i]._s = Te3DObject2::deserializeString(blockersfile);
-			for (unsigned int j = 0; j < 4l; j++) {
+			for (uint j = 0; j < 4l; j++) {
 				TeVector2f32::deserialize(blockersfile, _rectBlockers[i]._pts[j]);
 			}
 			_rectBlockers[i]._enabled = true;
@@ -925,7 +925,7 @@ void InGameScene::onMainWindowSizeChanged() {
 	TeCamera *mainWinCam = g_engine->getApplication()->mainWindowCamera();
 	_viewportSize = mainWinCam->viewportSize();
 	Common::Array<TeIntrusivePtr<TeCamera>> &cams = cameras();
-	for (unsigned int i = 0; i < cams.size(); i++) {
+	for (uint i = 0; i < cams.size(); i++) {
 		cams[i]->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
 	}
 }
@@ -1048,7 +1048,7 @@ void InGameScene::unloadCharacter(const Common::String &name) {
 		// TODO: deleteLater() something here..
 		_character = nullptr;
 	}
-	for (unsigned int i = 0; i < _characters.size(); i++) {
+	for (uint i = 0; i < _characters.size(); i++) {
 		Character *c = _characters[i];
 		if (c && c->_model->name() == name) {
 			c->removeAnim();
@@ -1065,10 +1065,10 @@ void InGameScene::unloadCharacter(const Common::String &name) {
 }
 
 void InGameScene::unloadObject(const Common::String &name) {
-	for (unsigned int i = 0; i < _object3Ds.size(); i++) {
+	for (uint i = 0; i < _object3Ds.size(); i++) {
 		if (_object3Ds[i]->model()->name() == name) {
 			// Remove from the scene models.
-			for (unsigned int j = 0; j < models().size(); j++) {
+			for (uint j = 0; j < models().size(); j++) {
 				if (models()[j] == _object3Ds[i]->model())	{
 					models().remove_at(j);
 					break;
@@ -1154,7 +1154,7 @@ void InGameScene::update() {
 		_waitTime = -1.0;
 		_waitTimeTimer.stop();
 		bool resumed = false;
-		for (unsigned int i = 0; i < game->yieldedCallbacks().size(); i++) {
+		for (uint i = 0; i < game->yieldedCallbacks().size(); i++) {
 			Game::YieldedCallback &yc = game->yieldedCallbacks()[i];
 			if (yc._luaFnName == "OnWaitFinished") {
 				TeLuaThread *thread = yc._luaThread;
@@ -1184,7 +1184,7 @@ void InGameScene::update() {
 
 bool InGameScene::AnimObject::onFinished() {
 	Game *game = g_engine->getGame();
-	for (unsigned int i = 0; i < game->yieldedCallbacks().size(); i++) {
+	for (uint i = 0; i < game->yieldedCallbacks().size(); i++) {
 		Game::YieldedCallback &yc = game->yieldedCallbacks()[i];
 		if (yc._luaFnName == "OnFinishedAnim" && yc._luaParam == _name) {
 			TeLuaThread *thread = yc._luaThread;
diff --git a/engines/tetraedge/game/inventory.cpp b/engines/tetraedge/game/inventory.cpp
index 05169ca366d..2a2be4ec074 100644
--- a/engines/tetraedge/game/inventory.cpp
+++ b/engines/tetraedge/game/inventory.cpp
@@ -211,10 +211,10 @@ bool Inventory::addObject(InventoryObject *obj) {
 			}
 			pageNo++;
 		}
-    }
+	}
 
 	int pageno = 0;
-	unsigned int totalSlots = 0;
+	uint totalSlots = 0;
 	bool retval = false;
 	const Common::String newObjName = obj->name();
 	auto invObjIter = _invObjects.begin();
@@ -498,13 +498,13 @@ bool Inventory::updateLayout() {
 //#define DEBUG_SAVELOAD 1
 
 Common::Error Inventory::syncState(Common::Serializer &s) {
-	unsigned int nitems = _invObjects.size();
+	uint nitems = _invObjects.size();
 	s.syncAsUint32LE(nitems);
 	if (s.isLoading()) {
 #if DEBUG_SAVELOAD
 		debug("Inventory::syncState: --- Loading %d inventory items: ---", nitems);
 #endif
-		for (unsigned int i = 0; i < nitems; i++) {
+		for (uint i = 0; i < nitems; i++) {
 			Common::String objname;
 			s.syncString(objname);
 			addObject(objname);
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 52156f6c68d..01a57c56126 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -1551,7 +1551,7 @@ static int tolua_ExportedFunctions_Random00(lua_State *L) {
 	tolua_Error err;
 	if (tolua_isnumber(L, 1, 0, &err) && tolua_isnoobj(L, 2, &err)) {
 		double d1 = tolua_tonumber(L, 1, 0.0);
-		unsigned int result = Random(d1);
+		uint result = Random(d1);
 		tolua_pushnumber(L, result);
 		return 1;
 	}
@@ -2111,7 +2111,7 @@ void LuaOpenBinds(lua_State *L) {
 				 tolua_ExportedFunctions_MoveCharacterToAndWaitForEnd00);
 	tolua_function(L, "MoveCharacterPlayerTo", tolua_ExportedFunctions_MoveCharacterPlayerTo00);
 	// tolua_function(L, "MoveCharacterPlayerToAndWaitForEnd",
-	// 			 tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00); // Unused
+	//				tolua_ExportedFunctions_MoveCharacterPlayerToAndWaitForEnd00); // Unused
 	// tolua_function(L, "MoveCharacterPlayerAtTo", tolua_ExportedFunctions_MoveCharacterPlayerAtTo00); // Unused
 	tolua_function(L, "SetCharacterPosition", tolua_ExportedFunctions_SetCharacterPosition00);
 	tolua_function(L, "PlaceCharacterOnDummy", tolua_ExportedFunctions_PlaceCharacterOnDummy00);
diff --git a/engines/tetraedge/game/main_menu.cpp b/engines/tetraedge/game/main_menu.cpp
index 9a02a952ac0..a996edf9263 100644
--- a/engines/tetraedge/game/main_menu.cpp
+++ b/engines/tetraedge/game/main_menu.cpp
@@ -199,7 +199,7 @@ bool MainMenu::onContinueGameButtonValidated() {
 	tryDisableButton("quitButton");
 
 	if (_confirmingTuto)
-	  return false;
+		return false;
 
 	app->captureFade();
 	leave();
diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
index 6d274095cb0..42a93841cb7 100644
--- a/engines/tetraedge/game/objectif.cpp
+++ b/engines/tetraedge/game/objectif.cpp
@@ -166,7 +166,7 @@ void Objectif::update() {
 		removeChildren();
 
 		int last_i = -1;
-		for (unsigned int i = 0; i < _tasks.size(); i++) {
+		for (uint i = 0; i < _tasks.size(); i++) {
 			if (!_tasks[i]._taskFlag)
 				continue;
 			if (last_i != -1 && _tasks[i]._headTask == _tasks[last_i]._headTask)
@@ -174,7 +174,7 @@ void Objectif::update() {
 			last_i = i;
 			createChildLayout(tasks, _tasks[i]._headTask, false);
 			// Creating the subtasks for this head
-			for (unsigned int j = 0; j < _tasks.size(); j++) {
+			for (uint j = 0; j < _tasks.size(); j++) {
 				if (_tasks[j]._taskFlag && _tasks[j]._headTask == _tasks[i]._headTask && _tasks[j]._subTask != "")
 					createChildLayout(tasks, _tasks[j]._subTask, true);
 			}
diff --git a/engines/tetraedge/metaengine.cpp b/engines/tetraedge/metaengine.cpp
index f88e5240f60..10d7d9cd0d7 100644
--- a/engines/tetraedge/metaengine.cpp
+++ b/engines/tetraedge/metaengine.cpp
@@ -36,11 +36,11 @@ bool TetraedgeMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
 		(f == kSavesUseExtendedFormat) ||
 		(f == kSimpleSavesNames) ||
-	    (f == kSupportsListSaves) ||
-	    (f == kSupportsDeleteSave) ||
-	    (f == kSavesSupportMetaInfo) ||
-	    (f == kSavesSupportThumbnail) ||
-	    (f == kSupportsLoadingDuringStartup);
+		(f == kSupportsListSaves) ||
+		(f == kSupportsDeleteSave) ||
+		(f == kSavesSupportMetaInfo) ||
+		(f == kSavesSupportThumbnail) ||
+		(f == kSupportsLoadingDuringStartup);
 }
 
 void TetraedgeMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
diff --git a/engines/tetraedge/te/micropather.cpp b/engines/tetraedge/te/micropather.cpp
index 2e469d6791f..4ad0c1af493 100644
--- a/engines/tetraedge/te/micropather.cpp
+++ b/engines/tetraedge/te/micropather.cpp
@@ -367,7 +367,7 @@ unsigned PathNodePool::Hash( void* voidval )
 	// public domain.
 	MP_UPTR val = (MP_UPTR)(voidval);
 	const unsigned char *p = (unsigned char *)(&val);
-	unsigned int h = 2166136261;
+	uint h = 2166136261;
 
 	for( size_t i=0; i<sizeof(MP_UPTR); ++i, ++p ) {
 		h ^= *p;
@@ -704,20 +704,20 @@ void MicroPather::DumpStats()
 
 void MicroPather::StatesInPool( Common::Array< void* >* stateVec )
 {
- 	stateVec->clear();
+	stateVec->clear();
 	pathNodePool.AllStates( frame, stateVec );
 }
 
 
 void PathNodePool::AllStates( unsigned frame, Common::Array< void* >* stateVec )
 {
-    for ( Block* b=blocks; b; b=b->nextBlock )
-    {
-    	for( unsigned i=0; i<allocate; ++i )
-    	{
-    	    if ( b->pathNode[i].frame == frame )
-	    	    stateVec->push_back( b->pathNode[i].state );
-    	}
+	for ( Block* b=blocks; b; b=b->nextBlock )
+	{
+		for( unsigned i=0; i<allocate; ++i )
+		{
+			if ( b->pathNode[i].frame == frame )
+				stateVec->push_back( b->pathNode[i].state );
+		}
 	}
 }
 
diff --git a/engines/tetraedge/te/micropather.h b/engines/tetraedge/te/micropather.h
index fa4fe3052ec..f4804f1a78a 100644
--- a/engines/tetraedge/te/micropather.h
+++ b/engines/tetraedge/te/micropather.h
@@ -107,7 +107,7 @@ namespace micropather
 	*/
 	class Graph
 	{
-	  public:
+	public:
 		virtual ~Graph() {}
 
 		/**
@@ -150,7 +150,7 @@ namespace micropather
 	*/
 	class PathNode
 	{
-	  public:
+	public:
 		void Init(	unsigned _frame,
 					void* _state,
 					float _costFromStart,
@@ -210,7 +210,7 @@ namespace micropather
 				totalCost = FLT_MAX;
 		}
 
-	  private:
+	private:
 
 		void operator=( const PathNode& );
 	};
@@ -311,7 +311,7 @@ namespace micropather
 
 			unsigned Hash() const {
 				const unsigned char *p = (const unsigned char *)(&start);
-				unsigned int h = 2166136261U;
+				uint h = 2166136261U;
 
 				for( unsigned i=0; i<sizeof(void*)*2; ++i, ++p ) {
 					h ^= *p;
@@ -363,7 +363,7 @@ namespace micropather
 	{
 		friend class micropather::PathNode;
 
-	  public:
+	public:
 		enum
 		{
 			SOLVED,
@@ -388,7 +388,7 @@ namespace micropather
 								  would be set to 8x8 (64)
 								- If your map is large, something like 1/4 the number of possible
 								  states is good.
-							    - If your state space is huge, use a multiple (5-10x) of the normal
+								- If your state space is huge, use a multiple (5-10x) of the normal
 								  path. "Occasionally" call Reset() to free unused memory.
 			@param typicalAdjacent	Used to determine cache size. The typical number of adjacent states
 									to a given state. (On a chessboard, 8.) Higher values use a little
@@ -431,7 +431,7 @@ namespace micropather
 		void StatesInPool( Common::Array< void* >* stateVec );
 		void GetCacheData( CacheData* data );
 
-	  private:
+	private:
 		MicroPather( const MicroPather& );	// undefined and unsupported
 		void operator=( const MicroPather ); // undefined and unsupported
 
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index 7519441900e..5f96a047c37 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -345,17 +345,17 @@ void Te3DObject2::deserializeVectorArray(Common::ReadStream &stream, Common::Arr
 	if (nentries > 1000000)
 		error("TeFreeMoveZone improbable number of vectors %d", nentries);
 	dest.resize(nentries);
-	for (unsigned int i = 0; i < nentries; i++)
+	for (uint i = 0; i < nentries; i++)
 		TeVector3f32::deserialize(stream, dest[i]);
 }
 
 /*static*/
-void Te3DObject2::deserializeUintArray(Common::ReadStream &stream, Common::Array<unsigned int> &dest) {
+void Te3DObject2::deserializeUintArray(Common::ReadStream &stream, Common::Array<uint> &dest) {
 	uint32 nentries = stream.readUint32LE();
 	if (nentries > 1000000)
 		error("TeFreeMoveZone improbable number of ints %d", nentries);
 	dest.resize(nentries);
-	for (unsigned int i = 0; i < nentries; i++)
+	for (uint i = 0; i < nentries; i++)
 		dest[i] = stream.readUint32LE();
 }
 
diff --git a/engines/tetraedge/te/te_3d_object2.h b/engines/tetraedge/te/te_3d_object2.h
index 3c5e090888a..c094957c0d0 100644
--- a/engines/tetraedge/te/te_3d_object2.h
+++ b/engines/tetraedge/te/te_3d_object2.h
@@ -144,7 +144,7 @@ public:
 	static bool loadAndCheckFourCC(Common::ReadStream &stream, const char *str);
 	static Common::String deserializeString(Common::ReadStream &stream);
 	static void deserializeVectorArray(Common::ReadStream &stream, Common::Array<TeVector3f32> &dest);
-	static void deserializeUintArray(Common::ReadStream &stream, Common::Array<unsigned int> &dest);
+	static void deserializeUintArray(Common::ReadStream &stream, Common::Array<uint> &dest);
 
 protected:
 	TeVector3f32 _size;
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index a40b475343d..8d4e7a9a00d 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -55,10 +55,8 @@ void Te3DTexture::copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y)
 	const TeVector3f32 offset((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0);
 	_matrix.translate(offset);
 	const TeVector3f32 borderScale(
-			   1.0 - (float)(_rightBorder + _leftBorder) /
-					 (float)_width,
-			   1.0 - (float)(_topBorder + _btmBorder) /
-					 (float)_height, 1.0);
+			1.0 - (float)(_rightBorder + _leftBorder) / (float)_width,
+			1.0 - (float)(_topBorder + _btmBorder) / (float)_height, 1.0);
 	_matrix.scale(borderScale);
 	bind();
 	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, _texWidth, _texHeight);
@@ -96,7 +94,7 @@ void Te3DTexture::create() {
 
 void Te3DTexture::destroy() {
 	if (_createdTexture) {
-	  glDeleteTextures(1, &_glTexture);
+		glDeleteTextures(1, &_glTexture);
 	}
 	_createdTexture = false;
 	_loaded = false;
@@ -200,7 +198,7 @@ bool Te3DTexture::load(const TeImage &img) {
 	_matrix.scale(TeVector3f32((float)_width / _texWidth, (float)_height / _texHeight, 1.0f));
 	_matrix.translate(TeVector3f32((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0f));
 	_matrix.scale(TeVector3f32(1.0 - (float)(_rightBorder + _leftBorder) / _width,
-			   1.0 - (float)(_topBorder + _btmBorder) / _height, 1.0f));
+					1.0 - (float)(_topBorder + _btmBorder) / _height, 1.0f));
 	if (_flipY) {
 		_matrix.translate(TeVector3f32(0.0f, 1.0f, 0.0f));
 		_matrix.scale(TeVector3f32(1.0f, -1.0f, 1.0f));
diff --git a/engines/tetraedge/te/te_animation.cpp b/engines/tetraedge/te/te_animation.cpp
index 5256ce11d5f..7048112b137 100644
--- a/engines/tetraedge/te/te_animation.cpp
+++ b/engines/tetraedge/te/te_animation.cpp
@@ -113,7 +113,7 @@ void TeAnimation::updateAll() {
 	Common::Array<TeAnimation *> &anims = *animations();
 	// Note: update can cause events which cascade into animtaions
 	// getting deleted, so be careful about the numbers.
-	for (unsigned int i = 0; i < anims.size(); i++) {
+	for (uint i = 0; i < anims.size(); i++) {
 		if (anims[i]->_runTimer.running()) {
 			float msFromStart = anims[i]->_runTimer.getTimeFromStart() / 1000.0;
 			anims[i]->update(msFromStart);
diff --git a/engines/tetraedge/te/te_bezier_curve.cpp b/engines/tetraedge/te/te_bezier_curve.cpp
index 400fbd1fc55..b92a6f9df98 100644
--- a/engines/tetraedge/te/te_bezier_curve.cpp
+++ b/engines/tetraedge/te/te_bezier_curve.cpp
@@ -44,18 +44,18 @@ void TeBezierCurve::draw() {
 	if (!worldVisible() || _controlPoints.empty())
 		return;
 
-    TeMesh mesh1;
-    TeMesh mesh2;
-    unsigned int npoints = _controlPoints.size();
+	TeMesh mesh1;
+	TeMesh mesh2;
+	uint npoints = _controlPoints.size();
 
 	mesh1.setConf(npoints, npoints, TeMesh::MeshMode_Points, 0, 0);
-	for (unsigned int i = 0; i < npoints; i++) {
+	for (uint i = 0; i < npoints; i++) {
 		mesh1.setVertex(i, _controlPoints[i]);
 		mesh1.setIndex(i, i);
 	}
 
 	mesh2.setConf(npoints, npoints, TeMesh::MeshMode_LineStrip, 0, 0);
-	for (unsigned int i = 0; i < npoints; i++) {
+	for (uint i = 0; i < npoints; i++) {
 		mesh2.setVertex(i, _controlPoints[i]);
 		mesh2.setNormal(i, TeVector3f32(0.0f, 1.0f, 0.0));
 		mesh2.setIndex(i, i);
@@ -81,7 +81,7 @@ float TeBezierCurve::length() {
 
 		TeVector3f32 lastpt = _controlPoints[0];
 		lastpt.y() = 0;
-		for (unsigned int i = 0; i < _numIterations; i++) {
+		for (uint i = 0; i < _numIterations; i++) {
 			float amount = (float)i / _numIterations;
 			TeVector3f32 pt = retrievePoint(amount);
 			pt.y() = 0;
@@ -111,7 +111,7 @@ float TeBezierCurve::rawLength() {
 		_rawLength = 0.0;
 		_rawLengths.clear();
 		_rawLengths.push_back(0.0);
-		for (unsigned int i = 1; i < _controlPoints.size(); i++) {
+		for (uint i = 1; i < _controlPoints.size(); i++) {
 			const TeVector3f32 diff = _controlPoints[i] - _controlPoints[i - 1];
 			_rawLength += diff.length();
 			_rawLengths.push_back(_rawLength);
@@ -200,7 +200,7 @@ void TeBezierCurve::deserialize(Common::ReadStream &stream, TeBezierCurve &curve
 	if (npoints > 1000000)
 		error("TeBezierCurve::deserialize improbable number of control ponts %d", npoints);
 
-	for (unsigned int i = 0; i < npoints; i++) {
+	for (uint i = 0; i < npoints; i++) {
 		TeVector3f32 vec;
 		TeVector3f32::deserialize(stream, vec);
 		curve._controlPoints.push_back(vec);
diff --git a/engines/tetraedge/te/te_bezier_curve.h b/engines/tetraedge/te/te_bezier_curve.h
index 358edb65a81..1626cb931f0 100644
--- a/engines/tetraedge/te/te_bezier_curve.h
+++ b/engines/tetraedge/te/te_bezier_curve.h
@@ -52,10 +52,10 @@ public:
 	static void deserialize(Common::ReadStream &stream, TeBezierCurve &curve);
 
 	const Common::Array<TeVector3f32> &controlPoints() { return _controlPoints; }
-	unsigned int numIterations() const { return _numIterations; }
+	uint numIterations() const { return _numIterations; }
 
 private:
-	unsigned int _numIterations;
+	uint _numIterations;
 	float _length;
 	float _rawLength;
 	bool _lengthNeedsUpdate;
diff --git a/engines/tetraedge/te/te_button_layout.h b/engines/tetraedge/te/te_button_layout.h
index 74abb0d398e..240123ece2e 100644
--- a/engines/tetraedge/te/te_button_layout.h
+++ b/engines/tetraedge/te/te_button_layout.h
@@ -110,7 +110,7 @@ private:
 	Common::String _validationSound;
 	float _validationSoundVolume;
 
-	Common::Array<unsigned int> _intArr;
+	Common::Array<uint> _intArr;
 
 	TeICallback1ParamPtr<const Common::Point &> _onMousePositionChangedMaxPriorityCallback;
 	TeICallback1ParamPtr<const Common::Point &> _onMousePositionChangedCallback;
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index 969aff846a9..3183ab78b5e 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -68,15 +68,15 @@ void TeCamera::applyTransformations() {
 void TeCamera::buildOrthoMatrix() {
 	float widthNorm = FLT_MAX;
 	if ((_orthogonalParamR - _orthogonalParamL) != 0.0) {
-	  widthNorm = 1.0 / (_orthogonalParamR - _orthogonalParamL);
+		widthNorm = 1.0 / (_orthogonalParamR - _orthogonalParamL);
 	}
 	float heightNorm = FLT_MAX;
 	if (_orthogonalParamB - _orthogonalParamT != 0.0) {
-	  heightNorm = 1.0 / (_orthogonalParamB - _orthogonalParamT);
+		heightNorm = 1.0 / (_orthogonalParamB - _orthogonalParamT);
 	}
 	float depthNorm = FLT_MAX;
 	if ((_orthFarVal - _orthNearVal) != 0.0) {
-	  depthNorm = 1.0 / (_orthFarVal - _orthNearVal);
+		depthNorm = 1.0 / (_orthFarVal - _orthNearVal);
 	}
 
 	_projectionMatrix.setValue(0, 0, widthNorm * 2.0f);
diff --git a/engines/tetraedge/te/te_color.h b/engines/tetraedge/te/te_color.h
index 9e65d9e5435..99704bca302 100644
--- a/engines/tetraedge/te/te_color.h
+++ b/engines/tetraedge/te/te_color.h
@@ -59,7 +59,7 @@ public:
 
 	Common::String dump() const {
 		return Common::String::format("TeColor(%d %d %d %d)",
-									  _c[0], _c[1], _c[2], _c[3]);
+									 _c[0], _c[1], _c[2], _c[3]);
 	}
 
 private:
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index cc925988fbe..bb6b76e292d 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -168,7 +168,7 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 				return testPath;
 
 			// also try the other way around
- 			if (!lang.empty() && !suffix.empty()) {
+			if (!lang.empty() && !suffix.empty()) {
 				testPath = dir.join(lang).joinInPlace(suffix).join(fname);
 				if (Common::File::exists(testPath) || Common::FSNode(testPath).exists())
 					return testPath;
diff --git a/engines/tetraedge/te/te_font3.cpp b/engines/tetraedge/te/te_font3.cpp
index 718084a7978..18c03a031cc 100644
--- a/engines/tetraedge/te/te_font3.cpp
+++ b/engines/tetraedge/te/te_font3.cpp
@@ -75,7 +75,7 @@ TeFont3::~TeFont3() {
 	unload();
 }
 
-Graphics::Font *TeFont3::getAtSize(unsigned int size) {
+Graphics::Font *TeFont3::getAtSize(uint size) {
 	if (_fonts.contains(size))
 		return _fonts.getVal(size);
 
@@ -94,7 +94,7 @@ Graphics::Font *TeFont3::getAtSize(unsigned int size) {
 	return newFont;
 }
 
-TeFont3::GlyphData TeFont3::glyph(unsigned int pxSize, unsigned int charcode) {
+TeFont3::GlyphData TeFont3::glyph(uint pxSize, uint charcode) {
 	Graphics::Font *font = getAtSize(pxSize);
 	Common::Rect bbox = font->getBoundingBox(charcode);
 	TeImage *img = new TeImage();
@@ -181,21 +181,21 @@ void TeFont3::unload() {
 void TeFont3::init() {
 }
 
-float TeFont3::ascender(unsigned int pxSize) {
+float TeFont3::ascender(uint pxSize) {
 	Graphics::Font *font = getAtSize(pxSize);
 	return font->getFontAscent();
 }
 
-float TeFont3::descender(unsigned int pxSize) {
+float TeFont3::descender(uint pxSize) {
 	error("TODO: Implement TeFont3::descender");
 }
 
-float TeFont3::height(unsigned int pxSize) {
+float TeFont3::height(uint pxSize) {
 	Graphics::Font *font = getAtSize(pxSize);
 	return font->getFontHeight();
 }
 
-TeVector3f32 TeFont3::kerning(unsigned int pxSize, unsigned int isocode1, unsigned int isocode2) {
+TeVector3f32 TeFont3::kerning(uint pxSize, uint isocode1, uint isocode2) {
 	uint32 uni1 = getUnicodeFromISO(isocode1);
 	uint32 uni2 = getUnicodeFromISO(isocode2);
 	Graphics::Font *font = getAtSize(pxSize);
diff --git a/engines/tetraedge/te/te_font3.h b/engines/tetraedge/te/te_font3.h
index e30918d66f1..44876c3393c 100644
--- a/engines/tetraedge/te/te_font3.h
+++ b/engines/tetraedge/te/te_font3.h
@@ -65,12 +65,12 @@ public:
 	bool load(const Common::Path &path);
 	void unload();
 
-	GlyphData glyph(unsigned int size, unsigned int charcode);
+	GlyphData glyph(uint size, uint charcode);
 
-	float ascender(unsigned int pxSize);
-	float descender(unsigned int pxSize);
-	float height(unsigned int pxSize);
-	TeVector3f32 kerning(unsigned int pxSize, unsigned int isocode1, unsigned int isocode2);
+	float ascender(uint pxSize);
+	float descender(uint pxSize);
+	float height(uint pxSize);
+	TeVector3f32 kerning(uint pxSize, uint isocode1, uint isocode2);
 	TeIntrusivePtr<Te3DTexture> getFontSizeData(int size) const {
 		return _fontSizeData[size];
 	}
@@ -84,12 +84,12 @@ public:
 
 private:
 	void init();
-	Graphics::Font *getAtSize(unsigned int size);
+	Graphics::Font *getAtSize(uint size);
 
 	Common::File _fontFile;
-	Common::HashMap<unsigned int, Graphics::Font *> _fonts;
+	Common::HashMap<uint, Graphics::Font *> _fonts;
 	Common::Path _loadedPath;
-	Common::HashMap<unsigned int, TeIntrusivePtr<Te3DTexture>> _fontSizeData;
+	Common::HashMap<uint, TeIntrusivePtr<Te3DTexture>> _fontSizeData;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index ba4dd874364..9302d69c309 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -236,7 +236,7 @@ TeIntrusivePtr<TeBezierCurve> TeFreeMoveZone::curve(const TeVector3f32 &startpt,
 
 /*static*/
 void TeFreeMoveZone::deserialize(Common::ReadStream &stream, TeFreeMoveZone &dest, Common::Array<TeBlocker> *blockers,
-               Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones) {
+			Common::Array<TeRectBlocker> *rectblockers, Common::Array<TeActZone> *actzones) {
 	dest.clear();
 	TePickMesh2::deserialize(stream, dest);
 	TeVector2f32::deserialize(stream, dest._gridOffsetSomething);
@@ -276,7 +276,7 @@ void TeFreeMoveZone::draw() {
 	TePickMesh2::draw();
 	TeMesh mesh;
 	mesh.setConf(_borders.size(), _borders.size(), TeMesh::MeshMode_Lines, 0, 0);
-	for (unsigned int i = 0; i < _borders.size(); i++) {
+	for (uint i = 0; i < _borders.size(); i++) {
 		mesh.setIndex(i, i);
 		mesh.setVertex(i, verticies()[_borders[i]]);
 	}
@@ -300,7 +300,7 @@ TeVector3f32 TeFreeMoveZone::findNearestPointOnBorder(const TeVector2f32 &pt) {
 
 static int segmentIntersection(const TeVector2f32 &s1start, const TeVector2f32 &s1end,
 						const TeVector2f32 &s2start, const TeVector2f32 &s2end,
-                       TeVector2f32 *sout, float *fout1, float *fout2) {
+						TeVector2f32 *sout, float *fout1, float *fout2) {
 	TeVector2f32 s1len = s1end - s1start;
 	TeVector2f32 s2len = s2end - s2start;
 	float squarelen = s1len.getX() * s2len.getX() + s1len.getY() * s2len.getY();
@@ -309,11 +309,11 @@ static int segmentIntersection(const TeVector2f32 &s1start, const TeVector2f32 &
 		result = 1;
 		float intersection1 = -((s1len.getY() * s1start.getX() +
 						(s1len.getX() * s2start.getY() - s1len.getX() * s1start.getY())) -
-                          s1len.getY() * s2start.getX()) / squarelen;
+						 s1len.getY() * s2start.getX()) / squarelen;
 		if (intersection1 >= 0.0f && intersection1 <= 1.0f) {
 			float intersection2 = -((s2len.getY() * s2start.getY() +
 						(s2len.getX() * s1start.getX() - s2len.getX() * s2start.getX())) -
-                          s2len.getY() * s1start.getY()) / squarelen;
+						 s2len.getY() * s1start.getY()) / squarelen;
 			if (intersection2 >= 0.0f && intersection2 <= 1.0f) {
 				result = 2;
 				if (sout || fout1 || fout2) {
@@ -333,35 +333,35 @@ byte TeFreeMoveZone::hasBlockerIntersection(const TeVector2s32 &pt) {
 	const float gridOffsetX = _gridOffsetSomething.getX();
 	const float gridOffsetY = _gridOffsetSomething.getX();
 	borders[0] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX(),
-             pt._y * gridOffsetY + _someGridVec1.getY());
+							  pt._y * gridOffsetY + _someGridVec1.getY());
 	borders[1] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX() + gridOffsetX,
-             pt._y * gridOffsetY + _someGridVec1.getY());
+							  pt._y * gridOffsetY + _someGridVec1.getY());
 	borders[2] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX(),
-             pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
+							  pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
 	borders[3] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX() + gridOffsetX,
-             pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
+							  pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
 
-	for (unsigned int i = 0; i < _blockers->size(); i++) {
+	for (uint i = 0; i < _blockers->size(); i++) {
 		const TeBlocker &blocker = (*_blockers)[i];
 
 		if (blocker._s != name())
 			continue;
 
-		for (unsigned int b = 0; b < 4; b++) {
+		for (uint b = 0; b < 4; b++) {
 			int si = segmentIntersection(borders[b], borders[(b + 1) % 4], blocker._pts[0],
-                                      blocker._pts[1], nullptr, nullptr, nullptr);
+										 blocker._pts[1], nullptr, nullptr, nullptr);
 			if (si == 2)
 				return 2;
 		}
 
-        TeVector2f32 borderVec = ((borders[0] + borders[3]) / 2.0) - blocker._pts[0];
-        TeVector2f32 blockerVec = blocker._pts[1] - blocker._pts[0];
-        float dotVal = borderVec.dotProduct(blockerVec.getNormalized());
-        float crosVal = borderVec.crossProduct(blockerVec);
-        if ((crosVal < 0.0) && (0.0 <= dotVal)) {
+		TeVector2f32 borderVec = ((borders[0] + borders[3]) / 2.0) - blocker._pts[0];
+		TeVector2f32 blockerVec = blocker._pts[1] - blocker._pts[0];
+		float dotVal = borderVec.dotProduct(blockerVec.getNormalized());
+		float crosVal = borderVec.crossProduct(blockerVec);
+		if ((crosVal < 0.0) && (0.0 <= dotVal)) {
 			if (dotVal < blockerVec.length())
 				return 1;
-        }
+		}
 	}
 	return 0;
 }
@@ -372,20 +372,20 @@ bool TeFreeMoveZone::hasCellBorderIntersection(const TeVector2s32 &pt) {
 	const float gridOffsetX = _gridOffsetSomething.getX();
 	const float gridOffsetY = _gridOffsetSomething.getX();
 	borders[0] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX(),
-             pt._y * gridOffsetY + _someGridVec1.getY());
+							  pt._y * gridOffsetY + _someGridVec1.getY());
 	borders[1] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX() + gridOffsetX,
-             pt._y * gridOffsetY + _someGridVec1.getY());
+							  pt._y * gridOffsetY + _someGridVec1.getY());
 	borders[2] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX(),
-             pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
+							  pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
 	borders[3] = TeVector2f32(pt._x * gridOffsetX + _someGridVec1.getX() + gridOffsetX,
-             pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
+							  pt._y * gridOffsetY + _someGridVec1.getY() + gridOffsetY);
 
 	int iresult = 0;
-	for (unsigned int border = 0; border < _borders.size() / 2; border++) {
+	for (uint border = 0; border < _borders.size() / 2; border++) {
 		TeVector2f32 v1;
 		TeVector2f32 v2;
-		unsigned int off1 = _pickMesh[_borders[border * 2]];
-		unsigned int off2 = _pickMesh[_borders[border * 2 + 1]];
+		uint off1 = _pickMesh[_borders[border * 2]];
+		uint off2 = _pickMesh[_borders[border * 2 + 1]];
 		if (!_loadedFromBin) {
 			v1 = TeVector2f32(_transformedVerticies[off1].x(), _transformedVerticies[off1].z());
 			v2 = TeVector2f32(_transformedVerticies[off2].x(), _transformedVerticies[off2].z());
@@ -443,47 +443,44 @@ void TeFreeMoveZone::preUpdateGrid() {
 
 		_gridWorldY = newVec.y();
 	}
-	for (unsigned int i = 0; i < _pickMesh.size(); i++) {
-		  unsigned int vertNo = _pickMesh[_pickMesh[i]];
+	for (uint i = 0; i < _pickMesh.size(); i++) {
+		uint vertNo = _pickMesh[_pickMesh[i]];
 
-		  if (!_loadedFromBin)
+		if (!_loadedFromBin)
 			newVec = _transformedVerticies[vertNo];
-		  else
+		else
 			newVec = gridInverse * _freeMoveZoneVerticies[vertNo];
 
-		  if (_someGridVec1.getX() <= newVec.x()) {
-			if (_someGridVec2.getX() < newVec.x()) {
-			  _someGridVec2.setX(newVec.x());
-			}
-		  } else {
+		if (_someGridVec1.getX() <= newVec.x()) {
+			if (_someGridVec2.getX() < newVec.x())
+				_someGridVec2.setX(newVec.x());
+		} else {
 			_someGridVec1.setX(newVec.x());
-		  }
+		}
 
-		  if (_someGridVec1.getY() <= newVec.z()) {
-			if (_someGridVec2.getY() < newVec.z()) {
-			  _someGridVec2.setY(newVec.z());
-			}
-		  } else {
+		if (_someGridVec1.getY() <= newVec.z()) {
+			if (_someGridVec2.getY() < newVec.z())
+				_someGridVec2.setY(newVec.z());
+		} else {
 			_someGridVec1.setY(newVec.z());
-		  }
+		}
 
-		  if (newVec.y() < _gridWorldY) {
+		if (newVec.y() < _gridWorldY)
 			_gridWorldY = newVec.y();
-		  }
 	}
 
 	if (!_loadedFromBin) {
 		if (!name().contains("19000"))
-		  _gridOffsetSomething = TeVector2f32(5.0f, 5.0f);
+			_gridOffsetSomething = TeVector2f32(5.0f, 5.0f);
 		else
-		  _gridOffsetSomething = TeVector2f32(2.0f, 2.0f);
+			_gridOffsetSomething = TeVector2f32(2.0f, 2.0f);
 	} else {
 		const TeVector2f32 gridVecDiff = _someGridVec2 - _someGridVec1;
 		float minSide = MIN(gridVecDiff.getX(), gridVecDiff.getY()) / 20.0f;
 		_gridOffsetSomething.setX(minSide);
 		_gridOffsetSomething.setY(minSide);
 
-		error("FIXME: Finish preUpdateGrid for non-loaded-from-bin case.");
+		error("FIXME: Finish preUpdateGrid for loaded-from-bin case.");
 		/*
 		// what's this field?
 		if (_field_0x414.x != 0.0)
@@ -517,10 +514,10 @@ Common::Array<TeVector3f32> TeFreeMoveZone::removeInsignificantPoints(const Comm
 	if (points.size() > 2) {
 		int point1 = 0;
 		int point2 = 2;
-        do {
+		do {
 			const TeVector2f32 pt1(points[point1].x(), points[point1].z());
 			const TeVector2f32 pt2(points[point2].x(), points[point2].z());
-			for (unsigned int i = 0; i * 2 < _borders.size() / 2; i++) {
+			for (uint i = 0; i * 2 < _borders.size() / 2; i++) {
 				const TeVector3f32 transpt3d1 = worldTransformationMatrix() * verticies()[_borders[i * 2]];
 				const TeVector2f32 transpt1(transpt3d1.x(), transpt3d1.z());
 				const TeVector3f32 transpt3d2 = worldTransformationMatrix() * verticies()[_borders[i * 2 + 1]];
@@ -531,13 +528,13 @@ Common::Array<TeVector3f32> TeFreeMoveZone::removeInsignificantPoints(const Comm
 			point1 = point2 - 1;
 			result.push_back(points[point1]);
 			point2++;
-		} while (point2 < points.size());
+		} while (point2 < (int)points.size());
 	}
 
 	if (result.back() != points[points.size() - 2]) {
-        result.push_back(points[points.size() - 1]);
+		result.push_back(points[points.size() - 1]);
 	} else {
-        result.back() = points[points.size() - 1];
+		result.back() = points[points.size() - 1];
 	}
 	return result;
 }
@@ -557,7 +554,7 @@ void TeFreeMoveZone::setCamera(TeIntrusivePtr<TeCamera> &cam, bool noRecalcProjP
 		_projectedPointsDirty = true;
 }
 
-void TeFreeMoveZone::setNbTriangles(unsigned int len) {
+void TeFreeMoveZone::setNbTriangles(uint len) {
 	_freeMoveZoneVerticies.resize(len * 3);
 
 	_gridDirty = true;
@@ -574,7 +571,7 @@ void TeFreeMoveZone::setPathFindingOccluder(const TeOBP &occluder) {
 	_gridDirty = true;
 }
 
-void TeFreeMoveZone::setVertex(unsigned int offset, const TeVector3f32 &vertex) {
+void TeFreeMoveZone::setVertex(uint offset, const TeVector3f32 &vertex) {
 	_freeMoveZoneVerticies[offset] = vertex;
 
 	_gridDirty = true;
@@ -614,23 +611,23 @@ void TeFreeMoveZone::updateBorders() {
 	updatePickMesh();
 
 	if (_verticies.size() > 2) {
-		for (unsigned int triNo1 = 0; triNo1 < _verticies.size() / 3; triNo1++) {
-			for (unsigned int vecNo1 = 0; vecNo1 < 3; vecNo1++) {
-				unsigned int left1 = triNo1 * 3 + vecNo1;
-				unsigned int left2 = triNo1 * 3 + (vecNo1 == 2 ? 0 : vecNo1 + 1);
+		for (uint triNo1 = 0; triNo1 < _verticies.size() / 3; triNo1++) {
+			for (uint vecNo1 = 0; vecNo1 < 3; vecNo1++) {
+				uint left1 = triNo1 * 3 + vecNo1;
+				uint left2 = triNo1 * 3 + (vecNo1 == 2 ? 0 : vecNo1 + 1);
 				const TeVector3f32 vleft1 = _verticies[left1];
 				const TeVector3f32 vleft2 = _verticies[left2];
 
 				bool skip = false;
-				for (unsigned int triNo2 = 0; triNo2 < _verticies.size() / 3; triNo2++) {
+				for (uint triNo2 = 0; triNo2 < _verticies.size() / 3; triNo2++) {
 					if (skip)
 						break;
 
-					for (unsigned int vecNo2 = 0; vecNo2 < 3; vecNo2++) {
+					for (uint vecNo2 = 0; vecNo2 < 3; vecNo2++) {
 						if (triNo2 == triNo1)
 							continue;
-						unsigned int right1 = triNo2 * 3 + vecNo2;
-						unsigned int right2 = triNo2 * 3 + (vecNo2 == 2 ? 0 : vecNo2 + 1);
+						uint right1 = triNo2 * 3 + vecNo2;
+						uint right2 = triNo2 * 3 + (vecNo2 == 2 ? 0 : vecNo2 + 1);
 						TeVector3f32 vright1 = _verticies[right1];
 						TeVector3f32 vright2 = _verticies[right2];
 						if (vright1 == vleft1 && vright2 == vleft2 && vright1 == vleft2 && vright2 == vleft1) {
@@ -645,7 +642,7 @@ void TeFreeMoveZone::updateBorders() {
 				}
 			}
 		}
-    }
+	}
 	_bordersDirty = false;
 }
 
@@ -669,25 +666,25 @@ void TeFreeMoveZone::updatePickMesh() {
 	_pickMesh.clear();
 	_pickMesh.reserve(_freeMoveZoneVerticies.size());
 	int vecNo = 0;
-    for (unsigned int tri = 0; tri < _freeMoveZoneVerticies.size() / 3; tri++) {
-        _pickMesh.push_back(vecNo);
-        _pickMesh.push_back(vecNo + 1);
-        _pickMesh.push_back(vecNo + 2);
-        vecNo += 3;
-    }
+	for (uint tri = 0; tri < _freeMoveZoneVerticies.size() / 3; tri++) {
+		_pickMesh.push_back(vecNo);
+		_pickMesh.push_back(vecNo + 1);
+		_pickMesh.push_back(vecNo + 2);
+		vecNo += 3;
+}
 
-    debug("[TeFreeMoveZone::updatePickMesh] %s nb triangles reduced from : %d to : %d", name().c_str(),
-             _freeMoveZoneVerticies.size() / 3, _pickMesh.size() / 3);
+	debug("[TeFreeMoveZone::updatePickMesh] %s nb triangles reduced from : %d to : %d", name().c_str(),
+			 _freeMoveZoneVerticies.size() / 3, _pickMesh.size() / 3);
 
-    TePickMesh2::setNbTriangles(_pickMesh.size() / 3);
+	TePickMesh2::setNbTriangles(_pickMesh.size() / 3);
 
-    for (unsigned int i = 0; i < _pickMesh.size(); i++) {
-        _verticies[i] = _freeMoveZoneVerticies[_pickMesh[i]];
-    }
-    _bordersDirty = true;
-    _pickMeshDirty = false;
-    _projectedPointsDirty = true;
-    _gridDirty = true;
+	for (uint i = 0; i < _pickMesh.size(); i++) {
+		_verticies[i] = _freeMoveZoneVerticies[_pickMesh[i]];
+	}
+	_bordersDirty = true;
+	_pickMeshDirty = false;
+	_projectedPointsDirty = true;
+	_gridDirty = true;
 }
 
 void TeFreeMoveZone::updateProjectedPoints() {
@@ -703,10 +700,10 @@ void TeFreeMoveZone::updateTransformedVertices() {
 
 	const TeMatrix4x4 worldTransform = worldTransformationMatrix();
 	_transformedVerticies.resize(_freeMoveZoneVerticies.size());
-	for (unsigned int i = 0; i < _transformedVerticies.size(); i++) {
-        _transformedVerticies[i] = worldTransform * _freeMoveZoneVerticies[i];
-    }
-    _transformedVerticiesDirty = false;
+	for (uint i = 0; i < _transformedVerticies.size(); i++) {
+		_transformedVerticies[i] = worldTransform * _freeMoveZoneVerticies[i];
+	}
+	_transformedVerticiesDirty = false;
 }
 
 /*========*/
@@ -792,7 +789,7 @@ void TeFreeMoveZoneGraph::deserialize(Common::ReadStream &stream) {
 	if (flaglen > 1000000 || (int)flaglen != _size._x * _size._y)
 		error("TeFreeMoveZoneGraph: Flags unexpected size, expect %d got %d", _size._x * _size._y, flaglen);
 	_flags.resize(flaglen);
-	for (unsigned int i = 0; i < flaglen; i++) {
+	for (uint i = 0; i < flaglen; i++) {
 		_flags[i] = stream.readByte();
 	}
 	_bordersDistance = stream.readFloatLE();
@@ -811,14 +808,14 @@ TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, c
 		return nullptr;
 	float closestDist = camera->orthoFarPlane();
 	Math::Ray camRay;
-	for (unsigned int i = 0; i < pickMeshes.size(); i++) {
+	for (uint i = 0; i < pickMeshes.size(); i++) {
 		TePickMesh2 *mesh = pickMeshes[i];
 		const TeMatrix4x4 meshWorldTransform = mesh->worldTransformationMatrix();
 		if (lastHitFirst) {
 			// Note: it seems like a bug in the original.. this never sets
 			// the ray parameters?? It should still find the right triangle below.
-			unsigned int tricount = mesh->verticies().size() / 3;
-			unsigned int vert = mesh->lastTriangleHit() * 3;
+			uint tricount = mesh->verticies().size() / 3;
+			uint vert = mesh->lastTriangleHit() * 3;
 			if (mesh->lastTriangleHit() >= tricount)
 				vert = 0;
 			const TeVector3f32 v1 = meshWorldTransform * mesh->verticies()[vert + 0];
@@ -830,7 +827,7 @@ TePickMesh2 *TeFreeMoveZone::findNearestMesh(TeIntrusivePtr<TeCamera> &camera, c
 			if (intResult && intersectDist < closestDist && intersectDist >= camera->orthoNearPlane())
 				return mesh;
 		}
-		for (unsigned int tri = 0; tri < mesh->verticies().size() / 3; tri++) {
+		for (uint tri = 0; tri < mesh->verticies().size() / 3; tri++) {
 			const TeVector3f32 v1 = meshWorldTransform * mesh->verticies()[tri * 3 + 0];
 			const TeVector3f32 v2 = meshWorldTransform * mesh->verticies()[tri * 3 + 1];
 			const TeVector3f32 v3 = meshWorldTransform * mesh->verticies()[tri * 3 + 2];
diff --git a/engines/tetraedge/te/te_free_move_zone.h b/engines/tetraedge/te/te_free_move_zone.h
index ec8cde052e4..45a96d1aeea 100644
--- a/engines/tetraedge/te/te_free_move_zone.h
+++ b/engines/tetraedge/te/te_free_move_zone.h
@@ -94,9 +94,9 @@ public:
 	Common::Array<TeVector3f32> removeInsignificantPoints(const Common::Array<TeVector3f32> &points);
 	void setBordersDistance(float dist);
 	void setCamera(TeIntrusivePtr<TeCamera> &cam, bool noRecalcProjPoints);
-	void setNbTriangles(unsigned int len);
+	void setNbTriangles(uint len);
 	void setPathFindingOccluder(const TeOBP &occluder);
-	void setVertex(unsigned int offset, const TeVector3f32 &vertex);
+	void setVertex(uint offset, const TeVector3f32 &vertex);
 	TeVector3f32 transformAStarGridInWorldSpace(const TeVector2s32 &gridpt);
 	float transformHeightMin(float minval);
 	TeVector3f32 transformVectorInWorldSpace(float param_3, float param_4);
@@ -122,9 +122,9 @@ private:
 	Common::Array<TeRectBlocker> *_rectBlockers;
 
 	Common::Array<TeVector3f32> _freeMoveZoneVerticies;
-	Common::Array<unsigned int> _pickMesh;
+	Common::Array<uint> _pickMesh;
 	Common::Array<TeVector3f32> _transformedVerticies;
-	Common::Array<unsigned int> _borders;
+	Common::Array<uint> _borders;
 
 	// TODO: Find better names..
 	TeVector2f32 _gridOffsetSomething;
diff --git a/engines/tetraedge/te/te_images_sequence.h b/engines/tetraedge/te/te_images_sequence.h
index fa0a5bdcb96..62ac59f00bc 100644
--- a/engines/tetraedge/te/te_images_sequence.h
+++ b/engines/tetraedge/te/te_images_sequence.h
@@ -61,11 +61,11 @@ public:
 
 private:
 	float _frameRate;
-	unsigned int _width;
-	unsigned int _height;
+	uint _width;
+	uint _height;
 	Common::Array<Common::FSNode> _files;
 	Common::Array<Graphics::ManagedSurface *> _cachedSurfaces;
-	unsigned int _curFrame;
+	uint _curFrame;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_layout.cpp b/engines/tetraedge/te/te_layout.cpp
index 3fde8410e1b..7578a111caf 100644
--- a/engines/tetraedge/te/te_layout.cpp
+++ b/engines/tetraedge/te/te_layout.cpp
@@ -244,9 +244,9 @@ void TeLayout::setPositionType(CoordinatesType newtype) {
 
 void TeLayout::setRatio(float val) {
 	if (_ratio != val) {
-	  _ratio = val;
-	  _sizeChanged = true;
-	  _worldMatrixChanged = true;
+		_ratio = val;
+		_sizeChanged = true;
+		_worldMatrixChanged = true;
 	}
 }
 
@@ -300,9 +300,9 @@ void TeLayout::setSizeType(CoordinatesType coordtype) {
 
 void TeLayout::setZPosition(float zpos) {
 	if (_userPosition.z() != zpos) {
-	  _userPosition.z() = zpos;
-	  _positionChanged = true;
-	  _worldMatrixChanged = true;
+		_userPosition.z() = zpos;
+		_positionChanged = true;
+		_worldMatrixChanged = true;
 	}
 }
 
diff --git a/engines/tetraedge/te/te_lua_context.cpp b/engines/tetraedge/te/te_lua_context.cpp
index c7a928cfe12..240a244bcc5 100644
--- a/engines/tetraedge/te/te_lua_context.cpp
+++ b/engines/tetraedge/te/te_lua_context.cpp
@@ -149,7 +149,7 @@ Common::Error TeLuaContext::syncState(Common::Serializer &s) {
 				lua_settop(_luaState, -2);
 				break;
 			}
-			unsigned int vtype = lua_type(_luaState, -1);
+			uint vtype = lua_type(_luaState, -1);
 			Common::String name = lua_tolstring(_luaState, -2, nullptr);
 			if (vtype == LUA_TBOOLEAN) {
 				TeLuaSaveVarType stype = Boolean;
diff --git a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
index c76e1316041..f376da34ee0 100644
--- a/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
+++ b/engines/tetraedge/te/te_lua_gui_lua_callbacks.cpp
@@ -132,21 +132,21 @@ static TeVector3f32 TeLuaToTeVector3f32(lua_State *L, int index, TeVector3f32 de
 		index--;
 		lua_gettable(L, index);
 		if (lua_isnumber(L, -1)) {
-		  retval.x() = TeLuaToF32(L, -1);
+			retval.x() = TeLuaToF32(L, -1);
 		}
 
 		lua_settop(L, -2);
 		lua_pushinteger(L, 2);
 		lua_gettable(L, index);
 		if (lua_isnumber(L, -1)) {
-		  retval.y() = TeLuaToF32(L, -1);
+			retval.y() = TeLuaToF32(L, -1);
 		}
 
 		lua_settop(L, -2);
 		lua_pushinteger(L, 3);
 		lua_gettable(L, index);
 		if (lua_isnumber(L, -1)) {
-		  retval.z() = TeLuaToF32(L, -1);
+			retval.z() = TeLuaToF32(L, -1);
 		}
 		lua_settop(L, -2);
 	}
@@ -166,7 +166,7 @@ static Common::Array<float> TeLuaToFloatArray(lua_State *L, int index) {
 			lua_settop(L, -2);
 		}
 	} else {
-	  warning("TeLuaToF32TeArray:: the lua value is not a table");
+		warning("TeLuaToF32TeArray:: the lua value is not a table");
 	}
 	return result;
 }
diff --git a/engines/tetraedge/te/te_lua_thread.cpp b/engines/tetraedge/te/te_lua_thread.cpp
index 9b32db5908a..4c1a8368f88 100644
--- a/engines/tetraedge/te/te_lua_thread.cpp
+++ b/engines/tetraedge/te/te_lua_thread.cpp
@@ -42,7 +42,7 @@ TeLuaThread::TeLuaThread(TeLuaContext *context) : _resumeCount(0), _lastResumeRe
 
 TeLuaThread::~TeLuaThread() {
 	luaL_unref(_luaThread, LUA_REGISTRYINDEX, _bottomRef);
-	unsigned int i;
+	uint i;
 	for (i = 0; i < _threadList.size(); i++)
 		if (_threadList[i] == this)
 			break;
diff --git a/engines/tetraedge/te/te_matrix4x4.cpp b/engines/tetraedge/te/te_matrix4x4.cpp
index c9fe992bb65..afaadaa1324 100644
--- a/engines/tetraedge/te/te_matrix4x4.cpp
+++ b/engines/tetraedge/te/te_matrix4x4.cpp
@@ -151,12 +151,12 @@ TeVector3f32 TeMatrix4x4::operator*(const TeVector3f32 &mul) const {
 	const float *d = getData();
 	float w = d[3] * x + d[7] * y + d[11] * z + d[15];
 	if (w == 0.0)
-	  w = 1e-09f;
+		w = 1e-09f;
 
 	return TeVector3f32
-			  ((d[0] * x + d[4] * y + d[8] *  z + d[12]) / w,
-			   (d[1] * x + d[5] * y + d[9] *  z + d[13]) / w,
-			   (d[2] * x + d[6] * y + d[10] * z + d[14]) / w);
+			((d[0] * x + d[4] * y + d[8] *  z + d[12]) / w,
+			 (d[1] * x + d[5] * y + d[9] *  z + d[13]) / w,
+			 (d[2] * x + d[6] * y + d[10] * z + d[14]) / w);
 }
 
 
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index c368fc5501b..ccc439470a0 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -102,7 +102,7 @@ void TeMesh::draw() {
 		} else {
 			assert(_faceCounts.size() == _materials.size());
 			int totalFaceCount = 0;
-			for (unsigned int i = 0; i < _faceCounts.size(); i++) {
+			for (uint i = 0; i < _faceCounts.size(); i++) {
 				if (!_faceCounts[i])
 					continue;
 				if (hasAlpha(i)) {
@@ -155,7 +155,7 @@ void TeMesh::draw() {
 	} else {
 		int totalFaceCount = 0;
 		assert(_faceCounts.size() == _materials.size());
-		for (unsigned int i = 0; i < _materials.size(); i++) {
+		for (uint i = 0; i < _materials.size(); i++) {
 			if (!_faceCounts[i])
 				continue;
 			if (!hasAlpha(i) || renderer->shadowMode() == TeRenderer::ShadowMode1 || !_shouldDraw) {
@@ -182,7 +182,7 @@ void TeMesh::draw() {
 		TeLight::disableAll();
 		glBegin(GL_LINES);
 		renderer->setCurrentColor(TeColor(255, 255, 255, 255));
-		for (unsigned int i = 0; i < verticies.size(); i++) {
+		for (uint i = 0; i < verticies.size(); i++) {
 			glVertex3f(verticies[i].x(), verticies[i].y(), verticies[i].z());
 			glVertex3f(verticies[i].x() + normals[i].x(),
 					verticies[i].y() + normals[i].y(),
@@ -261,7 +261,7 @@ void TeMesh::setColor(const TeColor &col) {
 		if (colnow.a() != 255)
 			_hasAlpha = true;
 
-		for (unsigned int i = 0; i < _verticies.size(); i++) {
+		for (uint i = 0; i < _verticies.size(); i++) {
 			_colors[i] = colnow;
 		}
 	}
@@ -275,7 +275,7 @@ void TeMesh::setColor(uint idx, const TeColor &col) {
 	_colors[idx] = col;
 }
 
-void TeMesh::setConf(unsigned long vertexCount, unsigned long indexCount, enum Mode mode, unsigned int materialCount, unsigned int materialIndexCount) {
+void TeMesh::setConf(unsigned long vertexCount, unsigned long indexCount, enum Mode mode, uint materialCount, uint materialIndexCount) {
 	destroy();
 	_initialMaterialIndexCount = materialIndexCount;
 	_verticies.resize(vertexCount);
@@ -309,21 +309,21 @@ void TeMesh::setConf(unsigned long vertexCount, unsigned long indexCount, enum M
 	}
 }
 
-void TeMesh::setIndex(unsigned int idx, unsigned int val) {
+void TeMesh::setIndex(uint idx, uint val) {
 	_indexes[idx] = val;
 }
 
-void TeMesh::setNormal(unsigned int idx, const TeVector3f32 &val) {
+void TeMesh::setNormal(uint idx, const TeVector3f32 &val) {
 	_normals.resize(_verticies.size());
 	_normals[idx] = val;
 }
 
-void TeMesh::setTextureUV(unsigned int idx, const TeVector2f32 &val) {
+void TeMesh::setTextureUV(uint idx, const TeVector2f32 &val) {
 	_uvs.resize(_verticies.size());
 	_uvs[idx] = val;
 }
 
-void TeMesh::setVertex(unsigned int idx, const TeVector3f32 &val) {
+void TeMesh::setVertex(uint idx, const TeVector3f32 &val) {
 	_verticies[idx] = val;
 }
 
@@ -375,10 +375,10 @@ void TeMesh::update(TeIntrusivePtr<TeModelVertexAnimation> vertexanim) {
 
 	const Common::Array<TeVector3f32> &animverts = vertexanim->getVertices();
 	assert(animverts.size() >= _verticies.size());
-	for (unsigned int i = 0; i < _verticies.size(); i++) {
+	for (uint i = 0; i < _verticies.size(); i++) {
 		_updatedVerticies[i] = animverts[i];
 	}
-	for (unsigned int i = 0; i < _normals.size(); i++) {
+	for (uint i = 0; i < _normals.size(); i++) {
 		_updatedNormals[i] = _normals[i];
 	}
 }
@@ -386,8 +386,8 @@ void TeMesh::update(TeIntrusivePtr<TeModelVertexAnimation> vertexanim) {
 void TeMesh::updateTo(const Common::Array<TeMatrix4x4> *matricies1, const Common::Array<TeMatrix4x4> *matricies2,
 				Common::Array<TeVector3f32> &verts, Common::Array<TeVector3f32> &normals) {
 	static const TeMatrix4x4 emptyMatrix;
-	for (unsigned int i = 0; i < _verticies.size(); i++) {
-		unsigned int m = _matricies[i];
+	for (uint i = 0; i < _verticies.size(); i++) {
+		uint m = _matricies[i];
 		const TeMatrix4x4 *mat;
 		if (m < matricies1->size()) {
 			mat = &((*matricies1)[m]);
diff --git a/engines/tetraedge/te/te_mesh.h b/engines/tetraedge/te/te_mesh.h
index 1399d88f858..bc71f399d7a 100644
--- a/engines/tetraedge/te/te_mesh.h
+++ b/engines/tetraedge/te/te_mesh.h
@@ -90,11 +90,11 @@ public:
 
 	void setColor(const TeColor &col) override;
 	void setColor(uint idx, const TeColor &col);
-	void setConf(unsigned long vertexCount, unsigned long indexCount, enum Mode mode, unsigned int materialCount, unsigned int materialIndexCount);
-	void setIndex(unsigned int idx, unsigned int val);
-	void setNormal(unsigned int idx, const TeVector3f32 &val);
-	void setTextureUV(unsigned int idx, const TeVector2f32 &val);
-	void setVertex(unsigned int idx, const TeVector3f32 &val);
+	void setConf(unsigned long vertexCount, unsigned long indexCount, enum Mode mode, uint materialCount, uint materialIndexCount);
+	void setIndex(uint idx, uint val);
+	void setNormal(uint idx, const TeVector3f32 &val);
+	void setTextureUV(uint idx, const TeVector2f32 &val);
+	void setVertex(uint idx, const TeVector3f32 &val);
 	void sortFaces();
 
 	void update(const Common::Array<TeMatrix4x4> *matricies1, const Common::Array<TeMatrix4x4> *matricies2);
@@ -115,11 +115,11 @@ public:
 	void setHasAlpha(bool val) { _hasAlpha = val; }
 
 	Common::Array<TeMaterial> &materials() { return _materials; }
-	void setUpdatedVertex(unsigned int idx, const TeVector3f32 &val) { _updatedVerticies[idx] = val; }
-	void setUpdatedNormal(unsigned int idx, const TeVector3f32 &val) { _updatedNormals[idx] = val; }
+	void setUpdatedVertex(uint idx, const TeVector3f32 &val) { _updatedVerticies[idx] = val; }
+	void setUpdatedNormal(uint idx, const TeVector3f32 &val) { _updatedNormals[idx] = val; }
 
-	const TeVector3f32 &preUpdatedVertex(unsigned int idx) const { return _verticies[idx]; }
-	const TeVector3f32 &preUpdatedNormal(unsigned int idx) const { return _normals[idx]; }
+	const TeVector3f32 &preUpdatedVertex(uint idx) const { return _verticies[idx]; }
+	const TeVector3f32 &preUpdatedNormal(uint idx) const { return _normals[idx]; }
 
 private:
 	Common::Array<unsigned char> _materialIndexes;
@@ -134,7 +134,7 @@ private:
 	Common::Array<TeColor> _colors;
 	Common::Array<TeMaterial> _materials;
 
-	unsigned int _glMeshMode;
+	uint _glMeshMode;
 
 	bool _matrixForced;
 	TeMatrix4x4 _forcedMatrix;
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index f2c1460302f..2ae90852127 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -128,7 +128,7 @@ void TeModel::forceMatrix(const TeMatrix4x4 &matrix) {
 	_forcedMatrix = matrix;
 }
 
-TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num) {
+TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, uint num) {
 	if (anim) {
 		int bone = anim->findBone(_bones[num]._name);
 		if (bone != -1)
@@ -137,10 +137,10 @@ TeTRS TeModel::getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num)
 	return _bones[num]._trs;
 }
 
-TeMatrix4x4 TeModel::lerpElementsMatrix(unsigned int weightsNum, const Common::Array<TeMatrix4x4> &matricies) {
+TeMatrix4x4 TeModel::lerpElementsMatrix(uint weightsNum, const Common::Array<TeMatrix4x4> &matricies) {
 	TeMatrix4x4 retval;
 	// Start with a 0 matrix.
-	for (unsigned int i = 0; i < 4; i++)
+	for (uint i = 0; i < 4; i++)
 		retval.setValue(i, i, 0);
 
 	const Common::Array<weightElement> &weights = _weightElements[weightsNum];
@@ -173,7 +173,7 @@ void TeModel::update() {
 	if (_bones.size()) {
 		Common::Array<TeMatrix4x4> matricies;
 		matricies.resize(_bones.size());
-		for (unsigned int i = 0; i < _bones.size(); i++) {
+		for (uint i = 0; i < _bones.size(); i++) {
 			const bone &b = _bones[i];
 			const TeMatrix4x4 matrix = TeMatrix4x4::fromTRS(b._trs);
 			if (b._parentBone == -1 || _bones.size() < 2) {
@@ -188,9 +188,9 @@ void TeModel::update() {
 
 		TeMatrix4x4 invertx;
 		invertx.scale(TeVector3f32(-1, 1, 1));
-		for (unsigned int b = 0; b < _bones.size(); b++) {
+		for (uint b = 0; b < _bones.size(); b++) {
 			TeTRS trs = getBone(_modelAnim, b);
-			for (unsigned int i = 0; i < _boneBlenders.size(); i++) {
+			for (uint i = 0; i < _boneBlenders.size(); i++) {
 				BonesBlender *blender = _boneBlenders[i];
 				float complete = blender->coef();
 				TeTRS endTRS = getBone(blender->_anim, b);
@@ -224,18 +224,18 @@ void TeModel::update() {
 		}
 
 		if (!_skinOffsets.empty() && !_bones.empty()) {
-			for (unsigned int b = 0; b < _bones.size(); b++) {
+			for (uint b = 0; b < _bones.size(); b++) {
 				_boneMatricies[b] = _boneMatricies[b] * _skinOffsets[b];
 			}
 		}
 
 		if (!_skipSkinOffsets && !_weightElements.empty()) {
-			for (unsigned int i = 0; i < _weightElements.size(); i++) {
+			for (uint i = 0; i < _weightElements.size(); i++) {
 				_lerpedElements[i] = lerpElementsMatrix(i, _boneMatricies);
 			}
 		}
 
-		for (unsigned int m = 0; m < _meshes.size(); m++) {
+		for (uint m = 0; m < _meshes.size(); m++) {
 			TeMesh &mesh = _meshes[m];
 			if (!mesh.visible())
 				continue;
@@ -252,7 +252,7 @@ void TeModel::update() {
 				if (_modelVertexAnim && mesh.name() == _modelVertexAnim->head())
 					verticies = &_modelVertexAnim->getVertices();
 
-				for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
+				for (uint i = 0; i < mesh.numVerticies(); i++) {
 					TeVector3f32 vertex;
 					if (!verticies) {
 						vertex = mesh.preUpdatedVertex(i);
@@ -273,7 +273,7 @@ void TeModel::update() {
 						updatednormal = _boneMatricies[idx] * normal;
 					} else {
 						idx -= _bones.size();
-						for (unsigned int w = 0; w < _weightElements[idx].size(); w++) {
+						for (uint w = 0; w < _weightElements[idx].size(); w++) {
 							const TeMatrix4x4 &wmatrix = _boneMatricies[_weightElements[idx][w]._x];
 							float weight = _weightElements[idx][w]._weight;
 							updatedvertex = updatedvertex + ((wmatrix * vertex) * weight);
@@ -374,7 +374,7 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 		error("[TeModel::load] Unable to find skeleton.");
 	}
 
-	for (unsigned int i = 0; i < _bones.size(); i++) {
+	for (uint i = 0; i < _bones.size(); i++) {
 		_bones[i]._name = Te3DObject2::deserializeString(stream);
 		loadAlign(stream);
 		_bones[i]._parentBone = stream.readUint32LE();
@@ -384,7 +384,7 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 		}
 	}
 
-	for (unsigned int m = 0; m < _meshes.size(); m++) {
+	for (uint m = 0; m < _meshes.size(); m++) {
 		if (!loadMesh(stream, _meshes[m])) {
 			error("[TeModel::load] Error on meshes loading.");
 		}
@@ -393,7 +393,7 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 	if (!loadAndCheckFourCC(stream, "WEIG")) {
 		error("[TeModel::load] Unable to load weight.");
 	}
-	for (unsigned int i = 0; i < _weightElements.size(); i++) {
+	for (uint i = 0; i < _weightElements.size(); i++) {
 		loadWeights(stream, _weightElements[i]);
 	}
 
@@ -444,7 +444,7 @@ bool TeModel::loadWeights(Common::ReadStream &stream, Common::Array<weightElemen
 	if (nweights > 100000)
 		error("Improbable number of weights %d", (int)nweights);
 	weights.resize(nweights);
-	for (unsigned int i = 0; i < nweights; i++) {
+	for (uint i = 0; i < nweights; i++) {
 		weights[i]._weight = stream.readFloatLE();
 		weights[i]._x = stream.readUint16LE();
 		stream.readUint16LE();
@@ -478,7 +478,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	if (!loadAndCheckFourCC(stream, "MTRL"))
 		return false;
 
-	for (unsigned int i = 0; i < mesh.materials().size(); i++) {
+	for (uint i = 0; i < mesh.materials().size(); i++) {
 		TeMaterial mat;
 		TeMaterial::deserialize(stream, mat, _texturePath);
 		if (_enableLights)
@@ -489,7 +489,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	if (!loadAndCheckFourCC(stream, "VERT"))
 		return false;
 
-	for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
+	for (uint i = 0; i < mesh.numVerticies(); i++) {
 		TeVector3f32 v;
 		TeVector3f32::deserialize(stream, v);
 		mesh.setVertex(i, v);
@@ -497,7 +497,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	if (mesh.hasUvs()) {
 		if (!loadAndCheckFourCC(stream, "TUVS"))
 			return false;
-		for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
+		for (uint i = 0; i < mesh.numVerticies(); i++) {
 			TeVector2f32 v;
 			TeVector2f32::deserialize(stream, v);
 			mesh.setTextureUV(i, v);
@@ -507,7 +507,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	if (!loadAndCheckFourCC(stream, "NORM"))
 		return false;
 
-	for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
+	for (uint i = 0; i < mesh.numVerticies(); i++) {
 		TeVector3f32 v;
 		TeVector3f32::deserialize(stream, v);
 		mesh.setNormal(i, v);
@@ -517,7 +517,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 		if (!loadAndCheckFourCC(stream, "COLS"))
 			return false;
 
-		for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
+		for (uint i = 0; i < mesh.numVerticies(); i++) {
 			TeColor c;
 			c.deserialize(stream);
 			mesh.setColor(i, c);
@@ -527,7 +527,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	if (!loadAndCheckFourCC(stream, "FCPM"))
 		return false;
 
-	for (unsigned int i = 0; i < mesh.materials().size(); i++) {
+	for (uint i = 0; i < mesh.materials().size(); i++) {
 		mesh.facesPerMaterial(i, stream.readUint16LE());
 	}
 
@@ -535,7 +535,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	if (!loadAndCheckFourCC(stream, "MTXI"))
 		return false;
 
-	for (unsigned int i = 0; i < mesh.numVerticies(); i++) {
+	for (uint i = 0; i < mesh.numVerticies(); i++) {
 		mesh.matrixIndex(i, stream.readUint16LE());
 	}
 
@@ -543,7 +543,7 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 	if (!loadAndCheckFourCC(stream, "IDXS"))
 		return false;
 
-	for (unsigned int i = 0; i < mesh.numIndexes(); i++) {
+	for (uint i = 0; i < mesh.numIndexes(); i++) {
 		mesh.setIndex(i, stream.readUint16LE());
 	}
 
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index ef9cf6da215..60862309f79 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -96,7 +96,7 @@ public:
 	int findModelBone(const Common::String &bname);
 	int findOrAddWeights(const Common::Array<weightElement> &weights);
 	void forceMatrix(const TeMatrix4x4 &matrix);
-	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, unsigned int num);
+	TeTRS getBone(TeIntrusivePtr<TeModelAnimation> anim, uint num);
 
 	/* Align the stream to the nearest 4 byte boudary*/
 	static void loadAlign(Common::SeekableReadStream &stream);
@@ -113,7 +113,7 @@ public:
 
 	void saveBone(Common::SeekableWriteStream &stream, unsigned long boneno);
 	void saveMesh(Common::SeekableWriteStream &stream, const TeMesh &mesh);
-	void saveModel(Common::SeekableWriteStream &stream, unsigned int num);
+	void saveModel(Common::SeekableWriteStream &stream, uint num);
 	void saveWeights(Common::SeekableWriteStream &stream, const Common::Array<weightElement> weights);
 
 	void setAnim(TeIntrusivePtr<TeModelAnimation> &anim, bool repeat);
@@ -135,7 +135,7 @@ public:
 	void setTexturePath(const Common::Path &path) { _texturePath = path; }
 
 protected:
-	TeMatrix4x4 lerpElementsMatrix(unsigned int weightNum, const Common::Array<TeMatrix4x4> &matricies);
+	TeMatrix4x4 lerpElementsMatrix(uint weightNum, const Common::Array<TeMatrix4x4> &matricies);
 	void optimize();
 
 	Common::Path _texturePath;
diff --git a/engines/tetraedge/te/te_model_animation.cpp b/engines/tetraedge/te/te_model_animation.cpp
index a5c28c8f329..7ab1f8bbc4a 100644
--- a/engines/tetraedge/te/te_model_animation.cpp
+++ b/engines/tetraedge/te/te_model_animation.cpp
@@ -80,7 +80,7 @@ void TeModelAnimation::destroy() {
 }
 
 int TeModelAnimation::findBone(const Common::String &bname) {
-	for (unsigned int i = 0; i < _boneNames.size(); i++) {
+	for (uint i = 0; i < _boneNames.size(); i++) {
 		if (_boneNames[i] == bname)
 			return i;
 	}
@@ -99,7 +99,7 @@ TeQuaternion TeModelAnimation::getNMORotation(unsigned long boneNo, float amount
 	if (boneNo < _nmoRotArrays.size()) {
 		const Common::Array<NMORotation> &arr = _nmoRotArrays[boneNo];
 		if (arr.size()) {
-			unsigned int i = 0;
+			uint i = 0;
 			while (i < arr.size() && arr[i]._f < amount)
 				i++;
 
@@ -123,7 +123,7 @@ TeVector3f32 TeModelAnimation::getNMOTranslation(unsigned long boneNo, float amo
 	if (boneNo < _nmoTransArrays.size()) {
 		const Common::Array<NMOTranslation> &arr = _nmoTransArrays[boneNo];
 		if (arr.size()) {
-			unsigned int i = 0;
+			uint i = 0;
 			while (i < arr.size() && arr[i]._f < amount)
 				i++;
 
@@ -143,13 +143,13 @@ TeVector3f32 TeModelAnimation::getNMOTranslation(unsigned long boneNo, float amo
 	return TeVector3f32(0, 0, 0);
 }
 
-//TeTRS TeModelAnimation::getTRS(const Common::String &boneName, unsigned long frame, bool param_5);
+//TeTRS TeModelAnimation::getTRS(const Common::String &boneName, unsigned long frame, bool forceUseFbx);
 
 TeTRS TeModelAnimation::getTRS(unsigned long boneNo, unsigned long frame, bool forceUseFbx) const {
 	TeTRS retval;
 
 	if (!_useNMOArrays || forceUseFbx) {
-		unsigned int nframes = 0;
+		uint nframes = 0;
 		if (!_useNMOArrays) {
 			nframes = _fbxArrays[0].size();
 		} else {
@@ -230,7 +230,7 @@ bool TeModelAnimation::load(Common::SeekableReadStream &stream) {
 	}
 
 	_speed = stream.readFloatLE();
-	for (unsigned int i = 0; i < numBones; i++) {
+	for (uint i = 0; i < numBones; i++) {
 		if (!Te3DObject2::loadAndCheckFourCC(stream, "BONE"))
 			return false;
 		const Common::String boneName = Te3DObject2::deserializeString(stream);
@@ -241,7 +241,7 @@ bool TeModelAnimation::load(Common::SeekableReadStream &stream) {
 		uint32 numTrans = stream.readUint32LE();
 		if (numTrans > 100000)
 			error("TeModelAnimation::load: Improbable number of bone translations %d", numTrans);
-		for (unsigned int j = 0; j < numTrans; j++) {
+		for (uint j = 0; j < numTrans; j++) {
 			float f = stream.readFloatLE();
 			TeVector3f32 trans;
 			TeVector3f32::deserialize(stream, trans);
@@ -252,7 +252,7 @@ bool TeModelAnimation::load(Common::SeekableReadStream &stream) {
 		uint32 numRots = stream.readUint32LE();
 		if (numRots > 100000)
 			error("TeModelAnimation::load: Improbable number of bone rotations %d", numRots);
-		for (unsigned int j = 0; j < numRots; j++) {
+		for (uint j = 0; j < numRots; j++) {
 			float f = stream.readFloatLE();
 			TeQuaternion rot;
 			TeQuaternion::deserialize(stream, rot);
diff --git a/engines/tetraedge/te/te_model_animation.h b/engines/tetraedge/te/te_model_animation.h
index 4604c818c76..5eb540f4362 100644
--- a/engines/tetraedge/te/te_model_animation.h
+++ b/engines/tetraedge/te/te_model_animation.h
@@ -67,10 +67,10 @@ public:
 	int findBone(const Common::String &bname);
 	int firstFrame() const;
 	TeMatrix4x4 getMatrix(const Common::String &name, unsigned long frame, bool param_5);
-	TeQuaternion getNMORotation(unsigned long param_3, float param_4) const;
-	TeVector3f32 getNMOTranslation(unsigned long param_3, float param_4) const;
-	TeTRS getTRS(const Common::String &boneName, unsigned long frame, bool param_5);
-	TeTRS getTRS(unsigned long boneNo, unsigned long frame, bool param_5) const;
+	TeQuaternion getNMORotation(unsigned long boneNo, float amount) const;
+	TeVector3f32 getNMOTranslation(unsigned long boneNo, float amount) const;
+	TeTRS getTRS(const Common::String &boneName, unsigned long frame, bool forceUseFbx);
+	TeTRS getTRS(unsigned long boneNo, unsigned long frame, bool forceUseFbx) const;
 	int lastFrame() const;
 	bool load(const Common::Path &path);
 	bool load(Common::SeekableReadStream &stream);
@@ -79,7 +79,7 @@ public:
 	void resizeFBXArrays(unsigned long len);
 	void resizeNMOArrays(unsigned long len);
 	void save(Common::SeekableWriteStream &stream);
-	void saveBone(Common::SeekableWriteStream &stream, uint param_2);
+	void saveBone(Common::SeekableWriteStream &stream, uint boneNo);
 	void setBoneName(uint boneNo, const Common::String &bname);
 	void setFrameLimits(int framemin, int framemax) {
 		_firstFrame = framemin;
@@ -93,13 +93,12 @@ public:
 
 	int curFrame2() const { return _curFrame2; }
 	float speed() const { return _speed; }
+	const Common::Path &loadedPath() const { return _loadedPath; }
 
+private:
 	TeIntrusivePtr<TeModel> _model;
-	int _firstFrame;
-	int _lastFrame;
 	Common::Path _loadedPath;
 
-private:
 	Common::Array<Common::Array<TeTRS>> _fbxArrays;
 	Common::Array<Common::Array<NMOTranslation>> _nmoTransArrays;
 	Common::Array<Common::Array<NMORotation>> _nmoRotArrays;
@@ -107,11 +106,16 @@ private:
 	Common::Array<Common::String> _boneNames;
 	int _curFrame;
 	int _curFrame2;
-	bool _curFrameValFresh;
+	int _firstFrame;
+	int _lastFrame;
 	int _repeatNum;
+
+	bool _curFrameValFresh;
 	bool _finishedSignalPending;
+
 	int _useNMOArrays;
 	int _numNMOFrames;
+
 	float _speed;
 
 };
diff --git a/engines/tetraedge/te/te_model_vertex_animation.cpp b/engines/tetraedge/te/te_model_vertex_animation.cpp
index 17479130bdc..cd71198e331 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.cpp
+++ b/engines/tetraedge/te/te_model_vertex_animation.cpp
@@ -37,7 +37,7 @@ void TeModelVertexAnimation::destroy() {
 	_keydata.clear();
 }
 
-TeVector3f32 TeModelVertexAnimation::getKeyVertex(unsigned long keyno, unsigned int vertexno) {
+TeVector3f32 TeModelVertexAnimation::getKeyVertex(unsigned long keyno, uint vertexno) {
 	assert(keyno < _keydata.size());
 	const KeyData &data = _keydata[keyno];
 	assert(vertexno < data._vectors.size());
@@ -57,7 +57,7 @@ const Common::Array<TeVector3f32> &TeModelVertexAnimation::getVertices() {
 		return lerpVtx;
 
 	float frame = fmod((_lastMillis / 1000.0) * 30, _keydata[_keydata.size() - 1]._frame);
-	unsigned int keyno = 0;
+	uint keyno = 0;
 	while (keyno < _keydata.size() - 1 && _keydata[keyno]._frame < frame)
 		keyno++;
 
@@ -66,7 +66,7 @@ const Common::Array<TeVector3f32> &TeModelVertexAnimation::getVertices() {
 	float nextFrame = _keydata[keyno + 1]._frame;
 	float interp = (frame - nextFrame) / (nextFrame - prevFrame);
 
-	for (unsigned int i = 0; i < _keydata[0]._vectors.size(); i++) {
+	for (uint i = 0; i < _keydata[0]._vectors.size(); i++) {
 		const TeVector3f32 prevVector = getKeyVertex(keyno, i);
 		const TeVector3f32 nextVector = getKeyVertex(keyno + 1, i);
 		lerpVtx[i] = prevVector * (1.0 - interp) + nextVector * interp;
diff --git a/engines/tetraedge/te/te_model_vertex_animation.h b/engines/tetraedge/te/te_model_vertex_animation.h
index 71e29eaccdd..87b30a1e38d 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.h
+++ b/engines/tetraedge/te/te_model_vertex_animation.h
@@ -56,7 +56,7 @@ public:
 	void destroy();
 
 	const Common::String &head() const { return _head; }
-	TeVector3f32 getKeyVertex(unsigned long keyno, unsigned int vertexno);
+	TeVector3f32 getKeyVertex(unsigned long keyno, uint vertexno);
 	const Common::Array<TeVector3f32> &getVertices();
 
 	bool load(Common::ReadStream &stream);
diff --git a/engines/tetraedge/te/te_music.cpp b/engines/tetraedge/te/te_music.cpp
index b3fbd96f681..59d93cce780 100644
--- a/engines/tetraedge/te/te_music.cpp
+++ b/engines/tetraedge/te/te_music.cpp
@@ -39,7 +39,7 @@ _retain(false) {
 TeMusic::~TeMusic() {
 	close();
 	Common::Array<TeMusic *> &m = g_engine->getSoundManager()->musics();
-	for (unsigned int i = 0; i < m.size(); i++) {
+	for (uint i = 0; i < m.size(); i++) {
 		if (m[i] == this) {
 			m.remove_at(i);
 			break;
diff --git a/engines/tetraedge/te/te_name_val_xml_parser.cpp b/engines/tetraedge/te/te_name_val_xml_parser.cpp
index c5a4d96f0be..748dec4548b 100644
--- a/engines/tetraedge/te/te_name_val_xml_parser.cpp
+++ b/engines/tetraedge/te/te_name_val_xml_parser.cpp
@@ -29,7 +29,7 @@ bool TeNameValXmlParser::parserCallback_value(ParserNode *node) {
 
 	// Replace """ with " character.  This is the only character
 	// entity used in the game files.
-	unsigned int qpos = valStr.find(""");
+	uint qpos = valStr.find(""");
 	while (qpos != Common::String::npos) {
 		valStr.replace(qpos, 6, "\"");
 		qpos = valStr.find(""");
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 7e4f160e4b3..4e027aa4408 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -38,10 +38,10 @@ void TePickMesh2::draw() {
 	if (!worldVisible())
 		return;
 
-	const unsigned int nverticies = _verticies.size();
+	const uint nverticies = _verticies.size();
 	TeMesh mesh;
 	mesh.setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
-	for (unsigned int i = 0; i < nverticies; i++) {
+	for (uint i = 0; i < nverticies; i++) {
 		mesh.setIndex(i, i);
 		mesh.setVertex(i, _verticies[i]);
 	}
@@ -84,7 +84,7 @@ bool TePickMesh2::intersect(const TeVector3f32 &origin, const TeVector3f32 &dir,
 	}
 
 	float lastHitDist = FLT_MAX;
-	for (unsigned int i = 0; i < _verticies.size() / 3; i++) {
+	for (uint i = 0; i < _verticies.size() / 3; i++) {
 		const TeVector3f32 triv1 = worldTrans * _verticies[i * 3 + 0];
 		const TeVector3f32 triv2 = worldTrans * _verticies[i * 3 + 1];
 		const TeVector3f32 triv3 = worldTrans * _verticies[i * 3 + 2];
@@ -140,12 +140,12 @@ bool TePickMesh2::pointInTriangle(const TeVector2f32 &p1, const TeVector2f32 &p2
 	return f1 != f2;
 }
 
-void TePickMesh2::setNbTriangles(unsigned int num) {
+void TePickMesh2::setNbTriangles(uint num) {
 	_verticies.resize(num * 3);
 	_lastTriangleHit = 0;
 }
 
-void TePickMesh2::setTriangle(unsigned int num, const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3) {
+void TePickMesh2::setTriangle(uint num, const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3) {
 	assert(num <= _verticies.size() / 3);
 	_verticies[num * 3 + 0] = v1;
 	_verticies[num * 3 + 1] = v2;
@@ -167,7 +167,7 @@ void TePickMesh2::deserialize(Common::ReadStream &stream, TePickMesh2 &mesh) {
 	mesh._verticies.resize(ntriangles * 3);
 	mesh._lastTriangleHit = 0;
 
-	for (unsigned int i = 0; i < ntriangles * 3; i++) {
+	for (uint i = 0; i < ntriangles * 3; i++) {
 		TeVector3f32 vec;
 		TeVector3f32::deserialize(stream, vec);
 		mesh._verticies[i] = vec;
diff --git a/engines/tetraedge/te/te_pick_mesh2.h b/engines/tetraedge/te/te_pick_mesh2.h
index 42c1a4235aa..bdda1bab0b6 100644
--- a/engines/tetraedge/te/te_pick_mesh2.h
+++ b/engines/tetraedge/te/te_pick_mesh2.h
@@ -41,11 +41,11 @@ public:
 
 	bool pointInTriangle(const TeVector2f32 &p1, const TeVector2f32 &p2, const TeVector2f32 &p3, const TeVector2f32 &p4) const;
 
-	void setLastTriangleHit(unsigned int lastHit) { _lastTriangleHit = lastHit; }
-	void setNbTriangles(unsigned int num);
+	void setLastTriangleHit(uint lastHit) { _lastTriangleHit = lastHit; }
+	void setNbTriangles(uint num);
 
-	void setTriangle(unsigned int num, const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3);
-	void triangle(unsigned int num, TeVector3f32 &v1out, TeVector3f32 &v2out, TeVector3f32 &v3out) const;
+	void setTriangle(uint num, const TeVector3f32 &v1, const TeVector3f32 &v2, const TeVector3f32 &v3);
+	void triangle(uint num, TeVector3f32 &v1out, TeVector3f32 &v2out, TeVector3f32 &v3out) const;
 
 	static void serialize(Common::WriteStream &stream, const TePickMesh2 &mesh);
 	static void deserialize(Common::ReadStream &stream, TePickMesh2 &mesh);
@@ -55,7 +55,7 @@ public:
 
 protected:
 	Common::Array<TeVector3f32> _verticies;
-	unsigned int _lastTriangleHit;
+	uint _lastTriangleHit;
 
 };
 
diff --git a/engines/tetraedge/te/te_ray_intersection.h b/engines/tetraedge/te/te_ray_intersection.h
index 5b9a65f13e4..fccb6f79a58 100644
--- a/engines/tetraedge/te/te_ray_intersection.h
+++ b/engines/tetraedge/te/te_ray_intersection.h
@@ -36,7 +36,7 @@ TePickMesh *getMesh(const TeVector3f32 &param_1, const TeVector3f32 &param_2, co
 
 // Replaced with Math::Ray::intersectTriangle
 //int intersect(const TeVector3f32 &rayPos, const TeVector3f32 &rayDir, const TeVector3f32 &v1,
-//              const TeVector3f32 &v2, const TeVector3f32 &v3, TeVector3f32 &vout, float &fout);
+//				const TeVector3f32 &v2, const TeVector3f32 &v3, TeVector3f32 &vout, float &fout);
 
 } // end namespace TeRayIntersection
 
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 3da806d02d5..bf97c200eba 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -54,7 +54,7 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 	int newPropsSize = _pendingTransparentMeshProperties + (mesh.shouldDrawMaybe() ? tricount : 1);
 	_transparentMeshProps.resize(newPropsSize);
 	if (meshMode == TeMesh::MeshMode_Triangles) {
-		for (unsigned int i = 0; i < tricount; i++) {
+		for (uint i = 0; i < tricount; i++) {
 			const uint meshNo0 = (i1 + i) * 3;
 			const uint propNo = (_numTransparentMeshes + i) * 3;
 
@@ -83,7 +83,7 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 			}
 		}
 	} else if (meshMode == TeMesh::MeshMode_TriangleStrip && tricount > 0) {
-		for (unsigned int i = 0; i < tricount; i++) {
+		for (uint i = 0; i < tricount; i++) {
 			const uint meshNo0 = (i1 + i);  // TODO: This appears to be the only difference between this and the above?
 			const uint propNo = (_numTransparentMeshes + i) * 3;
 
@@ -317,9 +317,9 @@ void TeRenderer::optimiseTransparentMeshProperties() {
 	if (_transparentMeshProps.size() <= 1)
 		return;
 
-	unsigned int i = 0;
-	for (unsigned int other = 1; other < _transparentMeshProps.size(); other++) {
-		unsigned int nextI = other;
+	uint i = 0;
+	for (uint other = 1; other < _transparentMeshProps.size(); other++) {
+		uint nextI = other;
 		if (_transparentMeshProps[i]._camera == _transparentMeshProps[other]._camera
 			&& _transparentMeshProps[i]._material == _transparentMeshProps[other]._material
 			&& _transparentMeshProps[i]._glTexEnvMode == _transparentMeshProps[other]._glTexEnvMode
@@ -359,7 +359,7 @@ static bool compareTransparentMeshProperties(const TeRenderer::TransparentMeshPr
 void TeRenderer::dumpTransparentMeshProps() const {
 	debug("** Transparent MeshProps: num:%ld pending:%d **", _numTransparentMeshes, _pendingTransparentMeshProperties);
 	debug("draw? / nverts / source / transl / zorder");
-	for (unsigned int i = 0; i < _transparentMeshProps.size(); i++) {
+	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
 		debug("%s %d %d %s %f",
 			  _transparentMeshProps[i]._shouldDraw ? "draw" : "nodr",
 			  _transparentMeshProps[i]._vertexCount,
@@ -373,7 +373,7 @@ void TeRenderer::dumpTransparentMeshProps() const {
 void TeRenderer::dumpTransparentMeshData() const {
 	debug("** Transparent Meshes: num:%ld pending:%d **", _numTransparentMeshes, _pendingTransparentMeshProperties);
 	debug("vert / normal / coord / color / vertNo");
-	for (unsigned int i = 0; i < _transparentMeshVertexes.size(); i++) {
+	for (uint i = 0; i < _transparentMeshVertexes.size(); i++) {
 		debug("%s %s %s %s %d",
 			  _transparentMeshVertexes[i].dump().c_str(),
 			  _transparentMeshNormals[i].dump().c_str(),
@@ -395,9 +395,9 @@ void TeRenderer::renderTransparentMeshes() {
 		 compareTransparentMeshProperties);
 
 	int vertsDrawn = 0;
-	for (unsigned int i = 0; i < _transparentMeshProps.size(); i++) {
+	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
 		const uint vcount = _transparentMeshProps[i]._vertexCount;
-		for (unsigned int j = 0; j < vcount; j++)
+		for (uint j = 0; j < vcount; j++)
 			_transparentMeshVertexNums[vertsDrawn + j] = (short)(_transparentMeshProps[i]._sourceTransparentMesh + j);
 		vertsDrawn += vcount;
 	}
@@ -421,7 +421,7 @@ void TeRenderer::renderTransparentMeshes() {
 	TeMatrix4x4 lastMatrix;
 
 	vertsDrawn = 0;
-	for (unsigned int i = 0; i < _transparentMeshProps.size(); i++) {
+	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
 		const TransparentMeshProperties &meshProperties = _transparentMeshProps[i];
 		if (!meshProperties._shouldDraw)
 			continue;
@@ -491,7 +491,7 @@ void TeRenderer::renderTransparentMeshes() {
 }
 
 void TeRenderer::reset() {
-	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+	clearBuffer(AllBuffers);
 	glMatrixMode(GL_PROJECTION);
 	_matrixMode = MM_GL_PROJECTION;
 	_matriciesStacks[MM_GL_PROJECTION].loadIdentity();
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index 752e29b8f55..a481d2f2936 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -70,7 +70,9 @@ public:
 	enum Buffer {
 		DepthBuffer = 1,
 		ColorBuffer = 2,
-		StencilBuffer = 4
+		StencilBuffer = 4,
+		ColorAndDepth = DepthBuffer | ColorBuffer,
+		AllBuffers = DepthBuffer | ColorBuffer | StencilBuffer
 	};
 
 	void addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsigned long i2, unsigned long i3);
diff --git a/engines/tetraedge/te/te_scrolling_layout.cpp b/engines/tetraedge/te/te_scrolling_layout.cpp
index bd15fffc8a8..5a0b931f682 100644
--- a/engines/tetraedge/te/te_scrolling_layout.cpp
+++ b/engines/tetraedge/te/te_scrolling_layout.cpp
@@ -97,19 +97,19 @@ void TeScrollingLayout::setScrollPosition(const TeVector3f32 &scrPos) {
 	pos.x() = CLIP(pos.x(), 0.0f, 1.0f);
 	pos.y() = CLIP(pos.y(), 0.0f, 1.0f);
 
-    const TeVector3f32 thisSize(xSize(), ySize(), 1.0);
-    const TeVector3f32 contentSize(_contentLayout->xSize(), _contentLayout->ySize(), 1.0);
-    TeVector3f32 sizeRatio;
+	const TeVector3f32 thisSize(xSize(), ySize(), 1.0);
+	const TeVector3f32 contentSize(_contentLayout->xSize(), _contentLayout->ySize(), 1.0);
+	TeVector3f32 sizeRatio;
 
-    if (thisSize.x() == 0.0 || thisSize.y() == 0.0) {
+	if (thisSize.x() == 0.0 || thisSize.y() == 0.0) {
 		sizeRatio = TeVector3f32(1.0, 1.0, 1.0);
-    } else {
+	} else {
 		sizeRatio = contentSize / thisSize;
-    }
+	}
 
-    TeVector3f32 posToSet = _contentLayout->userPosition();
-    const TeVector3f32 contentAnchor = _contentLayout->anchor();
-    if (!_enclose) {
+	TeVector3f32 posToSet = _contentLayout->userPosition();
+	const TeVector3f32 contentAnchor = _contentLayout->anchor();
+	if (!_enclose) {
 		if (thisSize.x() < contentSize.x()) {
 			float offset = (sizeRatio.x() + 1.0) * pos.x();
 			posToSet.x() = contentAnchor.x() * sizeRatio.x() + (1.0 - offset);
@@ -118,7 +118,7 @@ void TeScrollingLayout::setScrollPosition(const TeVector3f32 &scrPos) {
 			float offset = (sizeRatio.y() + 1.0) * pos.y();
 			posToSet.y() = contentAnchor.y() * sizeRatio.y() + (1.0 - offset);
 		}
-    } else {
+	} else {
 		if (thisSize.x() < contentSize.x()) {
 			float offset = (sizeRatio.x() - 1.0) * pos.x();
 			posToSet.x() = contentAnchor.x() * sizeRatio.x() - offset;
@@ -127,25 +127,25 @@ void TeScrollingLayout::setScrollPosition(const TeVector3f32 &scrPos) {
 			float offset = (sizeRatio.y() - 1.0) * pos.y();
 			posToSet.y() = contentAnchor.y() * sizeRatio.y() - offset;
 		}
-    }
+	}
 
-    _contentLayout->setPosition(posToSet);
-    _posUpdatedSignal.call();
+	_contentLayout->setPosition(posToSet);
+	_posUpdatedSignal.call();
 }
 
 TeVector3f32 TeScrollingLayout::scrollPosition() {
 	if (!_contentLayout)
 		return TeVector3f32();
 
-    const TeVector3f32 thisSize(xSize(), ySize(), 1.0);
-    const TeVector3f32 contentSize(_contentLayout->xSize(), _contentLayout->ySize(), 1.0);
+	const TeVector3f32 thisSize(xSize(), ySize(), 1.0);
+	const TeVector3f32 contentSize(_contentLayout->xSize(), _contentLayout->ySize(), 1.0);
 
 	TeVector3f32 sizeRatio;
-    if (thisSize.x() == 0.0 || thisSize.y() == 0.0) {
+	if (thisSize.x() == 0.0 || thisSize.y() == 0.0) {
 		sizeRatio = TeVector3f32(1.0, 1.0, 1.0);
-    } else {
+	} else {
 		sizeRatio = contentSize / thisSize;
-    }
+	}
 
 	TeVector3f32 result(0.0, 0.0, 0.0);
 	if (_enclose) {
@@ -165,7 +165,7 @@ TeVector3f32 TeScrollingLayout::scrollPosition() {
 }
 
 bool TeScrollingLayout::onAutoScrollDelayTimer() {
-  	_autoScrollDelayTimer.stop();
+	_autoScrollDelayTimer.stop();
 	playAutoScrollAnimation1();
 	return false;
 }
@@ -275,15 +275,15 @@ bool TeScrollingLayout::onMouseMove(const Common::Point &pt) {
 		}
 	}
 
-    setScrollPosition(scrollPos + offset);
-    _slideDownMousePos = inputmgr->lastMousePos();
-    TeVector3f32 nowMousePos(inputmgr->lastMousePos());
-    _insideMouseThreshold = (_lastMouseDownPos - nowMousePos).length() <= _mouseMoveThreshold;
-    long elapsed = _scrollTimer.timeElapsed();
-    if (elapsed > 0) {
+	setScrollPosition(scrollPos + offset);
+	_slideDownMousePos = inputmgr->lastMousePos();
+	TeVector3f32 nowMousePos(inputmgr->lastMousePos());
+	_insideMouseThreshold = (_lastMouseDownPos - nowMousePos).length() <= _mouseMoveThreshold;
+	long elapsed = _scrollTimer.timeElapsed();
+	if (elapsed > 0) {
 		_speed = offset / (float)(elapsed / 1000000.0);
-    }
-    return false;
+	}
+	return false;
 }
 
 bool TeScrollingLayout::onMouseLeftUp(const Common::Point &pt) {
@@ -292,7 +292,7 @@ bool TeScrollingLayout::onMouseLeftUp(const Common::Point &pt) {
 		_inertiaAnimation.setCurve(_inertiaAnimationCurve);
 		_inertiaAnimation._duration = _inertiaAnimationDuration;
 		_inertiaAnimation._startVal = _speed;
-		_inertiaAnimation._endVal = TeVector3f32(0.0, 0.0, 0.0);
+		_inertiaAnimation._endVal = TeVector3f32(0, 0, 0);
 		_inertiaAnimation._callbackObj = this;
 		_inertiaAnimation._callbackMethod = &TeScrollingLayout::setSpeed;
 		_inertiaAnimation.play();
diff --git a/engines/tetraedge/te/te_scrolling_layout.h b/engines/tetraedge/te/te_scrolling_layout.h
index 528019bf8c4..ea38d322451 100644
--- a/engines/tetraedge/te/te_scrolling_layout.h
+++ b/engines/tetraedge/te/te_scrolling_layout.h
@@ -41,7 +41,7 @@ public:
 	void setInertiaAnimationCurve(const Common::Array<float> &curve) {
 		_inertiaAnimationCurve = curve;
 	}
-	void setAutoScrollDelay(unsigned int val) {
+	void setAutoScrollDelay(uint val) {
 		_autoScrollDelay = val;
 	}
 	void setAutoScrollLoop(int loop) {
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index ed5015a16c7..2f5f39e131a 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -95,7 +95,7 @@ void TeTextBase2::build() {
 	img.create(_size._x, _size._y, nullpal, TeImage::RGBA8);
 	img.fill(_globalColor.r(), _globalColor.g(), _globalColor.b(), 0);
 
-	for (unsigned int i = 0; i < _wrappedLines.size(); i++) {
+	for (uint i = 0; i < _wrappedLines.size(); i++) {
 		drawLine(img, _wrappedLines[i], lineoffsets[i]);
 	}
 
@@ -155,12 +155,12 @@ void TeTextBase2::clearText() {
 	_valueWasSet = true;
 }
 
-void TeTextBase2::computeNbSpaces(Line &line, unsigned int startOffset, unsigned int endOffset) {
+void TeTextBase2::computeNbSpaces(Line &line, uint startOffset, uint endOffset) {
 	// only needed if we implement Justify
 	error("TODO: Implement TeTextBase2::computeNbSpaces");
 }
 
-TeColor TeTextBase2::currentColor(unsigned int offset) const {
+TeColor TeTextBase2::currentColor(uint offset) const {
 	if (_colors.size() == 0)
 		return _globalColor;
 	int closest_off = -1;
@@ -177,7 +177,7 @@ TeColor TeTextBase2::currentColor(unsigned int offset) const {
 	return result;
 }
 
-TeIntrusivePtr<TeFont3> TeTextBase2::currentFont(unsigned int offset) {
+TeIntrusivePtr<TeFont3> TeTextBase2::currentFont(uint offset) {
 	if (_fonts.size() == 0)
 		return TeIntrusivePtr<TeFont3>();
 	int closest_off = -1;
@@ -207,7 +207,7 @@ void TeTextBase2::draw() {
 	//	warning("TODO: Implement TeTextBase2::draw strikethrough support");
 }
 
-void TeTextBase2::drawEmptyChar(unsigned int offset) {
+void TeTextBase2::drawEmptyChar(uint offset) {
 	error("TODO: Implement TeTextBase2::drawEmptychar");
 }
 
@@ -219,31 +219,31 @@ void TeTextBase2::drawLine(TeImage &img, const Common::String &str, int yoffset)
 	font->draw(img, str, _fontSize, yoffset, TeColor(0, 0, 0, 255), _alignStyle);
 }
 
-unsigned int TeTextBase2::endOfWord(unsigned int offset) const {
+uint TeTextBase2::endOfWord(uint offset) const {
 	while (offset < _text.size() && !newLines(offset) && !isASpace(offset))
 		offset++;
 	return offset;
 }
 
-void TeTextBase2::insertNewLine(unsigned int offset) {
+void TeTextBase2::insertNewLine(uint offset) {
 	_lineBreaks.push_back(offset);
 }
 
-bool TeTextBase2::isASpace(unsigned int offset) const {
+bool TeTextBase2::isASpace(uint offset) const {
 	char c = _text[offset];
 	return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
 }
 
-int TeTextBase2::newLines(unsigned int offset) const {
+int TeTextBase2::newLines(uint offset) const {
 	int result = 0;
-	for (unsigned int off : _lineBreaks) {
+	for (uint off : _lineBreaks) {
 		if (off == offset)
 			result++;
 	}
 	return result;
 }
 
-int TeTextBase2::nextNonSpaceChar(unsigned int offset) {
+int TeTextBase2::nextNonSpaceChar(uint offset) {
 	while (isASpace(offset))
 		offset++;
 	return offset; // TODO: or offset - 1?
@@ -254,12 +254,12 @@ void TeTextBase2::setAlignStyle(TeFont3::AlignStyle style) {
 	_valueWasSet = true;
 }
 
-void TeTextBase2::setColor(unsigned int offset, const TeColor &color) {
+void TeTextBase2::setColor(uint offset, const TeColor &color) {
 	_colors.setVal(offset, color);
 	_valueWasSet = true;
 }
 
-void TeTextBase2::setFont(unsigned int offset, const TeIntrusivePtr<TeFont3> &newfont) {
+void TeTextBase2::setFont(uint offset, const TeIntrusivePtr<TeFont3> &newfont) {
 	_fonts.setVal(offset, newfont);
 	_valueWasSet = true;
 }
diff --git a/engines/tetraedge/te/te_text_base2.h b/engines/tetraedge/te/te_text_base2.h
index 3033669b7ff..b64f2fc757f 100644
--- a/engines/tetraedge/te/te_text_base2.h
+++ b/engines/tetraedge/te/te_text_base2.h
@@ -38,8 +38,8 @@ public:
 	TeTextBase2();
 
 	struct  Line {
-		unsigned int _startOffset;
-		unsigned int _endOffset;
+		uint _startOffset;
+		uint _endOffset;
 		float _height;
 		float _width;
 	};
@@ -54,17 +54,17 @@ public:
 	void clearStyles();
 	void clearText();
 
-	TeColor currentColor(unsigned int offset) const;
-	TeIntrusivePtr<TeFont3> currentFont(unsigned int offset);
+	TeColor currentColor(uint offset) const;
+	TeIntrusivePtr<TeFont3> currentFont(uint offset);
 	void draw();
-	unsigned int endOfWord(unsigned int i) const;
-	void insertNewLine(unsigned int offset);
-	bool isASpace(unsigned int offset) const;
-	int newLines(unsigned int offset) const;
-	int nextNonSpaceChar(unsigned int start);
+	uint endOfWord(uint i) const;
+	void insertNewLine(uint offset);
+	bool isASpace(uint offset) const;
+	int newLines(uint offset) const;
+	int nextNonSpaceChar(uint start);
 	void setAlignStyle(TeFont3::AlignStyle style);
-	void setColor(unsigned int offset, const TeColor &color);
-	void setFont(unsigned int offset, const TeIntrusivePtr<TeFont3> &newfont);
+	void setColor(uint offset, const TeColor &color);
+	void setFont(uint offset, const TeIntrusivePtr<TeFont3> &newfont);
 	void setFontSize(unsigned long fontSz);
 	void setGlobalColor(const TeColor &color);
 	void setInterLine(float val);
@@ -79,8 +79,8 @@ public:
 	const TeVector2s32 &size() const { return _size; }
 
 private:
-	void computeNbSpaces(Line &line, unsigned int startOffset, unsigned int endOffset);
-	void drawEmptyChar(unsigned int offset);
+	void computeNbSpaces(Line &line, uint startOffset, uint endOffset);
+	void drawEmptyChar(uint offset);
 	void drawLine(TeImage &img, const Common::String &str, int yoffset);
 
 	TeFont3::AlignStyle _alignStyle;
@@ -99,8 +99,8 @@ private:
 	Common::Array<Common::String> _wrappedLines;
 
 	Common::Array<uint32> _lineBreaks;
-	Common::HashMap<unsigned int, TeColor> _colors;
-	Common::HashMap<unsigned int, TeIntrusivePtr<TeFont3>> _fonts;
+	Common::HashMap<uint, TeColor> _colors;
+	Common::HashMap<uint, TeIntrusivePtr<TeFont3>> _fonts;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_text_layout.cpp b/engines/tetraedge/te/te_text_layout.cpp
index b448b96144f..2b55b0e4b8b 100644
--- a/engines/tetraedge/te/te_text_layout.cpp
+++ b/engines/tetraedge/te/te_text_layout.cpp
@@ -125,7 +125,7 @@ void TeTextLayout::setText(const Common::String &val) {
 	}
 	if (parser.style().size())
 		_base.setAlignStyle(_alignNameToEnum(parser.style()));
-	for (unsigned int offset : parser.lineBreaks())
+	for (uint offset : parser.lineBreaks())
 		_base.insertNewLine(offset);
 	_sizeChanged = true;
 }
diff --git a/engines/tetraedge/te/te_text_layout_xml_parser.h b/engines/tetraedge/te/te_text_layout_xml_parser.h
index c913b0c92cd..627f3a3cc6d 100644
--- a/engines/tetraedge/te/te_text_layout_xml_parser.h
+++ b/engines/tetraedge/te/te_text_layout_xml_parser.h
@@ -71,7 +71,7 @@ public:
 	int fontSize() const { return _fontSize; }
 	const Common::String &style() const { return _style; }
 	const Common::String &textContent() const { return _textContent; }
-	const Common::Array<unsigned int> &lineBreaks() const { return _lineBreaks; }
+	const Common::Array<uint> &lineBreaks() const { return _lineBreaks; }
 
 private:
 	TeColor _color;
@@ -79,7 +79,7 @@ private:
 	int _fontSize;
 	Common::String _style;
 	Common::String _textContent;
-	Common::Array<unsigned int> _lineBreaks;
+	Common::Array<uint> _lineBreaks;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index 5f60feec8b3..9c9d19eb939 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -256,9 +256,9 @@ void TeTiledSurface::updateSurface() {
 			getRangeIntersection(_leftCrop, 1.0 - _rightCrop, tile->_vec1.x(), tile->_vec2.x() + tile->_vec1.x(), &left, &right);
 			getRangeIntersection(_bottomCrop, 1.0 - _topCrop, tile->_vec1.y(), tile->_vec2.y() + tile->_vec1.y(), &top, &bottom);
 			if (right < left)
-			  right = left;
+				right = left;
 			if (bottom < top)
-			  bottom = top;
+				bottom = top;
 
 			const float scaled_l = (left - tile->_vec1.x()) / tile->_vec2.x();
 			const float scaled_r = (right - tile->_vec1.x()) / tile->_vec2.x();
diff --git a/engines/tetraedge/te/te_tiled_texture.cpp b/engines/tetraedge/te/te_tiled_texture.cpp
index b6438ea57b1..654225092e4 100644
--- a/engines/tetraedge/te/te_tiled_texture.cpp
+++ b/engines/tetraedge/te/te_tiled_texture.cpp
@@ -103,12 +103,11 @@ bool TeTiledTexture::load(const TeImage &img) {
 				_somethingSize += TeVector2s32(tiledata->_texture->width(), tiledata->_texture->height());
 			} else {
 				tiledata->_texture.release();
-				tiledata->_vec2 = TeVector3f32(0.0, 0.0, 0.0);
+				tiledata->_vec2 = TeVector3f32(0, 0, 0);
 				_somethingSize = Te3DTexture::optimisedSize(TeVector2s32(tileimage->w, tileimage->h));
 			}
-			tiledata->_vec1 = TeVector3f32
-					  (row * ((float)_tileSize._x / _totalSize._x),
-					   col * ((float)_tileSize._y / _totalSize._y), 0.0);
+			tiledata->_vec1 = TeVector3f32(row * ((float)_tileSize._x / _totalSize._x),
+										   col * ((float)_tileSize._y / _totalSize._y), 0.0);
 		}
 	}
 
@@ -128,8 +127,8 @@ bool TeTiledTexture::load(const TeIntrusivePtr<Te3DTexture> &texture) {
 	_tileArray.resize(1);
 	Tile *tileData = tile(TeVector2s32(0, 0));
 	tileData->_texture = texture;
-	tileData->_vec2 = TeVector3f32(1.0, 1.0, 0.0);
-	tileData->_vec1 = TeVector3f32(0.0, 0.0, 0.0);
+	tileData->_vec2 = TeVector3f32(1, 1, 0);
+	tileData->_vec1 = TeVector3f32(0, 0, 0);
 	setAccessName(texture->getAccessName().append(".tt"));
 	return true;
 }
@@ -144,7 +143,7 @@ long TeTiledTexture::numberOfRow() const {
 
 /*static*/
 TeImage *TeTiledTexture::optimisedTileImage(Common::Array<TeImage> &images, const TeVector2s32 &size,
-									   const Common::SharedPtr<TePalette> &pal, enum TeImage::Format format) {
+							const Common::SharedPtr<TePalette> &pal, enum TeImage::Format format) {
 	for (TeImage &image : images) {
 		if (image.w == size._x && image.h == size._y && image.teFormat() == format) {
 			return ℑ
diff --git a/engines/tetraedge/te/te_tiled_texture.h b/engines/tetraedge/te/te_tiled_texture.h
index 402a6d1aa21..a49ac9b2457 100644
--- a/engines/tetraedge/te/te_tiled_texture.h
+++ b/engines/tetraedge/te/te_tiled_texture.h
@@ -53,7 +53,7 @@ public:
 	long numberOfRow() const;
 
 	TeImage *optimisedTileImage(Common::Array<TeImage> &images, const TeVector2s32 &size,
-								  const Common::SharedPtr<TePalette> &pal, enum TeImage::Format format);
+								const Common::SharedPtr<TePalette> &pal, enum TeImage::Format format);
 
 	void release();
 	void save() {};
diff --git a/engines/tetraedge/tetraedge.h b/engines/tetraedge/tetraedge.h
index 290638adbc5..0ee0510fcac 100644
--- a/engines/tetraedge/tetraedge.h
+++ b/engines/tetraedge/tetraedge.h
@@ -85,9 +85,9 @@ public:
 
 	bool hasFeature(EngineFeature f) const override {
 		return
-		    (f == kSupportsLoadingDuringRuntime) ||
-		    (f == kSupportsSavingDuringRuntime) ||
-		    (f == kSupportsReturnToLauncher);
+			(f == kSupportsLoadingDuringRuntime) ||
+			(f == kSupportsSavingDuringRuntime) ||
+			(f == kSupportsReturnToLauncher);
 	};
 
 	bool canLoadGameStateCurrently() override {


Commit: b75e9faae392daa232ea4f3f01e24be97d1f0416
    https://github.com/scummvm/scummvm/commit/b75e9faae392daa232ea4f3f01e24be97d1f0416
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Remove unused Console class

Changed paths:
  R engines/tetraedge/console.cpp
  R engines/tetraedge/console.h
    engines/tetraedge/module.mk
    engines/tetraedge/tetraedge.cpp


diff --git a/engines/tetraedge/console.cpp b/engines/tetraedge/console.cpp
deleted file mode 100644
index c455866a69a..00000000000
--- a/engines/tetraedge/console.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/* 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/console.h"
-
-namespace Tetraedge {
-
-Console::Console() : GUI::Debugger() {
-	registerCmd("test",   WRAP_METHOD(Console, Cmd_test));
-}
-
-Console::~Console() {
-}
-
-bool Console::Cmd_test(int argc, const char **argv) {
-	debugPrintf("Test\n");
-	return true;
-}
-
-} // namespace Tetraedge
diff --git a/engines/tetraedge/console.h b/engines/tetraedge/console.h
deleted file mode 100644
index 210f467ab37..00000000000
--- a/engines/tetraedge/console.h
+++ /dev/null
@@ -1,40 +0,0 @@
-
-/* 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_CONSOLE_H
-#define TETRAEDGE_CONSOLE_H
-
-#include "gui/debugger.h"
-
-namespace Tetraedge {
-
-class Console : public GUI::Debugger {
-private:
-	bool Cmd_test(int argc, const char **argv);
-public:
-	Console();
-	~Console() override;
-};
-
-} // End of namespace Tetraedge
-
-#endif
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index 01df59acb24..7f55a5eb390 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -112,7 +112,6 @@ MODULE_OBJS := \
 	te/te_vector3f32.o \
 	te/te_visual_fade.o \
 	te/te_xml_gui.o \
-	console.o \
 	metaengine.o
 
 # This module can be built as a plugin
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 05bf6b71680..b7fa2f60aba 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -21,7 +21,6 @@
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/detection.h"
-#include "tetraedge/console.h"
 #include "common/scummsys.h"
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
@@ -194,9 +193,6 @@ Common::Error TetraedgeEngine::run() {
 	_renderer->init();
 	_renderer->reset();
 
-	// Set the engine's debugger console
-	setDebugger(new Console());
-
 	getInputMgr()->_keyUpSignal.add(this, &TetraedgeEngine::onKeyUp);
 
 	// If a savegame was selected from the launcher, load it.


Commit: 68790973baafd419705a5ffca7ddb4cf4a20b186
    https://github.com/scummvm/scummvm/commit/68790973baafd419705a5ffca7ddb4cf4a20b186
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Comment for detection entry

Changed paths:
    engines/tetraedge/detection_tables.h


diff --git a/engines/tetraedge/detection_tables.h b/engines/tetraedge/detection_tables.h
index a6db2b5a904..f141b5c3510 100644
--- a/engines/tetraedge/detection_tables.h
+++ b/engines/tetraedge/detection_tables.h
@@ -29,6 +29,9 @@ const PlainGameDescriptor GAME_NAMES[] = {
 };
 
 const ADGameDescription GAME_DESCRIPTIONS[] = {
+	// GOG and Steam releases
+	// Note: Full sum of GOG and Steam are different,
+	// but size and first 5000 bytes are the same.
 	{
 		"syberia",
 		nullptr,


Commit: 1a793bae5bdcde396bda2b7b2893a7f67f6a6be0
    https://github.com/scummvm/scummvm/commit/1a793bae5bdcde396bda2b7b2893a7f67f6a6be0
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Remove static objects and improve cleanup

Changed paths:
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/character.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/objectif.cpp
    engines/tetraedge/te/te_3d_object2.cpp
    engines/tetraedge/te/te_animation.cpp
    engines/tetraedge/te/te_animation.h
    engines/tetraedge/te/te_core.cpp
    engines/tetraedge/te/te_layout.cpp
    engines/tetraedge/te/te_lua_thread.cpp
    engines/tetraedge/te/te_lua_thread.h
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model_vertex_animation.cpp
    engines/tetraedge/te/te_model_vertex_animation.h
    engines/tetraedge/te/te_object.cpp
    engines/tetraedge/te/te_object.h
    engines/tetraedge/te/te_timer.cpp
    engines/tetraedge/te/te_timer.h
    engines/tetraedge/tetraedge.cpp


diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index bd0b9e3e526..f89442e05eb 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -291,6 +291,13 @@ void Application::create() {
 
 void Application::destroy() {
 	Character::animCacheFreeAll();
+
+	_globalBonusMenu.unload();
+	_bonusMenu.unload();
+	_mainMenu.unload();
+	_credits.leave();
+	_ownerErrorMenu.unload();
+	_splashScreens.unload();
 }
 
 void Application::startGame(bool newGame, int difficulty) {
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index ae9600d3edc..0790f25d382 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -36,10 +36,10 @@
 namespace Tetraedge {
 
 /*static*/ Common::HashMap<Common::String, Character::CharacterSettings> *Character::_globalCharacterSettings = nullptr;
+/*static*/ Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>> *Character::_animCacheMap = nullptr;
 
-/*static*/ Common::Array<Character::AnimCacheElement> Character::_animCache;
-/*static*/ Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>> Character::_animCacheMap;
-uint Character::_animCacheSize = 0;
+// /*static*/ Common::Array<Character::AnimCacheElement> *Character::_animCache = nullptr;
+// /*static*/ uint Character::_animCacheSize = 0;
 
 void Character::CharacterSettings::clear() {
 	_name.clear();
@@ -139,14 +139,24 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 	}
 }
 
-/*static*/ void Character::animCacheFreeAll() {
-	for (const auto &entry : _animCache)
-		_animCacheSize -= entry._size;
-	_animCache.clear();
-	_animCacheMap.clear();
+/*static*/
+void Character::animCacheFreeAll() {
+	/*
+	if (_animCache) {
+		for (const auto &entry : (*_animCache))
+			_animCacheSize -= entry._size;
+		delete _animCache;
+		_animCache = nullptr;
+	} */
+	if (_animCacheMap) {
+		delete _animCacheMap;
+		_animCacheMap = nullptr;
+	}
 }
 
-/*static*/ void Character::animCacheFreeOldest() {
+/*static*/
+void Character::animCacheFreeOldest() {
+	// Unused?
 	//_animCacheSize -= _animCache[_animCache.size() - 1]._size;
 	//_animCache.pop_back();
 }
@@ -154,9 +164,12 @@ void Character::addCallback(const Common::String &animKey, const Common::String
 /*static*/
 TeIntrusivePtr<TeModelAnimation> Character::animCacheLoad(const Common::Path &path) {
 	const Common::String pathStr = path.toString();
-	if (_animCacheMap.contains(pathStr)) {
+	if (!_animCacheMap) {
+		_animCacheMap = new Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>>();
+	}
+	if (_animCacheMap->contains(pathStr)) {
 		// Copy from the cache (keep the cached instance clean)
-		return new TeModelAnimation(*_animCacheMap.getVal(pathStr));
+		return new TeModelAnimation(*_animCacheMap->getVal(pathStr));
 	}
 
 	TeIntrusivePtr<TeModelAnimation> modelAnim = new TeModelAnimation();
@@ -164,7 +177,7 @@ TeIntrusivePtr<TeModelAnimation> Character::animCacheLoad(const Common::Path &pa
 		warning("Failed to load anim %s", path.toString().c_str());
 	}
 
-	_animCacheMap.setVal(pathStr, modelAnim);
+	_animCacheMap->setVal(pathStr, modelAnim);
 	return modelAnim;
 }
 
diff --git a/engines/tetraedge/game/character.h b/engines/tetraedge/game/character.h
index 8eb0b41e9da..a9bbe6df77c 100644
--- a/engines/tetraedge/game/character.h
+++ b/engines/tetraedge/game/character.h
@@ -238,9 +238,9 @@ private:
 
 	Common::HashMap<Common::String, Common::Array<Callback *>> _callbacks;
 
-	static Common::Array<AnimCacheElement> _animCache;
-	static Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>> _animCacheMap;
-	static uint _animCacheSize;
+	// static Common::Array<AnimCacheElement> *_animCache; // Never used?
+	// static uint _animCacheSize; // Never used?
+	static Common::HashMap<Common::String, TeIntrusivePtr<TeModelAnimation>> *_animCacheMap;
 	static Common::HashMap<Common::String, CharacterSettings> *_globalCharacterSettings;
 
 };
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index ec45dbf41e4..ae01a2294ef 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -736,18 +736,24 @@ void Game::leave(bool flag) {
 	if (!_enteredFlag2)
 		return;
 
+	Application *app = g_engine->getApplication();
+
 	deleteNoScale();
 	_entered = false;
 	_running = false;
 	_notifier.unload();
 	g_engine->getInputMgr()->_mouseLUpSignal.remove(this, &Game::onMouseClick);
 	_question2.unload();
+	TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayout("background");
+	if (cellbg)
+		app->frontLayout().removeChild(cellbg);
 	_inventory.cellphone()->leave();
 	_dialog2.unload();
 	_inventory.unload();
 	_documentsBrowser.unload();
 	_inventoryMenu.unload();
 	_gui1.unload();
+	_objectif.unload(); // not done in original, but should be.
 	_scene.close();
 	_forGui.unload();
 	if (_scene._character) {
@@ -786,7 +792,6 @@ void Game::leave(bool flag) {
 	_playedTimer.stop();
 	_enteredFlag2 = false;
 
-	Application *app = g_engine->getApplication();
 	app->lockCursor(false);
 	app->lockCursorFromAction(false);
 	// TODO: Set some inputmgr flag here?
diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
index 42a93841cb7..adf0bfb668c 100644
--- a/engines/tetraedge/game/objectif.cpp
+++ b/engines/tetraedge/game/objectif.cpp
@@ -80,6 +80,7 @@ void Objectif::load() {
 }
 
 void Objectif::leave() {
+	Application *app = g_engine->getApplication();
 	TeLayout *layout;
 	layout = _gui1.layout("background");
 	if (layout)
@@ -228,6 +229,13 @@ void Objectif::createChildLayout(TeLayout *layout, Common::String const &taskId,
 void Objectif::unload() {
 	removeChildren();
 	leave();
+
+	Application *app = g_engine->getApplication();
+	TeButtonLayout *btn = _gui2.buttonLayoutChecked("helpButton");
+	app->frontLayout().removeChild(btn);
+	btn = _gui1.buttonLayoutChecked("background");
+	app->frontLayout().removeChild(btn);
+
 	_gui1.unload();
 	_gui2.unload();
 	_tasks.clear();
diff --git a/engines/tetraedge/te/te_3d_object2.cpp b/engines/tetraedge/te/te_3d_object2.cpp
index 5f96a047c37..069d1bb04ef 100644
--- a/engines/tetraedge/te/te_3d_object2.cpp
+++ b/engines/tetraedge/te/te_3d_object2.cpp
@@ -36,6 +36,8 @@ Te3DObject2::~Te3DObject2() {
 	for (auto *child : _children) {
 		child->setParent(nullptr);
 	}
+	// clear list in case parent->removeChild triggers a signal which ends up referencing it.
+	_children.clear();
 	if (parent()) {
 		parent()->removeChild(this);
 	}
diff --git a/engines/tetraedge/te/te_animation.cpp b/engines/tetraedge/te/te_animation.cpp
index 7048112b137..2b56fb20740 100644
--- a/engines/tetraedge/te/te_animation.cpp
+++ b/engines/tetraedge/te/te_animation.cpp
@@ -23,12 +23,13 @@
 
 namespace Tetraedge {
 
+/*static*/
 Common::Array<TeAnimation *> *TeAnimation::_animations = nullptr;
 
-/*static*/ Common::Array<TeAnimation *> *TeAnimation::animations() {
+/*static*/
+Common::Array<TeAnimation *> *TeAnimation::animations() {
 	if (!_animations)
 		_animations = new Common::Array<TeAnimation *>();
-
 	return _animations;
 }
 
@@ -108,6 +109,12 @@ void TeAnimation::resumeAll() {
 	}
 }
 
+/*static*/
+void TeAnimation::cleanup() {
+	delete _animations;
+	_animations = nullptr;
+}
+
 /*static*/
 void TeAnimation::updateAll() {
 	Common::Array<TeAnimation *> &anims = *animations();
diff --git a/engines/tetraedge/te/te_animation.h b/engines/tetraedge/te/te_animation.h
index 576b0688776..f5e3d5766ad 100644
--- a/engines/tetraedge/te/te_animation.h
+++ b/engines/tetraedge/te/te_animation.h
@@ -49,6 +49,7 @@ public:
 	static void resumeAll();
 	static void updateAll();
 
+	static void cleanup();
 
 	TeSignal0Param &onStop() { return _onStopSignal; }
 	TeSignal0Param &onFinished() { return _onFinishedSignal; }
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index bb6b76e292d..73960d82ca2 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -125,8 +125,8 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 	const Common::Path fname = path.getLastComponent();
 	const Common::Path dir = path.getParent();
 
-	static const Common::Path pathSuffixes[] = {
-		"",
+	static const char *pathSuffixes[] = {
+		nullptr, // no suffix
 		"PC-MacOSX",
 		"PC-PS3-Android-MacOSX",
 		"PC-MacOSX-Xbox360-PS3",
@@ -156,10 +156,10 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 	for (int langtype = 0; langtype < ARRAYSIZE(langs); langtype++) {
 		const Common::Path &lang = langs[langtype];
 		for (int i = 0; i < ARRAYSIZE(pathSuffixes); i++) {
-			const Common::Path &suffix = pathSuffixes[i];
+			const char *suffix = pathSuffixes[i];
 
 			Common::Path testPath = dir;
-			if (!suffix.empty())
+			if (suffix)
 				testPath.joinInPlace(suffix);
 			if (!lang.empty())
 				testPath.joinInPlace(lang);
@@ -168,7 +168,7 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 				return testPath;
 
 			// also try the other way around
-			if (!lang.empty() && !suffix.empty()) {
+			if (!lang.empty() && suffix) {
 				testPath = dir.join(lang).joinInPlace(suffix).join(fname);
 				if (Common::File::exists(testPath) || Common::FSNode(testPath).exists())
 					return testPath;
diff --git a/engines/tetraedge/te/te_layout.cpp b/engines/tetraedge/te/te_layout.cpp
index 7578a111caf..3f0660cf2e2 100644
--- a/engines/tetraedge/te/te_layout.cpp
+++ b/engines/tetraedge/te/te_layout.cpp
@@ -51,7 +51,9 @@ TeLayout::TeLayout() : Te3DObject2(), _autoz(true), _needZUpdate(true), _updatin
 TeLayout::~TeLayout() {
 	if (parent() && _onParentSizeChangedCallback) {
 		parent()->onSizeChanged().remove(_onParentSizeChangedCallback);
+		_onParentSizeChangedCallback.reset();
 		parent()->onWorldTransformationMatrixChanged().remove(_onParentWorldTransformationMatrixChangedCallback);
+		_onParentWorldTransformationMatrixChangedCallback.reset();
 	}
 
 	if (_onChildSizeChangedCallback) {
diff --git a/engines/tetraedge/te/te_lua_thread.cpp b/engines/tetraedge/te/te_lua_thread.cpp
index 4c1a8368f88..5800e6d0e01 100644
--- a/engines/tetraedge/te/te_lua_thread.cpp
+++ b/engines/tetraedge/te/te_lua_thread.cpp
@@ -32,22 +32,24 @@
 
 namespace Tetraedge {
 
-/*static*/ Common::Array<TeLuaThread *> TeLuaThread::_threadList;
+/*static*/
+Common::Array<TeLuaThread *> *TeLuaThread::_threadList = nullptr;
 
 TeLuaThread::TeLuaThread(TeLuaContext *context) : _resumeCount(0), _lastResumeResult(0), _released(false) {
 	_luaThread = lua_newthread(context->luaState());
 	_bottomRef = luaL_ref(context->luaState(), LUA_REGISTRYINDEX);
-	_threadList.push_back(this);
+	threadList()->push_back(this);
 }
 
 TeLuaThread::~TeLuaThread() {
 	luaL_unref(_luaThread, LUA_REGISTRYINDEX, _bottomRef);
 	uint i;
-	for (i = 0; i < _threadList.size(); i++)
-		if (_threadList[i] == this)
+	Common::Array<TeLuaThread *> *threads = threadList();
+	for (i = 0; i < threads->size(); i++)
+		if ((*threads)[i] == this)
 			break;
-	if (i < _threadList.size())
-		_threadList.remove_at(i);
+	if (i < threads->size())
+		threads->remove_at(i);
 }
 
 /*static*/ TeLuaThread *TeLuaThread::create(TeLuaContext *context) {
@@ -226,14 +228,29 @@ void TeLuaThread::resume(const TeVariant &p1, const TeVariant &p2, const TeVaria
 	}
 }
 
-/*static*/ TeLuaThread *TeLuaThread::threadFromState(lua_State *state) {
-	for (auto &thread : _threadList) {
+/*static*/
+TeLuaThread *TeLuaThread::threadFromState(lua_State *state) {
+	Common::Array<TeLuaThread *> *threads = threadList();
+	for (auto &thread : *threads) {
 		if (thread->_luaThread == state)
 			return thread;
 	}
 	return nullptr;
 }
 
+/*static*/
+Common::Array<TeLuaThread *> *TeLuaThread::threadList() {
+	if (!_threadList)
+		_threadList = new Common::Array<TeLuaThread *>();
+	return _threadList;
+}
+
+/*static*/
+void TeLuaThread::cleanup() {
+	delete _threadList;
+	_threadList = nullptr;
+}
+
 int TeLuaThread::yield() {
 	return lua_yield(_luaThread, 0);
 }
diff --git a/engines/tetraedge/te/te_lua_thread.h b/engines/tetraedge/te/te_lua_thread.h
index a7c96efffea..86e554d613f 100644
--- a/engines/tetraedge/te/te_lua_thread.h
+++ b/engines/tetraedge/te/te_lua_thread.h
@@ -59,6 +59,8 @@ public:
 	static TeLuaThread *threadFromState(lua_State *state);
 	int yield();
 
+	static void cleanup();
+
 private:
 	void _resume(int nargs);
 
@@ -68,7 +70,8 @@ private:
 	int _lastResumeResult;
 	bool _released;
 
-	static Common::Array<TeLuaThread *> _threadList;
+	static Common::Array<TeLuaThread *> *threadList();
+	static Common::Array<TeLuaThread *> *_threadList;
 
 };
 
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index ccc439470a0..77cc00b0b04 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -373,7 +373,7 @@ void TeMesh::update(TeIntrusivePtr<TeModelVertexAnimation> vertexanim) {
 	_updatedVerticies.resize(_verticies.size());
 	_updatedNormals.resize(_normals.size());
 
-	const Common::Array<TeVector3f32> &animverts = vertexanim->getVertices();
+	const Common::Array<TeVector3f32> animverts = vertexanim->getVertices();
 	assert(animverts.size() >= _verticies.size());
 	for (uint i = 0; i < _verticies.size(); i++) {
 		_updatedVerticies[i] = animverts[i];
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 2ae90852127..919a12253ac 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -248,17 +248,17 @@ void TeModel::update() {
 				mesh.update(&_boneMatricies, &_lerpedElements);
 			} else {
 				mesh.resizeUpdatedTables(mesh.numVerticies());
-				const Common::Array<TeVector3f32> *verticies = nullptr;
+				Common::Array<TeVector3f32> verticies;
 				if (_modelVertexAnim && mesh.name() == _modelVertexAnim->head())
-					verticies = &_modelVertexAnim->getVertices();
+					verticies = _modelVertexAnim->getVertices();
 
 				for (uint i = 0; i < mesh.numVerticies(); i++) {
 					TeVector3f32 vertex;
-					if (!verticies) {
+					if (verticies.empty()) {
 						vertex = mesh.preUpdatedVertex(i);
 					} else {
-						if (i < verticies->size())
-							vertex = (*verticies)[i];
+						if (i < verticies.size())
+							vertex = verticies[i];
 					}
 					TeVector3f32 normal = mesh.preUpdatedNormal(i);
 					int idx = (int)mesh.matrixIndex(i);
@@ -268,7 +268,7 @@ void TeModel::update() {
 
 					if (idx < (int)_bones.size()) {
 						updatedvertex = vertex;
-						if (!verticies)
+						if (verticies.empty())
 							updatedvertex = _boneMatricies[idx] * updatedvertex;
 						updatednormal = _boneMatricies[idx] * normal;
 					} else {
diff --git a/engines/tetraedge/te/te_model_vertex_animation.cpp b/engines/tetraedge/te/te_model_vertex_animation.cpp
index cd71198e331..83073e17cc5 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.cpp
+++ b/engines/tetraedge/te/te_model_vertex_animation.cpp
@@ -49,10 +49,9 @@ TeVector3f32 TeModelVertexAnimation::getKeyVertex(unsigned long keyno, uint vert
 	return retval;
 }
 
-const Common::Array<TeVector3f32> &TeModelVertexAnimation::getVertices() {
-	static Common::Array<TeVector3f32> lerpVtx;
+Common::Array<TeVector3f32> TeModelVertexAnimation::getVertices() {
+	Common::Array<TeVector3f32> lerpVtx;
 
-	lerpVtx.clear();
 	if (_keydata.size() < 2)
 		return lerpVtx;
 
diff --git a/engines/tetraedge/te/te_model_vertex_animation.h b/engines/tetraedge/te/te_model_vertex_animation.h
index 87b30a1e38d..27ed4fffe91 100644
--- a/engines/tetraedge/te/te_model_vertex_animation.h
+++ b/engines/tetraedge/te/te_model_vertex_animation.h
@@ -57,7 +57,7 @@ public:
 
 	const Common::String &head() const { return _head; }
 	TeVector3f32 getKeyVertex(unsigned long keyno, uint vertexno);
-	const Common::Array<TeVector3f32> &getVertices();
+	Common::Array<TeVector3f32> getVertices();
 
 	bool load(Common::ReadStream &stream);
 	void save(Common::WriteStream &stream) const;
diff --git a/engines/tetraedge/te/te_object.cpp b/engines/tetraedge/te/te_object.cpp
index ea856557979..ac63db8e899 100644
--- a/engines/tetraedge/te/te_object.cpp
+++ b/engines/tetraedge/te/te_object.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "tetraedge/te/te_object.h"
+#include "common/debug.h"
 
 namespace Tetraedge {
 
@@ -27,17 +28,40 @@ TeObject::TeObject() {
 }
 
 void TeObject::deleteLater() {
-	_pendingDeleteList.push_back(this);
+	pendingDeleteList()->push_back(this);
 }
 
-/*static*/ void TeObject::deleteNow() {
-	uint len = _pendingDeleteList.size();
-	for (uint i = 0; i < len; i++) {
-		delete _pendingDeleteList[i];
+/*static*/
+void TeObject::deleteNow() {
+	Common::Array<TeObject *> *pending = pendingDeleteList();
+	for (auto *obj : (*pending)) {
+		delete obj;
 	}
-	_pendingDeleteList.clear();
+	pending->clear();
+}
+
+/*static*/
+Common::Array<TeObject *> *TeObject::_pendingDeleteList = nullptr;
+
+/*static*/
+Common::Array<TeObject *> *TeObject::pendingDeleteList() {
+	if (!_pendingDeleteList)
+		_pendingDeleteList = new Common::Array<TeObject *>();
+	return _pendingDeleteList;
+}
+
+/*static*/
+void TeObject::cleanup() {
+	// Should be deleted already, but if not..
+	if (_pendingDeleteList && _pendingDeleteList->size()) {
+		warning("Leaking %d objects on shutdown.", _pendingDeleteList->size());
+		for (auto *obj : (*_pendingDeleteList)) {
+			debug("Leaked %p", obj);
+		}
+	}
+	delete _pendingDeleteList;
+	_pendingDeleteList = nullptr;
 }
 
-Common::Array<TeObject *> TeObject::_pendingDeleteList;
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_object.h b/engines/tetraedge/te/te_object.h
index cb4b4a35188..6725d41a8ac 100644
--- a/engines/tetraedge/te/te_object.h
+++ b/engines/tetraedge/te/te_object.h
@@ -38,8 +38,11 @@ public:
 	/** Delete all pending objects now */
 	static void deleteNow();
 
+	static void cleanup();
+
 private:
-	static Common::Array<TeObject *> _pendingDeleteList;
+	static Common::Array<TeObject *> *_pendingDeleteList;
+	static Common::Array<TeObject *> *pendingDeleteList();
 
 };
 
diff --git a/engines/tetraedge/te/te_timer.cpp b/engines/tetraedge/te/te_timer.cpp
index 97e62ec6a21..ea02846a2a8 100644
--- a/engines/tetraedge/te/te_timer.cpp
+++ b/engines/tetraedge/te/te_timer.cpp
@@ -24,11 +24,11 @@
 namespace Tetraedge {
 
 
-/*static*/ bool TeTimer::_pausedAll;
-/*static*/ unsigned long TeTimer::_realTime;
-/*static*/ Common::Array<TeTimer *> TeTimer::_timers;
-/*static*/ Common::Array<TeTimer *> TeTimer::_pausedTimers;
-/*static*/ TeRealTimer *TeTimer::_realTimer;
+/*static*/ bool TeTimer::_pausedAll = false;
+/*static*/ unsigned long TeTimer::_realTime = 0;
+/*static*/ Common::Array<TeTimer *> *TeTimer::_timers = nullptr;
+/*static*/ Common::Array<TeTimer *> *TeTimer::_pausedTimers = nullptr;
+/*static*/ TeRealTimer *TeTimer::_realTimer = nullptr;
 
 
 TeTimer::TeTimer() : _stopped(true), _pausable(true), _alarmTime(0),
@@ -40,16 +40,19 @@ _startTime(0), _lastTimeElapsed(0), _startTimeOffset(0), _updated(false) {
 }
 
 TeTimer::~TeTimer() {
-	for (uint i = 0; i < _timers.size(); i++) {
-		if (_timers[i] == this) {
-			_timers.remove_at(i);
+	Common::Array<TeTimer *> *ts = timers();
+	for (uint i = 0; i < ts->size(); i++) {
+		if ((*ts)[i] == this) {
+			ts->remove_at(i);
 			break;
 		}
 	}
+
 	// Not done in original, but probably should be?
-	for (uint i = 0; i < _pausedTimers.size(); i++) {
-		if (_pausedTimers[i] == this) {
-			_pausedTimers.remove_at(i);
+	Common::Array<TeTimer *> *pts = pausedTimers();
+	for (uint i = 0; i < pts->size(); i++) {
+		if ((*pts)[i] == this) {
+			pts->remove_at(i);
 			break;
 		}
 	}
@@ -72,9 +75,9 @@ void TeTimer::start() {
 	_lastTimeElapsed = timeOffset;
 	_stopped = false;
 	_updated = false;
-	_timers.push_back(this);
+	timers()->push_back(this);
 	if (_pausedAll && _pausable) {
-		_pausedTimers.push_back(this);
+		pausedTimers()->push_back(this);
 		pause();
 	}
 }
@@ -83,9 +86,10 @@ void TeTimer::pause() {
 	if (!_stopped) {
 		_startTime = _realTime;
 		_stopped = true;
-		for (uint i = 0; i < _timers.size(); i++) {
-			if (_timers[i] == this) {
-				_timers.remove_at(i);
+		Common::Array<TeTimer *> *ts = timers();
+		for (uint i = 0; i < ts->size(); i++) {
+			if ((*ts)[i] == this) {
+				ts->remove_at(i);
 				break;
 			}
 		}
@@ -138,24 +142,25 @@ unsigned long TeTimer::time_() {
 
 void TeTimer::pausable(bool ispausable) {
 	_pausable = ispausable;
+	Common::Array<TeTimer *> *paused = pausedTimers();
 	if (!_pausable) {
-		for (uint i = 0; i < _pausedTimers.size(); i++) {
-			if (_pausedTimers[i] == this) {
-				_pausedTimers.remove_at(i);
+		for (uint i = 0; i < paused->size(); i++) {
+			if ((*paused)[i] == this) {
+				paused->remove_at(i);
 				break;
 			}
 		}
 	} else if (_pausedAll) {
 		// ensure this is paused now
 		bool add = true;
-		for (TeTimer *pausedTimer : _pausedTimers) {
+		for (TeTimer *pausedTimer : *paused) {
 			if (pausedTimer == this) {
 				add = false;
 				break;
 			}
 		}
 		if (add)
-			_pausedTimers.push_back(this);
+			paused->push_back(this);
 		pause();
 	}
 }
@@ -169,43 +174,72 @@ void TeTimer::setAlarmIn(unsigned long offset) {
 	_alarmSet = true;
 }
 
-/*static*/ TeRealTimer *TeTimer::realTimer() {
+/*static*/
+TeRealTimer *TeTimer::realTimer() {
 	if (!_realTimer)
 		_realTimer = new TeRealTimer();
 	return _realTimer;
 }
 
-/*static*/ void TeTimer::pauseAll() {
+/*static*/
+Common::Array<TeTimer *> *TeTimer::timers() {
+	if (!_timers)
+		_timers = new Common::Array<TeTimer *>();
+	return _timers;
+}
+
+/*static*/
+Common::Array<TeTimer *> *TeTimer::pausedTimers() {
+	if (!_pausedTimers)
+		_pausedTimers = new Common::Array<TeTimer *>();
+	return _pausedTimers;
+}
+
+/*static*/
+void TeTimer::pauseAll() {
 	if (_pausedAll)
 		return;
 
 	_pausedAll = true;
 	_realTime = realTimer()->getTimeFromStart();
-	for (TeTimer *timer : _timers) {
+	for (TeTimer *timer : (*timers())) {
 		if (timer->_stopped || !timer->_pausable)
 			continue;
-		_pausedTimers.push_back(timer);
+		pausedTimers()->push_back(timer);
 		timer->pause();
 	}
 }
 
-/*static*/ void TeTimer::resumeAll() {
+/*static*/
+void TeTimer::resumeAll() {
 	if (!_pausedAll)
 		return;
 
 	_pausedAll = false;
 
 	_realTime = realTimer()->getTimeFromStart();
-	for (TeTimer *timer : _pausedTimers) {
+	for (TeTimer *timer : (*pausedTimers())) {
 		timer->start();
 	}
-	_pausedTimers.clear();
+	pausedTimers()->clear();
 }
 
-/*static*/ void TeTimer::updateAll() {
+/*static*/
+void TeTimer::updateAll() {
 	_realTime = realTimer()->getTimeFromStart();
-	for (auto *timer : _timers)
+	for (auto *timer : (*timers()))
 		timer->update();
 }
 
+/*static*/
+void TeTimer::cleanup() {
+	delete _timers;
+	_timers = nullptr;
+	delete _pausedTimers;
+	_pausedTimers = nullptr;
+	delete _realTimer;
+	_realTimer = nullptr;
+}
+
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_timer.h b/engines/tetraedge/te/te_timer.h
index 824dfd70539..93d5d6d348d 100644
--- a/engines/tetraedge/te/te_timer.h
+++ b/engines/tetraedge/te/te_timer.h
@@ -53,10 +53,14 @@ public:
 	static void resumeAll();
 	static void updateAll();
 
+	static void cleanup();
+
 	bool running() const { return !_stopped; }
 
 private:
 	static TeRealTimer *realTimer();
+	static Common::Array<TeTimer *> *timers();
+	static Common::Array<TeTimer *> *pausedTimers();
 
 	unsigned long _startTime;
 	unsigned long _startTimeOffset;
@@ -71,8 +75,8 @@ private:
 
 	static bool _pausedAll;
 	static unsigned long _realTime;
-	static Common::Array<TeTimer *> _timers;
-	static Common::Array<TeTimer *> _pausedTimers;
+	static Common::Array<TeTimer *> *_timers;
+	static Common::Array<TeTimer *> *_pausedTimers;
 	static TeRealTimer *_realTimer;
 };
 
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index b7fa2f60aba..2c9c0302d99 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -37,6 +37,7 @@
 #include "tetraedge/te/te_core.h"
 #include "tetraedge/te/te_renderer.h"
 #include "tetraedge/te/te_resource_manager.h"
+#include "tetraedge/te/te_lua_thread.h"
 #include "tetraedge/te/te_sound_manager.h"
 #include "tetraedge/te/te_input_mgr.h"
 
@@ -52,6 +53,7 @@ TetraedgeEngine::TetraedgeEngine(OSystem *syst, const ADGameDescription *gameDes
 }
 
 TetraedgeEngine::~TetraedgeEngine() {
+	//TeObject::deleteNow();
 	delete _core;
 	delete _game;
 	delete _application;
@@ -61,6 +63,10 @@ TetraedgeEngine::~TetraedgeEngine() {
 	delete _inputMgr;
 	Object3D::cleanup();
 	Character::cleanup();
+	TeAnimation::cleanup();
+	TeLuaThread::cleanup();
+	TeTimer::cleanup();
+	TeObject::cleanup();
 }
 
 /*static*/
@@ -217,6 +223,11 @@ Common::Error TetraedgeEngine::run() {
 		g_system->delayMillis(10);
 	}
 
+	// Ensure game has stopped.
+	_game->leave(true);
+	TeObject::deleteNow();
+	_application->destroy();
+
 	return Common::kNoError;
 }
 


Commit: 16c607cd94b97a6b4ca6a2c1725adb9c9b8ffb98
    https://github.com/scummvm/scummvm/commit/16c607cd94b97a6b4ca6a2c1725adb9c9b8ffb98
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Split OpenGL-specific code out to allow TinyGL support

Changed paths:
  A engines/tetraedge/game/characters_shadow_opengl.cpp
  A engines/tetraedge/game/characters_shadow_opengl.h
  A engines/tetraedge/game/characters_shadow_tinygl.cpp
  A engines/tetraedge/game/characters_shadow_tinygl.h
  A engines/tetraedge/te/te_3d_texture_opengl.cpp
  A engines/tetraedge/te/te_3d_texture_opengl.h
  A engines/tetraedge/te/te_3d_texture_tinygl.cpp
  A engines/tetraedge/te/te_3d_texture_tinygl.h
  A engines/tetraedge/te/te_light_opengl.cpp
  A engines/tetraedge/te/te_light_opengl.h
  A engines/tetraedge/te/te_light_tinygl.cpp
  A engines/tetraedge/te/te_light_tinygl.h
  A engines/tetraedge/te/te_mesh_opengl.cpp
  A engines/tetraedge/te/te_mesh_opengl.h
  A engines/tetraedge/te/te_mesh_tinygl.cpp
  A engines/tetraedge/te/te_mesh_tinygl.h
  A engines/tetraedge/te/te_renderer_opengl.cpp
  A engines/tetraedge/te/te_renderer_opengl.h
  A engines/tetraedge/te/te_renderer_tinygl.cpp
  A engines/tetraedge/te/te_renderer_tinygl.h
    engines/tetraedge/game/billboard.cpp
    engines/tetraedge/game/character.cpp
    engines/tetraedge/game/characters_shadow.cpp
    engines/tetraedge/game/characters_shadow.h
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/game/in_game_scene.h
    engines/tetraedge/game/lua_binds.cpp
    engines/tetraedge/game/scene_lights_xml_parser.cpp
    engines/tetraedge/game/scene_lights_xml_parser.h
    engines/tetraedge/module.mk
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture.h
    engines/tetraedge/te/te_bezier_curve.cpp
    engines/tetraedge/te/te_free_move_zone.cpp
    engines/tetraedge/te/te_light.cpp
    engines/tetraedge/te/te_light.h
    engines/tetraedge/te/te_material.cpp
    engines/tetraedge/te/te_material.h
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_mesh.h
    engines/tetraedge/te/te_model.cpp
    engines/tetraedge/te/te_model.h
    engines/tetraedge/te/te_pick_mesh2.cpp
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer.h
    engines/tetraedge/te/te_resource_manager.h
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_text_base2.h
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_tiled_texture.cpp
    engines/tetraedge/te/te_visual_fade.cpp
    engines/tetraedge/tetraedge.cpp
    engines/tetraedge/tetraedge.h


diff --git a/engines/tetraedge/game/billboard.cpp b/engines/tetraedge/game/billboard.cpp
index d3c8f002568..466fc059650 100644
--- a/engines/tetraedge/game/billboard.cpp
+++ b/engines/tetraedge/game/billboard.cpp
@@ -32,7 +32,7 @@ Billboard::Billboard() : _hasPos2(false) {
 
 bool Billboard::load(const Common::String &path) {
 	_model = new TeModel();
-	TeIntrusivePtr<Te3DTexture> texture = new Te3DTexture();
+	TeIntrusivePtr<Te3DTexture> texture = Te3DTexture::makeInstance();
 	Game *game = g_engine->getGame();
 	Common::Path texpath = game->sceneZonePath().join(path);
 	texture->load(texpath);
@@ -70,22 +70,22 @@ void Billboard::calcVertex() {
 	fx = _pos.x();
 	fy = _pos.y();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
-	_model->meshes()[0].setVertex(0, meshVertex);
+	_model->meshes()[0]->setVertex(0, meshVertex);
 
 	fx = _pos.x();
 	fy = _pos.y() + _size.getY();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
-	_model->meshes()[0].setVertex(1, meshVertex);
+	_model->meshes()[0]->setVertex(1, meshVertex);
 
 	fx = _pos.x() + _size.getX();
 	fy = _pos.y();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
-	_model->meshes()[0].setVertex(2, meshVertex);
+	_model->meshes()[0]->setVertex(2, meshVertex);
 
 	fx = _pos.x() + _size.getX();
 	fy = _pos.y() + _size.getY();
 	meshVertex = camTotalInverse * TeVector3f32(fx + fx - 1.0f, fy + fy - 1.0f, posvec.z());
-	_model->meshes()[0].setVertex(3, meshVertex);
+	_model->meshes()[0]->setVertex(3, meshVertex);
 }
 
 void Billboard::position(const TeVector3f32 &pos) {
diff --git a/engines/tetraedge/game/character.cpp b/engines/tetraedge/game/character.cpp
index 0790f25d382..f57b8a3a1a2 100644
--- a/engines/tetraedge/game/character.cpp
+++ b/engines/tetraedge/game/character.cpp
@@ -370,7 +370,7 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 	_model->setScale(_characterSettings._defaultScale);
 
 	for (auto &mesh : _model->meshes())
-		mesh.setVisible(true);
+		mesh->setVisible(true);
 
 	// Set all mouthes not visible by default
 	_model->setVisibleByName("_B_", false);
@@ -388,7 +388,7 @@ bool Character::loadModel(const Common::String &mname, bool unused) {
 	_walkEndGAnimLen = animLengthFromFile(walkAnim(WalkPart_EndG), &_walkEndGAnimFrameCount);
 	_walkLoopAnimLen = animLengthFromFile(walkAnim(WalkPart_Loop), &_walkLoopAnimFrameCount);
 
-	TeIntrusivePtr<Te3DTexture> shadow = new Te3DTexture();
+	TeIntrusivePtr<Te3DTexture> shadow = Te3DTexture::makeInstance();
 	shadow->load("models/Textures/simple_shadow_alpha.tga");
 
 	for (int i = 0; i < 2; i++) {
diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
index 3593488eedb..09fc4c55537 100644
--- a/engines/tetraedge/game/characters_shadow.cpp
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -19,21 +19,22 @@
  *
  */
 
-#include "graphics/opengl/system_headers.h"
-
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/game/character.h"
 #include "tetraedge/game/characters_shadow.h"
+#include "tetraedge/game/characters_shadow_opengl.h"
+#include "tetraedge/game/characters_shadow_tinygl.h"
 #include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_renderer.h"
 #include "tetraedge/te/te_3d_texture.h"
 
+
 namespace Tetraedge {
 
 /*static*/
 Te3DObject2 *CharactersShadow::_camTarget = nullptr;
 
-CharactersShadow::CharactersShadow() {
+CharactersShadow::CharactersShadow() : _glTex(0), _texSize(0) {
 }
 
 void CharactersShadow::create(InGameScene *scene) {
@@ -46,14 +47,9 @@ void CharactersShadow::create(InGameScene *scene) {
 	_camera->setPerspectiveVal(1.0);
 	_camera->setName("_shadowCam");
 	_camera->viewport(0, 0, _texSize, _texSize);
-	Te3DTexture::unbind();
-	glGenTextures(1, &_glTex);
-	glBindTexture(GL_TEXTURE_2D, _glTex);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, _texSize, _texSize, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, nullptr);
+
+	createInternal();
+
 	renderer->disableTexture();
 }
 
@@ -70,28 +66,15 @@ void CharactersShadow::createTexture(InGameScene *scene) {
 	_camera->setFov((float)(scene->shadowFov() * M_PI / 180.0));
 	_camera->setOrthoPlanes(scene->shadowNearPlane(), scene->shadowFarPlane());
 	_camera->apply();
-
-	glClearColor(0.0, 0.0, 0.0, 0.0);
-	renderer->clearBuffer(TeRenderer::ColorAndDepth);
-
-	for (Character *character : scene->_characters) {
-		character->_model->draw();
-	}
-	scene->_character->_model->draw();
-	Te3DTexture::unbind();
-	glBindTexture(GL_TEXTURE_2D, _glTex);
-	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _texSize, _texSize);
-	renderer->clearBuffer(TeRenderer::ColorAndDepth);
+	
+	createTextureInternal(scene);
 
 	TeCamera::restore();
 	TeCamera::restore();
 }
 
 void CharactersShadow::destroy() {
-	TeRenderer *renderer = g_engine->getRenderer();
-	renderer->disableTexture();
-	glBindTexture(GL_TEXTURE_2D, 0);
-	glDeleteTextures(1, &_glTex);
+	deleteTexture();
 	if (_camera)
 		_camera = nullptr;
 	if (_camTarget) {
@@ -100,70 +83,20 @@ void CharactersShadow::destroy() {
 	}
 }
 
-void CharactersShadow::draw(InGameScene *scene) {
-	TeRenderer *renderer = g_engine->getRenderer();
-	glDepthMask(false);
-	renderer->disableZBuffer();
-	renderer->enableTexture();
-	glBindTexture(GL_TEXTURE_2D, _glTex);
-	Te3DTexture::unbind();
-	glBindTexture(GL_TEXTURE_2D, _glTex);
-	glEnable(GL_BLEND);
-	renderer->setCurrentColor(scene->shadowColor());
-
-	TeMatrix4x4 matrix;
-	matrix.translate(TeVector3f32(0.5f, 0.5f, 0.5f));
-	matrix.scale(TeVector3f32(0.5f, 0.5f, 0.5f));
-	matrix = matrix * _camera->projectionMatrix();
-
-	TeMatrix4x4 cammatrix = _camera->worldTransformationMatrix();
-	cammatrix.inverse();
-
-	matrix = matrix * cammatrix;
-
-	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
-
-	float f[4];
-	for (uint i = 0; i < 4; i++)
-		f[i] = matrix(i, 0);
-
-	glTexGenfv(GL_S, GL_EYE_PLANE, f);
-	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
-
-	for (uint i = 0; i < 4; i++)
-		f[i] = matrix(i, 1);
-
-	glTexGenfv(GL_T, GL_EYE_PLANE, f);
-	glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
-
-	for (uint i = 0; i < 4; i++)
-		f[i] = matrix(i, 2);
-
-	glTexGenfv(GL_R, GL_EYE_PLANE, f);
-	glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
-
-	for (uint i = 0; i < 4; i++)
-		f[i] = matrix(i, 3);
-
-	glTexGenfv(GL_Q, GL_EYE_PLANE, f);
-
-	Te3DTexture::unbind();
-	glBindTexture(GL_TEXTURE_2D, _glTex);
-	glEnable(GL_BLEND);
-	renderer->setCurrentColor(scene->shadowColor());
-
-	for (TeIntrusivePtr<TeModel> model : scene->zoneModels()) {
-		if (model->meshes().size() > 0 && model->meshes()[0].materials().empty()) {
-			model->meshes()[0].defaultMaterial(TeIntrusivePtr<Te3DTexture>());
-			model->meshes()[0].materials()[0]._enableSomethingDefault0 = true;
-			model->meshes()[0].materials()[0]._diffuseColor = scene->shadowColor();
-		}
-		model->draw();
-	}
-
-	renderer->disableTexture();
-	glDepthMask(true);
-	renderer->enableZBuffer();
+/*static*/
+CharactersShadow *CharactersShadow::makeInstance() {
+	Graphics::RendererType r = g_engine->preferredRendererType();
+
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+	if (r == Graphics::kRendererTypeOpenGL)
+		return new CharactersShadowOpenGL();
+#endif
+
+#if defined(USE_TINYGL)
+	if (r == Graphics::kRendererTypeTinyGL)
+		return new CharactersShadowTinyGL();
+#endif
+	error("Couldn't create CharactersShadow for selected renderer");
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/game/characters_shadow.h b/engines/tetraedge/game/characters_shadow.h
index e0b8c51efe7..46ac1f1cc68 100644
--- a/engines/tetraedge/game/characters_shadow.h
+++ b/engines/tetraedge/game/characters_shadow.h
@@ -31,14 +31,22 @@ class InGameScene;
 class CharactersShadow {
 public:
 	CharactersShadow();
+	virtual ~CharactersShadow() {};
 
 	void create(InGameScene *scene);
 	void createTexture(InGameScene *scene);
 	void destroy();
-	void draw(InGameScene *scene);
+	virtual void draw(InGameScene *scene) = 0;
 	//void drawTexture(); // empty?
 
-private:
+	// Make an instance which is correct for the preferred renderer
+	static CharactersShadow *makeInstance();
+
+protected:
+	virtual void createInternal() = 0;
+	virtual void createTextureInternal(InGameScene *scene) = 0;
+	virtual void deleteTexture() = 0;
+
 	uint _glTex;
 	int _texSize;
 	TeIntrusivePtr<TeCamera> _camera;
diff --git a/engines/tetraedge/game/characters_shadow_opengl.cpp b/engines/tetraedge/game/characters_shadow_opengl.cpp
new file mode 100644
index 00000000000..ccba697b9fe
--- /dev/null
+++ b/engines/tetraedge/game/characters_shadow_opengl.cpp
@@ -0,0 +1,132 @@
+/* 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 "graphics/opengl/system_headers.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/character.h"
+#include "tetraedge/game/characters_shadow_opengl.h"
+#include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_3d_texture_opengl.h"
+
+namespace Tetraedge {
+
+void CharactersShadowOpenGL::createInternal() {
+	Te3DTextureOpenGL::unbind();
+	glGenTextures(1, &_glTex);
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, _texSize, _texSize, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, nullptr);
+}
+
+void CharactersShadowOpenGL::createTextureInternal(InGameScene *scene) {
+	TeRenderer *renderer = g_engine->getRenderer();
+	glClearColor(0.0, 0.0, 0.0, 0.0);
+	renderer->clearBuffer(TeRenderer::ColorAndDepth);
+
+	for (Character *character : scene->_characters) {
+		character->_model->draw();
+	}
+	scene->_character->_model->draw();
+	Te3DTextureOpenGL::unbind();
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _texSize, _texSize);
+	renderer->clearBuffer(TeRenderer::ColorAndDepth);
+}
+
+void CharactersShadowOpenGL::deleteTexture() {
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->disableTexture();
+	glBindTexture(GL_TEXTURE_2D, 0);
+	glDeleteTextures(1, &_glTex);
+}
+
+void CharactersShadowOpenGL::draw(InGameScene *scene) {
+	TeRenderer *renderer = g_engine->getRenderer();
+	glDepthMask(false);
+	renderer->disableZBuffer();
+	renderer->enableTexture();
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	Te3DTextureOpenGL::unbind();
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	glEnable(GL_BLEND);
+	renderer->setCurrentColor(scene->shadowColor());
+
+	TeMatrix4x4 matrix;
+	matrix.translate(TeVector3f32(0.5f, 0.5f, 0.5f));
+	matrix.scale(TeVector3f32(0.5f, 0.5f, 0.5f));
+	matrix = matrix * _camera->projectionMatrix();
+
+	TeMatrix4x4 cammatrix = _camera->worldTransformationMatrix();
+	cammatrix.inverse();
+
+	matrix = matrix * cammatrix;
+
+	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+	float f[4];
+	for (uint i = 0; i < 4; i++)
+		f[i] = matrix(i, 0);
+
+	glTexGenfv(GL_S, GL_EYE_PLANE, f);
+	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+	for (uint i = 0; i < 4; i++)
+		f[i] = matrix(i, 1);
+
+	glTexGenfv(GL_T, GL_EYE_PLANE, f);
+	glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+	for (uint i = 0; i < 4; i++)
+		f[i] = matrix(i, 2);
+
+	glTexGenfv(GL_R, GL_EYE_PLANE, f);
+	glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+	for (uint i = 0; i < 4; i++)
+		f[i] = matrix(i, 3);
+
+	glTexGenfv(GL_Q, GL_EYE_PLANE, f);
+
+	Te3DTextureOpenGL::unbind();
+	glBindTexture(GL_TEXTURE_2D, _glTex);
+	glEnable(GL_BLEND);
+	renderer->setCurrentColor(scene->shadowColor());
+
+	for (TeIntrusivePtr<TeModel> model : scene->zoneModels()) {
+		if (model->meshes().size() > 0 && model->meshes()[0]->materials().empty()) {
+			model->meshes()[0]->defaultMaterial(TeIntrusivePtr<Te3DTexture>());
+			model->meshes()[0]->materials()[0]._enableSomethingDefault0 = true;
+			model->meshes()[0]->materials()[0]._diffuseColor = scene->shadowColor();
+		}
+		model->draw();
+	}
+
+	renderer->disableTexture();
+	glDepthMask(true);
+	renderer->enableZBuffer();
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/characters_shadow_opengl.h b/engines/tetraedge/game/characters_shadow_opengl.h
new file mode 100644
index 00000000000..84d7178c0f5
--- /dev/null
+++ b/engines/tetraedge/game/characters_shadow_opengl.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_CHARACTERS_SHADOW_OPENGL_H
+#define TETRAEDGE_GAME_CHARACTERS_SHADOW_OPENGL_H
+
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+
+#include "tetraedge/game/characters_shadow.h"
+
+namespace Tetraedge {
+
+class InGameScene;
+
+class CharactersShadowOpenGL : public CharactersShadow {
+public:
+	CharactersShadowOpenGL() {};
+
+	void draw(InGameScene *scene) override;
+
+protected:
+	virtual void createInternal() override;
+	virtual void createTextureInternal(InGameScene *scene) override;
+	virtual void deleteTexture() override;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_OPENGL
+
+#endif // TETRAEDGE_GAME_CHARACTERS_SHADOW_OPENGL_H
diff --git a/engines/tetraedge/game/characters_shadow_tinygl.cpp b/engines/tetraedge/game/characters_shadow_tinygl.cpp
new file mode 100644
index 00000000000..278a205c8a4
--- /dev/null
+++ b/engines/tetraedge/game/characters_shadow_tinygl.cpp
@@ -0,0 +1,137 @@
+/* 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 "graphics/tinygl/tinygl.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/game/character.h"
+#include "tetraedge/game/characters_shadow_tinygl.h"
+#include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_3d_texture_tinygl.h"
+
+namespace Tetraedge {
+
+void CharactersShadowTinyGL::createInternal() {
+	Te3DTextureTinyGL::unbind();
+	tglGenTextures(1, &_glTex);
+	tglBindTexture(TGL_TEXTURE_2D, _glTex);
+	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_LINEAR);
+	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_LINEAR);
+	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_CLAMP);
+	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_CLAMP);
+	// TODO: not supported in TGL
+	//tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_LUMINANCE_ALPHA, _texSize, _texSize, 0, TGL_LUMINANCE_ALPHA, TGL_UNSIGNED_BYTE, nullptr);
+}
+
+void CharactersShadowTinyGL::createTextureInternal(InGameScene *scene) {
+	TeRenderer *renderer = g_engine->getRenderer();
+	tglClearColor(0.0, 0.0, 0.0, 0.0);
+	renderer->clearBuffer(TeRenderer::ColorAndDepth);
+
+	for (Character *character : scene->_characters) {
+		character->_model->draw();
+	}
+	scene->_character->_model->draw();
+	Te3DTextureTinyGL::unbind();
+	tglBindTexture(TGL_TEXTURE_2D, _glTex);
+	// TODO: Find TGL equivalent for this..
+	// tglCopyTexSubImage2D(TGL_TEXTURE_2D, 0, 0, 0, 0, 0, _texSize, _texSize);
+	renderer->clearBuffer(TeRenderer::ColorAndDepth);
+}
+
+void CharactersShadowTinyGL::deleteTexture() {
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->disableTexture();
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+	tglDeleteTextures(1, &_glTex);
+}
+
+void CharactersShadowTinyGL::draw(InGameScene *scene) {
+	TeRenderer *renderer = g_engine->getRenderer();
+	tglDepthMask(false);
+	renderer->disableZBuffer();
+	renderer->enableTexture();
+	tglBindTexture(TGL_TEXTURE_2D, _glTex);
+	Te3DTextureTinyGL::unbind();
+	tglBindTexture(TGL_TEXTURE_2D, _glTex);
+	tglEnable(TGL_BLEND);
+	renderer->setCurrentColor(scene->shadowColor());
+
+	TeMatrix4x4 matrix;
+	matrix.translate(TeVector3f32(0.5f, 0.5f, 0.5f));
+	matrix.scale(TeVector3f32(0.5f, 0.5f, 0.5f));
+	matrix = matrix * _camera->projectionMatrix();
+
+	TeMatrix4x4 cammatrix = _camera->worldTransformationMatrix();
+	cammatrix.inverse();
+
+	matrix = matrix * cammatrix;
+
+	/* TODO: Find TGL equivalents for the following block. */
+	/*
+	tglTexGeni(TGL_S, TGL_TEXTURE_GEN_MODE, TGL_EYE_LINEAR);
+
+	float f[4];
+	for (uint i = 0; i < 4; i++)
+		f[i] = matrix(i, 0);
+
+	tglTexGenfv(TGL_S, TGL_EYE_PLANE, f);
+	tglTexGeni(TGL_T, TGL_TEXTURE_GEN_MODE, TGL_EYE_LINEAR);
+
+	for (uint i = 0; i < 4; i++)
+		f[i] = matrix(i, 1);
+
+	tglTexGenfv(TGL_T, TGL_EYE_PLANE, f);
+	tglTexGeni(TGL_R, TGL_TEXTURE_GEN_MODE, TGL_EYE_LINEAR);
+
+	for (uint i = 0; i < 4; i++)
+		f[i] = matrix(i, 2);
+
+	tglTexGenfv(TGL_R, TGL_EYE_PLANE, f);
+	tglTexGeni(TGL_Q, TGL_TEXTURE_GEN_MODE, TGL_EYE_LINEAR);
+
+	for (uint i = 0; i < 4; i++)
+		f[i] = matrix(i, 3);
+
+	tglTexGenfv(TGL_Q, TGL_EYE_PLANE, f);
+	*/
+
+	Te3DTextureTinyGL::unbind();
+	tglBindTexture(TGL_TEXTURE_2D, _glTex);
+	tglEnable(TGL_BLEND);
+	renderer->setCurrentColor(scene->shadowColor());
+
+	for (TeIntrusivePtr<TeModel> model : scene->zoneModels()) {
+		if (model->meshes().size() > 0 && model->meshes()[0]->materials().empty()) {
+			model->meshes()[0]->defaultMaterial(TeIntrusivePtr<Te3DTexture>());
+			model->meshes()[0]->materials()[0]._enableSomethingDefault0 = true;
+			model->meshes()[0]->materials()[0]._diffuseColor = scene->shadowColor();
+		}
+		model->draw();
+	}
+
+	renderer->disableTexture();
+	tglDepthMask(true);
+	renderer->enableZBuffer();
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/game/characters_shadow_tinygl.h b/engines/tetraedge/game/characters_shadow_tinygl.h
new file mode 100644
index 00000000000..165ccdaa0cc
--- /dev/null
+++ b/engines/tetraedge/game/characters_shadow_tinygl.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_CHARACTERS_SHADOW_TINYGL_H
+#define TETRAEDGE_GAME_CHARACTERS_SHADOW_TINYGL_H
+
+#if defined(USE_TINYGL)
+
+#include "tetraedge/game/characters_shadow.h"
+
+namespace Tetraedge {
+
+class InGameScene;
+
+class CharactersShadowTinyGL : public CharactersShadow {
+public:
+	CharactersShadowTinyGL() {};
+
+	void draw(InGameScene *scene) override;
+
+protected:
+	virtual void createInternal() override;
+	virtual void createTextureInternal(InGameScene *scene) override;
+	virtual void deleteTexture() override;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_TINYGL
+
+#endif // TETRAEDGE_GAME_CHARACTERS_SHADOW_TINYGL_H
diff --git a/engines/tetraedge/game/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 4270975a61e..0e1416d0f44 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -220,17 +220,18 @@ void InGameScene::close() {
 
 void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
 	TeIntrusivePtr<TeModel> model = new TeModel();
-	model->meshes().resize(1);
+	model->meshes().clear();
+	model->meshes().push_back(Common::SharedPtr<TeMesh>(TeMesh::makeInstance()));
 	model->setName("shadowReceiving");
 	model->setPosition(zone->position());
 	model->setRotation(zone->rotation());
 	model->setScale(zone->scale());
 	unsigned long nverticies = zone->verticies().size();
-	model->meshes()[0].setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
+	model->meshes()[0]->setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
 	for (uint i = 0; i < nverticies; i++) {
-		model->meshes()[0].setIndex(i, i);
-		model->meshes()[0].setVertex(i, zone->verticies()[i]);
-		model->meshes()[0].setNormal(i, TeVector3f32(0, 0, 1));
+		model->meshes()[0]->setIndex(i, i);
+		model->meshes()[0]->setVertex(i, zone->verticies()[i]);
+		model->meshes()[0]->setNormal(i, TeVector3f32(0, 0, 1));
 	}
 	_zoneModels.push_back(model);
 }
@@ -293,7 +294,7 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 	TeVector2f32 vec2;
 	TeQuaternion rot;
 	TeColor col;
-	TeMesh mesh;
+	Common::SharedPtr<TeMesh> mesh(TeMesh::makeInstance());
 
 	assert(pickmesh);
 
@@ -312,30 +313,30 @@ void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<Te
 	if (indexcount > 100000 || vertexcount > 100000)
 		error("InGameScene::deserializeModel: Unxpected counts %d %d", indexcount, vertexcount);
 
-	mesh.setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
+	mesh->setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
 	for (uint i = 0; i < indexcount; i++)
-		mesh.setIndex(i, stream.readUint32LE());
+		mesh->setIndex(i, stream.readUint32LE());
 
 	for (uint i = 0; i < vertexcount; i++) {
 		TeVector3f32::deserialize(stream, vec);
-		mesh.setVertex(i, vec);
+		mesh->setVertex(i, vec);
 	}
 	for (uint i = 0; i < vertexcount; i++) {
 		TeVector3f32::deserialize(stream, vec);
-		mesh.setNormal(i, vec);
+		mesh->setNormal(i, vec);
 	}
 	for (uint i = 0; i < vertexcount; i++) {
 		TeVector2f32::deserialize(stream, vec2);
-		mesh.setTextureUV(i, vec2);
+		mesh->setTextureUV(i, vec2);
 	}
 	for (uint i = 0; i < vertexcount; i++) {
 		col.deserialize(stream);
-		mesh.setColor(i, col);
+		mesh->setColor(i, col);
 	}
 
 	pickmesh->setNbTriangles(indexcount / 3);
 	for (uint i = 0; i < indexcount; i++) {
-		vec = mesh.vertex(mesh.index(i));
+		vec = mesh->vertex(mesh->index(i));
 		pickmesh->verticies()[i] = vec;
 	}
 	model->addMesh(mesh);
@@ -368,7 +369,7 @@ void InGameScene::draw() {
 
 	TeLight::updateGlobal();
 	for (uint i = 0; i < _lights.size(); i++)
-		_lights[i].update(i);
+		_lights[i]->update(i);
 
 	TeCamera::restore();
 }
@@ -556,9 +557,9 @@ bool InGameScene::load(const Common::Path &path) {
 		}
 	}
 	if (!_lights.empty()) {
-		TeLight::disableAll();
+		g_engine->getRenderer()->disableAllLights();
 		for (uint i = 0; i < _lights.size(); i++) {
-			_lights[i].disable(i);
+			_lights[i]->disable(i);
 		}
 		_lights.clear();
 	}
@@ -661,7 +662,7 @@ bool InGameScene::load(const Common::Path &path) {
 	for (TeFreeMoveZone *zone : _freeMoveZones) {
 		convertPathToMesh(zone);
 	}
-	_charactersShadow = new CharactersShadow();
+	_charactersShadow = CharactersShadow::makeInstance();
 	_charactersShadow->create(this);
 	onMainWindowSizeChanged();
 
@@ -701,9 +702,9 @@ bool InGameScene::loadLights(const Common::Path &path) {
 	_shadowNearPlane = parser.getShadowNearPlane();
 	_shadowFov = parser.getShadowFov();
 
-	TeLight::enableAll();
+	g_engine->getRenderer()->enableAllLights();
 	for (uint i = 0; i < _lights.size(); i++) {
-		_lights[i].enable(i);
+		_lights[i]->enable(i);
 	}
 
 #if DEBUG_LIGHTS
@@ -752,9 +753,9 @@ bool InGameScene::loadObjectMaterials(const Common::String &name) {
 
 		Common::Path mpath = _loadedPath.getParent().join(name).join(obj._name + ".png");
 		if (img.load(mpath)) {
-			Te3DTexture *tex = new Te3DTexture();
+			Te3DTexture *tex = Te3DTexture::makeInstance();
 			tex->load(img);
-			obj._model->meshes()[0].defaultMaterial(tex);
+			obj._model->meshes()[0]->defaultMaterial(tex);
 			retval = true;
 		}
 	}
@@ -960,7 +961,7 @@ TeLight *InGameScene::shadowLight() {
 	if (_shadowLightNo == -1) {
 		return nullptr;
 	}
-	return &_lights[_shadowLightNo];
+	return _lights[_shadowLightNo].get();
 }
 
 void InGameScene::setImagePathMarker(const Common::String &markerName, const Common::String &path) {
diff --git a/engines/tetraedge/game/in_game_scene.h b/engines/tetraedge/game/in_game_scene.h
index 2d6fdc46eb0..7f799b6059c 100644
--- a/engines/tetraedge/game/in_game_scene.h
+++ b/engines/tetraedge/game/in_game_scene.h
@@ -197,7 +197,7 @@ public:
 	Common::Array<Object3D *> object3Ds() { return _object3Ds; }
 	void setWaitTime(float usecs) { _waitTime = usecs; }
 	TeTimer &waitTimeTimer() { return _waitTimeTimer; }
-	Common::Array<TeLight> &lights() { return _lights; }
+	Common::Array<Common::SharedPtr<TeLight>> &lights() { return _lights; }
 
 
 private:
@@ -239,7 +239,7 @@ private:
 	TeLuaGUI _markerGui;
 	TeLuaGUI _hitObjectGui;
 
-	Common::Array<TeLight> _lights;
+	Common::Array<Common::SharedPtr<TeLight>> _lights;
 
 	TeVector2f32 _someScrollVector;
 	TeVector2f32 _viewportSize;
diff --git a/engines/tetraedge/game/lua_binds.cpp b/engines/tetraedge/game/lua_binds.cpp
index 01a57c56126..4b37235aa2d 100644
--- a/engines/tetraedge/game/lua_binds.cpp
+++ b/engines/tetraedge/game/lua_binds.cpp
@@ -993,11 +993,11 @@ static void EnableLight(uint lightno, bool enable) {
 	if (lightno > game->scene().lights().size()) {
 		error("[EnableLight] Light not found %d", lightno);
 	}
-	TeLight &light = game->scene().lights()[lightno];
+	Common::SharedPtr<TeLight> light = game->scene().lights()[lightno];
 	if (enable)
-		light.enable(lightno);
+		light->enable(lightno);
 	else
-		light.disable(lightno);
+		light->disable(lightno);
 }
 
 static int tolua_ExportedFunctions_EnableLight00(lua_State *L) {
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.cpp b/engines/tetraedge/game/scene_lights_xml_parser.cpp
index 81043ac87a7..c5fb3727518 100644
--- a/engines/tetraedge/game/scene_lights_xml_parser.cpp
+++ b/engines/tetraedge/game/scene_lights_xml_parser.cpp
@@ -56,7 +56,7 @@ bool SceneLightsXmlParser::parserCallback_Ambient(ParserNode *node) {
 	if (_parent == Parent_Global) {
 		TeLight::setGlobalAmbient(col);
 	} else {
-		_lights->back().setAmbient(col);
+		_lights->back()->setAmbient(col);
 	}
 	return true;
 }
@@ -68,13 +68,13 @@ bool SceneLightsXmlParser::parserCallback_Lights(ParserNode *node) {
 
 bool SceneLightsXmlParser::parserCallback_Light(ParserNode *node) {
 	_parent = Parent_Light;
-	_lights->push_back(TeLight());
+	_lights->push_back(Common::SharedPtr<TeLight>(TeLight::makeInstance()));
 	TeLightType ltype = TeLightType::LightTypeDirectional;
 	if (node->values["Type"] == "Spot")
 		ltype = TeLightType::LightTypeSpot;
 	else if (node->values["Type"] == "Point")
 		ltype = TeLightType::LightTypePoint;
-	_lights->back().setType(ltype);
+	_lights->back()->setType(ltype);
 	return true;
 }
 
@@ -82,14 +82,14 @@ bool SceneLightsXmlParser::parserCallback_Position(ParserNode *node) {
 	float x = atof(node->values["x"].c_str());
 	float y = atof(node->values["y"].c_str());
 	float z = atof(node->values["z"].c_str());
-	_lights->back().setPosition3d(TeVector3f32(x, y, z));
+	_lights->back()->setPosition3d(TeVector3f32(x, y, z));
 	return true;
 }
 
 bool SceneLightsXmlParser::parserCallback_Direction(ParserNode *node) {
 	float h = (atof(node->values["h"].c_str()) * M_PI) / 180.0;
 	float v = (atof(node->values["v"].c_str()) * M_PI) / 180.0;
-	_lights->back().setPositionRadial(TeVector2f32(h, v));
+	_lights->back()->setPositionRadial(TeVector2f32(h, v));
 	return true;
 }
 
@@ -98,7 +98,7 @@ bool SceneLightsXmlParser::parserCallback_Diffuse(ParserNode *node) {
 	if (!parseCol(node, col))
 		return false;
 
-	_lights->back().setDiffuse(col);
+	_lights->back()->setDiffuse(col);
 	return true;
 }
 
@@ -107,7 +107,7 @@ bool SceneLightsXmlParser::parserCallback_Specular(ParserNode *node) {
 	if (!parseCol(node, col))
 		return false;
 
-	_lights->back().setSpecular(col);
+	_lights->back()->setSpecular(col);
 	return true;
 }
 
@@ -117,9 +117,9 @@ bool SceneLightsXmlParser::parserCallback_Attenuation(ParserNode *node) {
 	float q = atof(node->values["quadratic"].c_str());
 	if (c < 0 || l < 0 || q < 0)
 		warning("Loaded invalid lighting attenuation vals %f %f %f", c, l, q);
-	_lights->back().setConstAtten(c);
-	_lights->back().setLinearAtten(l);
-	_lights->back().setQuadraticAtten(q);
+	_lights->back()->setConstAtten(c);
+	_lights->back()->setLinearAtten(l);
+	_lights->back()->setQuadraticAtten(q);
 	return true;
 }
 
@@ -127,7 +127,7 @@ bool SceneLightsXmlParser::parserCallback_Cutoff(ParserNode *node) {
 	float cutoff = atof(node->values["value"].c_str());
 	if (cutoff < 0.0f || (cutoff > 90.0f && cutoff != 180.0f))
 		warning("Loaded invalid lighting cutoff value %f", cutoff);
-	_lights->back().setCutoff((cutoff * M_PI) / 180.0);
+	_lights->back()->setCutoff((cutoff * M_PI) / 180.0);
 	return true;
 }
 
@@ -135,12 +135,12 @@ bool SceneLightsXmlParser::parserCallback_Exponent(ParserNode *node) {
 	float expon = atof(node->values["value"].c_str());
 	if (expon < 0.0f || expon > 128.0f)
 		warning("Loaded invalid lighting exponent value %f", expon);
-	_lights->back().setExponent(expon);
+	_lights->back()->setExponent(expon);
 	return true;
 }
 
 bool SceneLightsXmlParser::parserCallback_DisplaySize(ParserNode *node) {
-	_lights->back().setDisplaySize(atof(node->values["value"].c_str()));
+	_lights->back()->setDisplaySize(atof(node->values["value"].c_str()));
 	return true;
 }
 
diff --git a/engines/tetraedge/game/scene_lights_xml_parser.h b/engines/tetraedge/game/scene_lights_xml_parser.h
index a6159b0b099..ef701e3c8f7 100644
--- a/engines/tetraedge/game/scene_lights_xml_parser.h
+++ b/engines/tetraedge/game/scene_lights_xml_parser.h
@@ -31,7 +31,7 @@ namespace Tetraedge {
 
 class SceneLightsXmlParser : public Common::XMLParser {
 public:
-	void setLightArray(Common::Array<TeLight> *lights) {
+	void setLightArray(Common::Array<Common::SharedPtr<TeLight>> *lights) {
 		_lights = lights;
 	}
 	TeColor getShadowColor() { return _shadowColor; }
@@ -115,7 +115,7 @@ public:
 	} PARSER_END()
 
 private:
-	Common::Array<TeLight> *_lights;
+	Common::Array<Common::SharedPtr<TeLight>> *_lights;
 
 	enum ParentNodeType {
 		Parent_Global,
diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index 7f55a5eb390..635dd0450d6 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -10,6 +10,7 @@ MODULE_OBJS := \
 	game/character.o \
 	game/character_settings_xml_parser.o \
 	game/characters_shadow.o \
+	game/characters_shadow_opengl.o \
 	game/confirm.o \
 	game/credits.o \
 	game/dialog2.o \
@@ -42,6 +43,7 @@ MODULE_OBJS := \
 	te/micropather.o \
 	te/te_3d_object2.o \
 	te/te_3d_texture.o \
+	te/te_3d_texture_opengl.o \
 	te/te_act_zone.o \
 	te/te_animation.o \
 	te/te_bezier_curve.o \
@@ -66,6 +68,7 @@ MODULE_OBJS := \
 	te/te_jpeg.o \
 	te/te_layout.o \
 	te/te_light.o \
+	te/te_light_opengl.o \
 	te/te_list_layout.o \
 	te/te_lua_context.o \
 	te/te_lua_gui.o \
@@ -76,6 +79,7 @@ MODULE_OBJS := \
 	te/te_matricies_stack.o \
 	te/te_matrix4x4.o \
 	te/te_mesh.o \
+	te/te_mesh_opengl.o \
 	te/te_model.o \
 	te/te_model_animation.o \
 	te/te_model_vertex_animation.o \
@@ -90,6 +94,7 @@ MODULE_OBJS := \
 	te/te_ray_intersection.o \
 	te/te_real_timer.o \
 	te/te_renderer.o \
+	te/te_renderer_opengl.o \
 	te/te_resource.o \
 	te/te_resource_manager.o \
 	te/te_scene.o \
@@ -114,6 +119,16 @@ MODULE_OBJS := \
 	te/te_xml_gui.o \
 	metaengine.o
 
+ifdef USE_TINYGL
+MODULE_OBJS += \
+	game/characters_shadow_tinygl.o \
+	te/te_3d_texture_tinygl.o \
+	te/te_light_tinygl.o \
+	te/te_mesh_tinygl.o \
+	te/te_renderer_tinygl.o
+endif
+
+
 # This module can be built as a plugin
 ifeq ($(ENABLE_TETRAEDGE), DYNAMIC_PLUGIN)
 PLUGIN := 1
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index 8d4e7a9a00d..c3d25bcfb71 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -23,96 +23,18 @@
 
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/te/te_3d_texture.h"
+#include "tetraedge/te/te_3d_texture_opengl.h"
+#include "tetraedge/te/te_3d_texture_tinygl.h"
 #include "tetraedge/te/te_resource_manager.h"
 #include "tetraedge/te/te_renderer.h"
 
 namespace Tetraedge {
 
-static const uint NO_TEXTURE = 0xffffffff;
-
-Te3DTexture::Te3DTexture() : _glTexture(NO_TEXTURE), _createdTexture(false),
-_numFrames(1), _frameRate(0), _format(TeImage::INVALID)/*, _glPixelFormat(GL_INVALID_ENUM)*/ {
-	create();
+Te3DTexture::Te3DTexture() : _createdTexture(false),
+_numFrames(1), _frameRate(0), _format(TeImage::INVALID) {
 }
 
 Te3DTexture::~Te3DTexture() {
-	destroy();
-}
-
-void Te3DTexture::bind() const {
-	TeRenderer *renderer = g_engine->getRenderer();
-	glBindTexture(GL_TEXTURE_2D, _glTexture);
-	renderer->setMatrixMode(TeRenderer::MM_GL_TEXTURE);
-	renderer->loadMatrix(_matrix);
-	renderer->loadCurrentMatrixToGL();
-	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
-}
-
-void Te3DTexture::copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y) {
-	_matrix.setToIdentity();
-	const TeVector3f32 texScale((float)_width / _texWidth, (float)_height / _texHeight, 1.0);
-	_matrix.scale(texScale);
-	const TeVector3f32 offset((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0);
-	_matrix.translate(offset);
-	const TeVector3f32 borderScale(
-			1.0 - (float)(_rightBorder + _leftBorder) / (float)_width,
-			1.0 - (float)(_topBorder + _btmBorder) / (float)_height, 1.0);
-	_matrix.scale(borderScale);
-	bind();
-	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, _texWidth, _texHeight);
-}
-
-void Te3DTexture::writeTo(Graphics::Surface &surf) {
-	Graphics::Surface fullTex;
-	fullTex.create(_texWidth, _texHeight, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
-	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullTex.getPixels());
-	surf.create(_width, _height, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
-	surf.copyRectToSurface(fullTex, 0, 0, Common::Rect(_width, _height));
-	fullTex.free();
-}
-
-void Te3DTexture::create() {
-	_flipY = false;
-	_leftBorder = _btmBorder = _texWidth = _texHeight = 0;
-	_rightBorder = _topBorder = _width = _height = 0;
-	_format = TeImage::INVALID;
-	_loaded = false;
-	if (!_createdTexture)
-		glGenTextures(1, &_glTexture);
-	if (_glTexture == NO_TEXTURE) {
-		_createdTexture = false;
-		return;
-	}
-
-	_createdTexture = true;
-	glBindTexture(GL_TEXTURE_2D, _glTexture);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-}
-
-void Te3DTexture::destroy() {
-	if (_createdTexture) {
-		glDeleteTextures(1, &_glTexture);
-	}
-	_createdTexture = false;
-	_loaded = false;
-	_glTexture = NO_TEXTURE;
-}
-
-void Te3DTexture::forceTexData(uint gltexture, uint xsize, uint ysize) {
-	if (_glTexture != 0xffffffff) {
-		if (_createdTexture)
-			glDeleteTextures(1, &_glTexture);
-		_createdTexture = false;
-		_loaded = false;
-	}
-	_glTexture = gltexture;
-	_width = xsize;
-	_height = ysize;
-	_texWidth = xsize;
-	_texHeight = ysize;
 }
 
 bool Te3DTexture::hasAlpha() const {
@@ -127,13 +49,13 @@ TeIntrusivePtr<Te3DTexture> Te3DTexture::load2(const Common::Path &path, uint si
 
 	TeResourceManager *resMgr = g_engine->getResourceManager();
 	if (!resMgr->exists(fullPath)) {
-		TeIntrusivePtr<Te3DTexture> retval(new Te3DTexture());
+		TeIntrusivePtr<Te3DTexture> retval(makeInstance());
 		retval->load(path);
 		retval->setAccessName(fullPath);
 		resMgr->addResource(retval.get());
 		return retval;
 	} else {
-		return resMgr->getResource<Te3DTexture>(fullPath);
+		return resMgr->getResourceOrMakeInstance<Te3DTexture>(fullPath);
 	}
 }
 
@@ -146,67 +68,6 @@ bool Te3DTexture::load(const Common::Path &path) {
 	return true;
 }
 
-bool Te3DTexture::load(const TeImage &img) {
-	Common::Path accessName = img.getAccessName();
-	setAccessName(accessName.append(".3dtex"));
-
-	_width = img.w;
-	_height = img.h;
-	_format = img.teFormat();
-
-	// TODO? set some other fields from the image here.
-	// for now just set some good defaults.
-	_flipY = true;    //img._flipY;
-	_leftBorder = 0;  //img._leftBorder;
-	_btmBorder = 0;   //img._btmBorder;
-	_rightBorder = 0; //img._rightBorder;
-	_topBorder = 0;   //img._topBorder;
-
-	const TeVector2s32 optimizedSz = optimisedSize(img.bufSize());
-	_texWidth = optimizedSz._x;
-	_texHeight = optimizedSz._y;
-
-	glBindTexture(GL_TEXTURE_2D, _glTexture);
-	glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
-	glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
-	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
-	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
-	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
-	const void *imgdata = img.getPixels();
-	if (_format == TeImage::RGB8) {
-		/*GLenum glpxformat = GL_RGB;
-		if (_glPixelFormat != GL_INVALID_ENUM) {
-			glpxformat = _glPixelFormat;
-		}*/
-		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 glpxformat = GL_RGBA8;
-		if (_glPixelFormat != GL_INVALID_ENUM) {
-			glpxformat = _glPixelFormat;
-		}*/
-		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);
-	} else {
-		warning("Te3DTexture::load can't send image format %d to GL.", _format);
-	}
-
-	_matrix.setToIdentity();
-
-	_matrix.scale(TeVector3f32((float)_width / _texWidth, (float)_height / _texHeight, 1.0f));
-	_matrix.translate(TeVector3f32((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0f));
-	_matrix.scale(TeVector3f32(1.0 - (float)(_rightBorder + _leftBorder) / _width,
-					1.0 - (float)(_topBorder + _btmBorder) / _height, 1.0f));
-	if (_flipY) {
-		_matrix.translate(TeVector3f32(0.0f, 1.0f, 0.0f));
-		_matrix.scale(TeVector3f32(1.0f, -1.0f, 1.0f));
-	}
-	_loaded = true;
-	return true;
-}
-
 /*static*/
 TeVector2s32 Te3DTexture::optimisedSize(const TeVector2s32 &size) {
 	//
@@ -244,44 +105,19 @@ TeVector2s32 Te3DTexture::optimisedSize(const TeVector2s32 &size) {
 }
 
 /*static*/
-void Te3DTexture::unbind() {
-	TeRenderer *renderer = g_engine->getRenderer();
-	renderer->setMatrixMode(TeRenderer::MM_GL_TEXTURE);
-	renderer->loadIdentityMatrix();
-	renderer->loadCurrentMatrixToGL();
-	glBindTexture(GL_TEXTURE_2D, 0);
-	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
-}
-
-bool Te3DTexture::unload() {
-	glBindTexture(GL_TEXTURE_2D, _glTexture);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
-	_loaded = false;
-	return true;
-}
-
-void Te3DTexture::update(const TeImage &img, uint xoff, uint yoff) {
-	if (!img.w || !img.h)
-		return;
-
-	setAccessName(img.getAccessName().append(".3dtex"));
-	glBindTexture(GL_TEXTURE_2D, _glTexture);
-	glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
-	glPixelStorei(GL_UNPACK_LSB_FIRST, 0);
-	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
-	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
-	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
-	const void *imgdata = img.getPixels();
-	if (_format == TeImage::RGB8) {
-		glTexSubImage2D(GL_TEXTURE_2D, 0, xoff, yoff, img.w, img.h, GL_RGB, GL_UNSIGNED_BYTE, imgdata);
-	} else if (_format == TeImage::RGBA8) {
-		glTexSubImage2D(GL_TEXTURE_2D, 0, xoff, yoff, img.w, img.h, GL_RGBA, GL_UNSIGNED_BYTE, imgdata);
-	} else {
-		warning("Te3DTexture::update can't send image format %d to GL.", _format);
-	}
-	return;
+Te3DTexture *Te3DTexture::makeInstance() {
+	Graphics::RendererType r = g_engine->preferredRendererType();
+
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+	if (r == Graphics::kRendererTypeOpenGL)
+		return new Te3DTextureOpenGL();
+#endif
+
+#if defined(USE_TINYGL)
+	if (r == Graphics::kRendererTypeTinyGL)
+		return new Te3DTextureTinyGL();
+#endif
+	error("Couldn't create Te3DTexture for selected renderer");
 }
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_3d_texture.h b/engines/tetraedge/te/te_3d_texture.h
index b217d8cf6b8..3182853e967 100644
--- a/engines/tetraedge/te/te_3d_texture.h
+++ b/engines/tetraedge/te/te_3d_texture.h
@@ -39,32 +39,32 @@ public:
 	Te3DTexture();
 	virtual ~Te3DTexture();
 
-	void bind() const;
-	void copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y);
-	void create();
-	void destroy();
-
-	void forceTexData(uint gltexture, uint xsize, uint ysize);
+	virtual void bind() const = 0;
+	virtual void copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y) = 0;
+	virtual void create() = 0;
+	virtual void destroy() = 0;
+	virtual void forceTexData(uint gltexture, uint xsize, uint ysize) = 0;
 
 	TeImage::Format getFormat() const { return _format; }
 	bool hasAlpha() const;
 
 	bool load(const Common::Path &path);
-	bool load(const TeImage &img);
+	virtual bool load(const TeImage &img) = 0;
 	static TeIntrusivePtr<Te3DTexture> load2(const Common::Path &path, uint size);
 
 	static TeVector2s32 optimisedSize(const TeVector2s32 &size);
 
-	static void unbind();
-	bool unload();
-	void update(const TeImage &img, uint xoff, uint yoff);
+	virtual bool unload() = 0;
+	virtual void update(const TeImage &img, uint xoff, uint yoff) = 0;
 
-	void writeTo(Graphics::Surface &surf);
+	virtual void writeTo(Graphics::Surface &surf) = 0;
 
 	uint width() const { return _width; }
 	uint height() const { return _height; }
 
-private:
+	static Te3DTexture *makeInstance();
+
+protected:
 	uint _width;
 	uint _height;
 	int _numFrames;
@@ -72,8 +72,6 @@ private:
 	TeImage::Format _format;
 	bool _createdTexture;
 	bool _loaded;
-	uint _glTexture;
-	//uint _glPixelFormat;
 	TeMatrix4x4 _matrix;
 
 	uint _texWidth;
diff --git a/engines/tetraedge/te/te_3d_texture_opengl.cpp b/engines/tetraedge/te/te_3d_texture_opengl.cpp
new file mode 100644
index 00000000000..492c16320a0
--- /dev/null
+++ b/engines/tetraedge/te/te_3d_texture_opengl.cpp
@@ -0,0 +1,219 @@
+/* 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 "graphics/opengl/system_headers.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/te/te_3d_texture_opengl.h"
+#include "tetraedge/te/te_resource_manager.h"
+#include "tetraedge/te/te_renderer.h"
+
+namespace Tetraedge {
+
+static const uint NO_TEXTURE = 0xffffffff;
+
+Te3DTextureOpenGL::Te3DTextureOpenGL() : _glTexture(NO_TEXTURE)/*, _glPixelFormat(GL_INVALID_ENUM)*/ {
+	create();
+}
+
+Te3DTextureOpenGL::~Te3DTextureOpenGL() {
+	destroy();
+}
+
+void Te3DTextureOpenGL::bind() const {
+	TeRenderer *renderer = g_engine->getRenderer();
+	glBindTexture(GL_TEXTURE_2D, _glTexture);
+	renderer->setMatrixMode(TeRenderer::MM_GL_TEXTURE);
+	renderer->loadMatrix(_matrix);
+	renderer->loadCurrentMatrixToGL();
+	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
+}
+
+void Te3DTextureOpenGL::copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y) {
+	_matrix.setToIdentity();
+	const TeVector3f32 texScale((float)_width / _texWidth, (float)_height / _texHeight, 1.0);
+	_matrix.scale(texScale);
+	const TeVector3f32 offset((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0);
+	_matrix.translate(offset);
+	const TeVector3f32 borderScale(
+			1.0 - (float)(_rightBorder + _leftBorder) / (float)_width,
+			1.0 - (float)(_topBorder + _btmBorder) / (float)_height, 1.0);
+	_matrix.scale(borderScale);
+	bind();
+	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, x, y, _texWidth, _texHeight);
+}
+
+void Te3DTextureOpenGL::writeTo(Graphics::Surface &surf) {
+	Graphics::Surface fullTex;
+	fullTex.create(_texWidth, _texHeight, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, fullTex.getPixels());
+	surf.create(_width, _height, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+	surf.copyRectToSurface(fullTex, 0, 0, Common::Rect(_width, _height));
+	fullTex.free();
+}
+
+void Te3DTextureOpenGL::create() {
+	_flipY = false;
+	_leftBorder = _btmBorder = _texWidth = _texHeight = 0;
+	_rightBorder = _topBorder = _width = _height = 0;
+	_format = TeImage::INVALID;
+	_loaded = false;
+	if (!_createdTexture)
+		glGenTextures(1, &_glTexture);
+	if (_glTexture == NO_TEXTURE) {
+		_createdTexture = false;
+		return;
+	}
+
+	_createdTexture = true;
+	glBindTexture(GL_TEXTURE_2D, _glTexture);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+void Te3DTextureOpenGL::destroy() {
+	if (_createdTexture) {
+		glDeleteTextures(1, &_glTexture);
+	}
+	_createdTexture = false;
+	_loaded = false;
+	_glTexture = NO_TEXTURE;
+}
+
+void Te3DTextureOpenGL::forceTexData(uint gltexture, uint xsize, uint ysize) {
+	if (_glTexture != 0xffffffff) {
+		if (_createdTexture)
+			glDeleteTextures(1, &_glTexture);
+		_createdTexture = false;
+		_loaded = false;
+	}
+	_glTexture = gltexture;
+	_width = xsize;
+	_height = ysize;
+	_texWidth = xsize;
+	_texHeight = ysize;
+}
+
+bool Te3DTextureOpenGL::load(const TeImage &img) {
+	Common::Path accessName = img.getAccessName();
+	setAccessName(accessName.append(".3dtex"));
+
+	_width = img.w;
+	_height = img.h;
+	_format = img.teFormat();
+
+	// TODO? set some other fields from the image here.
+	// for now just set some good defaults.
+	_flipY = true;    //img._flipY;
+	_leftBorder = 0;  //img._leftBorder;
+	_btmBorder = 0;   //img._btmBorder;
+	_rightBorder = 0; //img._rightBorder;
+	_topBorder = 0;   //img._topBorder;
+
+	const TeVector2s32 optimizedSz = optimisedSize(img.bufSize());
+	_texWidth = optimizedSz._x;
+	_texHeight = optimizedSz._y;
+
+	glBindTexture(GL_TEXTURE_2D, _glTexture);
+	glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
+	glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+	const void *imgdata = img.getPixels();
+	if (_format == TeImage::RGB8) {
+		/*GLenum glpxformat = GL_RGB;
+		if (_glPixelFormat != GL_INVALID_ENUM) {
+			glpxformat = _glPixelFormat;
+		}*/
+		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 glpxformat = GL_RGBA8;
+		if (_glPixelFormat != GL_INVALID_ENUM) {
+			glpxformat = _glPixelFormat;
+		}*/
+		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);
+	} else {
+		warning("Te3DTexture::load can't send image format %d to GL.", _format);
+	}
+
+	_matrix.setToIdentity();
+
+	_matrix.scale(TeVector3f32((float)_width / _texWidth, (float)_height / _texHeight, 1.0f));
+	_matrix.translate(TeVector3f32((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0f));
+	_matrix.scale(TeVector3f32(1.0 - (float)(_rightBorder + _leftBorder) / _width,
+					1.0 - (float)(_topBorder + _btmBorder) / _height, 1.0f));
+	if (_flipY) {
+		_matrix.translate(TeVector3f32(0.0f, 1.0f, 0.0f));
+		_matrix.scale(TeVector3f32(1.0f, -1.0f, 1.0f));
+	}
+	_loaded = true;
+	return true;
+}
+
+/*static*/
+void Te3DTextureOpenGL::unbind() {
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->setMatrixMode(TeRenderer::MM_GL_TEXTURE);
+	renderer->loadIdentityMatrix();
+	renderer->loadCurrentMatrixToGL();
+	glBindTexture(GL_TEXTURE_2D, 0);
+	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
+}
+
+bool Te3DTextureOpenGL::unload() {
+	glBindTexture(GL_TEXTURE_2D, _glTexture);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+	_loaded = false;
+	return true;
+}
+
+void Te3DTextureOpenGL::update(const TeImage &img, uint xoff, uint yoff) {
+	if (!img.w || !img.h)
+		return;
+
+	setAccessName(img.getAccessName().append(".3dtex"));
+	glBindTexture(GL_TEXTURE_2D, _glTexture);
+	glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+	glPixelStorei(GL_UNPACK_LSB_FIRST, 0);
+	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+	glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+	glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+	const void *imgdata = img.getPixels();
+	if (_format == TeImage::RGB8) {
+		glTexSubImage2D(GL_TEXTURE_2D, 0, xoff, yoff, img.w, img.h, GL_RGB, GL_UNSIGNED_BYTE, imgdata);
+	} else if (_format == TeImage::RGBA8) {
+		glTexSubImage2D(GL_TEXTURE_2D, 0, xoff, yoff, img.w, img.h, GL_RGBA, GL_UNSIGNED_BYTE, imgdata);
+	} else {
+		warning("Te3DTexture::update can't send image format %d to GL.", _format);
+	}
+	return;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_3d_texture_opengl.h b/engines/tetraedge/te/te_3d_texture_opengl.h
new file mode 100644
index 00000000000..ad2a0801f75
--- /dev/null
+++ b/engines/tetraedge/te/te_3d_texture_opengl.h
@@ -0,0 +1,60 @@
+/* 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_TE_TE_3D_TEXTURE_OPENGL_H
+#define TETRAEDGE_TE_TE_3D_TEXTURE_OPENGL_H
+
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+
+#include "tetraedge/te/te_3d_texture.h"
+
+namespace Tetraedge {
+
+class Te3DTextureOpenGL : public Te3DTexture {
+public:
+	Te3DTextureOpenGL();
+	virtual ~Te3DTextureOpenGL();
+
+	void bind() const override;
+	void copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y) override;
+	void create() override;
+	void destroy() override;
+	void forceTexData(uint gltexture, uint xsize, uint ysize) override;
+
+	bool load(const TeImage &img) override;
+
+	static void unbind();
+	bool unload() override;
+	void update(const TeImage &img, uint xoff, uint yoff) override;
+
+	void writeTo(Graphics::Surface &surf) override;
+
+private:
+	uint _glTexture;
+	//uint _glPixelFormat;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_OPENGL
+
+#endif // TETRAEDGE_TE_TE_3D_TEXTURE_OPENGL_H
diff --git a/engines/tetraedge/te/te_3d_texture_tinygl.cpp b/engines/tetraedge/te/te_3d_texture_tinygl.cpp
new file mode 100644
index 00000000000..ab7292c46a5
--- /dev/null
+++ b/engines/tetraedge/te/te_3d_texture_tinygl.cpp
@@ -0,0 +1,220 @@
+/* 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 "graphics/tinygl/tinygl.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/te/te_3d_texture_tinygl.h"
+#include "tetraedge/te/te_resource_manager.h"
+#include "tetraedge/te/te_renderer.h"
+
+namespace Tetraedge {
+
+static const uint NO_TEXTURE = 0xffffffff;
+
+Te3DTextureTinyGL::Te3DTextureTinyGL() : _glTexture(NO_TEXTURE)/*, _glPixelFormat(TGL_INVALID_ENUM)*/ {
+	create();
+}
+
+Te3DTextureTinyGL::~Te3DTextureTinyGL() {
+	destroy();
+}
+
+void Te3DTextureTinyGL::bind() const {
+	TeRenderer *renderer = g_engine->getRenderer();
+	tglBindTexture(TGL_TEXTURE_2D, _glTexture);
+	renderer->setMatrixMode(TeRenderer::MM_GL_TEXTURE);
+	renderer->loadMatrix(_matrix);
+	renderer->loadCurrentMatrixToGL();
+	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
+}
+
+void Te3DTextureTinyGL::copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y) {
+	_matrix.setToIdentity();
+	const TeVector3f32 texScale((float)_width / _texWidth, (float)_height / _texHeight, 1.0);
+	_matrix.scale(texScale);
+	const TeVector3f32 offset((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0);
+	_matrix.translate(offset);
+	const TeVector3f32 borderScale(
+			1.0 - (float)(_rightBorder + _leftBorder) / (float)_width,
+			1.0 - (float)(_topBorder + _btmBorder) / (float)_height, 1.0);
+	_matrix.scale(borderScale);
+	bind();
+	//TODO: Come up with equivalent for TGL.
+	//tglCopyTexSubImage2D(TGL_TEXTURE_2D, 0, xoffset, yoffset, x, y, _texWidth, _texHeight);
+}
+
+void Te3DTextureTinyGL::writeTo(Graphics::Surface &surf) {
+	Graphics::Surface fullTex;
+	fullTex.create(_texWidth, _texHeight, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+	//TODO: Come up with equivalent for TGL.
+	//tglGetTexImage(TGL_TEXTURE_2D, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, fullTex.getPixels());
+	surf.create(_width, _height, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
+	surf.copyRectToSurface(fullTex, 0, 0, Common::Rect(_width, _height));
+	fullTex.free();
+}
+
+void Te3DTextureTinyGL::create() {
+	_flipY = false;
+	_leftBorder = _btmBorder = _texWidth = _texHeight = 0;
+	_rightBorder = _topBorder = _width = _height = 0;
+	_format = TeImage::INVALID;
+	_loaded = false;
+	if (!_createdTexture)
+		tglGenTextures(1, &_glTexture);
+	if (_glTexture == NO_TEXTURE) {
+		_createdTexture = false;
+		return;
+	}
+
+	_createdTexture = true;
+	tglBindTexture(TGL_TEXTURE_2D, _glTexture);
+	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_LINEAR);
+	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_LINEAR);
+	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_CLAMP_TO_EDGE);
+	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_CLAMP_TO_EDGE);
+}
+
+void Te3DTextureTinyGL::destroy() {
+	if (_createdTexture) {
+		tglDeleteTextures(1, &_glTexture);
+	}
+	_createdTexture = false;
+	_loaded = false;
+	_glTexture = NO_TEXTURE;
+}
+
+void Te3DTextureTinyGL::forceTexData(uint gltexture, uint xsize, uint ysize) {
+	if (_glTexture != 0xffffffff) {
+		if (_createdTexture)
+			tglDeleteTextures(1, &_glTexture);
+		_createdTexture = false;
+		_loaded = false;
+	}
+	_glTexture = gltexture;
+	_width = xsize;
+	_height = ysize;
+	_texWidth = xsize;
+	_texHeight = ysize;
+}
+
+bool Te3DTextureTinyGL::load(const TeImage &img) {
+	Common::Path accessName = img.getAccessName();
+	setAccessName(accessName.append(".3dtex"));
+
+	_width = img.w;
+	_height = img.h;
+	_format = img.teFormat();
+
+	// TODO? set some other fields from the image here.
+	// for now just set some good defaults.
+	_flipY = true;    //img._flipY;
+	_leftBorder = 0;  //img._leftBorder;
+	_btmBorder = 0;   //img._btmBorder;
+	_rightBorder = 0; //img._rightBorder;
+	_topBorder = 0;   //img._topBorder;
+
+	_texWidth = _width;
+	_texHeight = _height;
+
+	tglBindTexture(TGL_TEXTURE_2D, _glTexture);
+	//tglPixelStorei(TGL_UNPACK_SWAP_BYTES, TGL_FALSE);
+	//tglPixelStorei(TGL_UNPACK_LSB_FIRST, TGL_FALSE);
+	//tglPixelStorei(TGL_UNPACK_ROW_LENGTH, 0);
+	//tglPixelStorei(TGL_UNPACK_SKIP_ROWS, 0);
+	//tglPixelStorei(TGL_UNPACK_SKIP_PIXELS, 0);
+	//tglPixelStorei(TGL_UNPACK_ALIGNMENT, 1);
+
+	const void *imgdata = img.getPixels();
+	if (_format == TeImage::RGB8) {
+		/*GLenum glpxformat = GL_RGB;
+		if (_glPixelFormat != GL_INVALID_ENUM) {
+			glpxformat = _glPixelFormat;
+		}*/
+		tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, img.pitch / 3, img.h, 0, TGL_RGB, TGL_UNSIGNED_BYTE, imgdata);
+	} else if (_format == TeImage::RGBA8) {
+		/*GLenum glpxformat = GL_RGBA8;
+		if (_glPixelFormat != GL_INVALID_ENUM) {
+			glpxformat = _glPixelFormat;
+		}*/
+		tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, img.w, img.h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, imgdata);
+	} else {
+		warning("Te3DTexture::load can't send image format %d to GL.", _format);
+	}
+
+	_matrix.setToIdentity();
+
+	_matrix.scale(TeVector3f32((float)_width / _texWidth, (float)_height / _texHeight, 1.0f));
+	_matrix.translate(TeVector3f32((float)_leftBorder / _width, (float)_btmBorder / _height, 0.0f));
+	_matrix.scale(TeVector3f32(1.0 - (float)(_rightBorder + _leftBorder) / _width,
+					1.0 - (float)(_topBorder + _btmBorder) / _height, 1.0f));
+	if (_flipY) {
+		_matrix.translate(TeVector3f32(0.0f, 1.0f, 0.0f));
+		_matrix.scale(TeVector3f32(1.0f, -1.0f, 1.0f));
+	}
+	_loaded = true;
+	return true;
+}
+
+/*static*/
+void Te3DTextureTinyGL::unbind() {
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->setMatrixMode(TeRenderer::MM_GL_TEXTURE);
+	renderer->loadIdentityMatrix();
+	renderer->loadCurrentMatrixToGL();
+	tglBindTexture(TGL_TEXTURE_2D, 0);
+	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
+}
+
+bool Te3DTextureTinyGL::unload() {
+	tglBindTexture(TGL_TEXTURE_2D, _glTexture);
+	tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGB, 0, 0, 0, TGL_RGB, TGL_UNSIGNED_BYTE, NULL);
+	_loaded = false;
+	return true;
+}
+
+void Te3DTextureTinyGL::update(const TeImage &img, uint xoff, uint yoff) {
+	if (!img.w || !img.h)
+		return;
+
+	setAccessName(img.getAccessName().append(".3dtex"));
+	tglBindTexture(TGL_TEXTURE_2D, _glTexture);
+	tglPixelStorei(TGL_UNPACK_SWAP_BYTES, 0);
+	tglPixelStorei(TGL_UNPACK_LSB_FIRST, 0);
+	tglPixelStorei(TGL_UNPACK_ROW_LENGTH, 0);
+	tglPixelStorei(TGL_UNPACK_SKIP_ROWS, 0);
+	tglPixelStorei(TGL_UNPACK_SKIP_PIXELS, 0);
+	tglPixelStorei(TGL_UNPACK_ALIGNMENT, 1);
+
+	const void *imgdata = img.getPixels();
+	if (_format == TeImage::RGB8) {
+		//TODO: Come up with equivalent for TGL.
+		//tglTexSubImage2D(TGL_TEXTURE_2D, 0, xoff, yoff, img.w, img.h, TGL_RGB, TGL_UNSIGNED_BYTE, imgdata);
+	} else if (_format == TeImage::RGBA8) {
+		//TODO: Come up with equivalent for TGL.
+		//tglTexSubImage2D(TGL_TEXTURE_2D, 0, xoff, yoff, img.w, img.h, TGL_RGBA, TGL_UNSIGNED_BYTE, imgdata);
+	} else {
+		warning("Te3DTexture::update can't send image format %d to GL.", _format);
+	}
+	return;
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_3d_texture_tinygl.h b/engines/tetraedge/te/te_3d_texture_tinygl.h
new file mode 100644
index 00000000000..fa190631e07
--- /dev/null
+++ b/engines/tetraedge/te/te_3d_texture_tinygl.h
@@ -0,0 +1,60 @@
+/* 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_TE_TE_3D_TEXTURE_TINYGL_H
+#define TETRAEDGE_TE_TE_3D_TEXTURE_TINYGL_H
+
+#if defined(USE_TINYGL)
+
+#include "tetraedge/te/te_3d_texture.h"
+
+namespace Tetraedge {
+
+class Te3DTextureTinyGL : public Te3DTexture {
+public:
+	Te3DTextureTinyGL();
+	virtual ~Te3DTextureTinyGL();
+
+	void bind() const override;
+	void copyCurrentRender(uint xoffset, uint yoffset, uint x, uint y) override;
+	void create() override;
+	void destroy() override;
+	void forceTexData(uint gltexture, uint xsize, uint ysize) override;
+
+	bool load(const TeImage &img) override;
+
+	static void unbind();
+	bool unload() override;
+	void update(const TeImage &img, uint xoff, uint yoff) override;
+
+	void writeTo(Graphics::Surface &surf) override;
+
+private:
+	uint _glTexture;
+	//uint _glPixelFormat;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_TINYGL
+
+#endif // TETRAEDGE_TE_TE_3D_TEXTURE_TINYGL_H
diff --git a/engines/tetraedge/te/te_bezier_curve.cpp b/engines/tetraedge/te/te_bezier_curve.cpp
index b92a6f9df98..a7d9407352a 100644
--- a/engines/tetraedge/te/te_bezier_curve.cpp
+++ b/engines/tetraedge/te/te_bezier_curve.cpp
@@ -44,21 +44,21 @@ void TeBezierCurve::draw() {
 	if (!worldVisible() || _controlPoints.empty())
 		return;
 
-	TeMesh mesh1;
-	TeMesh mesh2;
+	Common::SharedPtr<TeMesh> mesh1(TeMesh::makeInstance());
+	Common::SharedPtr<TeMesh> mesh2(TeMesh::makeInstance());
 	uint npoints = _controlPoints.size();
 
-	mesh1.setConf(npoints, npoints, TeMesh::MeshMode_Points, 0, 0);
+	mesh1->setConf(npoints, npoints, TeMesh::MeshMode_Points, 0, 0);
 	for (uint i = 0; i < npoints; i++) {
-		mesh1.setVertex(i, _controlPoints[i]);
-		mesh1.setIndex(i, i);
+		mesh1->setVertex(i, _controlPoints[i]);
+		mesh1->setIndex(i, i);
 	}
 
-	mesh2.setConf(npoints, npoints, TeMesh::MeshMode_LineStrip, 0, 0);
+	mesh2->setConf(npoints, npoints, TeMesh::MeshMode_LineStrip, 0, 0);
 	for (uint i = 0; i < npoints; i++) {
-		mesh2.setVertex(i, _controlPoints[i]);
-		mesh2.setNormal(i, TeVector3f32(0.0f, 1.0f, 0.0));
-		mesh2.setIndex(i, i);
+		mesh2->setVertex(i, _controlPoints[i]);
+		mesh2->setNormal(i, TeVector3f32(0.0f, 1.0f, 0.0));
+		mesh2->setIndex(i, i);
 	}
 
 	TeRenderer *renderer = g_engine->getRenderer();
@@ -66,9 +66,9 @@ void TeBezierCurve::draw() {
 	renderer->pushMatrix();
 	renderer->multiplyMatrix(worldTransformationMatrix());
 	renderer->setCurrentColor(TeColor(0, 0xff, 0xff, 0xff));
-	mesh2.draw();
+	mesh2->draw();
 	renderer->setCurrentColor(TeColor(0xff, 0, 0xff, 0xff));
-	mesh1.draw();
+	mesh1->draw();
 	renderer->popMatrix();
 	renderer->setCurrentColor(prevColor);
 }
diff --git a/engines/tetraedge/te/te_free_move_zone.cpp b/engines/tetraedge/te/te_free_move_zone.cpp
index 9302d69c309..e4043dfa5b4 100644
--- a/engines/tetraedge/te/te_free_move_zone.cpp
+++ b/engines/tetraedge/te/te_free_move_zone.cpp
@@ -274,18 +274,18 @@ void TeFreeMoveZone::draw() {
 	TeRenderer *renderer = g_engine->getRenderer();
 	renderer->enableWireFrame();
 	TePickMesh2::draw();
-	TeMesh mesh;
-	mesh.setConf(_borders.size(), _borders.size(), TeMesh::MeshMode_Lines, 0, 0);
+	Common::SharedPtr<TeMesh> mesh(TeMesh::makeInstance());
+	mesh->setConf(_borders.size(), _borders.size(), TeMesh::MeshMode_Lines, 0, 0);
 	for (uint i = 0; i < _borders.size(); i++) {
-		mesh.setIndex(i, i);
-		mesh.setVertex(i, verticies()[_borders[i]]);
+		mesh->setIndex(i, i);
+		mesh->setVertex(i, verticies()[_borders[i]]);
 	}
 
 	const TeColor prevColor = renderer->currentColor();
 	renderer->pushMatrix();
 	renderer->multiplyMatrix(worldTransformationMatrix());
 	renderer->setCurrentColor(TeColor(0, 0x80, 0xff, 0xff));
-	mesh.draw();
+	mesh->draw();
 	renderer->popMatrix();
 	renderer->setCurrentColor(prevColor);
 
diff --git a/engines/tetraedge/te/te_light.cpp b/engines/tetraedge/te/te_light.cpp
index 74544399728..b1c4bdedaac 100644
--- a/engines/tetraedge/te/te_light.cpp
+++ b/engines/tetraedge/te/te_light.cpp
@@ -20,20 +20,19 @@
  */
 
 #include "common/math.h"
+#include "graphics/renderer.h"
+
+#include "tetraedge/tetraedge.h"
 
 #include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_light_opengl.h"
+#include "tetraedge/te/te_light_tinygl.h"
 #include "tetraedge/te/te_color.h"
 #include "tetraedge/te/te_quaternion.h"
 #include "tetraedge/te/te_vector3f32.h"
 
-#include "graphics/opengl/system_headers.h"
-
 namespace Tetraedge {
 
-static inline uint _toGlLight(uint lightno) {
-	return GL_LIGHT0 + lightno;
-}
-
 /*static*/
 TeColor TeLight::_globalAmbientColor;
 
@@ -51,31 +50,6 @@ TeVector3f32 TeLight::directionVector() const {
 	return TeVector3f32(cosx * cosy, siny, sinx * cosy);
 }
 
-void TeLight::disable(uint lightno) {
-	glDisable(_toGlLight(lightno));
-}
-
-void TeLight::enable(uint lightno) {
-	if (_colDiffuse.r() == 0 && _colDiffuse.g() == 0 && _colDiffuse.b() == 0)
-		glDisable(_toGlLight(lightno));
-	else
-		glEnable(_toGlLight(lightno));
-}
-
-/*static*/
-void TeLight::disableAll() {
-	glDisable(GL_LIGHTING);
-}
-
-/*static*/
-void TeLight::enableAll() {
-	glEnable(GL_LIGHTING);
-}
-
-void TeLight::draw(TeCamera &camera) {
-	error("TODO: Finish TeLight::draw");
-}
-
 void TeLight::transformDirPoint(const TeVector3f32 &pt1, TeVector3f32 &pt2) {
 	const TeQuaternion q1 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 1, 0), _positionRadial.getX() + M_PI);
 	const TeQuaternion q2 = TeQuaternion::fromAxisAndAngle(TeVector3f32(0, 0, -1), -_positionRadial.getY());
@@ -92,66 +66,9 @@ void TeLight::transformSpotPoint(TeVector3f32 &pt) {
 	pt += _position3d;
 }
 
-void TeLight::update(uint lightno) {
-	if (lightno > GL_MAX_LIGHTS)
-		error("Invalid light no %d", lightno);
-	const uint glLight = _toGlLight(lightno);
-
-	const float ambient[4] = {_colAmbient.r() / 255.0f, _colAmbient.g() / 255.0f,
-			_colAmbient.b() / 255.0f, 1.0};
-	glLightfv(glLight, GL_AMBIENT, ambient);
-
-	const float diff[4] = {_colDiffuse.r() / 255.0f, _colDiffuse.g() / 255.0f,
-			_colDiffuse.b() / 255.0f, 1.0};
-	glLightfv(glLight, GL_DIFFUSE, diff);
-
-	// WORKAROUND: Original game sets 0.01 as threshold here to avoid enabling
-	// the "shadow" light.  However, Syberia CitStation/31130 has shadowlight with
-	// values (4, 0, 0) which means it gets enabled and everything is dark.
-
-	if (diff[0] < 0.02f && diff[1] < 0.02f && diff[2] < 0.02f)
-		glDisable(glLight);
-
-	const float spec[4] = {_colSpecular.r() / 255.0f, _colSpecular.g() / 255.0f,
-			_colSpecular.b() / 255.0f, 1.0};
-	glLightfv(glLight, GL_SPECULAR, spec);
-
-	if (_type == LightTypeSpot || _type == LightTypePoint) {
-		const float pos[4] = {_position3d.x(), _position3d.y(), _position3d.z(), 1.0f};
-		glLightfv(glLight, GL_POSITION, pos);
-		glLightf(glLight, GL_CONSTANT_ATTENUATION, _constAtten);
-		glLightf(glLight, GL_LINEAR_ATTENUATION, _linearAtten);
-		glLightf(glLight, GL_QUADRATIC_ATTENUATION, _quadraticAtten);
-	}
-
-	if (_type == LightTypeDirectional) {
-		float cosx = cosf(_positionRadial.getX());
-		float cosy = cosf(_positionRadial.getY());
-		float sinx = sinf(_positionRadial.getX());
-		float siny = sinf(_positionRadial.getY());
-		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
-		glLightfv(glLight, GL_POSITION, pos);
-	}
-
-	if (_type == LightTypeSpot) {
-		float cosx = cosf(_positionRadial.getX());
-		float cosy = cosf(_positionRadial.getY());
-		float sinx = sinf(_positionRadial.getX());
-		float siny = sinf(_positionRadial.getY());
-		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
-		glLightfv(glLight, GL_SPOT_DIRECTION, pos);
-		glLightf(glLight, GL_SPOT_CUTOFF, (_cutoff * 180.0) / M_PI);
-		glLightf(glLight, GL_SPOT_EXPONENT, _exponent);
-	} else {
-		glLightf(glLight, GL_SPOT_CUTOFF, 180.0);
-	}
-}
-
 /*static*/
 void TeLight::updateGlobal() {
-	const float col[4] = {_globalAmbientColor.r() / 255.0f,
-			_globalAmbientColor.g() / 255.0f, _globalAmbientColor.b() / 255.0f, 1.0};
-	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
+	// TOOD: Call correct global.
 }
 
 Common::String TeLight::dump() const {
@@ -177,5 +94,20 @@ Common::String TeLight::dump() const {
 		_quadraticAtten, _cutoff, _exponent, _displaySize);
 }
 
+/*static*/
+TeLight *TeLight::makeInstance() {
+	Graphics::RendererType r = g_engine->preferredRendererType();
+
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+	if (r == Graphics::kRendererTypeOpenGL)
+		return new TeLightOpenGL();
+#endif
+
+#if defined(USE_TINYGL)
+	if (r == Graphics::kRendererTypeTinyGL)
+		return new TeLightTinyGL();
+#endif
+	error("Couldn't create TeLight for selected renderer");
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_light.h b/engines/tetraedge/te/te_light.h
index 38d45756e6f..e4223ebc82c 100644
--- a/engines/tetraedge/te/te_light.h
+++ b/engines/tetraedge/te/te_light.h
@@ -39,19 +39,18 @@ class TeCamera;
 class TeLight {
 public:
 	TeLight();
+	virtual ~TeLight() {};
 
 	TeVector3f32 directionVector() const;
-	void disable(uint lightno);
-	void enable(uint lightno);
-	static void disableAll();
-	static void enableAll();
+	virtual void disable(uint lightno) = 0;
+	virtual void enable(uint lightno) = 0;
 
-	void draw(TeCamera &camera);
+	virtual void draw(TeCamera &camera) = 0;
 
 	void transformDirPoint(const TeVector3f32 &pt1, TeVector3f32 &pt2);
 	void transformSpotPoint(TeVector3f32 &pt1);
 
-	void update(uint lightno);
+	virtual void update(uint lightno) = 0;
 	static void updateGlobal();
 	static void setGlobalAmbient(const TeColor &col) { _globalAmbientColor = col; }
 	static const TeColor &globalAmbient() { return _globalAmbientColor; }
@@ -75,7 +74,9 @@ public:
 
 	Common::String dump() const;
 
-private:
+	static TeLight *makeInstance();
+
+protected:
 	TeVector3f32 _position3d;
 	TeVector2f32 _positionRadial;
 
diff --git a/engines/tetraedge/te/te_light_opengl.cpp b/engines/tetraedge/te/te_light_opengl.cpp
new file mode 100644
index 00000000000..c0021afb485
--- /dev/null
+++ b/engines/tetraedge/te/te_light_opengl.cpp
@@ -0,0 +1,127 @@
+/* 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 "common/math.h"
+
+#include "tetraedge/te/te_light_opengl.h"
+#include "tetraedge/te/te_color.h"
+#include "tetraedge/te/te_quaternion.h"
+#include "tetraedge/te/te_vector3f32.h"
+
+#include "graphics/opengl/system_headers.h"
+
+namespace Tetraedge {
+
+static inline uint _toGlLight(uint lightno) {
+	return GL_LIGHT0 + lightno;
+}
+
+TeLightOpenGL::TeLightOpenGL() {
+}
+
+void TeLightOpenGL::disable(uint lightno) {
+	glDisable(_toGlLight(lightno));
+}
+
+void TeLightOpenGL::enable(uint lightno) {
+	if (_colDiffuse.r() == 0 && _colDiffuse.g() == 0 && _colDiffuse.b() == 0)
+		glDisable(_toGlLight(lightno));
+	else
+		glEnable(_toGlLight(lightno));
+}
+
+/*static*/
+void TeLightOpenGL::disableAll() {
+	glDisable(GL_LIGHTING);
+}
+
+/*static*/
+void TeLightOpenGL::enableAll() {
+	glEnable(GL_LIGHTING);
+}
+
+void TeLightOpenGL::draw(TeCamera &camera) {
+	error("TODO: Finish TeLightOpenGL::draw");
+}
+
+void TeLightOpenGL::update(uint lightno) {
+	if (lightno > GL_MAX_LIGHTS)
+		error("Invalid light no %d", lightno);
+	const uint glLight = _toGlLight(lightno);
+
+	const float ambient[4] = {_colAmbient.r() / 255.0f, _colAmbient.g() / 255.0f,
+			_colAmbient.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_AMBIENT, ambient);
+
+	const float diff[4] = {_colDiffuse.r() / 255.0f, _colDiffuse.g() / 255.0f,
+			_colDiffuse.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_DIFFUSE, diff);
+
+	// WORKAROUND: Original game sets 0.01 as threshold here to avoid enabling
+	// the "shadow" light.  However, Syberia CitStation/31130 has shadowlight with
+	// values (4, 0, 0) which means it gets enabled and everything is dark.
+
+	if (diff[0] < 0.02f && diff[1] < 0.02f && diff[2] < 0.02f)
+		glDisable(glLight);
+
+	const float spec[4] = {_colSpecular.r() / 255.0f, _colSpecular.g() / 255.0f,
+			_colSpecular.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_SPECULAR, spec);
+
+	if (_type == LightTypeSpot || _type == LightTypePoint) {
+		const float pos[4] = {_position3d.x(), _position3d.y(), _position3d.z(), 1.0f};
+		glLightfv(glLight, GL_POSITION, pos);
+		glLightf(glLight, GL_CONSTANT_ATTENUATION, _constAtten);
+		glLightf(glLight, GL_LINEAR_ATTENUATION, _linearAtten);
+		glLightf(glLight, GL_QUADRATIC_ATTENUATION, _quadraticAtten);
+	}
+
+	if (_type == LightTypeDirectional) {
+		float cosx = cosf(_positionRadial.getX());
+		float cosy = cosf(_positionRadial.getY());
+		float sinx = sinf(_positionRadial.getX());
+		float siny = sinf(_positionRadial.getY());
+		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
+		glLightfv(glLight, GL_POSITION, pos);
+	}
+
+	if (_type == LightTypeSpot) {
+		float cosx = cosf(_positionRadial.getX());
+		float cosy = cosf(_positionRadial.getY());
+		float sinx = sinf(_positionRadial.getX());
+		float siny = sinf(_positionRadial.getY());
+		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
+		glLightfv(glLight, GL_SPOT_DIRECTION, pos);
+		glLightf(glLight, GL_SPOT_CUTOFF, (_cutoff * 180.0) / M_PI);
+		glLightf(glLight, GL_SPOT_EXPONENT, _exponent);
+	} else {
+		glLightf(glLight, GL_SPOT_CUTOFF, 180.0);
+	}
+}
+
+/*static*/
+void TeLightOpenGL::updateGlobal() {
+	const float col[4] = {_globalAmbientColor.r() / 255.0f,
+			_globalAmbientColor.g() / 255.0f, _globalAmbientColor.b() / 255.0f, 1.0};
+	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_light_opengl.h b/engines/tetraedge/te/te_light_opengl.h
new file mode 100644
index 00000000000..561d86cc4d8
--- /dev/null
+++ b/engines/tetraedge/te/te_light_opengl.h
@@ -0,0 +1,54 @@
+/* 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_TE_TE_LIGHT_OPENGL_H
+#define TETRAEDGE_TE_TE_LIGHT_OPENGL_H
+
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+
+#include "tetraedge/te/te_light.h"
+
+namespace Tetraedge {
+
+class TeCamera;
+
+class TeLightOpenGL : public TeLight {
+public:
+	TeLightOpenGL();
+
+	void disable(uint lightno) override;
+	void enable(uint lightno) override;
+
+	void draw(TeCamera &camera) override;
+
+	void update(uint lightno) override;
+	static void updateGlobal();
+
+	static void disableAll();
+	static void enableAll();
+
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_OPENGL
+
+#endif // TETRAEDGE_TE_TE_LIGHT_OPENGL_H
diff --git a/engines/tetraedge/te/te_light_tinygl.cpp b/engines/tetraedge/te/te_light_tinygl.cpp
new file mode 100644
index 00000000000..af94d4c7c50
--- /dev/null
+++ b/engines/tetraedge/te/te_light_tinygl.cpp
@@ -0,0 +1,127 @@
+/* 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 "common/math.h"
+
+#include "tetraedge/te/te_light_tinygl.h"
+#include "tetraedge/te/te_color.h"
+#include "tetraedge/te/te_quaternion.h"
+#include "tetraedge/te/te_vector3f32.h"
+
+#include "graphics/opengl/system_headers.h"
+
+namespace Tetraedge {
+
+static inline uint _toGlLight(uint lightno) {
+	return GL_LIGHT0 + lightno;
+}
+
+TeLightTinyGL::TeLightTinyGL() {
+}
+
+void TeLightTinyGL::disable(uint lightno) {
+	glDisable(_toGlLight(lightno));
+}
+
+void TeLightTinyGL::enable(uint lightno) {
+	if (_colDiffuse.r() == 0 && _colDiffuse.g() == 0 && _colDiffuse.b() == 0)
+		glDisable(_toGlLight(lightno));
+	else
+		glEnable(_toGlLight(lightno));
+}
+
+/*static*/
+void TeLightTinyGL::disableAll() {
+	glDisable(GL_LIGHTING);
+}
+
+/*static*/
+void TeLightTinyGL::enableAll() {
+	glEnable(GL_LIGHTING);
+}
+
+void TeLightTinyGL::draw(TeCamera &camera) {
+	error("TODO: Finish TeLight::draw");
+}
+
+void TeLightTinyGL::update(uint lightno) {
+	if (lightno > GL_MAX_LIGHTS)
+		error("Invalid light no %d", lightno);
+	const uint glLight = _toGlLight(lightno);
+
+	const float ambient[4] = {_colAmbient.r() / 255.0f, _colAmbient.g() / 255.0f,
+			_colAmbient.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_AMBIENT, ambient);
+
+	const float diff[4] = {_colDiffuse.r() / 255.0f, _colDiffuse.g() / 255.0f,
+			_colDiffuse.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_DIFFUSE, diff);
+
+	// WORKAROUND: Original game sets 0.01 as threshold here to avoid enabling
+	// the "shadow" light.  However, Syberia CitStation/31130 has shadowlight with
+	// values (4, 0, 0) which means it gets enabled and everything is dark.
+
+	if (diff[0] < 0.02f && diff[1] < 0.02f && diff[2] < 0.02f)
+		glDisable(glLight);
+
+	const float spec[4] = {_colSpecular.r() / 255.0f, _colSpecular.g() / 255.0f,
+			_colSpecular.b() / 255.0f, 1.0};
+	glLightfv(glLight, GL_SPECULAR, spec);
+
+	if (_type == LightTypeSpot || _type == LightTypePoint) {
+		const float pos[4] = {_position3d.x(), _position3d.y(), _position3d.z(), 1.0f};
+		glLightfv(glLight, GL_POSITION, pos);
+		glLightf(glLight, GL_CONSTANT_ATTENUATION, _constAtten);
+		glLightf(glLight, GL_LINEAR_ATTENUATION, _linearAtten);
+		glLightf(glLight, GL_QUADRATIC_ATTENUATION, _quadraticAtten);
+	}
+
+	if (_type == LightTypeDirectional) {
+		float cosx = cosf(_positionRadial.getX());
+		float cosy = cosf(_positionRadial.getY());
+		float sinx = sinf(_positionRadial.getX());
+		float siny = sinf(_positionRadial.getY());
+		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
+		glLightfv(glLight, GL_POSITION, pos);
+	}
+
+	if (_type == LightTypeSpot) {
+		float cosx = cosf(_positionRadial.getX());
+		float cosy = cosf(_positionRadial.getY());
+		float sinx = sinf(_positionRadial.getX());
+		float siny = sinf(_positionRadial.getY());
+		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
+		glLightfv(glLight, GL_SPOT_DIRECTION, pos);
+		glLightf(glLight, GL_SPOT_CUTOFF, (_cutoff * 180.0) / M_PI);
+		glLightf(glLight, GL_SPOT_EXPONENT, _exponent);
+	} else {
+		glLightf(glLight, GL_SPOT_CUTOFF, 180.0);
+	}
+}
+
+/*static*/
+void TeLightTinyGL::updateGlobal() {
+	const float col[4] = {_globalAmbientColor.r() / 255.0f,
+			_globalAmbientColor.g() / 255.0f, _globalAmbientColor.b() / 255.0f, 1.0};
+	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_light_tinygl.h b/engines/tetraedge/te/te_light_tinygl.h
new file mode 100644
index 00000000000..a05470f86e9
--- /dev/null
+++ b/engines/tetraedge/te/te_light_tinygl.h
@@ -0,0 +1,52 @@
+/* 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_TE_TE_LIGHT_TINYGL_H
+#define TETRAEDGE_TE_TE_LIGHT_TINYGL_H
+
+#if defined(USE_TINYGL)
+
+#include "tetraedge/te/te_light.h"
+
+namespace Tetraedge {
+
+class TeLightTinyGL : public TeLight {
+public:
+	TeLightTinyGL();
+
+	void disable(uint lightno) override;
+	void enable(uint lightno) override;
+
+	void draw(TeCamera &camera) override;
+
+	void update(uint lightno) override;
+	static void updateGlobal();
+
+	static void disableAll();
+	static void enableAll();
+
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_TINYGL
+
+#endif // TETRAEDGE_TE_TE_LIGHT_TINYGL_H
diff --git a/engines/tetraedge/te/te_material.cpp b/engines/tetraedge/te/te_material.cpp
index af940165a7a..c01ca0f6839 100644
--- a/engines/tetraedge/te/te_material.cpp
+++ b/engines/tetraedge/te/te_material.cpp
@@ -20,7 +20,6 @@
  */
 
 #include "common/textconsole.h"
-#include "graphics/opengl/system_headers.h"
 
 #include "tetraedge/tetraedge.h"
 
@@ -64,99 +63,6 @@ Common::String TeMaterial::dump() const {
 			  _shininess, _enableLights ? "on" : "off");
 }
 
-void TeMaterial::apply() const {
-	//debug("TeMaterial::apply (%s)", dump().c_str());
-	static const float constColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
-	TeRenderer *renderer = g_engine->getRenderer();
-	if (renderer->shadowMode() == TeRenderer::ShadowMode0) {
-		if (_enableLights)
-			TeLight::enableAll();
-		else
-			TeLight::disableAll();
-
-		if (_texture) {
-			renderer->enableTexture();
-			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-			_texture->bind();
-		}
-
-		glDisable(GL_ALPHA_TEST);
-		if (_mode == MaterialMode0) {
-			glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constColor);
-			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
-			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
-			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
-			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
-			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
-			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
-			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
-		} else {
-			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-			if (_mode != MaterialMode1) {
-				glEnable(GL_ALPHA_TEST);
-				glAlphaFunc(GL_GREATER, 0.5);
-			}
-		}
-		const float ambient[4] = { _ambientColor.r() / 255.0f, _ambientColor.g() / 255.0f,
-			_ambientColor.b() / 255.0f, _ambientColor.a() / 255.0f };
-		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
-
-		const float specular[4] = { _specularColor.r() / 255.0f, _specularColor.g() / 255.0f,
-			_specularColor.b() / 255.0f, _specularColor.a() / 255.0f };
-		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
-
-		const float emission[4] = { _emissionColor.r() / 255.0f, _emissionColor.g() / 255.0f,
-			_emissionColor.b() / 255.0f, _emissionColor.a() / 255.0f };
-		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);
-
-		glMaterialf(GL_FRONT, GL_SHININESS, _shininess);
-
-		const float diffuse[4] = { _diffuseColor.r() / 255.0f, _diffuseColor.g() / 255.0f,
-			_diffuseColor.b() / 255.0f, _diffuseColor.a() / 255.0f };
-		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
-
-		renderer->setCurrentColor(_diffuseColor);
-	} else if (renderer->shadowMode() == TeRenderer::ShadowMode1) {
-		// NOTE: Diverge from original here, it sets 255.0 but the
-		// colors should be scaled -1.0 .. 1.0.
-		static const float fullColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
-		TeLight::disableAll();
-		glDisable(GL_ALPHA_TEST);
-		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, fullColor);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fullColor);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fullColor);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, fullColor);
-	}
-
-	//warning("TODO: Work out what TeMaterial::_enableSomethingDefault0 actually is.");
-	if (!_enableSomethingDefault0) {
-		glDisable(GL_TEXTURE_GEN_S);
-		glDisable(GL_TEXTURE_GEN_T);
-		glDisable(GL_TEXTURE_GEN_R);
-		glDisable(GL_TEXTURE_GEN_Q);
-	} else {
-		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-		glEnable(GL_TEXTURE_GEN_S);
-		glEnable(GL_TEXTURE_GEN_T);
-		glEnable(GL_TEXTURE_GEN_R);
-		glEnable(GL_TEXTURE_GEN_Q);
-		glEnable(GL_TEXTURE_2D);
-		TeLight::disableAll();
-		glDisable(GL_ALPHA_TEST);
-		renderer->enableTexture();
-		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-
-		const float diffuse[4] = { _diffuseColor.r() / 255.0f, _diffuseColor.g() / 255.0f,
-			_diffuseColor.b() / 255.0f, _diffuseColor.a() / 255.0f };
-
-		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, diffuse);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse);
-		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, diffuse);
-	}
-}
-
 bool TeMaterial::operator==(const TeMaterial &other) const {
 	return (_texture == other._texture) && (_ambientColor == other._ambientColor)
 		&& (_diffuseColor == other._diffuseColor) && (_specularColor == other._specularColor)
diff --git a/engines/tetraedge/te/te_material.h b/engines/tetraedge/te/te_material.h
index 7e0f3eec87d..2fd060abbc3 100644
--- a/engines/tetraedge/te/te_material.h
+++ b/engines/tetraedge/te/te_material.h
@@ -43,7 +43,7 @@ public:
 	TeMaterial(const TeMaterial &other) = default;
 	TeMaterial(TeIntrusivePtr<Te3DTexture> texture, Mode mode);
 
-	void apply() const;
+	// Note: apply() function from original moved to TeRenderer to remove OGL specific code from here
 	void defaultValues();
 	static void deserialize(Common::SeekableReadStream &stream, TeMaterial &material, const Common::Path &path);
 	static void serialize(Common::SeekableWriteStream &stream, TeMaterial &material);
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index 77cc00b0b04..30dbe00ecc2 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -19,18 +19,17 @@
  *
  */
 
-#include "graphics/opengl/system_headers.h"
-
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/te/te_renderer.h"
 #include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_mesh.h"
+#include "tetraedge/te/te_mesh_opengl.h"
+#include "tetraedge/te/te_mesh_tinygl.h"
 #include "tetraedge/te/te_material.h"
 
 namespace Tetraedge {
 
-TeMesh::TeMesh() : _matrixForced(false), _glMeshMode(GL_POINTS),
-_hasAlpha(false), _gltexEnvMode(GL_MODULATE), _initialMaterialIndexCount(0),
+TeMesh::TeMesh() : _matrixForced(false), _hasAlpha(false), _initialMaterialIndexCount(0),
 _drawWires(false), _shouldDraw(true) {
 }
 
@@ -66,158 +65,6 @@ void TeMesh::destroy() {
 	_matricies.clear();
 }
 
-void TeMesh::draw() {
-	if (!worldVisible())
-		return;
-
-	TeRenderer *renderer = g_engine->getRenderer();
-	renderer->pushMatrix();
-	if (_matrixForced)
-		renderer->multiplyMatrix(_forcedMatrix);
-	else
-		renderer->multiplyMatrix(worldTransformationMatrix());
-
-	/*
-	debug("Draw mesh %p (%s, %d verts %d norms %d indexes %d materials %d updated)", this, name().empty() ? "no name" : name().c_str(), _verticies.size(), _normals.size(), _indexes.size(), _materials.size(), _updatedVerticies.size());
-	debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
-	if (!_materials.empty())
-		debug("   material   %s", _materials[0].dump().c_str());
-	debug("   position   %s", position().dump().c_str());
-	debug("   worldPos   %s", worldPosition().dump().c_str());
-	debug("   scale      %s", scale().dump().c_str());
-	debug("   worldScale %s", worldScale().dump().c_str());
-	debug("   rotation   %s", rotation().dump().c_str());
-	debug("   worldRot   %s", worldRotation().dump().c_str());
-	*/
-
-	Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
-	Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
-	if (renderer->shadowMode() != TeRenderer::ShadowMode1) {
-		if (_faceCounts.empty()) {
-			if (hasAlpha(0) && _shouldDraw) {
-				renderer->addTransparentMesh(*this, 0, 0, 0);
-				renderer->popMatrix();
-				return;
-			}
-		} else {
-			assert(_faceCounts.size() == _materials.size());
-			int totalFaceCount = 0;
-			for (uint i = 0; i < _faceCounts.size(); i++) {
-				if (!_faceCounts[i])
-					continue;
-				if (hasAlpha(i)) {
-					renderer->addTransparentMesh(*this, totalFaceCount, _faceCounts[i], i);
-				}
-				totalFaceCount += _faceCounts[i];
-			}
-		}
-	}
-
-	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
-	renderer->pushMatrix();
-	renderer->loadCurrentMatrixToGL();
-	glEnableClientState(GL_VERTEX_ARRAY);
-	if (!normals.empty())
-		glEnableClientState(GL_NORMAL_ARRAY);
-
-	if (!_colors.empty())
-		glEnableClientState(GL_COLOR_ARRAY);
-
-	glVertexPointer(3, GL_FLOAT, 12, verticies.data());
-	if (!normals.empty())
-		glNormalPointer(GL_FLOAT, 12, normals.data());
-
-	if (!_uvs.empty() && renderer->shadowMode() != TeRenderer::ShadowMode2)
-		glTexCoordPointer(2, GL_FLOAT, 8, _uvs.data());
-
-	if (!_colors.empty())
-		glColorPointer(4, GL_UNSIGNED_BYTE, 4, _colors.data());
-
-	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, _gltexEnvMode);
-	if (renderer->scissorEnabled()) {
-		glEnable(GL_SCISSOR_TEST);
-		uint scissorx = renderer->scissorX();
-		uint scissory = renderer->scissorY();
-		uint scissorwidth = renderer->scissorWidth();
-		uint scissorheight = renderer->scissorHeight();
-		glScissor(scissorx, scissory, scissorwidth, scissorheight);
-	}
-
-	if (_faceCounts.empty()) {
-		if (!_materials.empty())
-			_materials[0].apply();
-
-		glDrawElements(_glMeshMode, _indexes.size(), GL_UNSIGNED_SHORT, _indexes.data());
-		if (!_materials.empty()) {
-			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-			renderer->disableTexture();
-		}
-	} else {
-		int totalFaceCount = 0;
-		assert(_faceCounts.size() == _materials.size());
-		for (uint i = 0; i < _materials.size(); i++) {
-			if (!_faceCounts[i])
-				continue;
-			if (!hasAlpha(i) || renderer->shadowMode() == TeRenderer::ShadowMode1 || !_shouldDraw) {
-				_materials[i].apply();
-				glDrawElements(_glMeshMode, _faceCounts[i] * 3, GL_UNSIGNED_SHORT, _indexes.data() + totalFaceCount * 3);
-				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-				renderer->disableTexture();
-			}
-			totalFaceCount += _faceCounts[i];
-		}
-	}
-
-	if (!renderer->scissorEnabled())
-		glDisable(GL_SCISSOR_TEST);
-
-	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-	glDisableClientState(GL_VERTEX_ARRAY);
-	glDisableClientState(GL_NORMAL_ARRAY);
-	glDisableClientState(GL_COLOR_ARRAY);
-
-	//renderer->setCurrentColor(renderer->currentColor()); // pointless?
-
-	if (_drawWires && !normals.empty()) {
-		TeLight::disableAll();
-		glBegin(GL_LINES);
-		renderer->setCurrentColor(TeColor(255, 255, 255, 255));
-		for (uint i = 0; i < verticies.size(); i++) {
-			glVertex3f(verticies[i].x(), verticies[i].y(), verticies[i].z());
-			glVertex3f(verticies[i].x() + normals[i].x(),
-					verticies[i].y() + normals[i].y(),
-					verticies[i].z() + normals[i].z());
-		}
-		glEnd();
-	}
-
-	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
-	renderer->popMatrix();
-	renderer->popMatrix();
-}
-
-TeMesh::Mode TeMesh::getMode() const {
-	// Do the reverse translation of setConf... why? I dunno.. the game does that..
-	switch(_glMeshMode) {
-	case GL_POINTS:
-		return MeshMode_Points;
-	case GL_LINES:
-		return MeshMode_Lines;
-	case GL_LINE_LOOP:
-		return MeshMode_LineLoop;
-	case GL_LINE_STRIP:
-		return MeshMode_LineStrip;
-	case GL_TRIANGLES:
-		return MeshMode_Triangles;
-	case GL_TRIANGLE_STRIP:
-		return MeshMode_TriangleStrip;
-	case GL_TRIANGLE_FAN:
-		return MeshMode_TriangleFan;
-	default:
-		return MeshMode_None;
-	}
-}
-
 bool TeMesh::hasAlpha(uint idx) {
 	// Note: I don't understand this logic, but it's what the game does..
 	bool hasGlobalAlpha = _hasAlpha && !_colors.empty();
@@ -248,10 +95,6 @@ void TeMesh::resizeUpdatedTables(unsigned long newSize) {
 	_updatedNormals.resize(newSize);
 }
 
-void TeMesh::setglTexEnvBlend() {
-	_gltexEnvMode = GL_BLEND;
-}
-
 void TeMesh::setColor(const TeColor &col) {
 	Te3DObject2::setColor(col);
 
@@ -282,31 +125,7 @@ void TeMesh::setConf(unsigned long vertexCount, unsigned long indexCount, enum M
 	_indexes.resize(indexCount);
 	_materials.resize(materialCount);
 	_matricies.resize(vertexCount);
-	switch(mode) {
-	case MeshMode_Points:
-		_glMeshMode = GL_POINTS;
-		break;
-	case MeshMode_Lines:
-		_glMeshMode = GL_LINES;
-		break;
-	case MeshMode_LineLoop:
-		_glMeshMode = GL_LINE_LOOP;
-		break;
-	case MeshMode_LineStrip:
-		_glMeshMode = GL_LINE_STRIP;
-		break;
-	case MeshMode_Triangles:
-		_glMeshMode = GL_TRIANGLES;
-		break;
-	case MeshMode_TriangleStrip:
-		_glMeshMode = GL_TRIANGLE_STRIP;
-		break;
-	case MeshMode_TriangleFan:
-		_glMeshMode = GL_TRIANGLE_FAN;
-		break;
-	default:
-		error("Setting invalid mesh mode.");
-	}
+	setMode(mode);
 }
 
 void TeMesh::setIndex(uint idx, uint val) {
@@ -403,26 +222,21 @@ void TeMesh::updateTo(const Common::Array<TeMatrix4x4> *matricies1, const Common
 	}
 }
 
-/*
-TeMesh &TeMesh::operator=(const TeMesh &other) {
-	copy(other);
-	return *this;
-}
+/*static*/
+TeMesh *TeMesh::makeInstance() {
+	Graphics::RendererType r = g_engine->preferredRendererType();
 
-void TeMesh::copy(const TeMesh &other) {
-	destroy();
-	_drawWires = false;
-	_hasAlpha = other._hasAlpha;
-	_shouldDraw = other._shouldDraw;
-	_verticies = other._verticies;
-	_normals = other._normals;
-	_uvs = other._uvs;
-	_colors = other._colors;
-
-	_glMeshMode = other._glMeshMode;
-	_gltexEnvMode = other._gltexEnvMode;
-	_matrixForced = other._matrixForced;
-	_forceMatrix = other._forceMatrix;
-}*/
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+	if (r == Graphics::kRendererTypeOpenGL)
+		return new TeMeshOpenGL();
+#endif
+
+#if defined(USE_TINYGL)
+	if (r == Graphics::kRendererTypeTinyGL)
+		return new TeMeshTinyGL();
+#endif
+	error("Couldn't create TeMesh for selected renderer");
+
+}
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_mesh.h b/engines/tetraedge/te/te_mesh.h
index bc71f399d7a..34211174fee 100644
--- a/engines/tetraedge/te/te_mesh.h
+++ b/engines/tetraedge/te/te_mesh.h
@@ -43,7 +43,8 @@ class TeModelVertexAnimation;
 class TeMesh : public Te3DObject2 {
 public:
 	TeMesh();
-	TeMesh(const TeMesh &other) = default;
+	
+	virtual ~TeMesh() {};
 
 	enum Mode {
 		MeshMode_None = 0,
@@ -65,12 +66,13 @@ public:
 	void create();
 	void defaultMaterial(const TeIntrusivePtr<Te3DTexture> &texture);
 	void destroy();
-	void draw() override;
 	void facesPerMaterial(uint idx, unsigned short value);
 	unsigned short facesPerMaterial(uint idx) const { return _faceCounts[idx]; }
 	void forceMatrix(const TeMatrix4x4 &matrix);
 	byte getFaceMaterial(uint idx);
-	TeMesh::Mode getMode() const;
+	virtual uint32 getTexEnvMode() const = 0;
+	virtual TeMesh::Mode getMode() const = 0;
+	virtual void setMode(enum Mode mode) = 0;
 	bool hasAlpha(uint idx);
 	bool hasColor() const { return !_colors.empty(); }
 	bool hasUvs() const { return !_uvs.empty(); }
@@ -83,8 +85,6 @@ public:
 	unsigned short matrixIndex(uint num) const { return _matricies[num]; }
 	TeVector3f32 normal(uint idx) const;
 
-	TeMesh &operator=(const TeMesh &other) = default;
-
 	void optimizeVerticies();
 	void resizeUpdatedTables(unsigned long newSize);
 
@@ -108,10 +108,9 @@ public:
 	uint numIndexes() const { return _indexes.size(); }
 	uint numVerticies() const { return _verticies.size(); }
 	bool shouldDrawMaybe() const { return _shouldDraw; }
-	uint32 gltexEnvMode() const { return _gltexEnvMode; }
 
 	void setShouldDraw(bool val) { _shouldDraw = val; }
-	void setglTexEnvBlend();
+	virtual void setglTexEnvBlend() = 0;
 	void setHasAlpha(bool val) { _hasAlpha = val; }
 
 	Common::Array<TeMaterial> &materials() { return _materials; }
@@ -121,7 +120,9 @@ public:
 	const TeVector3f32 &preUpdatedVertex(uint idx) const { return _verticies[idx]; }
 	const TeVector3f32 &preUpdatedNormal(uint idx) const { return _normals[idx]; }
 
-private:
+	static TeMesh *makeInstance();
+
+protected:
 	Common::Array<unsigned char> _materialIndexes;
 	Common::Array<TeVector3f32> _verticies;
 	Common::Array<TeVector3f32> _normals;
@@ -134,8 +135,6 @@ private:
 	Common::Array<TeColor> _colors;
 	Common::Array<TeMaterial> _materials;
 
-	uint _glMeshMode;
-
 	bool _matrixForced;
 	TeMatrix4x4 _forcedMatrix;
 	bool _hasAlpha;
@@ -143,8 +142,6 @@ private:
 	bool _drawWires;
 	bool _shouldDraw;
 
-	uint32 _gltexEnvMode;
-
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_mesh_opengl.cpp b/engines/tetraedge/te/te_mesh_opengl.cpp
new file mode 100644
index 00000000000..86ebdaeada5
--- /dev/null
+++ b/engines/tetraedge/te/te_mesh_opengl.cpp
@@ -0,0 +1,261 @@
+/* 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 "graphics/opengl/system_headers.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_mesh_opengl.h"
+#include "tetraedge/te/te_material.h"
+
+namespace Tetraedge {
+
+TeMeshOpenGL::TeMeshOpenGL() : _glMeshMode(GL_POINTS), _gltexEnvMode(GL_MODULATE) {
+}
+
+void TeMeshOpenGL::draw() {
+	if (!worldVisible())
+		return;
+
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->pushMatrix();
+	if (_matrixForced)
+		renderer->multiplyMatrix(_forcedMatrix);
+	else
+		renderer->multiplyMatrix(worldTransformationMatrix());
+
+	/*
+	debug("Draw mesh %p (%s, %d verts %d norms %d indexes %d materials %d updated)", this, name().empty() ? "no name" : name().c_str(), _verticies.size(), _normals.size(), _indexes.size(), _materials.size(), _updatedVerticies.size());
+	debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
+	if (!_materials.empty())
+		debug("   material   %s", _materials[0].dump().c_str());
+	debug("   position   %s", position().dump().c_str());
+	debug("   worldPos   %s", worldPosition().dump().c_str());
+	debug("   scale      %s", scale().dump().c_str());
+	debug("   worldScale %s", worldScale().dump().c_str());
+	debug("   rotation   %s", rotation().dump().c_str());
+	debug("   worldRot   %s", worldRotation().dump().c_str());
+	*/
+
+	Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
+	Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
+	if (renderer->shadowMode() != TeRenderer::ShadowMode1) {
+		if (_faceCounts.empty()) {
+			if (hasAlpha(0) && _shouldDraw) {
+				renderer->addTransparentMesh(*this, 0, 0, 0);
+				renderer->popMatrix();
+				return;
+			}
+		} else {
+			assert(_faceCounts.size() == _materials.size());
+			int totalFaceCount = 0;
+			for (uint i = 0; i < _faceCounts.size(); i++) {
+				if (!_faceCounts[i])
+					continue;
+				if (hasAlpha(i)) {
+					renderer->addTransparentMesh(*this, totalFaceCount, _faceCounts[i], i);
+				}
+				totalFaceCount += _faceCounts[i];
+			}
+		}
+	}
+
+	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
+	renderer->pushMatrix();
+	renderer->loadCurrentMatrixToGL();
+	glEnableClientState(GL_VERTEX_ARRAY);
+	if (!normals.empty())
+		glEnableClientState(GL_NORMAL_ARRAY);
+
+	if (!_colors.empty())
+		glEnableClientState(GL_COLOR_ARRAY);
+
+	glVertexPointer(3, GL_FLOAT, 12, verticies.data());
+	if (!normals.empty())
+		glNormalPointer(GL_FLOAT, 12, normals.data());
+
+	if (!_uvs.empty() && renderer->shadowMode() != TeRenderer::ShadowMode2)
+		glTexCoordPointer(2, GL_FLOAT, 8, _uvs.data());
+
+	if (!_colors.empty())
+		glColorPointer(4, GL_UNSIGNED_BYTE, 4, _colors.data());
+
+	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, _gltexEnvMode);
+	if (renderer->scissorEnabled()) {
+		glEnable(GL_SCISSOR_TEST);
+		uint scissorx = renderer->scissorX();
+		uint scissory = renderer->scissorY();
+		uint scissorwidth = renderer->scissorWidth();
+		uint scissorheight = renderer->scissorHeight();
+		glScissor(scissorx, scissory, scissorwidth, scissorheight);
+	}
+
+	if (_faceCounts.empty()) {
+		if (!_materials.empty())
+			renderer->applyMaterial(_materials[0]);
+
+		glDrawElements(_glMeshMode, _indexes.size(), GL_UNSIGNED_SHORT, _indexes.data());
+		if (!_materials.empty()) {
+			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+			renderer->disableTexture();
+		}
+	} else {
+		int totalFaceCount = 0;
+		assert(_faceCounts.size() == _materials.size());
+		for (uint i = 0; i < _materials.size(); i++) {
+			if (!_faceCounts[i])
+				continue;
+			if (!hasAlpha(i) || renderer->shadowMode() == TeRenderer::ShadowMode1 || !_shouldDraw) {
+				renderer->applyMaterial(_materials[i]);
+				glDrawElements(_glMeshMode, _faceCounts[i] * 3, GL_UNSIGNED_SHORT, _indexes.data() + totalFaceCount * 3);
+				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+				renderer->disableTexture();
+			}
+			totalFaceCount += _faceCounts[i];
+		}
+	}
+
+	if (!renderer->scissorEnabled())
+		glDisable(GL_SCISSOR_TEST);
+
+	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+	glDisableClientState(GL_VERTEX_ARRAY);
+	glDisableClientState(GL_NORMAL_ARRAY);
+	glDisableClientState(GL_COLOR_ARRAY);
+
+	//renderer->setCurrentColor(renderer->currentColor()); // pointless?
+
+	if (_drawWires && !normals.empty()) {
+		renderer->disableAllLights();
+		error("TODO: Properly implement _drawWires case in TeMesh::draw");
+		/*
+		// TODO: Reimplement without glBegin/glEnd
+		glBegin(GL_LINES);
+		renderer->setCurrentColor(TeColor(255, 255, 255, 255));
+		for (uint i = 0; i < verticies.size(); i++) {
+			glVertex3f(verticies[i].x(), verticies[i].y(), verticies[i].z());
+			glVertex3f(verticies[i].x() + normals[i].x(),
+					verticies[i].y() + normals[i].y(),
+					verticies[i].z() + normals[i].z());
+		}
+		glEnd();
+		*/
+	}
+
+	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
+	renderer->popMatrix();
+	renderer->popMatrix();
+}
+
+TeMesh::Mode TeMeshOpenGL::getMode() const {
+	// Do the reverse translation of setConf... why? I dunno.. the game does that..
+	switch(_glMeshMode) {
+	case GL_POINTS:
+		return MeshMode_Points;
+	case GL_LINES:
+		return MeshMode_Lines;
+	case GL_LINE_LOOP:
+		return MeshMode_LineLoop;
+	case GL_LINE_STRIP:
+		return MeshMode_LineStrip;
+	case GL_TRIANGLES:
+		return MeshMode_Triangles;
+	case GL_TRIANGLE_STRIP:
+		return MeshMode_TriangleStrip;
+	case GL_TRIANGLE_FAN:
+		return MeshMode_TriangleFan;
+	default:
+		return MeshMode_None;
+	}
+}
+
+void TeMeshOpenGL::setMode(enum Mode mode) {
+	switch(mode) {
+	case MeshMode_Points:
+		_glMeshMode = GL_POINTS;
+		break;
+	case MeshMode_Lines:
+		_glMeshMode = GL_LINES;
+		break;
+	case MeshMode_LineLoop:
+		_glMeshMode = GL_LINE_LOOP;
+		break;
+	case MeshMode_LineStrip:
+		_glMeshMode = GL_LINE_STRIP;
+		break;
+	case MeshMode_Triangles:
+		_glMeshMode = GL_TRIANGLES;
+		break;
+	case MeshMode_TriangleStrip:
+		_glMeshMode = GL_TRIANGLE_STRIP;
+		break;
+	case MeshMode_TriangleFan:
+		_glMeshMode = GL_TRIANGLE_FAN;
+		break;
+	default:
+		error("Invalid mesh mode %d", (int)mode);
+	}
+}
+
+void TeMeshOpenGL::setglTexEnvBlend() {
+	_gltexEnvMode = GL_BLEND;
+}
+
+uint32 TeMeshOpenGL::getTexEnvMode() const {
+	return _gltexEnvMode;
+}
+
+void TeMeshOpenGL::setConf(unsigned long vertexCount, unsigned long indexCount, enum Mode mode, uint materialCount, uint materialIndexCount) {
+	destroy();
+	_initialMaterialIndexCount = materialIndexCount;
+	_verticies.resize(vertexCount);
+	_indexes.resize(indexCount);
+	_materials.resize(materialCount);
+	_matricies.resize(vertexCount);
+	switch(mode) {
+	case MeshMode_Points:
+		_glMeshMode = GL_POINTS;
+		break;
+	case MeshMode_Lines:
+		_glMeshMode = GL_LINES;
+		break;
+	case MeshMode_LineLoop:
+		_glMeshMode = GL_LINE_LOOP;
+		break;
+	case MeshMode_LineStrip:
+		_glMeshMode = GL_LINE_STRIP;
+		break;
+	case MeshMode_Triangles:
+		_glMeshMode = GL_TRIANGLES;
+		break;
+	case MeshMode_TriangleStrip:
+		_glMeshMode = GL_TRIANGLE_STRIP;
+		break;
+	case MeshMode_TriangleFan:
+		_glMeshMode = GL_TRIANGLE_FAN;
+		break;
+	default:
+		error("Setting invalid mesh mode.");
+	}
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_mesh_opengl.h b/engines/tetraedge/te/te_mesh_opengl.h
new file mode 100644
index 00000000000..9e1b6fad2d9
--- /dev/null
+++ b/engines/tetraedge/te/te_mesh_opengl.h
@@ -0,0 +1,55 @@
+/* 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_TE_TE_MESH_OPENGL_H
+#define TETRAEDGE_TE_TE_MESH_OPENGL_H
+
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+
+#include "tetraedge/te/te_mesh.h"
+
+namespace Tetraedge {
+
+class TeMeshOpenGL : public TeMesh {
+public:
+	TeMeshOpenGL();
+
+	void copy(const TeMesh &other);
+	void draw() override;
+	TeMesh::Mode getMode() const override;
+	void setMode(enum Mode mode) override;
+
+	void setConf(unsigned long vertexCount, unsigned long indexCount, enum Mode mode, uint materialCount, uint materialIndexCount);
+
+	void setglTexEnvBlend() override;
+	uint32 getTexEnvMode() const override;
+
+private:
+	uint _glMeshMode;
+	uint32 _gltexEnvMode;
+
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_OPENGL
+
+#endif // TETRAEDGE_TE_TE_MESH_H
diff --git a/engines/tetraedge/te/te_mesh_tinygl.cpp b/engines/tetraedge/te/te_mesh_tinygl.cpp
new file mode 100644
index 00000000000..8863f5e5dc4
--- /dev/null
+++ b/engines/tetraedge/te/te_mesh_tinygl.cpp
@@ -0,0 +1,231 @@
+/* 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 "graphics/tinygl/tinygl.h"
+
+#include "tetraedge/tetraedge.h"
+#include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_mesh_tinygl.h"
+
+namespace Tetraedge {
+
+TeMeshTinyGL::TeMeshTinyGL() : _glMeshMode(TGL_POINTS), _gltexEnvMode(TGL_MODULATE) {
+}
+
+void TeMeshTinyGL::draw() {
+	if (!worldVisible())
+		return;
+
+	TeRenderer *renderer = g_engine->getRenderer();
+	renderer->pushMatrix();
+	if (_matrixForced)
+		renderer->multiplyMatrix(_forcedMatrix);
+	else
+		renderer->multiplyMatrix(worldTransformationMatrix());
+
+	/*
+	debug("Draw mesh %p (%s, %d verts %d norms %d indexes %d materials %d updated)", this, name().empty() ? "no name" : name().c_str(), _verticies.size(), _normals.size(), _indexes.size(), _materials.size(), _updatedVerticies.size());
+	debug("   renderMatrix %s", renderer->currentMatrix().toString().c_str());
+	if (!_materials.empty())
+		debug("   material   %s", _materials[0].dump().c_str());
+	debug("   position   %s", position().dump().c_str());
+	debug("   worldPos   %s", worldPosition().dump().c_str());
+	debug("   scale      %s", scale().dump().c_str());
+	debug("   worldScale %s", worldScale().dump().c_str());
+	debug("   rotation   %s", rotation().dump().c_str());
+	debug("   worldRot   %s", worldRotation().dump().c_str());
+	*/
+
+	Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
+	Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
+	if (renderer->shadowMode() != TeRenderer::ShadowMode1) {
+		if (_faceCounts.empty()) {
+			if (hasAlpha(0) && _shouldDraw) {
+				renderer->addTransparentMesh(*this, 0, 0, 0);
+				renderer->popMatrix();
+				return;
+			}
+		} else {
+			assert(_faceCounts.size() == _materials.size());
+			int totalFaceCount = 0;
+			for (uint i = 0; i < _faceCounts.size(); i++) {
+				if (!_faceCounts[i])
+					continue;
+				if (hasAlpha(i)) {
+					renderer->addTransparentMesh(*this, totalFaceCount, _faceCounts[i], i);
+				}
+				totalFaceCount += _faceCounts[i];
+			}
+		}
+	}
+
+	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
+	renderer->pushMatrix();
+	renderer->loadCurrentMatrixToGL();
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+	if (!normals.empty())
+		tglEnableClientState(TGL_NORMAL_ARRAY);
+
+	if (!_colors.empty())
+		tglEnableClientState(TGL_COLOR_ARRAY);
+
+	tglVertexPointer(3, TGL_FLOAT, 12, verticies.data());
+	if (!normals.empty())
+		tglNormalPointer(TGL_FLOAT, 12, normals.data());
+
+	if (!_uvs.empty() && renderer->shadowMode() != TeRenderer::ShadowMode2)
+		tglTexCoordPointer(2, TGL_FLOAT, 8, _uvs.data());
+
+	if (!_colors.empty())
+		tglColorPointer(4, TGL_UNSIGNED_BYTE, 4, _colors.data());
+
+	// TODO: not supported in TGL
+	//tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, _gltexEnvMode);
+	if (renderer->scissorEnabled()) {
+		tglEnable(TGL_SCISSOR_TEST);
+		// TODO: Scissor not supported by TGL
+		/*
+		uint scissorx = renderer->scissorX();
+		uint scissory = renderer->scissorY();
+		uint scissorwidth = renderer->scissorWidth();
+		uint scissorheight = renderer->scissorHeight();
+		//tglScissor(scissorx, scissory, scissorwidth, scissorheight);
+		*/
+	}
+
+	if (_faceCounts.empty()) {
+		if (!_materials.empty())
+			renderer->applyMaterial(_materials[0]);
+
+		tglDrawElements(_glMeshMode, _indexes.size(), TGL_UNSIGNED_SHORT, _indexes.data());
+		if (!_materials.empty()) {
+			tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
+			renderer->disableTexture();
+		}
+	} else {
+		int totalFaceCount = 0;
+		assert(_faceCounts.size() == _materials.size());
+		for (uint i = 0; i < _materials.size(); i++) {
+			if (!_faceCounts[i])
+				continue;
+			if (!hasAlpha(i) || renderer->shadowMode() == TeRenderer::ShadowMode1 || !_shouldDraw) {
+				renderer->applyMaterial(_materials[i]);
+				tglDrawElements(_glMeshMode, _faceCounts[i] * 3, TGL_UNSIGNED_SHORT, _indexes.data() + totalFaceCount * 3);
+				tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
+				renderer->disableTexture();
+			}
+			totalFaceCount += _faceCounts[i];
+		}
+	}
+
+	if (!renderer->scissorEnabled())
+		tglDisable(TGL_SCISSOR_TEST);
+
+	// TODO: not supported in TGL
+	//tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+	tglDisableClientState(TGL_NORMAL_ARRAY);
+	tglDisableClientState(TGL_COLOR_ARRAY);
+
+	//renderer->setCurrentColor(renderer->currentColor()); // pointless?
+
+	if (_drawWires && !normals.empty()) {
+		renderer->disableAllLights();
+		error("TODO: Properly implement _drawWires case in TeMesh::draw");
+		/*
+		// TODO: Reimplement without glBegin/glEnd
+		glBegin(TGL_LINES);
+		renderer->setCurrentColor(TeColor(255, 255, 255, 255));
+		for (uint i = 0; i < verticies.size(); i++) {
+			glVertex3f(verticies[i].x(), verticies[i].y(), verticies[i].z());
+			glVertex3f(verticies[i].x() + normals[i].x(),
+					verticies[i].y() + normals[i].y(),
+					verticies[i].z() + normals[i].z());
+		}
+		glEnd();
+		*/
+	}
+
+	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
+	renderer->popMatrix();
+	renderer->popMatrix();
+}
+
+TeMesh::Mode TeMeshTinyGL::getMode() const {
+	switch(_glMeshMode) {
+	case TGL_POINTS:
+		return MeshMode_Points;
+	case TGL_LINES:
+		return MeshMode_Lines;
+	case TGL_LINE_LOOP:
+		return MeshMode_LineLoop;
+	case TGL_LINE_STRIP:
+		return MeshMode_LineStrip;
+	case TGL_TRIANGLES:
+		return MeshMode_Triangles;
+	case TGL_TRIANGLE_STRIP:
+		return MeshMode_TriangleStrip;
+	case TGL_TRIANGLE_FAN:
+		return MeshMode_TriangleFan;
+	default:
+		return MeshMode_None;
+	}
+}
+
+void TeMeshTinyGL::setMode(enum Mode mode) {
+	switch(mode) {
+	case MeshMode_Points:
+		_glMeshMode = TGL_POINTS;
+		break;
+	case MeshMode_Lines:
+		_glMeshMode = TGL_LINES;
+		break;
+	case MeshMode_LineLoop:
+		_glMeshMode = TGL_LINE_LOOP;
+		break;
+	case MeshMode_LineStrip:
+		_glMeshMode = TGL_LINE_STRIP;
+		break;
+	case MeshMode_Triangles:
+		_glMeshMode = TGL_TRIANGLES;
+		break;
+	case MeshMode_TriangleStrip:
+		_glMeshMode = TGL_TRIANGLE_STRIP;
+		break;
+	case MeshMode_TriangleFan:
+		_glMeshMode = TGL_TRIANGLE_FAN;
+		break;
+	default:
+		error("Invalid mesh mode %d", (int)mode);
+	}
+}
+
+void TeMeshTinyGL::setglTexEnvBlend() {
+	_gltexEnvMode = TGL_BLEND;
+}
+
+uint32 TeMeshTinyGL::getTexEnvMode() const {
+	return _gltexEnvMode;
+}
+
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_mesh_tinygl.h b/engines/tetraedge/te/te_mesh_tinygl.h
new file mode 100644
index 00000000000..73d6e6cd8fa
--- /dev/null
+++ b/engines/tetraedge/te/te_mesh_tinygl.h
@@ -0,0 +1,54 @@
+/* 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_TE_TE_MESH_TINYGL_H
+#define TETRAEDGE_TE_TE_MESH_TINYGL_H
+
+#if defined(USE_TINYGL)
+
+#include "tetraedge/te/te_mesh.h"
+
+namespace Tetraedge {
+
+class TeMeshTinyGL : public TeMesh {
+public:
+	TeMeshTinyGL();
+
+	void copy(const TeMesh &other);
+	void draw() override;
+	TeMesh::Mode getMode() const override;
+	void setMode(enum Mode mode) override;
+
+	void setConf(unsigned long vertexCount, unsigned long indexCount, enum Mode mode, uint materialCount, uint materialIndexCount);
+
+	void setglTexEnvBlend() override;
+	uint32 getTexEnvMode() const override;
+
+private:
+	uint _glMeshMode;
+	uint32 _gltexEnvMode;
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_TINYGL
+
+#endif // TETRAEDGE_TE_TE_MESH_H
diff --git a/engines/tetraedge/te/te_model.cpp b/engines/tetraedge/te/te_model.cpp
index 919a12253ac..b80265f1701 100644
--- a/engines/tetraedge/te/te_model.cpp
+++ b/engines/tetraedge/te/te_model.cpp
@@ -113,13 +113,13 @@ void TeModel::draw() {
 			//debug("   rotation   %s", rotation().dump().c_str());
 			debug("   worldRot   %s", worldRotation().dump().c_str());
 		}*/
-		for (TeMesh &mesh : _meshes) {
+		for (auto &mesh : _meshes) {
 			// TODO: Set some flag (_drawWires?) in mesh to this->field_0x158??
-			mesh.draw();
+			mesh->draw();
 		}
 		renderer->popMatrix();
 		// Note: no corresponding enableAll - they can be enabled inside TeMaterial::apply
-		TeLight::disableAll();
+		renderer->disableAllLights();
 	}
 }
 
@@ -162,8 +162,8 @@ void TeModel::removeAnim() {
 
 void TeModel::setColor(const TeColor &col) {
 	Te3DObject2::setColor(col);
-	for (TeMesh &mesh : _meshes) {
-		mesh.setColor(col);
+	for (auto &mesh : _meshes) {
+		mesh->setColor(col);
 	}
 }
 
@@ -236,7 +236,7 @@ void TeModel::update() {
 		}
 
 		for (uint m = 0; m < _meshes.size(); m++) {
-			TeMesh &mesh = _meshes[m];
+			TeMesh &mesh = *_meshes[m];
 			if (!mesh.visible())
 				continue;
 
@@ -297,14 +297,14 @@ void TeModel::update() {
 		}
 	} else {
 		// No bones..
-		for (TeMesh &mesh : _meshes) {
+		for (auto &mesh : _meshes) {
 			if (!_modelVertexAnim) {
-				mesh.update(nullptr, nullptr);
+				mesh->update(nullptr, nullptr);
 			} else {
-				if (mesh.name() != _modelVertexAnim->head()) {
-					mesh.update(nullptr, nullptr);
+				if (mesh->name() != _modelVertexAnim->head()) {
+					mesh->update(nullptr, nullptr);
 				} else {
-					mesh.update(_modelVertexAnim);
+					mesh->update(_modelVertexAnim);
 				}
 			}
 		}
@@ -313,10 +313,10 @@ void TeModel::update() {
 
 TeModel::MeshBlender::MeshBlender(const Common::String &name, const Common::String &meshName, float amount, TeModel *model) :
 _name(name), _amount(amount) {
-	Common::Array<TeMesh> &meshes = model->_meshes;
+	const auto &meshes = model->_meshes;
 	uint i = 0;
 	for (; i < meshes.size(); i++) {
-		if (meshes[i].name().contains(meshName))
+		if (meshes[i]->name().contains(meshName))
 			break;
 	}
 	_meshNo = i;
@@ -356,6 +356,9 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 	if (meshCount > 100000)
 		error("TeModel::load: Unexpected number of meshes %d", meshCount);
 	_meshes.resize(meshCount);
+	for (uint i = 0; i < meshCount; i++)
+		_meshes[i].reset(TeMesh::makeInstance());
+
 	uint32 weightCount = stream.readUint32LE();
 	if (weightCount > 100000)
 		error("TeModel::load: Unexpected number of weights %d", weightCount);
@@ -385,7 +388,7 @@ bool TeModel::load(Common::SeekableReadStream &stream) {
 	}
 
 	for (uint m = 0; m < _meshes.size(); m++) {
-		if (!loadMesh(stream, _meshes[m])) {
+		if (!loadMesh(stream, *_meshes[m])) {
 			error("[TeModel::load] Error on meshes loading.");
 		}
 	}
@@ -552,19 +555,19 @@ bool TeModel::loadMesh(Common::SeekableReadStream &stream, TeMesh &mesh) {
 }
 
 void TeModel::setQuad(const TeIntrusivePtr<Te3DTexture> &tex, const Common::Array<TeVector3f32> &verts, const TeColor &col) {
-	TeMesh mesh;
-	mesh.setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
-	mesh.defaultMaterial(tex);
+	Common::SharedPtr<TeMesh> mesh(TeMesh::makeInstance());
+	mesh->setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
+	mesh->defaultMaterial(tex);
 
 	for (int i = 0; i < 2; i++) {
 		float f = (i == 0 ? 0.0f : 1.0f);
 		for (int j = 0; j < 2; j++) {
 			int index = i * 2 + j;
-			mesh.setVertex(index, verts[i * 2 + j]);
-			mesh.setTextureUV(index, TeVector2f32(f, (j == 0 ? 0.0f : 1.0f)));
-			mesh.setIndex(index, index);
+			mesh->setVertex(index, verts[i * 2 + j]);
+			mesh->setTextureUV(index, TeVector2f32(f, (j == 0 ? 0.0f : 1.0f)));
+			mesh->setIndex(index, index);
 			if (col.a() != 0)
-				mesh.setColor(index, col);
+				mesh->setColor(index, col);
 		}
 	}
 
@@ -573,7 +576,7 @@ void TeModel::setQuad(const TeIntrusivePtr<Te3DTexture> &tex, const Common::Arra
 	TeVector3f32 v3 = TeVector3f32::crossProduct(v1, v2);
 	v3.normalize();
 	for (int i = 0; i < 4; i++) {
-		mesh.setNormal(i, v3);
+		mesh->setNormal(i, v3);
 	}
 	_meshes.push_back(mesh);
 }
@@ -593,9 +596,9 @@ void TeModel::setVertexAnim(TeIntrusivePtr<TeModelVertexAnimation> &anim, bool r
 }
 
 void TeModel::setVisibleByName(const Common::String &name, bool vis) {
-	for (TeMesh &mesh : _meshes) {
-		if (mesh.name().contains(name)) {
-			mesh.setVisible(vis);
+	for (auto &mesh : _meshes) {
+		if (mesh->name().contains(name)) {
+			mesh->setVisible(vis);
 		}
 	}
 }
diff --git a/engines/tetraedge/te/te_model.h b/engines/tetraedge/te/te_model.h
index 60862309f79..52dfb296618 100644
--- a/engines/tetraedge/te/te_model.h
+++ b/engines/tetraedge/te/te_model.h
@@ -76,7 +76,7 @@ public:
 	TeModel();
 	virtual ~TeModel();
 
-	void addMesh(const TeMesh &mesh) {
+	void addMesh(Common::SharedPtr<TeMesh> mesh) {
 		_meshes.push_back(mesh);
 	}
 	TeIntrusivePtr<TeModelAnimation> anim() {
@@ -128,7 +128,7 @@ public:
 
 	TeSignal2Param<const Common::String &, TeMatrix4x4 &> &bonesUpdatedSignal() { return _bonesUpdatedSignal; }
 	Common::Array<BonesBlender *> &boneBlenders() { return _boneBlenders; }
-	Common::Array<TeMesh> &meshes() { return _meshes; }
+	Common::Array<Common::SharedPtr<TeMesh>> &meshes() { return _meshes; }
 	TeIntrusivePtr<TeTiledTexture> tiledTexture() { return _tiledTexture; }
 
 	void setEnableLights(bool val) { _enableLights = val; }
@@ -153,7 +153,7 @@ protected:
 	Common::Array<TeMatrix4x4> _boneMatricies;
 	Common::Array<TeMatrix4x4> _lerpedElements;
 	Common::Array<Common::Array<weightElement>> _weightElements;
-	Common::Array<TeMesh> _meshes;
+	Common::Array<Common::SharedPtr<TeMesh>> _meshes;
 
 	TeQuaternion _boneRotation;
 
diff --git a/engines/tetraedge/te/te_pick_mesh2.cpp b/engines/tetraedge/te/te_pick_mesh2.cpp
index 4e027aa4408..284316268a1 100644
--- a/engines/tetraedge/te/te_pick_mesh2.cpp
+++ b/engines/tetraedge/te/te_pick_mesh2.cpp
@@ -39,11 +39,11 @@ void TePickMesh2::draw() {
 		return;
 
 	const uint nverticies = _verticies.size();
-	TeMesh mesh;
-	mesh.setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
+	Common::SharedPtr<TeMesh> mesh(TeMesh::makeInstance());
+	mesh->setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
 	for (uint i = 0; i < nverticies; i++) {
-		mesh.setIndex(i, i);
-		mesh.setVertex(i, _verticies[i]);
+		mesh->setIndex(i, i);
+		mesh->setVertex(i, _verticies[i]);
 	}
 
 	TeRenderer *renderer = g_engine->getRenderer();
@@ -54,7 +54,7 @@ void TePickMesh2::draw() {
 	renderer->setCurrentColor(TeColor(0xff, 0, 0, 0xff));
 	renderer->pushMatrix();
 	renderer->multiplyMatrix(transformationMatrix());
-	mesh.draw();
+	mesh->draw();
 
 	renderer->popMatrix();
 	renderer->setCurrentColor(prevCol);
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index bf97c200eba..900164b320b 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -22,9 +22,12 @@
 #include "common/textconsole.h"
 #include "common/debug.h"
 
-#include "graphics/opengl/system_headers.h"
+#include "graphics/renderer.h"
+#include "tetraedge/tetraedge.h"
 
 #include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_renderer_opengl.h"
+#include "tetraedge/te/te_renderer_tinygl.h"
 #include "tetraedge/te/te_light.h"
 
 namespace Tetraedge {
@@ -138,7 +141,7 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 
 		destProperties._material = *mesh.material(materialno);
 		destProperties._matrix = currentMatrix;
-		destProperties._glTexEnvMode = mesh.gltexEnvMode();
+		destProperties._glTexEnvMode = mesh.getTexEnvMode();
 		destProperties._sourceTransparentMesh = _numTransparentMeshes * 3;
 		destProperties._hasColor = mesh.hasColor();
 		destProperties._zOrder = zOrder;
@@ -178,7 +181,7 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 			destProperties._camera = _currentCamera;
 
 			destProperties._material = *mesh.material(materialno);
-			destProperties._glTexEnvMode = mesh.gltexEnvMode();
+			destProperties._glTexEnvMode = mesh.getTexEnvMode();
 			destProperties._sourceTransparentMesh = meshPropNo;
 			destProperties._hasColor = mesh.hasColor();
 			destProperties._zOrder = zOrder;
@@ -194,17 +197,6 @@ void TeRenderer::addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsign
 	_pendingTransparentMeshProperties = _transparentMeshProps.size();
 }
 
-void TeRenderer::clearBuffer(TeRenderer::Buffer buf) {
-	GLenum glBuf = 0;
-	if (buf & StencilBuffer)
-		glBuf |= GL_STENCIL_BUFFER_BIT;
-	if (buf & DepthBuffer)
-		glBuf |= GL_DEPTH_BUFFER_BIT;
-	if (buf & ColorBuffer)
-		glBuf |= GL_COLOR_BUFFER_BIT;
-	glClear(glBuf);
-}
-
 void TeRenderer::create() {
 	_textureEnabled = false;
 	_currentCamera = nullptr;
@@ -217,68 +209,6 @@ TeMatrix4x4 TeRenderer::currentMatrix() {
 	return _matriciesStacks[_matrixMode].currentMatrix();
 }
 
-void TeRenderer::disableTexture() {
-	glDisable(GL_TEXTURE_2D);
-	_textureEnabled = false;
-}
-
-void TeRenderer::disableWireFrame() {
-	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-}
-
-void TeRenderer::disableZBuffer() {
-	glDisable(GL_DEPTH_TEST);
-	glDepthMask(GL_FALSE);
-}
-
-void TeRenderer::drawLine(const TeVector3f32 &from, const TeVector3f32 &to) {
-	error("TODO: Implement TeRenderer::drawLine");
-}
-
-void TeRenderer::enableTexture() {
-	glEnable(GL_TEXTURE_2D);
-	_textureEnabled = true;
-}
-
-void TeRenderer::enableWireFrame() {
-	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-}
-
-void TeRenderer::enableZBuffer() {
-	glEnable(GL_DEPTH_TEST);
-	glDepthMask(GL_TRUE);
-}
-
-void TeRenderer::init() {
-	glDisable(GL_CULL_FACE);
-	TeLight::disableAll();
-	glDisable(GL_COLOR_MATERIAL);
-	glEnable(GL_DEPTH_TEST);
-	glDepthMask(GL_TRUE);
-	glShadeModel(GL_SMOOTH);
-	glEnable(GL_BLEND);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-	glDepthFunc(GL_LEQUAL);
-	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);
-	glClearDepth(1.0);
-	glClearStencil(0);
-	_clearColor = TeColor(0, 0, 0, 255);
-	glClearColor(0, 0, 0, 1.0);
-	debug("[TeRenderer::init] Vendor : %s", glGetString(GL_VENDOR));
-	debug("[TeRenderer::init] Renderer : %s", glGetString(GL_RENDERER));
-	debug("[TeRenderer::init] Version : %s", glGetString(GL_VERSION));
-	int bits;
-	glGetIntegerv(GL_STENCIL_BITS, &bits);
-	debug("[TeRenderer::init] Sentil buffer bits : %d", bits);
-	glGetIntegerv(GL_DEPTH_BITS, &bits);
-	debug("[TeRenderer::init] Depth buffer bits : %d", bits);
-	//debug("[TeRenderer::init] Extensions : %s", glGetString(GL_EXTENSIONS));
-	//TeOpenGLExtensions::loadExtensions(); // this does nothing in the game?
-	_currentColor = TeColor(255, 255, 255, 255);
-	_scissorEnabled = false;
-	_scissorX = _scissorY = _scissorWidth = _scissorHeight = 0;
-}
-
 void TeRenderer::loadIdentityMatrix() {
 	_matriciesStacks[_matrixMode].loadIdentity();
 }
@@ -287,28 +217,11 @@ void TeRenderer::loadMatrix(const TeMatrix4x4 &matrix) {
 	_matriciesStacks[_matrixMode].loadMatrix(matrix);
 }
 
-void TeRenderer::loadMatrixToGL(const TeMatrix4x4 &matrix) {
-	//int mmode;
-	//glGetIntegerv(GL_MATRIX_MODE, &mmode);
-	//debug("loadMatrixToGL[0x%x]: %s", mmode, matrix.toString().c_str());
-	glLoadMatrixf(matrix.getData());
-}
-
 void TeRenderer::loadCurrentMatrixToGL() {
 	const TeMatrix4x4 current = currentMatrix();
 	loadMatrixToGL(current);
 }
 
-void TeRenderer::loadProjectionMatrix(const TeMatrix4x4 &matrix) {
-	glMatrixMode(GL_PROJECTION);
-	_matrixMode = MM_GL_PROJECTION;
-	_matriciesStacks[_matrixMode].loadIdentity();
-	_matriciesStacks[_matrixMode].loadMatrix(matrix);
-	glMatrixMode(GL_MODELVIEW);
-	_matrixMode = MM_GL_MODELVIEW;
-	_matriciesStacks[_matrixMode].loadIdentity();
-}
-
 void TeRenderer::multiplyMatrix(const TeMatrix4x4 &matrix) {
 	_matriciesStacks[_matrixMode].multiplyMatrix(matrix);
 }
@@ -346,16 +259,6 @@ void TeRenderer::pushMatrix() {
 	_matriciesStacks[_matrixMode].pushMatrix();
 }
 
-Common::String TeRenderer::renderer() {
-	return Common::String((const char *)glGetString(GL_RENDERER));
-}
-
-
-static bool compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
-											const TeRenderer::TransparentMeshProperties &p2) {
-	return (p1._zOrder < p2._zOrder);
-}
-
 void TeRenderer::dumpTransparentMeshProps() const {
 	debug("** Transparent MeshProps: num:%ld pending:%d **", _numTransparentMeshes, _pendingTransparentMeshProperties);
 	debug("draw? / nverts / source / transl / zorder");
@@ -384,122 +287,6 @@ void TeRenderer::dumpTransparentMeshData() const {
 	}
 }
 
-void TeRenderer::renderTransparentMeshes() {
-	if (!_numTransparentMeshes)
-		return;
-
-	glDepthMask(GL_FALSE);
-	//dumpTransparentMeshProps();
-
-	Common::sort(_transparentMeshProps.begin(), _transparentMeshProps.end(),
-		 compareTransparentMeshProperties);
-
-	int vertsDrawn = 0;
-	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
-		const uint vcount = _transparentMeshProps[i]._vertexCount;
-		for (uint j = 0; j < vcount; j++)
-			_transparentMeshVertexNums[vertsDrawn + j] = (short)(_transparentMeshProps[i]._sourceTransparentMesh + j);
-		vertsDrawn += vcount;
-	}
-
-	optimiseTransparentMeshProperties();
-
-	//dumpTransparentMeshProps();
-	//dumpTransparentMeshData();
-
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glEnableClientState(GL_NORMAL_ARRAY);
-	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	glEnableClientState(GL_COLOR_ARRAY);
-
-	glVertexPointer(3, GL_FLOAT, 12, _transparentMeshVertexes.data());
-	glNormalPointer(GL_FLOAT, 12, _transparentMeshNormals.data());
-	glTexCoordPointer(2, GL_FLOAT, 8, _transparentMeshCoords.data());
-	glColorPointer(4, GL_UNSIGNED_BYTE, 4, _transparentMeshColors.data());
-
-	TeMaterial lastMaterial;
-	TeMatrix4x4 lastMatrix;
-
-	vertsDrawn = 0;
-	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
-		const TransparentMeshProperties &meshProperties = _transparentMeshProps[i];
-		if (!meshProperties._shouldDraw)
-			continue;
-
-		const TeMaterial &material = meshProperties._material;
-
-		meshProperties._camera->applyProjection();
-		glMatrixMode(GL_MODELVIEW);
-		_matrixMode = MM_GL_MODELVIEW;
-		glPushMatrix();
-		_matriciesStacks[_matrixMode].pushMatrix();
-		_matriciesStacks[_matrixMode].loadMatrix(meshProperties._matrix);
-		glPushMatrix();
-		loadCurrentMatrixToGL();
-		if (material._texture) {
-			glEnable(GL_TEXTURE_2D);
-			_textureEnabled = true;
-		}
-		if (material._enableSomethingDefault0) {
-			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-			glDisableClientState(GL_COLOR_ARRAY);
-		}
-
-		if (material != lastMaterial) {
-			material.apply();
-			lastMaterial = material;
-		}
-
-		if (meshProperties._scissorEnabled) {
-			glEnable(GL_SCISSOR_TEST);
-			glScissor(meshProperties._scissorX,
-					  meshProperties._scissorY,
-					  meshProperties._scissorWidth,
-					  meshProperties._scissorHeight);
-		}
-		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, meshProperties._glTexEnvMode);
-		glDrawElements(GL_TRIANGLES, meshProperties._vertexCount, GL_UNSIGNED_SHORT,
-				   _transparentMeshVertexNums.data() + vertsDrawn);
-
-		vertsDrawn += meshProperties._vertexCount;
-
-		if (material._enableSomethingDefault0) {
-			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-			glEnableClientState(GL_COLOR_ARRAY);
-		}
-		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-		if (meshProperties._scissorEnabled) {
-			glDisable(GL_SCISSOR_TEST);
-		}
-		if (material._texture) {
-			glDisable(GL_TEXTURE_2D);
-			_textureEnabled = false;
-		}
-		glPopMatrix();
-		glPopMatrix();
-		_matriciesStacks[_matrixMode].popMatrix();
-		TeCamera::restore();
-	}
-	glDisableClientState(GL_VERTEX_ARRAY);
-	glDisableClientState(GL_NORMAL_ARRAY);
-	glDisableClientState(GL_COLOR_ARRAY);
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	_numTransparentMeshes = 0;
-	_pendingTransparentMeshProperties = 0;
-	glDepthMask(GL_TRUE);
-	_transparentMeshProps.clear();
-}
-
-void TeRenderer::reset() {
-	clearBuffer(AllBuffers);
-	glMatrixMode(GL_PROJECTION);
-	_matrixMode = MM_GL_PROJECTION;
-	_matriciesStacks[MM_GL_PROJECTION].loadIdentity();
-	glMatrixMode(GL_MODELVIEW);
-	_matrixMode = MM_GL_MODELVIEW;
-	_matriciesStacks[MM_GL_MODELVIEW].loadIdentity();
-}
-
 void TeRenderer::rotate(const TeQuaternion &rot) {
 	_matriciesStacks[_matrixMode].rotate(rot);
 }
@@ -512,33 +299,6 @@ void TeRenderer::scale(float xs, float ys, float zs) {
 	_matriciesStacks[_matrixMode].scale(TeVector3f32(xs, ys, zs));
 }
 
-void TeRenderer::setClearColor(const TeColor &col) {
-	_clearColor = col;
-	glClearColor(col.r() / 255.0f, col.g() / 255.0f, col.b() / 255.0f, col.a() / 255.0f);
-}
-
-void TeRenderer::setCurrentColor(const TeColor &col) {
-	if (col == _currentColor)
-		return;
-
-	glColor4ub(col.r(), col.g(), col.b(), col.a());
-	_currentColor = col;
-}
-
-void TeRenderer::setMatrixMode(enum MatrixMode mode) {
-	GLenum glmode = 0;
-	if (mode == MM_GL_TEXTURE)
-		glmode = GL_TEXTURE;
-	else if (mode == MM_GL_MODELVIEW)
-		glmode = GL_MODELVIEW;
-	else if (mode == MM_GL_PROJECTION)
-		glmode = GL_PROJECTION;
-
-	if (glmode)
-		glMatrixMode(glmode);
-	_matrixMode = mode;
-}
-
 void TeRenderer::setScissor(int x, int y, int w, int h) {
 	_scissorX = x;
 	_scissorY = y;
@@ -546,36 +306,26 @@ void TeRenderer::setScissor(int x, int y, int w, int h) {
 	_scissorHeight = h;
 }
 
-void TeRenderer::setViewport(int x, int y, int w, int h) {
-	glViewport(x, y, w, h);
+void TeRenderer::translate(float x, float y, float z) {
+	_matriciesStacks[_matrixMode].translate(TeVector3f32(x, y, z));
 }
 
-void TeRenderer::shadowMode(enum ShadowMode mode) {
-	_shadowMode = mode;
-	if (mode == ShadowMode0) {
-		glDisable(GL_CULL_FACE);
-		glShadeModel(GL_SMOOTH);
-		return;
-	}
 
-	if (mode == ShadowMode1) {
-		glEnable(GL_CULL_FACE);
-		glCullFace(GL_BACK);
-	} else { // ShadowMode2
-		glDisable(GL_CULL_FACE);
-	}
-	glEnable(GL_BLEND);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-	glShadeModel(GL_FLAT);
-	TeLight::disableAll();
-}
+/*static*/
+TeRenderer *TeRenderer::makeInstance() {
+	Graphics::RendererType r = g_engine->preferredRendererType();
 
-void TeRenderer::translate(float x, float y, float z) {
-	_matriciesStacks[_matrixMode].translate(TeVector3f32(x, y, z));
-}
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+	if (r == Graphics::kRendererTypeOpenGL)
+		return new TeRendererOpenGL();
+#endif
 
-Common::String TeRenderer::vendor() {
-	return Common::String((const char *)glGetString(GL_VENDOR));
+#if defined(USE_TINYGL)
+	if (r == Graphics::kRendererTypeTinyGL)
+		return new TeRendererTinyGL();
+#endif
+	error("Couldn't create TeRenderer for selected renderer");
 }
 
+
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index a481d2f2936..c50ecb7ac95 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -33,6 +33,7 @@ namespace Tetraedge {
 class TeRenderer {
 public:
 	TeRenderer();
+	virtual ~TeRenderer() {};
 
 	enum MatrixMode {
 		MM_GL_PROJECTION = 0,
@@ -49,6 +50,7 @@ public:
 	class TransparentMeshProperties {
 	public:
 		TransparentMeshProperties() : _camera(nullptr), _vertexCount(0), _shouldDraw(false), _scissorEnabled(false), _hasColor(false) {}
+
 		TeCamera *_camera;
 		int _vertexCount;
 		TeMatrix4x4 _matrix;
@@ -77,29 +79,31 @@ public:
 
 	void addTransparentMesh(const TeMesh &mesh, unsigned long i1, unsigned long i2, unsigned long i3);
 	void checkError(const Common::String &str) {};
-	void clearBuffer(Buffer buf);
+	virtual void clearBuffer(Buffer buf) = 0;
 	void create();
 	TeMatrix4x4 currentMatrix();
-	void disableTexture();
-	void disableWireFrame();
-	void disableZBuffer();
-	void drawLine(const TeVector3f32 &from, const TeVector3f32 &to);
-	void enableTexture();
-	void enableWireFrame();
-	void enableZBuffer();
+	virtual void disableAllLights() = 0;
+	virtual void disableTexture() = 0;
+	virtual void disableWireFrame() = 0;
+	virtual void disableZBuffer() = 0;
+	virtual void drawLine(const TeVector3f32 &from, const TeVector3f32 &to) = 0;
+	virtual void enableAllLights() = 0;
+	virtual void enableTexture() = 0;
+	virtual void enableWireFrame() = 0;
+	virtual void enableZBuffer() = 0;
 	//void extractFrameBufferToImg(const TeVector2s32 &from, const TeVector2s32 &to, TeImage &output);
-	void init();
+	virtual void init(uint width, uint height) = 0;
 	void loadIdentityMatrix();
 	void loadMatrix(const TeMatrix4x4 &matrix);
 	void loadCurrentMatrixToGL();
-	void loadProjectionMatrix(const TeMatrix4x4 &matrix);
+	virtual void loadProjectionMatrix(const TeMatrix4x4 &matrix) = 0;
 	void multiplyMatrix(const TeMatrix4x4 &matrix);
 	void optimiseTransparentMeshProperties();
 	void popMatrix();
 	void pushMatrix();
-	Common::String renderer();
-	void renderTransparentMeshes();
-	void reset();
+	virtual Common::String renderer() = 0;
+	virtual void renderTransparentMeshes() = 0;
+	virtual void reset() = 0;
 	void rotate(const TeQuaternion &rot);
 	void rotate(float angle, float rx, float ry, float rz);
 	void scale(float xs, float ys, float zs);
@@ -109,25 +113,29 @@ public:
 	int scissorX() const { return _scissorX; }
 	int scissorY() const { return _scissorY; }
 	void sendModelMatrix(const TeMatrix4x4 &matrix) {}
-	void setClearColor(const TeColor &col);
+	virtual void setClearColor(const TeColor &col) = 0;
 	void setCurrentCamera(TeCamera *camera) {
 		_currentCamera = camera;
 	}
-	void setCurrentColor(const TeColor &col);
-	void setMatrixMode(enum MatrixMode mode);
+	virtual void setCurrentColor(const TeColor &col) = 0;
+	virtual void setMatrixMode(enum MatrixMode mode) = 0;
 	void setScissor(int x, int y, int w, int h);
 	void setScissorEnabled(bool val) { _scissorEnabled = val; }
-	void setViewport(int x, int y, int w, int h);
-	void shadowMode(enum ShadowMode mode);
+	virtual void setViewport(int x, int y, int w, int h) = 0;
+	virtual void shadowMode(enum ShadowMode mode) = 0;
 	enum ShadowMode shadowMode() const { return _shadowMode; }
 	void translate(float x, float y, float z);
-	Common::String vendor();
+	virtual Common::String vendor() = 0;
 
 	void dumpTransparentMeshProps() const;
 	void dumpTransparentMeshData() const;
 	const TeColor &currentColor() const { return _currentColor; }
 
-private:
+	virtual void applyMaterial(const TeMaterial &m) = 0;
+
+	static TeRenderer *makeInstance();
+
+protected:
 	TeCamera *_currentCamera;
 	TeColor _currentColor;
 	TeColor _clearColor;
@@ -154,7 +162,7 @@ private:
 
 	TeMatriciesStack _matriciesStacks[3];  // one per matrix mode.
 
-	void loadMatrixToGL(const TeMatrix4x4 &matrix);
+	virtual void loadMatrixToGL(const TeMatrix4x4 &matrix) = 0;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_renderer_opengl.cpp b/engines/tetraedge/te/te_renderer_opengl.cpp
new file mode 100644
index 00000000000..abbf81550ae
--- /dev/null
+++ b/engines/tetraedge/te/te_renderer_opengl.cpp
@@ -0,0 +1,412 @@
+/* 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 "common/textconsole.h"
+#include "common/debug.h"
+
+#include "graphics/opengl/system_headers.h"
+
+#include "engines/util.h"
+
+#include "tetraedge/te/te_renderer.h"
+#include "tetraedge/te/te_renderer_opengl.h"
+#include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_light_opengl.h"
+#include "tetraedge/te/te_mesh_opengl.h"
+
+namespace Tetraedge {
+
+TeRendererOpenGL::TeRendererOpenGL() {
+}
+
+void TeRendererOpenGL::clearBuffer(TeRenderer::Buffer buf) {
+	GLenum glBuf = 0;
+	if (buf & StencilBuffer)
+		glBuf |= GL_STENCIL_BUFFER_BIT;
+	if (buf & DepthBuffer)
+		glBuf |= GL_DEPTH_BUFFER_BIT;
+	if (buf & ColorBuffer)
+		glBuf |= GL_COLOR_BUFFER_BIT;
+	glClear(glBuf);
+}
+
+void TeRendererOpenGL::disableAllLights() {
+	TeLightOpenGL::disableAll();
+}
+
+void TeRendererOpenGL::disableTexture() {
+	glDisable(GL_TEXTURE_2D);
+	_textureEnabled = false;
+}
+
+void TeRendererOpenGL::disableWireFrame() {
+	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+}
+
+void TeRendererOpenGL::disableZBuffer() {
+	glDisable(GL_DEPTH_TEST);
+	glDepthMask(GL_FALSE);
+}
+
+void TeRendererOpenGL::drawLine(const TeVector3f32 &from, const TeVector3f32 &to) {
+	error("TODO: Implement TeRenderer::drawLine");
+}
+
+void TeRendererOpenGL::enableAllLights() {
+	TeLightOpenGL::enableAll();
+}
+
+void TeRendererOpenGL::enableTexture() {
+	glEnable(GL_TEXTURE_2D);
+	_textureEnabled = true;
+}
+
+void TeRendererOpenGL::enableWireFrame() {
+	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+}
+
+void TeRendererOpenGL::enableZBuffer() {
+	glEnable(GL_DEPTH_TEST);
+	glDepthMask(GL_TRUE);
+}
+
+void TeRendererOpenGL::init(uint width, uint height) {
+	initGraphics3d(width, height);
+	glDisable(GL_CULL_FACE);
+	TeLightOpenGL::disableAll();
+	glDisable(GL_COLOR_MATERIAL);
+	glEnable(GL_DEPTH_TEST);
+	glDepthMask(GL_TRUE);
+	glShadeModel(GL_SMOOTH);
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	glDepthFunc(GL_LEQUAL);
+	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);
+	glClearDepth(1.0);
+	glClearStencil(0);
+	_clearColor = TeColor(0, 0, 0, 255);
+	glClearColor(0, 0, 0, 1.0);
+	debug("[TeRenderer::init] Vendor : %s", vendor().c_str());
+	debug("[TeRenderer::init] Renderer : %s", renderer().c_str());
+	debug("[TeRenderer::init] Version : %s", glGetString(GL_VERSION));
+	int bits;
+	glGetIntegerv(GL_STENCIL_BITS, &bits);
+	debug("[TeRenderer::init] Sentil buffer bits : %d", bits);
+	glGetIntegerv(GL_DEPTH_BITS, &bits);
+	debug("[TeRenderer::init] Depth buffer bits : %d", bits);
+	//debug("[TeRenderer::init] Extensions : %s", glGetString(GL_EXTENSIONS));
+	//TeOpenGLExtensions::loadExtensions(); // this does nothing in the game?
+	_currentColor = TeColor(255, 255, 255, 255);
+	_scissorEnabled = false;
+	_scissorX = _scissorY = _scissorWidth = _scissorHeight = 0;
+}
+
+void TeRendererOpenGL::loadMatrixToGL(const TeMatrix4x4 &matrix) {
+	//int mmode;
+	//glGetIntegerv(GL_MATRIX_MODE, &mmode);
+	//debug("loadMatrixToGL[0x%x]: %s", mmode, matrix.toString().c_str());
+	glLoadMatrixf(matrix.getData());
+}
+
+void TeRendererOpenGL::loadProjectionMatrix(const TeMatrix4x4 &matrix) {
+	glMatrixMode(GL_PROJECTION);
+	_matrixMode = MM_GL_PROJECTION;
+	_matriciesStacks[_matrixMode].loadIdentity();
+	_matriciesStacks[_matrixMode].loadMatrix(matrix);
+	glMatrixMode(GL_MODELVIEW);
+	_matrixMode = MM_GL_MODELVIEW;
+	_matriciesStacks[_matrixMode].loadIdentity();
+}
+
+Common::String TeRendererOpenGL::renderer() {
+	return Common::String((const char *)glGetString(GL_RENDERER));
+}
+
+
+static bool compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
+											const TeRenderer::TransparentMeshProperties &p2) {
+	return (p1._zOrder < p2._zOrder);
+}
+
+void TeRendererOpenGL::renderTransparentMeshes() {
+	if (!_numTransparentMeshes)
+		return;
+
+	glDepthMask(GL_FALSE);
+	//dumpTransparentMeshProps();
+
+	Common::sort(_transparentMeshProps.begin(), _transparentMeshProps.end(),
+		 compareTransparentMeshProperties);
+
+	int vertsDrawn = 0;
+	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
+		const uint vcount = _transparentMeshProps[i]._vertexCount;
+		for (uint j = 0; j < vcount; j++)
+			_transparentMeshVertexNums[vertsDrawn + j] = (short)(_transparentMeshProps[i]._sourceTransparentMesh + j);
+		vertsDrawn += vcount;
+	}
+
+	optimiseTransparentMeshProperties();
+
+	//dumpTransparentMeshProps();
+	//dumpTransparentMeshData();
+
+	glEnableClientState(GL_VERTEX_ARRAY);
+	glEnableClientState(GL_NORMAL_ARRAY);
+	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+	glEnableClientState(GL_COLOR_ARRAY);
+
+	glVertexPointer(3, GL_FLOAT, 12, _transparentMeshVertexes.data());
+	glNormalPointer(GL_FLOAT, 12, _transparentMeshNormals.data());
+	glTexCoordPointer(2, GL_FLOAT, 8, _transparentMeshCoords.data());
+	glColorPointer(4, GL_UNSIGNED_BYTE, 4, _transparentMeshColors.data());
+
+	TeMaterial lastMaterial;
+	TeMatrix4x4 lastMatrix;
+
+	vertsDrawn = 0;
+	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
+		const TransparentMeshProperties &meshProperties = _transparentMeshProps[i];
+		if (!meshProperties._shouldDraw)
+			continue;
+
+		const TeMaterial &material = meshProperties._material;
+
+		meshProperties._camera->applyProjection();
+		glMatrixMode(GL_MODELVIEW);
+		_matrixMode = MM_GL_MODELVIEW;
+		glPushMatrix();
+		_matriciesStacks[_matrixMode].pushMatrix();
+		_matriciesStacks[_matrixMode].loadMatrix(meshProperties._matrix);
+		glPushMatrix();
+		loadCurrentMatrixToGL();
+		if (material._texture) {
+			glEnable(GL_TEXTURE_2D);
+			_textureEnabled = true;
+		}
+		if (material._enableSomethingDefault0) {
+			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+			glDisableClientState(GL_COLOR_ARRAY);
+		}
+
+		if (material != lastMaterial) {
+			applyMaterial(material);
+			lastMaterial = material;
+		}
+
+		if (meshProperties._scissorEnabled) {
+			glEnable(GL_SCISSOR_TEST);
+			glScissor(meshProperties._scissorX,
+					  meshProperties._scissorY,
+					  meshProperties._scissorWidth,
+					  meshProperties._scissorHeight);
+		}
+		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, meshProperties._glTexEnvMode);
+		glDrawElements(GL_TRIANGLES, meshProperties._vertexCount, GL_UNSIGNED_SHORT,
+				   _transparentMeshVertexNums.data() + vertsDrawn);
+
+		vertsDrawn += meshProperties._vertexCount;
+
+		if (material._enableSomethingDefault0) {
+			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+			glEnableClientState(GL_COLOR_ARRAY);
+		}
+		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+		if (meshProperties._scissorEnabled) {
+			glDisable(GL_SCISSOR_TEST);
+		}
+		if (material._texture) {
+			glDisable(GL_TEXTURE_2D);
+			_textureEnabled = false;
+		}
+		glPopMatrix();
+		glPopMatrix();
+		_matriciesStacks[_matrixMode].popMatrix();
+		TeCamera::restore();
+	}
+	glDisableClientState(GL_VERTEX_ARRAY);
+	glDisableClientState(GL_NORMAL_ARRAY);
+	glDisableClientState(GL_COLOR_ARRAY);
+	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+	_numTransparentMeshes = 0;
+	_pendingTransparentMeshProperties = 0;
+	glDepthMask(GL_TRUE);
+	_transparentMeshProps.clear();
+}
+
+void TeRendererOpenGL::reset() {
+	clearBuffer(AllBuffers);
+	glMatrixMode(GL_PROJECTION);
+	_matrixMode = MM_GL_PROJECTION;
+	_matriciesStacks[MM_GL_PROJECTION].loadIdentity();
+	glMatrixMode(GL_MODELVIEW);
+	_matrixMode = MM_GL_MODELVIEW;
+	_matriciesStacks[MM_GL_MODELVIEW].loadIdentity();
+}
+
+void TeRendererOpenGL::setClearColor(const TeColor &col) {
+	_clearColor = col;
+	glClearColor(col.r() / 255.0f, col.g() / 255.0f, col.b() / 255.0f, col.a() / 255.0f);
+}
+
+void TeRendererOpenGL::setCurrentColor(const TeColor &col) {
+	if (col == _currentColor)
+		return;
+
+	glColor4ub(col.r(), col.g(), col.b(), col.a());
+	_currentColor = col;
+}
+
+void TeRendererOpenGL::setMatrixMode(enum MatrixMode mode) {
+	GLenum glmode = 0;
+	if (mode == MM_GL_TEXTURE)
+		glmode = GL_TEXTURE;
+	else if (mode == MM_GL_MODELVIEW)
+		glmode = GL_MODELVIEW;
+	else if (mode == MM_GL_PROJECTION)
+		glmode = GL_PROJECTION;
+
+	if (glmode)
+		glMatrixMode(glmode);
+	_matrixMode = mode;
+}
+
+void TeRendererOpenGL::setViewport(int x, int y, int w, int h) {
+	glViewport(x, y, w, h);
+}
+
+void TeRendererOpenGL::shadowMode(enum ShadowMode mode) {
+	_shadowMode = mode;
+	if (mode == ShadowMode0) {
+		glDisable(GL_CULL_FACE);
+		glShadeModel(GL_SMOOTH);
+		return;
+	}
+
+	if (mode == ShadowMode1) {
+		glEnable(GL_CULL_FACE);
+		glCullFace(GL_BACK);
+	} else { // ShadowMode2
+		glDisable(GL_CULL_FACE);
+	}
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	glShadeModel(GL_FLAT);
+	TeLightOpenGL::disableAll();
+}
+
+void TeRendererOpenGL::applyMaterial(const TeMaterial &m) {
+	//debug("TeMaterial::apply (%s)", dump().c_str());
+	static const float constColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+	if (_shadowMode == TeRenderer::ShadowMode0) {
+		if (m._enableLights)
+			TeLightOpenGL::enableAll();
+		else
+			TeLightOpenGL::disableAll();
+
+		if (m._texture) {
+			enableTexture();
+			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+			m._texture->bind();
+		}
+
+		glDisable(GL_ALPHA_TEST);
+		if (m._mode == TeMaterial::MaterialMode0) {
+			glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constColor);
+			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
+			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
+			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+		} else {
+			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+			if (m._mode != TeMaterial::MaterialMode1) {
+				glEnable(GL_ALPHA_TEST);
+				glAlphaFunc(GL_GREATER, 0.5);
+			}
+		}
+		const float ambient[4] = { m._ambientColor.r() / 255.0f, m._ambientColor.g() / 255.0f,
+			m._ambientColor.b() / 255.0f, m._ambientColor.a() / 255.0f };
+		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
+
+		const float specular[4] = { m._specularColor.r() / 255.0f, m._specularColor.g() / 255.0f,
+			m._specularColor.b() / 255.0f, m._specularColor.a() / 255.0f };
+		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
+
+		const float emission[4] = { m._emissionColor.r() / 255.0f, m._emissionColor.g() / 255.0f,
+			m._emissionColor.b() / 255.0f, m._emissionColor.a() / 255.0f };
+		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);
+
+		glMaterialf(GL_FRONT, GL_SHININESS, m._shininess);
+
+		const float diffuse[4] = { m._diffuseColor.r() / 255.0f, m._diffuseColor.g() / 255.0f,
+			m._diffuseColor.b() / 255.0f, m._diffuseColor.a() / 255.0f };
+		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
+
+		setCurrentColor(m._diffuseColor);
+	} else if (_shadowMode == TeRenderer::ShadowMode1) {
+		// NOTE: Diverge from original here, it sets 255.0 but the
+		// colors should be scaled -1.0 .. 1.0.
+		static const float fullColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+		TeLightOpenGL::disableAll();
+		glDisable(GL_ALPHA_TEST);
+		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, fullColor);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fullColor);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fullColor);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, fullColor);
+	}
+
+	//warning("TODO: Work out what TeMaterial::_enableSomethingDefault0 actually is.");
+	if (!m._enableSomethingDefault0) {
+		glDisable(GL_TEXTURE_GEN_S);
+		glDisable(GL_TEXTURE_GEN_T);
+		glDisable(GL_TEXTURE_GEN_R);
+		glDisable(GL_TEXTURE_GEN_Q);
+	} else {
+		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+		glEnable(GL_TEXTURE_GEN_S);
+		glEnable(GL_TEXTURE_GEN_T);
+		glEnable(GL_TEXTURE_GEN_R);
+		glEnable(GL_TEXTURE_GEN_Q);
+		glEnable(GL_TEXTURE_2D);
+		TeLightOpenGL::disableAll();
+		glDisable(GL_ALPHA_TEST);
+		enableTexture();
+		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+		const float diffuse[4] = { m._diffuseColor.r() / 255.0f, m._diffuseColor.g() / 255.0f,
+			m._diffuseColor.b() / 255.0f, m._diffuseColor.a() / 255.0f };
+
+		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, diffuse);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, diffuse);
+		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, diffuse);
+	}
+}
+
+Common::String TeRendererOpenGL::vendor() {
+	return Common::String((const char *)glGetString(GL_VENDOR));
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_renderer_opengl.h b/engines/tetraedge/te/te_renderer_opengl.h
new file mode 100644
index 00000000000..c6d7f76ab5e
--- /dev/null
+++ b/engines/tetraedge/te/te_renderer_opengl.h
@@ -0,0 +1,66 @@
+/* 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_TE_TE_RENDERER_OPENGL_H
+#define TETRAEDGE_TE_TE_RENDERER_OPENGL_H
+
+#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+
+#include "tetraedge/te/te_renderer.h"
+
+namespace Tetraedge {
+
+class TeRendererOpenGL : public TeRenderer {
+public:
+	TeRendererOpenGL();
+	void clearBuffer(TeRenderer::Buffer buf) override;
+	void disableAllLights() override;
+	void disableTexture() override;
+	void disableWireFrame() override;
+	void disableZBuffer() override;
+	void drawLine(const TeVector3f32 &from, const TeVector3f32 &to) override;
+	void enableAllLights() override;
+	void enableTexture() override;
+	void enableWireFrame() override;
+	void enableZBuffer() override;
+	void init(uint width, uint height) override;
+	void loadProjectionMatrix(const TeMatrix4x4 &matrix) override;
+	Common::String renderer() override;
+	void renderTransparentMeshes() override;
+	void reset() override;
+	void setClearColor(const TeColor &col) override;
+	void setCurrentColor(const TeColor &col) override;
+	void setMatrixMode(enum MatrixMode mode) override;
+	void setViewport(int x, int y, int w, int h) override;
+	void shadowMode(enum ShadowMode mode) override;
+	Common::String vendor() override;
+	void applyMaterial(const TeMaterial &m) override;
+
+protected:
+
+	void loadMatrixToGL(const TeMatrix4x4 &matrix) override;
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_OPENGL
+
+#endif // TETRAEDGE_TE_TE_RENDERER_OPENGL_H
diff --git a/engines/tetraedge/te/te_renderer_tinygl.cpp b/engines/tetraedge/te/te_renderer_tinygl.cpp
new file mode 100644
index 00000000000..a8eba20af17
--- /dev/null
+++ b/engines/tetraedge/te/te_renderer_tinygl.cpp
@@ -0,0 +1,431 @@
+/* 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 "common/textconsole.h"
+#include "common/debug.h"
+#include "common/system.h"
+#include "common/config-manager.h"
+
+#include "graphics/tinygl/tinygl.h"
+
+#include "engines/util.h"
+
+#include "tetraedge/te/te_renderer_tinygl.h"
+#include "tetraedge/te/te_light.h"
+#include "tetraedge/te/te_light_tinygl.h"
+#include "tetraedge/te/te_mesh_tinygl.h"
+
+namespace Tetraedge {
+
+TeRendererTinyGL::TeRendererTinyGL() {
+}
+
+void TeRendererTinyGL::clearBuffer(TeRenderer::Buffer buf) {
+	TGLenum glBuf = 0;
+	if (buf & StencilBuffer)
+		glBuf |= TGL_STENCIL_BUFFER_BIT;
+	if (buf & DepthBuffer)
+		glBuf |= TGL_DEPTH_BUFFER_BIT;
+	if (buf & ColorBuffer)
+		glBuf |= TGL_COLOR_BUFFER_BIT;
+	tglClear(glBuf);
+}
+
+void TeRendererTinyGL::disableAllLights() {
+	TeLightTinyGL::disableAll();
+}
+
+void TeRendererTinyGL::disableTexture() {
+	tglDisable(TGL_TEXTURE_2D);
+	_textureEnabled = false;
+}
+
+void TeRendererTinyGL::disableWireFrame() {
+	tglPolygonMode(TGL_FRONT_AND_BACK, TGL_FILL);
+}
+
+void TeRendererTinyGL::disableZBuffer() {
+	tglDisable(TGL_DEPTH_TEST);
+	tglDepthMask(TGL_FALSE);
+}
+
+void TeRendererTinyGL::drawLine(const TeVector3f32 &from, const TeVector3f32 &to) {
+	error("TODO: Implement TeRenderer::drawLine");
+}
+
+void TeRendererTinyGL::enableAllLights() {
+	TeLightTinyGL::enableAll();
+}
+
+void TeRendererTinyGL::enableTexture() {
+	tglEnable(TGL_TEXTURE_2D);
+	_textureEnabled = true;
+}
+
+void TeRendererTinyGL::enableWireFrame() {
+	tglPolygonMode(TGL_FRONT_AND_BACK, TGL_LINE);
+}
+
+void TeRendererTinyGL::enableZBuffer() {
+	tglEnable(TGL_DEPTH_TEST);
+	tglDepthMask(TGL_TRUE);
+}
+
+void TeRendererTinyGL::init(uint width, uint height) {
+	Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
+	initGraphics(width, height, &pixelFormat);
+
+	debug(2, "INFO: TinyGL front buffer pixel format: %s", pixelFormat.toString().c_str());
+	TinyGL::createContext(width, height, pixelFormat, 256, true, ConfMan.getBool("dirtyrects"));
+
+	tglViewport(0, 0, width, height);
+
+	tglDisable(TGL_CULL_FACE);
+	TeLightTinyGL::disableAll();
+	tglDisable(TGL_COLOR_MATERIAL);
+	tglEnable(TGL_DEPTH_TEST);
+	tglDepthMask(TGL_TRUE);
+	tglShadeModel(TGL_SMOOTH);
+	tglEnable(TGL_BLEND);
+	tglBlendFunc(TGL_SRC_ALPHA, TGL_ONE_MINUS_SRC_ALPHA);
+	tglDepthFunc(TGL_LEQUAL);
+	tglHint(TGL_PERSPECTIVE_CORRECTION_HINT, TGL_DONT_CARE);
+	tglClearDepth(1.0);
+	tglClearStencil(0);
+	_clearColor = TeColor(0, 0, 0, 255);
+	tglClearColor(0, 0, 0, 1.0);
+	debug("[TeRenderer::init] Vendor : %s", vendor().c_str());
+	debug("[TeRenderer::init] Renderer : %s", renderer().c_str());
+	debug("[TeRenderer::init] Version : (tinygl version)");
+	debug("[TeRenderer::init] Sentil buffer bits : (not supported)");
+	debug("[TeRenderer::init] Depth buffer bits : (not supported)");
+	//debug("[TeRenderer::init] Extensions : %s", glGetString(TGL_EXTENSIONS));
+	//TeOpenGLExtensions::loadExtensions(); // this does nothing in the game?
+	_currentColor = TeColor(255, 255, 255, 255);
+	_scissorEnabled = false;
+	_scissorX = _scissorY = _scissorWidth = _scissorHeight = 0;
+}
+
+
+void TeRendererTinyGL::loadMatrixToGL(const TeMatrix4x4 &matrix) {
+	//int mmode;
+	//glGetIntegerv(TGL_MATRIX_MODE, &mmode);
+	//debug("loadMatrixToGL[0x%x]: %s", mmode, matrix.toString().c_str());
+	tglLoadMatrixf(matrix.getData());
+}
+
+void TeRendererTinyGL::loadProjectionMatrix(const TeMatrix4x4 &matrix) {
+	tglMatrixMode(TGL_PROJECTION);
+	_matrixMode = MM_GL_PROJECTION;
+	_matriciesStacks[_matrixMode].loadIdentity();
+	_matriciesStacks[_matrixMode].loadMatrix(matrix);
+	tglMatrixMode(TGL_MODELVIEW);
+	_matrixMode = MM_GL_MODELVIEW;
+	_matriciesStacks[_matrixMode].loadIdentity();
+}
+
+Common::String TeRendererTinyGL::renderer() {
+	return "TinyGL";
+}
+
+
+static bool compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
+											const TeRenderer::TransparentMeshProperties &p2) {
+	return (p1._zOrder < p2._zOrder);
+}
+
+
+void TeRendererTinyGL::renderTransparentMeshes() {
+	if (!_numTransparentMeshes)
+		return;
+
+	tglDepthMask(TGL_FALSE);
+	//dumpTransparentMeshProps();
+
+	Common::sort(_transparentMeshProps.begin(), _transparentMeshProps.end(),
+		 compareTransparentMeshProperties);
+
+	int vertsDrawn = 0;
+	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
+		const uint vcount = _transparentMeshProps[i]._vertexCount;
+		for (uint j = 0; j < vcount; j++)
+			_transparentMeshVertexNums[vertsDrawn + j] = (short)(_transparentMeshProps[i]._sourceTransparentMesh + j);
+		vertsDrawn += vcount;
+	}
+
+	optimiseTransparentMeshProperties();
+
+	//dumpTransparentMeshProps();
+	//dumpTransparentMeshData();
+
+	tglEnableClientState(TGL_VERTEX_ARRAY);
+	tglEnableClientState(TGL_NORMAL_ARRAY);
+	tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
+	tglEnableClientState(TGL_COLOR_ARRAY);
+
+	tglVertexPointer(3, TGL_FLOAT, 12, _transparentMeshVertexes.data());
+	tglNormalPointer(TGL_FLOAT, 12, _transparentMeshNormals.data());
+	tglTexCoordPointer(2, TGL_FLOAT, 8, _transparentMeshCoords.data());
+	tglColorPointer(4, TGL_UNSIGNED_BYTE, 4, _transparentMeshColors.data());
+
+	TeMaterial lastMaterial;
+	TeMatrix4x4 lastMatrix;
+
+	vertsDrawn = 0;
+	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
+		const TransparentMeshProperties &meshProperties = _transparentMeshProps[i];
+		if (!meshProperties._shouldDraw)
+			continue;
+
+		const TeMaterial &material = meshProperties._material;
+
+		meshProperties._camera->applyProjection();
+		tglMatrixMode(TGL_MODELVIEW);
+		_matrixMode = MM_GL_MODELVIEW;
+		tglPushMatrix();
+		_matriciesStacks[_matrixMode].pushMatrix();
+		_matriciesStacks[_matrixMode].loadMatrix(meshProperties._matrix);
+		tglPushMatrix();
+		loadCurrentMatrixToGL();
+		if (material._texture) {
+			tglEnable(TGL_TEXTURE_2D);
+			_textureEnabled = true;
+		}
+		if (material._enableSomethingDefault0) {
+			tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
+			tglDisableClientState(TGL_COLOR_ARRAY);
+		}
+
+		if (material != lastMaterial) {
+			applyMaterial(material);
+			lastMaterial = material;
+		}
+
+		if (meshProperties._scissorEnabled) {
+			tglEnable(TGL_SCISSOR_TEST);
+			// No scissoring in TGL..
+			/*
+			tglScissor(meshProperties._scissorX,
+					  meshProperties._scissorY,
+					  meshProperties._scissorWidth,
+					  meshProperties._scissorHeight);*/
+		}
+		// TODO: not supported in TGL
+		//tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, meshProperties._glTexEnvMode);
+		tglDrawElements(TGL_TRIANGLES, meshProperties._vertexCount, TGL_UNSIGNED_SHORT,
+				   _transparentMeshVertexNums.data() + vertsDrawn);
+
+		vertsDrawn += meshProperties._vertexCount;
+
+		if (material._enableSomethingDefault0) {
+			tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
+			tglEnableClientState(TGL_COLOR_ARRAY);
+		}
+		// TODO: not supported in TGL
+		//tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
+		if (meshProperties._scissorEnabled) {
+			tglDisable(TGL_SCISSOR_TEST);
+		}
+		if (material._texture) {
+			tglDisable(TGL_TEXTURE_2D);
+			_textureEnabled = false;
+		}
+		tglPopMatrix();
+		tglPopMatrix();
+		_matriciesStacks[_matrixMode].popMatrix();
+		TeCamera::restore();
+	}
+	tglDisableClientState(TGL_VERTEX_ARRAY);
+	tglDisableClientState(TGL_NORMAL_ARRAY);
+	tglDisableClientState(TGL_COLOR_ARRAY);
+	tglDisableClientState(TGL_TEXTURE_COORD_ARRAY);
+	_numTransparentMeshes = 0;
+	_pendingTransparentMeshProperties = 0;
+	tglDepthMask(TGL_TRUE);
+	_transparentMeshProps.clear();
+}
+
+void TeRendererTinyGL::reset() {
+	clearBuffer(AllBuffers);
+	tglMatrixMode(TGL_PROJECTION);
+	_matrixMode = MM_GL_PROJECTION;
+	_matriciesStacks[MM_GL_PROJECTION].loadIdentity();
+	tglMatrixMode(TGL_MODELVIEW);
+	_matrixMode = MM_GL_MODELVIEW;
+	_matriciesStacks[MM_GL_MODELVIEW].loadIdentity();
+}
+
+void TeRendererTinyGL::setClearColor(const TeColor &col) {
+	_clearColor = col;
+	tglClearColor(col.r() / 255.0f, col.g() / 255.0f, col.b() / 255.0f, col.a() / 255.0f);
+}
+
+void TeRendererTinyGL::setCurrentColor(const TeColor &col) {
+	if (col == _currentColor)
+		return;
+
+	tglColor4ub(col.r(), col.g(), col.b(), col.a());
+	_currentColor = col;
+}
+
+void TeRendererTinyGL::setMatrixMode(enum MatrixMode mode) {
+	TGLenum glmode = 0;
+	if (mode == MM_GL_TEXTURE)
+		glmode = TGL_TEXTURE;
+	else if (mode == MM_GL_MODELVIEW)
+		glmode = TGL_MODELVIEW;
+	else if (mode == MM_GL_PROJECTION)
+		glmode = TGL_PROJECTION;
+
+	if (glmode)
+		tglMatrixMode(glmode);
+	_matrixMode = mode;
+}
+
+void TeRendererTinyGL::setViewport(int x, int y, int w, int h) {
+	tglViewport(x, y, w, h);
+}
+
+void TeRendererTinyGL::shadowMode(enum ShadowMode mode) {
+	_shadowMode = mode;
+	if (mode == ShadowMode0) {
+		tglDisable(TGL_CULL_FACE);
+		tglShadeModel(TGL_SMOOTH);
+		return;
+	}
+
+	if (mode == ShadowMode1) {
+		tglEnable(TGL_CULL_FACE);
+		tglCullFace(TGL_BACK);
+	} else { // ShadowMode2
+		tglDisable(TGL_CULL_FACE);
+	}
+	tglEnable(TGL_BLEND);
+	tglBlendFunc(TGL_SRC_ALPHA, TGL_ONE_MINUS_SRC_ALPHA);
+	tglShadeModel(TGL_FLAT);
+	TeLightTinyGL::disableAll();
+}
+
+void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
+	//debug("TeMaterial::apply (%s)", dump().c_str());
+	static const float constColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+	if (_shadowMode == TeRenderer::ShadowMode0) {
+		if (m._enableLights)
+			TeLightTinyGL::enableAll();
+		else
+			TeLightTinyGL::disableAll();
+
+		if (m._texture) {
+			enableTexture();
+			tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
+			m._texture->bind();
+		}
+
+		tglDisable(TGL_ALPHA_TEST);
+		if (m._mode == TeMaterial::MaterialMode0) {
+			/* TODO: Find TGL equivalents for this stuff*/
+			/*
+			tglTexEnvfv(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_COLOR, constColor);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_COMBINE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_RGB, TGL_MODULATE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_RGB, TGL_TEXTURE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_RGB, TGL_SRC_COLOR);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_ALPHA, TGL_REPLACE);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_ALPHA, TGL_CONSTANT);
+			tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_ALPHA, TGL_SRC_ALPHA);
+			*/
+		} else {
+			// TODO: not supported in TGL
+			//tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
+			if (m._mode != TeMaterial::MaterialMode1) {
+				tglEnable(TGL_ALPHA_TEST);
+				tglAlphaFunc(TGL_GREATER, 0.5);
+			}
+		}
+		const float ambient[4] = { m._ambientColor.r() / 255.0f, m._ambientColor.g() / 255.0f,
+			m._ambientColor.b() / 255.0f, m._ambientColor.a() / 255.0f };
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_AMBIENT, ambient);
+
+		const float specular[4] = { m._specularColor.r() / 255.0f, m._specularColor.g() / 255.0f,
+			m._specularColor.b() / 255.0f, m._specularColor.a() / 255.0f };
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_SPECULAR, specular);
+
+		const float emission[4] = { m._emissionColor.r() / 255.0f, m._emissionColor.g() / 255.0f,
+			m._emissionColor.b() / 255.0f, m._emissionColor.a() / 255.0f };
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_EMISSION, emission);
+
+		tglMaterialf(TGL_FRONT, TGL_SHININESS, m._shininess);
+
+		const float diffuse[4] = { m._diffuseColor.r() / 255.0f, m._diffuseColor.g() / 255.0f,
+			m._diffuseColor.b() / 255.0f, m._diffuseColor.a() / 255.0f };
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_DIFFUSE, diffuse);
+
+		setCurrentColor(m._diffuseColor);
+	} else if (_shadowMode == TeRenderer::ShadowMode1) {
+		// NOTE: Diverge from original here, it sets 255.0 but the
+		// colors should be scaled -1.0 .. 1.0.
+		static const float fullColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+		TeLightTinyGL::disableAll();
+		tglDisable(TGL_ALPHA_TEST);
+		// TODO: not supported in TGL
+		//tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_AMBIENT, fullColor);
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_DIFFUSE, fullColor);
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_SPECULAR, fullColor);
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_EMISSION, fullColor);
+	}
+
+	//warning("TODO: Work out what TeMaterial::_enableSomethingDefault0 actually is.");
+	if (!m._enableSomethingDefault0) {
+		tglDisable(TGL_TEXTURE_GEN_S);
+		tglDisable(TGL_TEXTURE_GEN_T);
+		tglDisable(TGL_TEXTURE_GEN_R);
+		tglDisable(TGL_TEXTURE_GEN_Q);
+	} else {
+		// TODO: not supported in TGL
+		//tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
+		tglEnable(TGL_TEXTURE_GEN_S);
+		tglEnable(TGL_TEXTURE_GEN_T);
+		tglEnable(TGL_TEXTURE_GEN_R);
+		tglEnable(TGL_TEXTURE_GEN_Q);
+		tglEnable(TGL_TEXTURE_2D);
+		TeLightTinyGL::disableAll();
+		tglDisable(TGL_ALPHA_TEST);
+		enableTexture();
+		// TODO: not supported in TGL
+		//tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
+
+		const float diffuse[4] = { m._diffuseColor.r() / 255.0f, m._diffuseColor.g() / 255.0f,
+			m._diffuseColor.b() / 255.0f, m._diffuseColor.a() / 255.0f };
+
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_AMBIENT, diffuse);
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_DIFFUSE, diffuse);
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_SPECULAR, diffuse);
+		tglMaterialfv(TGL_FRONT_AND_BACK, TGL_EMISSION, diffuse);
+	}
+}
+
+
+Common::String TeRendererTinyGL::vendor() {
+	return "TinyGL vendor";
+}
+
+} // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_renderer_tinygl.h b/engines/tetraedge/te/te_renderer_tinygl.h
new file mode 100644
index 00000000000..6599ff56a24
--- /dev/null
+++ b/engines/tetraedge/te/te_renderer_tinygl.h
@@ -0,0 +1,67 @@
+/* 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_TE_TE_RENDERER_TINYGL_H
+#define TETRAEDGE_TE_TE_RENDERER_TINYGL_H
+
+#if defined(USE_TINYGL)
+
+#include "tetraedge/te/te_renderer.h"
+
+namespace Tetraedge {
+
+class TeRendererTinyGL : public TeRenderer {
+public:
+	TeRendererTinyGL();
+
+	void clearBuffer(TeRenderer::Buffer buf) override;
+	void disableAllLights() override;
+	void disableTexture() override;
+	void disableWireFrame() override;
+	void disableZBuffer() override;
+	void drawLine(const TeVector3f32 &from, const TeVector3f32 &to) override;
+	void enableAllLights() override;
+	void enableTexture() override;
+	void enableWireFrame() override;
+	void enableZBuffer() override;
+	void init(uint width, uint height) override;
+	void loadProjectionMatrix(const TeMatrix4x4 &matrix) override;
+	Common::String renderer() override;
+	void renderTransparentMeshes() override;
+	void reset() override;
+	void setClearColor(const TeColor &col) override;
+	void setCurrentColor(const TeColor &col) override;
+	void setMatrixMode(enum MatrixMode mode) override;
+	void setViewport(int x, int y, int w, int h) override;
+	void shadowMode(enum ShadowMode mode) override;
+	void applyMaterial(const TeMaterial &m) override;
+	Common::String vendor() override;
+
+protected:
+
+	void loadMatrixToGL(const TeMatrix4x4 &matrix) override;
+};
+
+} // end namespace Tetraedge
+
+#endif // USE_TINYGL
+
+#endif // TETRAEDGE_TE_TE_RENDERER_H
diff --git a/engines/tetraedge/te/te_resource_manager.h b/engines/tetraedge/te/te_resource_manager.h
index c06834cf012..b1abcd699ff 100644
--- a/engines/tetraedge/te/te_resource_manager.h
+++ b/engines/tetraedge/te/te_resource_manager.h
@@ -80,6 +80,26 @@ public:
 		return retval;
 	}
 
+	template<class T> TeIntrusivePtr<T> getResourceOrMakeInstance(Common::Path &path) {
+		for (TeIntrusivePtr<TeResource> &resource : this->_resources) {
+			if (resource->getAccessName() == path) {
+				return TeIntrusivePtr<T>(dynamic_cast<T *>(resource.get()));
+			}
+		}
+
+		TeIntrusivePtr<T> retval;
+		// Note: original search logic here abstracted away in our version..
+		TeCore *core = g_engine->getCore();
+		path = core->findFile(path);
+		retval = T::makeInstance();
+
+		if (retval.get()) {
+			retval->load(path);
+			addResource(retval.get());
+		}
+		return retval;
+	}
+
 private:
 	Common::Array<TeIntrusivePtr<TeResource>> _resources;
 
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index 2f5f39e131a..5e1f56ff4c0 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -32,8 +32,13 @@ namespace Tetraedge {
 TeTextBase2::TeTextBase2() : _drawRect(0, 0), _size(0, 0),
 _alignStyle(TeFont3::AlignLeft), _interLine(0.0f), _globalColor(0xff, 0xff, 0xff, 0xff),
 _wrapMode(WrapModeFixed), _strikethrough(false), _fontSize(10), _valueWasSet(true) {
-	_mesh.setglTexEnvBlend();
-	_mesh.setShouldDraw(true);
+	_mesh = TeMesh::makeInstance();
+	_mesh->setglTexEnvBlend();
+	_mesh->setShouldDraw(true);
+}
+
+TeTextBase2::~TeTextBase2() {
+	delete _mesh;
 }
 
 #ifdef DUMP_RENDERED_FONTS
@@ -99,7 +104,7 @@ void TeTextBase2::build() {
 		drawLine(img, _wrappedLines[i], lineoffsets[i]);
 	}
 
-	TeIntrusivePtr<Te3DTexture> texture = new Te3DTexture();
+	TeIntrusivePtr<Te3DTexture> texture = Te3DTexture::makeInstance();
 	texture->load(img);
 
 #if DUMP_RENDERED_FONTS
@@ -109,32 +114,32 @@ void TeTextBase2::build() {
 	Image::writePNG(dumpFile, img);
 #endif
 
-	_mesh.setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
-	_mesh.defaultMaterial(texture);
-	_mesh.setglTexEnvBlend();
-	_mesh.setShouldDraw(true);
-	_mesh.setColor(_globalColor);
-	_mesh.setVertex(0, TeVector3f32(_size._x * -0.5f, _size._y * -0.5f, 0.0f));
-	_mesh.setTextureUV(0, TeVector2f32(0, 1));
-	_mesh.setNormal(0, TeVector3f32(0.0f, 0.0f, 1.0f));
-	_mesh.setColor(0, _globalColor);
-	_mesh.setVertex(1, TeVector3f32(_size._x * 0.5f, _size._y * -0.5f, 0.0f));
-	_mesh.setTextureUV(1, TeVector2f32(1, 1));
-	_mesh.setNormal(1, TeVector3f32(0.0f, 0.0f, 1.0f));
-	_mesh.setColor(1, _globalColor);
-	_mesh.setVertex(2, TeVector3f32(_size._x * 0.5f, _size._y * 0.5f, 0.0f));
-	_mesh.setTextureUV(2, TeVector2f32(1, 0));
-	_mesh.setNormal(2, TeVector3f32(0.0f, 0.0f, 1.0f));
-	_mesh.setColor(2, _globalColor);
-	_mesh.setVertex(3, TeVector3f32(_size._x * -0.5f, _size._y * 0.5f, 0.0f));
-	_mesh.setTextureUV(3, TeVector2f32(0, 0));
-	_mesh.setNormal(3, TeVector3f32(0.0f, 0.0f, 1.0f));
-	_mesh.setColor(3, _globalColor);
-	_mesh.setIndex(0, 0);
-	_mesh.setIndex(1, 1);
-	_mesh.setIndex(2, 3);
-	_mesh.setIndex(3, 2);
-	_mesh.setHasAlpha(true);
+	_mesh->setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
+	_mesh->defaultMaterial(texture);
+	_mesh->setglTexEnvBlend();
+	_mesh->setShouldDraw(true);
+	_mesh->setColor(_globalColor);
+	_mesh->setVertex(0, TeVector3f32(_size._x * -0.5f, _size._y * -0.5f, 0.0f));
+	_mesh->setTextureUV(0, TeVector2f32(0, 1));
+	_mesh->setNormal(0, TeVector3f32(0.0f, 0.0f, 1.0f));
+	_mesh->setColor(0, _globalColor);
+	_mesh->setVertex(1, TeVector3f32(_size._x * 0.5f, _size._y * -0.5f, 0.0f));
+	_mesh->setTextureUV(1, TeVector2f32(1, 1));
+	_mesh->setNormal(1, TeVector3f32(0.0f, 0.0f, 1.0f));
+	_mesh->setColor(1, _globalColor);
+	_mesh->setVertex(2, TeVector3f32(_size._x * 0.5f, _size._y * 0.5f, 0.0f));
+	_mesh->setTextureUV(2, TeVector2f32(1, 0));
+	_mesh->setNormal(2, TeVector3f32(0.0f, 0.0f, 1.0f));
+	_mesh->setColor(2, _globalColor);
+	_mesh->setVertex(3, TeVector3f32(_size._x * -0.5f, _size._y * 0.5f, 0.0f));
+	_mesh->setTextureUV(3, TeVector2f32(0, 0));
+	_mesh->setNormal(3, TeVector3f32(0.0f, 0.0f, 1.0f));
+	_mesh->setColor(3, _globalColor);
+	_mesh->setIndex(0, 0);
+	_mesh->setIndex(1, 1);
+	_mesh->setIndex(2, 3);
+	_mesh->setIndex(3, 2);
+	_mesh->setHasAlpha(true);
 }
 
 void TeTextBase2::clear() {
@@ -201,7 +206,7 @@ void TeTextBase2::draw() {
 	if (_valueWasSet)
 		build();
 
-	_mesh.draw();
+	_mesh->draw();
 
 	//if (_strikethrough)
 	//	warning("TODO: Implement TeTextBase2::draw strikethrough support");
diff --git a/engines/tetraedge/te/te_text_base2.h b/engines/tetraedge/te/te_text_base2.h
index b64f2fc757f..e862ff9e1f0 100644
--- a/engines/tetraedge/te/te_text_base2.h
+++ b/engines/tetraedge/te/te_text_base2.h
@@ -36,6 +36,7 @@ namespace Tetraedge {
 class TeTextBase2 {
 public:
 	TeTextBase2();
+	virtual ~TeTextBase2();
 
 	struct  Line {
 		uint _startOffset;
@@ -94,7 +95,7 @@ private:
 	Common::String _text;
 	bool _strikethrough;
 
-	TeMesh _mesh;
+	TeMesh *_mesh;
 
 	Common::Array<Common::String> _wrappedLines;
 
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index 9c9d19eb939..b9b9c7aba75 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -207,7 +207,9 @@ void TeTiledSurface::setColorKeyTolerence(float val) {
 void TeTiledSurface::setTiledTexture(const TeIntrusivePtr<TeTiledTexture> &texture) {
 	_tiledTexture = texture;
 	if (texture) {
-		_meshes.resize(texture->numberOfColumns() * texture->numberOfRow());
+		_meshes.clear();
+		for (uint i = 0; i < texture->numberOfColumns() * texture->numberOfRow(); i++)
+			_meshes.push_back(Common::SharedPtr<TeMesh>(TeMesh::makeInstance()));
 
 		setAccessName(texture->getAccessName().append(".surface"));
 		updateSurface();
@@ -242,7 +244,7 @@ void TeTiledSurface::updateSurface() {
 	int meshno = 0;
 	for (long row = 0; row < rows; row++) {
 		for (long col = 0; col < cols; col++) {
-			TeMesh &mesh = _meshes[meshno];
+			TeMesh &mesh = *_meshes[meshno];
 			mesh.setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
 
 			mesh.setShouldDraw(_shouldDraw);
diff --git a/engines/tetraedge/te/te_tiled_texture.cpp b/engines/tetraedge/te/te_tiled_texture.cpp
index 654225092e4..a6b9b233706 100644
--- a/engines/tetraedge/te/te_tiled_texture.cpp
+++ b/engines/tetraedge/te/te_tiled_texture.cpp
@@ -95,7 +95,7 @@ bool TeTiledTexture::load(const TeImage &img) {
 
 			Tile *tiledata = tile(TeVector2s32(row, col));
 			if (!_skipBlank || (int)tileimage->countPixelsOfColor(TeColor(0, 0, 0, 0)) != (tileimage->h * tileimage->w)) {
-				tiledata->_texture = new Te3DTexture();
+				tiledata->_texture = Te3DTexture::makeInstance();
 				tiledata->_texture->load(*tileimage);
 				tiledata->_vec2 = TeVector3f32
 						((float)tiledata->_texture->width() / (float)_totalSize._x,
diff --git a/engines/tetraedge/te/te_visual_fade.cpp b/engines/tetraedge/te/te_visual_fade.cpp
index f63cc2167d7..1840c210bce 100644
--- a/engines/tetraedge/te/te_visual_fade.cpp
+++ b/engines/tetraedge/te/te_visual_fade.cpp
@@ -101,7 +101,7 @@ void TeVisualFade::init() {
 	if (_texturePtr) {
 		_texturePtr->destroy();
 	} else {
-		_texturePtr = new Te3DTexture();
+		_texturePtr = Te3DTexture::makeInstance();
 	}
 	_texturePtr->create();
 	// create an image the size of the window, no palette, format 6.
diff --git a/engines/tetraedge/tetraedge.cpp b/engines/tetraedge/tetraedge.cpp
index 2c9c0302d99..7565ba429aa 100644
--- a/engines/tetraedge/tetraedge.cpp
+++ b/engines/tetraedge/tetraedge.cpp
@@ -190,13 +190,11 @@ bool TetraedgeEngine::onKeyUp(const Common::KeyState &state) {
 }
 
 Common::Error TetraedgeEngine::run() {
-	initGraphics3d(getDefaultScreenWidth(), getDefaultScreenHeight());
-
 	configureSearchPaths();
 	// from BasicOpenGLView::prepareOpenGL..
 	_application = new Application();
-	_renderer = new TeRenderer();
-	_renderer->init();
+	_renderer = TeRenderer::makeInstance();
+	_renderer->init(getDefaultScreenWidth(), getDefaultScreenHeight());
 	_renderer->reset();
 
 	getInputMgr()->_keyUpSignal.add(this, &TetraedgeEngine::onKeyUp);
@@ -244,10 +242,39 @@ void TetraedgeEngine::openConfigDialog() {
 	syncSoundSettings();
 }
 
+
+Graphics::RendererType TetraedgeEngine::preferredRendererType() const {
+	Common::String rendererConfig = ConfMan.get("renderer");
+	Graphics::RendererType desiredRendererType = Graphics::Renderer::parseTypeCode(rendererConfig);
+	uint32 availableRendererTypes = Graphics::Renderer::getAvailableTypes();
+
+	availableRendererTypes &=
+#if defined(USE_OPENGL_GAME)
+			Graphics::kRendererTypeOpenGL |
+#endif
+#if defined(USE_OPENGL_SHADERS)
+			Graphics::kRendererTypeOpenGLShaders |
+#endif
+#if defined(USE_TINYGL)
+			Graphics::kRendererTypeTinyGL |
+#endif
+			0;
+
+	Graphics::RendererType matchingRendererType = Graphics::Renderer::getBestMatchingType(desiredRendererType, availableRendererTypes);
+	// Currently no difference between shaders and otherwise for this engine.
+	if (matchingRendererType == Graphics::kRendererTypeOpenGLShaders)
+		matchingRendererType = Graphics::kRendererTypeOpenGL;
+	
+	if (matchingRendererType == 0) {
+		error("No supported renderer available.");
+	}
+	
+	return matchingRendererType;
+}
+
 /*static*/
 void TetraedgeEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
 	g_engine->getApplication()->getSavegameThumbnail(thumb);
 }
 
-
 } // namespace Tetraedge
diff --git a/engines/tetraedge/tetraedge.h b/engines/tetraedge/tetraedge.h
index 0ee0510fcac..e97739a411c 100644
--- a/engines/tetraedge/tetraedge.h
+++ b/engines/tetraedge/tetraedge.h
@@ -34,6 +34,7 @@
 #include "engines/engine.h"
 #include "engines/savestate.h"
 #include "graphics/screen.h"
+#include "graphics/renderer.h"
 
 #include "tetraedge/detection.h"
 
@@ -127,6 +128,10 @@ public:
 	bool onKeyUp(const Common::KeyState &state);
 
 	static Common::StringArray splitString(const Common::String &text, char c);
+	
+	/* Pick the renderer type to use.
+	   Currently will only return kRendererTypeOpenGL or kRendererTypeTinyGL */
+	Graphics::RendererType preferredRendererType() const;
 
 private:
 	void configureSearchPaths();


Commit: fd496bdd55ea1c91b295aa40cb4bf29bef442b9b
    https://github.com/scummvm/scummvm/commit/fd496bdd55ea1c91b295aa40cb4bf29bef442b9b
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Remove unused variable

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


diff --git a/engines/tetraedge/game/objectif.cpp b/engines/tetraedge/game/objectif.cpp
index adf0bfb668c..213c8e8ca3f 100644
--- a/engines/tetraedge/game/objectif.cpp
+++ b/engines/tetraedge/game/objectif.cpp
@@ -80,7 +80,6 @@ void Objectif::load() {
 }
 
 void Objectif::leave() {
-	Application *app = g_engine->getApplication();
 	TeLayout *layout;
 	layout = _gui1.layout("background");
 	if (layout)


Commit: 47739e458578d0a969902f706e7d8ad6f4d3f4b7
    https://github.com/scummvm/scummvm/commit/47739e458578d0a969902f706e7d8ad6f4d3f4b7
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Only compile openGL files when OGL is enabled

Changed paths:
    engines/tetraedge/module.mk


diff --git a/engines/tetraedge/module.mk b/engines/tetraedge/module.mk
index 635dd0450d6..72a89086889 100644
--- a/engines/tetraedge/module.mk
+++ b/engines/tetraedge/module.mk
@@ -10,7 +10,6 @@ MODULE_OBJS := \
 	game/character.o \
 	game/character_settings_xml_parser.o \
 	game/characters_shadow.o \
-	game/characters_shadow_opengl.o \
 	game/confirm.o \
 	game/credits.o \
 	game/dialog2.o \
@@ -43,7 +42,6 @@ MODULE_OBJS := \
 	te/micropather.o \
 	te/te_3d_object2.o \
 	te/te_3d_texture.o \
-	te/te_3d_texture_opengl.o \
 	te/te_act_zone.o \
 	te/te_animation.o \
 	te/te_bezier_curve.o \
@@ -68,7 +66,6 @@ MODULE_OBJS := \
 	te/te_jpeg.o \
 	te/te_layout.o \
 	te/te_light.o \
-	te/te_light_opengl.o \
 	te/te_list_layout.o \
 	te/te_lua_context.o \
 	te/te_lua_gui.o \
@@ -79,7 +76,6 @@ MODULE_OBJS := \
 	te/te_matricies_stack.o \
 	te/te_matrix4x4.o \
 	te/te_mesh.o \
-	te/te_mesh_opengl.o \
 	te/te_model.o \
 	te/te_model_animation.o \
 	te/te_model_vertex_animation.o \
@@ -94,7 +90,6 @@ MODULE_OBJS := \
 	te/te_ray_intersection.o \
 	te/te_real_timer.o \
 	te/te_renderer.o \
-	te/te_renderer_opengl.o \
 	te/te_resource.o \
 	te/te_resource_manager.o \
 	te/te_scene.o \
@@ -128,6 +123,14 @@ MODULE_OBJS += \
 	te/te_renderer_tinygl.o
 endif
 
+ifdef USE_OPENGL
+MODULE_OBJS += \
+	game/characters_shadow_opengl.o \
+	te/te_3d_texture_opengl.o \
+	te/te_light_opengl.o \
+	te/te_mesh_opengl.o \
+	te/te_renderer_opengl.o
+endif
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_TETRAEDGE), DYNAMIC_PLUGIN)


Commit: e6c805952377331b3d6d6e8764f9b8293da80c16
    https://github.com/scummvm/scummvm/commit/e6c805952377331b3d6d6e8764f9b8293da80c16
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Add TinyGL implementation for TeLight.

Changed paths:
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_light_tinygl.cpp


diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index c3d25bcfb71..d2946022c4f 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#include "graphics/opengl/system_headers.h"
-
 #include "tetraedge/tetraedge.h"
 #include "tetraedge/te/te_3d_texture.h"
 #include "tetraedge/te/te_3d_texture_opengl.h"
diff --git a/engines/tetraedge/te/te_light_tinygl.cpp b/engines/tetraedge/te/te_light_tinygl.cpp
index af94d4c7c50..fddb0cb4783 100644
--- a/engines/tetraedge/te/te_light_tinygl.cpp
+++ b/engines/tetraedge/te/te_light_tinygl.cpp
@@ -21,41 +21,41 @@
 
 #include "common/math.h"
 
+#include "graphics/tinygl/tinygl.h"
+
 #include "tetraedge/te/te_light_tinygl.h"
 #include "tetraedge/te/te_color.h"
 #include "tetraedge/te/te_quaternion.h"
 #include "tetraedge/te/te_vector3f32.h"
 
-#include "graphics/opengl/system_headers.h"
-
 namespace Tetraedge {
 
 static inline uint _toGlLight(uint lightno) {
-	return GL_LIGHT0 + lightno;
+	return TGL_LIGHT0 + lightno;
 }
 
 TeLightTinyGL::TeLightTinyGL() {
 }
 
 void TeLightTinyGL::disable(uint lightno) {
-	glDisable(_toGlLight(lightno));
+	tglDisable(_toGlLight(lightno));
 }
 
 void TeLightTinyGL::enable(uint lightno) {
 	if (_colDiffuse.r() == 0 && _colDiffuse.g() == 0 && _colDiffuse.b() == 0)
-		glDisable(_toGlLight(lightno));
+		tglDisable(_toGlLight(lightno));
 	else
-		glEnable(_toGlLight(lightno));
+		tglEnable(_toGlLight(lightno));
 }
 
 /*static*/
 void TeLightTinyGL::disableAll() {
-	glDisable(GL_LIGHTING);
+	tglDisable(TGL_LIGHTING);
 }
 
 /*static*/
 void TeLightTinyGL::enableAll() {
-	glEnable(GL_LIGHTING);
+	tglEnable(TGL_LIGHTING);
 }
 
 void TeLightTinyGL::draw(TeCamera &camera) {
@@ -63,35 +63,35 @@ void TeLightTinyGL::draw(TeCamera &camera) {
 }
 
 void TeLightTinyGL::update(uint lightno) {
-	if (lightno > GL_MAX_LIGHTS)
+	if (lightno > TGL_MAX_LIGHTS)
 		error("Invalid light no %d", lightno);
 	const uint glLight = _toGlLight(lightno);
 
 	const float ambient[4] = {_colAmbient.r() / 255.0f, _colAmbient.g() / 255.0f,
 			_colAmbient.b() / 255.0f, 1.0};
-	glLightfv(glLight, GL_AMBIENT, ambient);
+	tglLightfv(glLight, TGL_AMBIENT, ambient);
 
 	const float diff[4] = {_colDiffuse.r() / 255.0f, _colDiffuse.g() / 255.0f,
 			_colDiffuse.b() / 255.0f, 1.0};
-	glLightfv(glLight, GL_DIFFUSE, diff);
+	tglLightfv(glLight, TGL_DIFFUSE, diff);
 
 	// WORKAROUND: Original game sets 0.01 as threshold here to avoid enabling
 	// the "shadow" light.  However, Syberia CitStation/31130 has shadowlight with
 	// values (4, 0, 0) which means it gets enabled and everything is dark.
 
 	if (diff[0] < 0.02f && diff[1] < 0.02f && diff[2] < 0.02f)
-		glDisable(glLight);
+		tglDisable(glLight);
 
 	const float spec[4] = {_colSpecular.r() / 255.0f, _colSpecular.g() / 255.0f,
 			_colSpecular.b() / 255.0f, 1.0};
-	glLightfv(glLight, GL_SPECULAR, spec);
+	tglLightfv(glLight, TGL_SPECULAR, spec);
 
 	if (_type == LightTypeSpot || _type == LightTypePoint) {
 		const float pos[4] = {_position3d.x(), _position3d.y(), _position3d.z(), 1.0f};
-		glLightfv(glLight, GL_POSITION, pos);
-		glLightf(glLight, GL_CONSTANT_ATTENUATION, _constAtten);
-		glLightf(glLight, GL_LINEAR_ATTENUATION, _linearAtten);
-		glLightf(glLight, GL_QUADRATIC_ATTENUATION, _quadraticAtten);
+		tglLightfv(glLight, TGL_POSITION, pos);
+		tglLightf(glLight, TGL_CONSTANT_ATTENUATION, _constAtten);
+		tglLightf(glLight, TGL_LINEAR_ATTENUATION, _linearAtten);
+		tglLightf(glLight, TGL_QUADRATIC_ATTENUATION, _quadraticAtten);
 	}
 
 	if (_type == LightTypeDirectional) {
@@ -100,7 +100,7 @@ void TeLightTinyGL::update(uint lightno) {
 		float sinx = sinf(_positionRadial.getX());
 		float siny = sinf(_positionRadial.getY());
 		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
-		glLightfv(glLight, GL_POSITION, pos);
+		tglLightfv(glLight, TGL_POSITION, pos);
 	}
 
 	if (_type == LightTypeSpot) {
@@ -109,11 +109,11 @@ void TeLightTinyGL::update(uint lightno) {
 		float sinx = sinf(_positionRadial.getX());
 		float siny = sinf(_positionRadial.getY());
 		const float pos[4] = {cosx * cosy, siny, sinx * cosy, 0.0f};
-		glLightfv(glLight, GL_SPOT_DIRECTION, pos);
-		glLightf(glLight, GL_SPOT_CUTOFF, (_cutoff * 180.0) / M_PI);
-		glLightf(glLight, GL_SPOT_EXPONENT, _exponent);
+		tglLightfv(glLight, TGL_SPOT_DIRECTION, pos);
+		tglLightf(glLight, TGL_SPOT_CUTOFF, (_cutoff * 180.0) / M_PI);
+		tglLightf(glLight, TGL_SPOT_EXPONENT, _exponent);
 	} else {
-		glLightf(glLight, GL_SPOT_CUTOFF, 180.0);
+		tglLightf(glLight, TGL_SPOT_CUTOFF, 180.0);
 	}
 }
 
@@ -121,7 +121,7 @@ void TeLightTinyGL::update(uint lightno) {
 void TeLightTinyGL::updateGlobal() {
 	const float col[4] = {_globalAmbientColor.r() / 255.0f,
 			_globalAmbientColor.g() / 255.0f, _globalAmbientColor.b() / 255.0f, 1.0};
-	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
+	tglLightModelfv(TGL_LIGHT_MODEL_AMBIENT, col);
 }
 
 } // end namespace Tetraedge


Commit: 8dfe5e4b887cd137501b04d21207e69a9892ba05
    https://github.com/scummvm/scummvm/commit/8dfe5e4b887cd137501b04d21207e69a9892ba05
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Clean up OGL/TGL classes slightly

Changed paths:
    engines/tetraedge/te/te_3d_texture_opengl.cpp
    engines/tetraedge/te/te_3d_texture_tinygl.cpp
    engines/tetraedge/te/te_mesh_opengl.cpp
    engines/tetraedge/te/te_mesh_tinygl.cpp
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer_opengl.cpp
    engines/tetraedge/te/te_renderer_tinygl.cpp


diff --git a/engines/tetraedge/te/te_3d_texture_opengl.cpp b/engines/tetraedge/te/te_3d_texture_opengl.cpp
index 492c16320a0..2d0f42bc848 100644
--- a/engines/tetraedge/te/te_3d_texture_opengl.cpp
+++ b/engines/tetraedge/te/te_3d_texture_opengl.cpp
@@ -101,12 +101,8 @@ void Te3DTextureOpenGL::destroy() {
 }
 
 void Te3DTextureOpenGL::forceTexData(uint gltexture, uint xsize, uint ysize) {
-	if (_glTexture != 0xffffffff) {
-		if (_createdTexture)
-			glDeleteTextures(1, &_glTexture);
-		_createdTexture = false;
-		_loaded = false;
-	}
+	if (_glTexture != 0xffffffff)
+		destroy();
 	_glTexture = gltexture;
 	_width = xsize;
 	_height = ysize;
@@ -144,17 +140,9 @@ bool Te3DTextureOpenGL::load(const TeImage &img) {
 
 	const void *imgdata = img.getPixels();
 	if (_format == TeImage::RGB8) {
-		/*GLenum glpxformat = GL_RGB;
-		if (_glPixelFormat != GL_INVALID_ENUM) {
-			glpxformat = _glPixelFormat;
-		}*/
 		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 glpxformat = GL_RGBA8;
-		if (_glPixelFormat != GL_INVALID_ENUM) {
-			glpxformat = _glPixelFormat;
-		}*/
 		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);
 	} else {
diff --git a/engines/tetraedge/te/te_3d_texture_tinygl.cpp b/engines/tetraedge/te/te_3d_texture_tinygl.cpp
index ab7292c46a5..f4e98a8c496 100644
--- a/engines/tetraedge/te/te_3d_texture_tinygl.cpp
+++ b/engines/tetraedge/te/te_3d_texture_tinygl.cpp
@@ -103,12 +103,8 @@ void Te3DTextureTinyGL::destroy() {
 }
 
 void Te3DTextureTinyGL::forceTexData(uint gltexture, uint xsize, uint ysize) {
-	if (_glTexture != 0xffffffff) {
-		if (_createdTexture)
-			tglDeleteTextures(1, &_glTexture);
-		_createdTexture = false;
-		_loaded = false;
-	}
+	if (_glTexture != 0xffffffff)
+		destroy();
 	_glTexture = gltexture;
 	_width = xsize;
 	_height = ysize;
@@ -145,16 +141,8 @@ bool Te3DTextureTinyGL::load(const TeImage &img) {
 
 	const void *imgdata = img.getPixels();
 	if (_format == TeImage::RGB8) {
-		/*GLenum glpxformat = GL_RGB;
-		if (_glPixelFormat != GL_INVALID_ENUM) {
-			glpxformat = _glPixelFormat;
-		}*/
 		tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, img.pitch / 3, img.h, 0, TGL_RGB, TGL_UNSIGNED_BYTE, imgdata);
 	} else if (_format == TeImage::RGBA8) {
-		/*GLenum glpxformat = GL_RGBA8;
-		if (_glPixelFormat != GL_INVALID_ENUM) {
-			glpxformat = _glPixelFormat;
-		}*/
 		tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, img.w, img.h, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, imgdata);
 	} else {
 		warning("Te3DTexture::load can't send image format %d to GL.", _format);
diff --git a/engines/tetraedge/te/te_mesh_opengl.cpp b/engines/tetraedge/te/te_mesh_opengl.cpp
index 86ebdaeada5..97925714e77 100644
--- a/engines/tetraedge/te/te_mesh_opengl.cpp
+++ b/engines/tetraedge/te/te_mesh_opengl.cpp
@@ -56,8 +56,6 @@ void TeMeshOpenGL::draw() {
 	debug("   worldRot   %s", worldRotation().dump().c_str());
 	*/
 
-	Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
-	Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
 	if (renderer->shadowMode() != TeRenderer::ShadowMode1) {
 		if (_faceCounts.empty()) {
 			if (hasAlpha(0) && _shouldDraw) {
@@ -82,6 +80,9 @@ void TeMeshOpenGL::draw() {
 	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
 	renderer->pushMatrix();
 	renderer->loadCurrentMatrixToGL();
+
+	const Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
+	const Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
 	glEnableClientState(GL_VERTEX_ARRAY);
 	if (!normals.empty())
 		glEnableClientState(GL_NORMAL_ARRAY);
diff --git a/engines/tetraedge/te/te_mesh_tinygl.cpp b/engines/tetraedge/te/te_mesh_tinygl.cpp
index 8863f5e5dc4..acedd32db39 100644
--- a/engines/tetraedge/te/te_mesh_tinygl.cpp
+++ b/engines/tetraedge/te/te_mesh_tinygl.cpp
@@ -55,8 +55,6 @@ void TeMeshTinyGL::draw() {
 	debug("   worldRot   %s", worldRotation().dump().c_str());
 	*/
 
-	Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
-	Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
 	if (renderer->shadowMode() != TeRenderer::ShadowMode1) {
 		if (_faceCounts.empty()) {
 			if (hasAlpha(0) && _shouldDraw) {
@@ -78,6 +76,9 @@ void TeMeshTinyGL::draw() {
 		}
 	}
 
+	const Common::Array<TeVector3f32> &normals = (_updatedVerticies.empty() ? _normals : _updatedNormals);
+	const Common::Array<TeVector3f32> &verticies = (_updatedVerticies.empty() ? _verticies : _updatedVerticies);
+
 	renderer->setMatrixMode(TeRenderer::MM_GL_MODELVIEW);
 	renderer->pushMatrix();
 	renderer->loadCurrentMatrixToGL();
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 900164b320b..875de0ff9f3 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -226,10 +226,31 @@ void TeRenderer::multiplyMatrix(const TeMatrix4x4 &matrix) {
 	_matriciesStacks[_matrixMode].multiplyMatrix(matrix);
 }
 
+
+static bool compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
+											const TeRenderer::TransparentMeshProperties &p2) {
+	return (p1._zOrder < p2._zOrder);
+}
+
 void TeRenderer::optimiseTransparentMeshProperties() {
 	if (_transparentMeshProps.size() <= 1)
 		return;
 
+	// Note: this first bit of logic is in renderTransparentMeshes in the
+	// original game, but was moved here to split out OGL-specific code.
+	//dumpTransparentMeshProps();
+
+	Common::sort(_transparentMeshProps.begin(), _transparentMeshProps.end(),
+		 compareTransparentMeshProperties);
+
+	int vertTotal = 0;
+	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
+		const uint vcount = _transparentMeshProps[i]._vertexCount;
+		for (uint j = 0; j < vcount; j++)
+			_transparentMeshVertexNums[vertTotal + j] = (short)(_transparentMeshProps[i]._sourceTransparentMesh + j);
+		vertTotal += vcount;
+	}
+
 	uint i = 0;
 	for (uint other = 1; other < _transparentMeshProps.size(); other++) {
 		uint nextI = other;
@@ -249,6 +270,9 @@ void TeRenderer::optimiseTransparentMeshProperties() {
 		}
 		i = nextI;
 	}
+
+	//dumpTransparentMeshProps();
+	//dumpTransparentMeshData();
 }
 
 void TeRenderer::popMatrix() {
diff --git a/engines/tetraedge/te/te_renderer_opengl.cpp b/engines/tetraedge/te/te_renderer_opengl.cpp
index abbf81550ae..83a12262aec 100644
--- a/engines/tetraedge/te/te_renderer_opengl.cpp
+++ b/engines/tetraedge/te/te_renderer_opengl.cpp
@@ -28,7 +28,6 @@
 
 #include "tetraedge/te/te_renderer.h"
 #include "tetraedge/te/te_renderer_opengl.h"
-#include "tetraedge/te/te_light.h"
 #include "tetraedge/te/te_light_opengl.h"
 #include "tetraedge/te/te_mesh_opengl.h"
 
@@ -141,34 +140,16 @@ Common::String TeRendererOpenGL::renderer() {
 }
 
 
-static bool compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
-											const TeRenderer::TransparentMeshProperties &p2) {
-	return (p1._zOrder < p2._zOrder);
-}
-
 void TeRendererOpenGL::renderTransparentMeshes() {
 	if (!_numTransparentMeshes)
 		return;
 
 	glDepthMask(GL_FALSE);
-	//dumpTransparentMeshProps();
-
-	Common::sort(_transparentMeshProps.begin(), _transparentMeshProps.end(),
-		 compareTransparentMeshProperties);
-
-	int vertsDrawn = 0;
-	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
-		const uint vcount = _transparentMeshProps[i]._vertexCount;
-		for (uint j = 0; j < vcount; j++)
-			_transparentMeshVertexNums[vertsDrawn + j] = (short)(_transparentMeshProps[i]._sourceTransparentMesh + j);
-		vertsDrawn += vcount;
-	}
 
+	// Note: some code moved to optimiseTransparentMeshProperties to minimise
+	// non-OGL-speicifc code.
 	optimiseTransparentMeshProperties();
 
-	//dumpTransparentMeshProps();
-	//dumpTransparentMeshData();
-
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -182,7 +163,7 @@ void TeRendererOpenGL::renderTransparentMeshes() {
 	TeMaterial lastMaterial;
 	TeMatrix4x4 lastMatrix;
 
-	vertsDrawn = 0;
+	int vertsDrawn = 0;
 	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
 		const TransparentMeshProperties &meshProperties = _transparentMeshProps[i];
 		if (!meshProperties._shouldDraw)
@@ -377,7 +358,7 @@ void TeRendererOpenGL::applyMaterial(const TeMaterial &m) {
 		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, fullColor);
 	}
 
-	//warning("TODO: Work out what TeMaterial::_enableSomethingDefault0 actually is.");
+	// TODO: Work out what TeMaterial::_enableSomethingDefault0 actually is.
 	if (!m._enableSomethingDefault0) {
 		glDisable(GL_TEXTURE_GEN_S);
 		glDisable(GL_TEXTURE_GEN_T);
diff --git a/engines/tetraedge/te/te_renderer_tinygl.cpp b/engines/tetraedge/te/te_renderer_tinygl.cpp
index a8eba20af17..c5810e55443 100644
--- a/engines/tetraedge/te/te_renderer_tinygl.cpp
+++ b/engines/tetraedge/te/te_renderer_tinygl.cpp
@@ -158,24 +158,11 @@ void TeRendererTinyGL::renderTransparentMeshes() {
 		return;
 
 	tglDepthMask(TGL_FALSE);
-	//dumpTransparentMeshProps();
-
-	Common::sort(_transparentMeshProps.begin(), _transparentMeshProps.end(),
-		 compareTransparentMeshProperties);
-
-	int vertsDrawn = 0;
-	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
-		const uint vcount = _transparentMeshProps[i]._vertexCount;
-		for (uint j = 0; j < vcount; j++)
-			_transparentMeshVertexNums[vertsDrawn + j] = (short)(_transparentMeshProps[i]._sourceTransparentMesh + j);
-		vertsDrawn += vcount;
-	}
 
+	// Note: some code moved to optimiseTransparentMeshProperties to minimise
+	// non-OGL-speicifc code.
 	optimiseTransparentMeshProperties();
 
-	//dumpTransparentMeshProps();
-	//dumpTransparentMeshData();
-
 	tglEnableClientState(TGL_VERTEX_ARRAY);
 	tglEnableClientState(TGL_NORMAL_ARRAY);
 	tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
@@ -189,7 +176,7 @@ void TeRendererTinyGL::renderTransparentMeshes() {
 	TeMaterial lastMaterial;
 	TeMatrix4x4 lastMatrix;
 
-	vertsDrawn = 0;
+	int vertsDrawn = 0;
 	for (uint i = 0; i < _transparentMeshProps.size(); i++) {
 		const TransparentMeshProperties &meshProperties = _transparentMeshProps[i];
 		if (!meshProperties._shouldDraw)


Commit: 91f1e17ed3ea94351852c6a9299723726801e57f
    https://github.com/scummvm/scummvm/commit/91f1e17ed3ea94351852c6a9299723726801e57f
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Fixes to get to main menu of Syberia 2

Changed paths:
    engines/tetraedge/detection_tables.h
    engines/tetraedge/game/application.cpp
    engines/tetraedge/game/confirm.cpp
    engines/tetraedge/te/te_core.cpp


diff --git a/engines/tetraedge/detection_tables.h b/engines/tetraedge/detection_tables.h
index f141b5c3510..685464a620c 100644
--- a/engines/tetraedge/detection_tables.h
+++ b/engines/tetraedge/detection_tables.h
@@ -35,7 +35,18 @@ const ADGameDescription GAME_DESCRIPTIONS[] = {
 	{
 		"syberia",
 		nullptr,
-		AD_ENTRY1s("MacOS/Syberia", "6951fb8f71fe06f34684564625f73cd8", 10640592),
+		AD_ENTRY1s("MacOS/Syberia", "d:6951fb8f71fe06f34684564625f73cd8", 10640592),
+		Common::EN_ANY,
+		Common::kPlatformMacintosh,
+		ADGF_UNSTABLE,
+		GUIO1(GUIO_NONE)
+	},
+
+	// GOG release
+	{
+		"syberia2",
+		nullptr,
+		AD_ENTRY1s("MacOS/Syberia 2", "d:c447586a3cb3d46d6127b467e7fb9a86", 12021136),
 		Common::EN_ANY,
 		Common::kPlatformMacintosh,
 		ADGF_UNSTABLE,
diff --git a/engines/tetraedge/game/application.cpp b/engines/tetraedge/game/application.cpp
index f89442e05eb..b063e135ea3 100644
--- a/engines/tetraedge/game/application.cpp
+++ b/engines/tetraedge/game/application.cpp
@@ -144,7 +144,7 @@ void Application::create() {
 	int i = 0;
 	Common::Path textFilePath;
 	while (i < ARRAYSIZE(allLangs)) {
-		textFilePath = textsPath.join(core->language() + ".xml");
+		textFilePath = core->findFile(textsPath.join(core->language() + ".xml"));
 		if (Common::File::exists(textFilePath))
 			break;
 		core->language(allLangs[i]);
diff --git a/engines/tetraedge/game/confirm.cpp b/engines/tetraedge/game/confirm.cpp
index ff1bbdf6da4..399d349b336 100644
--- a/engines/tetraedge/game/confirm.cpp
+++ b/engines/tetraedge/game/confirm.cpp
@@ -34,7 +34,12 @@ Confirm::Confirm() {
 
 void Confirm::enter(const Common::String &guiPath, const Common::String &y) {
 	_gui.load(guiPath);
-	TeLayout *backgroundLayout = _gui.layoutChecked("background");
+	TeLayout *backgroundLayout = _gui.layout("background");
+	if (!backgroundLayout) {
+		warning("confirm script not loaded, default to Yes.");
+		onButtonYes();
+		return;
+	}
 	backgroundLayout->setRatioMode(TeILayout::RATIO_MODE_NONE);
 
 	Application *app = g_engine->getApplication();
diff --git a/engines/tetraedge/te/te_core.cpp b/engines/tetraedge/te/te_core.cpp
index 73960d82ca2..9f6059fa01a 100644
--- a/engines/tetraedge/te/te_core.cpp
+++ b/engines/tetraedge/te/te_core.cpp
@@ -129,9 +129,13 @@ Common::Path TeCore::findFile(const Common::Path &path) {
 		nullptr, // no suffix
 		"PC-MacOSX",
 		"PC-PS3-Android-MacOSX",
+		"PC-MacOSX-Android-iPhone-iPad",
 		"PC-MacOSX-Xbox360-PS3",
 		"PC-MacOSX-PS3-Xbox360",
 		"PC-MacOSX-Xbox360-PS3/PC-MacOSX",
+		"PC-MacOSX-MacOSXAppStore-Android-iPhone-iPad",
+		"PC-MacOSX-MacOSXAppStore-Xbox360-Android-iPad-iPhone",
+		"Android-iPhone-iPad-PC-MacOSX",
 		"Full",
 		"Part1-Full",
 		"Part2-Full-Part1",


Commit: b310f5dea3931549f2054e6c1660c41ac4b534e9
    https://github.com/scummvm/scummvm/commit/b310f5dea3931549f2054e6c1660c41ac4b534e9
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Fix compiler guards for GL (no shaders)

Changed paths:
    engines/tetraedge/game/characters_shadow.cpp
    engines/tetraedge/game/characters_shadow_opengl.h
    engines/tetraedge/te/te_3d_texture.cpp
    engines/tetraedge/te/te_3d_texture_opengl.h
    engines/tetraedge/te/te_light.cpp
    engines/tetraedge/te/te_light_opengl.h
    engines/tetraedge/te/te_mesh.cpp
    engines/tetraedge/te/te_mesh_opengl.h
    engines/tetraedge/te/te_renderer.cpp
    engines/tetraedge/te/te_renderer_opengl.h


diff --git a/engines/tetraedge/game/characters_shadow.cpp b/engines/tetraedge/game/characters_shadow.cpp
index 09fc4c55537..c982e3cb4e0 100644
--- a/engines/tetraedge/game/characters_shadow.cpp
+++ b/engines/tetraedge/game/characters_shadow.cpp
@@ -87,7 +87,7 @@ void CharactersShadow::destroy() {
 CharactersShadow *CharactersShadow::makeInstance() {
 	Graphics::RendererType r = g_engine->preferredRendererType();
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 	if (r == Graphics::kRendererTypeOpenGL)
 		return new CharactersShadowOpenGL();
 #endif
diff --git a/engines/tetraedge/game/characters_shadow_opengl.h b/engines/tetraedge/game/characters_shadow_opengl.h
index 84d7178c0f5..fb57e8fcbc0 100644
--- a/engines/tetraedge/game/characters_shadow_opengl.h
+++ b/engines/tetraedge/game/characters_shadow_opengl.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_GAME_CHARACTERS_SHADOW_OPENGL_H
 #define TETRAEDGE_GAME_CHARACTERS_SHADOW_OPENGL_H
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 
 #include "tetraedge/game/characters_shadow.h"
 
diff --git a/engines/tetraedge/te/te_3d_texture.cpp b/engines/tetraedge/te/te_3d_texture.cpp
index d2946022c4f..d033dd2526b 100644
--- a/engines/tetraedge/te/te_3d_texture.cpp
+++ b/engines/tetraedge/te/te_3d_texture.cpp
@@ -106,7 +106,7 @@ TeVector2s32 Te3DTexture::optimisedSize(const TeVector2s32 &size) {
 Te3DTexture *Te3DTexture::makeInstance() {
 	Graphics::RendererType r = g_engine->preferredRendererType();
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 	if (r == Graphics::kRendererTypeOpenGL)
 		return new Te3DTextureOpenGL();
 #endif
diff --git a/engines/tetraedge/te/te_3d_texture_opengl.h b/engines/tetraedge/te/te_3d_texture_opengl.h
index ad2a0801f75..be1d24b4d03 100644
--- a/engines/tetraedge/te/te_3d_texture_opengl.h
+++ b/engines/tetraedge/te/te_3d_texture_opengl.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_TE_TE_3D_TEXTURE_OPENGL_H
 #define TETRAEDGE_TE_TE_3D_TEXTURE_OPENGL_H
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 
 #include "tetraedge/te/te_3d_texture.h"
 
diff --git a/engines/tetraedge/te/te_light.cpp b/engines/tetraedge/te/te_light.cpp
index b1c4bdedaac..afde1e45cf7 100644
--- a/engines/tetraedge/te/te_light.cpp
+++ b/engines/tetraedge/te/te_light.cpp
@@ -98,7 +98,7 @@ Common::String TeLight::dump() const {
 TeLight *TeLight::makeInstance() {
 	Graphics::RendererType r = g_engine->preferredRendererType();
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 	if (r == Graphics::kRendererTypeOpenGL)
 		return new TeLightOpenGL();
 #endif
diff --git a/engines/tetraedge/te/te_light_opengl.h b/engines/tetraedge/te/te_light_opengl.h
index 561d86cc4d8..1cd4085ec64 100644
--- a/engines/tetraedge/te/te_light_opengl.h
+++ b/engines/tetraedge/te/te_light_opengl.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_TE_TE_LIGHT_OPENGL_H
 #define TETRAEDGE_TE_TE_LIGHT_OPENGL_H
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 
 #include "tetraedge/te/te_light.h"
 
diff --git a/engines/tetraedge/te/te_mesh.cpp b/engines/tetraedge/te/te_mesh.cpp
index 30dbe00ecc2..106e7617342 100644
--- a/engines/tetraedge/te/te_mesh.cpp
+++ b/engines/tetraedge/te/te_mesh.cpp
@@ -226,7 +226,7 @@ void TeMesh::updateTo(const Common::Array<TeMatrix4x4> *matricies1, const Common
 TeMesh *TeMesh::makeInstance() {
 	Graphics::RendererType r = g_engine->preferredRendererType();
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 	if (r == Graphics::kRendererTypeOpenGL)
 		return new TeMeshOpenGL();
 #endif
diff --git a/engines/tetraedge/te/te_mesh_opengl.h b/engines/tetraedge/te/te_mesh_opengl.h
index 9e1b6fad2d9..e8ea7d85140 100644
--- a/engines/tetraedge/te/te_mesh_opengl.h
+++ b/engines/tetraedge/te/te_mesh_opengl.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_TE_TE_MESH_OPENGL_H
 #define TETRAEDGE_TE_TE_MESH_OPENGL_H
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 
 #include "tetraedge/te/te_mesh.h"
 
diff --git a/engines/tetraedge/te/te_renderer.cpp b/engines/tetraedge/te/te_renderer.cpp
index 875de0ff9f3..153bf7a650c 100644
--- a/engines/tetraedge/te/te_renderer.cpp
+++ b/engines/tetraedge/te/te_renderer.cpp
@@ -339,7 +339,7 @@ void TeRenderer::translate(float x, float y, float z) {
 TeRenderer *TeRenderer::makeInstance() {
 	Graphics::RendererType r = g_engine->preferredRendererType();
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 	if (r == Graphics::kRendererTypeOpenGL)
 		return new TeRendererOpenGL();
 #endif
diff --git a/engines/tetraedge/te/te_renderer_opengl.h b/engines/tetraedge/te/te_renderer_opengl.h
index c6d7f76ab5e..a04f383c05a 100644
--- a/engines/tetraedge/te/te_renderer_opengl.h
+++ b/engines/tetraedge/te/te_renderer_opengl.h
@@ -22,7 +22,7 @@
 #ifndef TETRAEDGE_TE_TE_RENDERER_OPENGL_H
 #define TETRAEDGE_TE_TE_RENDERER_OPENGL_H
 
-#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
+#if defined(USE_OPENGL_GAME)
 
 #include "tetraedge/te/te_renderer.h"
 


Commit: dbe6da9980dd2e836bc656e721c65138fdc39fd7
    https://github.com/scummvm/scummvm/commit/dbe6da9980dd2e836bc656e721c65138fdc39fd7
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Remove compile warnings

Changed paths:
    engines/tetraedge/game/document.cpp
    engines/tetraedge/game/document.h
    engines/tetraedge/game/game.cpp
    engines/tetraedge/game/how_to.h
    engines/tetraedge/te/te_3d_texture_tinygl.cpp
    engines/tetraedge/te/te_camera.cpp
    engines/tetraedge/te/te_camera.h
    engines/tetraedge/te/te_color.cpp
    engines/tetraedge/te/te_color.h
    engines/tetraedge/te/te_font3.cpp
    engines/tetraedge/te/te_image.cpp
    engines/tetraedge/te/te_image.h
    engines/tetraedge/te/te_light.cpp
    engines/tetraedge/te/te_light.h
    engines/tetraedge/te/te_light_opengl.cpp
    engines/tetraedge/te/te_light_tinygl.cpp
    engines/tetraedge/te/te_object.cpp
    engines/tetraedge/te/te_renderer_tinygl.cpp
    engines/tetraedge/te/te_sprite_layout.h
    engines/tetraedge/te/te_text_base2.cpp
    engines/tetraedge/te/te_tiled_surface.cpp
    engines/tetraedge/te/te_tiled_texture.cpp
    engines/tetraedge/te/te_visual_fade.cpp


diff --git a/engines/tetraedge/game/document.cpp b/engines/tetraedge/game/document.cpp
index e31e7b29a44..75bf42ff255 100644
--- a/engines/tetraedge/game/document.cpp
+++ b/engines/tetraedge/game/document.cpp
@@ -23,7 +23,7 @@
 
 namespace Tetraedge {
 
-Document::Document(DocumentsBrowser *browser) : _browser(browser) {
+Document::Document(DocumentsBrowser *browser) /*: _browser(browser)*/ {
 
 }
 
diff --git a/engines/tetraedge/game/document.h b/engines/tetraedge/game/document.h
index 307e1f4f882..60771eb3156 100644
--- a/engines/tetraedge/game/document.h
+++ b/engines/tetraedge/game/document.h
@@ -53,7 +53,7 @@ public:
 	void unload();
 
 private:
-	DocumentsBrowser *_browser;
+	//DocumentsBrowser *_browser;
 	TeLuaGUI _gui;
 	TeSignal1Param<Document &> _onButtonDownSignal;
 };
diff --git a/engines/tetraedge/game/game.cpp b/engines/tetraedge/game/game.cpp
index ae01a2294ef..f6fbdc39958 100644
--- a/engines/tetraedge/game/game.cpp
+++ b/engines/tetraedge/game/game.cpp
@@ -1123,6 +1123,7 @@ bool Game::onMouseClick(const Common::Point &pt) {
 	return false;
 }
 
+#if ENABLE_CUSTOM_CURSOR_CHECKS
 // Note: None of these cursor files seem to be actually shipped with the game
 // but the logic is reproduced here just in case there's some different
 // version that uses them.
@@ -1144,6 +1145,7 @@ static const char cursorsTable[][2][80] = {
 	{"Type04", "pictures/Type04.png"},
 	{"Type05", "pictures/Type05.png"}
 };
+#endif
 
 bool Game::onMouseMove() {
 	if (!_entered)
diff --git a/engines/tetraedge/game/how_to.h b/engines/tetraedge/game/how_to.h
index d4ce9211887..79a252f704b 100644
--- a/engines/tetraedge/game/how_to.h
+++ b/engines/tetraedge/game/how_to.h
@@ -39,7 +39,7 @@ private:
 	bool updateDisplay(uint oldval, uint newval);
 
 	bool _entered;
-	uint _val;
+	//uint _val;
 };
 
 } // end namespace Tetraedge
diff --git a/engines/tetraedge/te/te_3d_texture_tinygl.cpp b/engines/tetraedge/te/te_3d_texture_tinygl.cpp
index f4e98a8c496..4b00e9f5b29 100644
--- a/engines/tetraedge/te/te_3d_texture_tinygl.cpp
+++ b/engines/tetraedge/te/te_3d_texture_tinygl.cpp
@@ -192,7 +192,7 @@ void Te3DTextureTinyGL::update(const TeImage &img, uint xoff, uint yoff) {
 	tglPixelStorei(TGL_UNPACK_SKIP_PIXELS, 0);
 	tglPixelStorei(TGL_UNPACK_ALIGNMENT, 1);
 
-	const void *imgdata = img.getPixels();
+	//const void *imgdata = img.getPixels();
 	if (_format == TeImage::RGB8) {
 		//TODO: Come up with equivalent for TGL.
 		//tglTexSubImage2D(TGL_TEXTURE_2D, 0, xoff, yoff, img.w, img.h, TGL_RGB, TGL_UNSIGNED_BYTE, imgdata);
diff --git a/engines/tetraedge/te/te_camera.cpp b/engines/tetraedge/te/te_camera.cpp
index 3183ab78b5e..4065f354ca9 100644
--- a/engines/tetraedge/te/te_camera.cpp
+++ b/engines/tetraedge/te/te_camera.cpp
@@ -31,7 +31,7 @@ namespace Tetraedge {
 
 TeCamera::TeCamera() : _projectionMatrixType(0), _orthogonalParamL(0.0f),
 	_orthogonalParamR(1.0f), _orthogonalParamT(1.0f), _orthogonalParamB(0.0f),
-	_orthNearVal(10.0f), _orthFarVal(4000.0f), _transformA(0), _transformB(0),
+	_orthNearVal(10.0f), _orthFarVal(4000.0f), _transformA(0), /*_transformB(0),*/
 	_fov(40.0f), _somePerspectiveVal(1.0f)
 {
 }
diff --git a/engines/tetraedge/te/te_camera.h b/engines/tetraedge/te/te_camera.h
index 1afc99d426c..39c337ca908 100644
--- a/engines/tetraedge/te/te_camera.h
+++ b/engines/tetraedge/te/te_camera.h
@@ -96,7 +96,7 @@ private:
 	uint _viewportH;
 
 	int _transformA;
-	int _transformB;
+	//int _transformB; // never used?
 
 	float _orthogonalParamL;
 	float _orthogonalParamR;
diff --git a/engines/tetraedge/te/te_color.cpp b/engines/tetraedge/te/te_color.cpp
index ee57a87ab90..0714779a7b4 100644
--- a/engines/tetraedge/te/te_color.cpp
+++ b/engines/tetraedge/te/te_color.cpp
@@ -43,10 +43,14 @@ TeColor::TeColor(uint16 shortcol) {
 TeColor::TeColor(byte r, byte g, byte b, byte a) : _c{r, g, b, a} {
 }
 
-uint32 TeColor::getPacked() {
+uint32 TeColor::getPacked() const {
 	return (g() & 0xf8) << 2 | (r() & 0xf8) << 7 | (b() >> 3);
 }
 
+uint32 TeColor::getPacked32() const {
+	return (r() << 24) | (g() << 16) | (b() << 8) | a();
+}
+
 bool TeColor::serialize(Common::WriteStream &stream) const {
 	for (int i = 0; i < 4; i++)
 		stream.writeByte(_c[i]);
diff --git a/engines/tetraedge/te/te_color.h b/engines/tetraedge/te/te_color.h
index 99704bca302..537a75d8f1a 100644
--- a/engines/tetraedge/te/te_color.h
+++ b/engines/tetraedge/te/te_color.h
@@ -44,7 +44,8 @@ public:
 	const byte &b() const { return _c[2]; };
 	const byte &a() const { return _c[3]; };
 
-	uint32 getPacked();
+	uint32 getPacked() const;
+	uint32 getPacked32() const;
 
 	bool serialize(Common::WriteStream &stream) const;
 	bool deserialize(Common::ReadStream &stream);
diff --git a/engines/tetraedge/te/te_font3.cpp b/engines/tetraedge/te/te_font3.cpp
index 18c03a031cc..5a697c0cb04 100644
--- a/engines/tetraedge/te/te_font3.cpp
+++ b/engines/tetraedge/te/te_font3.cpp
@@ -99,7 +99,7 @@ TeFont3::GlyphData TeFont3::glyph(uint pxSize, uint charcode) {
 	Common::Rect bbox = font->getBoundingBox(charcode);
 	TeImage *img = new TeImage();
 	Common::SharedPtr<TePalette> nullpal;
-	img->create(bbox.width(), bbox.height(), nullpal, TeImage::RGBA8);
+	img->createImg(bbox.width(), bbox.height(), nullpal, TeImage::RGBA8);
 	font->drawChar(img, charcode, 0, 0, 0xffffffff);
 	GlyphData retval;
 	retval._charcode = charcode;
diff --git a/engines/tetraedge/te/te_image.cpp b/engines/tetraedge/te/te_image.cpp
index 01d5a638eaf..72cba1e30ff 100644
--- a/engines/tetraedge/te/te_image.cpp
+++ b/engines/tetraedge/te/te_image.cpp
@@ -43,12 +43,13 @@ unsigned long TeImage::countPixelsOfColor(const TeColor &col) const {
 	error("TODO: Implement TeImage::countPixelsOfColor");
 }
 
+/*
 void TeImage::create() {
 	// Never used, but in original seems to do the same as destroy??
 	destroy();
-}
+}*/
 
-void TeImage::create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
+void TeImage::createImg(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
 			Format teformat, uint bufxsize, uint bufysize) {
 	_teFormat = teformat;
 	Graphics::PixelFormat pxformat = ((teformat == TeImage::RGB8) ?
@@ -99,7 +100,7 @@ bool TeImage::load(const Common::Path &path) {
 	}
 
 	Common::SharedPtr<TePalette> nullpal;
-	create(codec->width(), codec->height(), nullpal, codec->imageFormat(), codec->width(), codec->height());
+	createImg(codec->width(), codec->height(), nullpal, codec->imageFormat(), codec->width(), codec->height());
 
 	if (!codec->update(0, *this)) {
 		error("TeImage::load: Failed to update from %s.", path.toString().c_str());
diff --git a/engines/tetraedge/te/te_image.h b/engines/tetraedge/te/te_image.h
index e363ef3d574..c0ec7d241fa 100644
--- a/engines/tetraedge/te/te_image.h
+++ b/engines/tetraedge/te/te_image.h
@@ -57,11 +57,11 @@ public:
 	void copy(TeImage &dest, const TeVector2s32 &vec1, const TeVector2s32 &vec2,
 			  const TeVector2s32 &vec3) const;
 	unsigned long countPixelsOfColor(const TeColor &col) const;
-	void create();
-	void create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &palette, Format newformat) {
-		create(xsize, ysize, palette, newformat, xsize, ysize);
+	//void create(); // never used?
+	void createImg(uint xsize, uint ysize, Common::SharedPtr<TePalette> &palette, Format newformat) {
+		createImg(xsize, ysize, palette, newformat, xsize, ysize);
 	}
-	void create(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
+	void createImg(uint xsize, uint ysize, Common::SharedPtr<TePalette> &pal,
 				Format format, uint bufxsize, uint bufysize);
 	void deserialize(Common::ReadStream &stream);
 	void destroy();
diff --git a/engines/tetraedge/te/te_light.cpp b/engines/tetraedge/te/te_light.cpp
index afde1e45cf7..2f648b547de 100644
--- a/engines/tetraedge/te/te_light.cpp
+++ b/engines/tetraedge/te/te_light.cpp
@@ -34,7 +34,7 @@
 namespace Tetraedge {
 
 /*static*/
-TeColor TeLight::_globalAmbientColor;
+uint32 TeLight::_globalAmbientColor;
 
 TeLight::TeLight() : _colAmbient(0, 0, 0, 0xff), _colDiffuse(0, 0, 0, 0xff), _colSpecular(0xff, 0xff, 0xff, 0xff),
 _constAtten(1.0f), _linearAtten(0.0f), _quadraticAtten(0.0f), _cutoff(0.0f), _exponent(0.0f), _type(LightTypePoint),
diff --git a/engines/tetraedge/te/te_light.h b/engines/tetraedge/te/te_light.h
index e4223ebc82c..919b5ed4ad4 100644
--- a/engines/tetraedge/te/te_light.h
+++ b/engines/tetraedge/te/te_light.h
@@ -52,8 +52,8 @@ public:
 
 	virtual void update(uint lightno) = 0;
 	static void updateGlobal();
-	static void setGlobalAmbient(const TeColor &col) { _globalAmbientColor = col; }
-	static const TeColor &globalAmbient() { return _globalAmbientColor; }
+	static void setGlobalAmbient(const TeColor &col) { _globalAmbientColor = col.getPacked32(); }
+	static TeColor globalAmbient() { return TeColor(_globalAmbientColor); }
 
 	void setSpecular(const TeColor &col) { _colSpecular = col; }
 	void setDiffuse(const TeColor &col) { _colDiffuse = col; }
@@ -86,7 +86,7 @@ protected:
 
 	enum TeLightType _type;
 
-	static TeColor _globalAmbientColor;
+	static uint32 _globalAmbientColor;
 
 	float _constAtten;
 	float _linearAtten;
diff --git a/engines/tetraedge/te/te_light_opengl.cpp b/engines/tetraedge/te/te_light_opengl.cpp
index c0021afb485..2ea36871092 100644
--- a/engines/tetraedge/te/te_light_opengl.cpp
+++ b/engines/tetraedge/te/te_light_opengl.cpp
@@ -119,8 +119,9 @@ void TeLightOpenGL::update(uint lightno) {
 
 /*static*/
 void TeLightOpenGL::updateGlobal() {
-	const float col[4] = {_globalAmbientColor.r() / 255.0f,
-			_globalAmbientColor.g() / 255.0f, _globalAmbientColor.b() / 255.0f, 1.0};
+	const TeColor globalAmbient(_globalAmbientColor);
+	const float col[4] = {globalAmbient.r() / 255.0f,
+			globalAmbient.g() / 255.0f, globalAmbient.b() / 255.0f, 1.0};
 	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
 }
 
diff --git a/engines/tetraedge/te/te_light_tinygl.cpp b/engines/tetraedge/te/te_light_tinygl.cpp
index fddb0cb4783..64ebaafcb2f 100644
--- a/engines/tetraedge/te/te_light_tinygl.cpp
+++ b/engines/tetraedge/te/te_light_tinygl.cpp
@@ -119,8 +119,9 @@ void TeLightTinyGL::update(uint lightno) {
 
 /*static*/
 void TeLightTinyGL::updateGlobal() {
-	const float col[4] = {_globalAmbientColor.r() / 255.0f,
-			_globalAmbientColor.g() / 255.0f, _globalAmbientColor.b() / 255.0f, 1.0};
+	const TeColor globalAmbient(_globalAmbientColor);
+	const float col[4] = {globalAmbient.r() / 255.0f,
+			globalAmbient.g() / 255.0f, globalAmbient.b() / 255.0f, 1.0};
 	tglLightModelfv(TGL_LIGHT_MODEL_AMBIENT, col);
 }
 
diff --git a/engines/tetraedge/te/te_object.cpp b/engines/tetraedge/te/te_object.cpp
index ac63db8e899..da5e228719c 100644
--- a/engines/tetraedge/te/te_object.cpp
+++ b/engines/tetraedge/te/te_object.cpp
@@ -56,7 +56,7 @@ void TeObject::cleanup() {
 	if (_pendingDeleteList && _pendingDeleteList->size()) {
 		warning("Leaking %d objects on shutdown.", _pendingDeleteList->size());
 		for (auto *obj : (*_pendingDeleteList)) {
-			debug("Leaked %p", obj);
+			debug("Leaked %p", (void *)obj);
 		}
 	}
 	delete _pendingDeleteList;
diff --git a/engines/tetraedge/te/te_renderer_tinygl.cpp b/engines/tetraedge/te/te_renderer_tinygl.cpp
index c5810e55443..6a40e503057 100644
--- a/engines/tetraedge/te/te_renderer_tinygl.cpp
+++ b/engines/tetraedge/te/te_renderer_tinygl.cpp
@@ -147,12 +147,6 @@ Common::String TeRendererTinyGL::renderer() {
 }
 
 
-static bool compareTransparentMeshProperties(const TeRenderer::TransparentMeshProperties &p1,
-											const TeRenderer::TransparentMeshProperties &p2) {
-	return (p1._zOrder < p2._zOrder);
-}
-
-
 void TeRendererTinyGL::renderTransparentMeshes() {
 	if (!_numTransparentMeshes)
 		return;
@@ -313,7 +307,7 @@ void TeRendererTinyGL::shadowMode(enum ShadowMode mode) {
 
 void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
 	//debug("TeMaterial::apply (%s)", dump().c_str());
-	static const float constColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+	//static const float constColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
 	if (_shadowMode == TeRenderer::ShadowMode0) {
 		if (m._enableLights)
 			TeLightTinyGL::enableAll();
diff --git a/engines/tetraedge/te/te_sprite_layout.h b/engines/tetraedge/te/te_sprite_layout.h
index 4784cabb8e2..1f2058b804f 100644
--- a/engines/tetraedge/te/te_sprite_layout.h
+++ b/engines/tetraedge/te/te_sprite_layout.h
@@ -64,7 +64,7 @@ public:
 
 private:
 	bool _sizeSet;
-	bool _allowFloatTranslate;
+	//bool _allowFloatTranslate;
 
 };
 
diff --git a/engines/tetraedge/te/te_text_base2.cpp b/engines/tetraedge/te/te_text_base2.cpp
index 5e1f56ff4c0..c87b7808c5a 100644
--- a/engines/tetraedge/te/te_text_base2.cpp
+++ b/engines/tetraedge/te/te_text_base2.cpp
@@ -97,7 +97,7 @@ void TeTextBase2::build() {
 
 	TeImage img;
 	Common::SharedPtr<TePalette> nullpal;
-	img.create(_size._x, _size._y, nullpal, TeImage::RGBA8);
+	img.createImg(_size._x, _size._y, nullpal, TeImage::RGBA8);
 	img.fill(_globalColor.r(), _globalColor.g(), _globalColor.b(), 0);
 
 	for (uint i = 0; i < _wrappedLines.size(); i++) {
diff --git a/engines/tetraedge/te/te_tiled_surface.cpp b/engines/tetraedge/te/te_tiled_surface.cpp
index b9b9c7aba75..ae07f11a0f7 100644
--- a/engines/tetraedge/te/te_tiled_surface.cpp
+++ b/engines/tetraedge/te/te_tiled_surface.cpp
@@ -103,7 +103,7 @@ bool TeTiledSurface::load(const Common::Path &path) {
 			}
 
 			Common::SharedPtr<TePalette> nullpal;
-			img.create(_codec->width(), _codec->height(), nullpal, _imgFormat, bufx, bufy);
+			img.createImg(_codec->width(), _codec->height(), nullpal, _imgFormat, bufx, bufy);
 
 			if (_codec->update(0, img)) {
 #if DUMP_LOADED_IMAGES
@@ -168,7 +168,7 @@ bool TeTiledSurface::onFrameAnimCurrentFrameChanged() {
 	int bufxsize = MIN(vidSize._x + 4, optimisedSize._x);
 
 	Common::SharedPtr<TePalette> nullPal;
-	img.create(vidSize._x, vidSize._y, nullPal, _imgFormat, bufxsize, bufysize);
+	img.createImg(vidSize._x, vidSize._y, nullPal, _imgFormat, bufxsize, bufysize);
 	if (_codec->update(_frameAnim.lastFrameShown(), img))
 		update(img);
 	return _codec->isAtEnd();
diff --git a/engines/tetraedge/te/te_tiled_texture.cpp b/engines/tetraedge/te/te_tiled_texture.cpp
index a6b9b233706..2c4de242da9 100644
--- a/engines/tetraedge/te/te_tiled_texture.cpp
+++ b/engines/tetraedge/te/te_tiled_texture.cpp
@@ -152,7 +152,7 @@ TeImage *TeTiledTexture::optimisedTileImage(Common::Array<TeImage> &images, cons
 	images.resize(images.size() + 1);
 	TeImage &newImg = images.back();
 	Common::SharedPtr<TePalette> nullPal;
-	newImg.create((uint)size._x, (uint)size._y, nullPal, format);
+	newImg.createImg((uint)size._x, (uint)size._y, nullPal, format);
 	return &newImg;
 }
 
diff --git a/engines/tetraedge/te/te_visual_fade.cpp b/engines/tetraedge/te/te_visual_fade.cpp
index 1840c210bce..3eaf7f30dc2 100644
--- a/engines/tetraedge/te/te_visual_fade.cpp
+++ b/engines/tetraedge/te/te_visual_fade.cpp
@@ -108,7 +108,7 @@ void TeVisualFade::init() {
 	Common::SharedPtr<TePalette> nullpal;
 	_image.destroy();
 	// TODO: should this get actual window size instead of default?
-	_image.create(g_engine->getDefaultScreenWidth(),
+	_image.createImg(g_engine->getDefaultScreenWidth(),
 			g_engine->getDefaultScreenHeight(), nullpal, TeImage::RGBA8);
 	_texturePtr->load(_image);
 	g_engine->getRenderer()->enableTexture();


Commit: 8591ae2260ec6c6a202a6d6893a1ebde0164c011
    https://github.com/scummvm/scummvm/commit/8591ae2260ec6c6a202a6d6893a1ebde0164c011
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-01-16T17:36:43+01:00

Commit Message:
TETRAEDGE: Fix updating of global light value

Changed paths:
    engines/tetraedge/game/in_game_scene.cpp
    engines/tetraedge/te/te_light.cpp
    engines/tetraedge/te/te_light.h
    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/in_game_scene.cpp b/engines/tetraedge/game/in_game_scene.cpp
index 0e1416d0f44..d3e4ccc0128 100644
--- a/engines/tetraedge/game/in_game_scene.cpp
+++ b/engines/tetraedge/game/in_game_scene.cpp
@@ -367,7 +367,7 @@ void InGameScene::draw() {
 	}
 #endif
 
-	TeLight::updateGlobal();
+	g_engine->getRenderer()->updateGlobalLight();
 	for (uint i = 0; i < _lights.size(); i++)
 		_lights[i]->update(i);
 
diff --git a/engines/tetraedge/te/te_light.cpp b/engines/tetraedge/te/te_light.cpp
index 2f648b547de..fc47f6b319a 100644
--- a/engines/tetraedge/te/te_light.cpp
+++ b/engines/tetraedge/te/te_light.cpp
@@ -66,11 +66,6 @@ void TeLight::transformSpotPoint(TeVector3f32 &pt) {
 	pt += _position3d;
 }
 
-/*static*/
-void TeLight::updateGlobal() {
-	// TOOD: Call correct global.
-}
-
 Common::String TeLight::dump() const {
 	const char *ltype;
 	switch (_type) {
diff --git a/engines/tetraedge/te/te_light.h b/engines/tetraedge/te/te_light.h
index 919b5ed4ad4..64b75f5c31c 100644
--- a/engines/tetraedge/te/te_light.h
+++ b/engines/tetraedge/te/te_light.h
@@ -51,7 +51,6 @@ public:
 	void transformSpotPoint(TeVector3f32 &pt1);
 
 	virtual void update(uint lightno) = 0;
-	static void updateGlobal();
 	static void setGlobalAmbient(const TeColor &col) { _globalAmbientColor = col.getPacked32(); }
 	static TeColor globalAmbient() { return TeColor(_globalAmbientColor); }
 
diff --git a/engines/tetraedge/te/te_renderer.h b/engines/tetraedge/te/te_renderer.h
index c50ecb7ac95..3f7c964adee 100644
--- a/engines/tetraedge/te/te_renderer.h
+++ b/engines/tetraedge/te/te_renderer.h
@@ -131,6 +131,7 @@ public:
 	void dumpTransparentMeshData() const;
 	const TeColor &currentColor() const { return _currentColor; }
 
+	virtual void updateGlobalLight() = 0;
 	virtual void applyMaterial(const TeMaterial &m) = 0;
 
 	static TeRenderer *makeInstance();
diff --git a/engines/tetraedge/te/te_renderer_opengl.cpp b/engines/tetraedge/te/te_renderer_opengl.cpp
index 83a12262aec..4aeaf16632e 100644
--- a/engines/tetraedge/te/te_renderer_opengl.cpp
+++ b/engines/tetraedge/te/te_renderer_opengl.cpp
@@ -386,6 +386,10 @@ void TeRendererOpenGL::applyMaterial(const TeMaterial &m) {
 	}
 }
 
+void TeRendererOpenGL::updateGlobalLight() {
+	TeLightOpenGL::updateGlobal();
+}
+
 Common::String TeRendererOpenGL::vendor() {
 	return Common::String((const char *)glGetString(GL_VENDOR));
 }
diff --git a/engines/tetraedge/te/te_renderer_opengl.h b/engines/tetraedge/te/te_renderer_opengl.h
index a04f383c05a..7bdd43ab1c2 100644
--- a/engines/tetraedge/te/te_renderer_opengl.h
+++ b/engines/tetraedge/te/te_renderer_opengl.h
@@ -53,6 +53,7 @@ public:
 	void shadowMode(enum ShadowMode mode) override;
 	Common::String vendor() override;
 	void applyMaterial(const TeMaterial &m) override;
+	void updateGlobalLight() override;
 
 protected:
 
diff --git a/engines/tetraedge/te/te_renderer_tinygl.cpp b/engines/tetraedge/te/te_renderer_tinygl.cpp
index 6a40e503057..9654f530c3b 100644
--- a/engines/tetraedge/te/te_renderer_tinygl.cpp
+++ b/engines/tetraedge/te/te_renderer_tinygl.cpp
@@ -404,6 +404,9 @@ void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
 	}
 }
 
+void TeRendererTinyGL::updateGlobalLight() {
+	TeLightTinyGL::updateGlobal();
+}
 
 Common::String TeRendererTinyGL::vendor() {
 	return "TinyGL vendor";
diff --git a/engines/tetraedge/te/te_renderer_tinygl.h b/engines/tetraedge/te/te_renderer_tinygl.h
index 6599ff56a24..60db224e5b0 100644
--- a/engines/tetraedge/te/te_renderer_tinygl.h
+++ b/engines/tetraedge/te/te_renderer_tinygl.h
@@ -53,6 +53,7 @@ public:
 	void setViewport(int x, int y, int w, int h) override;
 	void shadowMode(enum ShadowMode mode) override;
 	void applyMaterial(const TeMaterial &m) override;
+	void updateGlobalLight() override;
 	Common::String vendor() override;
 
 protected:




More information about the Scummvm-git-logs mailing list