diff --git a/build/demos/levels/demo_narrow_passage/demo_narrow_passage.ent b/build/demos/levels/demo_narrow_passage/demo_narrow_passage.ent
new file mode 100644
index 0000000000000000000000000000000000000000..f360fb1cbc80b9b9182b55b5e5022c74f9b07d63
--- /dev/null
+++ b/build/demos/levels/demo_narrow_passage/demo_narrow_passage.ent
@@ -0,0 +1,21 @@
+
+[{D8CE1F61-7941-498F-BD0A-D768A8257ADB}]
+up_point = 3.700012 0.000000 0.000000
+speed = 0.500000
+rotation = 0.000000 -0.707107 0.000000 -0.707107
+parent = 
+origin = 0.000008 0.100000 -1.849973
+name = 
+flags = 0
+classname = func_narrow_passage
+OnPlayerGetOn = 
+OnPlayerGetOff = 
+
+[{DC740742-575B-4B03-873A-1717475B2375}]
+team = -842150451
+rotation = 0.000000 0.000000 0.000000 1.000000
+parent = 
+origin = 0.096405 0.000000 -3.485951
+name = 
+flags = 0
+classname = info_player_spawn
diff --git a/build/demos/levels/demo_narrow_passage/demo_narrow_passage.lvl b/build/demos/levels/demo_narrow_passage/demo_narrow_passage.lvl
new file mode 100644
index 0000000000000000000000000000000000000000..dfbcb5c3c2cdedaf67d790c0d329a20ea86e2f49
--- /dev/null
+++ b/build/demos/levels/demo_narrow_passage/demo_narrow_passage.lvl
@@ -0,0 +1,24 @@
+[level]
+type = indoor
+local_name = demo_narrow_passage
+
+[terrax]
+vp_layout = 0
+grid_step = 4
+grid_show = 1
+cam3_view = 1
+cam3_scale = 0.009128
+cam3_rot = 0.000000 0.000000 0.000000 1.000000
+cam3_pos = -0.616946 0.147523 -1000.000000
+cam2_view = 2
+cam2_scale = 0.008899
+cam2_rot = 0.000000 0.707107 0.000000 0.707107
+cam2_pos = 1000.000000 1.106782 -17.269983
+cam1_view = 0
+cam1_scale = 0.034539
+cam1_rot = -0.707107 0.000000 0.000000 0.707107
+cam1_pos = -5.000654 1000.000000 0.807223
+cam0_view = -1
+cam0_scale = 1.000000
+cam0_rot = 0.219844 0.603690 -0.178066 -0.745332
+cam0_pos = -8.303119 6.760478 -3.441035
diff --git a/build/demos/levels/demo_narrow_passage/editor/groups.json b/build/demos/levels/demo_narrow_passage/editor/groups.json
new file mode 100644
index 0000000000000000000000000000000000000000..0d4f101c7a37a4c875e6999bee1a287fdb733380
--- /dev/null
+++ b/build/demos/levels/demo_narrow_passage/editor/groups.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/build/demos/levels/demo_narrow_passage/editor/proxies.json b/build/demos/levels/demo_narrow_passage/editor/proxies.json
new file mode 100644
index 0000000000000000000000000000000000000000..0d4f101c7a37a4c875e6999bee1a287fdb733380
--- /dev/null
+++ b/build/demos/levels/demo_narrow_passage/editor/proxies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/build/demos/levels/demo_narrow_passage/editor/xcsg.json b/build/demos/levels/demo_narrow_passage/editor/xcsg.json
new file mode 100644
index 0000000000000000000000000000000000000000..ab68f82be5bdb7d2920113c26a0f50de31a0b6a2
--- /dev/null
+++ b/build/demos/levels/demo_narrow_passage/editor/xcsg.json
@@ -0,0 +1,48 @@
+{"brushes":[
+	{
+		"brush": [{"v":[[-5,-0.2,5],[-5,-0.2,-5],[5,-0.2,-5],[5,-0.2,5],[-5,0,5],[-5,0,-5],[5,0,-5],[5,0,5]],"e":[[0,1,0],[1,2,0],[2,3,0],[3,0,0],[4,5,0],[5,6,0],[6,7,0],[4,7,0],[0,4,0],[1,5,0],[2,6,0],[3,7,0]],"f":[{"t":{"s":[-0,-0,-1,4.76837e-007,1],"t":[0,-1,0,0,1],"m":0},"e":[8,4,9,0],"n":[-1,-0,-0],"i":0},{"t":{"s":[1,0,0,9.53674e-007,1],"t":[0,-1,0,0,1],"m":0},"e":[9,5,10,1],"n":[0,0,-1],"i":0},{"t":{"s":[-0,0,1,-4.76837e-007,1],"t":[0,-1,0,0,1],"m":0},"e":[10,6,11,2],"n":[1,0,-0],"i":0},{"t":{"s":[-1,0,0,-9.53674e-007,1],"t":[0,-1,0,0,1],"m":0},"e":[11,7,8,3],"n":[0,0,1],"i":0},{"t":{"s":[1,0,-0,9.53674e-007,1],"t":[0,0,1,-4.76837e-007,1],"m":0},"e":[0,1,2,3],"n":[0,-1,0],"i":0},{"t":{"s":[-1,0,0,-9.53674e-007,1],"t":[0,0,1,-4.76837e-007,1],"m":0},"e":[7,6,5,4],"n":[-0,1,-0],"i":0}],"m":["dev_floor"]}]
+		,"guid": "{6A81568F-6D71-402D-AED0-B5F6B29E7C92}"
+		,"color": "0.000000 0.101126 0.607227"
+		,"name": ""
+	}
+	,{
+		"brush": [{"v":[[-1.00002,6.55651e-006,-1.6],[-0.200004,6.07967e-006,-1.6],[-0.200001,-1.19209e-007,1.60005],[-1.00001,3.57628e-007,1.60005],[-1.00001,2.20004,-1.6],[-0.200004,2.20004,-1.6],[-0.2,2.20003,1.60005],[-1.00001,2.20003,1.60005]],"e":[[0,1,0],[1,2,0],[2,3,0],[3,0,0],[4,5,0],[5,6,0],[6,7,0],[4,7,0],[0,4,0],[1,5,0],[2,6,0],[3,7,0]],"f":[{"t":{"s":[1.00001,-2.38715e-007,-1.55717e-006,18.5,1],"t":[-2.38717e-007,-1.00002,-1.92885e-006,-0.399965,1],"m":1},"e":[8,4,9,0],"n":[-1.55717e-006,1.92885e-006,-1.00001],"i":0},{"t":{"s":[1.55717e-006,-1.92885e-006,1.00001,1.59998,1],"t":[-2.38717e-007,-1.00002,-1.92885e-006,-0.399965,1],"m":1},"e":[9,5,10,1],"n":[1.00001,-2.38715e-007,-1.55717e-006],"i":0},{"t":{"s":[-1.00001,2.38715e-007,1.55717e-006,-18.5,1],"t":[-2.38717e-007,-1.00002,-1.92885e-006,-0.399965,1],"m":1},"e":[10,6,11,2],"n":[1.55717e-006,-1.92885e-006,1.00001],"i":0},{"t":{"s":[-1.55717e-006,1.92885e-006,-1.00001,-1.59998,1],"t":[-2.38717e-007,-1.00002,-1.92885e-006,-0.399965,1],"m":1},"e":[11,7,8,3],"n":[-1.00001,2.38715e-007,1.55717e-006],"i":0},{"t":{"s":[1.55717e-006,-1.92885e-006,1.00001,1.59998,1],"t":[-1.00001,2.38715e-007,1.55717e-006,-18.5,1],"m":1},"e":[0,1,2,3],"n":[-2.38717e-007,-1.00002,-1.92885e-006],"i":0},{"t":{"s":[-1.55717e-006,1.92885e-006,-1.00001,-1.59998,1],"t":[-1.00001,2.38715e-007,1.55717e-006,-18.5,1],"m":1},"e":[7,6,5,4],"n":[2.38717e-007,1.00002,1.92885e-006],"i":0}],"m":["asphalt_01_A","dev_wall"]}]
+		,"guid": "{7B6F5ECE-1D4B-41FF-9553-4EC6989AFA4F}"
+		,"color": "0.000000 0.101126 0.607227"
+		,"name": ""
+	}
+	,{
+		"brush": [{"v":[[0.199986,6.55651e-006,-1.6],[0.999996,6.07967e-006,-1.6],[1,-1.19209e-007,1.60005],[0.19999,3.57628e-007,1.60005],[0.199986,2.20004,-1.59999],[0.999996,2.20004,-1.59999],[1,2.20003,1.60005],[0.19999,2.20003,1.60005]],"e":[[0,1,0],[1,2,0],[2,3,0],[3,0,0],[4,5,0],[5,6,0],[6,7,0],[4,7,0],[0,4,0],[1,5,0],[2,6,0],[3,7,0]],"f":[{"t":{"s":[1.00001,-2.38715e-007,-1.55717e-006,17.7,1],"t":[-2.38717e-007,-1.00002,-1.92885e-006,-0.399964,1],"m":1},"e":[8,4,9,0],"n":[-1.55717e-006,1.92885e-006,-1.00001],"i":0},{"t":{"s":[1.55717e-006,-1.92885e-006,1.00001,1.59997,1],"t":[-2.38717e-007,-1.00002,-1.92885e-006,-0.399964,1],"m":1},"e":[9,5,10,1],"n":[1.00001,-2.38715e-007,-1.55717e-006],"i":0},{"t":{"s":[-1.00001,2.38715e-007,1.55717e-006,-17.7,1],"t":[-2.38717e-007,-1.00002,-1.92885e-006,-0.399964,1],"m":1},"e":[10,6,11,2],"n":[1.55717e-006,-1.92885e-006,1.00001],"i":0},{"t":{"s":[-1.55717e-006,1.92885e-006,-1.00001,-1.59997,1],"t":[-2.38717e-007,-1.00002,-1.92885e-006,-0.399964,1],"m":1},"e":[11,7,8,3],"n":[-1.00001,2.38715e-007,1.55717e-006],"i":0},{"t":{"s":[1.55717e-006,-1.92885e-006,1.00001,1.59997,1],"t":[-1.00001,2.38715e-007,1.55717e-006,-17.7,1],"m":1},"e":[0,1,2,3],"n":[-2.38717e-007,-1.00002,-1.92885e-006],"i":0},{"t":{"s":[-1.55717e-006,1.92885e-006,-1.00001,-1.59997,1],"t":[-1.00001,2.38715e-007,1.55717e-006,-17.7,1],"m":1},"e":[7,6,5,4],"n":[2.38717e-007,1.00002,1.92885e-006],"i":0}],"m":["asphalt_01_A","dev_wall"]}]
+		,"guid": "{FC09E082-D68A-40AE-8BDA-22244DC3B346}"
+		,"color": "0.000000 0.273974 0.827866"
+		,"name": ""
+	}
+	,{
+		"brush": [{"v":[[-5.4,-0.2,5.4],[-5.4,-0.2,5],[5.4,-0.2,5],[5.4,-0.2,5.4],[-5.4,2,5.4],[-5.4,2,5],[5.4,2,5],[5.4,2,5.4]],"e":[[0,1,0],[1,2,0],[2,3,0],[3,0,0],[4,5,0],[5,6,0],[6,7,0],[4,7,0],[0,4,0],[1,5,0],[2,6,0],[3,7,0]],"f":[{"t":{"s":[-0,-0,-1,0,1],"t":[0,-1,0,0,1],"m":0},"e":[8,4,9,0],"n":[-1,-0,-0],"i":0},{"t":{"s":[1,0,0,0,1],"t":[0,-1,0,0,1],"m":0},"e":[9,5,10,1],"n":[0,0,-1],"i":0},{"t":{"s":[-0,0,1,0,1],"t":[0,-1,0,0,1],"m":0},"e":[10,6,11,2],"n":[1,0,-0],"i":0},{"t":{"s":[-1,0,0,0,1],"t":[0,-1,0,0,1],"m":0},"e":[11,7,8,3],"n":[0,0,1],"i":0},{"t":{"s":[1,0,-0,0,1],"t":[0,0,1,0,1],"m":0},"e":[0,1,2,3],"n":[0,-1,0],"i":0},{"t":{"s":[-1,0,0,0,1],"t":[0,0,1,0,1],"m":0},"e":[7,6,5,4],"n":[-0,1,-0],"i":0}],"m":["dev_wall"]}]
+		,"guid": "{9954F53A-4FDD-4A58-BA7C-4BE5A1C0985A}"
+		,"color": "0.000000 0.256697 0.873049"
+		,"name": ""
+	}
+	,{
+		"brush": [{"v":[[-5.4,-0.2,5],[-5.4,-0.2,-5],[-5,-0.2,-5],[-5,-0.2,5],[-5.4,2,5],[-5.4,2,-5],[-5,2,-5],[-5,2,5]],"e":[[0,1,0],[1,2,0],[2,3,0],[3,0,0],[4,5,0],[5,6,0],[6,7,0],[4,7,0],[0,4,0],[1,5,0],[2,6,0],[3,7,0]],"f":[{"t":{"s":[0,0,-1,-5.20001,1],"t":[0,-1,0,5.96046e-008,1],"m":0},"e":[8,4,9,0],"n":[-1,0,0],"i":0},{"t":{"s":[1,0,0,5.2,1],"t":[0,-1,0,5.96046e-008,1],"m":0},"e":[9,5,10,1],"n":[0,0,-1],"i":0},{"t":{"s":[0,0,1,5.20001,1],"t":[0,-1,0,5.96046e-008,1],"m":0},"e":[10,6,11,2],"n":[1,0,0],"i":0},{"t":{"s":[-1,0,0,-5.2,1],"t":[0,-1,0,5.96046e-008,1],"m":0},"e":[11,7,8,3],"n":[0,0,1],"i":0},{"t":{"s":[1,0,0,5.2,1],"t":[0,0,1,5.20001,1],"m":0},"e":[0,1,2,3],"n":[0,-1,0],"i":0},{"t":{"s":[-1,0,0,-5.2,1],"t":[0,0,1,5.20001,1],"m":0},"e":[7,6,5,4],"n":[0,1,0],"i":0}],"m":["dev_wall"]}]
+		,"guid": "{0EB30997-28DE-4FD5-AE94-5117B1A7B4C8}"
+		,"color": "0.000000 0.256697 0.873049"
+		,"name": ""
+	}
+	,{
+		"brush": [{"v":[[-5.4,-0.2,-5],[-5.4,-0.2,-5.4],[5.4,-0.2,-5.4],[5.4,-0.2,-5],[-5.4,2,-5],[-5.4,2,-5.4],[5.4,2,-5.4],[5.4,2,-5]],"e":[[0,1,0],[1,2,0],[2,3,0],[3,0,0],[4,5,0],[5,6,0],[6,7,0],[4,7,0],[0,4,0],[1,5,0],[2,6,0],[3,7,0]],"f":[{"t":{"s":[0,0,-1,-10.4,1],"t":[0,-1,0,0,1],"m":0},"e":[8,4,9,0],"n":[-1,0,0],"i":0},{"t":{"s":[1,0,0,0,1],"t":[0,-1,0,0,1],"m":0},"e":[9,5,10,1],"n":[0,0,-1],"i":0},{"t":{"s":[0,0,1,10.4,1],"t":[0,-1,0,0,1],"m":0},"e":[10,6,11,2],"n":[1,0,0],"i":0},{"t":{"s":[-1,0,0,0,1],"t":[0,-1,0,0,1],"m":0},"e":[11,7,8,3],"n":[0,0,1],"i":0},{"t":{"s":[1,0,0,0,1],"t":[0,0,1,10.4,1],"m":0},"e":[0,1,2,3],"n":[0,-1,0],"i":0},{"t":{"s":[-1,0,0,0,1],"t":[0,0,1,10.4,1],"m":0},"e":[7,6,5,4],"n":[0,1,0],"i":0}],"m":["dev_wall"]}]
+		,"guid": "{69DE4191-351F-4FF1-8781-E915708CE2E3}"
+		,"color": "0.000000 0.256697 0.873049"
+		,"name": ""
+	}
+	,{
+		"brush": [{"v":[[5,-0.2,5],[5,-0.2,-5],[5.4,-0.2,-5],[5.4,-0.2,5],[5,2,5],[5,2,-5],[5.4,2,-5],[5.4,2,5]],"e":[[0,1,0],[1,2,0],[2,3,0],[3,0,0],[4,5,0],[5,6,0],[6,7,0],[4,7,0],[0,4,0],[1,5,0],[2,6,0],[3,7,0]],"f":[{"t":{"s":[0,0,-1,-5.20001,1],"t":[0,-1,0,1.19209e-007,1],"m":0},"e":[8,4,9,0],"n":[-1,0,0],"i":0},{"t":{"s":[1,0,0,-5.2,1],"t":[0,-1,0,1.19209e-007,1],"m":0},"e":[9,5,10,1],"n":[0,0,-1],"i":0},{"t":{"s":[0,0,1,5.20001,1],"t":[0,-1,0,1.19209e-007,1],"m":0},"e":[10,6,11,2],"n":[1,0,0],"i":0},{"t":{"s":[-1,0,0,5.2,1],"t":[0,-1,0,1.19209e-007,1],"m":0},"e":[11,7,8,3],"n":[0,0,1],"i":0},{"t":{"s":[1,0,0,-5.2,1],"t":[0,0,1,5.20001,1],"m":0},"e":[0,1,2,3],"n":[0,-1,0],"i":0},{"t":{"s":[-1,0,0,5.2,1],"t":[0,0,1,5.20001,1],"m":0},"e":[7,6,5,4],"n":[0,1,0],"i":0}],"m":["dev_wall"]}]
+		,"guid": "{28576378-47EB-43D6-A078-BD3C9CA2E5C2}"
+		,"color": "0.000000 0.256697 0.873049"
+		,"name": ""
+	}
+
+]
+,"models":[
+
+]}
diff --git a/build/demos/levels/demo_narrow_passage/xcsg/0.dse b/build/demos/levels/demo_narrow_passage/xcsg/0.dse
new file mode 100644
index 0000000000000000000000000000000000000000..dea4c998fac322c334132b823d35f6eae528f01e
Binary files /dev/null and b/build/demos/levels/demo_narrow_passage/xcsg/0.dse differ
diff --git a/build/demos/levels/demo_narrow_passage/xcsg/1.dse b/build/demos/levels/demo_narrow_passage/xcsg/1.dse
new file mode 100644
index 0000000000000000000000000000000000000000..d08f2d778bfdc15f7c66b61f7edb5edc575a0abc
Binary files /dev/null and b/build/demos/levels/demo_narrow_passage/xcsg/1.dse differ
diff --git a/build/gamesource/config/entities/defaults.ent b/build/gamesource/config/entities/defaults.ent
index 25415f45b0b61333be30f9cc608e47bd3e0aafc4..7afee2706ba3b69ee7ad261ea2c2882be74164b4 100644
--- a/build/gamesource/config/entities/defaults.ent
+++ b/build/gamesource/config/entities/defaults.ent
@@ -39,3 +39,7 @@ inv_stackable = 0
 
 [base_weapon]
 inv_equip_type = 1
