diff --git a/build/demos/levels/demo_ladder/demo_ladder.ent b/build/demos/levels/demo_ladder/demo_ladder.ent
index 25b90276a296c05a9c8cc5c1e52b1563df6589cb..f762571a88a4dff1c2df1267cd70a185a928d94a 100644
--- a/build/demos/levels/demo_ladder/demo_ladder.ent
+++ b/build/demos/levels/demo_ladder/demo_ladder.ent
@@ -68,7 +68,7 @@ name =
 model = 
 is_static = 1
 glow_color_ref = 
-glow_color = 0.000000 0.000000 0.000000
+glow_color = 0.000000 0.273974 0.827866
 flags = 0
 classname = prop_static
 auto_physbox = 1
@@ -130,10 +130,10 @@ OnTurnOn =
 OnTurnOff = 
 
 [{F7FAD41B-4168-41D2-9E82-51DA455F22B6}]
-up_point = 12.860000 5.000000 10.004517
+up_point = 0.000000 4.000000 0.000001
 rotation = 0.000000 0.000000 0.000000 1.000000
 parent = 
-origin = 12.858377 1.000000 10.004519
+origin = 12.849999 1.000000 9.999999
 name = 
 flags = 0
 classname = func_ladder
diff --git a/proj/sxgame/vs2013/sxgame.vcxproj b/proj/sxgame/vs2013/sxgame.vcxproj
index 5f6b91c46b9adb2f15159afad05d05fe1692eb35..b70be725fa3ad8ec5a1892e3129ac3e8a9dfdfee 100644
--- a/proj/sxgame/vs2013/sxgame.vcxproj
+++ b/proj/sxgame/vs2013/sxgame.vcxproj
@@ -210,6 +210,7 @@
     <ClCompile Include="..\..\..\source\game\GUIInventoryController.cpp" />
     <ClCompile Include="..\..\..\source\game\HUDcontroller.cpp" />
     <ClCompile Include="..\..\..\source\game\InfoParticlePlayer.cpp" />
+    <ClCompile Include="..\..\..\source\game\LadderMovementController.cpp" />
     <ClCompile Include="..\..\..\source\game\LightDirectional.cpp" />
     <ClCompile Include="..\..\..\source\game\LightPoint.cpp" />
     <ClCompile Include="..\..\..\source\game\LightSun.cpp" />
@@ -291,7 +292,9 @@
     <ClInclude Include="..\..\..\source\game\GUIInventoryController.h" />
     <ClInclude Include="..\..\..\source\game\HUDcontroller.h" />
     <ClInclude Include="..\..\..\source\game\IGameState.h" />
+    <ClInclude Include="..\..\..\source\game\IMovementController.h" />
     <ClInclude Include="..\..\..\source\game\InfoParticlePlayer.h" />
+    <ClInclude Include="..\..\..\source\game\LadderMovementController.h" />
     <ClInclude Include="..\..\..\source\game\LightSun.h" />
     <ClInclude Include="..\..\..\source\game\LogicAuto.h" />
     <ClInclude Include="..\..\..\source\game\LogicConsole.h" />
diff --git a/proj/sxgame/vs2013/sxgame.vcxproj.filters b/proj/sxgame/vs2013/sxgame.vcxproj.filters
index e9732da19c4698d9cc891d33dfa33eb6877a8a2c..912083c6e42413dc9b11a3faf46109c7a44d1191 100644
--- a/proj/sxgame/vs2013/sxgame.vcxproj.filters
+++ b/proj/sxgame/vs2013/sxgame.vcxproj.filters
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
     <Filter Include="Source Files">
@@ -115,6 +115,12 @@
     <Filter Include="Header Files\ents\info">
       <UniqueIdentifier>{93ee5d69-dabd-4584-8e15-0acd713e614f}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Header Files\character_movement">
+      <UniqueIdentifier>{3852670f-3617-4451-bfc1-c0aba9c194fa}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\character_movement">
+      <UniqueIdentifier>{7f56c9eb-5a04-4d29-ab96-b7e3d8d368f1}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\source\game\sxgame_dll.cpp">
@@ -360,6 +366,9 @@
     <ClCompile Include="..\..\..\source\game\CraftSystem.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\source\game\LadderMovementController.cpp">
+      <Filter>Source Files\character_movement</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\..\source\game\sxgame.h">
@@ -629,6 +638,12 @@
     <ClInclude Include="..\..\..\source\game\CraftSystem.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\source\game\IMovementController.h">
+      <Filter>Header Files\character_movement</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\source\game\LadderMovementController.h">
+      <Filter>Header Files\character_movement</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\..\source\game\sxgame.rc">
diff --git a/source/game/BaseCharacter.cpp b/source/game/BaseCharacter.cpp
index a1a345a11da22a5493b34092060b0ceaa9d63beb..89dd1892c1856f74000bcce7c6f347d71c9ce70d 100644
--- a/source/game/BaseCharacter.cpp
+++ b/source/game/BaseCharacter.cpp
@@ -8,6 +8,7 @@ See the license in LICENSE
 #include "GameData.h"
 #include "BaseTool.h"
 #include "BaseWeapon.h"
+#include "FuncLadder.h"
 
 #include <aigrid/sxaigrid.h>
 
@@ -123,12 +124,21 @@ CBaseCharacter::~CBaseCharacter()
 
 	mem_release(m_pHandsModelResource);
 
+	mem_release(m_pMovementController);
+
 	if(m_idQuadCurr >= 0)
 	{
 		//SAIG_QuadSetState(m_idQuadCurr, AIQUAD_STATE_FREE);
 	}
 }
 
