Vector2<Angle> PseudoJet::AnglesMinTo(PseudoJet const& jet) const
{
    auto angles = jet.Angles();
    auto angles_1 = Angles(false);
    auto angles_2 = Angles(true);
    return angles - angles_1 < angles - angles_2  ?  angles_1 : angles_2;
}
Example #2
0
/*
=============
Vec3::ToAngles
=============
*/
Angles Vec3::ToAngles( void ) const {
  float forward;
  float yaw;
  float pitch;
  
  if ( ( x == 0.0f ) && ( y == 0.0f ) ) {
    yaw = 0.0f;
    if ( z > 0.0f ) {
      pitch = 90.0f;
    } else {
      pitch = 270.0f;
    }
  } else {
    yaw = RAD2DEG( atan2( y, x ) );
    if ( yaw < 0.0f ) {
      yaw += 360.0f;
    }

    forward = ( float )Math::Sqrt( x * x + y * y );
    pitch = RAD2DEG( atan2( z, forward ) );
    if ( pitch < 0.0f ) {
      pitch += 360.0f;
    }
  }

  return Angles( -pitch, yaw, 0.0f );
}
Example #3
0
Angles PolarDiagram::getAngles(const float &speed){
    if(debug)
        qDebug() << Q_FUNC_INFO;
    if(contains(speed))
        return diagram.value(speed);
    else
        return Angles();
}
Example #4
0
UniqueID		RotExpr::EvalA(HP& c)
{
	if (icpt.complex)
	{
		HP a;
		UniqueID t=icpt.EvalA(a);
		if (t)	RETURN(t);
		int v1;
		if ((v1=hdg.Evaluate())!=ENABLE_COMPLEX_VAL)
			c.H=Angles(v1)+a.H;
		if ((v1=pitch.Evaluate())!=ENABLE_COMPLEX_VAL)
			c.P=Angles(v1)+a.P;
	}
	else
	{
		int v1;
		if ((v1=hdg.Evaluate())!=ENABLE_COMPLEX_VAL)
			c.H=Angles(v1);
		if ((v1=pitch.Evaluate())!=ENABLE_COMPLEX_VAL)
			c.P=Angles(v1);
	}
	return UID_Null;
}
Example #5
0
void EldritchFramework::CreateMinimapView() {
  PRINTF("EldritchFramework::CreateMinimapView\n");

  SafeDelete(m_MinimapView);

  STATICHASH(EldMinimap);

  STATICHASH(MinimapRTWidth);
  const float fMinimapRTWidth =
      ConfigManager::GetFloat(sMinimapRTWidth, 0.0f, sEldMinimap);

  STATICHASH(MinimapRTHeight);
  const float fMinimapRTHeight =
      ConfigManager::GetFloat(sMinimapRTHeight, 0.0f, sEldMinimap);

  STATICHASH(MinimapViewDistance);
  const float ViewDistance =
      ConfigManager::GetFloat(sMinimapViewDistance, 0.0f, sEldMinimap);

  STATICHASH(MinimapViewPitch);
  const float ViewPitch = DEGREES_TO_RADIANS(
      ConfigManager::GetFloat(sMinimapViewPitch, 0.0f, sEldMinimap));

  STATICHASH(MinimapViewFOV);
  const float FOV = ConfigManager::GetFloat(sMinimapViewFOV, 0.0f, sEldMinimap);

  STATICHASH(MinimapViewNearClip);
  const float NearClip =
      ConfigManager::GetFloat(sMinimapViewNearClip, 0.0f, sEldMinimap);

  STATICHASH(MinimapViewFarClip);
  const float FarClip =
      ConfigManager::GetFloat(sMinimapViewFarClip, 0.0f, sEldMinimap);

  const Angles EyeOrientation = Angles(ViewPitch, 0.0f, 0.0f);
  const Vector EyeDirection = EyeOrientation.ToVector();
  const Vector EyePosition = -EyeDirection * ViewDistance;
  const float AspectRatio = fMinimapRTWidth / fMinimapRTHeight;

  m_MinimapView = new View(EyePosition, EyeOrientation, FOV, AspectRatio,
                           NearClip, FarClip);
}
/*virtual*/ void WBActionEldStopMotion::Execute()
{
	WBAction::Execute();

	STATIC_HASHED_STRING( EventOwner );
	WBEntity* const pEntity = WBActionStack::Top().GetEntity( sEventOwner );

	if( pEntity )
	{
		WBCompEldTransform* const pTransform = pEntity->GetTransformComponent<WBCompEldTransform>();
		if( pTransform )
		{
			pTransform->SetGravity( 0.0f );
			pTransform->SetVelocity( Vector() );
			pTransform->SetAcceleration( Vector() );
			pTransform->SetRotationalVelocity( Angles() );
			pTransform->SetCanMove( false );
		}
	}
}
Example #7
0
Angles WBEvent::SParameter::CoerceAngles() const
{
	return m_Type == EWBEPT_Angles ? GetAngles() : Angles();
}
Example #8
0
Angles WBEvent::GetAngles( const HashedString& Name ) const
{
	TParameterMap::Iterator ParamIter = m_Parameters.Search( Name );
	return ParamIter.IsValid() ? ParamIter.GetValue().CoerceAngles() : Angles();
}
void WBCompEldWatson::TickPrimed()
{
	if( !EldritchGame::IsPlayerAlive() )
	{
		// Player can't cause Watson to teleport and attack when dead
		return;
	}

	if( !EldritchGame::IsPlayerVisible() )
	{
		// Player can't cause Watson to teleport and attack when invisible
		return;
	}

	if( !IsPlayerVulnerable( true ) )
	{
		// Do nothing, can't attack until player is vulnerable
		return;
	}

	WBEntity* const pEntity = GetEntity();
	DEVASSERT( pEntity );

	WBEntity* const pPlayer = EldritchGame::GetPlayer();
	ASSERT( pPlayer );

	WBCompEldTransform* const pTransform = pEntity->GetTransformComponent<WBCompEldTransform>();
	DEVASSERT( pTransform );

	WBCompEldTransform* const pPlayerTransform = pPlayer->GetTransformComponent<WBCompEldTransform>();
	ASSERT( pPlayerTransform );

	const Vector			PlayerLocation		= pPlayerTransform->GetLocation();
	const Angles			PlayerOrientation	= pPlayerTransform->GetOrientation();
	EldritchWorld* const	pWorld				= GetWorld();
	ASSERT( pWorld );

	const Angles				PlayerOrientation2D	= Angles( 0.0f, 0.0f, PlayerOrientation.Yaw );
	const Vector				PlayerFacing2D		= PlayerOrientation2D.ToVector();
	WBCompEldCollision* const	pCollision			= GET_WBCOMP( pEntity, EldCollision );
	const Vector				Extents				= pCollision->GetExtents();
	Vector						TeleportLocation	= PlayerLocation - ( PlayerFacing2D * m_TeleportDist );
	CollisionInfo FindSpotInfo;
	FindSpotInfo.m_CollideWorld		= true;
	FindSpotInfo.m_CollideEntities	= true;
	FindSpotInfo.m_UserFlags		= EECF_EntityCollision;
	FindSpotInfo.m_CollidingEntity	= pEntity;

	if( pCollision->MatchesAllCollisionFlags( EECF_BlocksEntities ) )
	{
		// HACK: Test against all entities, since Watson is a blocking entity.
		FindSpotInfo.m_UserFlags |= EECF_CollideAllEntities;
	}

	if( !pWorld->FindSpot( TeleportLocation, Extents, FindSpotInfo ) )
	{
		// Do nothing, we don't fit behind player
		return;
	}

	const Vector				NewToPlayer2D		= ( PlayerLocation - TeleportLocation ).Get2D();
	const Angles				NewOrientation		= NewToPlayer2D.ToAngles();

	pTransform->SetLocation( TeleportLocation );
	pTransform->SetOrientation( NewOrientation );

	WB_MAKE_EVENT( OnWatsonTeleported, GetEntity() );
	WB_DISPATCH_EVENT( GetEventManager(), OnWatsonTeleported, GetEntity() );

	// Need to prime again before we can do another teleport
	m_Primed = false;
}
bool WBCompEldWatson::IsPlayerVulnerable( const bool CheckMinDistance ) const
{
	WBEntity* const pEntity = GetEntity();
	DEVASSERT( pEntity );

	WBEntity* const pPlayer = EldritchGame::GetPlayer();
	ASSERT( pPlayer );

	WBCompEldTransform* const pTransform = pEntity->GetTransformComponent<WBCompEldTransform>();
	DEVASSERT( pTransform );

	WBCompEldTransform* const pPlayerTransform = pPlayer->GetTransformComponent<WBCompEldTransform>();
	ASSERT( pPlayerTransform );

	const Vector	EyeLocation			= pTransform->GetLocation() + Vector( 0.0f, 0.0f, m_EyeOffsetZ );
	const Vector	PlayerLocation		= pPlayerTransform->GetLocation();
	const Vector	PlayerViewLocation	= EldritchGame::GetPlayerViewLocation();
	const Vector	ToPlayer			= PlayerViewLocation - EyeLocation;
	const float		DistanceSq			= ToPlayer.LengthSquared();

	if( CheckMinDistance && DistanceSq < m_MinAttackDistSq )
	{
		// Do nothing, we're already close to player.
		return false;
	}

	if( DistanceSq > m_MaxAttackDistSq )
	{
		// Do nothing, we're too far from the player
		return false;
	}

	// Do both a 3D and 2D check. 3D check is necessary so Watson doesn't teleport
	// when player is looking up or down at it, and 2D is necessary so Watson doesn't
	// teleport when player is looking up or down *away* from it.
	const Angles	PlayerOrientation	= pPlayerTransform->GetOrientation();
	const Angles	PlayerOrientation2D	= Angles( 0.0f, 0.0f, PlayerOrientation.Yaw );
	const Vector	PlayerFacing		= PlayerOrientation.ToVector();
	const Vector	PlayerFacing2D		= PlayerOrientation2D.ToVector();
	const Vector	ToPlayer2D			= ToPlayer.Get2D();
	const float		CosPlayerAngle		= PlayerFacing.Dot( ToPlayer );
	const float		CosPlayerAngle2D	= PlayerFacing2D.Dot( ToPlayer2D );
	if( CosPlayerAngle < 0.0f || CosPlayerAngle2D < 0.0f )
	{
		// Do nothing, player is facing us.
		return false;
	}

	EldritchWorld* const	pWorld	= GetWorld();
	ASSERT( pWorld );

	CollisionInfo OcclusionInfo;
	OcclusionInfo.m_CollideWorld		= true;
	OcclusionInfo.m_CollideEntities		= true;
	OcclusionInfo.m_UserFlags			= EECF_Occlusion;
	OcclusionInfo.m_StopAtAnyCollision	= true;
	if( pWorld->LineCheck( EyeLocation, PlayerViewLocation, OcclusionInfo ) )
	{
		// Do nothing, player is occluded.
		return false;
	}

	return true;
}
Example #11
0
/*virtual*/ void EldritchFramework::Initialize() {
  XTRACE_FUNCTION;

  XTRACE_BEGIN(PreFramework3D);
  ReverseHash::Initialize();

  PackStream::StaticAddPackageFile("eldritch-base.cpk");
  FrameworkUtil::MinimalLoadConfigFiles("Config/default.ccf");

  InitializePackages();

  InitializeDLC();

  // Load prefs over anything in the defaults.
  LoadPrefsConfig();

  LOADPRINTLEVELS;

  STATICHASH(Version);
  STATICHASH(ContentSyncer);
  SimpleString LocalVersion =
      ConfigManager::GetString(sVersion, "", sContentSyncer);
  PRINTF("Version: %s\n", LocalVersion.CStr());

  XTRACE_BEGIN(InitializeFactories);
  PRINTF("Initializing factories...\n");

  PRINTF("Initializing SDP factories.\n");
  SDPFactory::InitializeBaseFactories();
#define ADDSDPFACTORY(type) \
  SDPFactory::RegisterSDPFactory(#type, SDP##type::Factory);
#include "eldritchsdps.h"
#undef ADDSDPFACTORY

  PRINTF("Initializing UI factories.\n");
  UIFactory::InitializeBaseFactories();
#define ADDUISCREENFACTORY(type) \
  UIFactory::RegisterUIScreenFactory(#type, UIScreen##type::Factory);
#include "eldritchuiscreens.h"
#undef ADDUISCREENFACTORY

  PRINTF("Initializing anim event factories.\n");
#define ADDANIMEVENTFACTORY(type) \
  AnimEventFactory::GetInstance()->Register(#type, AnimEvent##type::Factory);
#include "eldritchanimevents.h"
#undef ADDANIMEVENTFACTORY

  PRINTF("Initializing PE factories.\n");
  WBParamEvaluatorFactory::InitializeBaseFactories();
#define ADDWBPEFACTORY(type) \
  WBParamEvaluatorFactory::RegisterFactory(#type, WBPE##type::Factory);
#include "rodinwbpes.h"
#include "eldritchwbpes.h"
#undef ADDWBPEFACTORY

  PRINTF("Initializing action factories.\n");
  WBActionFactory::InitializeBaseFactories();
#define ADDWBACTIONFACTORY(type) \
  WBActionFactory::RegisterFactory(#type, WBAction##type::Factory);
#include "uiwbactions.h"
#include "rodinwbactions.h"
#include "eldritchwbactions.h"
#undef ADDWBPEFACTORY

  PRINTF("Initializing BT factories.\n");
  RodinBTNodeFactory::InitializeBaseFactories();
#define ADDRODINBTNODEFACTORY(type) \
  RodinBTNodeFactory::RegisterFactory(#type, RodinBTNode##type::Factory);
#include "eldritchrodinbtnodes.h"
#undef ADDRODINBTNODEFACTORY

  // Initialize core and Eldritch Workbench component factories.
  PRINTF("Initializing component factories.\n");
  WBComponent::InitializeBaseFactories();
#define ADDWBCOMPONENT(type) \
  WBComponent::RegisterWBCompFactory(#type, WBComp##type::Factory);
#include "rodinwbcomponents.h"
#include "eldritchwbcomponents.h"
#undef ADDWBCOMPONENT
  XTRACE_END;

  PRINTF("Factories initialized.\n");

  // Create input system before framework so it will exist for UI. But don't
  // attach devices yet, as they don't exist.
  PRINTF("Initializing input system.\n");
  m_InputSystem = new InputSystem;
  m_InputSystem->Initialize("EldritchInput");
  XTRACE_END;

  Framework3D::Initialize();

  STATICHASH(DisplayWidth);
  STATICHASH(DisplayHeight);
  m_DisplayWidth = ConfigManager::GetInt(sDisplayWidth);
  m_DisplayHeight = ConfigManager::GetInt(sDisplayHeight);

#if BUILD_WINDOWS
  m_CheckForUpdates = new CheckForUpdates(m_UIManager);
#endif

  m_Controller = new XInputController;

  m_TargetManager = new EldritchTargetManager(m_Renderer);
  m_TargetManager->CreateTargets(m_Display->m_Width, m_Display->m_Height);

  m_Audio3DListener = new EldritchSound3DListener;
  m_Audio3DListener->Initialize();

  ASSERT(m_AudioSystem);
  m_AudioSystem->Set3DListener(m_Audio3DListener);

  STATICHASH(FOV);
  const float FOV = ConfigManager::GetFloat(sFOV, 90.0f);

  STATICHASH(ForegroundFOV);
  const float FGFOV = ConfigManager::GetFloat(sForegroundFOV, 60.0f);

  STATICHASH(NearClip);
  const float NearClip = ConfigManager::GetFloat(sNearClip, 0.1f);

  STATICHASH(FarClip);
  const float FarClip = ConfigManager::GetFloat(sFarClip, 0.1f);

  const float fDisplayWidth = static_cast<float>(m_DisplayWidth);
  const float fDisplayHeight = static_cast<float>(m_DisplayHeight);
  const float AspectRatio = fDisplayWidth / fDisplayHeight;

  m_MainView =
      new View(Vector(), Angles(), FOV, AspectRatio, NearClip, FarClip);
  m_FGView =
      new View(Vector(), Angles(), FGFOV, AspectRatio, NearClip, FarClip);
  CreateHUDView();
  CreateMirrorView();
  CreateMinimapView();

  CreateBuckets();

  m_InputSystem->SetKeyboard(m_Keyboard);
  m_InputSystem->SetMouse(m_Mouse);
  m_InputSystem->SetController(m_Controller);
  m_InputSystem->SetClock(m_Clock);

  WBActionStack::Initialize();

  PRINTF("Initializing Eldritch.\n");

  InitializeWorld(HashedString(), false);

  m_Game = new EldritchGame;
  m_Game->RefreshRTDependentSystems();
  m_Game->Initialize();

  // Initialize config stuff
  {
    STATICHASH(InvertY);
    const bool InvertY = ConfigManager::GetBool(sInvertY);

    STATIC_HASHED_STRING(TurnY);
    m_InputSystem->SetMouseInvert(sTurnY, InvertY);
    m_InputSystem->SetControllerInvert(sTurnY, InvertY);

    STATICHASH(ControllerPower);
    const float ControllerPower = ConfigManager::GetFloat(sControllerPower);

    STATIC_HASHED_STRING(MoveX);
    m_InputSystem->SetControllerPower(sMoveX, ControllerPower);
    STATIC_HASHED_STRING(MoveY);
    m_InputSystem->SetControllerPower(sMoveY, ControllerPower);
    STATIC_HASHED_STRING(TurnX);
    m_InputSystem->SetControllerPower(sTurnX, ControllerPower);
    m_InputSystem->SetControllerPower(sTurnY, ControllerPower);
  }

  // Initialize UI sliders. This could be neater.
  // This also pushes the initial values to their respective systems, which is
  // pret-ty terrible design.
  {
    WBEventManager* pEventManager = WBWorld::GetInstance()->GetEventManager();

    {
      STATICHASH(MouseSpeed);
      const float MouseSpeed = ConfigManager::GetFloat(sMouseSpeed, 1.0f);

      STATIC_HASHED_STRING(ControlsOptionsScreen);
      STATIC_HASHED_STRING(MouseSpeedSlider);
      WB_MAKE_EVENT(SetUISliderValue, NULL);
      WB_SET_AUTO(SetUISliderValue, Hash, Screen, sControlsOptionsScreen);
      WB_SET_AUTO(SetUISliderValue, Hash, Widget, sMouseSpeedSlider);
      WB_SET_AUTO(SetUISliderValue, Float, SliderValue,
                  GetSliderValueFromMouseSpeed(MouseSpeed));
      WB_DISPATCH_EVENT(pEventManager, SetUISliderValue, NULL);
    }

    {
      STATICHASH(ControllerSpeed);
      const float ControllerSpeed =
          ConfigManager::GetFloat(sControllerSpeed, 1.0f);

      STATIC_HASHED_STRING(ControlsOptionsScreen);
      STATIC_HASHED_STRING(ControllerSpeedSlider);
      WB_MAKE_EVENT(SetUISliderValue, NULL);
      WB_SET_AUTO(SetUISliderValue, Hash, Screen, sControlsOptionsScreen);
      WB_SET_AUTO(SetUISliderValue, Hash, Widget, sControllerSpeedSlider);
      WB_SET_AUTO(SetUISliderValue, Float, SliderValue,
                  GetSliderValueFromControllerSpeed(ControllerSpeed));
      WB_DISPATCH_EVENT(pEventManager, SetUISliderValue, NULL);
    }

    {
      STATICHASH(Brightness);
      const float Brightness = ConfigManager::GetFloat(sBrightness, 1.0f);

      STATIC_HASHED_STRING(BrightnessScreen);
      STATIC_HASHED_STRING(BrightnessSlider);
      WB_MAKE_EVENT(SetUISliderValue, NULL);
      WB_SET_AUTO(SetUISliderValue, Hash, Screen, sBrightnessScreen);
      WB_SET_AUTO(SetUISliderValue, Hash, Widget, sBrightnessSlider);
      WB_SET_AUTO(SetUISliderValue, Float, SliderValue,
                  GetSliderValueFromBrightness(Brightness));
      WB_DISPATCH_EVENT(pEventManager, SetUISliderValue, NULL);
    }

    {
      STATIC_HASHED_STRING(DisplayOptionsScreen);
      STATIC_HASHED_STRING(FOVSlider);
      WB_MAKE_EVENT(SetUISliderValue, NULL);
      WB_SET_AUTO(SetUISliderValue, Hash, Screen, sDisplayOptionsScreen);
      WB_SET_AUTO(SetUISliderValue, Hash, Widget, sFOVSlider);
      WB_SET_AUTO(SetUISliderValue, Float, SliderValue,
                  GetSliderValueFromFOV(FOV));
      WB_DISPATCH_EVENT(pEventManager, SetUISliderValue, NULL);
    }

    {
      STATICHASH(MasterVolume);
      const float MasterVolume = ConfigManager::GetFloat(sMasterVolume);

      STATIC_HASHED_STRING(AudioOptionsScreen);
      STATIC_HASHED_STRING(VolumeSlider);
      WB_MAKE_EVENT(SetUISliderValue, NULL);
      WB_SET_AUTO(SetUISliderValue, Hash, Screen, sAudioOptionsScreen);
      WB_SET_AUTO(SetUISliderValue, Hash, Widget, sVolumeSlider);
      WB_SET_AUTO(SetUISliderValue, Float, SliderValue, MasterVolume);
      WB_DISPATCH_EVENT(pEventManager, SetUISliderValue, NULL);
    }

    {
      STATICHASH(MusicVolume);
      const float MusicVolume = ConfigManager::GetFloat(sMusicVolume);

      STATIC_HASHED_STRING(AudioOptionsScreen);
      STATIC_HASHED_STRING(MusicVolumeSlider);
      WB_MAKE_EVENT(SetUISliderValue, NULL);
      WB_SET_AUTO(SetUISliderValue, Hash, Screen, sAudioOptionsScreen);
      WB_SET_AUTO(SetUISliderValue, Hash, Widget, sMusicVolumeSlider);
      WB_SET_AUTO(SetUISliderValue, Float, SliderValue, MusicVolume);
      WB_DISPATCH_EVENT(pEventManager, SetUISliderValue, NULL);
    }
  }

  // Initialize UI callbacks
  {
    UIScreenEldSetRes* pSetRes =
        m_UIManager->GetScreen<UIScreenEldSetRes>("SetResScreen");
    pSetRes->SetUICallback(SUICallback(EldritchFramework::OnSetRes, nullptr));

    UIScreenFade* pFade = m_UIManager->GetScreen<UIScreenFade>("Fade");
    pFade->SetFadeCallback(
        SUICallback(EldritchFramework::OnFadeFinished, nullptr));
  }

  WB_MAKE_EVENT(ResetToInitialScreens, NULL);
  WB_DISPATCH_EVENT(WBWorld::GetInstance()->GetEventManager(),
                    ResetToInitialScreens, NULL);

  {
    // HACK: Too content aware
    STATIC_HASHED_STRING(MKGLogoScreen);
    WB_MAKE_EVENT(PushUIScreen, NULL);
    WB_SET_AUTO(PushUIScreen, Hash, Screen, sMKGLogoScreen);
    WB_DISPATCH_EVENT(WBWorld::GetInstance()->GetEventManager(), PushUIScreen,
                      NULL);
  }

  // Tick world once to pump the event queue. Fixes title screen bugs.
  m_World->Tick(0.0f);

  // All done, show the window finally.
  SafeDelete(m_SplashWindow);
#if BUILD_WINDOWS_NO_SDL
  m_Window->Show(m_CmdShow);
#elif BUILD_SDL
  m_Window->Show();
#endif

  // Reattach GL context if needed.
  m_Renderer->Refresh();

  PRINTF("Eldritch initialization complete.\n");
}
Example #12
0
void WBCompEldRope::DropRope() {
  WBEntity* const pEntity = GetEntity();
  DEVASSERT(pEntity);

  WBCompEldTransform* const pTransform =
      pEntity->GetTransformComponent<WBCompEldTransform>();
  DEVASSERT(pTransform);

  // HookVector is directed away from the surface (same as hit normal)
  const Vector HookVector = Quantize(pTransform->GetOrientation().ToVector());
  const Vector DropVector = Vector(0.0f, 0.0f, -1.0f);

  ASSERT(HookVector.LengthSquared() == 1.0f);
  ASSERT((HookVector.y == 0.0f && HookVector.z == 0.0f) ||
         (HookVector.x == 0.0f && HookVector.z == 0.0f) ||
         (HookVector.x == 0.0f && HookVector.y == 0.0f));

  WBCompEldCollision* const pCollision = GET_WBCOMP(pEntity, EldCollision);
  DEVASSERT(pCollision);

  WBCompEldMesh* const pMesh = GET_WBCOMP(pEntity, EldMesh);
  DEVASSERT(pMesh);

  EldritchWorld* const pWorld = GetWorld();

  const Vector HitLocation = pTransform->GetLocation();
  const Vector StartLocation = HitLocation + (HookVector * m_HookLength);
  const Ray TraceRay = Ray(StartLocation, DropVector);

  CollisionInfo Info;
  Info.m_CollideWorld = true;
  Info.m_CollideEntities = true;
  Info.m_CollidingEntity = pEntity;
  Info.m_UserFlags = EECF_CollideAsEntity | EECF_CollideStaticEntities;

  if (pWorld->Trace(TraceRay, Info)) {
    m_Dropped = true;

    // Reset orientation now that it is being "baked" into extents.
    pTransform->SetOrientation(Angles());

    // StartLocation is where the rope attaches to the hook
    // EndLocation is where the rope dangles above the ground
    ASSERT(Info.m_Plane.m_Normal.Equals(Vector::Up, EPSILON));
    const Vector EndLocation =
        Info.m_Intersection + Info.m_Plane.m_Normal * m_DangleHeight;
    pTransform->SetLocation(0.5f * (StartLocation + EndLocation));

    const Vector HalfExtents = (0.5f * (EndLocation - StartLocation)).GetAbs();
    const Vector CollisionFatten =
        Vector(m_CollisionFatten, m_CollisionFatten, m_CollisionFatten);
    pCollision->SetExtents(HalfExtents + CollisionFatten);

    const Vector MeshFatten = Vector(m_MeshFatten, m_MeshFatten, m_MeshFatten);
    pMesh->SetMeshScale(HalfExtents + MeshFatten);

    m_Anchor = HitLocation - (HookVector * m_AnchorDepth);

    // Spawn rope hook entity and set up its transform and anchor
    {
      WBEntity* const pHookEntity =
          WBWorld::GetInstance()->CreateEntity(m_HookEntity);
      ASSERT(pHookEntity);

      WBCompEldTransform* const pHookTransform =
          pHookEntity->GetTransformComponent<WBCompEldTransform>();
      ASSERT(pHookTransform);

      WBCompEldAnchor* const pHookAnchor = GET_WBCOMP(pHookEntity, EldAnchor);
      ASSERT(pHookAnchor);

      pHookTransform->SetLocation(StartLocation);
      pHookTransform->SetOrientation((-HookVector).ToAngles());
    }
  } else {
    WARNDESC("Rope could not be launched.");
    pEntity->Destroy();
  }
}