+
+[func_narrow_passage]
+speed = 0.5
+up_point = 2 0 0
diff --git a/proj/sxgame/vs2013/sxgame.vcxproj b/proj/sxgame/vs2013/sxgame.vcxproj
index 352079e145bc85c81339c2e840445cefe550ba5f..ed2254be7a058b4d6c1e7c30706b82c90624826a 100644
--- a/proj/sxgame/vs2013/sxgame.vcxproj
+++ b/proj/sxgame/vs2013/sxgame.vcxproj
@@ -202,6 +202,7 @@
     <ClCompile Include="..\..\..\source\game\EntityManager.cpp" />
     <ClCompile Include="..\..\..\source\game\EnvSkybox.cpp" />
     <ClCompile Include="..\..\..\source\game\FuncLadder.cpp" />
+    <ClCompile Include="..\..\..\source\game\FuncNarrowPassage.cpp" />
     <ClCompile Include="..\..\..\source\game\FuncRotating.cpp" />
     <ClCompile Include="..\..\..\source\game\FuncTrain.cpp" />
     <ClCompile Include="..\..\..\source\game\GameData.cpp" />
@@ -220,6 +221,7 @@
     <ClCompile Include="..\..\..\source\game\LogicEntityExists.cpp" />
     <ClCompile Include="..\..\..\source\game\LogicRelay.cpp" />
     <ClCompile Include="..\..\..\source\game\LogicStringbuilder.cpp" />
