|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "hitarea.h"
// To handle scaling
#include "materialsystem/imaterialsystem.h"
#include "animdata.h"
#include "inputsystem/inputenums.h"
#include "inputsystem/analogcode.h"
#include "inputsystem/buttoncode.h"
#include "gameuisystemmgr.h"
#include "graphicgroup.h"
#include "inputgameui.h"
#include "graphicscriptinterface.h"
#include "inputgameui.h"
#include "gameuisystemmgr.h"
#include "gameuiscript.h"
#include "gameuisystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define DEBUG_INPUT_EVENTS 0
// Class factory for scripting.
class CHitAreaClassFactory : IGameUIGraphicClassFactory { public:
CHitAreaClassFactory() { Assert( g_pGameUISystemMgrImpl ); g_pGameUISystemMgrImpl->RegisterGraphicClassFactory( "hitarea", this ); }
// Returns an instance of a graphic interface (keyvalues owned by caller)
virtual CGameGraphic *CreateNewGraphicClass( KeyValues *kvRequest, CGameUIDefinition *pMenu ) { Assert( pMenu ); CHitArea *pNewGraphic = NULL;
const char *pName = kvRequest->GetString( "name", NULL ); if ( pName ) { pNewGraphic = new CHitArea( pName ); // Rects are normally 0,0, doing this so we can see script created rects.
pNewGraphic->SetScale( 100, 100 ); pMenu->AddGraphicToLayer( pNewGraphic, SUBLAYER_STATIC ); // Now set the attributes.
for ( KeyValues *arg = kvRequest->GetFirstSubKey(); arg != NULL; arg = arg->GetNextKey() ) { pNewGraphic->HandleScriptCommand( arg ); } } return pNewGraphic; } }; static CHitAreaClassFactory g_CDynamicRectClassFactory;
BEGIN_DMXELEMENT_UNPACK ( CHitArea ) DMXELEMENT_UNPACK_FIELD_UTLSTRING( "name", "", m_pName ) DMXELEMENT_UNPACK_FIELD( "center", "0 0", Vector2D, m_Geometry.m_Center ) DMXELEMENT_UNPACK_FIELD( "scale", "0 0", Vector2D, m_Geometry.m_Scale ) DMXELEMENT_UNPACK_FIELD( "rotation", "0", float, m_Geometry.m_Rotation ) DMXELEMENT_UNPACK_FIELD( "maintainaspectratio", "0", bool, m_Geometry.m_bMaintainAspectRatio ) DMXELEMENT_UNPACK_FIELD( "sublayertype", "0", int, m_Geometry.m_Sublayer ) DMXELEMENT_UNPACK_FIELD( "visible", "1", bool, m_Geometry.m_bVisible ) DMXELEMENT_UNPACK_FIELD( "initialstate", "-1", int, m_CurrentState ) DMXELEMENT_UNPACK_FIELD( "dragenabled", "0", bool, m_bDragEnabled ) DMXELEMENT_UNPACK_FIELD_UTLSTRING( "on_mouse_left_clicked_cmd", "", m_OnMouseLeftClickedScriptCommand ) END_DMXELEMENT_UNPACK( CHitArea, s_HitAreaUnpack )
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CHitArea::CHitArea( const char *pName ) { m_bCanAcceptInput = true; m_bCanStartDragging = false; m_IsDragging = false; m_DragStartCursorPos[0] = 0; m_DragStartCursorPos[1] = 0; m_DragCurrentCursorPos[0] = 0; m_DragCurrentCursorPos[1] = 0;
// DME default values.
m_pName = pName; m_Geometry.m_Center.x = 0; m_Geometry.m_Center.y = 0; m_Geometry.m_Scale.x = 0; m_Geometry.m_Scale.y = 0; m_Geometry.m_Rotation = 0; m_Geometry.m_bMaintainAspectRatio = 0; m_Geometry.m_Sublayer = 0; m_Geometry.m_bVisible = true; m_CurrentState = -1; m_bDragEnabled = false; m_OnMouseLeftClickedScriptCommand = NULL;
m_Geometry.m_RelativePositions.AddToTail( Vector2D( -.5, -.5 ) ); m_Geometry.m_RelativePositions.AddToTail( Vector2D( .5, -.5 ) ); m_Geometry.m_RelativePositions.AddToTail( Vector2D( .5, .5 ) ); m_Geometry.m_RelativePositions.AddToTail( Vector2D( -.5, .5 ) );
CTriangle triangle; triangle.m_PointIndex[0] = 0; triangle.m_PointIndex[1] = 1; triangle.m_PointIndex[2] = 2; m_Geometry.m_Triangles.AddToTail( triangle ); triangle.m_PointIndex[0] = 0; triangle.m_PointIndex[1] = 2; triangle.m_PointIndex[2] = 3; m_Geometry.m_Triangles.AddToTail( triangle ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CHitArea::~CHitArea() { // TODO: move to manager?/ as it should control allocations and deallocations.
g_pInputGameUI->PanelDeleted( this ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CHitArea::Unserialize( CDmxElement *pGraphic ) { pGraphic->UnpackIntoStructure( this, s_HitAreaUnpack );
// GEOMETRY
CDmxAttribute *pRelativePositions = pGraphic->GetAttribute( "relativepositions" ); if ( !pRelativePositions || pRelativePositions->GetType() != AT_VECTOR2_ARRAY ) { return false; } const CUtlVector< Vector2D > &relpositions = pRelativePositions->GetArray< Vector2D >( ); int nCount = relpositions.Count(); m_Geometry.m_RelativePositions.RemoveAll(); for ( int i = 0; i < nCount; ++i ) { m_Geometry.m_RelativePositions.AddToTail( Vector2D( relpositions[i].x, relpositions[i].y ) ); }
CDmxAttribute *pTriangles = pGraphic->GetAttribute( "triangles" ); if ( !pTriangles || pTriangles->GetType() != AT_ELEMENT_ARRAY ) { return false; } const CUtlVector< CDmxElement * > &triangles = pTriangles->GetArray< CDmxElement * >( ); nCount = triangles.Count(); m_Geometry.m_Triangles.RemoveAll(); for ( int i = 0; i < nCount; ++i ) { CDmxAttribute *pPoints = triangles[i]->GetAttribute( "positionindexes" ); const CUtlVector< int > &points = pPoints->GetArray< int >( );
CTriangle triangle; triangle.m_PointIndex[0] = points[0]; triangle.m_PointIndex[1] = points[1]; triangle.m_PointIndex[2] = points[2];
m_Geometry.m_Triangles.AddToTail( triangle ); }
// ANIMSTATES
CDmxAttribute *pImageAnims = pGraphic->GetAttribute( "imageanims" ); if ( !pImageAnims || pImageAnims->GetType() != AT_ELEMENT_ARRAY ) { return false; } const CUtlVector< CDmxElement * > &imageanims = pImageAnims->GetArray< CDmxElement * >( ); nCount = imageanims.Count(); for ( int i = 0; i < nCount; ++i ) { CAnimData *pAnimData = new CAnimData; if ( !pAnimData->Unserialize( imageanims[i] ) ) { delete pAnimData; return false; } m_Anims.AddToTail( pAnimData ); }
SetState( "default" );
return true; }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CHitArea::UpdateGeometry() { if ( m_CurrentState == -1 ) return;
DmeTime_t flAnimTime = GetAnimationTimePassed();
// Update center location
m_Anims[ m_CurrentState ]->m_CenterPosAnim.GetValue( flAnimTime, &m_Geometry.m_Center ); // Update scale
m_Anims[ m_CurrentState ]->m_ScaleAnim.GetValue( flAnimTime, &m_Geometry.m_Scale );
// Update rotation
m_Anims[ m_CurrentState ]->m_RotationAnim.GetValue( flAnimTime, &m_Geometry.m_Rotation ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CHitArea::UpdateRenderData( color32 parentColor, CUtlVector< RenderGeometryList_t > &renderGeometryLists, int firstListIndex ) { m_Geometry.CalculateExtents(); bool bDrawHitAreas = false;
if ( !m_Geometry.m_bVisible ) return;
if ( bDrawHitAreas ) { // Time to invent some render data to draw this thing.
int i = renderGeometryLists[firstListIndex].AddToTail(); CRenderGeometry &renderGeometry = renderGeometryLists[firstListIndex][i];
int nCount = m_Geometry.m_RelativePositions.Count(); for ( int i = 0; i < nCount; ++i ) { // Position
Vector relativePosition( m_Geometry.m_RelativePositions[i].x, m_Geometry.m_RelativePositions[i].y, 0 ); Vector screenpos; VectorTransform( relativePosition, m_Geometry.m_RenderToScreen, screenpos ); renderGeometry.m_Positions.AddToTail( Vector2D( screenpos.x, screenpos.y ) );;
// Vertex Color
color32 hitAreaColor; hitAreaColor.r = 255; hitAreaColor.g = 100; hitAreaColor.b = 100; hitAreaColor.a = 255;
renderGeometry.m_VertexColors.AddToTail( hitAreaColor ); }
// TexCoords
renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 0) ); renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 0) ); renderGeometry.m_TextureCoords.AddToTail( Vector2D( 1 , 1) ); renderGeometry.m_TextureCoords.AddToTail( Vector2D( 0 , 1) );
// Triangles
nCount = m_Geometry.m_Triangles.Count(); for ( int i = 0; i < nCount; ++i ) { renderGeometry.m_Triangles.AddToTail( m_Geometry.m_Triangles[i] ); }
// Anim Info
renderGeometry.m_SheetSequenceNumber = 0; renderGeometry.m_AnimationRate = 1; renderGeometry.m_bAnimate = 0; renderGeometry.m_pImageAlias = NULL; }
// Now transform our array of positions into local graphic coord system.
int nCount = m_Geometry.m_RelativePositions.Count(); m_ScreenPositions.RemoveAll(); for ( int i = 0; i < nCount; ++i ) { // Position
Vector relativePosition( m_Geometry.m_RelativePositions[i].x, m_Geometry.m_RelativePositions[i].y, 0 ); Vector screenpos; VectorTransform( relativePosition, m_Geometry.m_RenderToScreen, screenpos ); m_ScreenPositions.AddToTail( Vector2D( screenpos.x, screenpos.y ) ); } }
//-----------------------------------------------------------------------------
// Determine if x,y is inside the graphic.
//-----------------------------------------------------------------------------
bool CHitArea::HitTest( int x, int y ) { if ( !m_bCanAcceptInput ) // note if graphic is invisible, this is false
return false;
if ( m_ScreenPositions.Count() == 0 ) return false;
for ( int i = 0; i < m_Geometry.GetTriangleCount(); ++i ) { if ( PointTriangleHitTest( m_ScreenPositions[ m_Geometry.m_Triangles[i].m_PointIndex[0] ], m_ScreenPositions[ m_Geometry.m_Triangles[i].m_PointIndex[1] ], m_ScreenPositions[ m_Geometry.m_Triangles[i].m_PointIndex[2] ], Vector2D( x, y ) ) ) { //Msg( "%d, %d hit\n", x, y );
return true; } }
return false; }
//-----------------------------------------------------------------------------
// Event that occurs when cursor enters the geometry area
//-----------------------------------------------------------------------------
void CHitArea::OnCursorEnter() { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnCursorEnter\n" ); #endif
if ( m_pGroup ) { m_pGroup->SetState( "AUTO_GAINMOUSEFOCUS" ); }
KeyValues *kvEvent = new KeyValues( "OnMouseFocusGained" ); KeyValues::AutoDelete autodelete( kvEvent );
// chain to main system if this graphic doesn't handle it.
g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); g_pGameUISystemMgrImpl->OnMouseFocusGained( this ); }
//-----------------------------------------------------------------------------
// Event that occurs when cursor leaves the geometry area
//-----------------------------------------------------------------------------
void CHitArea::OnCursorExit() { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnCursorExit\n" ); #endif
if ( m_pGroup ) { m_pGroup->SetState( "AUTO_LOSEMOUSEFOCUS" ); }
KeyValues *kvEvent = new KeyValues( "OnMouseFocusLost" ); KeyValues::AutoDelete autodelete( kvEvent );
// chain to main system if this graphic doesn't handle it.
g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); g_pGameUISystemMgrImpl->OnMouseFocusLost( this ); }
#define DRAG_THRESHOLD_SQUARED 16
//-----------------------------------------------------------------------------
// Event that occurs when cursor moved inside the geometry area
//-----------------------------------------------------------------------------
void CHitArea::OnCursorMove( const int &cursorX, const int &cursorY ) { //Msg( "CHitArea::OnCursorMove\n" );
if ( m_bCanStartDragging ) { m_DragCurrentCursorPos[0] = cursorX; m_DragCurrentCursorPos[1] = cursorY;
float dx = m_DragCurrentCursorPos[0] - m_DragStartCursorPos[0]; float dy = m_DragCurrentCursorPos[1] - m_DragStartCursorPos[1]; float distance = dx * dx + dy * dy; if ( distance > DRAG_THRESHOLD_SQUARED ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::Starting dragging\n" ); #endif
OnDragStartCallScriptEvent( cursorX, cursorY ); m_bCanStartDragging = false; m_IsDragging = true; } } else if ( m_IsDragging ) { m_DragCurrentCursorPos[0] = cursorX; m_DragCurrentCursorPos[1] = cursorY; OnDragCallScriptEvent( cursorX, cursorY ); } }
//-----------------------------------------------------------------------------
// Event that occurs when left mouse button is pressed
//-----------------------------------------------------------------------------
void CHitArea::OnMouseDown( const ButtonCode_t &code ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnMouseDown\n" ); #endif
// Drag and drop supported for left mouse button only.
// This hit area must be drag enabled to support dragging.
if ( code == MOUSE_LEFT && m_bDragEnabled ) { m_bCanStartDragging = true; g_pInputGameUI->GetCursorPos( m_DragStartCursorPos[0], m_DragStartCursorPos[1] ); }
if ( m_pGroup ) { if ( code == MOUSE_LEFT ) { m_pGroup->SetState( "AUTO_MOUSELEFTDOWN" ); } else if ( code == MOUSE_RIGHT ) { m_pGroup->SetState( "AUTO_MOUSERIGHTDOWN" ); } else if ( code == MOUSE_MIDDLE ) { m_pGroup->SetState( "AUTO_MOUSEMIDDLEDOWN" ); } }
// Check for scripting for this control first.
KeyValues *kvEvent = new KeyValues( "OnMouseDown" ); KeyValues::AutoDelete autodelete( kvEvent ); kvEvent->SetInt( "code", code );
// Always call generic click handler to allow host-overrides
kvEvent->SetName( "OnMouseClicked" ); g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent );
// Call assigned handlers
if ( code == MOUSE_LEFT && !m_OnMouseLeftClickedScriptCommand.IsEmpty() ) { kvEvent->SetName( m_OnMouseLeftClickedScriptCommand ); bool bExecuted = g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); if ( !bExecuted ) { Warning( "Unable to find script function %s (assigned to OnMouseLeftClicked)\n", kvEvent->GetName() ); } } }
//-----------------------------------------------------------------------------
// Event that occurs when mouse button is released
// Script events should be tied to mouse release events only, not mouse down.
// Scripts should only fire if this graphic has mousefocus.
//-----------------------------------------------------------------------------
void CHitArea::OnMouseUp( const ButtonCode_t &code, bool bFireScripts ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnMouseUp\n" ); #endif
m_bCanStartDragging = false; if ( m_pGroup ) { if ( code == MOUSE_LEFT ) { m_pGroup->SetState( "AUTO_MOUSELEFTUP" ); } else if ( code == MOUSE_RIGHT ) { m_pGroup->SetState( "AUTO_MOUSERIGHTUP" ); } else if ( code == MOUSE_MIDDLE ) { m_pGroup->SetState( "AUTO_MOUSEMIDDLEUP" ); } }
if ( m_IsDragging ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::Stopped dragging\n" ); #endif
OnDragStopCallScriptEvent( m_DragCurrentCursorPos[0], m_DragCurrentCursorPos[1] ); } else if ( bFireScripts ) { KeyValues *kvEvent = new KeyValues( "OnMouseUp" ); KeyValues::AutoDelete autodelete( kvEvent ); kvEvent->SetInt( "code", code );
// Always call generic click handler to allow host-overrides
g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); }
m_IsDragging = false; }
//-----------------------------------------------------------------------------
// Event that occurs when mouse button is double clicked
//-----------------------------------------------------------------------------
void CHitArea::OnMouseDoubleClick( const ButtonCode_t &code ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnMouseDoubleClick\n" ); #endif
if ( m_pGroup ) { m_pGroup->SetState( "AUTO_MOUSEDOUBLECLICK" ); } }
//-----------------------------------------------------------------------------
// Event that occurs when a key is pressed
//-----------------------------------------------------------------------------
void CHitArea::OnKeyDown( const ButtonCode_t &code ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnKeyDown\n" ); #endif
// Pad input gives you pressed and released messages only for buttons
if ( IsJoystickCode( code ) ) { KeyValues *kvEvent = new KeyValues( "OnButtonPressed" ); KeyValues::AutoDelete autodelete( kvEvent ); kvEvent->SetInt( "code", code );
// chain to main system if this graphic doesn't handle it.
g_pGameUISystemMgrImpl->OnKeyCodeTyped( code ); g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); } else { if ( m_pGroup ) { m_pGroup->SetState( "AUTO_KEYDOWN" ); } } }
//-----------------------------------------------------------------------------
// Event that occurs when a key is released
//-----------------------------------------------------------------------------
void CHitArea::OnKeyUp( const ButtonCode_t &code ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnKeyUp\n" ); #endif
// Pad input gives you pressed and released messages only for buttons
if ( IsJoystickCode( code ) ) { KeyValues *kvEvent = new KeyValues( "OnButtonReleased" ); KeyValues::AutoDelete autodelete( kvEvent ); kvEvent->SetInt( "code", code );
// chain to main system if this graphic doesn't handle it.
g_pGameUISystemMgrImpl->OnKeyCodeTyped( code ); g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); } else { if ( m_pGroup ) { m_pGroup->SetState( "AUTO_KEYUP" ); } } }
//-----------------------------------------------------------------------------
// Event that occurs when a key code is typed
//-----------------------------------------------------------------------------
void CHitArea::OnKeyCodeTyped( const ButtonCode_t &code ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnKeyCodeTyped\n" ); #endif
Assert( g_pInputGameUI->GetKeyFocus() == this );
KeyValues *kvEvent = new KeyValues( "OnKeyTyped" ); KeyValues::AutoDelete autodelete( kvEvent ); kvEvent->SetInt( "code", code );
// chain to main system if this graphic doesn't handle it.
g_pGameUISystemMgrImpl->OnKeyCodeTyped( code ); g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); }
//-----------------------------------------------------------------------------
// Event that occurs when a key is typed
//-----------------------------------------------------------------------------
void CHitArea::OnKeyTyped( const wchar_t &unichar ) { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnKeyTyped\n" ); #endif
Assert( g_pInputGameUI->GetKeyFocus() == this );
// chain to main system if this graphic doesn't handle it.
g_pGameUISystemMgrImpl->OnKeyTyped( unichar ); }
//-----------------------------------------------------------------------------
// Event that occurs when key focus is lost
//-----------------------------------------------------------------------------
void CHitArea::OnLoseKeyFocus() { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnLoseKeyFocus\n" ); #endif
if ( m_pGroup ) { m_pGroup->SetState( "AUTO_LOSEKEYFOCUS" ); }
KeyValues *kvEvent = new KeyValues( "OnKeyFocusLost" ); KeyValues::AutoDelete autodelete( kvEvent );
// chain to main system if this graphic doesn't handle it.
g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); }
//-----------------------------------------------------------------------------
// Event that occurs when key focus is gained
//-----------------------------------------------------------------------------
void CHitArea::OnGainKeyFocus() { #if ( DEBUG_INPUT_EVENTS )
Msg( "CHitArea::OnGainKeyFocus\n" ); #endif
if ( m_pGroup ) { m_pGroup->SetState( "AUTO_GAINKEYFOCUS" ); }
KeyValues *kvEvent = new KeyValues( "OnKeyFocusGained" ); KeyValues::AutoDelete autodelete( kvEvent );
// chain to main system if this graphic doesn't handle it.
g_pGameUISystemMgrImpl->OnGameGraphicScriptEvent( this, kvEvent ); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CHitArea::OnDragStartCallScriptEvent( const int &cursorX, const int &cursorY ) {
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CHitArea::OnDragCallScriptEvent( const int &cursorX, const int &cursorY ) {
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CHitArea::OnDragStopCallScriptEvent( const int &cursorX, const int &cursorY ) {
}
//-----------------------------------------------------------------------------
// Handle focus updating on visibility change.
//-----------------------------------------------------------------------------
void CHitArea::SetVisible( bool bVisible ) { m_Geometry.m_bVisible = bVisible; m_bCanAcceptInput = bVisible; m_bCanStartDragging = false; if ( bVisible ) { g_pGameUISystemMgrImpl->ForceFocusUpdate(); } else { // Untested
g_pInputGameUI->GraphicHidden( this ); } }
//-----------------------------------------------------------------------------
// Handle commands from scripting
//-----------------------------------------------------------------------------
KeyValues * CHitArea::HandleScriptCommand( KeyValues *args ) { char const *szCommand = args->GetName();
if ( !Q_stricmp( "SetDragEnabled", szCommand ) ) { m_bDragEnabled = args->GetBool( "dragenabled", 0 ); return NULL; } else if ( !Q_stricmp( "SetMouseLeftClickedCommand", szCommand ) ) { m_OnMouseLeftClickedScriptCommand = args->GetString( "command", "" ); return NULL; }
if ( !Q_stricmp( "RequestFocus", szCommand ) ) { g_pGameUISystemMgrImpl->RequestKeyFocus( this, args ); return NULL; }
return CGameGraphic::HandleScriptCommand( args ); }
|