CFuncMoveLinear ParentingFix: Difference between revisions
m (If this isn't done, the game will crash when trying to change position after loading a save.) |
(Changed to a more lightweight fix from Mapbase) |
||
Line 1: | Line 1: | ||
{{ | 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>. | ||
The first offender is this bit of code in <code>Spawn()</code>: | |||
<source lang=cpp> | <source lang=cpp> | ||
m_vecPosition1 = GetAbsOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition); | |||
m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance); | |||
m_vecFinalDest = GetAbsOrigin(); | |||
</source> | </source> | ||
Replace that with this code: | |||
<source lang=cpp> | <source lang=cpp> | ||
m_vecPosition1 = GetLocalOrigin() - (m_vecMoveDir * m_flMoveDistance * m_flStartPosition); | |||
m_vecPosition2 = m_vecPosition1 + (m_vecMoveDir * m_flMoveDistance); | |||
m_vecFinalDest = GetLocalOrigin(); | |||
m_vecReference = GetLocalOrigin(); | |||
</source> | </source> | ||
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> | ||
if ( GetAbsOrigin() == m_vecPosition2 ) | |||
{ | { | ||
m_OnFullyOpen.FireOutput( this, this ); | |||
} | } | ||
else if ( GetAbsOrigin() == m_vecPosition1 ) | |||
if ( | |||
{ | { | ||
m_OnFullyClosed.FireOutput( this, this ); | |||
} | } | ||
</source> | </source> | ||
Replace | Replace that with this code: | ||
<source lang=cpp> | <source lang=cpp> | ||
if ( GetLocalOrigin() == m_vecPosition2 ) | |||
if ( GetLocalOrigin() == | |||
{ | { | ||
m_OnFullyOpen.FireOutput( this, this ); | m_OnFullyOpen.FireOutput( this, this ); | ||
} | } | ||
else if ( GetLocalOrigin() == | else if ( GetLocalOrigin() == m_vecPosition1 ) | ||
{ | { | ||
m_OnFullyClosed.FireOutput( this, this ); | m_OnFullyClosed.FireOutput( this, this ); | ||
} | } | ||
</source> | </source> | ||
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 | void SetParent( CBaseEntity* pNewParent, int iAttachment = -1 ); | ||
</source> | |||
In the same file, add these lines under the declaration of <code>m_flMoveDistance</code>: | |||
<source lang=cpp> | |||
// For the parenting fix. | |||
// Prevents position inconsistencies when changing parent. | |||
Vector | Vector m_vecReference; | ||
</source> | |||
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 | // 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:: | 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) | |||
if ( | |||
{ | { | ||
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> | </source> | ||
That should be everything to get <code>func_movelinear</code> 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|ent_fire}} usage. |
Revision as of 16:18, 21 September 2019
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</code, 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.