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 };