+    <ClCompile Include="..\..\..\source\game\NarrowPassageMovementController.cpp" />
     <ClCompile Include="..\..\..\source\game\NPCBase.cpp" />
     <ClCompile Include="..\..\..\source\game\NPCZombie.cpp" />
     <ClCompile Include="..\..\..\source\game\PathCorner.cpp" />
@@ -286,6 +288,7 @@
     <ClInclude Include="..\..\..\source\game\EntityPointer.h" />
     <ClInclude Include="..\..\..\source\game\EnvSkybox.h" />
     <ClInclude Include="..\..\..\source\game\FuncLadder.h" />
+    <ClInclude Include="..\..\..\source\game\FuncNarrowPassage.h" />
     <ClInclude Include="..\..\..\source\game\FuncRotating.h" />
     <ClInclude Include="..\..\..\source\game\GameStateManager.h" />
     <ClInclude Include="..\..\..\source\game\CharacterInventory.h" />
@@ -303,6 +306,7 @@
     <ClInclude Include="..\..\..\source\game\LogicEntityExists.h" />
     <ClInclude Include="..\..\..\source\game\LogicRelay.h" />
     <ClInclude Include="..\..\..\source\game\LogicStringbuilder.h" />
+    <ClInclude Include="..\..\..\source\game\NarrowPassageMovementController.h" />
     <ClInclude Include="..\..\..\source\game\physics_util.h" />
     <ClInclude Include="..\..\..\source\game\PointChangelevel.h" />
     <ClInclude Include="..\..\..\source\game\PropBreakable.h" />
