CFuncMoveLinear ParentingFix

From Valve Developer Community
Jump to: navigation, search

The issue is that two parts of func_movelinear's code use GetAbsOrigin(), the position relative to the world, instead of GetLocalOrigin(), the position relative to the world or parent. All of these changes are done in func_movelinear.cpp and func_movelinear.h.

The first offender is this bit of code in Spawn():

	m_vecPosition1 = GetAbsOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition);
	m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance);
	m_vecFinalDest = GetAbsOrigin();

Replace that with this code:

	m_vecPosition1 = GetLocalOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition);
	m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance);
	m_vecFinalDest = GetLocalOrigin();
	m_vecReference = GetLocalOrigin();

Notice the usage of m_vecReference. That will be declared later in the article.

The other offender is MoveDone():

	if ( GetAbsOrigin() == m_vecPosition2 )
	{
		m_OnFullyOpen.FireOutput( this, this );
	}
	else if ( GetAbsOrigin() == m_vecPosition1 )
	{
		m_OnFullyClosed.FireOutput( this, this );
	}

Replace that with this code:

	if ( GetLocalOrigin() == m_vecPosition2 )
	{
		m_OnFullyOpen.FireOutput( this, this );
	}
	else if ( GetLocalOrigin() == m_vecPosition1 )
	{
		m_OnFullyClosed.FireOutput( this, this );
	}

Even though the usage of GetAbsOrigin() is the main problem and the entity should now work properly when used with the parentname keyvalue, the entity may still stop working properly if SetParent or ClearParent are used, as the position the end points are relative to isn't actually saved and is lost when being re/unparented, causing inconsistencies with the func_movelinear's end points.

First, add this to func_movelinear.h, perhaps under the declaration of ShouldSavePhysics().

void		SetParent( CBaseEntity* pNewParent, int iAttachment = -1 );

In the same file, add these lines under the declaration of m_flMoveDistance:

	// For the parenting fix.
	// Prevents position inconsistencies when changing parent.
	Vector		m_vecReference;

Now, in func_movelinear.cpp, add this definition to the entity's DATADESC, preferably under m_flMoveDistance:

	DEFINE_FIELD( m_vecReference, FIELD_VECTOR ),

That m_vecReference variable was used earlier in the article as part of Spawn(). It saves the place where the func_movelinear started for when the entity is parented to something else, which ensures the end points are transferred correctly.

Now, let's add that SetParent() we declared:

//-----------------------------------------------------------------------------
// Purpose: Sets the movement parent of this entity. This entity will be moved
//			to a local coordinate calculated from its current absolute offset
//			from the parent entity and will then follow the parent entity.
// Input  : pParentEntity - This entity's new parent in the movement hierarchy.
//-----------------------------------------------------------------------------
void CFuncMoveLinear::SetParent( CBaseEntity *pParentEntity, int iAttachment )
{
	Vector oldLocal = GetLocalOrigin();

	BaseClass::SetParent( pParentEntity, iAttachment );

	// SOLID_NONE indicates we haven't spawned yet
	if (GetSolid() != SOLID_NONE)
	{
		m_vecReference = ((m_vecReference - oldLocal) + GetLocalOrigin());
		m_vecPosition1 = m_vecReference - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition);
		m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance);
		m_vecFinalDest = m_vecReference - m_vecFinalDest;
	}
}

That should be everything to get func_movelinear to work while parented. It might be simpler than you'd expect, but please keep an eye out for any problems, as this was mostly just tested on one map with a bunch of ent_fire usage.