//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include #include "hud.h" #include "itextmessage.h" #include "materialsystem/imaterial.h" #include "materialsystem/itexture.h" #include "materialsystem/imaterialsystem.h" #include "imovehelper.h" #include "checksum_crc.h" #include "decals.h" #include "iefx.h" #include "view_scene.h" #include "filesystem.h" #include "model_types.h" #include "engine/IEngineTrace.h" #include "engine/ivmodelinfo.h" #include "c_te_effect_dispatch.h" #include #include #include #include "view.h" #include "ixboxsystem.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" ConVar localplayer_visionflags("localplayer_visionflags", "0", FCVAR_DEVELOPMENTONLY); //----------------------------------------------------------------------------- // ConVars //----------------------------------------------------------------------------- #ifdef _DEBUG ConVar r_FadeProps("r_FadeProps", "1"); #endif bool g_MakingDevShots = false; extern ConVar cl_leveloverview; //----------------------------------------------------------------------------- // Purpose: Performs a var args printf into a static return buffer // Input : *format - // ... - // Output : char //----------------------------------------------------------------------------- char *VarArgs(const char *format, ...) { va_list argptr; static char string[1024]; va_start(argptr, format); Q_vsnprintf(string, sizeof(string), format, argptr); va_end(argptr); return string; } //----------------------------------------------------------------------------- // Purpose: Returns true if the entity index corresponds to a player slot // Input : index - // Output : bool //----------------------------------------------------------------------------- bool IsPlayerIndex(int index) { return (index >= 1 && index <= gpGlobals->maxClients) ? true : false; } int GetLocalPlayerIndex(void) { C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); if (player) return player->entindex(); else return 0; // game not started yet } int GetLocalPlayerVisionFilterFlags(bool bWeaponsCheck /*= false */) { C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); if (player) return player->GetVisionFilterFlags(bWeaponsCheck); else return 0; } bool IsLocalPlayerUsingVisionFilterFlags(int nFlags, bool bWeaponsCheck /* = false */) { int nLocalPlayerFlags = GetLocalPlayerVisionFilterFlags(bWeaponsCheck); if (!bWeaponsCheck) { // We can only modify the RJ flags with normal checks that won't take the forced kill cam flags that can happen in weapon checks int nRJShaderFlags = nLocalPlayerFlags; if (nRJShaderFlags != 0 && GameRules() && !GameRules()->AllowMapVisionFilterShaders()) { nRJShaderFlags = 0; } if (nRJShaderFlags != localplayer_visionflags.GetInt()) { localplayer_visionflags.SetValue(nRJShaderFlags); } } return (nLocalPlayerFlags & nFlags) == nFlags; } bool IsLocalPlayerSpectator(void) { C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); if (player) return player->IsObserver(); else return false; // game not started yet } int GetSpectatorMode(void) { C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); if (player) return player->GetObserverMode(); else return OBS_MODE_NONE; // game not started yet } int GetSpectatorTarget(void) { C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); if (player) { CBaseEntity * target = player->GetObserverTarget(); if (target) return target->entindex(); else return 0; } else { return 0; // game not started yet } } int GetLocalPlayerTeam(void) { C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if (pPlayer) return pPlayer->GetTeamNumber(); else return TEAM_UNASSIGNED; } //----------------------------------------------------------------------------- // Purpose: Convert angles to -180 t 180 range // Input : angles - //----------------------------------------------------------------------------- void NormalizeAngles(QAngle& angles) { int i; // Normalize angles to -180 to 180 range for (i = 0; i < 3; i++) { if (angles[i] > 180.0) { angles[i] -= 360.0; } else if (angles[i] < -180.0) { angles[i] += 360.0; } } } //----------------------------------------------------------------------------- // Purpose: Interpolate Euler angles using quaternions to avoid singularities // Input : start - // end - // output - // frac - //----------------------------------------------------------------------------- void InterpolateAngles(const QAngle& start, const QAngle& end, QAngle& output, float frac) { Quaternion src, dest; // Convert to quaternions AngleQuaternion(start, src); AngleQuaternion(end, dest); Quaternion result; // Slerp QuaternionSlerp(src, dest, frac, result); // Convert to euler QuaternionAngles(result, output); } //----------------------------------------------------------------------------- // Purpose: Simple linear interpolation // Input : frac - // src - // dest - // output - //----------------------------------------------------------------------------- void InterpolateVector(float frac, const Vector& src, const Vector& dest, Vector& output) { int i; for (i = 0; i < 3; i++) { output[i] = src[i] + frac * (dest[i] - src[i]); } } client_textmessage_t *TextMessageGet(const char *pName) { return engine->TextMessageGet(pName); } //----------------------------------------------------------------------------- // Purpose: ScreenHeight returns the height of the screen, in pixels // Output : int //----------------------------------------------------------------------------- int ScreenHeight(void) { int w, h; GetHudSize(w, h); return h; } //----------------------------------------------------------------------------- // Purpose: ScreenWidth returns the width of the screen, in pixels // Output : int //----------------------------------------------------------------------------- int ScreenWidth(void) { int w, h; GetHudSize(w, h); return w; } //----------------------------------------------------------------------------- // Purpose: Return the difference between two angles // Input : destAngle - // srcAngle - // Output : float //----------------------------------------------------------------------------- float UTIL_AngleDiff(float destAngle, float srcAngle) { float delta; delta = destAngle - srcAngle; if (destAngle > srcAngle) { while (delta >= 180) delta -= 360; } else { while (delta <= -180) delta += 360; } return delta; } float UTIL_WaterLevel(const Vector &position, float minz, float maxz) { Vector midUp = position; midUp.z = minz; if (!(UTIL_PointContents(midUp) & MASK_WATER)) return minz; midUp.z = maxz; if (UTIL_PointContents(midUp) & MASK_WATER) return maxz; float diff = maxz - minz; while (diff > 1.0) { midUp.z = minz + diff / 2.0; if (UTIL_PointContents(midUp) & MASK_WATER) { minz = midUp.z; } else { maxz = midUp.z; } diff = maxz - minz; } return midUp.z; } void UTIL_Bubbles(const Vector& mins, const Vector& maxs, int count) { Vector mid = (mins + maxs) * 0.5; float flHeight = UTIL_WaterLevel(mid, mid.z, mid.z + 1024); flHeight = flHeight - mins.z; CPASFilter filter(mid); int bubbles = modelinfo->GetModelIndex("sprites/bubble.vmt"); te->Bubbles(filter, 0.0, &mins, &maxs, flHeight, bubbles, count, 8.0); } void UTIL_ScreenShake(const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake) { // Nothing for now } char TEXTURETYPE_Find(trace_t *ptr) { surfacedata_t *psurfaceData = physprops->GetSurfaceData(ptr->surface.surfaceProps); return psurfaceData->game.material; } //----------------------------------------------------------------------------- // Purpose: Make a tracer effect //----------------------------------------------------------------------------- void UTIL_Tracer(const Vector &vecStart, const Vector &vecEnd, int iEntIndex, int iAttachment, float flVelocity, bool bWhiz, char *pCustomTracerName) { CEffectData data; data.m_vStart = vecStart; data.m_vOrigin = vecEnd; data.m_hEntity = ClientEntityList().EntIndexToHandle(iEntIndex); data.m_flScale = flVelocity; // Flags if (bWhiz) { data.m_fFlags |= TRACER_FLAG_WHIZ; } if (iAttachment != TRACER_DONT_USE_ATTACHMENT) { data.m_fFlags |= TRACER_FLAG_USEATTACHMENT; // Stomp the start, since it's not going to be used anyway data.m_vStart[0] = iAttachment; } // Fire it off if (pCustomTracerName) { DispatchEffect(pCustomTracerName, data); } else { DispatchEffect("Tracer", data); } } //------------------------------------------------------------------------------ // Purpose : Creates both an decal and any associated impact effects (such // as flecks) for the given iDamageType and the trace's end position // Input : // Output : //------------------------------------------------------------------------------ void UTIL_ImpactTrace(trace_t *pTrace, int iDamageType, const char *pCustomImpactName) { C_BaseEntity *pEntity = pTrace->m_pEnt; // Is the entity valid, is the surface sky? if (!pEntity || (pTrace->surface.flags & SURF_SKY)) return; if (pTrace->fraction == 1.0) return; // don't decal nodraw surfaces if (pTrace->surface.flags & SURF_NODRAW) return; pEntity->ImpactTrace(pTrace, iDamageType, pCustomImpactName); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int UTIL_PrecacheDecal(const char *name, bool preload) { return effects->Draw_DecalIndexFromName((char*)name); } extern short g_sModelIndexSmoke; void UTIL_Smoke(const Vector &origin, const float scale, const float framerate) { CPVSFilter filter(origin); te->Smoke(filter, 0.0f, &origin, g_sModelIndexSmoke, scale, framerate); } void UTIL_SetOrigin(C_BaseEntity *entity, const Vector &vecOrigin) { entity->SetLocalOrigin(vecOrigin); } //#define PRECACHE_OTHER_ONCE // UNDONE: Do we need this to avoid doing too much of this? Measure startup times and see #if PRECACHE_OTHER_ONCE #include "utlsymbol.h" class CPrecacheOtherList : public CAutoServerSystem { public: virtual void LevelInitPreEntity(); virtual void LevelShutdownPostEntity(); bool AddOrMarkPrecached(const char *pClassname); private: CUtlSymbolTable m_list; }; void CPrecacheOtherList::LevelInitPreEntity() { m_list.RemoveAll(); } void CPrecacheOtherList::LevelShutdownPostEntity() { m_list.RemoveAll(); } //----------------------------------------------------------------------------- // Purpose: mark or add // Input : *pEntity - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CPrecacheOtherList::AddOrMarkPrecached(const char *pClassname) { CUtlSymbol sym = m_list.Find(pClassname); if (sym.IsValid()) return false; m_list.AddString(pClassname); return true; } CPrecacheOtherList g_PrecacheOtherList; #endif void UTIL_PrecacheOther(const char *szClassname) { #if PRECACHE_OTHER_ONCE // already done this one?, if not, mark as done if (!g_PrecacheOtherList.AddOrMarkPrecached(szClassname)) return; #endif // Client should only do this once entities are coming down from server!!! // Assert( engine->IsConnected() ); C_BaseEntity *pEntity = CreateEntityByName(szClassname); if (!pEntity) { Warning("NULL Ent in UTIL_PrecacheOther\n"); return; } if (pEntity) { pEntity->Precache(); } // Bye bye pEntity->Release(); } static csurface_t g_NullSurface = { "**empty**", 0 }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void UTIL_SetTrace(trace_t& trace, const Ray_t& ray, C_BaseEntity *ent, float fraction, int hitgroup, unsigned int contents, const Vector& normal, float intercept) { trace.startsolid = (fraction == 0.0f); trace.fraction = fraction; VectorCopy(ray.m_Start, trace.startpos); VectorMA(ray.m_Start, fraction, ray.m_Delta, trace.endpos); VectorCopy(normal, trace.plane.normal); trace.plane.dist = intercept; trace.m_pEnt = C_BaseEntity::Instance(ent); trace.hitgroup = hitgroup; trace.surface = g_NullSurface; trace.contents = contents; } //----------------------------------------------------------------------------- // Purpose: Get the x & y positions of a world position in screenspace // Returns true if it's onscreen //----------------------------------------------------------------------------- bool GetVectorInScreenSpace(Vector pos, int& iX, int& iY, Vector *vecOffset) { Vector screen; // Apply the offset, if one was specified if (vecOffset != NULL) pos += *vecOffset; // Transform to screen space int iFacing = ScreenTransform(pos, screen); iX = 0.5f * (1.0f + screen[0]) * ScreenWidth(); iY = 0.5f * (1.0f - screen[1]) * ScreenHeight(); // Make sure the player's facing it if (iFacing) { // We're actually facing away from the Target. Stomp the screen position. iX = -640; iY = -640; return false; } return true; } //----------------------------------------------------------------------------- // Purpose: Get the x & y positions of a world position in HUD space // Returns true if it's onscreen //----------------------------------------------------------------------------- bool GetVectorInHudSpace(Vector pos, int& iX, int& iY, Vector *vecOffset) { Vector screen; // Apply the offset, if one was specified if (vecOffset != NULL) pos += *vecOffset; // Transform to HUD space int iFacing = HudTransform(pos, screen); iX = 0.5f * (1.0f + screen[0]) * ScreenWidth(); iY = 0.5f * (1.0f - screen[1]) * ScreenHeight(); // Make sure the player's facing it if (iFacing) { // We're actually facing away from the Target. Stomp the screen position. iX = -640; iY = -640; return false; } return true; } //----------------------------------------------------------------------------- // Purpose: Get the x & y positions of an entity in screenspace // Returns true if it's onscreen //----------------------------------------------------------------------------- bool GetTargetInScreenSpace(C_BaseEntity *pTargetEntity, int& iX, int& iY, Vector *vecOffset) { return GetVectorInScreenSpace(pTargetEntity->WorldSpaceCenter(), iX, iY, vecOffset); } //----------------------------------------------------------------------------- // Purpose: Get the x & y positions of an entity in Vgui space // Returns true if it's onscreen //----------------------------------------------------------------------------- bool GetTargetInHudSpace(C_BaseEntity *pTargetEntity, int& iX, int& iY, Vector *vecOffset) { return GetVectorInHudSpace(pTargetEntity->WorldSpaceCenter(), iX, iY, vecOffset); } //----------------------------------------------------------------------------- // Purpose: // Input : *player - // msg_dest - // *msg_name - // *param1 - // *param2 - // *param3 - // *param4 - //----------------------------------------------------------------------------- void ClientPrint(C_BasePlayer *player, int msg_dest, const char *msg_name, const char *param1 /*= NULL*/, const char *param2 /*= NULL*/, const char *param3 /*= NULL*/, const char *param4 /*= NULL*/) { } //----------------------------------------------------------------------------- // class CFlaggedEntitiesEnum //----------------------------------------------------------------------------- // enumerate entities that match a set of edict flags into a static array class CFlaggedEntitiesEnum : public IPartitionEnumerator { public: CFlaggedEntitiesEnum(C_BaseEntity **pList, int listMax, int flagMask); // This gets called by the enumeration methods with each element // that passes the test. virtual IterationRetval_t EnumElement(IHandleEntity *pHandleEntity); int GetCount() { return m_count; } bool AddToList(C_BaseEntity *pEntity); private: C_BaseEntity **m_pList; int m_listMax; int m_flagMask; int m_count; }; CFlaggedEntitiesEnum::CFlaggedEntitiesEnum(C_BaseEntity **pList, int listMax, int flagMask) { m_pList = pList; m_listMax = listMax; m_flagMask = flagMask; m_count = 0; } bool CFlaggedEntitiesEnum::AddToList(C_BaseEntity *pEntity) { if (m_count >= m_listMax) return false; m_pList[m_count] = pEntity; m_count++; return true; } IterationRetval_t CFlaggedEntitiesEnum::EnumElement(IHandleEntity *pHandleEntity) { IClientEntity *pClientEntity = cl_entitylist->GetClientEntityFromHandle(pHandleEntity->GetRefEHandle()); C_BaseEntity *pEntity = pClientEntity ? pClientEntity->GetBaseEntity() : NULL; if (pEntity) { if (m_flagMask && !(pEntity->GetFlags() & m_flagMask)) // Does it meet the criteria? return ITERATION_CONTINUE; if (!AddToList(pEntity)) return ITERATION_STOP; } return ITERATION_CONTINUE; } //----------------------------------------------------------------------------- // Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted // Input : **pList - // listMax - // &mins - // &maxs - // flagMask - // Output : int //----------------------------------------------------------------------------- int UTIL_EntitiesInBox(C_BaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask, int partitionMask) { CFlaggedEntitiesEnum boxEnum(pList, listMax, flagMask); partition->EnumerateElementsInBox(partitionMask, mins, maxs, false, &boxEnum); return boxEnum.GetCount(); } //----------------------------------------------------------------------------- // Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted // Input : **pList - // listMax - // ¢er - // radius - // flagMask - // Output : int //----------------------------------------------------------------------------- int UTIL_EntitiesInSphere(C_BaseEntity **pList, int listMax, const Vector ¢er, float radius, int flagMask, int partitionMask) { CFlaggedEntitiesEnum sphereEnum(pList, listMax, flagMask); partition->EnumerateElementsInSphere(partitionMask, center, radius, false, &sphereEnum); return sphereEnum.GetCount(); } //----------------------------------------------------------------------------- // Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted // Input : **pList - // listMax - // &ray - // flagMask - // Output : int //----------------------------------------------------------------------------- int UTIL_EntitiesAlongRay(C_BaseEntity **pList, int listMax, const Ray_t &ray, int flagMask, int partitionMask) { CFlaggedEntitiesEnum rayEnum(pList, listMax, flagMask); partition->EnumerateElementsAlongRay(partitionMask, ray, false, &rayEnum); return rayEnum.GetCount(); } CEntitySphereQuery::CEntitySphereQuery(const Vector ¢er, float radius, int flagMask, int partitionMask) { m_listIndex = 0; m_listCount = UTIL_EntitiesInSphere(m_pList, ARRAYSIZE(m_pList), center, radius, flagMask, partitionMask); } CBaseEntity *CEntitySphereQuery::GetCurrentEntity() { if (m_listIndex < m_listCount) return m_pList[m_listIndex]; return NULL; } //----------------------------------------------------------------------------- // Purpose: Slightly modified strtok. Does not modify the input string. Does // not skip over more than one separator at a time. This allows parsing // strings where tokens between separators may or may not be present: // // Door01,,,0 would be parsed as "Door01" "" "" "0" // Door01,Open,,0 would be parsed as "Door01" "Open" "" "0" // // Input : token - Returns with a token, or zero length if the token was missing. // str - String to parse. // sep - Character to use as separator. UNDONE: allow multiple separator chars // Output : Returns a pointer to the next token to be parsed. //----------------------------------------------------------------------------- const char *nexttoken(char *token, const char *str, char sep) { if ((str == NULL) || (*str == '\0')) { *token = '\0'; return(NULL); } // // Copy everything up to the first separator into the return buffer. // Do not include separators in the return buffer. // while ((*str != sep) && (*str != '\0')) { *token++ = *str++; } *token = '\0'; // // Advance the pointer unless we hit the end of the input string. // if (*str == '\0') { return(str); } return(++str); } //----------------------------------------------------------------------------- // Purpose: // Input : font - // *str - // Output : int //----------------------------------------------------------------------------- int UTIL_ComputeStringWidth(vgui::HFont& font, const char *str) { float pixels = 0; const char *p = str; const char *pAfter = p + 1; const char *pBefore = "\0"; while (*p) { #if USE_GETKERNEDCHARWIDTH float wide, abcA; vgui::surface()->GetKernedCharWidth(font, *p, *pBefore, *pAfter, wide, abcA); pixels += wide; #else pixels += vgui::surface()->GetCharacterWidth(font, *p); #endif pBefore = p; p++; if (*p) pAfter = p + 1; else pAfter = "\0"; } return (int)ceil(pixels); } //----------------------------------------------------------------------------- // Purpose: // Input : font - // *str - // Output : int //----------------------------------------------------------------------------- int UTIL_ComputeStringWidth(vgui::HFont& font, const wchar_t *str) { float pixels = 0; const wchar_t *p = str; const wchar_t *pAfter = p + 1; const wchar_t *pBefore = L"\0"; while (*p) { #if USE_GETKERNEDCHARWIDTH float wide, abcA; vgui::surface()->GetKernedCharWidth(font, *p, *pBefore, *pAfter, wide, abcA); pixels += wide; #else pixels += vgui::surface()->GetCharacterWidth(font, *p); #endif pBefore = p; p++; if (*p) pAfter = p + 1; else pAfter = L"\0"; } return (int)ceil(pixels); } //----------------------------------------------------------------------------- // Purpose: Scans player names //Passes the player name to be checked in a KeyValues pointer //with the keyname "name" // - replaces '&' with '&&' so they will draw in the scoreboard // - replaces '#' at the start of the name with '*' //----------------------------------------------------------------------------- void UTIL_MakeSafeName(const char *oldName, char *newName, int newNameBufSize) { Assert(newNameBufSize >= sizeof(newName[0])); int newpos = 0; for (const char *p = oldName; *p != 0 && newpos < newNameBufSize - 1; p++) { //check for a '#' char at the beginning if (p == oldName && *p == '#') { newName[newpos] = '*'; newpos++; } else if (*p == '%') { // remove % chars newName[newpos] = '*'; newpos++; } else if (*p == '&') { //insert another & after this one if (newpos + 2 < newNameBufSize) { newName[newpos] = '&'; newName[newpos + 1] = '&'; newpos += 2; } } else { newName[newpos] = *p; newpos++; } } newName[newpos] = 0; } //----------------------------------------------------------------------------- // Purpose: Scans player names and replaces characters that vgui won't // display properly // Input : *oldName - player name to be fixed up // Output : *char - static buffer with the safe name //----------------------------------------------------------------------------- const char * UTIL_SafeName(const char *oldName) { static char safeName[MAX_PLAYER_NAME_LENGTH * 2 + 1]; UTIL_MakeSafeName(oldName, safeName, sizeof(safeName)); return safeName; } //----------------------------------------------------------------------------- // Purpose: Looks up key bindings for commands and replaces them in string. // %% will get replaced with its bound control, e.g. %attack2% // Input buffer sizes are in bytes rather than unicode character count // for consistency with other APIs. If inbufsizebytes is 0 a NULL-terminated // input buffer is assumed, or you can pass the size of the input buffer if // not NULL-terminated. //----------------------------------------------------------------------------- void UTIL_ReplaceKeyBindings(const wchar_t *inbuf, int inbufsizebytes, OUT_Z_BYTECAP(outbufsizebytes) wchar_t *outbuf, int outbufsizebytes) { Assert(outbufsizebytes >= sizeof(outbuf[0])); // copy to a new buf if there are vars outbuf[0] = 0; if (!inbuf || !inbuf[0]) return; int pos = 0; const wchar_t *inbufend = NULL; if (inbufsizebytes > 0) { inbufend = inbuf + (inbufsizebytes / 2); } while (inbuf != inbufend && *inbuf != 0) { // check for variables if (*inbuf == '%') { ++inbuf; const wchar_t *end = wcschr(inbuf, '%'); if (end && (end != inbuf)) // make sure we handle %% in the string, which should be treated in the output as % { wchar_t token[64]; wcsncpy(token, inbuf, end - inbuf); token[end - inbuf] = 0; inbuf += end - inbuf; // lookup key names char binding[64]; g_pVGuiLocalize->ConvertUnicodeToANSI(token, binding, sizeof(binding)); const char *key = engine->Key_LookupBinding(*binding == '+' ? binding + 1 : binding); if (!key) { key = IsX360() ? "" : "< not bound >"; } //!! change some key names into better names char friendlyName[64]; bool bAddBrackets = false; if (IsX360()) { if (!key || !key[0]) { Q_snprintf(friendlyName, sizeof(friendlyName), "#GameUI_None"); bAddBrackets = true; } else { Q_snprintf(friendlyName, sizeof(friendlyName), "#GameUI_KeyNames_%s", key); } } else { Q_snprintf(friendlyName, sizeof(friendlyName), "%s", key); } Q_strupr(friendlyName); wchar_t *locName = g_pVGuiLocalize->Find(friendlyName); if (!locName || wcslen(locName) <= 0) { g_pVGuiLocalize->ConvertANSIToUnicode(friendlyName, token, sizeof(token)); outbuf[pos] = '\0'; wcscat(outbuf, token); pos += wcslen(token); } else { outbuf[pos] = '\0'; if (bAddBrackets) { wcscat(outbuf, L"["); pos += 1; } wcscat(outbuf, locName); pos += wcslen(locName); if (bAddBrackets) { wcscat(outbuf, L"]"); pos += 1; } } } else { outbuf[pos] = *inbuf; ++pos; } } else { outbuf[pos] = *inbuf; ++pos; } ++inbuf; } outbuf[pos] = '\0'; } //----------------------------------------------------------------------------- // Purpose: // Input : *filename - // *pLength - // Output : byte //----------------------------------------------------------------------------- byte *UTIL_LoadFileForMe(const char *filename, int *pLength) { byte *buffer; FileHandle_t file; file = filesystem->Open(filename, "rb", "GAME"); if (FILESYSTEM_INVALID_HANDLE == file) { if (pLength) *pLength = 0; return NULL; } int size = filesystem->Size(file); buffer = new byte[size + 1]; if (!buffer) { Warning("UTIL_LoadFileForMe: Couldn't allocate buffer of size %i for file %s\n", size + 1, filename); filesystem->Close(file); return NULL; } filesystem->Read(buffer, size, file); filesystem->Close(file); // Ensure null terminator buffer[size] = 0; if (pLength) { *pLength = size; } return buffer; } //----------------------------------------------------------------------------- // Purpose: // Input : *buffer - //----------------------------------------------------------------------------- void UTIL_FreeFile(byte *buffer) { delete[] buffer; } //----------------------------------------------------------------------------- // Compute distance fade //----------------------------------------------------------------------------- static unsigned char ComputeDistanceFade(C_BaseEntity *pEntity, float flMinDist, float flMaxDist) { if ((flMinDist <= 0) && (flMaxDist <= 0)) return 255; if (flMinDist > flMaxDist) { ::V_swap(flMinDist, flMaxDist); } // If a negative value is provided for the min fade distance, then base it off the max. if (flMinDist < 0) { flMinDist = flMaxDist - 400; if (flMinDist < 0) { flMinDist = 0; } } flMinDist *= flMinDist; flMaxDist *= flMaxDist; float flCurrentDistanceSq = CurrentViewOrigin().DistToSqr(pEntity->WorldSpaceCenter()); C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer(); if (pLocal) { float flDistFactor = pLocal->GetFOVDistanceAdjustFactor(); flCurrentDistanceSq *= flDistFactor * flDistFactor; } // If I'm inside the minimum range than don't resort to alpha trickery if (flCurrentDistanceSq <= flMinDist) return 255; if (flCurrentDistanceSq >= flMaxDist) return 0; // NOTE: Because of the if-checks above, flMinDist != flMinDist here float flFalloffFactor = 255.0f / (flMaxDist - flMinDist); int nAlpha = flFalloffFactor * (flMaxDist - flCurrentDistanceSq); return clamp(nAlpha, 0, 255); } //----------------------------------------------------------------------------- // Compute fade amount //----------------------------------------------------------------------------- unsigned char UTIL_ComputeEntityFade(C_BaseEntity *pEntity, float flMinDist, float flMaxDist, float flFadeScale) { unsigned char nAlpha = 255; // If we're taking devshots, don't fade props at all if (g_MakingDevShots || cl_leveloverview.GetFloat() > 0) return 255; #ifdef _DEBUG if (r_FadeProps.GetBool()) #endif { nAlpha = ComputeDistanceFade(pEntity, flMinDist, flMaxDist); // NOTE: This computation for the center + radius is invalid! // The center of the sphere is at the center of the OBB, which is not necessarily // at the render origin. But it should be close enough. Vector vecMins, vecMaxs; pEntity->GetRenderBounds(vecMins, vecMaxs); float flRadius = vecMins.DistTo(vecMaxs) * 0.5f; Vector vecAbsCenter; if (modelinfo->GetModelType(pEntity->GetModel()) == mod_brush) { Vector vecRenderMins, vecRenderMaxs; pEntity->GetRenderBoundsWorldspace(vecRenderMins, vecRenderMaxs); VectorAdd(vecRenderMins, vecRenderMaxs, vecAbsCenter); vecAbsCenter *= 0.5f; } else { vecAbsCenter = pEntity->GetRenderOrigin(); } unsigned char nGlobalAlpha = IsXbox() ? 255 : modelinfo->ComputeLevelScreenFade(vecAbsCenter, flRadius, flFadeScale); unsigned char nDistAlpha; if (!engine->IsLevelMainMenuBackground()) { nDistAlpha = modelinfo->ComputeViewScreenFade(vecAbsCenter, flRadius, flFadeScale); } else { nDistAlpha = 255; } if (nDistAlpha < nGlobalAlpha) { nGlobalAlpha = nDistAlpha; } if (nGlobalAlpha < nAlpha) { nAlpha = nGlobalAlpha; } } return nAlpha; } //----------------------------------------------------------------------------- // Purpose: Given a vector, clamps the scalar axes to MAX_COORD_FLOAT ranges from worldsize.h // Input : *pVecPos - //----------------------------------------------------------------------------- void UTIL_BoundToWorldSize(Vector *pVecPos) { Assert(pVecPos); for (int i = 0; i < 3; ++i) { (*pVecPos)[i] = clamp((*pVecPos)[i], MIN_COORD_FLOAT, MAX_COORD_FLOAT); } } #ifdef _X360 #define MAP_KEY_FILE_DIR "cfg" #else #define MAP_KEY_FILE_DIR "media" #endif //----------------------------------------------------------------------------- // Purpose: Returns the filename to count map loads in //----------------------------------------------------------------------------- bool UTIL_GetMapLoadCountFileName(const char *pszFilePrependName, char *pszBuffer, int iBuflen) { if (IsX360()) { #ifdef _X360 if (XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED) return false; #endif } if (IsX360()) { Q_snprintf(pszBuffer, iBuflen, "%s:/%s", MAP_KEY_FILE_DIR, pszFilePrependName); } else { Q_snprintf(pszBuffer, iBuflen, "%s/%s", MAP_KEY_FILE_DIR, pszFilePrependName); } return true; } #ifdef TF_CLIENT_DLL #define MAP_KEY_FILE "viewed.res" #else #define MAP_KEY_FILE "mapkeys.res" #endif void UTIL_IncrementMapKey(const char *pszCustomKey) { if (!pszCustomKey) return; char szFilename[_MAX_PATH]; if (!UTIL_GetMapLoadCountFileName(MAP_KEY_FILE, szFilename, _MAX_PATH)) return; int iCount = 1; KeyValues *kvMapLoadFile = new KeyValues(MAP_KEY_FILE); if (kvMapLoadFile) { kvMapLoadFile->LoadFromFile(g_pFullFileSystem, szFilename, "MOD"); char mapname[MAX_MAP_NAME]; Q_FileBase(engine->GetLevelName(), mapname, sizeof(mapname)); Q_strlower(mapname); // Increment existing, or add a new one KeyValues *pMapKey = kvMapLoadFile->FindKey(mapname); if (pMapKey) { iCount = pMapKey->GetInt(pszCustomKey, 0) + 1; pMapKey->SetInt(pszCustomKey, iCount); } else { KeyValues *pNewKey = new KeyValues(mapname); if (pNewKey) { pNewKey->SetString(pszCustomKey, "1"); kvMapLoadFile->AddSubKey(pNewKey); } } // Write it out // force create this directory incase it doesn't exist filesystem->CreateDirHierarchy(MAP_KEY_FILE_DIR, "MOD"); CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER); kvMapLoadFile->RecursiveSaveToFile(buf, 0); g_pFullFileSystem->WriteFile(szFilename, "MOD", buf); kvMapLoadFile->deleteThis(); } if (IsX360()) { #ifdef _X360 xboxsystem->FinishContainerWrites(); #endif } } int UTIL_GetMapKeyCount(const char *pszCustomKey) { if (!pszCustomKey) return 0; char szFilename[_MAX_PATH]; if (!UTIL_GetMapLoadCountFileName(MAP_KEY_FILE, szFilename, _MAX_PATH)) return 0; int iCount = 0; KeyValues *kvMapLoadFile = new KeyValues(MAP_KEY_FILE); if (kvMapLoadFile) { // create an empty file if none exists if (!g_pFullFileSystem->FileExists(szFilename, "MOD")) { // force create this directory incase it doesn't exist filesystem->CreateDirHierarchy(MAP_KEY_FILE_DIR, "MOD"); CUtlBuffer buf(0, 0, CUtlBuffer::TEXT_BUFFER); g_pFullFileSystem->WriteFile(szFilename, "MOD", buf); } kvMapLoadFile->LoadFromFile(g_pFullFileSystem, szFilename, "MOD"); char mapname[MAX_MAP_NAME]; Q_FileBase(engine->GetLevelName(), mapname, sizeof(mapname)); Q_strlower(mapname); KeyValues *pMapKey = kvMapLoadFile->FindKey(mapname); if (pMapKey) { iCount = pMapKey->GetInt(pszCustomKey); } kvMapLoadFile->deleteThis(); } return iCount; } bool UTIL_HasLoadedAnyMap() { char szFilename[_MAX_PATH]; if (!UTIL_GetMapLoadCountFileName(MAP_KEY_FILE, szFilename, _MAX_PATH)) return false; return g_pFullFileSystem->FileExists(szFilename, "MOD"); }