PlaceEventTranslator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: PlaceEventTranslator.cpp ///////////////////////////////////////////////////////////
  24. // Author: Steven Johnson, Dec 2001
  25. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  26. #include "Common/GameAudio.h"
  27. #include "Common/Player.h"
  28. #include "Common/PlayerList.h"
  29. #include "Common/ThingTemplate.h"
  30. #include "Common/BuildAssistant.h"
  31. #include "GameLogic/Object.h"
  32. #include "GameLogic/GameLogic.h"
  33. #include "GameClient/CommandXlat.h"
  34. #include "GameClient/PlaceEventTranslator.h"
  35. #include "GameClient/Drawable.h"
  36. #include "GameClient/Eva.h"
  37. //-------------------------------------------------------------------------------------------------
  38. PlaceEventTranslator::PlaceEventTranslator() : m_frameOfUpButton(-1)
  39. {
  40. }
  41. //-------------------------------------------------------------------------------------------------
  42. PlaceEventTranslator::~PlaceEventTranslator()
  43. {
  44. }
  45. //-------------------------------------------------------------------------------------------------
  46. /** Translator to process raw input messages into the "place something" message(s) */
  47. //-------------------------------------------------------------------------------------------------
  48. GameMessageDisposition PlaceEventTranslator::translateGameMessage(const GameMessage *msg)
  49. {
  50. GameMessageDisposition disp = KEEP_MESSAGE;
  51. switch(msg->getType())
  52. {
  53. //---------------------------------------------------------------------------------------------
  54. case GameMessage::MSG_RAW_MOUSE_LEFT_BUTTON_DOWN:
  55. {
  56. // if we're in a building placement mode, do the place and send to all players
  57. const ThingTemplate *build = TheInGameUI->getPendingPlaceType();
  58. if( build && TheInGameUI->isPlacementAnchored() == FALSE )
  59. {
  60. ICoord2D mouse = msg->getArgument(0)->pixel;
  61. Coord3D world;
  62. // translate mouse position to world position
  63. TheTacticalView->screenToTerrain( &mouse, &world );
  64. //
  65. // placing things causes a dozer to go over and build it ... get the dozer in question
  66. // from the in game UI
  67. //
  68. Object *builderObject = TheGameLogic->findObjectByID( TheInGameUI->getPendingPlaceSourceObjectID() );
  69. // if our source object is gone cancel this whole placement process
  70. if( builderObject == NULL )
  71. {
  72. TheInGameUI->placeBuildAvailable( NULL, NULL );
  73. break;
  74. } // end if
  75. // set this location as the placement anchor
  76. TheInGameUI->setPlacementStart( &mouse );
  77. /*
  78. //
  79. // This block of code checks for valid placement on a down mouse click, but since we can
  80. // rotate a building into a valid location, this check prevents us from placing things
  81. // down in some legal locations
  82. //
  83. // get the type of thing we want to build
  84. const ThingTemplate *whatToBuild = TheInGameUI->getPendingPlaceType();
  85. //
  86. // if the spot at which they choose to place this thing is illegal we won't start
  87. // the placement anchor, instead we play a "can't do that" sound
  88. //
  89. LegalBuildCode lbc;
  90. lbc = TheBuildAssistant->isLocationLegalToBuild( &world,
  91. whatToBuild,
  92. TheInGameUI->getPlacementAngle(),
  93. BuildAssistant::USE_QUICK_PATHFIND |
  94. BuildAssistant::TERRAIN_RESTRICTIONS |
  95. BuildAssistant::CLEAR_PATH |
  96. BuildAssistant::NO_OBJECT_OVERLAP,
  97. builderObject );
  98. if( lbc != LBC_OK )
  99. {
  100. static const Sound *noCanDoSound = TheAudio->Sounds->getSound( "NoCanDoSound" );
  101. // play a can't do that sound
  102. TheAudio->Sounds->playSound( noCanDoSound );
  103. // display a message to the user as to why you can't build there
  104. TheInGameUI->displayCantBuildMessage( lbc );
  105. } // end if
  106. else
  107. {
  108. // start placement anchor
  109. TheInGameUI->setPlacementStart(&mouse);
  110. } // end else
  111. */
  112. // used the input
  113. disp = DESTROY_MESSAGE;
  114. }
  115. break;
  116. }
  117. //---------------------------------------------------------------------------------------------
  118. case GameMessage::MSG_MOUSE_LEFT_DOUBLE_CLICK:
  119. case GameMessage::MSG_MOUSE_LEFT_CLICK:
  120. {
  121. // if we're in a building placement mode, do the place and send to all players
  122. const ThingTemplate *build = TheInGameUI->getPendingPlaceType();
  123. // ... and also remove any radius cursor that is active.
  124. // (srj sez: not sure if this is always necessary... more of a failsafe to make it go away.)
  125. TheInGameUI->setRadiusCursorNone();
  126. if (build && TheInGameUI->isPlacementAnchored())
  127. {
  128. GameMessage *placeMsg;
  129. // Player *player = ThePlayerList->getLocalPlayer();
  130. Coord3D world;
  131. Real angle;
  132. ICoord2D anchorStart, anchorEnd;
  133. Bool isLineBuild = TheBuildAssistant->isLineBuildTemplate( build );
  134. // get the angle of the drawable at the cursor to use as the initial angle
  135. angle = TheInGameUI->getPlacementAngle();
  136. // get start point from the anchor arrow used to place and select angles
  137. TheInGameUI->getPlacementPoints( &anchorStart, &anchorEnd );
  138. // translate the screen position of start to world target location
  139. TheTacticalView->screenToTerrain( &anchorStart, &world );
  140. // get the source object ID of the thing that is "building" the object
  141. ObjectID builderID = INVALID_ID;
  142. Object *builderObj = TheGameLogic->findObjectByID( TheInGameUI->getPendingPlaceSourceObjectID() );
  143. if( builderObj )
  144. builderID = builderObj->getID();
  145. //Kris: September 27, 2002
  146. //Make sure we have enough CASH to build it! It's possible that between the
  147. //time we initiated it and the time we confirm it, a hacker has stolen some of
  148. //our cash!
  149. CanMakeType cmt = TheBuildAssistant->canMakeUnit( builderObj, build );
  150. if( cmt != CANMAKE_OK )
  151. {
  152. if (cmt == CANMAKE_NO_MONEY)
  153. {
  154. TheEva->setShouldPlay(EVA_InsufficientFunds);
  155. TheInGameUI->message( "GUI:NotEnoughMoneyToBuild" );
  156. break;
  157. }
  158. else if (cmt == CANMAKE_QUEUE_FULL)
  159. {
  160. TheInGameUI->message( "GUI:ProductionQueueFull" );
  161. break;
  162. }
  163. else if (cmt == CANMAKE_PARKING_PLACES_FULL)
  164. {
  165. TheInGameUI->message( "GUI:ParkingPlacesFull" );
  166. break;
  167. }
  168. else if( cmt == CANMAKE_MAXED_OUT_FOR_PLAYER )
  169. {
  170. TheInGameUI->message( "GUI:UnitMaxedOut" );
  171. break;
  172. }
  173. // get out of pending placement mode, this will also clear the arrow anchor status
  174. TheInGameUI->placeBuildAvailable( NULL, NULL );
  175. break;
  176. }
  177. // check to see if this is a legal location to build something at
  178. LegalBuildCode lbc;
  179. lbc = TheBuildAssistant->isLocationLegalToBuild( &world,
  180. build,
  181. angle,
  182. BuildAssistant::USE_QUICK_PATHFIND |
  183. BuildAssistant::TERRAIN_RESTRICTIONS |
  184. BuildAssistant::CLEAR_PATH |
  185. BuildAssistant::NO_OBJECT_OVERLAP |
  186. BuildAssistant::SHROUD_REVEALED,
  187. builderObj, NULL );
  188. if( lbc == LBC_OK )
  189. {
  190. /** @todo Do not send local player id as argument once we have player ids
  191. tied into all messages automatically */
  192. // create the right kind of message
  193. if( isLineBuild )
  194. placeMsg = TheMessageStream->appendMessage( GameMessage::MSG_DOZER_CONSTRUCT_LINE );
  195. else
  196. placeMsg = TheMessageStream->appendMessage( GameMessage::MSG_DOZER_CONSTRUCT );
  197. placeMsg->appendIntegerArgument(build->getTemplateID());
  198. placeMsg->appendLocationArgument(world);
  199. placeMsg->appendRealArgument(angle);
  200. if( isLineBuild )
  201. {
  202. Coord3D worldEnd;
  203. TheTacticalView->screenToTerrain( &anchorEnd, &worldEnd );
  204. placeMsg->appendLocationArgument( worldEnd );
  205. } // end if
  206. pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), placeMsg->getType() );
  207. // get out of pending placement mode, this will also clear the arrow anchor status
  208. TheInGameUI->placeBuildAvailable( NULL, NULL );
  209. } // end if, location legal to build at
  210. else
  211. {
  212. // can't place, display why
  213. TheInGameUI->displayCantBuildMessage( lbc );
  214. //Cannot build here -- play the voice sound from the dozer
  215. AudioEventRTS sound = *builderObj->getTemplate()->getPerUnitSound( "VoiceNoBuild" );
  216. sound.setObjectID( builderObj->getID() );
  217. TheAudio->addAudioEvent( &sound );
  218. // play a can't do that sound (UI beep type sound)
  219. static AudioEventRTS noCanDoSound( "NoCanDoSound" );
  220. TheAudio->addAudioEvent( &noCanDoSound );
  221. // unhook the anchor so they can try again
  222. TheInGameUI->setPlacementStart( NULL );
  223. } // end else
  224. // used the input
  225. disp = DESTROY_MESSAGE;
  226. m_frameOfUpButton = TheGameLogic->getFrame();
  227. }
  228. if (disp == DESTROY_MESSAGE)
  229. TheInGameUI->clearAttackMoveToMode();
  230. break;
  231. }
  232. //---------------------------------------------------------------------------------------------
  233. case GameMessage::MSG_RAW_MOUSE_POSITION:
  234. {
  235. // if a building placement is in progress update the destination position
  236. if (TheInGameUI->isPlacementAnchored())
  237. {
  238. const Int PLACEMENT_DRAG_THRESHOLD_DIST = 5; // in pixels away from anchor point
  239. ICoord2D mouse = msg->getArgument(0)->pixel;
  240. //
  241. // we will only process placement end point sets (clicking, and dragging to set angles)
  242. // if we have moved far enough away from the start point
  243. //
  244. ICoord2D start;
  245. TheInGameUI->getPlacementPoints( &start, NULL );
  246. Int x, y;
  247. x = mouse.x - start.x;
  248. y = mouse.y - start.y;
  249. if( sqrt( (x * x) + (y * y) ) >= PLACEMENT_DRAG_THRESHOLD_DIST )
  250. {
  251. TheInGameUI->setPlacementEnd(&mouse);
  252. disp = DESTROY_MESSAGE;
  253. } // end if
  254. }
  255. break;
  256. }
  257. }
  258. return disp;
  259. }