/* ** 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 ); } }