Moderator elections are being held. See Valve Developer Community:Moderator elections for more details.
Users who would like to run for moderator must be autoconfirmed and have at least 100 edits. Users can check their own edit count at Special:Preferences.
Users who would like to run for moderator must be autoconfirmed and have at least 100 edits. Users can check their own edit count at Special:Preferences.
The Message template has been deleted. A list of pages that transclude it are at Valve Developer Community:Message transclusions.
CAreaPortalOneWay
Contents
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.png](/w/images/thumb/4/45/Tip.png/9px-Tip.png)
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
andCAreaPortal::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.png](/w/images/thumb/c/cc/Note.png/10px-Note.png)
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."
]