CAreaPortalOneWay: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(some entity source code)
 
 
(7 intermediate revisions by one other user not shown)
Line 1: Line 1:
This is free entity source code for <code>[[CAreaPortalOneWay]]</code>, an [[areaportal]] that is only open when the player is within 90&deg; of a specified direction relative to it. It is useful in brightly-lit holes/windows that can be seen out of but not into.
{{toc-right}}
 
This is source code for '''<code>CAreaPortalOneWay</code>''', an [[areaportal]] that is only open when the player is a certain side of its plane. It is useful in brightly-lit windows that can be seen out of but not into.


{{tip|If the player needs to walk ''through'' the portal, [[func_areaportalwindow]] is a better choice.}}
{{tip|If the player needs to walk ''through'' the portal, [[func_areaportalwindow]] is a better choice.}}


== C++ ==
== Changes ==
 
; June 11 2009
: ''Complete rewrite. Any maps using the earlier version of the entity must be re-compiled.''
:* Support open/closed state
:* One-way functionality can be toggled
:* One-way direction can be inverted
:* Can be grouped by mapper to share a single state with other portals, reducing calculations
:* Can be set to avoid 'latency pop' when crossing plane
:* Requires access to private members <code>CFuncAreaPortalBase::m_AreaPortalsElement</code> and <code>CAreaPortal::m_state</code>
; June 9 2008
: ''Initial release.''
 
== C++ code ==
 
