Eva.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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. // GameClient/Eva.cpp /////////////////////////////////////////////////////////////////////////////
  24. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  25. #include "GameClient/Eva.h"
  26. #include "Common/Player.h"
  27. #include "Common/PlayerList.h"
  28. #include "GameLogic/GameLogic.h"
  29. #ifdef _INTERNAL
  30. // for occasional debugging...
  31. //#pragma optimize("", off)
  32. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  33. #endif
  34. //-------------------------------------------------------------------------------------------------
  35. const char *TheEvaMessageNames[] =
  36. {
  37. "LOWPOWER",
  38. "INSUFFICIENTFUNDS",
  39. "SUPERWEAPONDETECTED_PARTICLECANNON",
  40. "SUPERWEAPONDETECTED_NUKE",
  41. "SUPERWEAPONDETECTED_SCUDSTORM",
  42. "SUPERWEAPONLAUNCHED_PARTICLECANNON",
  43. "SUPERWEAPONLAUNCHED_NUKE",
  44. "SUPERWEAPONLAUNCHED_SCUDSTORM",
  45. "BUILDINGLOST",
  46. "BASEUNDERATTACK",
  47. "ALLYUNDERATTACK",
  48. "BEACONDETECTED",
  49. "UNITLOST",
  50. "GENERALLEVELUP",
  51. "VEHICLESTOLEN",
  52. "BUILDINGSTOLEN",
  53. "CASHSTOLEN",
  54. "UPGRADECOMPLETE",
  55. "BUILDINGBEINGSTOLEN",
  56. "EVA_INVALID",
  57. };
  58. //------------------------------------------------------------------------------ INI::parseEvaEvent
  59. void INI::parseEvaEvent( INI* ini )
  60. {
  61. AsciiString name;
  62. // read the name
  63. const char* c = ini->getNextToken();
  64. name.set( c );
  65. EvaCheckInfo *check = TheEva->newEvaCheckInfo( name );
  66. if (!check) {
  67. // could be null because it already exists.
  68. return;
  69. }
  70. // parse the ini definition
  71. ini->initFromINI( check, check->getFieldParse() );
  72. }
  73. //----------------------------------------------------------------------------------- EvaSideSounds
  74. static void parseSideSoundsList( INI *ini, void *instance, void *store, const void* userData )
  75. {
  76. std::vector<EvaSideSounds> *sounds = (std::vector<EvaSideSounds>*) store;
  77. EvaSideSounds newSounds;
  78. ini->initFromINI( &newSounds, newSounds.getFieldParse() );
  79. // This could be made more efficient, but to be honest, it shouldn't be that slow.
  80. sounds->push_back(newSounds);
  81. }
  82. //----------------------------------------------------------------------------------- EvaSideSounds
  83. const FieldParse EvaSideSounds::s_evaSideSounds[] =
  84. {
  85. { "Side", INI::parseAsciiString, NULL, offsetof( EvaSideSounds, m_side) },
  86. { "Sounds", INI::parseSoundsList, NULL, offsetof( EvaSideSounds, m_soundNames) },
  87. { 0, 0, 0, 0 },
  88. };
  89. //------------------------------------------------------------------------------------ EvaCheckInfo
  90. EvaCheckInfo::EvaCheckInfo() :
  91. m_message(EVA_COUNT),
  92. m_priority(0), // lowest of all priorities
  93. m_framesBetweenChecks(900), // 30 seconds at 30 fps
  94. m_framesToExpire(150) // 5 seconds at 30 fps
  95. {
  96. }
  97. //-------------------------------------------------------------------------------------------------
  98. const FieldParse EvaCheckInfo::s_evaEventInfo[] =
  99. {
  100. { "Priority", INI::parseUnsignedInt, NULL, offsetof( EvaCheckInfo, m_priority ) },
  101. { "TimeBetweenChecksMS", INI::parseDurationUnsignedInt, NULL, offsetof( EvaCheckInfo, m_framesBetweenChecks ) },
  102. { "ExpirationTimeMS", INI::parseDurationUnsignedInt, NULL, offsetof( EvaCheckInfo, m_framesToExpire) },
  103. { "SideSounds", parseSideSoundsList, NULL, offsetof( EvaCheckInfo, m_evaSideSounds ) },
  104. { 0, 0, 0, 0 },
  105. };
  106. //-------------------------------------------------------------------------------------------------
  107. const ShouldPlayFunc Eva::s_shouldPlayFuncs[] =
  108. {
  109. Eva::shouldPlayLowPower,
  110. Eva::shouldPlayGenericHandler,
  111. Eva::shouldPlayGenericHandler,
  112. Eva::shouldPlayGenericHandler,
  113. Eva::shouldPlayGenericHandler,
  114. Eva::shouldPlayGenericHandler,
  115. Eva::shouldPlayGenericHandler,
  116. Eva::shouldPlayGenericHandler,
  117. Eva::shouldPlayGenericHandler,
  118. Eva::shouldPlayGenericHandler,
  119. Eva::shouldPlayGenericHandler,
  120. Eva::shouldPlayGenericHandler,
  121. Eva::shouldPlayGenericHandler,
  122. Eva::shouldPlayGenericHandler,
  123. Eva::shouldPlayGenericHandler,
  124. Eva::shouldPlayGenericHandler,
  125. Eva::shouldPlayGenericHandler,
  126. Eva::shouldPlayGenericHandler,
  127. Eva::shouldPlayGenericHandler,
  128. NULL,
  129. };
  130. //-------------------------------------------------------------------------------------------------
  131. EvaCheck::EvaCheck() :
  132. m_evaInfo(NULL),
  133. m_triggeredOnFrame(TRIGGEREDON_NOT),
  134. m_timeForNextCheck(NEXT_CHECK_NOW),
  135. m_alreadyPlayed(FALSE)
  136. {
  137. }
  138. //-------------------------------------------------------------------------------------------------
  139. Eva::Eva() :
  140. m_localPlayer(NULL),
  141. m_previousBuildingCount(0),
  142. m_previousUnitCount(0),
  143. m_enabled(TRUE)
  144. {
  145. for (Int i = 0; i < EVA_COUNT; ++i) {
  146. m_shouldPlay[i] = FALSE;
  147. }
  148. }
  149. //-------------------------------------------------------------------------------------------------
  150. Eva::~Eva()
  151. {
  152. EvaCheckInfoPtrVecIt it;
  153. for (it = m_allCheckInfos.begin(); it != m_allCheckInfos.end(); ++it) {
  154. if (*it)
  155. (*it)->deleteInstance();
  156. }
  157. }
  158. //-------------------------------------------------------------------------------------------------
  159. void Eva::init()
  160. {
  161. // parse the INI here, etc.
  162. INI ini;
  163. ini.load( AsciiString( "Data\\INI\\Eva.ini" ), INI_LOAD_OVERWRITE, NULL);
  164. }
  165. //-------------------------------------------------------------------------------------------------
  166. void Eva::reset()
  167. {
  168. m_previousUnitCount = 0;
  169. m_previousBuildingCount = 0;
  170. // remove all pending counters, etc, here.
  171. EvaCheckVecIt it;
  172. for (it = m_checks.begin(); it != m_checks.end(); /* empty */) {
  173. it = m_checks.erase(it);
  174. }
  175. // remove all things flagged as "need to play"
  176. for (Int i = 0; i < EVA_COUNT; ++i) {
  177. m_shouldPlay[i] = FALSE;
  178. }
  179. // If we were previously disabled, re-enable ourselves.
  180. m_enabled = TRUE;
  181. }
  182. //-------------------------------------------------------------------------------------------------
  183. void Eva::update()
  184. {
  185. if (!m_enabled) {
  186. return;
  187. }
  188. m_localPlayer = ThePlayerList->getLocalPlayer();
  189. UnsignedInt frame = TheGameLogic->getFrame();
  190. // Don't update for the first few frames. This way, we don't have to deal with our initial power
  191. // being 0, etc.
  192. if (frame < 2) {
  193. return;
  194. }
  195. for (Int mesg = (Int)EVA_FIRST; mesg < (Int)EVA_COUNT; ++mesg) {
  196. if (isTimeForCheck((EvaMessage)mesg, frame)) {
  197. if (messageShouldPlay((EvaMessage)mesg, frame)) {
  198. playMessage((EvaMessage)mesg, frame);
  199. }
  200. }
  201. }
  202. processPlayingMessages(frame);
  203. m_localPlayer = NULL;
  204. // Reset all of the flags that have been set to true that haven't actually been probed, because
  205. // they will need to trigger again to be valid messages.
  206. for (Int i = EVA_FIRST; i < EVA_COUNT; ++i) {
  207. m_shouldPlay[i] = FALSE;
  208. }
  209. }
  210. //-------------------------------------------------------------------------------------------------
  211. EvaMessage Eva::nameToMessage(const AsciiString& name)
  212. {
  213. for (Int i = EVA_FIRST; i < EVA_COUNT; ++i) {
  214. if (name.compareNoCase(TheEvaMessageNames[i]) == 0) {
  215. return (EvaMessage) i;
  216. }
  217. }
  218. DEBUG_CRASH(("Invalid requested Eva message translation :%s: jkmcd", name.str()));
  219. return EVA_COUNT;
  220. }
  221. //-------------------------------------------------------------------------------------------------
  222. AsciiString Eva::messageToName(EvaMessage message)
  223. {
  224. if (message >= EVA_FIRST && message < EVA_COUNT)
  225. return TheEvaMessageNames[message];
  226. DEBUG_CRASH(("Invalid requested Eva message translation. jkmcd"));
  227. return AsciiString::TheEmptyString;
  228. }
  229. //-------------------------------------------------------------------------------------------------
  230. EvaCheckInfo *Eva::newEvaCheckInfo(AsciiString name)
  231. {
  232. EvaMessage mesg = nameToMessage(name);
  233. // Only return a new one if there isn't an existing one.
  234. EvaCheckInfoPtrVecIt it;
  235. for (it = m_allCheckInfos.begin(); it != m_allCheckInfos.end(); ++it) {
  236. if (*it && (*it)->m_message == mesg)
  237. return NULL;
  238. }
  239. EvaCheckInfo *checkInfo = newInstance(EvaCheckInfo);
  240. m_allCheckInfos.push_back(checkInfo);
  241. checkInfo->m_message = mesg;
  242. return checkInfo;
  243. }
  244. //-------------------------------------------------------------------------------------------------
  245. const EvaCheckInfo *Eva::getEvaCheckInfo(AsciiString name)
  246. {
  247. EvaMessage mesg = nameToMessage(name);
  248. // Only return a new one if there isn't an existing one.
  249. EvaCheckInfoPtrVecIt it;
  250. for (it = m_allCheckInfos.begin(); it != m_allCheckInfos.end(); ++it) {
  251. if (*it && (*it)->m_message == mesg)
  252. return *it;
  253. }
  254. return NULL;
  255. }
  256. //-------------------------------------------------------------------------------------------------
  257. void Eva::setShouldPlay(EvaMessage messageToPlay)
  258. {
  259. m_shouldPlay[messageToPlay] = TRUE;
  260. }
  261. //-------------------------------------------------------------------------------------------------
  262. void Eva::setEvaEnabled(Bool enabled)
  263. {
  264. // clear out any waiting messages.
  265. for (Int i = EVA_FIRST; i < EVA_COUNT; ++i) {
  266. m_shouldPlay[i] = FALSE;
  267. }
  268. m_enabled = enabled;
  269. }
  270. //-------------------------------------------------------------------------------------------------
  271. Bool Eva::isTimeForCheck(EvaMessage messageToTest, UnsignedInt currentFrame) const
  272. {
  273. EvaCheckVec::const_iterator it;
  274. for (it = m_checks.begin(); it != m_checks.end(); ++it) {
  275. if (it->m_evaInfo->m_message == messageToTest) {
  276. return FALSE;
  277. }
  278. }
  279. return TRUE;
  280. }
  281. //-------------------------------------------------------------------------------------------------
  282. Bool Eva::messageShouldPlay(EvaMessage messageToTest, UnsignedInt currentFrame) const
  283. {
  284. if (m_localPlayer == NULL) {
  285. return FALSE;
  286. }
  287. m_messageBeingTested = messageToTest;
  288. return s_shouldPlayFuncs[messageToTest](m_localPlayer);
  289. }
  290. //-------------------------------------------------------------------------------------------------
  291. Bool Eva::shouldPlayLowPower( Player *localPlayer )
  292. {
  293. // @todo make eva sensitive to whether player can do anything about it...
  294. // "Low power, Low power, Low power, yadda yadda yadda..."
  295. //const ThingTemplate *chinaReactorTemplate = findTemplate(;
  296. //const ThingTemplate *americanReactorTemplate;
  297. //if ( chinaReactorTemplate && americanReactorTemplate )
  298. //{
  299. // if ( ! (localPlayer->canBuild(chinaReactorTemplate) || localPlayer->canBuild(americanReactorTemplate)) )
  300. // return FALSE
  301. //}
  302. return !localPlayer->getEnergy()->hasSufficientPower();
  303. }
  304. //-------------------------------------------------------------------------------------------------
  305. Bool Eva::shouldPlayGenericHandler( Player * )
  306. {
  307. if (TheEva->m_shouldPlay[TheEva->m_messageBeingTested]) {
  308. TheEva->m_shouldPlay[TheEva->m_messageBeingTested] = FALSE;
  309. return TRUE;
  310. }
  311. return FALSE;
  312. }
  313. //-------------------------------------------------------------------------------------------------
  314. void Eva::playMessage(EvaMessage messageToTest, UnsignedInt currentFrame)
  315. {
  316. EvaCheck check;
  317. check.m_evaInfo = getEvaCheckInfo(Eva::messageToName(messageToTest));
  318. if (!check.m_evaInfo) {
  319. return;
  320. }
  321. check.m_timeForNextCheck = currentFrame + check.m_evaInfo->m_framesBetweenChecks;
  322. check.m_triggeredOnFrame = currentFrame;
  323. check.m_alreadyPlayed = FALSE;
  324. m_checks.push_back(check);
  325. }
  326. //-------------------------------------------------------------------------------------------------
  327. void Eva::processPlayingMessages(UnsignedInt currentFrame)
  328. {
  329. // First pass, remove all the objects that can check after this frame.
  330. EvaCheckVecIt it;
  331. for (it = m_checks.begin(); it != m_checks.end(); /* empty */) {
  332. // These are requests that will be available next frame because they've played.
  333. if (it->m_timeForNextCheck <= currentFrame + 1 && it->m_alreadyPlayed) {
  334. it = m_checks.erase(it);
  335. continue;
  336. }
  337. // These are requests that never got a chance to play and have since expired.
  338. if (it->m_triggeredOnFrame + it->m_evaInfo->m_framesToExpire <= currentFrame && !it->m_alreadyPlayed) {
  339. it = m_checks.erase(it);
  340. continue;
  341. }
  342. ++it;
  343. }
  344. // It's possible, although unlikely, that we removed everything in the list.
  345. if (m_checks.begin() == m_checks.end()) {
  346. return;
  347. }
  348. // If we're currently playing some audio, we're done.
  349. if (m_evaSpeech.isCurrentlyPlaying()) {
  350. return;
  351. }
  352. // Okay. No one is currently playing anything, so lets find an event and trigger it.
  353. EvaCheckVecIt storedIt = m_checks.end();
  354. UnsignedInt highestPriority = 0;
  355. for (it = m_checks.begin(); it != m_checks.end(); ++it) {
  356. if (it->m_evaInfo->m_priority > highestPriority && !it->m_alreadyPlayed) {
  357. storedIt = it;
  358. highestPriority = it->m_evaInfo->m_priority;
  359. }
  360. }
  361. // There wasn't anything waiting to play.
  362. if (storedIt == m_checks.end()) {
  363. return;
  364. }
  365. // We've got a winner!
  366. AsciiString side = ThePlayerList->getLocalPlayer()->getSide();
  367. Int numSides = storedIt->m_evaInfo->m_evaSideSounds.size();
  368. for (Int i = 0; i < numSides; ++i) {
  369. if (side.compareNoCase(storedIt->m_evaInfo->m_evaSideSounds[i].m_side) == 0) {
  370. // Its this one.
  371. if (storedIt->m_evaInfo->m_evaSideSounds[i].m_soundNames.size() > 0) {
  372. Int soundToPlay = GameClientRandomValue(0, storedIt->m_evaInfo->m_evaSideSounds[i].m_soundNames.size() - 1);
  373. m_evaSpeech.setEventName(storedIt->m_evaInfo->m_evaSideSounds[i].m_soundNames[soundToPlay]);
  374. } else {
  375. // clear it.
  376. m_evaSpeech.setEventName(AsciiString::TheEmptyString);
  377. }
  378. }
  379. }
  380. // Update the entry
  381. storedIt->m_alreadyPlayed = true;
  382. storedIt->m_timeForNextCheck = currentFrame + storedIt->m_evaInfo->m_framesBetweenChecks;
  383. // Now that we correctly filter messages, we need to set the player index for who should hear the
  384. // sound to the local player.
  385. m_evaSpeech.setPlayerIndex(m_localPlayer->getPlayerIndex());
  386. m_evaSpeech.setPlayingHandle(TheAudio->addAudioEvent(&m_evaSpeech));
  387. }
  388. //-------------------------------------------------------------------------------------------------
  389. Eva *TheEva = NULL;