2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "platform/platform.h"
# include "gui/core/guiControl.h"
# include "gui/controls/guiBitmapCtrl.h"
# include "console/consoleTypes.h"
# include "scene/sceneManager.h"
# include "T3D/gameBase/gameConnection.h"
# include "T3D/shapeBase.h"
# include "gfx/gfxDrawUtil.h"
# include "console/engineAPI.h"
2025-06-18 16:05:35 +00:00
# include "gui/core/guiOffscreenCanvas.h"
# include "T3D/tsStatic.h"
# include "materials/baseMatInstance.h"
# include "materials/matInstance.h"
# include "materials/materialDefinition.h"
2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
/// Vary basic cross hair hud.
/// Uses the base bitmap control to render a bitmap, and decides whether
/// to draw or not depending on the current control object and it's state.
/// If there is ShapeBase object under the cross hair and it's named,
/// then a small health bar is displayed.
class GuiCrossHairHud : public GuiBitmapCtrl
{
typedef GuiBitmapCtrl Parent ;
2017-06-23 16:36:20 +00:00
LinearColorF mDamageFillColor ;
LinearColorF mDamageFrameColor ;
2012-09-19 15:15:01 +00:00
Point2I mDamageRectSize ;
Point2I mDamageOffset ;
2025-06-18 16:05:35 +00:00
PlatformTimer * mFrameTime ;
2012-09-19 15:15:01 +00:00
protected :
void drawDamage ( Point2I offset , F32 damage , F32 opacity ) ;
public :
GuiCrossHairHud ( ) ;
2025-06-18 16:05:35 +00:00
~ GuiCrossHairHud ( ) ;
2012-09-19 15:15:01 +00:00
2024-03-18 18:13:00 +00:00
void onRender ( Point2I , const RectI & ) override ;
2012-09-19 15:15:01 +00:00
static void initPersistFields ( ) ;
DECLARE_CONOBJECT ( GuiCrossHairHud ) ;
DECLARE_CATEGORY ( " Gui Game " ) ;
DECLARE_DESCRIPTION ( " Basic cross hair hud. Reacts to state of control object. \n "
" Also displays health bar for named objects under the cross hair. " ) ;
} ;
/// Valid object types for which the cross hair will render, this
/// should really all be script controlled.
static const U32 ObjectMask = PlayerObjectType | VehicleObjectType ;
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT ( GuiCrossHairHud ) ;
ConsoleDocClass ( GuiCrossHairHud ,
" @brief Basic cross hair hud. Reacts to state of control object. Also displays health bar for named objects under the cross hair. \n \n "
" Uses the base bitmap control to render a bitmap, and decides whether to draw or not depending "
" on the current control object and it's state. If there is ShapeBase object under the cross hair "
" and it's named, then a small health bar is displayed. \n \n "
" @tsexample \n "
" \n new GuiCrossHairHud() "
" { \n "
" damageFillColor = \" 1.0 0.0 0.0 1.0 \" ; // Fills with a solid red color \n "
" damageFrameColor = \" 1.0 1.0 1.0 1.0 \" ; // Solid white frame color \n "
" damageRect = \" 15 5 \" ; \n "
" damageOffset = \" 0 -10 \" ; \n "
" }; \n "
" @endtsexample \n "
" @ingroup GuiGame \n "
) ;
GuiCrossHairHud : : GuiCrossHairHud ( )
{
mDamageFillColor . set ( 0.0f , 1.0f , 0.0f , 1.0f ) ;
mDamageFrameColor . set ( 1.0f , 0.6f , 0.0f , 1.0f ) ;
mDamageRectSize . set ( 50 , 4 ) ;
mDamageOffset . set ( 0 , 32 ) ;
2025-06-18 16:05:35 +00:00
mFrameTime = PlatformTimer : : create ( ) ;
}
GuiCrossHairHud : : ~ GuiCrossHairHud ( )
{
SAFE_DELETE ( mFrameTime ) ;
2012-09-19 15:15:01 +00:00
}
void GuiCrossHairHud : : initPersistFields ( )
{
2023-01-27 07:13:15 +00:00
docsURL ;
2012-09-19 15:15:01 +00:00
addGroup ( " Damage " ) ;
addField ( " damageFillColor " , TypeColorF , Offset ( mDamageFillColor , GuiCrossHairHud ) , " As the health bar depletes, this color will represent the health loss amount. " ) ;
addField ( " damageFrameColor " , TypeColorF , Offset ( mDamageFrameColor , GuiCrossHairHud ) , " Color for the health bar's frame. " ) ;
addField ( " damageRect " , TypePoint2I , Offset ( mDamageRectSize , GuiCrossHairHud ) , " Size for the health bar portion of the control. " ) ;
addField ( " damageOffset " , TypePoint2I , Offset ( mDamageOffset , GuiCrossHairHud ) , " Offset for drawing the damage portion of the health control. " ) ;
endGroup ( " Damage " ) ;
Parent : : initPersistFields ( ) ;
}
//-----------------------------------------------------------------------------
void GuiCrossHairHud : : onRender ( Point2I offset , const RectI & updateRect )
{
// Must have a connection and player control object
GameConnection * conn = GameConnection : : getConnectionToServer ( ) ;
if ( ! conn )
return ;
2025-06-18 16:08:40 +00:00
GameBase * control = dynamic_cast < GameBase * > ( conn - > getCameraObject ( ) ) ;
2012-09-19 15:15:01 +00:00
if ( ! control | | ! ( control - > getTypeMask ( ) & ObjectMask ) | | ! conn - > isFirstPerson ( ) )
2025-06-18 16:08:40 +00:00
return ;
2012-09-19 15:15:01 +00:00
// Parent render.
Parent : : onRender ( offset , updateRect ) ;
// Get control camera info
MatrixF cam ;
Point3F camPos ;
conn - > getControlCameraTransform ( 0 , & cam ) ;
cam . getColumn ( 3 , & camPos ) ;
// Extend the camera vector to create an endpoint for our ray
Point3F endPos ;
cam . getColumn ( 1 , & endPos ) ;
endPos * = gClientSceneGraph - > getVisibleDistance ( ) ;
endPos + = camPos ;
// Collision info. We're going to be running LOS tests and we
// don't want to collide with the control object.
2025-06-18 16:05:35 +00:00
static U32 losMask = TerrainObjectType | ShapeBaseObjectType | StaticShapeObjectType ;
2025-06-18 16:08:40 +00:00
control - > disableCollision ( ) ;
2012-09-19 15:15:01 +00:00
RayInfo info ;
if ( gClientContainer . castRay ( camPos , endPos , losMask , & info ) ) {
2025-06-18 16:05:35 +00:00
// is this a tsstatic? then it could be a offscreen canvas, check the list.
if ( TSStatic * ts = dynamic_cast < TSStatic * > ( info . object ) )
{
if ( mFrameTime - > getElapsedMs ( ) > 32 )
{
2025-08-11 05:06:42 +00:00
if ( GuiOffscreenCanvas : : sActiveOffscreenCanvas )
GuiOffscreenCanvas : : sActiveOffscreenCanvas - > setActive ( false ) ;
2025-06-18 19:44:37 +00:00
GuiOffscreenCanvas : : sActiveOffscreenCanvas = NULL ;
2025-06-18 16:05:35 +00:00
mFrameTime - > reset ( ) ;
Point3F newStart , newEnd ;
ts - > getWorldTransform ( ) . mulP ( camPos , & newStart ) ;
ts - > getWorldTransform ( ) . mulP ( endPos , & newEnd ) ;
newStart . convolveInverse ( ts - > getScale ( ) ) ;
newEnd . convolveInverse ( ts - > getScale ( ) ) ;
info . generateTexCoord = true ;
if ( ts - > getShapeInstance ( ) - > castRayOpcode ( 0 , newStart , newEnd , & info ) )
{
MatInstance * matInst = dynamic_cast < MatInstance * > ( info . material ) ;
if ( matInst )
{
Material * mat = matInst - > getMaterial ( ) ;
if ( mat & & mat - > getDiffuseMapAsset ( 0 ) . notNull ( ) & & mat - > getDiffuseMapAsset ( 0 ) - > isNamedTarget ( ) )
{
String canvasName = String ( mat - > getDiffuseMapAsset ( 0 ) - > getImageFile ( ) ) . substr ( 1 , ( U32 ) strlen ( mat - > getDiffuseMapAsset ( 0 ) - > getImageFile ( ) ) - 1 ) ;
for ( GuiOffscreenCanvas * canvas : GuiOffscreenCanvas : : sList )
{
if ( canvas - > getTarget ( ) - > getName ( ) = = canvasName )
{
if ( ! canvas - > canInteract ( ) | | canvas - > getMaxInteractDistance ( ) < info . distance )
{
break ;
}
Point2I canvasSize = canvas - > getWindowSize ( ) ;
Point2I newCursorPos ( mRound ( mClampF ( ( info . texCoord . x * canvasSize . x ) , 0.0f , ( F32 ) canvasSize . x ) ) ,
mRound ( mClampF ( ( info . texCoord . y * canvasSize . y ) , 0.0f , ( F32 ) canvasSize . y ) ) ) ;
canvas - > setCursorPos ( newCursorPos ) ;
canvas - > markDirty ( ) ;
GuiOffscreenCanvas : : sActiveOffscreenCanvas = canvas ;
2025-08-11 05:06:42 +00:00
GuiOffscreenCanvas : : sActiveOffscreenCanvas - > setActive ( true ) ;
2025-06-18 16:05:35 +00:00
break ;
}
}
}
}
}
}
}
2012-09-19 15:15:01 +00:00
// Hit something... but we'll only display health for named
// ShapeBase objects. Could mask against the object type here
// and do a static cast if it's a ShapeBaseObjectType, but this
// isn't a performance situation, so I'll just use dynamic_cast.
if ( ShapeBase * obj = dynamic_cast < ShapeBase * > ( info . object ) )
if ( obj - > getShapeName ( ) ) {
offset . x = updateRect . point . x + updateRect . extent . x / 2 ;
offset . y = updateRect . point . y + updateRect . extent . y / 2 ;
drawDamage ( offset + mDamageOffset , obj - > getDamageValue ( ) , 1 ) ;
}
}
// Restore control object collision
2025-06-18 16:08:40 +00:00
control - > enableCollision ( ) ;
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
/**
Display a damage bar ubove the shape .
This is a support funtion , called by onRender .
*/
void GuiCrossHairHud : : drawDamage ( Point2I offset , F32 damage , F32 opacity )
{
mDamageFillColor . alpha = mDamageFrameColor . alpha = opacity ;
// Damage should be 0->1 (0 being no damage,or healthy), but
// we'll just make sure here as we flip it.
damage = mClampF ( 1 - damage , 0 , 1 ) ;
// Center the bar
RectI rect ( offset , mDamageRectSize ) ;
rect . point . x - = mDamageRectSize . x / 2 ;
// Draw the border
2017-06-23 16:36:20 +00:00
GFX - > getDrawUtil ( ) - > drawRect ( rect , mDamageFrameColor . toColorI ( ) ) ;
2012-09-19 15:15:01 +00:00
// Draw the damage % fill
rect . point + = Point2I ( 1 , 1 ) ;
rect . extent - = Point2I ( 1 , 1 ) ;
rect . extent . x = ( S32 ) ( rect . extent . x * damage ) ;
if ( rect . extent . x = = 1 )
rect . extent . x = 2 ;
if ( rect . extent . x > 0 )
2017-06-23 16:36:20 +00:00
GFX - > getDrawUtil ( ) - > drawRectFill ( rect , mDamageFillColor . toColorI ( ) ) ;
2012-09-19 15:15:01 +00:00
}