| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- /*
- ** 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 <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: PlaceEventTranslator.cpp ///////////////////////////////////////////////////////////
- // Author: Steven Johnson, Dec 2001
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/BuildAssistant.h"
- #include "Common/GameAudio.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/SpecialPower.h"
- #include "Common/ThingTemplate.h"
- #include "GameClient/CommandXlat.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/Eva.h"
- #include "GameClient/PlaceEventTranslator.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/Module/ProductionUpdate.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //-------------------------------------------------------------------------------------------------
- PlaceEventTranslator::PlaceEventTranslator() : m_frameOfUpButton(-1)
- {
- }
- //-------------------------------------------------------------------------------------------------
- PlaceEventTranslator::~PlaceEventTranslator()
- {
- }
- //-------------------------------------------------------------------------------------------------
- /** Translator to process raw input messages into the "place something" message(s) */
- //-------------------------------------------------------------------------------------------------
- GameMessageDisposition PlaceEventTranslator::translateGameMessage(const GameMessage *msg)
- {
- GameMessageDisposition disp = KEEP_MESSAGE;
- switch(msg->getType())
- {
- //---------------------------------------------------------------------------------------------
- case GameMessage::MSG_RAW_MOUSE_LEFT_BUTTON_DOWN:
- {
- // if we're in a building placement mode, do the place and send to all players
- const ThingTemplate *build = TheInGameUI->getPendingPlaceType();
- if( build && TheInGameUI->isPlacementAnchored() == FALSE )
- {
- ICoord2D mouse = msg->getArgument(0)->pixel;
- Coord3D world;
- // translate mouse position to world position
- TheTacticalView->screenToTerrain( &mouse, &world );
- //
- // placing things causes a dozer to go over and build it ... get the dozer in question
- // from the in game UI
- //
- Object *builderObject = TheGameLogic->findObjectByID( TheInGameUI->getPendingPlaceSourceObjectID() );
- // if our source object is gone cancel this whole placement process
- if( builderObject == NULL )
- {
- TheInGameUI->placeBuildAvailable( NULL, NULL );
- break;
- } // end if
- // set this location as the placement anchor
- TheInGameUI->setPlacementStart( &mouse );
- /*
- //
- // This block of code checks for valid placement on a down mouse click, but since we can
- // rotate a building into a valid location, this check prevents us from placing things
- // down in some legal locations
- //
- // get the type of thing we want to build
- const ThingTemplate *whatToBuild = TheInGameUI->getPendingPlaceType();
- //
- // if the spot at which they choose to place this thing is illegal we won't start
- // the placement anchor, instead we play a "can't do that" sound
- //
- LegalBuildCode lbc;
- lbc = TheBuildAssistant->isLocationLegalToBuild( &world,
- whatToBuild,
- TheInGameUI->getPlacementAngle(),
- BuildAssistant::USE_QUICK_PATHFIND |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::NO_OBJECT_OVERLAP,
- builderObject );
- if( lbc != LBC_OK )
- {
- static const Sound *noCanDoSound = TheAudio->Sounds->getSound( "NoCanDoSound" );
- // play a can't do that sound
- TheAudio->Sounds->playSound( noCanDoSound );
- // display a message to the user as to why you can't build there
- TheInGameUI->displayCantBuildMessage( lbc );
- } // end if
- else
- {
- // start placement anchor
- TheInGameUI->setPlacementStart(&mouse);
- } // end else
- */
-
- // used the input
- disp = DESTROY_MESSAGE;
- }
- break;
- }
- //---------------------------------------------------------------------------------------------
- case GameMessage::MSG_MOUSE_LEFT_DOUBLE_CLICK:
- case GameMessage::MSG_MOUSE_LEFT_CLICK:
- {
- // if we're in a building placement mode, do the place and send to all players
- const ThingTemplate *build = TheInGameUI->getPendingPlaceType();
- // ... and also remove any radius cursor that is active.
- // (srj sez: not sure if this is always necessary... more of a failsafe to make it go away.)
- TheInGameUI->setRadiusCursorNone();
- if (build && TheInGameUI->isPlacementAnchored())
- {
- GameMessage *placeMsg;
- // Player *player = ThePlayerList->getLocalPlayer();
- Coord3D world;
- Real angle;
- ICoord2D anchorStart, anchorEnd;
- Bool isLineBuild = TheBuildAssistant->isLineBuildTemplate( build );
- // get the angle of the drawable at the cursor to use as the initial angle
- angle = TheInGameUI->getPlacementAngle();
- // get start point from the anchor arrow used to place and select angles
- TheInGameUI->getPlacementPoints( &anchorStart, &anchorEnd );
- // translate the screen position of start to world target location
- TheTacticalView->screenToTerrain( &anchorStart, &world );
- // get the source object ID of the thing that is "building" the object
- ObjectID builderID = INVALID_ID;
- Object *builderObj = TheGameLogic->findObjectByID( TheInGameUI->getPendingPlaceSourceObjectID() );
- if( builderObj )
- builderID = builderObj->getID();
- //Kris: September 27, 2002
- //Make sure we have enough CASH to build it! It's possible that between the
- //time we initiated it and the time we confirm it, a hacker has stolen some of
- //our cash!
- CanMakeType cmt = TheBuildAssistant->canMakeUnit( builderObj, build );
- if( cmt != CANMAKE_OK )
- {
- if (cmt == CANMAKE_NO_MONEY)
- {
- TheEva->setShouldPlay(EVA_InsufficientFunds);
- TheInGameUI->message( "GUI:NotEnoughMoneyToBuild" );
- break;
- }
- else if (cmt == CANMAKE_QUEUE_FULL)
- {
- TheInGameUI->message( "GUI:ProductionQueueFull" );
- break;
- }
- else if (cmt == CANMAKE_PARKING_PLACES_FULL)
- {
- TheInGameUI->message( "GUI:ParkingPlacesFull" );
- break;
- }
- else if( cmt == CANMAKE_MAXED_OUT_FOR_PLAYER )
- {
- TheInGameUI->message( "GUI:UnitMaxedOut" );
- break;
- }
- // get out of pending placement mode, this will also clear the arrow anchor status
- TheInGameUI->placeBuildAvailable( NULL, NULL );
- break;
- }
- // check to see if this is a legal location to build something at
- LegalBuildCode lbc;
- lbc = TheBuildAssistant->isLocationLegalToBuild( &world,
- build,
- angle,
- BuildAssistant::USE_QUICK_PATHFIND |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::NO_OBJECT_OVERLAP |
- BuildAssistant::SHROUD_REVEALED |
- BuildAssistant::IGNORE_STEALTHED |
- BuildAssistant::FAIL_STEALTHED_WITHOUT_FEEDBACK,
- builderObj, NULL );
- if( lbc == LBC_OK )
- {
- //Are we building this structure via the special power system? (special case for sneak attack)
- if( builderObj )
- {
- ProductionUpdateInterface *puInterface = builderObj->getProductionUpdateInterface();
- if( puInterface )
- {
- const CommandButton *commandButton = puInterface->getSpecialPowerConstructionCommandButton();
- if( commandButton )
- {
- //If we get this far, then we aren't going to really build the object using the production update
- //interface. Instead, we're going to trigger the special power to create it magically without a
- //dozer/worker.
- placeMsg = TheMessageStream->appendMessage( GameMessage::MSG_DO_SPECIAL_POWER_AT_LOCATION );
- placeMsg->appendIntegerArgument( commandButton->getSpecialPowerTemplate()->getID() ); //The ID of the special power template.
- placeMsg->appendLocationArgument( world ); //Position of special to be fired.
- placeMsg->appendRealArgument( angle ); //Angle of special to be fired.
- placeMsg->appendObjectIDArgument( INVALID_ID ); //There is no object in the way.
- placeMsg->appendIntegerArgument( commandButton->getOptions() ); //Command button options.
- placeMsg->appendObjectIDArgument( builderObj->getID() ); //The source object responsible for firing the special.
-
- // get out of pending placement mode, this will also clear the arrow anchor status
- TheInGameUI->placeBuildAvailable( NULL, NULL );
- // used the input
- disp = DESTROY_MESSAGE;
- m_frameOfUpButton = TheGameLogic->getFrame();
- break;
- }
- }
- }
- // create the right kind of message
- if( isLineBuild )
- placeMsg = TheMessageStream->appendMessage( GameMessage::MSG_DOZER_CONSTRUCT_LINE );
- else
- placeMsg = TheMessageStream->appendMessage( GameMessage::MSG_DOZER_CONSTRUCT );
- placeMsg->appendIntegerArgument(build->getTemplateID());
- placeMsg->appendLocationArgument(world);
- placeMsg->appendRealArgument(angle);
- if( isLineBuild )
- {
- Coord3D worldEnd;
- TheTacticalView->screenToTerrain( &anchorEnd, &worldEnd );
- placeMsg->appendLocationArgument( worldEnd );
- } // end if
- pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), placeMsg->getType() );
- // get out of pending placement mode, this will also clear the arrow anchor status
- TheInGameUI->placeBuildAvailable( NULL, NULL );
- } // end if, location legal to build at
- else
- {
- // can't place, display why
- TheInGameUI->displayCantBuildMessage( lbc );
- //Cannot build here -- play the voice sound from the dozer
- AudioEventRTS sound = *builderObj->getTemplate()->getPerUnitSound( "VoiceNoBuild" );
- sound.setObjectID( builderObj->getID() );
- TheAudio->addAudioEvent( &sound );
- // play a can't do that sound (UI beep type sound)
- static AudioEventRTS noCanDoSound( "NoCanDoSound" );
- TheAudio->addAudioEvent( &noCanDoSound );
- // unhook the anchor so they can try again
- TheInGameUI->setPlacementStart( NULL );
- } // end else
-
- // used the input
- disp = DESTROY_MESSAGE;
- m_frameOfUpButton = TheGameLogic->getFrame();
- }
- if (disp == DESTROY_MESSAGE)
- TheInGameUI->clearAttackMoveToMode();
- break;
- }
- //---------------------------------------------------------------------------------------------
- case GameMessage::MSG_RAW_MOUSE_POSITION:
- {
- // if a building placement is in progress update the destination position
- if (TheInGameUI->isPlacementAnchored())
- {
- const Int PLACEMENT_DRAG_THRESHOLD_DIST = 5; // in pixels away from anchor point
- ICoord2D mouse = msg->getArgument(0)->pixel;
- //
- // we will only process placement end point sets (clicking, and dragging to set angles)
- // if we have moved far enough away from the start point
- //
- ICoord2D start;
- TheInGameUI->getPlacementPoints( &start, NULL );
-
- Int x, y;
- x = mouse.x - start.x;
- y = mouse.y - start.y;
- if( sqrt( (x * x) + (y * y) ) >= PLACEMENT_DRAG_THRESHOLD_DIST )
- {
-
- TheInGameUI->setPlacementEnd(&mouse);
- disp = DESTROY_MESSAGE;
- } // end if
- }
- break;
- }
- }
- return disp;
- }
|