The-Simpsons-Hit-and-Run/game/code/input/FEMouse.cpp

338 lines
11 KiB
C++

#include <input/FEMouse.h>
#include <presentation/gui/guimanager.h>
#include <presentation/gui/guisystem.h>
#include <presentation/gui/guiuserinputhandler.h>
#include <gameflow/gameflow.h>
#include <contexts/contextenum.h>
#include <contexts/gameplay/gameplaycontext.h>
#include <main/win32platform.h>
#ifdef ENABLE_DYNA_LOADED_IMAGES
const char* DYNAMIC_RESOURCES_DIR = "art\\frontend\\dynaload\\images\\";
#endif
const float WIN_TO_P3D_POINT_SCALE_FACTOR = 2.0f;
/******************************************************************************
Construction/Destruction
*****************************************************************************/
FEMouse::FEMouse()
: m_bMoved( false ),
m_bClickable( true ),
m_bSelectable( true ),
m_bMovable( true ),
m_oldPositionX(0),
m_oldPositionY(0),
m_hotSpotType( HOTSPOT_BUTTON ),
m_horzDir( NO_HORZ_MOVEMENT ),
m_bClickAndStop(false),
m_bInGame( false ),
m_bInGameOverride( false ),
m_inGamePosX( 0 ),
m_inGamePosY( 0 ),
m_buttonDownSelection( 0 )
{
m_pCursor = new MouseCursor();
memset( m_button, 0, sizeof(m_button));
}
FEMouse::~FEMouse()
{
delete m_pCursor;
m_pCursor = NULL;
}
void FEMouse::InitMouseCursor( tDrawable* pCursor )
{
m_pCursor->Set( pCursor );
}
eFEMouseHorzDir FEMouse::OnSliderHorizontalClickDrag() const
{
if (IsLeftButtonDown() &&
m_hotSpotType == HOTSPOT_SLIDER &&
m_horzDir != NO_HORZ_MOVEMENT &&
m_bMoved == true )
{
return m_horzDir;
}
else return NO_HORZ_MOVEMENT;
}
bool FEMouse::DidWeMove( int newX, int newY ) const
{
return ( (newX != m_oldPositionX) || (newY != m_oldPositionY) );
}
void FEMouse::Move( int mouseX, int mouseY, long screenWidth, long screenHeight )
{
if( !m_bMovable ) return;
m_bMoved = true;
// let us know what direction it moved horizontally.
if( m_oldPositionX < mouseX ) m_horzDir = MDIR_RIGHT;
else if( m_oldPositionX > mouseX ) m_horzDir = MDIR_LEFT;
else m_horzDir = NO_HORZ_MOVEMENT;
m_oldPositionX = mouseX;
m_oldPositionY = mouseY;
// Normalize the windows mouse coordinates that are in pixel units, to 2d world units.
// From ( -1 to 1 ) on the major axis and ( -height/width to -height/width ) on the minor.
// We must scale the incoming coordinates and offset them to the center.
// We must also handle the difference in width and height, to compensate for the aspect
// ratio of the display.
const float ASPECT = (float)screenWidth/(float)screenHeight; // The screen aspect ratio.
float deltaX = ( (mouseX/( screenWidth/WIN_TO_P3D_POINT_SCALE_FACTOR )) - 1.0f );
float deltaY = ( 1.0f - (mouseY/(screenHeight/WIN_TO_P3D_POINT_SCALE_FACTOR)))/ASPECT;
m_pCursor->SetPos( deltaX/WIN_TO_P3D_POINT_SCALE_FACTOR, deltaY/WIN_TO_P3D_POINT_SCALE_FACTOR );
//rDebugPrintf("Mouse moved to X: %g Y: %g \n", deltaX, deltaY);
}
void FEMouse::Update()
{
bool ingame = m_bInGame && ! m_bInGameOverride;
bool bLoading = (GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY) ||
(GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT) ||
(GetGameFlow()->GetCurrentContext() == CONTEXT_EXIT);
if( ingame || bLoading ) return;
if( !m_pCursor->IsVisible() ) return;
CGuiWindow* pCurrentWindow = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow();
CGuiMenu* pCurrentMenu = pCurrentWindow->HasMenu();
if( !pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade()) )//m_bSelectable )
{
// Dusit here,
// Took out the update only on mouse moved test.
// This is a problem when a new front-end screen item appears underneath
// the mouse cursor, but the mouse hasn't moved yet, so it can't click
// on the front-end item. So we either need to get notified when that
// happens and manually update the mouse focus via callback, or just
// do it every frame. I think it's reasonable to do it every frame; otherwise, if
// the call is really expensive, we'd end up with low framerate while mouse is
// moving and good framerate when mouse is stationary (which is not
// a good optimization).
//
//if( m_bMoved ) //Only update the hotspot checking if the mouse moved!
//{
//
m_hotSpotType = pCurrentWindow->CheckCursorAgainstHotspots( m_pCursor->XPos()*WIN_TO_P3D_POINT_SCALE_FACTOR,
m_pCursor->YPos()*WIN_TO_P3D_POINT_SCALE_FACTOR);
if( m_hotSpotType == HOTSPOT_NONE )
{
// if the user is still holding onto the button, don't tamper with its state.
// we deal with that case later (just below).
if( m_button[ BUTTON_LEFT ] != BUTTON_CLICKHOLD )
{
m_button[ BUTTON_LEFT ] = BUTTON_IDLE;
}
}
m_bMoved = false;
rAssert( !m_bMoved );
if( pCurrentMenu )
{
if( m_button[ BUTTON_LEFT ] == BUTTON_CLICKHOLD )
{
// deal with highlighting the hotspot...
CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
switch( m_hotSpotType )
{
case HOTSPOT_BUTTON:
if( m_buttonDownSelection == pCurrentMenu->GetSelection() && m_bClickable )
{
pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 1 );
}
break;
case HOTSPOT_NONE:
if( m_bClickable )
{
pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
}
break;
default: break;
}
}
else
{
// If nothing is being held down we should eliminate the highlighting.
// This fixes the problem where if you hold down LMB then right-click to
// escape to previous screen, when you go back to the first screen, the
// highlighting will still be in effect.
//
if( m_bClickable && m_bSelectable )
{
pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
}
}
}
}
m_pCursor->Render();
}
void FEMouse::ButtonDown( eFEMouseButton buttonType )
{
// don't allow clicking when selectable not on
CGuiMenu* pCurrentMenu = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow()->HasMenu();
if( !(!pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade())) )//!m_bSelectable )
{
return;
}
m_button[buttonType] = BUTTON_CLICKHOLD;
// what follows concerns only left mouse button
if( buttonType != BUTTON_LEFT )
{
return;
}
// remember what menu item it was that we pressed down on...
m_buttonDownSelection = pCurrentMenu ? pCurrentMenu->GetSelection() : -1;
// deal with highlighting the hotspot...
if( pCurrentMenu )
{
CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
switch( m_hotSpotType )
{
case HOTSPOT_BUTTON:
if( m_bClickable )
{
pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 1 );
}
break;
case HOTSPOT_NONE:
if( m_bClickable )
{
pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
}
break;
default: break;
}
}
}
void FEMouse::ButtonUp( enum eFEMouseButton buttonType )
{
// don't allow clicking when selectable not on
CGuiMenu* pCurrentMenu = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow()->HasMenu();
if( !(!pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade())) )//!m_bSelectable )
{
return;
}
m_button[ buttonType ] = BUTTON_CLICKED;
// what follows concerns only left mouse button
if( buttonType != BUTTON_LEFT )
{
return;
}
/////////////////////////////////////////////////////////////////
// Deal with the consequence of left-clicking a menu button
//
// if we release mouse button on something other than the item we
// were on when we pressed the mouse button, we don't do anything
//
int currSelection = pCurrentMenu ? pCurrentMenu->GetSelection() : -1;
if( currSelection != m_buttonDownSelection )
{
return;
}
// at this point, the hotspot we're currently on matches the
// one we were at when we pressed the left mouse button... so click!
CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
switch( m_hotSpotType )
{
case HOTSPOT_BUTTON:
userInputHandler->Select();
m_bSelectable = false; // need to disable selectable IMMEDIATELY (not when screen exits)
break;
case HOTSPOT_ARROWLEFT:
userInputHandler->Left();
break;
case HOTSPOT_ARROWRIGHT:
userInputHandler->Right();
break;
case HOTSPOT_ARROWUP:
userInputHandler->Up();
break;
case HOTSPOT_ARROWDOWN:
userInputHandler->Down();
break;
case HOTSPOT_LTRIGGER:
userInputHandler->L1();
break;
case HOTSPOT_RTRIGGER:
userInputHandler->R1();
break;
default: break;
}
// reset left button state
m_button[ BUTTON_LEFT ] = BUTTON_IDLE;
}
void FEMouse::SetInGameMode( bool ingame )
{
m_bInGame = ingame;
SetupInGameMode();
}
void FEMouse::SetInGameOverride( bool override )
{
m_bInGameOverride = override;
SetupInGameMode();
}
void FEMouse::SetupInGameMode()
{
bool ingame = m_bInGame && ! m_bInGameOverride;
m_pCursor->SetVisible( !ingame );
if( ingame )
{
RECT windowRect;
GetWindowRect( Win32Platform::GetInstance()->GetHwnd(), &windowRect );
// Save the old cursor position.
POINT oldPos;
GetCursorPos( & oldPos );
m_inGamePosX = oldPos.x;
m_inGamePosY = oldPos.y;
// Center the cursor.
SetCursorPos( ( windowRect.left + windowRect.right ) / 2,
( windowRect.top + windowRect.bottom ) / 2 );
// Set up the clipping rectangle.
windowRect.top += 30;
windowRect.bottom -= 10;
windowRect.left += 10;
windowRect.right -= 10;
ClipCursor( &windowRect );
}
else
{
// Remove the clipping rectangle.
ClipCursor( NULL );
// Restore the old cursor position.
if( m_inGamePosX != -1 )
{
SetCursorPos( m_inGamePosX, m_inGamePosY );
m_inGamePosX = -1;
}
}
}