| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622 |
- /*
- ** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: ControlBarCallback.cpp ///////////////////////////////////////////////////////////////////
- // Author: Colin Day - October 2001
- // Desc: GUI Control bar at the bottom of the screen that houses the
- // the build buttons, radar etc.
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/NameKeyGenerator.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/MessageStream.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Radar.h"
- #include "GameClient/AnimateWindowManager.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/GameWindow.h"
- #include "GameClient/Gadget.h"
- #include "GameClient/GadgetTextEntry.h"
- #include "GameClient/GameClient.h"
- #include "GameClient/GUICallbacks.h"
- #include "GameClient/InGameUI.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/GameWindowManager.h"
- #include "GameClient/LanguageFilter.h"
- #include "GameClient/CommandXlat.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/ScriptEngine.h"
- //external declarations of the Gadgets the callbacks can use
- WindowLayout *popupCommunicatorLayout = NULL;
- //-------------------------------------------------------------------------------------------------
- /** Input procedure for the left HUD */
- //-------------------------------------------------------------------------------------------------
- WindowMsgHandledType LeftHUDInput( GameWindow *window, UnsignedInt msg,
- WindowMsgData mData1, WindowMsgData mData2 )
- {
-
- // get player
- Player *player = ThePlayerList->getLocalPlayer();
- //
- // if the player doesn't have a radar, or the radar is hidden, and the radar is not being
- // forced to on, we just eat input over the radar window
- //
- if( !TheRadar->isRadarForced() && (TheRadar->isRadarHidden() || !player->hasRadar()) )
- return MSG_HANDLED;
-
- // If the middle mouse button is depressed, then just let the message fall all the
- // way back to the usual middle mouse button processing.
- // jkmcd
- if( TheMouse->getMouseStatus()->middleState == MBS_Down )
- return MSG_IGNORED;
- switch( msg )
- {
- /** @todo
- This is wrong. The radar should be in the message stream, and eat all messages and propagate them
- as a new message with the coords converted to world coords and the message flagged as being from
- the radar. This would let all of the normal processing occur, and allow individual commands to easily
- reject being used on the radar.
- */
- // ------------------------------------------------------------------------
- case GWM_NONE:
- case GWM_MOUSE_ENTERING:
- case GWM_MOUSE_LEAVING:
- {
- //
- // consider changing the mouse cursor if we are not in the process of firing
- // targeted "superweapons" which we can use the radar itself to fire
- //
- Bool targeting = FALSE;
- const CommandButton *command = TheInGameUI->getGUICommand();
- if( command
- && (command->getCommandType() == GUI_COMMAND_SPECIAL_POWER || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER)
- && BitTest( command->getOptions(), NEED_TARGET_POS ) )
- targeting = TRUE;
- if( targeting == FALSE )
- {
- const DrawableList *drawableList = TheInGameUI->getAllSelectedLocalDrawables();
- Mouse::MouseCursor cur = Mouse::ARROW;
- if (!(drawableList->empty() || msg == GWM_MOUSE_LEAVING))
- {
- if (command && command->getCommandType() == GUI_COMMAND_ATTACK_MOVE)
- {
- cur = Mouse::ATTACKMOVETO;
- }
- else
- {
- cur = Mouse::MOVETO;
- }
- }
- // Groovy
- TheMouse->setCursor(cur);
- } // end if
- return MSG_HANDLED;
- }
- // --------------------------------------------------------------------------------------------
- case GWM_MOUSE_POS:
- {
- // get mouse position
- ICoord2D mouse;
- mouse.x = mData1 & 0xFFFF;
- mouse.y = mData1 >> 16;
- // get window screen position
- ICoord2D screenPos;
- window->winGetScreenPosition( &screenPos.x, &screenPos.y );
- // set mouse position to be relative to this window
- mouse.x -= screenPos.x;
- mouse.y -= screenPos.y;
- // is the mouse in the radar window
- ICoord2D radar;
- if( (TheRadar->isRadarHidden() == FALSE || TheRadar->isRadarForced()) &&
- TheRadar->localPixelToRadar( &mouse, &radar ) )
- {
- /*
- //
- // this is an example piece of code to find the object under the pixel position
- // of the radar ... should we in the future wish to allow commands to be executed
- // on objects throught he radar. note tho that this is extremely hard to do because
- // the pixels on the radar are very small and it's hard to do accurate targeting
- //
- Object *obj = TheRadar->objectUnderRadarPixel( &mouse );
- UnicodeString msg;
- if( obj )
- msg.format( L"Object under mouse on radar '%S'(%d)",
- obj->getTemplate()->getName().str(), obj->getID() );
- else
- msg.format( L"Mouse (%d,%d) in Radar window L(%d,%d)", mouse.x, mouse.y, radar.x, radar.y );
- TheInGameUI->message( msg );
- */
- // keep the cursor for any context commands
- const CommandButton *command = TheInGameUI->getGUICommand();
- if( command
- && (command->getCommandType() == GUI_COMMAND_SPECIAL_POWER || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER)
- && BitTest( command->getOptions(), NEED_TARGET_POS ) )
- {
- Int index = TheMouse->getCursorIndex( command->getCursorName() );
- if( index != Mouse::INVALID_MOUSE_CURSOR )
- TheMouse->setCursor( (Mouse::MouseCursor)index );
- else
- TheMouse->setCursor( Mouse::CROSS );
- } // end if
- else
- {
- // Else we are not super targeting, so we have to try to refresh the move cursor.
- // We can't just do this on Enter and Exit, because hotkeys allow state to change
- // while we are in the radar.
- const DrawableList *drawableList = TheInGameUI->getAllSelectedLocalDrawables();
- Mouse::MouseCursor cur = Mouse::ARROW;
- if (!(drawableList->empty() || msg == GWM_MOUSE_LEAVING))
- {
- if (command && command->getCommandType() == GUI_COMMAND_ATTACK_MOVE)
- {
- cur = Mouse::ATTACKMOVETO;
- }
- else
- {
- cur = Mouse::MOVETO;
- }
- }
- // Groovy
- TheMouse->setCursor(cur);
- }
- } // end if
- break;
- } // end case mouse position
- // ------------------------------------------------------------------------
- case GWM_RIGHT_UP:// Here to eat
- case GWM_LEFT_UP:// Here to eat
- break;
- case GWM_RIGHT_DOWN:
- case GWM_LEFT_DOWN:
- {
- ICoord2D mouse;
- ICoord2D radar;
- ICoord2D size;
- ICoord2D screenPos;
- Coord3D world;
- // get window size
- window->winGetSize( &size.x, &size.y );
- // get mouse position
- mouse.x = mData1 & 0xFFFF;
- mouse.y = mData1 >> 16;
-
- // get window screen position
- window->winGetScreenPosition( &screenPos.x, &screenPos.y );
- // set mouse position to be relative to this window
- mouse.x -= screenPos.x;
- mouse.y -= screenPos.y;
- //
- // translate mouse position to radar position ... we know that the mouse
- // location given to us here is relative to the HUD window, which is
- // completely drawn with the radar ... so it's just a translation from
- // our window size we're drawing into to the radar cell size
- //
- if( (TheRadar->isRadarHidden() == FALSE || TheRadar->isRadarForced()) &&
- TheRadar->localPixelToRadar( &mouse, &radar ) &&
- TheRadar->radarToWorld( &radar, &world ) )
- {
- // No drawables, or a right click automatically means its a look at.
- // Having drawables and being in attack move mode means that we should attack move.
- // Having drawables and not being in attack move mode means that we should move.
- const DrawableList *drawableList = TheInGameUI->getAllSelectedLocalDrawables(); // locally-owned only
-
- // see if the user wants to move the tactical view
- if ( drawableList->empty()
- || (! TheGlobalData->m_useAlternateMouse && msg == GWM_RIGHT_DOWN)
- || (TheGlobalData->m_useAlternateMouse && msg == GWM_LEFT_DOWN) )
- {
- TheTacticalView->lookAt( &world );
- break;
- }
- // evaluate any special powers that can be executed from the radar
- const CommandButton *command = TheInGameUI->getGUICommand();
- if( command
- && (command->getCommandType() == GUI_COMMAND_SPECIAL_POWER || command->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER)
- && BitTest( command->getOptions(), NEED_TARGET_POS )
- )
- {
- // do the command
- TheGameClient->evaluateContextCommand( NULL, &world, CommandTranslator::DO_COMMAND );
- } // end if
- else if( command && command->getCommandType() == GUI_COMMAND_ATTACK_MOVE)
- {
- // Attack move has changed from a modifier to a command, so it moves up here.
- GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_DO_ATTACKMOVETO );
- msg->appendLocationArgument( world );
- // Play the unit voice response
- pickAndPlayUnitVoiceResponse(TheInGameUI->getAllSelectedDrawables(), GameMessage::MSG_DO_ATTACKMOVETO);
- }
- else
- {
- GameMessage *newMsg = NULL;
- // Do the superweapon stuff here, before issuing these other messages
- // GS Leaving commented out to show that isInAttackMoveToMode is NEVER SET. It's a command now, not a modifier.
- // if (TheInGameUI->isInAttackMoveToMode())
- // {
- // newMsg = TheMessageStream->appendMessage(GameMessage::MSG_DO_ATTACKMOVETO);
- // newMsg->appendLocationArgument(world);
- // // Play the unit voice response
- // pickAndPlayUnitVoiceResponse(drawableList, GameMessage::MSG_DO_ATTACKMOVETO);
- // break;
- // }
- newMsg = TheMessageStream->appendMessage(GameMessage::MSG_DO_MOVETO);
- newMsg->appendLocationArgument(world);
- // Play the unit voice response
- pickAndPlayUnitVoiceResponse(drawableList, GameMessage::MSG_DO_MOVETO);
-
- } // end else
- }
-
- break;
- } // end left down
- // ------------------------------------------------------------------------
- default:
- return MSG_IGNORED;
- } // end switch( msg )
- TheInGameUI->clearAttackMoveToMode();
- return MSG_HANDLED;
- } // end LeftHUDInput
- //-------------------------------------------------------------------------------------------------
- /** Input procedure for the control bar */
- //-------------------------------------------------------------------------------------------------
- WindowMsgHandledType ControlBarInput( GameWindow *window, UnsignedInt msg,
- WindowMsgData mData1, WindowMsgData mData2 )
- {
- return MSG_IGNORED;
- } // end ControlBarInput
- void ToggleQuitMenu(void);
- //-------------------------------------------------------------------------------------------------
- /** System callback for the control bar parent */
- //-------------------------------------------------------------------------------------------------
- WindowMsgHandledType ControlBarSystem( GameWindow *window, UnsignedInt msg,
- WindowMsgData mData1, WindowMsgData mData2 )
- {
- static NameKeyType buttonCommunicator = NAMEKEY_INVALID;
- if(TheScriptEngine && TheScriptEngine->isGameEnding())
- return MSG_IGNORED;
- switch( msg )
- {
- // --------------------------------------------------------------------------------------------
- case GWM_CREATE:
- {
- // get ids for our children controls
- buttonCommunicator = TheNameKeyGenerator->nameToKey( AsciiString("ControlBar.wnd:PopupCommunicator") );
- break;
- } // end create
- //---------------------------------------------------------------------------------------------
- case GBM_MOUSE_ENTERING:
- case GBM_MOUSE_LEAVING:
- {
- GameWindow *control = (GameWindow *)mData1;
- TheControlBar->processContextSensitiveButtonTransition( control, (GadgetGameMessage)msg);
- break;
- }
- //---------------------------------------------------------------------------------------------
- case GBM_SELECTED:
- case GBM_SELECTED_RIGHT:
- {
- GameWindow *control = (GameWindow *)mData1;
- static NameKeyType beaconPlacementButtonID = NAMEKEY("ControlBar.wnd:ButtonPlaceBeacon");
- static NameKeyType beaconDeleteButtonID = NAMEKEY("ControlBar.wnd:ButtonDeleteBeacon");
- static NameKeyType beaconClearTextButtonID = NAMEKEY("ControlBar.wnd:ButtonClearBeaconText");
- static NameKeyType beaconGeneralButtonID = NAMEKEY("ControlBar.wnd:ButtonGeneral");
- // static NameKeyType buttonSmallID = NAMEKEY("ControlBar.wnd:ButtonSmall");
- // static NameKeyType buttonMediumID = NAMEKEY("ControlBar.wnd:ButtonMedium");
- static NameKeyType buttonLargeID = NAMEKEY("ControlBar.wnd:ButtonLarge");
- static NameKeyType buttonOptions = NAMEKEY("ControlBar.wnd:ButtonOptions");
- static NameKeyType buttonIdleWorker = NAMEKEY("ControlBar.wnd:ButtonIdleWorker");
- Int controlID = control->winGetWindowId();
- if( controlID == buttonCommunicator )
- {
- ToggleDiplomacy(FALSE);
- }
- else if( controlID == beaconPlacementButtonID && TheGameLogic->isInMultiplayerGame() &&
- ThePlayerList->getLocalPlayer()->isPlayerActive())
- {
- const CommandButton *commandButton = TheControlBar->findCommandButton( "Command_PlaceBeacon" );
- TheInGameUI->setGUICommand( commandButton );
- }
- else if( controlID == beaconDeleteButtonID && TheGameLogic->isInMultiplayerGame() )
- {
- TheMessageStream->appendMessage( GameMessage::MSG_REMOVE_BEACON );
- }
- else if( controlID == beaconClearTextButtonID && TheGameLogic->isInMultiplayerGame() )
- {
- static NameKeyType textID = NAMEKEY("ControlBar.wnd:EditBeaconText");
- GameWindow *win = TheWindowManager->winGetWindowFromId(NULL, textID);
- if (win)
- {
- GadgetTextEntrySetText( win, UnicodeString::TheEmptyString );
- }
- }
- else if( controlID == beaconGeneralButtonID)
- {
- HideQuitMenu( );
- TheControlBar->togglePurchaseScience();
- }
- //else if( controlID == buttonSmallID)
- // {
- // TheControlBar->switchControlBarStage( CONTROL_BAR_STAGE_LOW );
- // }
- // else if( controlID == buttonMediumID)
- // {
- // TheControlBar->switchControlBarStage( CONTROL_BAR_STAGE_SQUISHED );
- // }
-
- else if( controlID == buttonLargeID)
- {
- TheControlBar->toggleControlBarStage();
- }
- else if( controlID == buttonOptions)
- {
- ToggleQuitMenu();
- }
- else if( controlID == buttonIdleWorker)
- {
- HideQuitMenu( );
- TheInGameUI->selectNextIdleWorker();
- }
- else
- {
- //
- // all buttons from all the context sensitive user interface windows are part of the
- // control bar, send the button processing that way
- //
- TheControlBar->processContextSensitiveButtonClick( control, (GadgetGameMessage)msg );
- }
- break;
- } // end button selected
- //---------------------------------------------------------------------------------------------
- case GEM_EDIT_DONE:
- {
- GameWindow *control = (GameWindow *)mData1;
- Int controlID = control->winGetWindowId();
- static NameKeyType textID = NAMEKEY("ControlBar.wnd:EditBeaconText");
- if (controlID == textID)
- {
- // set beacon text
- if (TheInGameUI->getSelectCount() == 1)
- {
- GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_SET_BEACON_TEXT );
- UnicodeString newText = GadgetTextEntryGetText( control );
- TheLanguageFilter->filterLine(newText);
- const WideChar * c = newText.str();
- while ( c && *c )
- {
- msg->appendWideCharArgument( *c++ );
- }
- msg->appendWideCharArgument( L'\0' ); // trailing NULL
- }
- }
- break;
- } // end edit done
- //---------------------------------------------------------------------------------------------
- default:
- return MSG_IGNORED;
- } // end switch( msg )
- return MSG_HANDLED;
- } // end ControlBarSystem
- extern void showReplayControls( void );
- extern void hideReplayControls( void );
- extern void toggleReplayControls( void );
- //-------------------------------------------------------------------------------------------------
- /** Force the control bar to be shown */
- //-------------------------------------------------------------------------------------------------
- void ShowControlBar( Bool immediate )
- {
- showReplayControls();
- if(TheControlBar)
- TheControlBar->showSpecialPowerShortcut();
- if (TheWindowManager)
- {
- Int id = (Int)TheNameKeyGenerator->nameToKey(AsciiString("ControlBar.wnd:ControlBarParent"));
- GameWindow *window = TheWindowManager->winGetWindowFromId(NULL, id);
- if (window)
- {
- TheControlBar->switchControlBarStage(CONTROL_BAR_STAGE_DEFAULT);
- TheTacticalView->setHeight((Int)(TheDisplay->getHeight() * 0.80f));
- if (TheControlBar->m_animateWindowManager && !immediate)
- {
- TheControlBar->m_animateWindowManager->reset();
- //TheControlBar->m_animateWindowManager->registerGameWindow(window, WIN_ANIMATION_SLIDE_BOTTOM_TIMED, TRUE, 1000, 0);
- TheControlBar->m_animateWindowManager->registerGameWindow(window, WIN_ANIMATION_SLIDE_BOTTOM, TRUE, 500, 0);
- TheControlBar->animateSpecialPowerShortcut(TRUE);
- }
- window->winHide(FALSE);
- }
- }
- // We want to get everything recalced since this is a major state change.
- if(TheControlBar)
- TheControlBar->markUIDirty();
- }// void ShowControlBar(void)
- //-------------------------------------------------------------------------------------------------
- /** Force the control bar to be hidden */
- //-------------------------------------------------------------------------------------------------
- void HideControlBar( Bool immediate )
- {
- hideReplayControls();
- if(TheControlBar)
- TheControlBar->hideSpecialPowerShortcut();
- if (TheWindowManager)
- {
- Int id = (Int)TheNameKeyGenerator->nameToKey(AsciiString("ControlBar.wnd:ControlBarParent"));
- GameWindow *window = TheWindowManager->winGetWindowFromId(NULL, id);
- if (window)
- {
- #ifdef SLIDE_LETTERBOX
- TheTacticalView->setHeight((Int)(TheDisplay->getHeight() * 0.80f));
- #else
- TheTacticalView->setHeight(TheDisplay->getHeight());
- #endif
- }
- if (immediate)
- {
- window->winHide(TRUE);
- if(TheControlBar)
- TheControlBar->hideSpecialPowerShortcut();
-
- }
- else
- {
- TheControlBar->m_animateWindowManager->reverseAnimateWindow();
- TheControlBar->animateSpecialPowerShortcut(FALSE);
- }
- //Always get rid of the purchase science screen!
- if( TheControlBar )
- {
- TheControlBar->hidePurchaseScience();
- }
- }
- }//void HideControlBar( void )
- //-------------------------------------------------------------------------------------------------
- /** Toggle the control bar on or off */
- //-------------------------------------------------------------------------------------------------
- void ToggleControlBar( Bool immediate )
- {
- toggleReplayControls();
- if (TheWindowManager)
- {
- Int id = (Int)TheNameKeyGenerator->nameToKey(AsciiString("ControlBar.wnd:ControlBarParent"));
- GameWindow *window = TheWindowManager->winGetWindowFromId(NULL, id);
- if (window)
- {
- if (window->winIsHidden())
- {
- if(TheControlBar)
- TheControlBar->showSpecialPowerShortcut();
- //now hidden, we're making it visible again so shrink viewport under the window
- TheTacticalView->setHeight((Int)(TheDisplay->getHeight() * 0.80f));
- window->winHide(!window->winIsHidden());
- TheControlBar->switchControlBarStage(CONTROL_BAR_STAGE_DEFAULT);
- if (TheControlBar->m_animateWindowManager && !immediate)
- {
- TheControlBar->m_animateWindowManager->reset();
- //TheControlBar->m_animateWindowManager->registerGameWindow(window, WIN_ANIMATION_SLIDE_BOTTOM_TIMED, FALSE, 500, 0);
- TheControlBar->m_animateWindowManager->registerGameWindow(window, WIN_ANIMATION_SLIDE_BOTTOM, TRUE, 500, 0);
- TheControlBar->animateSpecialPowerShortcut(TRUE);
- }
- }
- else
- {
- if(TheControlBar)
- TheControlBar->hideSpecialPowerShortcut();
- TheTacticalView->setHeight(TheDisplay->getHeight());
- window->winHide(!window->winIsHidden());
- }
-
- }
- }
- }// end void ToggleControlBar( void )
- //-------------------------------------------------------------------------------------------------
- /** Resize the control bar */
- //-------------------------------------------------------------------------------------------------
|