CAreaPortalOneWay
From Valve Developer Community
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.
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_AreaPortalsElementandCAreaPortal::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).
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." ]
