//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #ifndef FUNC_AREAPORTALBASE_H #define FUNC_AREAPORTALBASE_H #ifdef _WIN32 #pragma once #endif #include "baseentity.h" #include "utllinkedlist.h" // Shared stuff between door portals and window portals. class CFuncAreaPortalBase : public CBaseEntity { DECLARE_CLASS(CFuncAreaPortalBase, CBaseEntity); public: DECLARE_DATADESC(); CFuncAreaPortalBase(); virtual ~CFuncAreaPortalBase(); // Areaportals must be placed in each map for preprocess, they can't use // transitions virtual int ObjectCaps(void) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } // This is called each frame for each client to all portals to close // when the viewer is far enough away, or on the backside. // // The default implementation closes the portal if the viewer (plus some // padding) is on the backside of the portal. Return false if you close the // portal. // // Returns whether or not the (server part of) the engine was told to shut // the portal off for purposes of flowing through portals to determine which // areas to send to the client. // // // bIsOpenOnClient is usually the same as the return value but NOT always. // Here's why (explained here in depth because this case was discovered in a // lengthy debugging session): // // - Each CFuncAreaPortalBase represents two dareaportal_t's (matched by // CFuncAreaPortalBase::m_portalNumber // and dareaportal_t::m_PortalKey). Each dareaportal_t leads into one of // the connected areas and the dareaportal_t's have opposite-facing // planes. // // - The engine's SetAreaPortalState function takes a portal key and closes // BOTH dareaportal_t's associated with it. // // - UpdateVisibility may decide a portal leading out of the _area you're // sitting in_ can be closed // for purposes of flowing through areas because you're on the backside of // the dareaportal_t that you would flow out of. // // - At the same time, you might be able to look through the other // dareaportal_t attached to // that portal key (right back into the area you're standing in). // // - An illustration: // // -------------------- // | | // | |--------|aaaaaa| // | | |bbbbbb| <---- aaaa and bbbb area both for //PortalKey1 // |**| | | // | | | area | // |**| | 2 | // | | |------| // | | | | // | ---------- area | // | 1 | // |------------------| // // "aaaa" and "bbbb" each represent a different dareaportal_t, (aaa leads // into area 2 and bbb leads into area 1). They both represent the same // portal key though (call it PortalKey1). // // When standing in area 1 (where the "area 1" text is), the engine will // check aaaa and it'll notice that you're on the wrong side of aaaa to be // looking through it, so it'll say that PortalKey1 is closed for purposes // of flowing through areas on the server (it turns out you can get to area // 2 through the portal right above the "area 1" text). // // If you told the client that PortalKey1 was closed, then you'd get a pop // when moving from area 1 to area 2 because the client would think you // couldn't see through PortalKey1 into area 1 UNTIL the server transmitted // the new updated PortalKey bits, which can be lagged. // // That's why we have bIsOpenOnClient which doesn't include this backfacing // test. // // .. and they all lived happily ever after. // The End // // // // Note: when you're standing in the space between the **'s, then the server // would stop transmitting the contents of area 2 because there would be no // portal you were on the correct side of to see into area 2. virtual bool UpdateVisibility(const Vector &vOrigin, float fovDistanceAdjustFactor, bool &bIsOpenOnClient); public: // This matches two dareaportal_t::m_PortalKeys. int m_portalNumber; int m_iPortalVersion; private: unsigned short m_AreaPortalsElement; // link into g_AreaPortals. }; extern CUtlLinkedList g_AreaPortals; #endif // FUNC_AREAPORTALBASE_H