+void CBaseCharacter::setMovementController(IMovementController *pController)
+{
+	mem_release(m_pMovementController);
+	m_pMovementController = pController;
+	add_ref(m_pMovementController);
+	SAFE_CALL(m_pMovementController, setCharacter, this);
+}
 
 void CBaseCharacter::attack(BOOL state)
 {
@@ -450,8 +460,12 @@ void CBaseCharacter::onPhysicsStep()
 	{
 		return;
 	}
-	float3 vPos = m_pGhostObject->getPosition();
-	setPos(vPos - float3(0.0f, m_fCapsHeight * m_fCurrentHeight * 0.5f, 0.0f));
+
+	if(!m_pMovementController)
+	{
+		float3 vPos = m_pGhostObject->getPosition();
+		setPos(vPos - float3(0.0f, m_fCapsHeight * m_fCurrentHeight * 0.5f, 0.0f));
+	}
 
 	m_pHeadEnt->setOffsetPos(getHeadOffset());
 
@@ -537,12 +551,17 @@ void CBaseCharacter::use(bool start)
 
 	if(start)
 	{
+		if(m_pMovementController && m_pMovementController->handleUse())
+		{
+			return;
+		}
+
 		float3 start = getHead()->getPos();
 		float3 dir = getHead()->getOrient() * float3(0.0f, 0.0f, 1.0f);
 		float3 end = start + dir * 2.0f;
 
 		CClosestNotMeRayResultCallback cb(m_pGhostObject);
-		GetPhysWorld()->rayTest(start, end, &cb);
+		GetPhysWorld()->rayTest(start, end, &cb, CG_CHARACTER, CG_ALL ^ (CG_HITBOX | CG_STATIC | CG_TRIGGER | CG_WATER));
 
 		if(cb.hasHit() && cb.m_result.pCollisionObject->getUserPointer() && cb.m_result.pCollisionObject->getUserTypeId() == 1)
 		{
diff --git a/source/game/BaseCharacter.h b/source/game/BaseCharacter.h
index a630276b95b7d16c5d21949727c21edf07b51bfc..ad1cbcb3deb5d8c108b57232050f6dbd064683c3 100644
--- a/source/game/BaseCharacter.h
+++ b/source/game/BaseCharacter.h
@@ -20,6 +20,7 @@ See the license in LICENSE
 #include "LightDirectional.h"
 #include "CharacterInventory.h"
 #include "PointEntity.h"
+#include "IMovementController.h"
 
 class CBaseTool;
 
@@ -136,8 +137,15 @@ public:
 
 	void onPostLoad() override;
 
+	void setMovementController(IMovementController *pController);
+
 	void renderEditor(bool is3D, bool bRenderSelection, IXGizmoRenderer *pRenderer) override;
 
+	IXCharacterController* getCharacterController()
+	{
+		return(m_pCharacter);
+	}
+
 protected:
 	//! Фонарик
 	CLightDirectional* m_flashlight;
@@ -188,6 +196,8 @@ protected:
 
 	virtual float3 getHeadOffset();
 
+	IMovementController *m_pMovementController = NULL;
+
 private:
 	static IEventChannel<XEventPhysicsStep> *m_pTickEventChannel;
 	CCharacterPhysicsTickEventListener m_physicsTicker;
diff --git a/source/game/FuncLadder.cpp b/source/game/FuncLadder.cpp
index c969b8b665fd1f2cf2ccb2815a478abd4638219b..d738af4db3ef96e1cfa156f757778ed0520dc3ba 100644
--- a/source/game/FuncLadder.cpp
+++ b/source/game/FuncLadder.cpp
@@ -1,4 +1,6 @@
 #include "FuncLadder.h"
+#include "BaseCharacter.h"
+#include "LadderMovementController.h"
 
 
 BEGIN_PROPTABLE(CInfoLadderDismount)
@@ -30,39 +32,158 @@ END_PROPTABLE()
 
 REGISTER_ENTITY(CFuncLadder, func_ladder);
 
+IEventChannel<XEventPhysicsStep> *CFuncLadder::m_pTickEventChannel = NULL;
+
+CFuncLadder::CFuncLadder():
+	m_physicsTicker(this)
+{
+	if(!m_pTickEventChannel)
+	{
+		m_pTickEventChannel = Core_GetIXCore()->getEventChannel<XEventPhysicsStep>(EVENT_PHYSICS_STEP_GUID);
+	}
+}
+
+CFuncLadder::~CFuncLadder()
+{
+	disable();
+	mem_release(m_pCollideShape);
+	mem_release(m_pGhostObject);
+}
+
 void CFuncLadder::setUpPoint(const float3 &vUp)
 {
-	m_isUpSet = true;
 	m_vUpPoint = vUp;
+
+	initPhysics();
 }
 
-void CFuncLadder::setPos(const float3 &pos)
+void CFuncLadder::createPhysBody()
 {
-	BaseClass::setPos(pos);
-	if(!m_isUpSet)
+	if(!m_pGhostObject)
 	{
-		m_vUpPoint = (float3)(pos + float3(0.0f, 2.0f, 0.0f));
+		GetPhysics()->newGhostObject(&m_pGhostObject);
+		m_pGhostObject->setPosition(getPos());
+		m_pGhostObject->setUserPointer(this);
+		m_pGhostObject->setUserTypeId(1);
+		m_pGhostObject->setCollisionFlags(m_pGhostObject->getCollisionFlags() ^ XCF_NO_CONTACT_RESPONSE);
+		m_pGhostObject->setCollisionShape(m_pCollideShape);
+		if(m_isEnabled)
+		{
+			GetPhysWorld()->addCollisionObject(m_pGhostObject, CG_LADDER, CG_CHARACTER);
+		}
+	}
+	else
+	{
+		m_pGhostObject->setCollisionShape(m_pCollideShape);
 	}
 }
 
+void CFuncLadder::setPos(const float3 &pos)
+{
+	BaseClass::setPos(pos);
+	initPhysics();
+	SAFE_CALL(m_pGhostObject, setPosition, pos);
+}
+
 void CFuncLadder::updateFlags()
 {
 	BaseClass::updateFlags();
 
-	m_isEnabled = !(getFlags() & LADDER_INITIALLY_DISABLED);
+	if(getFlags() & LADDER_INITIALLY_DISABLED)
+	{
+		disable();
+	}
+	else
+	{
+		enable();
+	}
 }
 
 void CFuncLadder::turnOn(inputdata_t *pInputdata)
 {
-	m_isEnabled = true;
+	enable();
 }
+
 void CFuncLadder::turnOff(inputdata_t *pInputdata)
 {
-	m_isEnabled = false;
+	disable();
 }
+
 void CFuncLadder::toggle(inputdata_t *pInputdata)
 {
-	m_isEnabled = !m_isEnabled;
+	if(m_isEnabled)
+	{
+		turnOff(pInputdata);
+	}
+	else
+	{
+		turnOn(pInputdata);
+	}
+}
+
+void CFuncLadder::initPhysics()
+{
+	//TODO: сделать обработку ситуации когда m_vUpPoint ниже getPos
+	mem_release(m_pCollideShape);
+
+	float3 vDelta = getOrient() * m_vUpPoint;
+	SMAABB aabb = getBound();
+	float3 vMinDelta, vMaxDelta;
+
+	float3_t aPointsBot[] = { 
+		{aabb.vMin.x, aabb.vMax.y, aabb.vMin.z }, 
+		{aabb.vMax.x, aabb.vMax.y, aabb.vMin.z },
+		{aabb.vMin.x, aabb.vMax.y, aabb.vMax.z },
+		{aabb.vMax.x, aabb.vMax.y, aabb.vMax.z }
+	};
+
+	vMinDelta = aabb.vMin + vDelta;
+	vMaxDelta = aabb.vMax + vDelta;
+
+	float3_t aPointsTop[] = {
+		{ vMinDelta.x, vMinDelta.y, vMinDelta.z },
+		{ vMaxDelta.x, vMinDelta.y, vMinDelta.z },
+		{ vMinDelta.x, vMinDelta.y, vMaxDelta.z },
+		{ vMaxDelta.x, vMinDelta.y, vMaxDelta.z }
+	};
+
+	const UINT c_uSize = 14;
+	UINT uIndex = 0;
+	float3_t aShapePoints[c_uSize] = {};
+
+	if(vDelta.x > 0.0f || vDelta.z > 0.0f)
+	{
+		aShapePoints[uIndex++] = aPointsBot[0];
+		aShapePoints[uIndex++] = aPointsTop[3];
+	}
+	if(vDelta.x < 0.0f || vDelta.z < 0.0f)
+	{
+		aShapePoints[uIndex++] = aPointsBot[3];
+		aShapePoints[uIndex++] = aPointsTop[0];
+	}
+	if(vDelta.x < 0.0f || vDelta.z > 0.0f)
+	{
+		aShapePoints[uIndex++] = aPointsBot[1];
+		aShapePoints[uIndex++] = aPointsTop[2];
+	}
+	if(vDelta.x > 0.0f || vDelta.z < 0.0f)
+	{
+		aShapePoints[uIndex++] = aPointsBot[2];
+		aShapePoints[uIndex++] = aPointsTop[1];
+	}
+
+	aShapePoints[uIndex++] = aabb.vMin;
+	aShapePoints[uIndex++] = {aabb.vMin.x, aabb.vMin.y, aabb.vMax.z};
+	aShapePoints[uIndex++] = {aabb.vMax.x, aabb.vMin.y, aabb.vMax.z};
+	aShapePoints[uIndex++] = {aabb.vMax.x, aabb.vMin.y, aabb.vMin.z};
+
+	aShapePoints[uIndex++] = vMaxDelta;
+	aShapePoints[uIndex++] = {vMaxDelta.x, vMaxDelta.y, vMinDelta.z};
+	aShapePoints[uIndex++] = {vMinDelta.x, vMaxDelta.y, vMinDelta.z};
+	aShapePoints[uIndex++] = {vMinDelta.x, vMaxDelta.y, vMaxDelta.z};
+
+	GetPhysics()->newConvexHullShape(uIndex, aShapePoints, &m_pCollideShape, sizeof(float3_t), false);
+	createPhysBody();
 }
 
 void CFuncLadder::renderEditor(bool is3D, bool bRenderSelection, IXGizmoRenderer *pRenderer)
@@ -73,35 +194,267 @@ void CFuncLadder::renderEditor(bool is3D, bool bRenderSelection, IXGizmoRenderer
 		pRenderer->setColor(c_vLineColor);
 		pRenderer->setLineWidth(is3D ? 0.02f : 1.0f);
 
-		SMAABB aabb;
-		getMinMax(&aabb.vMin, &aabb.vMax);
+		SMAABB aabb = getBound();
 		pRenderer->drawAABB(aabb + getPos());
-		pRenderer->drawAABB(aabb + m_vUpPoint);
+		pRenderer->drawAABB(aabb + getUpPos());
 		pRenderer->jumpTo(getPos());
-		pRenderer->lineTo(m_vUpPoint);
+		pRenderer->lineTo(getUpPos());
 	}
 }
 
 void CFuncLadder::getMinMax(float3 *min, float3 *max)
 {
+	SMAABB aabb = getBound();
+	aabb = SMAABBConvex(aabb, aabb + getUpPos() - getPos());
+
 	if(min)
 	{
-		min->x = -0.25f;
-		min->y = 0.0f;
-		min->z = -0.25f;
+		*min = aabb.vMin;
 	}
 
 	if(max)
 	{
-		max->x = 0.25f;
-		max->y = 1.8f;
-		max->z = 0.25f;
+		*max = aabb.vMax;
+	}
+}
+
+SMAABB CFuncLadder::getBound()
+{
+	return(SMAABB(float3(-0.25f, 0.0f, -0.25f), float3(0.25f, 1.8f, 0.25f)));
+}
+
+bool SMAABBIntersectLine(const SMAABB &aabb, const float3 &vStart, const float3 &vEnd, float3 *pvOut, float3 *pvNormal)
+{
+	float3 vPoint;
+	SMPLANE plane;
+
+	plane = SMPLANE(float3(1.0f, 0.0f, 0.0f), -aabb.vMax.x);
+	if(plane.intersectLine(&vPoint, vStart, vEnd) && SMIsAABBInsideAABB(SMAABB(vPoint, vPoint), aabb))
+	{
+		*pvOut = vPoint;
+		if(pvNormal)
+		{
+			*pvNormal = float3(1.0f, 0.0f, 0.0f);
+		}
+		return(true);
+	}
+	
+	plane = SMPLANE(float3(-1.0f, 0.0f, 0.0f), aabb.vMin.x);
+	if(plane.intersectLine(&vPoint, vStart, vEnd) && SMIsAABBInsideAABB(SMAABB(vPoint, vPoint), aabb))
+	{
+		*pvOut = vPoint;
+		if(pvNormal)
+		{
+			*pvNormal = float3(-1.0f, 0.0f, 0.0f);
+		}
+		return(true);
 	}
+
+	plane = SMPLANE(float3(0.0f, 1.0f, 0.0f), -aabb.vMax.y);
+	if(plane.intersectLine(&vPoint, vStart, vEnd) && SMIsAABBInsideAABB(SMAABB(vPoint, vPoint), aabb))
+	{
+		*pvOut = vPoint;
+		if(pvNormal)
+		{
+			*pvNormal = float3(0.0f, 1.0f, 0.0f);
+		}
+		return(true);
+	}
+
+	plane = SMPLANE(float3(0.0f, -1.0f, 0.0f), aabb.vMin.y);
+	if(plane.intersectLine(&vPoint, vStart, vEnd) && SMIsAABBInsideAABB(SMAABB(vPoint, vPoint), aabb))
+	{
+		*pvOut = vPoint;
+		if(pvNormal)
+		{
+			*pvNormal = float3(0.0f, -1.0f, 0.0f);
+		}
+		return(true);
+	}
+
+	plane = SMPLANE(float3(0.0f, 0.0f, 1.0f), -aabb.vMax.z);
+	if(plane.intersectLine(&vPoint, vStart, vEnd) && SMIsAABBInsideAABB(SMAABB(vPoint, vPoint), aabb))
+	{
+		*pvOut = vPoint;
+		if(pvNormal)
+		{
+			*pvNormal = float3(0.0f, 0.0f, 1.0f);
+		}
+		return(true);
+	}
+
+	plane = SMPLANE(float3(0.0f, 0.0f, -1.0f), aabb.vMin.z);
+	if(plane.intersectLine(&vPoint, vStart, vEnd) && SMIsAABBInsideAABB(SMAABB(vPoint, vPoint), aabb))
+	{
+		*pvOut = vPoint;
+		if(pvNormal)
+		{
+			*pvNormal = float3(0.0f, 0.0f, -1.0f);
+		}
+		return(true);
+	}
+
+	return(false);
 }
 
 bool CFuncLadder::rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut, float3 *pvNormal, bool isRayInWorldSpace, bool bReturnNearestPoint)
 {
-	// TODO Implement me!
+	assert(isRayInWorldSpace);
+
+ 	SMAABB aabb = getBound();
+
+	if(bReturnNearestPoint)
+	{
+		bool b0, b1;
+		float3 vPos0, vPos1;
+		float3 vNormal0, vNormal1;
+		float3 *pvNormal0 = NULL, *pvNormal1 = NULL;
+		if(pvNormal)
+		{
+			pvNormal0 = &vNormal0;
+			pvNormal1 = &vNormal1;
+		}
+
+		b0 = SMAABBIntersectLine(aabb + getPos(), vStart, vEnd, &vPos0, pvNormal0);
+		b1 = SMAABBIntersectLine(aabb + getUpPos(), vStart, vEnd, &vPos1, pvNormal1);
+
+		if(b0 && b1)
+		{
+			if(SMVector3Length2(vPos0 - vStart) > SMVector3Length2(vPos1 - vStart))
+			{
+				b0 = false;
+			}
+			else
+			{
+				b1 = false;
+			}
+		}
+
+		if(b0)
+		{
+			*pvOut = vPos0;
+			if(pvNormal)
+			{
+				*pvNormal = vNormal0;
+			}
+		}
+		else if(b1)
+		{
+			*pvOut = vPos1;
+			if(pvNormal)
+			{
+				*pvNormal = vNormal1;
+			}
+		}
+
+		return(b0 || b1);
+	}
+	else
+	{
+		if(SMAABBIntersectLine(aabb + getPos(), vStart, vEnd, pvOut, pvNormal) || SMAABBIntersectLine(aabb + getUpPos(), vStart, vEnd, pvOut, pvNormal))
+		{
+			return(true);
+		}
+	}
 	
 	return(false);
 }
+
+void CFuncLadder::onUse(CBaseEntity *pUser)
+{
+	connectToLadder(pUser);
+	BaseClass::onUse(pUser);
+}
+
+float3 CFuncLadder::getUpPos()
+{
+	return(getOrient() * m_vUpPoint + getPos());
+}
+
+void CFuncLadder::connectToLadder(CBaseEntity *pEntity)
+{
+	if(fstrcmp(pEntity->getClassName(), "player"))
+	{
+		return;
+	}
+	CBaseCharacter *pCharacter = (CBaseCharacter*)pEntity;
+	CLadderMovementController *pController = new CLadderMovementController(this);
+	pCharacter->setMovementController(pController);
+	mem_release(pController);
+}
+
+void CFuncLadder::onPhysicsStep()
+{
+	if(!m_pGhostObject || !m_isEnabled)
+	{
+		return;
+	}
+
+	Array<CBaseEntity*> aCurrentTouchingEntities(m_aTouchedEntities.size());
+
+	for(UINT i = 0, l = m_pGhostObject->getOverlappingPairCount(); i < l; ++i)
+	{
+		IXCollisionPair *pair = m_pGhostObject->getOverlappingPair(i);
+
+		for(UINT j = 0, jl = pair->getContactManifoldCount(); j < jl; ++j)
+		{
+			IXContactManifold *pManifold = pair->getContactManifold(j);
+			if(pManifold->getContactCount() > 0)
+			{
+				IXCollisionObject *p0 = pair->getObject0();
+				IXCollisionObject *p1 = pair->getObject1();
+
+				const IXCollisionObject *pObject = p0 == m_pGhostObject ? p1 : p0;
+
+				if(pObject->getUserPointer() && pObject->getUserTypeId() == 1)
+				{
+					CBaseEntity *pEnt = (CBaseEntity*)pObject->getUserPointer();
+					if(pEnt)
+					{
+						aCurrentTouchingEntities.push_back(pEnt);
+						if(m_aTouchedEntities.indexOf(pEnt) < 0)
+						{
+							connectToLadder(pEnt);
+							//printf("touched %s\n", pEnt->getClassName());
+						}
+					}
+				}
+				break;
+			}
+		}
+	}
+
+	m_aTouchedEntities = aCurrentTouchingEntities;
+}
+
+
+void CFuncLadder::enable()
+{
+	if(!m_isEnabled)
+	{
+		if(m_pGhostObject)
+		{
+			GetPhysWorld()->addCollisionObject(m_pGhostObject, CG_LADDER, CG_CHARACTER);
+		}
+		m_pTickEventChannel->addListener(&m_physicsTicker);
+		m_isEnabled = true;
+	}
+}
+
+void CFuncLadder::disable()
+{
+	if(m_isEnabled)
+	{
+		if(m_pGhostObject)
+		{
+			GetPhysWorld()->removeCollisionObject(m_pGhostObject);
+		}
+		m_pTickEventChannel->removeListener(&m_physicsTicker);
+		m_isEnabled = false;
+	}
+}
+
+void CPhysicsLadderTickEventListener::onEvent(const XEventPhysicsStep *pData)
+{
+	m_pLadder->onPhysicsStep();
+}
diff --git a/source/game/FuncLadder.h b/source/game/FuncLadder.h
index ad349f999f50edebe0941cdd6f21e606a6d89b38..5ea6f958bfcd4d5bb266696cab76d1a3d4e0ac8b 100644
--- a/source/game/FuncLadder.h
+++ b/source/game/FuncLadder.h
@@ -15,12 +15,28 @@ public:
 
 #define LADDER_INITIALLY_DISABLED ENT_FLAG_0
 
+class CFuncLadder;
+class CPhysicsLadderTickEventListener final : public IEventListener<XEventPhysicsStep>
+{
+public:
+	CPhysicsLadderTickEventListener(CFuncLadder *pLadder) :
+		m_pLadder(pLadder)
+	{
+	}
+	void onEvent(const XEventPhysicsStep *pData) override;
+
+private:
+	CFuncLadder *m_pLadder;
+};
+
 class CFuncLadder: public CPointEntity
 {
 	DECLARE_CLASS(CFuncLadder, CPointEntity);
 	DECLARE_PROPTABLE();
+	friend class CPhysicsLadderTickEventListener;
 public:
-	DECLARE_TRIVIAL_CONSTRUCTOR();
+	DECLARE_CONSTRUCTOR();
+	~CFuncLadder();
 
 	void setPos(const float3 &pos) override;
 
@@ -30,21 +46,39 @@ public:
 
 	bool rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut = NULL, float3 *pvNormal = NULL, bool isRayInWorldSpace = true, bool bReturnNearestPoint = false) override;
 
+	void onUse(CBaseEntity *pUser) override;
+
+	float3 getUpPos();
+
 private:
+	void connectToLadder(CBaseEntity *pEntity);
+	void createPhysBody();
 	void setUpPoint(const float3 &vUp);
 	void updateFlags() override;
 
 	void turnOn(inputdata_t *pInputdata);
 	void turnOff(inputdata_t *pInputdata);
 	void toggle(inputdata_t *pInputdata);
+	void initPhysics();
+	void enable();
+	void disable();
+	void onPhysicsStep();
+
+	SMAABB getBound();
 
 private:
-	float3_t m_vUpPoint;
-	bool m_isUpSet = false;
-	bool m_isEnabled = true;
+	float3_t m_vUpPoint = float3_t(0.0f, 2.0f, 0.0f);
+	bool m_isEnabled = false;
 
 	output_t m_onPlayerGetOn;
 	output_t m_onPlayerGetOff;
+
+	IXGhostObject *m_pGhostObject = NULL;
+	IXConvexHullShape *m_pCollideShape = NULL;
+	static IEventChannel<XEventPhysicsStep> *m_pTickEventChannel;
+	CPhysicsLadderTickEventListener m_physicsTicker;
+
+	Array<CBaseEntity*> m_aTouchedEntities;
 };
 
 #endif
