/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Mouse.cpp ////////////////////////////////////////////////////////////////////////////////
// Created: Colin Day, June 2001
// Desc: Basic mouse interactions
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Debug.h"
#include "Common/MessageStream.h"
#include "Common/GameEngine.h"
#include "Common/GlobalData.h"
#include "Common/INI.h"
#include "GameClient/Display.h"
#include "GameClient/DisplayStringManager.h"
#include "GameClient/GameClient.h"
#include "GameClient/GameText.h"
#include "GameClient/GameWindow.h"
#include "GameClient/InGameUI.h"
#include "GameClient/Keyboard.h"
#include "GameClient/Mouse.h"
#include "GameClient/GlobalLanguage.h"
#include "GameLogic/ScriptEngine.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
Mouse *TheMouse = NULL;
const char *Mouse::RedrawModeName[RM_MAX] = {
"Mouse:Windows",
"Mouse:W3D",
"Mouse:Poly",
"Mouse:DX8",
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
static const FieldParse TheMouseCursorFieldParseTable[] =
{
{ "CursorText", INI::parseAsciiString, NULL, offsetof( CursorInfo, cursorText ) },
{ "CursorTextColor", INI::parseRGBAColorInt, NULL, offsetof( CursorInfo, cursorTextColor ) },
{ "CursorTextDropColor", INI::parseRGBAColorInt, NULL, offsetof( CursorInfo, cursorTextDropColor ) },
{ "W3DModel", INI::parseAsciiString, NULL, offsetof( CursorInfo, W3DModelName ) },
{ "W3DAnim", INI::parseAsciiString, NULL, offsetof( CursorInfo, W3DAnimName ) },
{ "W3DScale", INI::parseReal, NULL, offsetof( CursorInfo, W3DScale ) },
{ "Loop", INI::parseBool, NULL, offsetof( CursorInfo, loop ) },
{ "Image", INI::parseAsciiString, NULL, offsetof( CursorInfo, imageName ) },
{ "Texture", INI::parseAsciiString, NULL, offsetof( CursorInfo, textureName ) },
{ "HotSpot", INI::parseICoord2D, NULL, offsetof( CursorInfo, hotSpotPosition ) },
{ "Frames", INI::parseInt, NULL, offsetof( CursorInfo, numFrames ) },
{ "FPS", INI::parseReal, NULL, offsetof( CursorInfo, fps)},
{ "Directions", INI::parseInt, NULL, offsetof( CursorInfo, numDirections ) },
};
static const FieldParse TheMouseFieldParseTable[] =
{
{ "TooltipFontName", INI::parseAsciiString,NULL, offsetof( Mouse, m_tooltipFontName ) },
{ "TooltipFontSize", INI::parseInt, NULL, offsetof( Mouse, m_tooltipFontSize ) },
{ "TooltipFontIsBold", INI::parseBool, NULL, offsetof( Mouse, m_tooltipFontIsBold ) },
{ "TooltipAnimateBackground", INI::parseBool, NULL, offsetof( Mouse, m_tooltipAnimateBackground ) },
{ "TooltipFillTime", INI::parseInt, NULL, offsetof( Mouse, m_tooltipFillTime ) },
{ "TooltipDelayTime", INI::parseInt, NULL, offsetof( Mouse, m_tooltipDelayTime ) },
{ "TooltipTextColor", INI::parseRGBAColorInt, NULL, offsetof( Mouse, m_tooltipColorText ) },
{ "TooltipHighlightColor", INI::parseRGBAColorInt, NULL, offsetof( Mouse, m_tooltipColorHighlight ) },
{ "TooltipShadowColor", INI::parseRGBAColorInt, NULL, offsetof( Mouse, m_tooltipColorShadow ) },
{ "TooltipBackgroundColor", INI::parseRGBAColorInt, NULL, offsetof( Mouse, m_tooltipColorBackground ) },
{ "TooltipBorderColor", INI::parseRGBAColorInt, NULL, offsetof( Mouse, m_tooltipColorBorder ) },
{ "TooltipWidth", INI::parsePercentToReal,NULL, offsetof( Mouse, m_tooltipWidth ) },
{ "CursorMode", INI::parseInt, NULL, offsetof( Mouse, m_currentRedrawMode ) },
{ "UseTooltipAltTextColor", INI::parseBool, NULL, offsetof( Mouse, m_useTooltipAltTextColor ) },
{ "UseTooltipAltBackColor", INI::parseBool, NULL, offsetof( Mouse, m_useTooltipAltBackColor ) },
{ "AdjustTooltipAltColor", INI::parseBool, NULL, offsetof( Mouse, m_adjustTooltipAltColor ) },
{ "OrthoCamera", INI::parseBool, NULL, offsetof( Mouse, m_orthoCamera ) },
{ "OrthoZoom", INI::parseReal, NULL, offsetof( Mouse, m_orthoZoom ) },
{ "DragTolerance", INI::parseUnsignedInt, NULL, offsetof( Mouse, m_dragTolerance) },
{ "DragTolerance3D", INI::parseUnsignedInt, NULL, offsetof( Mouse, m_dragTolerance3D) },
{ "DragToleranceMS", INI::parseUnsignedInt, NULL, offsetof( Mouse, m_dragToleranceMS) },
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Move the mouse in either relative or absolute coords */
//-------------------------------------------------------------------------------------------------
void Mouse::moveMouse( Int x, Int y, Int relOrAbs )
{
if( relOrAbs == MOUSE_MOVE_RELATIVE )
{
m_currMouse.pos.x += x;
m_currMouse.pos.y += y;
}
else
{
m_currMouse.pos.x = x;
m_currMouse.pos.y = y;
}
if( m_currMouse.pos.x > m_maxX )
m_currMouse.pos.x = m_maxX;
else if( m_currMouse.pos.x < m_minX )
m_currMouse.pos.x = m_minX;
if( m_currMouse.pos.y > m_maxY )
m_currMouse.pos.y = m_maxY;
else if( m_currMouse.pos.y < m_minY )
m_currMouse.pos.y = m_minY;
} // end moveMouse
//-------------------------------------------------------------------------------------------------
/** Get the current information for the mouse from the device */
//-------------------------------------------------------------------------------------------------
void Mouse::updateMouseData( )
{
static Bool busy = FALSE;
Int index = 0;
UnsignedByte result;
// prevent reentrancy in the event we make this mouse multi-threaded
if( busy == FALSE )
{
busy = TRUE;
// Get latest mouse events from DirectX
do
{
do
{
result = getMouseEvent( &m_mouseEvents[ index ], TRUE );
}
while( result == MOUSE_LOST );
index++;
}
while( (result != MOUSE_NONE) &&
(index < sizeof( m_mouseEvents ) / sizeof( MouseIO )) );
busy = FALSE;
} // end if
if( index > 0 )
m_eventsThisFrame = index - 1;
else
m_eventsThisFrame = 0;
if( index != 0 )
m_deadInputFrame = m_inputFrame;
} // end updateMouseData
//-------------------------------------------------------------------------------------------------
/** Combine mouse events into the main mouse variables */
//-------------------------------------------------------------------------------------------------
void Mouse::processMouseEvent( Int index )
{
Int movementType;
m_currMouse.leftEvent = MOUSE_EVENT_NONE;
m_currMouse.rightEvent = MOUSE_EVENT_NONE;
m_currMouse.middleEvent = MOUSE_EVENT_NONE;
m_currMouse.wheelPos = 0;
// what type of movement commands are we setup for
if( m_inputMovesAbsolute == TRUE )
movementType = MOUSE_MOVE_ABSOLUTE;
else
movementType = MOUSE_MOVE_RELATIVE;
// set the time of this event to the correct time
m_currMouse.time = m_mouseEvents[ index ].time;
if( index == 0 )
checkForDrag();
// add Mouse Position Changes to Master Position
moveMouse( m_mouseEvents[ index ].pos.x,
m_mouseEvents[ index ].pos.y,
movementType );
// Cumulate Wheel Adjustments
m_currMouse.wheelPos += m_mouseEvents[ index ].wheelPos;
// Check Left Mouse State
if( m_mouseEvents[ index ].leftFrame )
{
if( m_currMouse.leftState != m_mouseEvents[ index ].leftState )
{
// State Change
if( m_mouseEvents[ index ].leftState == MBS_Down )
{
// Mouse Down
m_currMouse.leftEvent = GWM_LEFT_DOWN;
m_currMouse.leftState = MBS_Down;
m_currMouse.leftFrame = m_inputFrame;
}
else if ( m_mouseEvents[ index ].leftState == MBS_DoubleClick )
{
// Mouse Double Click
m_currMouse.leftEvent = GWM_LEFT_DOUBLE_CLICK;
m_currMouse.leftState = MBS_DoubleClick;
m_currMouse.leftFrame = m_inputFrame;
}
else
{
// Mouse Up
m_currMouse.leftEvent = GWM_LEFT_UP;
m_currMouse.leftState = MBS_Up;
m_currMouse.leftFrame = m_inputFrame;
}
}
}
else if( m_currMouse.leftState != MBS_Up &&
( (m_prevMouse.leftEvent == GWM_LEFT_DOWN) ||
(m_prevMouse.leftEvent == GWM_LEFT_DRAG) ) )
{
m_currMouse.leftEvent = GWM_LEFT_DRAG;
}
// Check Right Mouse State
if( m_mouseEvents[ index ].rightFrame )
{
if( m_currMouse.rightState != m_mouseEvents[ index ].rightState )
{
// State Change
if( m_mouseEvents[ index ].rightState == MBS_Down )
{
// Mouse Down
m_currMouse.rightEvent = GWM_RIGHT_DOWN;
m_currMouse.rightState = MBS_Down;
m_currMouse.rightFrame = m_inputFrame;
}
else if( m_mouseEvents[ index ].rightState == MBS_DoubleClick )
{
// Mouse Double Click
m_currMouse.rightEvent = GWM_RIGHT_DOUBLE_CLICK;
m_currMouse.rightState = MBS_DoubleClick;
m_currMouse.rightFrame = m_inputFrame;
}
else
{
// Mouse Up
m_currMouse.rightEvent = GWM_RIGHT_UP;
m_currMouse.rightState = MBS_Up;
m_currMouse.rightFrame = m_inputFrame;
}
}
}
else if( m_currMouse.rightState != MBS_Up &&
( (m_prevMouse.rightEvent == GWM_RIGHT_DOWN) ||
(m_prevMouse.rightEvent == GWM_RIGHT_DRAG) ) )
{
m_currMouse.rightEvent = GWM_RIGHT_DRAG;
}
// Check Middle Mouse State
if( m_mouseEvents[ index ].middleFrame )
{
if( m_currMouse.middleState != m_mouseEvents[index].middleState )
{
// State Change
if( m_mouseEvents[index].middleState == MBS_Down )
{
m_currMouse.middleEvent = GWM_MIDDLE_DOWN;
m_currMouse.middleState = MBS_Down;
m_currMouse.middleFrame = m_inputFrame;
}
else if( m_mouseEvents[index].middleState == MBS_DoubleClick )
{
m_currMouse.middleEvent = GWM_MIDDLE_DOUBLE_CLICK;
m_currMouse.middleState = MBS_DoubleClick;
m_currMouse.middleFrame = m_inputFrame;
}
else
{
// Mouse Up
m_currMouse.middleEvent = GWM_MIDDLE_UP;
m_currMouse.middleState = MBS_Up;
m_currMouse.middleFrame = m_inputFrame;
}
}
}
else if( m_currMouse.middleState != MBS_Up &&
( (m_prevMouse.middleEvent == GWM_MIDDLE_DOWN) ||
(m_prevMouse.middleEvent == GWM_MIDDLE_DRAG) ) )
{
m_currMouse.middleEvent = GWM_MIDDLE_DRAG;
}
m_currMouse.deltaPos.x = m_currMouse.pos.x - m_prevMouse.pos.x;
m_currMouse.deltaPos.y = m_currMouse.pos.y - m_prevMouse.pos.y;
// DEBUG_LOG(("Mouse dx %d, dy %d, index %d, frame %d\n", m_currMouse.deltaPos.x, m_currMouse.deltaPos.y, index, m_inputFrame));
// // check if mouse is still and flag tooltip
// if( ((dx*dx) + (dy*dy)) < CURSOR_MOVE_TOL_SQ )
// {
// // cursor is still
// //m_stillTime++;
// Int delay = m_tooltipDelayTime;
// if(m_tooltipDelay >= 0 )
// delay = m_tooltipDelay;
//
// if( now - m_stillTime >= delay )
// {
// if (!m_displayTooltip)
// {
// m_highlightPos = 0;
// m_highlightUpdateStart = timeGetTime();
// }
//
// // display tooltip for current window
// m_displayTooltip = TRUE;
// }
// }
// else
// {
// // cursor moved
// //m_stillTime = 0;
// m_stillTime = now;
// m_displayTooltip = FALSE;
// }
m_prevMouse = m_currMouse;
} // end processMouseEvent
//-------------------------------------------------------------------------------------------------
/** Check for mouse drag */
//-------------------------------------------------------------------------------------------------
void Mouse::checkForDrag( void )
{
if( m_currMouse.leftState &&
( (m_prevMouse.leftEvent == GWM_LEFT_DOWN) ||
(m_prevMouse.leftEvent == GWM_LEFT_DRAG) ) )
{
m_currMouse.leftEvent = GWM_LEFT_DRAG;
}
if( m_currMouse.rightState &&
( (m_prevMouse.rightEvent == GWM_RIGHT_DOWN) ||
(m_prevMouse.rightEvent == GWM_RIGHT_DRAG) ) )
{
m_currMouse.rightEvent = GWM_RIGHT_DRAG;
}
if( m_currMouse.middleState &&
( (m_prevMouse.middleEvent == GWM_MIDDLE_DOWN) ||
(m_prevMouse.middleEvent == GWM_MIDDLE_DRAG) ) )
{
m_currMouse.middleEvent = GWM_MIDDLE_DRAG;
}
} // end checkForDrag
//-------------------------------------------------------------------------------------------------
/** Check for mouse click, using allowed drag forgiveness */
//-------------------------------------------------------------------------------------------------
Bool Mouse::isClick(const ICoord2D *anchor, const ICoord2D *dest, UnsignedInt previousMouseClick, UnsignedInt currentMouseClick)
{
ICoord2D delta;
delta.x = anchor->x - dest->x;
delta.y = anchor->y - dest->y;
// if the mouse hasn't moved further than the tolerance distance
// or the click took less than the tolerance duration
if ( abs(delta.x) > m_dragTolerance
|| abs(delta.y) > m_dragTolerance
|| currentMouseClick - previousMouseClick > m_dragToleranceMS)
{
return FALSE;
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
CursorInfo::CursorInfo( void )
{
// Added Sadullah Nader
// Initializations missing and needed
cursorName.clear();
cursorText.clear();
cursorTextColor.red = cursorTextColor.green = cursorTextColor.blue = 0;
cursorTextDropColor.red = cursorTextDropColor.blue = cursorTextDropColor.green = 0;
//
textureName.clear();
imageName.clear();
W3DModelName.clear();
W3DAnimName.clear();
W3DScale = 1.0f;
loop = TRUE;
//Assume hotspot is at the center of a 32x32 image.
hotSpotPosition.x=16;
hotSpotPosition.y=16;
numFrames = 1; //assume no animation
fps=20.0f;
numDirections=1;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Mouse::Mouse( void )
{
// device info
m_numButtons = 0;
m_numAxes = 0;
m_forceFeedback = FALSE;
//Added By Sadullah Nader
//Initializations missing and needed
m_dragTolerance = 0;
m_dragTolerance3D = 0;
m_dragToleranceMS = 0;
//
//m_tooltipString.clear(); // redundant
m_displayTooltip = FALSE;
m_tooltipDisplayString = NULL;
m_tooltipDelay = -1; // default value
// initialize all the mouse io data
memset( m_mouseEvents, 0, sizeof( m_mouseEvents ) );
memset( &m_currMouse, 0, sizeof( m_currMouse ) );
memset( &m_prevMouse, 0, sizeof( m_prevMouse ) );
m_minX = 0;
m_maxX = 0;
m_minY = 0;
m_maxY = 0;
m_eventsThisFrame = 0;
m_inputFrame = 0;
m_deadInputFrame =0;
m_inputMovesAbsolute = FALSE;
m_currentCursor = ARROW;
if (TheGlobalData && TheGlobalData->m_winCursors)
m_currentRedrawMode = RM_WINDOWS;
else
m_currentRedrawMode = RM_W3D;//RM_WINDOWS;
m_visible = FALSE;
m_tooltipFontName = "Times New Roman";
m_tooltipFontSize = 12;
m_tooltipFontIsBold = FALSE;
m_tooltipAnimateBackground = TRUE;
m_tooltipFillTime = 50;
m_tooltipDelayTime = 50;
#define setColor(x, r, g, b, a) { x.red = r; x.green = g; x.blue = b; x.alpha = a; }
setColor(m_tooltipColorText, 220, 220, 220, 255);
setColor(m_tooltipColorHighlight, 255, 255, 0, 255);
setColor(m_tooltipColorShadow, 0, 0, 0, 255);
setColor(m_tooltipColorBackground, 20, 20, 0, 127);
setColor(m_tooltipColorBorder, 0, 0, 0, 255);
#undef setColor
m_tooltipWidth = 15.0f;
m_lastTooltipWidth = 0.0f;
m_useTooltipAltTextColor = FALSE;
m_useTooltipAltBackColor = FALSE;
m_adjustTooltipAltColor = FALSE;
m_orthoCamera = FALSE;
m_orthoZoom = 1.0f;
m_isTooltipEmpty = TRUE;
m_cursorTextDisplayString = NULL;
m_cursorTextColor.red = 255;
m_cursorTextColor.green = 255;
m_cursorTextColor.blue = 255;
m_cursorTextColor.alpha = 255;
m_cursorTextDropColor.red = 255;
m_cursorTextDropColor.green = 255;
m_cursorTextDropColor.blue = 255;
m_cursorTextDropColor.alpha = 255;
m_highlightPos = 0;
m_highlightUpdateStart = 0;
m_stillTime = 0;
m_tooltipTextColor.red = 255;
m_tooltipTextColor.green = 255;
m_tooltipTextColor.blue = 255;
m_tooltipTextColor.alpha = 255;
m_tooltipBackColor.red = 0;
m_tooltipBackColor.green = 0;
m_tooltipBackColor.blue = 0;
m_tooltipBackColor.alpha = 255;
} // end Mouse
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Mouse::~Mouse( void )
{
if(m_tooltipDisplayString)
TheDisplayStringManager->freeDisplayString(m_tooltipDisplayString);
m_tooltipDisplayString = NULL;
if( m_cursorTextDisplayString )
TheDisplayStringManager->freeDisplayString( m_cursorTextDisplayString );
m_cursorTextDisplayString = NULL;
} // end ~Mouse
/**Had to move this out of main init() because I need this data to properly initialize
the Win32 version of the mouse (by preloading resources before D3D device is created).*/
void Mouse::parseIni(void)
{
INI ini;
ini.load( AsciiString( "Data\\INI\\Mouse.ini" ), INI_LOAD_OVERWRITE, NULL );
}
//-------------------------------------------------------------------------------------------------
/** Initialize the mouse */
//-------------------------------------------------------------------------------------------------
void Mouse::init( void )
{
if (TheGlobalData && TheGlobalData->m_winCursors)
m_currentRedrawMode = RM_WINDOWS;
// device info
m_numButtons = 2; // by default just have 2 buttons
m_numAxes = 2; // by default a normal mouse moves in a 2d plane
m_forceFeedback = FALSE;
mouseNotifyResolutionChange();
m_tooltipString.clear(); // redundant
m_displayTooltip = FALSE;
// initialize all the mouse io data
memset( m_mouseEvents, 0, sizeof( m_mouseEvents ) );
memset( &m_currMouse, 0, sizeof( m_currMouse ) );
memset( &m_prevMouse, 0, sizeof( m_prevMouse ) );
m_minX = 0;
m_maxX = 799;
m_minY = 0;
m_maxY = 599;
m_inputFrame = 0;
m_deadInputFrame =0;
m_inputMovesAbsolute = FALSE;
m_eventsThisFrame = 0;
m_currentCursor = ARROW;
// allocate a new display string
m_cursorTextDisplayString = TheDisplayStringManager->newDisplayString();
} // end init
//-------------------------------------------------------------------------------------------------
/** Tell mouse system display resolution changed. */
//-------------------------------------------------------------------------------------------------
void Mouse::mouseNotifyResolutionChange( void )
{
if(m_tooltipDisplayString)
TheDisplayStringManager->freeDisplayString(m_tooltipDisplayString);
m_tooltipDisplayString = NULL;
m_tooltipDisplayString = TheDisplayStringManager->newDisplayString();
if (TheGlobalLanguageData && TheGlobalLanguageData->m_tooltipFontName.name.isNotEmpty())
{
m_tooltipDisplayString->setFont( TheFontLibrary->getFont(
TheGlobalLanguageData->m_tooltipFontName.name,
TheGlobalLanguageData->adjustFontSize(TheGlobalLanguageData->m_tooltipFontName.size),
TheGlobalLanguageData->m_tooltipFontName.bold) );
}
else
{
m_tooltipDisplayString->setFont( TheFontLibrary->getFont(
m_tooltipFontName,
TheGlobalLanguageData->adjustFontSize(m_tooltipFontSize),
m_tooltipFontIsBold ) );
}
m_tooltipDisplayString->setWordWrap(120);
} // end reset
//-------------------------------------------------------------------------------------------------
/** Reset mouse system */
//-------------------------------------------------------------------------------------------------
void Mouse::reset( void )
{
///@ todo Write Mouse::reset() if there needs to be anything here
// reset the text of the cursor text
if ( m_cursorTextDisplayString )
m_cursorTextDisplayString->reset();
} // end reset
//-------------------------------------------------------------------------------------------------
/** Update the states of the mouse position and buttons */
//-------------------------------------------------------------------------------------------------
void Mouse::update( void )
{
// increment input frame
m_inputFrame++;
// update the mouse data
updateMouseData( );
} // end update
//-------------------------------------------------------------------------------------------------
/** Given the current state of this input device, turn the raw input
* data into raw stream messages and place those messages on the stream.
* NOTE that the click messages replace up messages in the mouse, so we
* are going to propagate those click messages in addition to up messages */
//-------------------------------------------------------------------------------------------------
void Mouse::createStreamMessages( void )
{
// santiy
if( TheMessageStream == NULL )
return; // no place to put messages
GameMessage *msg = NULL;
UnsignedInt now = timeGetTime();
// basic position messages are always created
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_POSITION );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
Int delay = m_tooltipDelayTime;
if(m_tooltipDelay >= 0 )
delay = m_tooltipDelay;
if( TheGlobalData->m_scriptDebug )
{
//No delay while scriptdebugging!
delay = 0;
}
if( now - m_stillTime >= delay )
{
if (!m_displayTooltip)
{
m_highlightPos = 0;
m_highlightUpdateStart = timeGetTime();
}
// display tooltip for current window
m_displayTooltip = TRUE;
}
else
{
//DEBUG_LOG(("%d %d %d %d\n", TheGameClient->getFrame(), delay, now, m_stillTime));
m_displayTooltip = FALSE;
}
for (Int i = 0; i < m_eventsThisFrame; ++i)
{
processMouseEvent(i);
if (m_currMouse.deltaPos.x || m_currMouse.deltaPos.y)
m_stillTime = now;
// button messages
msg = NULL;
switch( m_currMouse.leftEvent )
{
case GWM_LEFT_DOWN:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_LEFT_BUTTON_DOWN );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_LEFT_DOUBLE_CLICK:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_LEFT_DOUBLE_CLICK );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_LEFT_UP:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_LEFT_BUTTON_UP );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_LEFT_DRAG:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_LEFT_DRAG );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendPixelArgument( m_currMouse.deltaPos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
break;
} // end switch
msg = NULL;
switch( m_currMouse.middleEvent )
{
//-------------------------------------------------------------------------
case GWM_MIDDLE_DOWN:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_MIDDLE_BUTTON_DOWN );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_MIDDLE_DOUBLE_CLICK:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_MIDDLE_DOUBLE_CLICK );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_MIDDLE_UP:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_MIDDLE_BUTTON_UP );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_MIDDLE_DRAG:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_MIDDLE_DRAG );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendPixelArgument( m_currMouse.deltaPos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
break;
} // end switch
msg = NULL;
switch( m_currMouse.rightEvent )
{
//-------------------------------------------------------------------------
case GWM_RIGHT_DOWN:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_DOWN );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_RIGHT_DOUBLE_CLICK:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_RIGHT_DOUBLE_CLICK );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_RIGHT_UP:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_UP );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
msg->appendIntegerArgument( m_currMouse.time );
break;
case GWM_RIGHT_DRAG:
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_RIGHT_DRAG );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendPixelArgument( m_currMouse.deltaPos );
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
break;
} // end switch
// wheel pos
msg = NULL;
if( m_currMouse.wheelPos != 0 )
{
msg = TheMessageStream->appendMessage( GameMessage::MSG_RAW_MOUSE_WHEEL );
msg->appendPixelArgument( m_currMouse.pos );
msg->appendIntegerArgument( m_currMouse.wheelPos / 120 ); // wheel delta
msg->appendIntegerArgument( TheKeyboard->getModifierFlags() );
} // end if
} // end for
} // end createStreamMessages
//-------------------------------------------------------------------------------------------------
/** Set the string to display at the cursor for the tooltip */
//-------------------------------------------------------------------------------------------------
void Mouse::setCursorTooltip( UnicodeString tooltip, Int delay, const RGBColor *color, Real width )
{
//DEBUG_LOG(("%d Tooltip: %ls\n", TheGameClient->getFrame(), tooltip.str()));
m_isTooltipEmpty = tooltip.isEmpty();
m_tooltipDelay = delay;
Bool forceRecalc = FALSE;
if ( !tooltip.isEmpty() && width != m_lastTooltipWidth )
{
forceRecalc = TRUE;
Int widthInPixels = (Int)(TheDisplay->getWidth()*m_tooltipWidth*width);
if (widthInPixels < 10)
{
widthInPixels = 120;
}
else if (widthInPixels > TheDisplay->getWidth())
{
widthInPixels = TheDisplay->getWidth();
}
//DEBUG_LOG(("Setting tooltip width to %d pixels (%g%% of the normal tooltip width)\n", widthInPixels, width*100));
m_tooltipDisplayString->setWordWrap( widthInPixels );
m_lastTooltipWidth = width;
}
if (forceRecalc || !m_isTooltipEmpty && tooltip.compare(m_tooltipDisplayString->getText()))
{
m_tooltipDisplayString->setText(tooltip);
//DEBUG_LOG(("Tooltip: %ls\n", tooltip.str()));
}
if (color)
{
if (m_useTooltipAltTextColor)
{
if (m_adjustTooltipAltColor)
{
m_tooltipTextColor.red = REAL_TO_INT((color->red + 1.0f) * 255.0f / 2.0f);
m_tooltipTextColor.green = REAL_TO_INT((color->green + 1.0f) * 255.0f / 2.0f);
m_tooltipTextColor.blue = REAL_TO_INT((color->blue + 1.0f) * 255.0f / 2.0f);
}
else
{
m_tooltipTextColor.red = REAL_TO_INT(color->red * 255.0f);
m_tooltipTextColor.green = REAL_TO_INT(color->green * 255.0f);
m_tooltipTextColor.blue = REAL_TO_INT(color->blue * 255.0f);
}
m_tooltipTextColor.alpha = m_tooltipColorText.alpha;
}
if (m_useTooltipAltBackColor)
{
if (m_adjustTooltipAltColor)
{
m_tooltipBackColor.red = REAL_TO_INT(color->red * 255.0f * 0.5f);
m_tooltipBackColor.green = REAL_TO_INT(color->green * 255.0f * 0.5f);
m_tooltipBackColor.blue = REAL_TO_INT(color->blue * 255.0f * 0.5f);
}
else
{
m_tooltipBackColor.red = REAL_TO_INT(color->red * 255.0f);
m_tooltipBackColor.green = REAL_TO_INT(color->green * 255.0f);
m_tooltipBackColor.blue = REAL_TO_INT(color->blue * 255.0f);
}
m_tooltipBackColor.alpha = m_tooltipColorBackground.alpha;
}
}
else
{
m_tooltipTextColor = m_tooltipColorText;
m_tooltipBackColor = m_tooltipColorBackground;
}
} // end setCursorTooltip
// ------------------------------------------------------------------------------------------------
/** Set the text for the mouse cursor ... note that this is *NOT* the tooltip text we
* can set to be at the mouse position */
// ------------------------------------------------------------------------------------------------
void Mouse::setMouseText( UnicodeString text,
const RGBAColorInt *color,
const RGBAColorInt *dropColor )
{
// sanity, if no display string has been created, get out of here
if( m_cursorTextDisplayString == NULL )
return;
// set the text into the cursor display string
m_cursorTextDisplayString->setText( text );
// save the colors to draw in
if( color )
m_cursorTextColor = *color;
if( dropColor )
m_cursorTextDropColor = *dropColor;
} // end setMouseText
//-------------------------------------------------------------------------------------------------
/** Move the mouse to the position */
//-------------------------------------------------------------------------------------------------
void Mouse::setPosition( Int x, Int y )
{
m_currMouse.pos.x = x;
m_currMouse.pos.y = y;
} // end setPosition
//-------------------------------------------------------------------------------------------------
/** This default implemtation of SetMouseLimits will just set the limiting
* rectangle to be the width and height of the game display with the
* origin in the upper left at (0,0). However, if the game is running
* in a windowed mode then these limits should reflect the SCREEN
* coords that the mouse is allowed to move in. Also, if the game is in
* a window you may want to adjust for any title bar available in
* the operating system. For system specific limits and windows etc,
* just override this function in the device implementation of the mouse */
//-------------------------------------------------------------------------------------------------
void Mouse::setMouseLimits( void )
{
m_minX = 0;
m_minY = 0;
if( TheDisplay )
{
m_maxX = TheDisplay->getWidth();
m_maxY = TheDisplay->getHeight();
} // end if
} // end setMouseLimits
//-------------------------------------------------------------------------------------------------
/** Draw the mouse */
//-------------------------------------------------------------------------------------------------
void Mouse::draw( void )
{
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Mouse::resetTooltipDelay( void )
{
m_stillTime = timeGetTime();
m_displayTooltip = FALSE;
}
//-------------------------------------------------------------------------------------------------
/** Draw the mouse tooltip if one is set */
//-------------------------------------------------------------------------------------------------
void Mouse::drawTooltip( void )
{
if (TheScriptEngine->getFade()!=ScriptEngine::FADE_NONE) {
return;
}
/// @todo: Still need to put in display logic so it puts the tool tips in a visable position on the edge of the screen
if( m_displayTooltip && TheDisplay && m_tooltipDisplayString && (m_tooltipDisplayString->getTextLength() > 0) && !m_isTooltipEmpty)
{
Int width, xPos;
Int height, yPos;
m_tooltipDisplayString->getSize(&width,&height);
xPos = m_currMouse.pos.x + 20;
yPos = m_currMouse.pos.y;// + 20;
if( xPos + width + 4 > m_maxX ) // +4 for spill
{
//xPos = m_maxX - width;
xPos -= 20 + width;
}
if( yPos + height + 4 > m_maxY ) // +4 for spill
{
//yPos = m_maxY - height;
yPos -= /*40 +*/ height;
}
Int boxWidth = (m_tooltipAnimateBackground)?(min(width, m_highlightPos)):width;
#define GMC(x) GameMakeColor(x.red, x.green, x.blue, x.alpha)
#define COLOR(x) GMC(m_tooltipColor##x)
TheDisplay->drawFillRect(xPos, yPos, boxWidth + 2,height + 2, GMC(m_tooltipBackColor));//GameMakeColor(0,0,0,125));
TheDisplay->drawOpenRect(xPos, yPos, boxWidth + 2,height + 2, 1.0, COLOR(Border));//GameMakeColor(20,20,20,255));
// build clip rect
IRegion2D clipRegion;
clipRegion.lo.x = xPos+2;
clipRegion.lo.y = yPos+1;
clipRegion.hi.x = xPos+2+m_highlightPos;
clipRegion.hi.y = yPos+1+height;
m_tooltipDisplayString->setClipRegion(&clipRegion);
m_tooltipDisplayString->draw(xPos +2, yPos +1, GMC(m_tooltipTextColor), COLOR(Shadow));//GameMakeColor(220,220,220,255),GameMakeColor(20,20,20,125));
// highlight section
const Int HIGHLIGHT_WIDTH = 15;
clipRegion.lo.x = xPos+2+m_highlightPos-HIGHLIGHT_WIDTH;
clipRegion.lo.y = yPos+1;
clipRegion.hi.x = xPos+2+m_highlightPos;
clipRegion.hi.y = yPos+1+height;
m_tooltipDisplayString->setClipRegion(&clipRegion);
m_tooltipDisplayString->draw(xPos +2, yPos +1, COLOR(Highlight), COLOR(Shadow));//GameMakeColor(255,255,0,255),GameMakeColor(20,20,20,125));
// get ready for the next part of the anim
if (m_highlightPos < width + HIGHLIGHT_WIDTH)
{
UnsignedInt now = timeGetTime();
m_highlightPos = (width*(now-m_highlightUpdateStart))/m_tooltipFillTime;
}
} // end if
} // end drawTooltip
// ------------------------------------------------------------------------------------------------
/** Draw the cursor text at the mouse position. Note that this is *NOT* the tooltip text */
// ------------------------------------------------------------------------------------------------
void Mouse::drawCursorText( void )
{
// sanity
if( m_cursorTextDisplayString == NULL )
return;
// get the colors to draw the text in an acceptable format
Color color, dropColor;
color = GameMakeColor( m_cursorTextColor.red,
m_cursorTextColor.green,
m_cursorTextColor.blue,
m_cursorTextColor.alpha );
dropColor = GameMakeColor( m_cursorTextDropColor.red,
m_cursorTextDropColor.green,
m_cursorTextDropColor.blue,
m_cursorTextDropColor.alpha );
// get the size of the text to draw
Int width, height;
m_cursorTextDisplayString->getSize( &width, &height );
// draw the text around the cursor position
Int x, y;
x = m_currMouse.pos.x - width / 2;
y = m_currMouse.pos.y - height / 2;
m_cursorTextDisplayString->draw( x, y, color, dropColor );
} // end drawCursorText
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Int Mouse::getCursorIndex(const AsciiString& name)
{
if (name.isEmpty())
return INVALID_MOUSE_CURSOR;
/** @todo This is silly to have to define these names from INI in the code ...
* that should be changed (CBD) */
static char *CursorININames[NUM_MOUSE_CURSORS] =
{
//"InvalidMouseCursor", // this entry is not actually a mouse cursor, but just a
// reminder that it does exist
"None",
"Normal",
"Arrow",
"Scroll",
"Target",
"Move",
"AttackMove",
"AttackObj",
"ForceAttackObj",
"ForceAttackGround",
"Build",
"InvalidBuild",
"GenericInvalid",
"Select",
"EnterFriendly",
"EnterAggressive",
"SetRallyPoint",
"GetRepaired",
"GetHealed",
"DoRepair",
"ResumeConstruction",
"CaptureBuilding",
"SnipeVehicle",
"LaserGuidedMissiles",
"TankHunterTNTAttack",
"StabAttack",
"PlaceRemoteCharge",
"PlaceTimedCharge",
"Defector",
#ifdef ALLOW_DEMORALIZE
"Demoralize",
#endif
"Dock",
#ifdef ALLOW_SURRENDER
"PickUpPrisoner",
"ReturnToPrison",
#endif
"FireFlame",
#ifdef ALLOW_SURRENDER
"FireTranqDarts",
"FireStunBullets",
#endif
"FireBomb",
"PlaceBeacon",
"DisguiseAsVehicle",
"Waypoint",
"OutRange",
"StabAttackInvalid",
"PlaceChargeInvalid",
"Hack",
"ParticleUplinkCannon",
};
for (Int i=0; icursorText.isEmpty() == FALSE )
setMouseText( TheGameText->fetch( cursorInfo->cursorText.str() ),
&(cursorInfo->cursorTextColor),
&(cursorInfo->cursorTextDropColor) );
else
setMouseText( UnicodeString( L"" ), NULL, NULL );
} // end if
} // end setCursor
//-------------------------------------------------------------------------------------------------
/** Parse MouseCursor entry */
//-------------------------------------------------------------------------------------------------
void INI::parseMouseCursorDefinition( INI* ini )
{
const char *c;
AsciiString name;
// read the name
c = ini->getNextToken();
name.set( c );
if( TheMouse )
{
Int index = TheMouse->getCursorIndex(name);
if (index != Mouse::INVALID_MOUSE_CURSOR)
{
CursorInfo *cursorInfo = &(TheMouse->m_cursorInfo[index]);
cursorInfo->cursorName = name;
// parse the ini weapon definition
ini->initFromINI( cursorInfo, TheMouseCursorFieldParseTable );
}
}
}
//-------------------------------------------------------------------------------------------------
/** Parse MouseCursor entry */
//-------------------------------------------------------------------------------------------------
void INI::parseMouseDefinition( INI* ini )
{
if( TheMouse )
{
// parse the ini weapon definition
ini->initFromINI( TheMouse, TheMouseFieldParseTable );
}
}