CFuncMoveLinear ParentingFix: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
(Clarified the fix's origin)
 
(5 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{warning|This has not been fully tested. Two func_movelinear entities, one parented to the other, has been tested.}}
The issue is that two parts of {{ent|func_movelinear}}'s code use {{ent|GetAbsOrigin()}}, the position relative to the world, instead of {{ent|GetLocalOrigin()}}, the position relative to the world ''or parent''. All of these changes are done in <code>func_movelinear.cpp</code> and <code>func_movelinear.h</code>.


Add the following lines to func_movelinear.h under the declaration of m_flMoveDistance:
The first offender is this bit of code in <code>Spawn()</code>:
<source lang=cpp>
<source lang=cpp>
EHANDLE m_hPosition1; // Used to mark end position
m_vecPosition1 = GetAbsOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition);
EHANDLE m_hPosition2; // Used to mark start position
m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance);
m_vecFinalDest = GetAbsOrigin();
</source>
</source>


Locate these lines in func_movelinear.cpp:
Replace that with this code:
<source lang=cpp>
<source lang=cpp>
m_vecPosition1 = GetAbsOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition);
m_vecPosition1 = GetLocalOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition);
m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance);
m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance);
m_vecFinalDest = GetLocalOrigin();
m_vecReference = GetLocalOrigin();
</source>
</source>


Replace them with this:
Notice the usage of <code>m_vecReference</code>. That will be declared later in the article.
 
The other offender is <code>MoveDone()</code>:
<source lang=cpp>
<source lang=cpp>
m_hPosition1 = CreateEntityByName( "info_target" );
if ( GetAbsOrigin() == m_vecPosition2 )
m_hPosition2 = CreateEntityByName( "info_target" );
 
m_vecPosition1 = GetAbsOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition);
m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance);
 
// Update position reference entities
if ( m_hPosition1 != NULL )
{
{
m_hPosition1->SetAbsOrigin( m_vecPosition1 );
m_OnFullyOpen.FireOutput( this, this );
if ( GetParent() )
m_hPosition1->SetParent( GetParent() );
}
}
 
else if ( GetAbsOrigin() == m_vecPosition1 )
if ( m_hPosition2 != NULL )
{
{
m_hPosition2->SetAbsOrigin( m_vecPosition2 );
m_OnFullyClosed.FireOutput( this, this );
if ( GetParent() )
m_hPosition2->SetParent( GetParent() );
}
}
</source>
</source>


Replace the function CFuncMoveLinear::MoveDone with the following code:
Replace that with this code:
<source lang=cpp>
<source lang=cpp>
void CFuncMoveLinear::MoveDone( void )
if ( GetLocalOrigin() == m_vecPosition2 )
{
// Stop sounds at the next think, rather than here as another
// SetPosition call might immediately follow the end of this move
SetThink(&CFuncMoveLinear::StopMoveSound);
SetNextThink( gpGlobals->curtime + 0.1f );
BaseClass::MoveDone();
 
if ( GetLocalOrigin() == m_hPosition2->GetLocalOrigin() )
{
{
m_OnFullyOpen.FireOutput( this, this );
m_OnFullyOpen.FireOutput( this, this );
}
}
else if ( GetLocalOrigin() == m_hPosition1->GetLocalOrigin() )
else if ( GetLocalOrigin() == m_vecPosition1 )
{
{
m_OnFullyClosed.FireOutput( this, this );
m_OnFullyClosed.FireOutput( this, this );
}
}
}
</source>
</source>


Replace the functions Use, SetPosition, InputOpen, and InputClose with the following:
Even though the usage of <code>GetAbsOrigin()</code> is the main problem and the entity should now work properly when used with the <code>parentname</code> keyvalue, the entity may still stop working properly if <code>SetParent</code> or <code>ClearParent</code> 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 <code>func_movelinear</code>'s end points.
 
First, add this to <code>func_movelinear.h</code>, perhaps under the declaration of <code>ShouldSavePhysics()</code>.
<source lang=cpp>
<source lang=cpp>
void CFuncMoveLinear::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
void SetParent( CBaseEntity* pNewParent, int iAttachment = -1 );
{
</source>
if ( useType != USE_SET ) // Momentary buttons will pass down a float in here
return;


if ( value > 1.0 )
In the same file, add these lines under the declaration of <code>m_flMoveDistance</code>:
value = 1.0;
<source lang=cpp>
Vector move = m_hPosition1->GetLocalOrigin() + (value * (m_hPosition2->GetLocalOrigin() - m_hPosition1->GetLocalOrigin()));
// For the parenting fix.
// Prevents position inconsistencies when changing parent.
Vector delta = move - GetLocalOrigin();
Vector m_vecReference;
float speed = delta.Length() * 10;
</source>


MoveTo(move, speed);
Now, in <code>func_movelinear.cpp</code>, add this definition to the entity's <code>DATADESC</code>, preferably under <code>m_flMoveDistance</code>:
}
<source lang=cpp>
DEFINE_FIELD( m_vecReference, FIELD_VECTOR ),
</source>


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


Now, let's add that <code>SetParent()</code> we declared:
<source lang=cpp>
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Sets the position as a value from [0..1].
// 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::SetPosition( float flPosition )
void CFuncMoveLinear::SetParent( CBaseEntity *pParentEntity, int iAttachment )
{
{
Vector vTargetPos = m_hPosition1->GetLocalOrigin() + ( flPosition * (m_hPosition2->GetLocalOrigin() - m_hPosition1->GetLocalOrigin()));
Vector oldLocal = GetLocalOrigin();
if ((vTargetPos - GetLocalOrigin()).Length() > 0.001)
 
BaseClass::SetParent( pParentEntity, iAttachment );
 
// SOLID_NONE indicates we haven't spawned yet
if (GetSolid() != SOLID_NONE)
{
{
MoveTo(vTargetPos, m_flSpeed);
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;
}
}
}
}
</source>


That should be everything to get <code>func_movelinear</code> to work while parented.


//------------------------------------------------------------------------------
== Conclusion ==
// Purpose:
This is an improved and more lightweight version of a [https://developer.valvesoftware.com/w/index.php?title=CFuncMoveLinear_ParentingFix&oldid=218480 previous fix] which used two different endpoint entities. The current fix was originally developed for {{Game link|Mapbase}}.
//------------------------------------------------------------------------------
void CFuncMoveLinear::InputOpen( inputdata_t &inputdata )
{
if (GetLocalOrigin() != m_hPosition2->GetLocalOrigin())
{
MoveTo(m_hPosition2->GetLocalOrigin(), m_flSpeed);
}
}


Please keep an eye out for any problems, as this was mostly just tested on one map with a bunch of {{ent|ent_fire}} usage.


//------------------------------------------------------------------------------
[[Category:Free source code|F]]
// Purpose:
//------------------------------------------------------------------------------
void CFuncMoveLinear::InputClose( inputdata_t &inputdata )
{
if (GetLocalOrigin() != m_hPosition1->GetLocalOrigin())
{
MoveTo(m_hPosition1->GetLocalOrigin(), m_flSpeed);
}
}
</source>

Latest revision as of 17:13, 16 June 2020

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.

Conclusion

This is an improved and more lightweight version of a previous fix which used two different endpoint entities. The current fix was originally developed for Mapbase Mapbase .

Please keep an eye out for any problems, as this was mostly just tested on one map with a bunch of ent_fire usage.