diff --git a/proj/sxgame/vs2013/sxgame.vcxproj.filters b/proj/sxgame/vs2013/sxgame.vcxproj.filters
index 1c43d9d0f046b7cbb831da942932a1fa3a5d66eb..a55e5221581f52b1d4122d9d45a574bf1fd7af2e 100644
--- a/proj/sxgame/vs2013/sxgame.vcxproj.filters
+++ b/proj/sxgame/vs2013/sxgame.vcxproj.filters
@@ -378,6 +378,12 @@
     <ClCompile Include="..\..\..\source\game\FuncLadder.cpp">
       <Filter>Source Files\ents\func\mover</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\source\game\FuncNarrowPassage.cpp">
+      <Filter>Source Files\ents\func\mover</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\source\game\NarrowPassageMovementController.cpp">
+      <Filter>Source Files\character_movement</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\..\source\game\sxgame.h">
@@ -656,6 +662,12 @@
     <ClInclude Include="..\..\..\source\game\FuncLadder.h">
       <Filter>Header Files\ents\func\mover</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\source\game\FuncNarrowPassage.h">
+      <Filter>Header Files\ents\func\mover</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\source\game\NarrowPassageMovementController.h">
+      <Filter>Header Files\character_movement</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\..\source\game\sxgame.rc">
diff --git a/source/game/BaseMover.cpp b/source/game/BaseMover.cpp
index 5b6fdf15284322439c8ef6e4ab2624bf2989534c..d1eed1100fb19396ccf75ec46eaaea9469b83150 100644
--- a/source/game/BaseMover.cpp
+++ b/source/game/BaseMover.cpp
@@ -5,8 +5,12 @@ TODO("Trigger OnPlayerGetOn/OnPlayerGetOff events");
 TODO("Handle MOVER_NO_AUTOMOUNT flag");
 
 BEGIN_PROPTABLE(CBaseMover)
