etqw-sdk/source/game/guis/UICrosshairInfo.cpp

1126 lines
36 KiB
C++

// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "UserInterfaceManager.h"
#include "UIWindow.h"
#include "UICrosshairInfo.h"
#include "UserInterfaceLocal.h"
#include "../CrosshairInfo.h"
#include "../Player.h"
#include "../misc/WorldToScreen.h"
#include "../script/Script_Helper.h"
#include "../roles/WayPointManager.h"
#include "../ContentMask.h"
#include "../../sys/sys_local.h"
idCVar g_damageIndicatorFadeTime( "g_damageIndicatorFadeTime", "2.0", CVAR_FLOAT | CVAR_GAME | CVAR_NOCHEAT, "number of seconds that a damage indicator stays visible" );
idCVar g_damageIndicatorWidth( "g_damageIndicatorWidth", "256", CVAR_FLOAT | CVAR_GAME | CVAR_NOCHEAT, "width of the damage indicators" );
idCVar g_damageIndicatorHeight( "g_damageIndicatorHeight", "128", CVAR_FLOAT | CVAR_GAME | CVAR_NOCHEAT, "height of the damage indicators" );
idCVar g_damageIndicatorColor( "g_damageIndicatorColor", "1 0 0", CVAR_GAME | CVAR_NOCHEAT, "color of the damage indicators" );
idCVar g_damageIndicatorAlphaScale( "g_damageIndicatorAlphaScale", "0.3", CVAR_FLOAT | CVAR_GAME | CVAR_NOCHEAT, "alpha of the damage indicators" );
idCVar g_repairIndicatorColor( "g_repairIndicatorColor", "0 1 0", CVAR_GAME | CVAR_NOCHEAT, "color of the repair indicators" );
idCVar g_repairIndicatorAlphaScale( "g_repairIndicatorAlphaScale", "0.3", CVAR_FLOAT | CVAR_GAME | CVAR_NOCHEAT, "alpha of the repair indicators" );
idCVar g_waypointAlphaScale( "g_waypointAlphaScale", "0.7", CVAR_GAME | CVAR_FLOAT | CVAR_ARCHIVE | CVAR_PROFILE, "alpha to apply to world-based objective icons" );
idCVar g_showWayPoints( "g_showWayPoints", "1", CVAR_GAME | CVAR_BOOL | CVAR_ARCHIVE | CVAR_PROFILE, "show or hide world-based objective icons" );
idCVar g_waypointSizeMin( "g_waypointSizeMin", "16", CVAR_GAME | CVAR_FLOAT | CVAR_ARCHIVE | CVAR_PROFILE, "min world-view icon size" );
idCVar g_waypointSizeMax( "g_waypointSizeMax", "32", CVAR_GAME | CVAR_FLOAT | CVAR_ARCHIVE | CVAR_PROFILE, "max world-view icon size" );
idCVar g_waypointDistanceMin( "g_waypointDistanceMin", "512", CVAR_GAME | CVAR_FLOAT | CVAR_ARCHIVE | CVAR_PROFILE, "max distance at which to show min icon size" );
idCVar g_waypointDistanceMax( "g_waypointDistanceMax", "3084", CVAR_GAME | CVAR_FLOAT | CVAR_ARCHIVE | CVAR_PROFILE, "min distance at which to show max icon size" );
using namespace sdProperties;
const char sdUITemplateFunctionInstance_IdentifierCrosshairInfo[] = "sdUICrosshairInfoFunction";
idHashMap< sdUICrosshairInfo::CrosshairInfoTemplateFunction* > sdUICrosshairInfo::crosshairInfoFunctions;
const int sdUICrosshairInfo::lineTextScale = 12;
static const float HIGHLIGHT_TIME = 2000.0f;
/*
================
sdUICrosshairInfo::sdUICrosshairInfo
================
*/
sdUICrosshairInfo::sdUICrosshairInfo( void ) {
crosshairParts.SetNum( CP_MAX );
scriptState.GetProperties().RegisterProperty( "barMaterial", barMaterial );
scriptState.GetProperties().RegisterProperty( "barFillMaterial", barFillMaterial );
scriptState.GetProperties().RegisterProperty( "barLineMaterial", barLineMaterial );
scriptState.GetProperties().RegisterProperty( "barLineColor", barLineColor );
scriptState.GetProperties().RegisterProperty( "bracketBaseColor", bracketBaseColor );
scriptState.GetProperties().RegisterProperty( "bracketLeftMaterial", bracketLeftMaterial );
scriptState.GetProperties().RegisterProperty( "bracketRightMaterial", bracketRightMaterial );
scriptState.GetProperties().RegisterProperty( "damageMaterial", damageMaterial );
scriptState.GetProperties().RegisterProperty( "crosshairInfoFlags", crosshairInfoFlags );
barLineColor = colorWhite;
bracketBaseColor = colorWhite;
UI_ADD_STR_CALLBACK( barMaterial, sdUICrosshairInfo, OnBarMaterialChanged );
UI_ADD_STR_CALLBACK( barFillMaterial, sdUICrosshairInfo, OnBarFillMaterialChanged );
UI_ADD_STR_CALLBACK( barLineMaterial, sdUICrosshairInfo, OnBarLineMaterialChanged );
UI_ADD_STR_CALLBACK( bracketLeftMaterial, sdUICrosshairInfo, OnLeftBracketMaterialChanged );
UI_ADD_STR_CALLBACK( bracketRightMaterial, sdUICrosshairInfo, OnRightBracketMaterialChanged );
UI_ADD_STR_CALLBACK( damageMaterial, sdUICrosshairInfo, OnDamageMaterialChanged );
waypointTextFadeTime = 0;
bracketFadeTime = 0;
dockedIcons.SetNum( 6 );
}
/*
================
sdUICrosshairInfo::~sdUICrosshairInfo
================
*/
sdUICrosshairInfo::~sdUICrosshairInfo( void ) {
DisconnectGlobalCallbacks();
}
/*
============
sdUICrosshairInfo::GetRepeaterCrosshairInfo
============
*/
const sdCrosshairInfo& sdUICrosshairInfo::GetRepeaterCrosshairInfo( void ) {
return gameLocal.playerView.CalcRepeaterCrosshairInfo();
}
/*
============
sdUICrosshairInfo::DrawCrosshairInfo
============
*/
void sdUICrosshairInfo::DrawCrosshairInfo( idPlayer* player ) {
const float width = SCREEN_WIDTH * 1.0f / deviceContext->GetAspectRatioCorrection();
const sdCrosshairInfo& info = gameLocal.serverIsRepeater ? GetRepeaterCrosshairInfo() : player->GetCrosshairInfoDirect();
if( info.IsValid() && ( TestCrosshairInfoFlag( CF_CROSSHAIR ) || g_radialMenuStyle.GetInteger() == 1 ) ) {
float w, h;
idVec4 color;
idVec2 point( ( width * 0.5f ), ( SCREEN_HEIGHT * 0.5f ) + 30.0f );
for( int i = 0; i < info.GetNumLines(); i++ ) {
const chInfoLine_t& line = info.GetLine( i );
switch( line.type ) {
case CI_TEXT: {
sdBounds2D rect( 0.0f, point.y, width, lineTextHeight );
color = line.foreColor;
color[ 3 ] *= info.GetAlpha();
sdUIWindow::DrawText( line.text.c_str(), color, lineTextScale, rect, cachedFontHandle, DTF_CENTER | DTF_BOTTOM | DTF_SINGLELINE | DTF_DROPSHADOW );
point.y += lineTextHeight + 4.0f;
break;
}
case CI_BAR: {
w = line.xy.x;
h = line.xy.y;
color = borderColor;//line.backColor;
color[ 3 ] *= info.GetAlpha();
idVec4 rect( point.x - ( w * 0.5f ), point.y, w, h );
DrawThreeHorizontalParts( rect, color, vec2_one, crosshairParts[ CP_BAR_FILL_LEFT ], crosshairParts[ CP_BAR_FILL_CENTER ], crosshairParts[ CP_BAR_FILL_RIGHT ] );
color = line.foreColor;
color[ 3 ] *= info.GetAlpha();
sdBounds2D clipRect( rect.x, rect.y, rect.z * line.frac, rect.w );
deviceContext->PushClipRect( clipRect );
DrawHorizontalProgress( rect, color, vec2_one, crosshairParts[ CP_BAR_LEFT ], crosshairParts[ CP_BAR_CENTER ], crosshairParts[ CP_BAR_RIGHT ] );
deviceContext->PopClipRect();
color = barLineColor;
color[ 3 ] *= info.GetAlpha();
DrawThreeHorizontalParts( rect, color, vec2_one, crosshairParts[ CP_BAR_LINE_LEFT ], crosshairParts[ CP_BAR_LINE_CENTER ], crosshairParts[ CP_BAR_LINE_RIGHT ] );
point.y += h + 4.0f;
break;
}
case CI_IMAGE: {
color = line.foreColor;
color[ 3 ] *= info.GetAlpha();
deviceContext->DrawMaterial( point.x - ( line.xy.x * 0.5f ), point.y, line.xy.x, line.xy.y, line.material, color );
point.y += line.xy.y + 4.0f;
break;
}
}
}
}
}
/*
============
sdUICrosshairInfo::DrawDamageIndicators
============
*/
void sdUICrosshairInfo::DrawDamageIndicators( idPlayer* player ) {
const float width = SCREEN_WIDTH * 1.0f / deviceContext->GetAspectRatioCorrection();
static idWinding2D drawWinding;
float w = g_damageIndicatorWidth.GetFloat();
float h = g_damageIndicatorHeight.GetFloat();
float x = -( w * 0.5f );
float y = -( h * 0.5f ) - g_damageIndicatorHeight.GetFloat();
float viewYaw = player->renderView.viewaxis.ToAngles().yaw;
float fadeTime = SEC2MS( g_damageIndicatorFadeTime.GetFloat() );
idMat2 matrix;
for( int i = 0; i < player->damageEvents.Num(); i++ ) {
const idPlayer::damageEvent_t& event = player->damageEvents[ i ];
if( event.hitTime <= 0 ) {
continue;
}
idVec4 color;
if ( event.hitDamage > 0 ) {
color.ToVec3() = sdTypeFromString< idVec3 >( g_damageIndicatorColor.GetString() );
} else {
color.ToVec3() = sdTypeFromString< idVec3 >( g_repairIndicatorColor.GetString() );
}
color.w = 1.0f - static_cast< float > ( gameLocal.time - event.hitTime ) / fadeTime;
if ( event.hitDamage > 0 ) {
color.w *= g_damageIndicatorAlphaScale.GetFloat();
} else {
color.w *= g_repairIndicatorAlphaScale.GetFloat();
}
if( color.w <= 0.0f || color.w > 1.0f ) {
continue;
}
uiDrawPart_t* part = NULL;
if( event.hitDamage <= 20.0f ) {
part = &crosshairParts[ CP_SMALL_DAMAGE ];
} else if( event.hitDamage <= 40.0f ) {
part = &crosshairParts[ CP_MEDIUM_DAMAGE ];
} else {
part = &crosshairParts[ CP_LARGE_DAMAGE ];
}
drawWinding.Clear();
drawWinding.AddPoint( x, y, part->mi.st0.x, part->mi.st0.y );
drawWinding.AddPoint( x + w, y, part->mi.st1.x, part->mi.st0.y );
drawWinding.AddPoint( x + w, y + h, part->mi.st1.x, part->mi.st1.y );
drawWinding.AddPoint( x, y + h, part->mi.st0.x, part->mi.st1.y );
float angle = event.hitAngle;
if ( event.updateDirection ) {
angle -= viewYaw;
}
matrix.Rotation( DEG2RAD( angle ) );
for( int i = 0; i < drawWinding.GetNumPoints(); i++ ) {
idVec2& pt = drawWinding[ i ];
pt *= matrix;
pt.x += width * 0.5f;
pt.y += SCREEN_HEIGHT * 0.5f;
}
deviceContext->DrawWindingMaterial( drawWinding, part->mi.material, color );
}
}
/*
============
sdUICrosshairInfo::DrawLocal
============
*/
void sdUICrosshairInfo::DrawLocal() {
if( g_showCrosshairInfo.GetInteger() != 1 ) {
return;
}
idPlayer* player = gameLocal.GetLocalViewPlayer();
if ( player == NULL ) {
if ( gameLocal.serverIsRepeater ) {
DrawCrosshairInfo( NULL );
}
return;
}
idEntity* entity = DrawContextEntity( player );
DrawWayPoints( entity );
DrawEntityIcons();
DrawPlayerIcons2D();
ClearInactiveDockIcons();
DrawDockedIcons();
DrawCrosshairInfo( player );
DrawDamageIndicators( player );
}
/*
============
sdUICrosshairInfo::ApplyLayout
============
*/
void sdUICrosshairInfo::ApplyLayout() {
if( windowState.recalculateLayout ) {
BeginLayout();
lineTextHeight = deviceContext->GetFontHeight( cachedFontHandle, lineTextScale );
EndLayout();
}
sdUIObject::ApplyLayout();
}
/*
============
sdUICrosshairInfo::FindFunction
============
*/
const sdUICrosshairInfo::CrosshairInfoTemplateFunction* sdUICrosshairInfo::FindFunction( const char* name ) {
sdUICrosshairInfo::CrosshairInfoTemplateFunction** ptr;
return crosshairInfoFunctions.Get( name, &ptr ) ? *ptr : NULL;
}
/*
============
sdUICrosshairInfo::GetFunction
============
*/
sdUIFunctionInstance* sdUICrosshairInfo::GetFunction( const char* name ) {
const CrosshairInfoTemplateFunction* function = sdUICrosshairInfo::FindFunction( name );
if ( !function ) {
return sdUIWindow::GetFunction( name );
}
return new sdUITemplateFunctionInstance< sdUICrosshairInfo, sdUITemplateFunctionInstance_IdentifierCrosshairInfo >( this, function );
}
/*
============
sdUICrosshairInfo::RunNamedMethod
============
*/
bool sdUICrosshairInfo::RunNamedMethod( const char* name, sdUIFunctionStack& stack ) {
const CrosshairInfoTemplateFunction* func = sdUICrosshairInfo::FindFunction( name );
if ( !func ) {
return sdUIWindow::RunNamedMethod( name, stack );
}
CALL_MEMBER_FN_PTR( this, func->GetFunction() )( stack );
return true;
}
/*
============
sdUICrosshairInfo::InitFunctions
============
*/
void sdUICrosshairInfo::InitFunctions() {
sdDeclGUI::AddDefine( va( "CF_CROSSHAIR %i", CF_CROSSHAIR ) );
sdDeclGUI::AddDefine( va( "CF_PLAYER_ICONS %i", CF_PLAYER_ICONS ) );
sdDeclGUI::AddDefine( va( "CF_TASKS %i", CF_TASKS ) );
sdDeclGUI::AddDefine( va( "CF_OBJ_MIS %i", CF_OBJ_MIS ) );
}
/*
============
sdUICrosshairInfo::OnBarMaterialChanged
============
*/
void sdUICrosshairInfo::OnBarMaterialChanged( const idStr& oldValue, const idStr& newValue ) {
GetUI()->SetupPart( crosshairParts[ CP_BAR_LEFT ], "b", newValue );
GetUI()->SetupPart( crosshairParts[ CP_BAR_CENTER ], "c", newValue );
GetUI()->SetupPart( crosshairParts[ CP_BAR_RIGHT ], "e", newValue );
}
/*
============
sdUICrosshairInfo::OnBarFillMaterialChanged
============
*/
void sdUICrosshairInfo::OnBarFillMaterialChanged( const idStr& oldValue, const idStr& newValue ) {
GetUI()->SetupPart( crosshairParts[ CP_BAR_FILL_LEFT ], "l", newValue );
GetUI()->SetupPart( crosshairParts[ CP_BAR_FILL_CENTER ], "c", newValue );
GetUI()->SetupPart( crosshairParts[ CP_BAR_FILL_RIGHT ], "r", newValue );
}
/*
============
sdUICrosshairInfo::OnBarLineMaterialChanged
============
*/
void sdUICrosshairInfo::OnBarLineMaterialChanged( const idStr& oldValue, const idStr& newValue ) {
GetUI()->SetupPart( crosshairParts[ CP_BAR_LINE_LEFT ], "l", newValue );
GetUI()->SetupPart( crosshairParts[ CP_BAR_LINE_CENTER ], "c", newValue );
GetUI()->SetupPart( crosshairParts[ CP_BAR_LINE_RIGHT ], "r", newValue );
}
/*
============
sdUICrosshairInfo::EndLevelLoad
============
*/
void sdUICrosshairInfo::EndLevelLoad() {
sdUIWindow::EndLevelLoad();
sdUserInterfaceLocal::LookupPartSizes( crosshairParts.Begin(), crosshairParts.Num() );
}
/*
============
sdUICrosshairInfo::OnDamageMaterialChanged
============
*/
void sdUICrosshairInfo::OnDamageMaterialChanged( const idStr& oldValue, const idStr& newValue ) {
GetUI()->SetupPart( crosshairParts[ CP_SMALL_DAMAGE ], "s", newValue );
GetUI()->SetupPart( crosshairParts[ CP_MEDIUM_DAMAGE ], "m", newValue );
GetUI()->SetupPart( crosshairParts[ CP_LARGE_DAMAGE ], "l", newValue );
}
/*
============
sdUICrosshairInfo::OnLeftBracketMaterialChanged
============
*/
void sdUICrosshairInfo::OnLeftBracketMaterialChanged( const idStr& oldValue, const idStr& newValue ) {
GetUI()->SetupPart( crosshairParts[ CP_L_BRACKET_TOP ], "t", newValue );
GetUI()->SetupPart( crosshairParts[ CP_L_BRACKET_CENTER ], "c", newValue );
GetUI()->SetupPart( crosshairParts[ CP_L_BRACKET_BOTTOM ], "b", newValue );
}
/*
============
sdUICrosshairInfo::OnRightBracketMaterialChanged
============
*/
void sdUICrosshairInfo::OnRightBracketMaterialChanged( const idStr& oldValue, const idStr& newValue ) {
GetUI()->SetupPart( crosshairParts[ CP_R_BRACKET_TOP ], "t", newValue );
GetUI()->SetupPart( crosshairParts[ CP_R_BRACKET_CENTER ], "c", newValue );
GetUI()->SetupPart( crosshairParts[ CP_R_BRACKET_BOTTOM ], "b", newValue );
}
/*
===============
sdUICrosshairInfo::DrawEntityIcons
==============
*/
void sdUICrosshairInfo::DrawEntityIcons( void ) {
idPlayer* viewPlayer = gameLocal.GetLocalViewPlayer();
if ( viewPlayer == NULL ) {
return;
}
// TODO: Add crosshair info flag like for player icons?
const float width = SCREEN_WIDTH * 1.0f / deviceContext->GetAspectRatioCorrection();
sdWorldToScreenConverter converter( gameLocal.playerView.GetCurrentView() );
// find out how many icons we need to draw
int numIcons = 0;
for ( idLinkList< idEntity >* node = gameLocal.GetIconEntities(); node; node = node->NextNode() ) {
idEntity* ent = node->Owner();
assert( ent->GetDisplayIconInterface() );
if ( ent->GetDisplayIconInterface()->HasIcon( viewPlayer, converter ) ) {
numIcons++;
}
}
if ( numIcons == 0 ) {
return;
}
// allocate temporary storage for the icons & fill them in
sdEntityDisplayIconInfo* icons = ( sdEntityDisplayIconInfo* )_alloca16( numIcons * sizeof( sdEntityDisplayIconInfo ) );
int iconUpto = 0;
for ( idLinkList< idEntity >* node = gameLocal.GetIconEntities(); node; node = node->NextNode() ) {
idEntity* ent = node->Owner();
assert( ent->GetDisplayIconInterface() );
if ( ent->GetDisplayIconInterface()->GetEntityDisplayIconInfo( viewPlayer, converter, icons[ iconUpto ] ) ) {
iconUpto++;
}
}
// sort them by distance
sdEntityDisplayIconInfo** sortedIcons = ( sdEntityDisplayIconInfo** )_alloca16( numIcons * sizeof( sdEntityDisplayIconInfo* ) );
for ( int i = 0; i < numIcons; i++ ) {
sortedIcons[ i ] = &icons[ i ];
}
sdQuickSort( sortedIcons, sortedIcons + numIcons, sdEntityDisplayIconInfo::SortByDistance );
// now we can draw
for ( int i = 0; i < numIcons; i++ ) {
sdEntityDisplayIconInfo* icon = sortedIcons[ i ];
bool draw = true;
const float MARGIN_OFFSET = ( width * 0.0125f );
const float LEFT_MARGIN = MARGIN_OFFSET;
const float RIGHT_MARGIN = width - MARGIN_OFFSET;
if ( icon->origin.x + icon->size.x <= LEFT_MARGIN || icon->origin.x - icon->size.x >= RIGHT_MARGIN ) {
draw = false;
}
if ( draw ) {
float height = icon->origin.y;
idVec2 screenPos( icon->origin.x - ( icon->size.x * 0.5f ), height - icon->size.y );
deviceContext->DrawMaterial( screenPos.x, screenPos.y, icon->size.x, icon->size.y, icon->material, icon->color );
}
}
}
/*
===============
sdUICrosshairInfo::DrawPlayerIcons2D
==============
*/
void sdUICrosshairInfo::DrawPlayerIcons2D( void ) {
idPlayer* viewPlayer = gameLocal.GetLocalViewPlayer();
if ( viewPlayer == NULL ) {
return;
}
if ( viewPlayer->IsBeingBriefed() ) {
return;
}
if ( !TestCrosshairInfoFlag( CF_PLAYER_ICONS ) ) {
return;
}
const float width = SCREEN_WIDTH * 1.0f / deviceContext->GetAspectRatioCorrection();
sdWorldToScreenConverter converter( gameLocal.playerView.GetCurrentView() );
// fill the icons list up
sdPlayerDisplayIconList playerIcons;
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* other = gameLocal.GetClient( i );
if ( other == NULL || other->IsInLimbo() ) {
continue;
}
if ( !pm_thirdPerson.GetBool() && viewPlayer == other ) {
continue;
}
sdPlayerDisplayIcon icon;
if ( other->GetPlayerIcon( viewPlayer, icon, converter ) ) {
playerIcons.Append( icon );
}
}
// sort the list by distance
sdPlayerDisplayIconPtrList sortedPlayerIcons;
sortedPlayerIcons.SetNum( playerIcons.Num() );
for ( int i = 0; i < playerIcons.Num(); i++ ) {
sortedPlayerIcons[ i ] = &playerIcons[ i ];
}
sortedPlayerIcons.Sort( sdPlayerDisplayIcon::SortByDistance );
for ( int i = 0; i < sortedPlayerIcons.Num(); i++ ) {
sdPlayerDisplayIcon* icon = sortedPlayerIcons[ i ];
bool draw = true;
const float HALF_ICON_WIDTH = icon->icon.size.x * 0.5f;
const float MARGIN_OFFSET = ( width * 0.0125f );
const float LEFT_MARGIN = MARGIN_OFFSET;
const float RIGHT_MARGIN = width - MARGIN_OFFSET;
sdBounds2D iconBounds;
iconBounds.Clear();
iconBounds.AddPoint( icon->icon.size * 0.5f );
iconBounds.AddPoint( icon->arrowIcon.size * 0.5f );
if ( icon->offScreenIcon.material != NULL ) {
if ( icon->origin.x - iconBounds.GetMaxs().x <= LEFT_MARGIN ) {
draw = false;
idVec2 startOrg( LEFT_MARGIN, icon->origin.y );
idVec2 endOrg( startOrg.x, SCREEN_HEIGHT * 0.5f );
MakeDockIcon( icon->player, true, startOrg, endOrg, icon->offScreenIcon, icon->icon );
} else if ( ( icon->origin.x + iconBounds.GetMaxs().x ) >= RIGHT_MARGIN ) {
draw = false;
idVec2 startOrg( RIGHT_MARGIN, icon->origin.y );
idVec2 endOrg( startOrg.x, SCREEN_HEIGHT * 0.5f );
MakeDockIcon( icon->player, false, startOrg, endOrg, icon->offScreenIcon, icon->icon );
}
} else {
if ( icon->origin.x <= LEFT_MARGIN || ( icon->origin.x + icon->icon.size.x ) >= RIGHT_MARGIN ) {
draw = false;
}
}
if ( draw ) {
float height = icon->origin.y;
if ( icon->arrowIcon.material != NULL ) {
idVec2 screenPos( icon->origin.x - ( icon->arrowIcon.size.x * 0.5f ), height - icon->arrowIcon.size.y );
deviceContext->DrawMaterial( screenPos.x, screenPos.y, icon->arrowIcon.size.x, icon->arrowIcon.size.y, icon->arrowIcon.material, icon->arrowIcon.color );
height -= icon->arrowIcon.size.y;
}
idVec2 screenPos( icon->origin.x - ( icon->icon.size.x * 0.5f ), height - icon->icon.size.y );
deviceContext->DrawMaterial( screenPos.x, screenPos.y, icon->icon.size.x, icon->icon.size.y, icon->icon.material, icon->icon.color );
}
}
}
/*
============
sdUICrosshairInfo::DrawWayPoints
============
*/
void sdUICrosshairInfo::DrawWayPoints( idEntity* exclude ) {
if ( !g_showWayPoints.GetBool() && !sdWayPointManager::GetInstance().GetShowWayPoints() ) {
return;
}
if ( !TestCrosshairInfoFlag( CF_TASKS | CF_OBJ_MIS ) ) {
return;
}
idVec4 color = colorWhite;
idPlayer* localPlayer = gameLocal.GetLocalPlayer();
if ( localPlayer == NULL || localPlayer->IsSpectating() ) {
return;
}
idPlayer* viewPlayer = gameLocal.GetLocalViewPlayer();
if ( viewPlayer == NULL ) {
return;
}
if ( viewPlayer->IsBeingBriefed() ) {
return;
}
const float screenWidth = SCREEN_WIDTH * 1.0f / deviceContext->GetAspectRatioCorrection();
const float MARGIN_SIZE = ( screenWidth * 0.0125f );
const float LEFT_MARGIN = MARGIN_SIZE;
const float RIGHT_MARGIN = screenWidth - MARGIN_SIZE;
idVec3 org = viewPlayer->renderView.vieworg;
idMat3 axes = viewPlayer->renderView.viewaxis;
sdWorldToScreenConverter converter( gameLocal.playerView.GetCurrentView() );
sdWayPoint* highlightedWaypoint = NULL;
idVec2 bestDistanceFromCenter( idMath::INFINITY, idMath::INFINITY );
idVec2 bestDrawCenter;
float bestIconWidth;
float bestWidth;
float bestDistSqr;
float bestLeft;
for ( sdWayPoint* wayPoint = sdWayPointManager::GetInstance().GetFirstActiveWayPoint(); wayPoint != NULL; wayPoint = wayPoint->GetActiveNode().Next() ) {
if ( !wayPoint->IsValid() ) {
continue;
}
idBounds bounds = wayPoint->GetBounds();
idVec3 size = bounds.GetSize();
idVec3 center = bounds.GetCenter();
float smallestAxis = idMath::INFINITY;
float largestAxis = -idMath::INFINITY;
for( int i = 0; i < 3; i++ ) {
if( size[ i ] > largestAxis ) {
largestAxis = size[ i ];
}
if( size[ i ] < smallestAxis ) {
smallestAxis = size[ i ];
}
}
//bounds.GetMins().Set( -smallestAxis, -smallestAxis, -smallestAxis );
//bounds.GetMaxs().Set( smallestAxis, smallestAxis, smallestAxis );
//bounds.TranslateSelf( center - bounds.GetCenter() );
idMat3 axes = wayPoint->GetOrientation();
idVec3 origin = wayPoint->GetPosition();
float distSquare = ( org - origin ).LengthSqr();
float fadeAlpha = distSquare / Square( largestAxis * 2.0f ) - 0.25f;
float playerDist = ( viewPlayer->GetPhysics()->GetOrigin() - wayPoint->GetPosition() ).LengthFast() - g_waypointDistanceMin.GetFloat();
float waypointDist = g_waypointDistanceMax.GetFloat() - g_waypointDistanceMin.GetFloat();
float rangePct = waypointDist != 0.0f ? idMath::ClampFloat( 0.0f, 1.0f, playerDist / waypointDist ) : 0.0f;
float iconWidth = g_waypointSizeMin.GetFloat() + ( g_waypointSizeMax.GetFloat() - g_waypointSizeMin.GetFloat() ) * ( 1.0f - rangePct );
if ( fadeAlpha <= 0.0f ) {
continue;
}
fadeAlpha = idMath::ClampFloat( 0.0f, 1.0f, fadeAlpha );
sdBounds2D screenBounds;
converter.Transform( bounds, axes, origin, screenBounds );
float left = screenBounds.GetLeft();
float right = screenBounds.GetRight();
float top = screenBounds.GetTop();
float bottom = screenBounds.GetBottom();
float width = screenBounds.GetWidth();
float height = screenBounds.GetHeight();
float visiblePct = idMath::ClampFloat( 0.0f, 100.0f, 100.0f * ( ( width * height ) / ( screenWidth * SCREEN_HEIGHT ) ) );
// update the visibility of the waypoint
idVec3 temp;
bool isVisible = true;
if ( wayPoint->ShouldCheckLineOfSight() ){
isVisible = wayPoint->GetOwner()->IsVisibleOcclusionTest();
// ao: implicitly know it's a task if there's LOS check
if ( !TestCrosshairInfoFlag( CF_TASKS ) ) {
isVisible = false;
}
} else {
if ( !wayPoint->IsVisible() ) {
isVisible = false;
}
// ao: implicitly know it's a mission/objective if there's no LOS check
if ( !TestCrosshairInfoFlag( CF_OBJ_MIS ) ) {
isVisible = false;
}
}
if ( !isVisible ) {
visiblePct = 0.0f;
}
wayPoint->SetVisible( isVisible );
// update highlighting
if ( visiblePct > 0.05f && visiblePct < 40.f ) {
if ( !wayPoint->IsHighlightActive() ) {
wayPoint->MakeHighlightActive();
}
} else {
if ( wayPoint->IsHighlightActive() ) {
wayPoint->MakeHighlightInActive();
}
}
idVec4 color = colorWhite;
if ( wayPoint->GetFlags() & sdWayPoint::WF_TEAMCOLOR ) {
idEntity* owner = wayPoint->GetOwner();
if ( owner != NULL ) {
teamAllegiance_t allegiance = owner->GetEntityAllegiance( viewPlayer );
if ( allegiance != TA_NEUTRAL ) {
color = idEntity::GetColorForAllegiance( allegiance );
}
}
}
float factor = wayPoint->GetActiveFraction() * fadeAlpha;
color[ 3 ] *= factor;
idVec4 highlightColor = color;
highlightColor[ 3 ] *= wayPoint->GetHighLightActiveFraction() * fadeAlpha;
float scaleFactor = ( 1.0f - ( visiblePct / 0.33f ) );
converter.Transform( bounds, axes, origin, screenBounds );
idVec2 drawCenter = screenBounds.GetCenter();
float resize = 0.0f;
if ( gameLocal.ToGuiTime( gameLocal.time ) < wayPoint->GetFlashEndTime() ) {
resize = 2.0f;
}
idVec2 drawSize( iconWidth + resize, iconWidth + resize );
idVec2 drawHalfSize = drawSize * 0.5f;
idVec2 drawOrigin = drawCenter - ( drawSize * 0.5f );
drawOrigin.y = idMath::ClampFloat( MARGIN_SIZE, SCREEN_HEIGHT - MARGIN_SIZE - drawSize.y, drawOrigin.y );
idVec2 distanceFromCenter( idMath::Fabs( ( screenWidth * 0.5f ) - drawCenter.x ) / ( screenWidth * 0.5f ),
idMath::Fabs( ( SCREEN_HEIGHT * 0.5f ) - drawCenter.y ) / ( SCREEN_HEIGHT * 0.5f ) );
idVec4 localColor = colorWhite;
if ( gameLocal.ToGuiTime( gameLocal.time ) < wayPoint->GetFlashEndTime() ) {
localColor.w = idMath::Cos( gameLocal.ToGuiTime( gameLocal.time ) * 0.02f ) * 0.5f + 0.5f;
}
localColor.w = localColor.w * fadeAlpha * g_waypointAlphaScale.GetFloat();
iconInfo_t icon;
icon.material = wayPoint->GetOffScreenMaterial();
icon.color = idVec4( color.x, color.y, color.z, idMath::ClampFloat( 0.35f, 1.0f, color.w * scaleFactor ) );
icon.size = drawHalfSize;
if ( drawOrigin.x <= LEFT_MARGIN ) {
if ( localColor.w >= 0.01f ) {
idVec2 startOrg( LEFT_MARGIN, drawOrigin.y );
idVec2 endOrg( startOrg.x, SCREEN_HEIGHT * 0.5f );
if ( isVisible ) {
MakeDockIcon( wayPoint, true, endOrg, endOrg, icon, iconInfo_t() );
}
}
} else if ( ( drawOrigin.x + drawSize.x ) >= RIGHT_MARGIN ) {
if ( localColor.w >= 0.01f ) {
idVec2 startOrg( RIGHT_MARGIN, drawOrigin.y );
idVec2 endOrg( startOrg.x, SCREEN_HEIGHT * 0.5f );
if ( isVisible ) {
MakeDockIcon( wayPoint, false, endOrg, endOrg, icon, iconInfo_t() );
}
}
} else {
if ( isVisible ) {
if ( wayPoint->GetMaterial() != NULL ) {
float time = wayPoint->Selected();
if( time >= 0.0f ) {
float value = 1.0f - idMath::ClampFloat( 0.0f, 1.0f, static_cast< float >( gameLocal.time - time ) / HIGHLIGHT_TIME );
deviceContext->SetRegister( 4, value );
} else {
deviceContext->SetRegister( 4, 0.0f );
}
deviceContext->DrawMaterial( drawOrigin.x, drawOrigin.y, drawSize.x, drawSize.y, wayPoint->GetMaterial(), localColor );
}
}
}
if( isVisible ) {
sdDockedScreenIcon* icon = FindDockedIcon( wayPoint );
if( icon != NULL ) {
icon->highlightedTime = wayPoint->Selected();
}
}
if( isVisible && distanceFromCenter.x < 0.1f && distanceFromCenter.y < 0.1f && distanceFromCenter.x < bestDistanceFromCenter.x && distanceFromCenter.y < bestDistanceFromCenter.y ) {
waypointTextFadeTime = gameLocal.time;
bestDistanceFromCenter = distanceFromCenter;
highlightedWaypoint = wayPoint;
bestDrawCenter = drawCenter;
bestIconWidth = iconWidth;
bestWidth = width;
bestDistSqr = distSquare;
bestLeft = left;
}
if ( wayPoint->Bracketed() ) {
if( exclude != NULL && wayPoint->GetOwner() == exclude ) {
if( idMath::ClampFloat( 0.0f, 1.0f, idMath::Fabs( bracketFadeTime - gameLocal.time ) / ( float )sdWayPoint::ACTIVATE_TIME ) <= idMath::FLT_EPSILON ) {
continue;
}
}
idVec3 bracketOrigin = wayPoint->GetBracketPosition();
converter.Transform( bounds, axes, bracketOrigin, screenBounds );
DrawBrackets( screenBounds, highlightColor );
}
}
static const int TEXT_FADE_TIME = SEC2MS( 1 );
const sdCrosshairInfo& info = localPlayer->GetCrosshairInfoDirect();
if( !info.IsValid() && highlightedWaypoint != NULL && ( gameLocal.time - waypointTextFadeTime ) <= TEXT_FADE_TIME ) {
static const int TEXT_SCALE = 12;
idVec4 colorLocal = colorWhite;
colorLocal[ 3 ] *= 1.0f - ( static_cast< float >( gameLocal.time - waypointTextFadeTime ) / TEXT_FADE_TIME );
idWStrList args;
args.SetNum( 2 );
args[ 0 ] = va( L"%i", ( int )InchesToMetres( idMath::Sqrt( bestDistSqr ) ) );
args[ 1 ] = common->LocalizeText( "game/meters" );
idWStr text = common->LocalizeText( "game/range", args );
sdBounds2D rect( bestLeft, bestDrawCenter.y + bestIconWidth, bestWidth, lineTextHeight );
sdUIWindow::DrawText( text.c_str(), colorLocal, TEXT_SCALE, rect, cachedFontHandle, DTF_CENTER | DTF_BOTTOM | DTF_SINGLELINE | DTF_DROPSHADOW );
rect.TranslateSelf( 0.0f, lineTextHeight );
sdUIWindow::DrawText( highlightedWaypoint->GetText(), colorLocal, TEXT_SCALE, rect, cachedFontHandle, DTF_CENTER | DTF_BOTTOM | DTF_SINGLELINE | DTF_DROPSHADOW );
}
}
/*
============
sdUICrosshairInfo::DrawDockedIcons
============
*/
void sdUICrosshairInfo::DrawDockedIcons( void ) {
idVec2 offsets( 0.f, 0.f );
const float DOCK_SPACING = 32.0f;
for ( int i = 0; i < dockedIcons.Num(); i++ ) {
const sdDockedScreenIcon& dock = dockedIcons[ i ];
if ( !dock.active ) {
continue;
}
if ( dock.arrowIcon.material == NULL ) {
assert( false );
continue;
}
float percent = static_cast< float >( gameLocal.time - ( dock.startTime + 300 ) ) / sdDockedScreenIcon::DOCK_ANIMATION_MS;
percent = idMath::ClampFloat( 0.0f, 1.0f, percent );
idVec2 drawOrigin = Lerp( dock.startOrigin, dock.endOrigin, percent );
int index = dock.flipped ? 1 : 0;
drawOrigin.y += offsets[ index ];
float scale = dock.flipped ? 1.0f : -1.0f;
float xOffset = 0.f;
float yOffset = 0.f;
float time = dock.highlightedTime;
float value = 1.0f - idMath::ClampFloat( 0.0f, 1.0f, dock.highlightedTime < 0 ? 1.0f : ( static_cast< float >( gameLocal.time - time ) / HIGHLIGHT_TIME ) );
float drawScale = 1.0f + ( 1.0f * value );
deviceContext->SetRegister( 4, value );
if ( dock.icon.material != NULL ) {
idVec2 screenPos = drawOrigin;
screenPos.y -= ( DOCK_SPACING * 0.5f );
if ( !dock.flipped ) {
screenPos.x -= dock.icon.size.x;
}
float drawScale = 1.0f;
deviceContext->DrawMaterial( screenPos.x, screenPos.y, dock.icon.size.x * drawScale, dock.icon.size.y * drawScale, dock.icon.material, dock.icon.color, 1.f );
xOffset -= scale * dock.arrowIcon.size.x;
yOffset = dock.icon.size.y;
}
idVec2 screenPos = drawOrigin;
if ( dock.icon.material != NULL ) {
screenPos.y -= ( DOCK_SPACING * 0.5f - dock.icon.size.y * 0.25f );
} else {
screenPos.y -= ( DOCK_SPACING * 0.5f );
}
if ( !dock.flipped ) {
screenPos.x -= dock.arrowIcon.size.x;
}
deviceContext->DrawMaterial( xOffset + screenPos.x, screenPos.y, dock.arrowIcon.size.x * drawScale, dock.arrowIcon.size.y * drawScale, dock.arrowIcon.material, dock.arrowIcon.color, scale );
if ( dock.arrowIcon.size.y > yOffset ) {
yOffset = dock.arrowIcon.size.y;
}
offsets[ index ] += yOffset * 1.25f;
}
}
/*
============
sdUICrosshairInfo::FindDockedIcon
============
*/
sdDockedScreenIcon* sdUICrosshairInfo::FindDockedIcon( void* tag ) {
for ( int i = 0; i < dockedIcons.Num(); i++ ) {
if ( !dockedIcons[ i ].active ) {
continue;
}
if ( dockedIcons[ i ].tag == tag ) {
return &dockedIcons[ i ];
}
}
return NULL;
}
/*
============
sdUICrosshairInfo::FindFreeDockedIcon
============
*/
sdDockedScreenIcon* sdUICrosshairInfo::FindFreeDockedIcon( void ) {
for( int i = 0; i < dockedIcons.Num(); i++ ) {
if ( !dockedIcons[ i ].active ) {
return &dockedIcons[ i ];
}
}
return NULL;
}
/*
============
sdUICrosshairInfo::MakeDockIcon
============
*/
void sdUICrosshairInfo::MakeDockIcon( void* tag, bool flipped, const idVec2& startOrg, const idVec2& endOrg, const iconInfo_t& arrowInfo, const iconInfo_t& iconInfo ) {
sdDockedScreenIcon* icon = FindDockedIcon( tag );
if ( icon == NULL ) {
icon = FindFreeDockedIcon();
if ( icon == NULL ) {
return;
}
icon->tag = tag;
icon->startTime = gameLocal.time;
icon->active = true;
}
icon->icon = iconInfo;
icon->arrowIcon = arrowInfo;
icon->endOrigin = endOrg;
icon->flipped = flipped;
icon->lastUpdateTime = gameLocal.time;
icon->startOrigin = startOrg;
// Can get some very large y values
if ( icon->startOrigin.y > SCREEN_HEIGHT ) {
icon->startOrigin.y = SCREEN_HEIGHT + icon->icon.size.y;
} else if ( icon->startOrigin.y + icon->icon.size.y < 0 ) {
icon->startOrigin.y = -icon->icon.size.y;
}
}
/*
============
sdUICrosshairInfo::ClearInactiveDockIcons
============
*/
void sdUICrosshairInfo::ClearInactiveDockIcons( void ) {
for ( int i = 0; i < dockedIcons.Num(); i++ ) {
if ( dockedIcons[ i ].lastUpdateTime != gameLocal.time ) {
dockedIcons[ i ].active = false;
}
}
}
/*
============
sdUICrosshairInfo::DrawBrackets
============
*/
void sdUICrosshairInfo::DrawBrackets( const sdBounds2D& bounds, const idVec4& color ) {
const float screenWidth = SCREEN_WIDTH * 1.0f / deviceContext->GetAspectRatioCorrection();
float left = bounds.GetLeft();
float right = bounds.GetRight();
float top = bounds.GetTop();
float bottom = bounds.GetBottom();
float width = bounds.GetWidth();
float height = bounds.GetHeight();
idVec2 center( left + width * 0.5f, top + height * 0.5f );
float size = width * 0.25f;
//
// left half
//
idVec4 rect;
rect.Set( left - crosshairParts[ CP_L_BRACKET_TOP ].width, top, crosshairParts[ CP_L_BRACKET_TOP ].width, height );
DrawThreeVerticalParts( rect, color, vec2_one, crosshairParts[ CP_L_BRACKET_TOP ], crosshairParts[ CP_L_BRACKET_CENTER ], crosshairParts[ CP_L_BRACKET_BOTTOM ] );
rect.Set( right, top, crosshairParts[ CP_L_BRACKET_TOP ].width, height );
DrawThreeVerticalParts( rect, color, vec2_one, crosshairParts[ CP_R_BRACKET_TOP ], crosshairParts[ CP_R_BRACKET_CENTER ], crosshairParts[ CP_R_BRACKET_BOTTOM ] );
}
/*
============
sdUICrosshairInfo::DrawContextEntity
============
*/
idEntity* sdUICrosshairInfo::DrawContextEntity( idPlayer* player ) {
return NULL;
/* jrad - disabled this, as it was confusing
idEntity* entity = gameLocal.localPlayerProperties.GetContextEntity();
// see if we've moused over something that can be acted on
if( entity == NULL ) {
const sdProgram::sdFunction* callback = player->GetScriptFunction( "ContextUpdateOrder" );
if ( callback != NULL ) {
sdScriptHelper h;
h.Push( "" );
player->CallNonBlockingScriptEvent( callback, h );
const char* returnVal = h.GetReturnedString();
idStrList list;
idSplitStringIntoList( list, returnVal, "|" );
bool enabled;
if ( list.Num() >= 5 ) {
enabled = sdTypeFromString< bool >( list[ 4 ] );
} else {
enabled = true;
}
if ( enabled && list.Num() >= 6 ) {
enabled &= list[ 5 ].Icmp( "Invalid" ) != 0;
}
if( enabled ) {
const sdCrosshairInfo& info = player->GetCrosshairInfoDirect();
entity = info.GetEntity();
bracketFadeTime = gameLocal.time;
lastBracketEntity = entity;
}
}
}
// try using a cached one
if( entity == NULL ) {
entity = lastBracketEntity;
}
if( entity != NULL ) {
idVec3 org = entity->GetPhysics()->GetOrigin();
idMat3 axes = entity->GetAxis();
idBounds bounds = entity->GetPhysics()->GetBounds();
sdWorldToScreenConverter converter( gameLocal.playerView.GetCurrentView() );
sdBounds2D bracketBounds;
converter.Transform( bounds, axes, org, bracketBounds );
idVec4 color = colorWhite;
color.w = idMath::ClampFloat( 0.0f, 1.0f, idMath::Fabs( bracketFadeTime - gameLocal.time ) / ( float )sdWayPoint::ACTIVATE_TIME );
color.w = 1.0f - color.w;
DrawBrackets( bracketBounds, color );
}
return entity;
*/
}