Paste this at the end of <code>\game\server\func_areaportal.cpp</code> (as there is no header file to #include).
 
{{note|You must to grant access to <code>CFuncAreaPortalBase::m_AreaPortalsElement</code> and <code>CAreaPortal::m_state</code> before this code will compile. The best way to do so is to move both from <code>private</code> access to <code>protected</code>.}}
 
<source lang=cpp>// An areaportal that automatically closes and opens depending on the direction of the client.
// http://developer.valvesoftware.com/wiki/CAreaPortalOneWay
class CAreaPortalOneWay : public CAreaPortal // CAPOW!
{
DECLARE_CLASS( CAreaPortalOneWay, CAreaPortal );
DECLARE_DATADESC();
 
public:
Vector m_vecOpenVector;
bool m_bAvoidPop;
bool m_bOneWayActive;
void Spawn();
void Activate();
int  Restore(IRestore &restore);
bool UpdateVisibility( const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient );
 
void InputDisableOneWay( inputdata_t &inputdata );
void InputEnableOneWay( inputdata_t &inputdata );
void InputToggleOneWay( inputdata_t &inputdata );
void InputInvertOneWay( inputdata_t &inputdata );
 
protected:
void RemoteUpdate( bool IsOpen );
 
bool m_bRemotelyUpdated;
bool m_bRemoteCalcWasOpen;
CHandle<CAreaPortalOneWay> m_hNextPortal; // This get saved to disc...
CAreaPortalOneWay* m_pNextPortal; // ...while this gets used at runtime, avoiding loads of casts
 
private:
void UpdateNextPortal( bool IsOpen );
 
// These two are irrelevant once the entity has established itself
string_t m_strGroupName;
Vector m_vecOrigin_; // The portal won't compile properly if vecOrigin itself has a value, but it's okay to move something in at runtime
};
 
LINK_ENTITY_TO_CLASS( func_areaportal_oneway, CAreaPortalOneWay );
 
BEGIN_DATADESC( CAreaPortalOneWay )
DEFINE_KEYFIELD( m_vecOpenVector, FIELD_VECTOR, "onewayfacing" ),
DEFINE_KEYFIELD( m_bAvoidPop, FIELD_BOOLEAN, "avoidpop" ),
DEFINE_KEYFIELD_NOT_SAVED( m_vecOrigin_, FIELD_VECTOR, "origin_" ),
DEFINE_KEYFIELD_NOT_SAVED( m_strGroupName, FIELD_STRING, "group" ),
DEFINE_FIELD( m_bOneWayActive, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hNextPortal, FIELD_EHANDLE ),
DEFINE_INPUTFUNC( FIELD_VOID, "DisableOneWay", InputDisableOneWay ),
DEFINE_INPUTFUNC( FIELD_VOID, "EnableOneWay", InputEnableOneWay ),
DEFINE_INPUTFUNC( FIELD_VOID, "ToggleOneWay", InputToggleOneWay ),
DEFINE_INPUTFUNC( FIELD_VOID, "InvertOneWay", InputInvertOneWay ),
END_DATADESC()
 
void CAreaPortalOneWay::Spawn()
{
// Convert our angle from Hammer to a proper vector
QAngle angOpenDir = QAngle( m_vecOpenVector.x, m_vecOpenVector.y, m_vecOpenVector.z );
AngleVectors( angOpenDir, &m_vecOpenVector );
 
SetLocalOrigin(m_vecOrigin_);
m_bOneWayActive = true;
m_bRemotelyUpdated = false;
 
BaseClass::Spawn();
}
 
void CAreaPortalOneWay::Activate()
{
// Optimisation: share open/closed value for CAPOWs with the same GroupName.
if (m_strGroupName != NULL_STRING)
// m_AreaPortalsElement is a private member of CFuncAreaPortalBase. If this can't be changed,
// replace with g_AreaPortals.Find(this). DEFINITELY DO NOT use m_portalNumber!
for( unsigned short i = m_AreaPortalsElement; i != g_AreaPortals.InvalidIndex(); i = g_AreaPortals.Next(i) )
{
CAreaPortalOneWay* pCur = dynamic_cast<CAreaPortalOneWay*>(g_AreaPortals[i]);
 
if ( pCur && pCur != this && strcmp( STRING(m_strGroupName),STRING(pCur->m_strGroupName) ) == 0 )
{
m_pNextPortal = pCur;
m_hNextPortal = pCur;
break;
}
}
 
BaseClass::Activate();
}
 
int CAreaPortalOneWay::Restore(IRestore &restore)
{
if ( m_hNextPortal.IsValid() )
m_pNextPortal = m_hNextPortal.Get();
 
return BaseClass::Restore(restore);
}
 
// Disable the CAPOW (becomes a normal AP)
void CAreaPortalOneWay::InputDisableOneWay( inputdata_t &inputdata )
{
m_bOneWayActive = false;
}
 
// Re-enable the CAPOW
void CAreaPortalOneWay::InputEnableOneWay( inputdata_t &inputdata )
{
m_bOneWayActive = true;
}
 
// Toggle CAPOW
void CAreaPortalOneWay::InputToggleOneWay( inputdata_t &inputdata )
{
m_bOneWayActive = !m_bOneWayActive;
}
 
// Flip the one way direction
void CAreaPortalOneWay::InputInvertOneWay( inputdata_t &inputdata )
{
m_vecOpenVector.Negate();
}
 
// Recieve a shared state from another CAPOW, then pass it on to the next
void CAreaPortalOneWay::RemoteUpdate( bool IsOpen )
{
m_bRemotelyUpdated = true;
m_bRemoteCalcWasOpen = IsOpen;
UpdateNextPortal(IsOpen);
}
 
// Inline func since this code is required three times
inline void CAreaPortalOneWay::UpdateNextPortal( bool IsOpen )
{
if (m_pNextPortal)
m_pNextPortal->RemoteUpdate(IsOpen);
}


Paste this at the end of <code>game\server\func_areaportal.cpp</code> (there is no header file).
#define VIEWER_PADDING 80 // Value copied from func_areaportalbase.cpp


<span style="color: blue">class</span> CAreaPortalOneWay : <span style="color: blue">public</span> CAreaPortal
bool CAreaPortalOneWay::UpdateVisibility( const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient )
{
{
DECLARE_CLASS( CAreaPortalOneWay, CAreaPortal );
if (!m_bOneWayActive)
DECLARE_DATADESC();
return BaseClass::UpdateVisibility( vOrigin, fovDistanceAdjustFactor, bIsOpenOnClient );
 
Vector m_vecOpenVector;
if( m_portalNumber == -1 || m_state == AREAPORTAL_CLOSED ) // m_state is a private member of CAreaPortal by default
string_t m_szLinkedEnt; <span style="color:green">// Anything with an origin will do</span>
{
bIsOpenOnClient = false;
<span style="color:blue">void</span> Activate();
return false;
<span style="color:blue">bool</span> UpdateVisibility( <span style="color:blue">const</span> Vector &vOrigin, <span style="color:blue">float</span> fovDistanceAdjustFactor, <span style="color:blue">bool</span> &bIsOpenOnClient );
}
};
 
// Has another CAPOW on our plane already done a calculation?
LINK_ENTITY_TO_CLASS( func_areaportal_oneway, CAreaPortalOneWay );
// Note that the CAPOW chain is traversed with new values in RemoteUpdate(), NOT here
if (m_bRemotelyUpdated)
BEGIN_DATADESC( CAreaPortalOneWay )
{
DEFINE_KEYFIELD( m_vecOpenVector, FIELD_VECTOR, <span style="color:brown">"onewayfacing"</span> ),
m_bRemotelyUpdated = false;
DEFINE_KEYFIELD( m_szLinkedEnt, FIELD_STRING, <span style="color:brown">"linkedent"</span> ),
return m_bRemoteCalcWasOpen ? BaseClass::UpdateVisibility( vOrigin, fovDistanceAdjustFactor, bIsOpenOnClient ) : false;
END_DATADESC()
}
 
<span style="color:blue">void</span> CAreaPortalOneWay::Activate()
// ***********************
{
// If we've got this far then we're the first CAPOW in the chain this frame
BaseClass::Activate();
// and need to calculate a value and pass it along said chain ourselves
// ***********************
<span style="color:green">// Get our brush</span>
 
CBaseEntity* pBrush = gEntList.FindEntityByName(NULL,m_szLinkedEnt);
float dist = VIEWER_PADDING; // Assume open for backfacing tests...
<span style="color:blue">if</span> ( !pBrush || pBrush->GetAbsOrigin() == Vector(0,0,0) ) <span style="color:green">// FIXME: Once in a blue moon (0,0,0) might be valid!</span>
VPlane plane;
{
if( engine->GetAreaPortalPlane(vOrigin,m_portalNumber,&plane) )
Warning( <span style="color:brown">"%s has missing/invalid associated entity! DELETING!\n"</span>, GetDebugName() );
dist = plane.DistTo(vOrigin); // ...but if we find a plane, use a genuine figure instead.
UTIL_Remove(<span style="color:blue">this</span>);
// This is done because GetAreaPortalPlane only works for
<span style="color:blue">return</span>;
// portals facing the current area.
}
 
// We can use LocalOrigin here because APs never have parents.
SetAbsOrigin( pBrush->GetAbsOrigin() );
float dot = DotProduct(m_vecOpenVector,vOrigin - GetLocalOrigin());
 
<span style="color:green">// Convert our angle from Hammer to a proper vector</span>
if( dot > 0 )
QAngle angOpenDir = QAngle( m_vecOpenVector.x, m_vecOpenVector.y, m_vecOpenVector.z );
{
AngleVectors( angOpenDir, &m_vecOpenVector );
// We are on the open side of the portal. Pass the result on!
UpdateNextPortal(true);
<span style="color:green">// Re-scale the vector's axes to between 0 and 1</span>
VectorNormalize(m_vecOpenVector);
// The following backfacing check is the inverse of CFuncAreaPortalBase's:
// it /closes/ the portal if the camera is /behind/ the plane. IsOpenOnClient
<span style="color:green">// Move our vector so that it's an offset from the brush's origin</span>
// is left alone as per func_areaportalbase.h
m_vecOpenVector = GetAbsOrigin() * m_vecOpenVector;
return dist < -VIEWER_PADDING ? false : true;
}
}
else // Closed side
<span style="color:blue">bool</span> CAreaPortalOneWay::UpdateVisibility( <span style="color:blue">const</span> Vector &vOrigin, <span style="color:blue">float</span> fovDistanceAdjustFactor, <span style="color:blue">bool</span> &bIsOpenOnClient )
{
{
// To avoid latency pop when crossing the portal's plane, it is only
<span style="color:green">// Move vOrigin so that it's an offset from the brush</span>
// closed on the client if said client is outside the "padding zone".
<span style="color:green">// (Need a new var since it's const)</span>
if ( !m_bAvoidPop || (m_bAvoidPop && dist > VIEWER_PADDING) )
Vector point = vOrigin - GetAbsOrigin();
bIsOpenOnClient = false;
VectorNormalize(point);
 
// We are definitely closed on the server, however.
<span style="color:green">// Is the adjusted vOrigin behind openvector?</span>
UpdateNextPortal(false);
<span style="color:blue">if</span> ( DotProduct( m_vecOpenVector, point ) < 0 )
return false;
<span style="color:blue">return</span> false;
}
<span style="color:blue">else</span>
}</source>
<span style="color:green">// Let the baseclass close the portal if it wants</span>
<span style="color:blue">return</span> BaseClass::UpdateVisibility( vOrigin, fovDistanceAdjustFactor, bIsOpenOnClient );
}


=== Doors ===
=== Doors ===


If you want to attach a door to a <code>CAreaPortalOneWay</code>, you will need to modify its code so that it finds the "func_areaportal_oneway" entity.
If you want to attach a prop or func door to a <code>CAreaPortalOneWay</code>, you will need to modify the doors' code so that they search for <code>func_areaportal*</code> (with a wildcard).
 
''CBasePropDoor::UpdateAreaPortals & CBaseDoor::UpdateAreaPortals:''
  <span style="color: red">- while ((pPortal = gEntList.FindEntityByClassname(pPortal, "func_areaportal")) != NULL)</span>
  <span style="color: green">+ while ((pPortal = gEntList.FindEntityByClassname(pPortal, "func_areaportal*")) != NULL)</span>


Note the new asterisk.
''CBasePropDoor::UpdateAreaPortals, CBaseDoor::UpdateAreaPortals:''
  - <span style="color: red">while ((pPortal = gEntList.FindEntityByClassname(pPortal, "func_areaportal")) != NULL)</span>
  + <span style="color: green">while ((pPortal = gEntList.FindEntityByClassname(pPortal, "func_areaportal*")) != NULL)</span>


== FGD ==
== FGD ==


@SolidClass base(func_areaportal) color(0 255 255) = func_areaportal_oneway :
<source lang=cpp>@SolidClass base(func_areaportal) color(0 255 255) = func_areaportal_oneway : "An areaportal that is only open when viewed from one direction."
"An areaportal that is only open when the player is within 90 degrees of the specified  direction."
[
[
origin_(origin) readonly : "Origin" : : "Point from which the areaportal's location is determined (they are a special case and cannot use the normal value). Read-only."
onewayfacing(angle) : "Open direction" : "0 0 0" : "The portal will be open when the player is within 90 degrees of this direction, relative to the origin of the linked entity."
group(string) : "One-way group" : : "Optimisation: oneway portals in the same group share a single closed/open state. Use this, for example, on walls full of one-way windows."
linkedent(target_destination) : "Linked entity (required)" : "" : "An entity of some sort is required to bring the compiled portal back into world co-ordinates. It will probably be a brush that fills the gap left by the portal when closed."
onewayfacing(angle) : "Open direction" : "0 0 0" : "The portal will be open when the player is within 90 degrees of this direction."
]
avoidpop(choices) : "Avoid latency pop" : 0 : "Enable this if it becomes noticeable that the portal stays closed momentarily after the player walks past it. The portal will open 80 units in advance." =
[
0 : "No"
1 : "Yes"
]
input DisableOneWay(void) : "Disable the one-way behaviour of the portal."
input EnableOneWay(void) : "Enable the one-way behaviour of the portal."
input ToggleOneWay(void) : "Toggle the one-way behaviour of the portal."
input InvertOneWay(void) : "Flip the one-way direction."
]</source>


== See also ==
== See also ==
Line 91: Line 249:
*[[Areaportal]]
*[[Areaportal]]


[[Category:Snippets]]
[[Category:Free source code|A]]

Latest revision as of 13:53, 30 March 2025

This is source code for CAreaPortalOneWay, an areaportal that is only open when the player is a certain side of its plane. It is useful in brightly-lit windows that can be seen out of but not into.

Tip.pngTip:If the player needs to walk through the portal, func_areaportalwindow is a better choice.

Changes

June 11 2009
Complete rewrite. Any maps using the earlier version of the entity must be re-compiled.
  • Support open/closed state
  • One-way functionality can be toggled
  • One-way direction can be inverted
  • Can be grouped by mapper to share a single state with other portals, reducing calculations
  • Can be set to avoid 'latency pop' when crossing plane
  • Requires access to private members CFuncAreaPortalBase::m_AreaPortalsElement and CAreaPortal::m_state
June 9 2008
Initial release.

C++ code

Paste this at the end of \game\server\func_areaportal.cpp (as there is no header file to #include).

Note.pngNote:You must to grant access to CFuncAreaPortalBase::m_AreaPortalsElement and CAreaPortal::m_state before this code will compile. The best way to do so is to move both from private access to protected.
// An areaportal that automatically closes and opens depending on the direction of the client.
// http://developer.valvesoftware.com/wiki/CAreaPortalOneWay
class CAreaPortalOneWay : public CAreaPortal // CAPOW!
{
	DECLARE_CLASS( CAreaPortalOneWay, CAreaPortal );
	DECLARE_DATADESC();

public:
	Vector	 m_vecOpenVector;
	bool	 m_bAvoidPop;
	bool	 m_bOneWayActive;
	
	void Spawn();
	void Activate();
	int  Restore(IRestore &restore);
	bool UpdateVisibility( const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient );

	void InputDisableOneWay( inputdata_t &inputdata );
	void InputEnableOneWay( inputdata_t &inputdata );
	void InputToggleOneWay( inputdata_t &inputdata );
	void InputInvertOneWay( inputdata_t &inputdata );

protected:
	void RemoteUpdate( bool IsOpen );

	bool m_bRemotelyUpdated;
	bool m_bRemoteCalcWasOpen;
	CHandle<CAreaPortalOneWay> m_hNextPortal; // This get saved to disc...
	CAreaPortalOneWay* m_pNextPortal; // ...while this gets used at runtime, avoiding loads of casts

private:
	void UpdateNextPortal( bool IsOpen );

	// These two are irrelevant once the entity has established itself
	string_t m_strGroupName;
	Vector	 m_vecOrigin_; // The portal won't compile properly if vecOrigin itself has a value, but it's okay to move something in at runtime
};

LINK_ENTITY_TO_CLASS( func_areaportal_oneway, CAreaPortalOneWay );

BEGIN_DATADESC( CAreaPortalOneWay )
	DEFINE_KEYFIELD( m_vecOpenVector, FIELD_VECTOR, "onewayfacing" ),
	DEFINE_KEYFIELD( m_bAvoidPop, FIELD_BOOLEAN, "avoidpop" ),
	DEFINE_KEYFIELD_NOT_SAVED( m_vecOrigin_, FIELD_VECTOR, "origin_" ),
	DEFINE_KEYFIELD_NOT_SAVED( m_strGroupName, FIELD_STRING, "group" ),
	DEFINE_FIELD( m_bOneWayActive, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_hNextPortal, FIELD_EHANDLE ),
	DEFINE_INPUTFUNC( FIELD_VOID, "DisableOneWay", InputDisableOneWay ),
	DEFINE_INPUTFUNC( FIELD_VOID, "EnableOneWay", InputEnableOneWay ),
	DEFINE_INPUTFUNC( FIELD_VOID, "ToggleOneWay", InputToggleOneWay ),
	DEFINE_INPUTFUNC( FIELD_VOID, "InvertOneWay", InputInvertOneWay ),
END_DATADESC()

void CAreaPortalOneWay::Spawn()
{
	// Convert our angle from Hammer to a proper vector
	QAngle angOpenDir = QAngle( m_vecOpenVector.x, m_vecOpenVector.y, m_vecOpenVector.z );
	AngleVectors( angOpenDir, &m_vecOpenVector );

	SetLocalOrigin(m_vecOrigin_);
	m_bOneWayActive = true;
	m_bRemotelyUpdated = false;

	BaseClass::Spawn();
}

void CAreaPortalOneWay::Activate()
{
	// Optimisation: share open/closed value for CAPOWs with the same GroupName.
	if (m_strGroupName != NULL_STRING)
		// m_AreaPortalsElement is a private member of CFuncAreaPortalBase. If this can't be changed, 
		// replace with g_AreaPortals.Find(this). DEFINITELY DO NOT use m_portalNumber!
		for( unsigned short i = m_AreaPortalsElement; i != g_AreaPortals.InvalidIndex(); i = g_AreaPortals.Next(i) )
		{
			CAreaPortalOneWay* pCur = dynamic_cast<CAreaPortalOneWay*>(g_AreaPortals[i]);

			if ( pCur && pCur != this && strcmp( STRING(m_strGroupName),STRING(pCur->m_strGroupName) ) == 0 )
			{
				m_pNextPortal = pCur;
				m_hNextPortal = pCur;
				break;
			}
		}

	BaseClass::Activate();
}

int CAreaPortalOneWay::Restore(IRestore &restore)
{
	if ( m_hNextPortal.IsValid() )
		m_pNextPortal = m_hNextPortal.Get();

	return BaseClass::Restore(restore);
}

// Disable the CAPOW (becomes a normal AP)
void CAreaPortalOneWay::InputDisableOneWay( inputdata_t &inputdata )
{
	m_bOneWayActive = false;
}

// Re-enable the CAPOW
void CAreaPortalOneWay::InputEnableOneWay( inputdata_t &inputdata )
{
	m_bOneWayActive = true;
}

// Toggle CAPOW
void CAreaPortalOneWay::InputToggleOneWay( inputdata_t &inputdata )
{
	m_bOneWayActive = !m_bOneWayActive;
}

// Flip the one way direction
void CAreaPortalOneWay::InputInvertOneWay( inputdata_t &inputdata )
{
	m_vecOpenVector.Negate();
}

// Recieve a shared state from another CAPOW, then pass it on to the next
void CAreaPortalOneWay::RemoteUpdate( bool IsOpen )
{
	m_bRemotelyUpdated = true;
	m_bRemoteCalcWasOpen = IsOpen;
	UpdateNextPortal(IsOpen);
}

// Inline func since this code is required three times
inline void CAreaPortalOneWay::UpdateNextPortal( bool IsOpen )
{
	if (m_pNextPortal)
		m_pNextPortal->RemoteUpdate(IsOpen);
}

#define VIEWER_PADDING 80 // Value copied from func_areaportalbase.cpp

bool CAreaPortalOneWay::UpdateVisibility( const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient )
{
	if (!m_bOneWayActive)
		return BaseClass::UpdateVisibility( vOrigin, fovDistanceAdjustFactor, bIsOpenOnClient );

	if( m_portalNumber == -1 || m_state == AREAPORTAL_CLOSED ) // m_state is a private member of CAreaPortal by default
	{
		bIsOpenOnClient = false;
		return false;
	}

	// Has another CAPOW on our plane already done a calculation?
	// Note that the CAPOW chain is traversed with new values in RemoteUpdate(), NOT here
	if (m_bRemotelyUpdated)
	{
		m_bRemotelyUpdated = false;
		return m_bRemoteCalcWasOpen ? BaseClass::UpdateVisibility( vOrigin, fovDistanceAdjustFactor, bIsOpenOnClient ) : false;
	}

	// ***********************
	// If we've got this far then we're the first CAPOW in the chain this frame
	// and need to calculate a value and pass it along said chain ourselves
	// ***********************

	float dist = VIEWER_PADDING; // Assume open for backfacing tests...
	VPlane plane;
	if( engine->GetAreaPortalPlane(vOrigin,m_portalNumber,&plane) )
		dist = plane.DistTo(vOrigin);	// ...but if we find a plane, use a genuine figure instead.
										// This is done because GetAreaPortalPlane only works for 
										// portals facing the current area.

	// We can use LocalOrigin here because APs never have parents.
	float dot = DotProduct(m_vecOpenVector,vOrigin - GetLocalOrigin());

	if( dot > 0 )
	{
		// We are on the open side of the portal. Pass the result on!
		UpdateNextPortal(true);
		
		// The following backfacing check is the inverse of CFuncAreaPortalBase's: 
		// it /closes/ the portal if the camera is /behind/ the plane. IsOpenOnClient
		// is left alone as per func_areaportalbase.h
		return dist < -VIEWER_PADDING ? false : true;
	}
	else // Closed side
	{
		// To avoid latency pop when crossing the portal's plane, it is only 
		// closed on the client if said client is outside the "padding zone".
		if ( !m_bAvoidPop || (m_bAvoidPop && dist > VIEWER_PADDING) )
			bIsOpenOnClient = false;

		// We are definitely closed on the server, however.
		UpdateNextPortal(false);
		return false;
	}
}

Doors

If you want to attach a prop or func door to a CAreaPortalOneWay, you will need to modify the doors' code so that they search for func_areaportal* (with a wildcard).

CBasePropDoor::UpdateAreaPortals, CBaseDoor::UpdateAreaPortals:
  - while ((pPortal = gEntList.FindEntityByClassname(pPortal, "func_areaportal")) != NULL)
  + while ((pPortal = gEntList.FindEntityByClassname(pPortal, "func_areaportal*")) != NULL)

FGD

@SolidClass base(func_areaportal) color(0 255 255) = func_areaportal_oneway :	"An areaportal that is only open when viewed from one direction."
[
	origin_(origin) readonly : "Origin" : : "Point from which the areaportal's location is determined (they are a special case and cannot use the normal value). Read-only."
	group(string) : "One-way group" : : "Optimisation: oneway portals in the same group share a single closed/open state. Use this, for example, on walls full of one-way windows."
	onewayfacing(angle) : "Open direction" : "0 0 0" : "The portal will be open when the player is within 90 degrees of this direction."
	avoidpop(choices) : "Avoid latency pop" : 0 : "Enable this if it becomes noticeable that the portal stays closed momentarily after the player walks past it. The portal will open 80 units in advance." =
	[
		0 : "No"
		1 : "Yes"
	]
	
	input DisableOneWay(void) : "Disable the one-way behaviour of the portal."
	input EnableOneWay(void) : "Enable the one-way behaviour of the portal."
	input ToggleOneWay(void) : "Toggle the one-way behaviour of the portal."
	input InvertOneWay(void) : "Flip the one-way direction."
]

See also