+	//! Конечная точка
 	DEFINE_FIELD_VECTORFN(m_vUpPoint, PDFF_USE_GIZMO, "up_point", "End point", setUpPoint, EDITOR_POINTCOORD)
 
+	//! Скорость движения
+	DEFINE_FIELD_FLOAT(m_fSpeed, PDFF_NONE, "speed", "Speed", EDITOR_TEXTFIELD)
+
 	//! Игрок присоединился
 	DEFINE_OUTPUT(m_onPlayerGetOn, "OnPlayerGetOn", "On player get on")
 	//! Игрок отсоединился
@@ -19,8 +23,11 @@ BEGIN_PROPTABLE(CBaseMover)
 	//! Переключает состояние
 	DEFINE_INPUT(toggle, "toggle", "Toggle", PDF_NONE)
 
+	
+
 	//! Изначально выключена
 	DEFINE_FLAG(MOVER_INITIALLY_DISABLED, "Start disabled")
+	//! Отключить автомонтирование (требуется нажать кнопку взаимодействия)
 	DEFINE_FLAG(MOVER_NO_AUTOMOUNT, "Disable automount")
 END_PROPTABLE()
 
@@ -365,6 +372,11 @@ float3 CBaseMover::getUpPos()
 	return(getOrient() * m_vUpPoint + getPos());
 }
 
