OCLUpdate.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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: OCLUpdate.cpp /////////////////////////////////////////////////////////////////////////
  24. // Author: Graham Smallwood, August2002
  25. // Desc: Update Spits out an OCL on a timer
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
  29. #include "Common/RandomValue.h"
  30. #include "Common/Xfer.h"
  31. #include "Common/Player.h"
  32. #include "Common/PlayerTemplate.h"
  33. #include "Common/UnicodeString.h"
  34. #include "GameLogic/GameLogic.h"
  35. #include "GameLogic/Object.h"
  36. #include "GameLogic/ObjectCreationList.h"
  37. #include "GameLogic/PartitionManager.h"
  38. #include "GameLogic/Module/OCLUpdate.h"
  39. #include "GameLogic/TerrainLogic.h"
  40. //-------------------------------------------------------------------------------------------------
  41. void parseFactionObjectCreationList( INI *ini, void *instance, void *store, const void *userData )
  42. {
  43. OCLUpdateModuleData::FactionOCLInfo info;
  44. info.m_factionName = "";
  45. info.m_ocl = 0;
  46. const char *token = ini->getNextToken( ini->getSepsColon() );
  47. if ( stricmp(token, "Faction") == 0 )
  48. {
  49. token = ini->getNextTokenOrNull( ini->getSepsColon() );
  50. if (!token) throw INI_INVALID_DATA;
  51. info.m_factionName = token;
  52. }
  53. else
  54. throw INI_INVALID_DATA;
  55. token = ini->getNextTokenOrNull( ini->getSepsColon() );
  56. if ( stricmp(token, "OCL") == 0 )
  57. ini->parseObjectCreationList( ini, instance, &info.m_ocl, NULL );
  58. else
  59. throw INI_INVALID_DATA;
  60. // Insert the info into the ocl hashmap
  61. OCLUpdateModuleData::FactionOCLList * theList = (OCLUpdateModuleData::FactionOCLList*)store;
  62. theList->push_back(info);
  63. } // end parseFactionObjectCreationList
  64. //-------------------------------------------------------------------------------------------------
  65. OCLUpdateModuleData::OCLUpdateModuleData()
  66. {
  67. m_minDelay = 0;
  68. m_maxDelay = 0;
  69. m_ocl = NULL;
  70. m_factionOCL.clear();
  71. m_isCreateAtEdge = FALSE;
  72. m_isFactionTriggered = FALSE;
  73. }
  74. //-------------------------------------------------------------------------------------------------
  75. /*static*/ void OCLUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  76. {
  77. UpdateModuleData::buildFieldParse(p);
  78. static const FieldParse dataFieldParse[] =
  79. {
  80. { "OCL", INI::parseObjectCreationList, NULL, offsetof( OCLUpdateModuleData, m_ocl ) },
  81. { "FactionOCL", parseFactionObjectCreationList, NULL, offsetof( OCLUpdateModuleData, m_factionOCL ) },
  82. { "MinDelay", INI::parseDurationUnsignedInt, NULL, offsetof( OCLUpdateModuleData, m_minDelay ) },
  83. { "MaxDelay", INI::parseDurationUnsignedInt, NULL, offsetof( OCLUpdateModuleData, m_maxDelay ) },
  84. { "CreateAtEdge", INI::parseBool, NULL, offsetof( OCLUpdateModuleData, m_isCreateAtEdge ) },
  85. { "FactionTriggered", INI::parseBool, NULL, offsetof( OCLUpdateModuleData, m_isFactionTriggered ) },
  86. { 0, 0, 0, 0 }
  87. };
  88. p.add(dataFieldParse);
  89. }
  90. //-------------------------------------------------------------------------------------------------
  91. //-------------------------------------------------------------------------------------------------
  92. OCLUpdate::OCLUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  93. {
  94. m_nextCreationFrame = 0;
  95. m_timerStartedFrame = 0;
  96. m_isFactionNeutral = TRUE;
  97. m_currentPlayerColor = 0;
  98. }
  99. //-------------------------------------------------------------------------------------------------
  100. //-------------------------------------------------------------------------------------------------
  101. OCLUpdate::~OCLUpdate( void )
  102. {
  103. }
  104. //-------------------------------------------------------------------------------------------------
  105. //-------------------------------------------------------------------------------------------------
  106. UpdateSleepTime OCLUpdate::update( void )
  107. {
  108. if( getObject()->isDisabled() )
  109. {
  110. m_nextCreationFrame++;
  111. return UPDATE_SLEEP_NONE;
  112. }
  113. const OCLUpdateModuleData *data = getOCLUpdateModuleData();
  114. // Test if the OCL update is faction dependant. If so, check for faction changes
  115. if (data->m_isFactionTriggered)
  116. {
  117. Player *player = getObject()->getControllingPlayer();
  118. // Test for when a player captures the building
  119. if (m_isFactionNeutral)
  120. {
  121. if( player && player->isPlayableSide() )
  122. {
  123. m_currentPlayerColor = player->getPlayerColor();
  124. m_isFactionNeutral = FALSE;
  125. setNextCreationFrame();
  126. }
  127. }
  128. // Test for when the building has been made neutral or when it changes faction
  129. else
  130. {
  131. // If this is no longer under player control, then we set the faction to neutral
  132. if( !player || !player->isPlayableSide() )
  133. {
  134. m_isFactionNeutral = TRUE;
  135. }
  136. // If another player has taken control, reset the timer
  137. else if( player && player->getPlayerColor() != m_currentPlayerColor)
  138. {
  139. m_currentPlayerColor = player->getPlayerColor();
  140. setNextCreationFrame();
  141. }
  142. }
  143. // If the building is neutal, skip futher update
  144. if (m_isFactionNeutral)
  145. return UPDATE_SLEEP_NONE;
  146. }
  147. /// @todo srj use SLEEPY_UPDATE here
  148. if( shouldCreate() )
  149. {
  150. if( m_nextCreationFrame == 0 )
  151. {
  152. // You don't get to actually spread the first try, you start on a timer, then go
  153. setNextCreationFrame();
  154. return UPDATE_SLEEP_NONE;
  155. }
  156. setNextCreationFrame();
  157. Coord3D creationCoord;
  158. if( getOCLUpdateModuleData()->m_isCreateAtEdge )
  159. creationCoord = TheTerrainLogic->findClosestEdgePoint( getObject()->getPosition() );
  160. else
  161. creationCoord = *getObject()->getPosition();
  162. // If this is faction triggered, search through the faction specific OCLs to find the match
  163. if (data->m_isFactionTriggered)
  164. {
  165. std::string playerFactionName("");
  166. Player *player = getObject()->getControllingPlayer();
  167. if (!player) return UPDATE_SLEEP_NONE;
  168. const PlayerTemplate *playerT = player->getPlayerTemplate();
  169. if (!playerT) return UPDATE_SLEEP_NONE;
  170. // Get and store the faction side to compare with the faction ocl list
  171. if (playerT->getSide().str()) playerFactionName = playerT->getSide().str();
  172. // Loop through the list of faction ocls to find the matching faction that triggeres the specific ocls
  173. for (OCLUpdateModuleData::FactionOCLList::const_iterator it = data->m_factionOCL.begin(); it != data->m_factionOCL.end(); ++it)
  174. {
  175. OCLUpdateModuleData::FactionOCLInfo info = *it;
  176. if (playerFactionName == info.m_factionName)
  177. {
  178. ObjectCreationList::create( info.m_ocl, getObject(), &creationCoord, getObject()->getPosition(), getObject()->getOrientation() );
  179. break;
  180. }
  181. }
  182. }
  183. // Use the non faction OCL information
  184. else
  185. {
  186. ObjectCreationList::create( data->m_ocl, getObject(), &creationCoord, getObject()->getPosition(), getObject()->getOrientation() );
  187. }
  188. }
  189. return UPDATE_SLEEP_NONE;
  190. }
  191. //-------------------------------------------------------------------------------------------------
  192. void OCLUpdate::resetTimer()
  193. {
  194. setNextCreationFrame();
  195. }
  196. // ------------------------------------------------------------------------------------------------
  197. // ------------------------------------------------------------------------------------------------
  198. Bool OCLUpdate::shouldCreate()
  199. {
  200. if( TheGameLogic->getFrame() < m_nextCreationFrame )
  201. return FALSE;//too soon
  202. if( getObject()->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  203. return FALSE;// not built yet
  204. return TRUE;
  205. }
  206. // ------------------------------------------------------------------------------------------------
  207. // ------------------------------------------------------------------------------------------------
  208. void OCLUpdate::setNextCreationFrame()
  209. {
  210. UnsignedInt delay = GameLogicRandomValue( getOCLUpdateModuleData()->m_minDelay,
  211. getOCLUpdateModuleData()->m_maxDelay );
  212. m_timerStartedFrame = TheGameLogic->getFrame();
  213. m_nextCreationFrame = m_timerStartedFrame + delay;
  214. }
  215. // ------------------------------------------------------------------------------------------------
  216. // ------------------------------------------------------------------------------------------------
  217. Real OCLUpdate::getCountdownPercent() const
  218. {
  219. UnsignedInt now = TheGameLogic->getFrame();
  220. return 1.0f - (( m_nextCreationFrame - now ) / (float)( m_nextCreationFrame - m_timerStartedFrame ));
  221. }
  222. // ------------------------------------------------------------------------------------------------
  223. // ------------------------------------------------------------------------------------------------
  224. UnsignedInt OCLUpdate::getRemainingFrames() const
  225. {
  226. UnsignedInt now = TheGameLogic->getFrame();
  227. return ( m_nextCreationFrame - now );
  228. }
  229. // ------------------------------------------------------------------------------------------------
  230. /** CRC */
  231. // ------------------------------------------------------------------------------------------------
  232. void OCLUpdate::crc( Xfer *xfer )
  233. {
  234. // extend base class
  235. UpdateModule::crc( xfer );
  236. } // end crc
  237. // ------------------------------------------------------------------------------------------------
  238. /** Xfer method
  239. * Version Info:
  240. * 1: Initial version */
  241. // ------------------------------------------------------------------------------------------------
  242. void OCLUpdate::xfer( Xfer *xfer )
  243. {
  244. // version
  245. XferVersion currentVersion = 1;
  246. XferVersion version = currentVersion;
  247. xfer->xferVersion( &version, currentVersion );
  248. // extend base class
  249. UpdateModule::xfer( xfer );
  250. // next creation frame
  251. xfer->xferUnsignedInt( &m_nextCreationFrame );
  252. // timer stated frame
  253. xfer->xferUnsignedInt( &m_timerStartedFrame );
  254. // faction status
  255. xfer->xferBool( &m_isFactionNeutral );
  256. // current owning player color
  257. xfer->xferInt( &m_currentPlayerColor );
  258. } // end xfer
  259. // ------------------------------------------------------------------------------------------------
  260. /** Load post process */
  261. // ------------------------------------------------------------------------------------------------
  262. void OCLUpdate::loadPostProcess( void )
  263. {
  264. // extend base class
  265. UpdateModule::loadPostProcess();
  266. } // end loadPostProcess