diff --git a/source/game/IMovementController.h b/source/game/IMovementController.h
new file mode 100644
index 0000000000000000000000000000000000000000..cf98817c7be270ed835ee1b96a9d304c2d471df6
--- /dev/null
+++ b/source/game/IMovementController.h
@@ -0,0 +1,19 @@
+#ifndef __IMOVEMENTCONTROLLER_H
+#define __IMOVEMENTCONTROLLER_H
+
+#include <gdefines.h>
+
+class CBaseCharacter;
+class IMovementController: public IXUnknown
+{
+public:
+	virtual void setCharacter(CBaseCharacter *pCharacter) = 0;
+
+	virtual void handleMove(const float3 &vDir) = 0;
+	virtual void handleJump() = 0;
+	virtual bool handleUse() = 0;
+
+	virtual void update(float fDt) = 0;
+};
+
+#endif
diff --git a/source/game/LadderMovementController.cpp b/source/game/LadderMovementController.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef824bf04907b941823c1931fcf2562008b94d13
--- /dev/null
+++ b/source/game/LadderMovementController.cpp
@@ -0,0 +1,130 @@
+#include "LadderMovementController.h"
+#include "BaseCharacter.h"
+#include "FuncLadder.h"
+#include "Player.h"
+
+CLadderMovementController::CLadderMovementController(CFuncLadder *pLadder)
+{
+	m_vLadderPoint[0] = pLadder->getPos();
+	m_vLadderPoint[1] = pLadder->getUpPos();
+
+	m_vLadderDir = SMVector3Normalize(m_vLadderPoint[1] - m_vLadderPoint[0]);
+}
+CLadderMovementController::~CLadderMovementController()
+{
+	if(m_pCharacter)
+	{
+		m_pCharacter->getCharacterController()->setGravity(float3(0.0f, -10.0f, 0.0f));
+	}
+}
+
+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 CLadderMovementController::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 CLadderMovementController::handleMove(const float3 &vDir)
+{
+	m_vMoveDir = vDir;
+}
+
+void CLadderMovementController::handleJump()
+{
+	m_bWillDismount = true;
+}
+
+bool CLadderMovementController::handleUse()
+{
+	m_bWillDismount = true;
+	return(true);
+}
+
+void CLadderMovementController::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 * 3.0f;
+		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/LadderMovementController.h b/source/game/LadderMovementController.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b34f2256e1e0e4051f4f39cf1d2a40e87046739
--- /dev/null
+++ b/source/game/LadderMovementController.h
@@ -0,0 +1,41 @@
+#ifndef __LADDERMOVEMENTCONTROLLER_H
+#define __LADDERMOVEMENTCONTROLLER_H
+
+#include "IMovementController.h"
+
+class CFuncLadder;
+class CLadderMovementController: public IXUnknownImplementation<IMovementController>
+{
+public:
+	CLadderMovementController(CFuncLadder *pLadder);
+	~CLadderMovementController();
+
+	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;
+
+	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.cpp b/source/game/Player.cpp
index 73b5bf07f9c9c832051a1324c3363a99cae8f54e..3268ecebb50c9c7d4afd863eda2d5b270151064f 100644
--- a/source/game/Player.cpp
+++ b/source/game/Player.cpp
@@ -8,6 +8,7 @@ See the license in LICENSE
 #include "Player.h"
 #include "LightDirectional.h"
 #include "BaseAmmo.h"
+#include "FuncLadder.h"
 
 #include "BaseWeapon.h"
 
@@ -149,6 +150,7 @@ void CPlayer::updateInput(float dt)
 	// m_vWpnShakeAngles = (float3)(m_vWpnShakeAngles * 0.4f);
 	m_vWpnShakeAngles = (float3)(m_vWpnShakeAngles / (1.05f + dt));
 
+	// Handle look
 	if(!*editor_mode || SSInput_GetKeyState(SIM_LBUTTON))
 	{
 		SSInput_GetMouseDelta(&x, &y);
@@ -208,7 +210,7 @@ void CPlayer::updateInput(float dt)
 		{
 			//dt *= 5.0f;
 		}
-		dt *= 10.0f;
+		//dt *= 10.0f;
 		float3 dir;
 		bool mov = false;
 		if(m_uMoveDir & PM_FORWARD)
@@ -235,31 +237,43 @@ void CPlayer::updateInput(float dt)
 			mov = true;
 		}
 
-		if(m_uMoveDir & PM_CROUCH || (m_fCurrentHeight < 0.99f && !m_pCharacter->canStandUp((m_fCapsHeight - m_fCapsRadius * 2.0f) * (1.0f - m_fCurrentHeight))))
+		if(m_uMoveDir & PM_OBSERVER)
+		{
+			setPos(getPos() + m_pHeadEnt->getOrient() * (SMVector3Normalize(dir) * dt * 10.0f));
+		}
+		else if(m_pMovementController)
 		{
-			m_fCurrentHeight -= dt;
-			float fMinHeight = (m_fCapsHeightCrouch - m_fCapsRadius * 2.0f) / (m_fCapsHeight - m_fCapsRadius * 2.0f);
-			if(m_fCurrentHeight < fMinHeight)
+			m_vCurrentSpeed = {0.0f, 0.0f, 0.0f};
+
+			if(m_uMoveDir & PM_JUMP)
 			{
-				m_fCurrentHeight = fMinHeight;
+				m_pMovementController->handleJump();
 			}
+			m_pMovementController->handleMove(m_pHeadEnt->getOrient() * SMVector3Normalize(dir));
+			m_pMovementController->update(dt);
 		}
 		else
 		{
-			m_fCurrentHeight += dt;
-			if(m_fCurrentHeight > 1.0f)
+			if(m_uMoveDir & PM_CROUCH || (m_fCurrentHeight < 0.99f && !m_pCharacter->canStandUp((m_fCapsHeight - m_fCapsRadius * 2.0f) * (1.0f - m_fCurrentHeight))))
 			{
-				m_fCurrentHeight = 1.0f;
+				m_fCurrentHeight -= dt * 10.0f;
+				float fMinHeight = (m_fCapsHeightCrouch - m_fCapsRadius * 2.0f) / (m_fCapsHeight - m_fCapsRadius * 2.0f);
+				if(m_fCurrentHeight < fMinHeight)
+				{
+					m_fCurrentHeight = fMinHeight;
+				}
 			}
-		}
-		m_pCollideShape->setLocalScaling(float3(1.0f, m_fCurrentHeight, 1.0f));
+			else
+			{
+				m_fCurrentHeight += dt * 10.0f;
+				if(m_fCurrentHeight > 1.0f)
+				{
+					m_fCurrentHeight = 1.0f;
+				}
+			}
+			m_pCollideShape->setLocalScaling(float3(1.0f, m_fCurrentHeight, 1.0f));
+
 
-		if(m_uMoveDir & PM_OBSERVER)
-		{
-			setPos(getPos() + m_pHeadEnt->getOrient() * (SMVector3Normalize(dir) * dt));
-		}
-		else
-		{
 			dir = SMQuaternion(m_vPitchYawRoll.y, 'y') * (SMVector3Normalize(dir)/* * dt*/);
 			dir *= 3.5f;
 			if(m_uMoveDir & PM_CROUCH)
@@ -306,7 +320,7 @@ void CPlayer::updateInput(float dt)
 
 			if(onGround())
 			{
-				float fAccel = *cl_acceleration * dt;
+				float fAccel = *cl_acceleration * dt * 10.0f;
 				float3 fAccelDir = m_vTargetSpeed - m_vCurrentSpeed;
 				float fMaxAccel = SMVector3Length(fAccelDir);
 
@@ -330,7 +344,7 @@ void CPlayer::updateInput(float dt)
 
 					
 
-					m_fViewbobStep += dt * *cl_bob_period * fBobCoeff;
+					m_fViewbobStep += dt * 10.0f * *cl_bob_period * fBobCoeff;
 					
 
 					/*if(m_uMoveDir & PM_RUN)
@@ -380,7 +394,7 @@ void CPlayer::updateInput(float dt)
 			//vel = getDiscreteLinearVelocity();
 			//printf("%f, %f, %f\n", vel.x, vel.y, vel.z);
 
-			m_vWpnShakeAngles.x += -vel.y * 0.05f;
+			m_vWpnShakeAngles.x += -vel.y * 0.01f;
 			const float fMaxAng = SM_PI * 0.1f;
 			m_vWpnShakeAngles.x = clampf(m_vWpnShakeAngles.x, -fMaxAng, fMaxAng);
 		}
@@ -461,6 +475,8 @@ void CPlayer::spawn()
 			m_uMoveDir &= ~PM_OBSERVER;
 			m_pCrosshair->enable();
 
+			setMovementController(NULL);
+
 			GameData::m_pHUDcontroller->setPlayerRot(m_vPitchYawRoll);
 			GameData::m_pHUDcontroller->setPlayerPos(getPos());
 			GameData::m_pHUDcontroller->setPlayerHealth(m_fHealth);
diff --git a/source/game/Player.h b/source/game/Player.h
index 797497644def32e57199073f33e0faed5ca8d53b..34ab9367236655984586e182949ce97c0ea35329 100644
--- a/source/game/Player.h
+++ b/source/game/Player.h
@@ -25,6 +25,7 @@ See the license in LICENSE
 //! Класс игрока  \ingroup cbaseanimating
 class CPlayer: public CBaseCharacter
 {
+	friend class CLadderMovementController;
 	DECLARE_CLASS(CPlayer, CBaseCharacter);
 	DECLARE_PROPTABLE();
 public:
diff --git a/source/physics/CharacterController.cpp b/source/physics/CharacterController.cpp
index 6af897f2e0bad280cd2fc9e1b0328c039c004efb..b7dee7a3abf8f8004a33b0b516219c47fef6aef5 100644
--- a/source/physics/CharacterController.cpp
+++ b/source/physics/CharacterController.cpp
@@ -65,23 +65,23 @@ void XMETHODCALLTYPE CCharacterController::unregisterInWorld()
 	}
 }
 
-void XMETHODCALLTYPE CCharacterController::setMaxJumpHeight(float fHeight) const
+void XMETHODCALLTYPE CCharacterController::setMaxJumpHeight(float fHeight)
 {
 	m_pController->setMaxJumpHeight(fHeight);
 }
-void XMETHODCALLTYPE CCharacterController::setJumpSpeed(float fSpeed) const
+void XMETHODCALLTYPE CCharacterController::setJumpSpeed(float fSpeed)
 {
 	m_pController->setJumpSpeed(fSpeed);
 }
-void XMETHODCALLTYPE CCharacterController::setGravity(const float3_t &vSpeed) const
+void XMETHODCALLTYPE CCharacterController::setGravity(const float3_t &vSpeed)
 {
 	m_pController->setGravity(F3_BTVEC(vSpeed));
 }
-void XMETHODCALLTYPE CCharacterController::setFallSpeed(float fSpeed) const
+void XMETHODCALLTYPE CCharacterController::setFallSpeed(float fSpeed)
 {
 	m_pController->setFallSpeed(fSpeed);
 }
-void XMETHODCALLTYPE CCharacterController::setMaxPenetrationDepth(float fMaxDepth) const
+void XMETHODCALLTYPE CCharacterController::setMaxPenetrationDepth(float fMaxDepth)
 {
 	m_pController->setMaxPenetrationDepth(fMaxDepth);
 }
diff --git a/source/physics/CharacterController.h b/source/physics/CharacterController.h
index 24cbe40613b280be45843e8b7c98dd48e8e32999..f3af6e1cb3321f38682a11dfcfcfd45810b7affb 100644
--- a/source/physics/CharacterController.h
+++ b/source/physics/CharacterController.h
@@ -24,11 +24,11 @@ public:
 	void XMETHODCALLTYPE registerInWorld(IXPhysicsWorld *pWorld) override;
 	void XMETHODCALLTYPE unregisterInWorld() override;
 
-	void XMETHODCALLTYPE setMaxJumpHeight(float fHeight) const override;
-	void XMETHODCALLTYPE setJumpSpeed(float fSpeed) const override;
-	void XMETHODCALLTYPE setGravity(const float3_t &vSpeed) const override;
-	void XMETHODCALLTYPE setFallSpeed(float fSpeed) const override;
-	void XMETHODCALLTYPE setMaxPenetrationDepth(float fMaxDepth) const override;
+	void XMETHODCALLTYPE setMaxJumpHeight(float fHeight) override;
+	void XMETHODCALLTYPE setJumpSpeed(float fSpeed) override;
+	void XMETHODCALLTYPE setGravity(const float3_t &vSpeed) override;
+	void XMETHODCALLTYPE setFallSpeed(float fSpeed) override;
+	void XMETHODCALLTYPE setMaxPenetrationDepth(float fMaxDepth) override;
 
 private:
 	btKinematicCharacterController *m_pController;
diff --git a/source/terrax/mainWindow.cpp b/source/terrax/mainWindow.cpp
index 6996e46fb3b992688fdb054b67f5426a06bdd360..2b9b8270d6d0ea98f04ab754583d675e0cc27b04 100644
--- a/source/terrax/mainWindow.cpp
+++ b/source/terrax/mainWindow.cpp
@@ -4532,15 +4532,16 @@ XDECLARE_PROP_GIZMO(Radius, void XMETHODCALLTYPE onChange(float fNewRadius, IXEd
 XDECLARE_PROP_GIZMO(Handle, void XMETHODCALLTYPE moveTo(const float3 &vNewPos, IXEditorGizmoHandle *pGizmo) override
 {
 	pGizmo->setPos(vNewPos);
+	float3_t vTmp = m_pObj->getOrient().Conjugate() * (vNewPos - m_pObj->getPos());
 	char tmp[64];
-	sprintf(tmp, "%f %f %f", vNewPos.x, vNewPos.y, vNewPos.z);
+	sprintf(tmp, "%f %f %f", vTmp.x, vTmp.y, vTmp.z);
 	m_pCommand->setKV(m_field.szKey, tmp);
 }, void init()
 {
 	float3_t vec;
 	if(sscanf(m_pObj->getKV(m_field.szKey), "%f %f %f", &vec.x, &vec.y, &vec.z))
 	{
-		m_pGizmo->setPos(vec);
+		m_pGizmo->setPos(m_pObj->getOrient() * vec + m_pObj->getPos());
 	}
 });
 
diff --git a/source/terrax/terrax.cpp b/source/terrax/terrax.cpp
index 56df654753d9174e57e199ded9859da02a34a061..3c256fea72a642fd489ff99dbf2b43a3c2ff8ea8 100644
--- a/source/terrax/terrax.cpp
+++ b/source/terrax/terrax.cpp
@@ -1278,28 +1278,33 @@ int main(int argc, char **argv)
 								}
 
 								CProxyObject *pProxy = new CProxyObject(guid);
-								pProxy->setDstObject(guidTarget);
-
-								for(UINT k = 0, kl = pSArr->size(); k < kl; ++k)
+								if(pProxy->setDstObject(guidTarget))
 								{
-									szGUID = pSArr->at(k)->getString();
-									if(!szGUID || !XGUIDFromString(&guid, szGUID))
+									for(UINT k = 0, kl = pSArr->size(); k < kl; ++k)
 									{
-										LibReport(REPORT_MSG_LEVEL_ERROR, "Invalid model '%u' guid in proxy '%u' in '%s'. '%s'\n", k, j, szFile, szGUID ? szGUID : "");
-										continue;
+										szGUID = pSArr->at(k)->getString();
+										if(!szGUID || !XGUIDFromString(&guid, szGUID))
+										{
+											LibReport(REPORT_MSG_LEVEL_ERROR, "Invalid model '%u' guid in proxy '%u' in '%s'. '%s'\n", k, j, szFile, szGUID ? szGUID : "");
+											continue;
+										}
+
+										pProxy->addSrcModel(guid);
 									}
 
-									pProxy->addSrcModel(guid);
-								}
-
-								g_apProxies.push_back(pProxy);
+									g_apProxies.push_back(pProxy);
 
-								//pProxy->build();
+									//pProxy->build();
 
-								add_ref(pProxy);
-								g_pLevelObjects.push_back(pProxy);
+									add_ref(pProxy);
+									g_pLevelObjects.push_back(pProxy);
 
-								isLoaded = true;
+									isLoaded = true;
+								}
+								else
+								{
+									mem_release(pProxy);
+								}
 							}
 						}
 					}
@@ -1849,7 +1854,7 @@ void XRender3D()
 			pvData = NULL;
 
 			XEnumerateObjects([&](IXEditorObject *pObj, bool isProxy, ICompoundObject *pParent){
-				float3_t vPos = pObj->getPos();
+				//float3_t vPos = pObj->getPos();
 				TODO("Add visibility check");
 				/*if(fViewportBorders.x > vPos.x || fViewportBorders.z < vPos.x || fViewportBorders.y < vPos.z) // not visible
 				{
diff --git a/source/xcommon/physics/IXCharacterController.h b/source/xcommon/physics/IXCharacterController.h
index ac56270ab3de0a7052f25c577eac16a279b474ad..4f35e0c5a974c3a8c16e47906f371f5075ee859d 100644
--- a/source/xcommon/physics/IXCharacterController.h
+++ b/source/xcommon/physics/IXCharacterController.h
@@ -20,11 +20,11 @@ public:
 	virtual void XMETHODCALLTYPE registerInWorld(IXPhysicsWorld *pWorld) = 0;
 	virtual void XMETHODCALLTYPE unregisterInWorld() = 0;
 	
-	virtual void XMETHODCALLTYPE setMaxJumpHeight(float fHeight) const = 0;
-	virtual void XMETHODCALLTYPE setJumpSpeed(float fSpeed) const = 0;
-	virtual void XMETHODCALLTYPE setGravity(const float3_t &vSpeed) const = 0;
-	virtual void XMETHODCALLTYPE setFallSpeed(float fSpeed) const = 0;
-	virtual void XMETHODCALLTYPE setMaxPenetrationDepth(float fMaxDepth) const = 0;
+	virtual void XMETHODCALLTYPE setMaxJumpHeight(float fHeight) = 0;
+	virtual void XMETHODCALLTYPE setJumpSpeed(float fSpeed) = 0;
+	virtual void XMETHODCALLTYPE setGravity(const float3_t &vSpeed) = 0;
+	virtual void XMETHODCALLTYPE setFallSpeed(float fSpeed) = 0;
+	virtual void XMETHODCALLTYPE setMaxPenetrationDepth(float fMaxDepth) = 0;
 
 
 	/*
diff --git a/source/xcommon/physics/IXCollisionObject.h b/source/xcommon/physics/IXCollisionObject.h
index 7b0747039016d814bebed3b07266452dee4f27c2..2592ff18ba23132be9401e7af755333e4b78eaad 100644
--- a/source/xcommon/physics/IXCollisionObject.h
+++ b/source/xcommon/physics/IXCollisionObject.h
@@ -21,6 +21,7 @@ enum COLLISION_GROUP
 	CG_BULLETFIRE = BIT(8),
 	CG_NPCVIEW = BIT(9),
 	CG_DOOR = BIT(10),
+	CG_LADDER = BIT(11),
 
 	CG_ALL = 0xFFFFFFFF
 };