+float CBaseMover::getSpeed()
+{
+	return(m_fSpeed);
+}
+
 void CBaseMover::handleCharacterMount(CBaseEntity *pEntity)
 {
 	if(fstrcmp(pEntity->getClassName(), "player"))
@@ -461,7 +473,7 @@ void CBaseMover::newMovementController(IMovementController **ppOut)
 
 //##########################################################################
 
-void CPhysicsLadderTickEventListener::onEvent(const XEventPhysicsStep *pData)
+void CPhysicsMoverTickEventListener::onEvent(const XEventPhysicsStep *pData)
 {
 	m_pMover->onPhysicsStep();
 }
diff --git a/source/game/BaseMover.h b/source/game/BaseMover.h
index 6b9776be3f463f4f2f7f94da7f3c157c571497f4..6323dd550a296dbe2ee587d8e6763d71bf660e23 100644
--- a/source/game/BaseMover.h
+++ b/source/game/BaseMover.h
@@ -7,10 +7,10 @@
 #define MOVER_NO_AUTOMOUNT ENT_FLAG_1
 
 class CBaseMover;
-class CPhysicsLadderTickEventListener final: public IEventListener<XEventPhysicsStep>
+class CPhysicsMoverTickEventListener final: public IEventListener<XEventPhysicsStep>
 {
 public:
-	CPhysicsLadderTickEventListener(CBaseMover *pMover):
+	CPhysicsMoverTickEventListener(CBaseMover *pMover):
 		m_pMover(pMover)
 	{
 	}
@@ -25,7 +25,7 @@ class CBaseMover: public CPointEntity
 {
 	DECLARE_CLASS(CBaseMover, CPointEntity);
 	DECLARE_PROPTABLE();
-	friend class CPhysicsLadderTickEventListener;
+	friend class CPhysicsMoverTickEventListener;
 public:
 	DECLARE_CONSTRUCTOR();
 	~CBaseMover();
@@ -42,6 +42,8 @@ public:
 
 	float3 getUpPos();
 
+	float getSpeed();
+
 private:
 	void handleCharacterMount(CBaseEntity *pEntity);
 	void createPhysBody();
@@ -70,9 +72,11 @@ private:
 	IXGhostObject *m_pGhostObject = NULL;
 	IXConvexHullShape *m_pCollideShape = NULL;
 	static IEventChannel<XEventPhysicsStep> *m_pTickEventChannel;
-	CPhysicsLadderTickEventListener m_physicsTicker;
+	CPhysicsMoverTickEventListener m_physicsTicker;
 
 	Array<CBaseEntity*> m_aTouchedEntities;
+
+	float m_fSpeed = 3.0f;
 };
 
 #endif
diff --git a/source/game/FuncLadder.h b/source/game/FuncLadder.h
index 9fd556a77cdc7c2cb0083241b99100d0f7f7c287..3f6e6b1e75d837830b200f553a5931c42f1c5e6c 100644
--- a/source/game/FuncLadder.h
+++ b/source/game/FuncLadder.h
@@ -17,7 +17,6 @@ class CFuncLadder: public CBaseMover
 {
 	DECLARE_CLASS(CFuncLadder, CBaseMover);
 	DECLARE_PROPTABLE();
-	friend class CPhysicsLadderTickEventListener;
 public:
 	DECLARE_TRIVIAL_CONSTRUCTOR();
 	
diff --git a/source/game/FuncNarrowPassage.cpp b/source/game/FuncNarrowPassage.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..37f562bed935a087110f53a779177ab9c654aa32
--- /dev/null
+++ b/source/game/FuncNarrowPassage.cpp
@@ -0,0 +1,15 @@
+#include "FuncNarrowPassage.h"
+#include "BaseCharacter.h"
+#include "NarrowPassageMovementController.h"
+
+
+BEGIN_PROPTABLE(CFuncNarrowPassage)
+	// empty
+END_PROPTABLE()
+
+REGISTER_ENTITY(CFuncNarrowPassage, func_narrow_passage);
+
+void CFuncNarrowPassage::newMovementController(IMovementController **ppOut)
+{
+	*ppOut = new CNarrowPassageMovementController(this);
+}
diff --git a/source/game/FuncNarrowPassage.h b/source/game/FuncNarrowPassage.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5501f54a20b99ccc9b5d3f3df5dd225f3ccea6a
--- /dev/null
+++ b/source/game/FuncNarrowPassage.h
@@ -0,0 +1,17 @@
+#ifndef __FUNC_NARROW_PASSAGE_H
+#define __FUNC_NARROW_PASSAGE_H
+
+#include "BaseMover.h"
+
+class CFuncNarrowPassage: public CBaseMover
+{
+	DECLARE_CLASS(CFuncNarrowPassage, CBaseMover);
+	DECLARE_PROPTABLE();
+public:
+	DECLARE_TRIVIAL_CONSTRUCTOR();
+	
+private:
+	void newMovementController(IMovementController **ppOut) override;
+};
+
+#endif
diff --git a/source/game/LadderMovementController.cpp b/source/game/LadderMovementController.cpp
index ef824bf04907b941823c1931fcf2562008b94d13..3e6b7a10ef074faeba1e71f9c23899785ab02455 100644
--- a/source/game/LadderMovementController.cpp
+++ b/source/game/LadderMovementController.cpp
@@ -9,6 +9,8 @@ CLadderMovementController::CLadderMovementController(CFuncLadder *pLadder)
 	m_vLadderPoint[1] = pLadder->getUpPos();
 
 	m_vLadderDir = SMVector3Normalize(m_vLadderPoint[1] - m_vLadderPoint[0]);
+
+	m_fSpeed = pLadder->getSpeed();
 }
 CLadderMovementController::~CLadderMovementController()
 {
@@ -18,7 +20,7 @@ CLadderMovementController::~CLadderMovementController()
 	}
 }
 
-float3 SMProjectPointOnLine(const float3 &vPos, const float3 &vStart, const float3 &vEnd)
+static float3 SMProjectPointOnLine(const float3 &vPos, const float3 &vStart, const float3 &vEnd)
 {
 	float3 vN = SMVector3Normalize(vEnd - vStart);
 	float fDot0 = SMVector3Dot(vN, vPos - vStart);
@@ -88,7 +90,7 @@ void CLadderMovementController::update(float fDt)
 	{
 		float fDot = SMVector3Dot(m_vLadderDir, m_vMoveDir);
 
-		float3 vSpeed = m_vLadderDir * 3.0f;
+		float3 vSpeed = m_vLadderDir * m_fSpeed;
 		float3 vNewPos;
 
 		if(fDot > /*-SM_PIDIV4*/ -SMToRadian(10.0f))
diff --git a/source/game/LadderMovementController.h b/source/game/LadderMovementController.h
index 0b34f2256e1e0e4051f4f39cf1d2a40e87046739..9cdf95cc03810af5b58d17a077d704c26e7fb98c 100644
--- a/source/game/LadderMovementController.h
+++ b/source/game/LadderMovementController.h
@@ -26,6 +26,8 @@ private:
 
 	float3_t m_vMoveDir;
 
+	float m_fSpeed = 3.0f;
+
 	struct
 	{
 		bool is = false;
diff --git a/source/game/NarrowPassageMovementController.cpp b/source/game/NarrowPassageMovementController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d4b9cce1e5d9680f50e1871d16919ee66af13e1a
--- /dev/null
+++ b/source/game/NarrowPassageMovementController.cpp
@@ -0,0 +1,132 @@
+#include "NarrowPassageMovementController.h"
+#include "BaseCharacter.h"
+#include "FuncNarrowPassage.h"
+#include "Player.h"
+
+CNarrowPassageMovementController::CNarrowPassageMovementController(CFuncNarrowPassage *pPassage)
+{
+	m_vLadderPoint[0] = pPassage->getPos();
+	m_vLadderPoint[1] = pPassage->getUpPos();
+
+	m_vLadderDir = SMVector3Normalize(m_vLadderPoint[1] - m_vLadderPoint[0]);
+
+	m_fSpeed = pPassage->getSpeed();
+}
+CNarrowPassageMovementController::~CNarrowPassageMovementController()
+{
+	if(m_pCharacter)
+	{
+		m_pCharacter->getCharacterController()->setGravity(float3(0.0f, -10.0f, 0.0f));
+	}
+}
+
+static float3 SMProjectPointOnLine(const float3 &vPos, const float3 &vStart, const float3 &vEnd)
+{
+	float3 vN = SMVector3Normalize(vEnd - vStart);
+	float fDot0 = SMVector3Dot(vN, vPos - vStart);
+	if(fDot0 <= 0.0f)
+	{
+		return(vStart);
+	}
+
+	float fDot1 = SMVector3Dot(vN, vPos - vEnd);
+	if(fDot1 >= 0.0f)
+	{
+		return(vEnd);
+	}
+
+	return(vStart + vN * fDot0);
+}
+
+void CNarrowPassageMovementController::setCharacter(CBaseCharacter *pCharacter)
+{
+	m_pCharacter = pCharacter;
+
+	IXCharacterController *pCharacterController = pCharacter->getCharacterController();
+
+	pCharacterController->setGravity(float3(0.0f, 0.0f, 0.0f));
+	pCharacterController->setVelocityForTimeInterval(float3(0.0f, 0.0f, 0.0f), 0.0f);
+
+	m_mounting.is = true;
+	m_mounting.fFrac = 0.0f;
+	m_mounting.vStartPos = m_pCharacter->getPos();
+	m_mounting.vTargetPos = SMProjectPointOnLine(m_pCharacter->getPos(), m_vLadderPoint[0], m_vLadderPoint[1]);
+}
+
+void CNarrowPassageMovementController::handleMove(const float3 &vDir)
+{
+	m_vMoveDir = vDir;
+}
+
+void CNarrowPassageMovementController::handleJump()
+{
+	m_bWillDismount = true;
+}
+
+bool CNarrowPassageMovementController::handleUse()
+{
+	m_bWillDismount = true;
+	return(true);
+}
+
+void CNarrowPassageMovementController::update(float fDt)
+{
+	if(m_mounting.is)
+	{
+		m_mounting.fFrac += 7.0f * fDt;
+		if(m_mounting.fFrac > 1.0f)
+		{
+			m_mounting.fFrac = 1.0f;
+			m_mounting.is = false;
+		}
+		m_pCharacter->setPos(vlerp(m_mounting.vStartPos, m_mounting.vTargetPos, m_mounting.fFrac));
+	}
+	else if(m_bWillDismount)
+	{
+		((CPlayer*)m_pCharacter)->m_vCurrentSpeed = m_vMoveDir;
+		m_pCharacter->setMovementController(NULL);
+	}
+	else if(!SMIsZero(SMVector3Length2(m_vMoveDir)))
+	{
+		float fDot = SMVector3Dot(m_vLadderDir, m_vMoveDir);
+
+		float3 vSpeed = m_vLadderDir * m_fSpeed;
+		float3 vNewPos;
+
+		if(fDot > /*-SM_PIDIV4*/ -SMToRadian(10.0f))
+		{
+			if(SMIsZero(SMVector3Length2(m_vLadderPoint[1] - m_pCharacter->getPos())))
+			{
+				m_bWillDismount = true;
+			}
+			else
+			{
+				vNewPos = m_pCharacter->getPos() + vSpeed * fDt;
+				if(SMVector3Length2(vNewPos - m_vLadderPoint[0]) > SMVector3Length2(m_vLadderPoint[1] - m_vLadderPoint[0]))
+				{
+					vNewPos = m_vLadderPoint[1];
+				}
+			}
+		}
+		else
+		{
+			if(SMIsZero(SMVector3Length2(m_vLadderPoint[0] - m_pCharacter->getPos())))
+			{
+				m_bWillDismount = true;
+			}
+			else
+			{
+				vNewPos = m_pCharacter->getPos() - vSpeed * fDt;
+				if(SMVector3Length2(vNewPos - m_vLadderPoint[1]) > SMVector3Length2(m_vLadderPoint[1] - m_vLadderPoint[0]))
+				{
+					vNewPos = m_vLadderPoint[0];
+				}
+			}
+		}
+
+		if(!m_bWillDismount)
+		{
+			m_pCharacter->setPos(vNewPos);
+		}
+	}
+}
diff --git a/source/game/NarrowPassageMovementController.h b/source/game/NarrowPassageMovementController.h
new file mode 100644
index 0000000000000000000000000000000000000000..86ea5df9803cf059e6a70fac518ab6eee8bfbbf1
--- /dev/null
+++ b/source/game/NarrowPassageMovementController.h
@@ -0,0 +1,43 @@
+#ifndef __NARROWPASSAGEMOVEMENTCONTROLLER_H
+#define __NARROWPASSAGEMOVEMENTCONTROLLER_H
+
+#include "IMovementController.h"
+
+class CFuncNarrowPassage;
+class CNarrowPassageMovementController: public IXUnknownImplementation<IMovementController>
+{
+public:
+	CNarrowPassageMovementController(CFuncNarrowPassage *pPassage);
+	~CNarrowPassageMovementController();
+
+	void setCharacter(CBaseCharacter *pCharacter) override;
+
+	void handleMove(const float3 &vDir) override;
+	void handleJump() override;
+	bool handleUse() override;
+
+	void update(float fDt) override;
+
+private:
+	CBaseCharacter *m_pCharacter;
+
+	float3_t m_vLadderPoint[2];
+	float3_t m_vLadderDir;
+
+	float3_t m_vMoveDir;
+
+	float m_fSpeed = 3.0f;
+
+	struct
+	{
+		bool is = false;
+		float fFrac = 0.0f;
+		float3_t vStartPos;
+		float3_t vTargetPos;
+	}
+	m_mounting;
+
+	bool m_bWillDismount = false;
+};
+
+#endif
diff --git a/source/game/Player.h b/source/game/Player.h
index 34ab9367236655984586e182949ce97c0ea35329..e8416d717ab8d854ebc55ff1b85f74d24ebb1714 100644
--- a/source/game/Player.h
+++ b/source/game/Player.h
@@ -25,7 +25,9 @@ See the license in LICENSE
 //! Класс игрока  \ingroup cbaseanimating
 class CPlayer: public CBaseCharacter
 {
+	TODO("Fix that");
 	friend class CLadderMovementController;
+	friend class CNarrowPassageMovementController;
 	DECLARE_CLASS(CPlayer, CBaseCharacter);
 	DECLARE_PROPTABLE();
 public: